additional-email-templates
This commit is contained in:
parent
e232c50e52
commit
9d889e6209
|
|
@ -13,7 +13,11 @@ export const CustomizationPanel: React.FC<CustomizationPanelProps> = ({ active }
|
|||
const [showBackgroundPicker, setShowBackgroundPicker] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Send initial config to preview
|
||||
// Save to localStorage for persistence and cross-tab sync
|
||||
if (typeof window !== "undefined") {
|
||||
localStorage.setItem("email-branding-config", JSON.stringify(config));
|
||||
|
||||
// Send message for immediate updates
|
||||
window.postMessage(
|
||||
{
|
||||
type: "CUSTOMIZATION_UPDATE",
|
||||
|
|
@ -21,6 +25,14 @@ export const CustomizationPanel: React.FC<CustomizationPanelProps> = ({ active }
|
|||
},
|
||||
"*"
|
||||
);
|
||||
|
||||
// Dispatch custom event for same-tab updates
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("customization-update", {
|
||||
detail: { config },
|
||||
})
|
||||
);
|
||||
}
|
||||
}, [config]);
|
||||
|
||||
const handleColorChange = (colorType: "primary" | "secondary" | "background") => (
|
||||
|
|
|
|||
5
.storybook/manager.ts
Normal file
5
.storybook/manager.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { addons } from "@storybook/manager-api";
|
||||
|
||||
addons.setConfig({
|
||||
panelPosition: "bottom",
|
||||
});
|
||||
|
|
@ -2,6 +2,22 @@ import type { Preview } from "@storybook/react";
|
|||
import { CustomizationDecorator } from "../stories/CustomizationDecorator";
|
||||
import React from "react";
|
||||
|
||||
// Load Google Fonts for better rendering in Storybook
|
||||
const googleFonts = [
|
||||
"Roboto",
|
||||
"Open+Sans",
|
||||
"Lato",
|
||||
"Montserrat",
|
||||
"Poppins",
|
||||
];
|
||||
|
||||
if (typeof document !== "undefined") {
|
||||
const link = document.createElement("link");
|
||||
link.rel = "stylesheet";
|
||||
link.href = `https://fonts.googleapis.com/css2?${googleFonts.map(font => `family=${font}:wght@400;600;700`).join("&")}&display=swap`;
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
|
||||
const preview: Preview = {
|
||||
parameters: {
|
||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
||||
|
|
|
|||
198
README.md
Normal file
198
README.md
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
# Fortune System Email Templates
|
||||
|
||||
A collection of React Email templates for iGaming systems with live customization capabilities. Preview templates in Storybook and customize colors, fonts, and branding in real-time.
|
||||
|
||||
## Features
|
||||
|
||||
- 📧 **Email Templates**: 5 professional email templates for promotional and reporting purposes
|
||||
- Promotional: Raffle, Referral Bonus, Deposit Bonus
|
||||
- Reports: Weekly Activity Report, Monthly Activity Report
|
||||
- 🎨 **Live Customization**: Real-time preview updates as you change colors and fonts
|
||||
- 🎯 **Color Pickers**: Visual color pickers for primary, secondary, and background colors
|
||||
- 🔤 **Font Selection**: Dropdown with 10 popular web-safe and Google Fonts
|
||||
- ⚙️ **Configurable Branding**: Easy configuration through a centralized config file
|
||||
- 📱 **Email Compatible**: Uses React Email components for maximum email client compatibility
|
||||
- 🎭 **Storybook Preview**: Professional preview system for showcasing templates to clients
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
fortune-sys-emails/
|
||||
├── src/
|
||||
│ ├── emails/
|
||||
│ │ ├── promotional/ # Promotional email templates
|
||||
│ │ └── reports/ # Report email templates
|
||||
│ ├── components/ # Reusable email components
|
||||
│ ├── config/ # Branding configuration
|
||||
│ ├── hooks/ # React hooks
|
||||
│ └── utils/ # Helper functions
|
||||
├── .storybook/ # Storybook configuration
|
||||
├── stories/ # Storybook stories
|
||||
└── package.json
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
1. Install dependencies:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
2. Start Storybook:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
3. Open your browser to `http://localhost:6006`
|
||||
|
||||
## Usage
|
||||
|
||||
### Viewing Templates
|
||||
|
||||
1. Start Storybook with `npm run dev`
|
||||
2. Navigate to the "Emails" section in the Storybook sidebar
|
||||
3. Browse through the available templates:
|
||||
- **Promotional**: Raffle Email, Referral Bonus Email, Deposit Bonus Email
|
||||
- **Reports**: Weekly Report Email, Monthly Report Email
|
||||
|
||||
### Customizing Branding
|
||||
|
||||
#### Method 1: Live Customization Panel
|
||||
|
||||
1. Navigate to "Customization > Customization Panel" in Storybook
|
||||
2. Use the controls to:
|
||||
- Select a font from the dropdown (10 popular fonts available)
|
||||
- Pick colors using the color pickers:
|
||||
- Primary Color
|
||||
- Secondary Color
|
||||
- Background Color
|
||||
3. Changes are applied instantly to all email templates
|
||||
4. Customizations are saved in browser localStorage for persistence
|
||||
|
||||
#### Method 2: Configuration File
|
||||
|
||||
Edit `src/config/branding.config.ts` to set default branding:
|
||||
|
||||
```typescript
|
||||
export const defaultBrandingConfig: BrandingConfig = {
|
||||
companyName: "Your Company Name",
|
||||
logoUrl: "https://your-logo-url.com/logo.png",
|
||||
colors: {
|
||||
primary: "#0066CC",
|
||||
secondary: "#00CC66",
|
||||
background: "#F5F5F5",
|
||||
text: "#333333",
|
||||
},
|
||||
font: {
|
||||
family: "Arial, sans-serif",
|
||||
},
|
||||
contact: {
|
||||
email: "support@yourcompany.com",
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### Available Fonts
|
||||
|
||||
The following fonts are available in the customization panel:
|
||||
|
||||
- Arial
|
||||
- Helvetica
|
||||
- Times New Roman
|
||||
- Georgia
|
||||
- Verdana
|
||||
- Roboto
|
||||
- Open Sans
|
||||
- Lato
|
||||
- Montserrat
|
||||
- Poppins
|
||||
|
||||
## Email Templates
|
||||
|
||||
### Promotional Templates
|
||||
|
||||
#### Raffle Email
|
||||
- **Purpose**: Promote raffle events and contests
|
||||
- **Props**: `raffleName`, `prizeAmount`, `entryDeadline`, `drawDate`, `entryLink`, `participantName`
|
||||
|
||||
#### Referral Bonus Email
|
||||
- **Purpose**: Encourage user referrals with bonus offers
|
||||
- **Props**: `referrerName`, `referralBonus`, `referredBonus`, `referralCode`, `referralLink`, `expirationDate`
|
||||
|
||||
#### Deposit Bonus Email
|
||||
- **Purpose**: Promote deposit bonuses and special offers
|
||||
- **Props**: `playerName`, `bonusPercentage`, `maxBonus`, `minimumDeposit`, `bonusCode`, `depositLink`, `expirationDate`
|
||||
|
||||
### Report Templates
|
||||
|
||||
#### Weekly Report Email
|
||||
- **Purpose**: Weekly activity summary for administrators
|
||||
- **Props**: `reportPeriod`, `totalDeposits`, `totalWithdrawals`, `activeUsers`, `newUsers`, `totalRevenue`, `topGames`
|
||||
|
||||
#### Monthly Report Email
|
||||
- **Purpose**: Comprehensive monthly activity report
|
||||
- **Props**: `reportMonth`, `totalDeposits`, `totalWithdrawals`, `activeUsers`, `newUsers`, `totalRevenue`, `averageDeposit`, `retentionRate`, `topGames`, `growthStats`
|
||||
|
||||
## Customization Workflow
|
||||
|
||||
1. **Open Storybook**: Run `npm run dev`
|
||||
2. **Open Customization Panel**: Navigate to "Customization > Customization Panel"
|
||||
3. **Customize**: Adjust colors and fonts using the controls
|
||||
4. **Preview**: Navigate to any email template to see the changes
|
||||
5. **Share**: Share the Storybook URL with clients for feedback
|
||||
6. **Export**: Copy the customization values and update `branding.config.ts` for persistence
|
||||
|
||||
## Development
|
||||
|
||||
### Adding New Templates
|
||||
|
||||
1. Create a new component in `src/emails/` (appropriate subfolder)
|
||||
2. Use the `EmailLayout` component as a wrapper
|
||||
3. Use `useBrandingConfig` hook to access branding configuration
|
||||
4. Create a story file in `stories/` directory
|
||||
|
||||
Example:
|
||||
|
||||
```typescript
|
||||
import { EmailLayout } from "../../components/EmailLayout";
|
||||
import { useBrandingConfig } from "../../hooks/useBrandingConfig";
|
||||
|
||||
export const MyNewEmail = () => {
|
||||
const config = useBrandingConfig();
|
||||
return (
|
||||
<EmailLayout title="My Email">
|
||||
{/* Your email content */}
|
||||
</EmailLayout>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Building
|
||||
|
||||
Build Storybook for static hosting:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
The built files will be in the `storybook-static` directory.
|
||||
|
||||
## Technologies Used
|
||||
|
||||
- **React Email**: For email-compatible React components
|
||||
- **Storybook**: For component preview and documentation
|
||||
- **TypeScript**: For type safety
|
||||
- **React Color**: For color picker functionality
|
||||
|
||||
## Notes
|
||||
|
||||
- All templates are responsive and tested for email client compatibility
|
||||
- Customizations made in the panel are stored in browser localStorage
|
||||
- The configuration file (`branding.config.ts`) sets the default values
|
||||
- Google Fonts (Roboto, Open Sans, Lato, Montserrat, Poppins) need to be loaded in your email system or use web-safe fonts for maximum compatibility
|
||||
|
||||
## License
|
||||
|
||||
Private project - All rights reserved
|
||||
343
src/emails/admin/SportsbookTicketIssuesEmail.tsx
Normal file
343
src/emails/admin/SportsbookTicketIssuesEmail.tsx
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
import {
|
||||
Section,
|
||||
Text,
|
||||
Heading,
|
||||
Hr,
|
||||
Row,
|
||||
Column,
|
||||
} from "@react-email/components";
|
||||
import { EmailLayout } from "../../components/EmailLayout";
|
||||
import { useBrandingConfig } from "../../hooks/useBrandingConfig";
|
||||
import { formatCurrency, formatDate } from "../../utils/emailHelpers";
|
||||
|
||||
interface SportsbookTicketIssuesEmailProps {
|
||||
ticketId?: string;
|
||||
issueType?: "void" | "cancellation" | "correction" | "settlement_error";
|
||||
ticketDetails?: {
|
||||
ticketNumber: string;
|
||||
betType: string;
|
||||
event: string;
|
||||
selection: string;
|
||||
stake: number;
|
||||
potentialPayout: number;
|
||||
status: string;
|
||||
placedDate: Date | string;
|
||||
};
|
||||
reason?: string;
|
||||
actionTaken?: string;
|
||||
affectedUser?: {
|
||||
username: string;
|
||||
email: string;
|
||||
accountId: string;
|
||||
};
|
||||
resolutionNotes?: string;
|
||||
administrator?: string;
|
||||
}
|
||||
|
||||
export const SportsbookTicketIssuesEmail = ({
|
||||
ticketId = "TKT-2024-001234",
|
||||
issueType = "void",
|
||||
ticketDetails = {
|
||||
ticketNumber: "TKT-2024-001234",
|
||||
betType: "Single",
|
||||
event: "Manchester United vs Liverpool",
|
||||
selection: "Manchester United Win",
|
||||
stake: 100,
|
||||
potentialPayout: 250,
|
||||
status: "Void",
|
||||
placedDate: new Date(),
|
||||
},
|
||||
reason = "Event cancellation - Match postponed due to weather conditions",
|
||||
actionTaken = "Ticket voided and stake refunded to user account",
|
||||
affectedUser = {
|
||||
username: "player123",
|
||||
email: "player@example.com",
|
||||
accountId: "ACC-12345",
|
||||
},
|
||||
resolutionNotes = "Refund processed automatically. User notified via email.",
|
||||
administrator = "Admin Team",
|
||||
}: SportsbookTicketIssuesEmailProps) => {
|
||||
const config = useBrandingConfig();
|
||||
|
||||
const getIssueTypeLabel = (type: string) => {
|
||||
switch (type) {
|
||||
case "void":
|
||||
return "Ticket Voided";
|
||||
case "cancellation":
|
||||
return "Ticket Cancelled";
|
||||
case "correction":
|
||||
return "Ticket Correction";
|
||||
case "settlement_error":
|
||||
return "Settlement Error";
|
||||
default:
|
||||
return "Ticket Issue";
|
||||
}
|
||||
};
|
||||
|
||||
const getSeverityColor = (type: string) => {
|
||||
switch (type) {
|
||||
case "settlement_error":
|
||||
return "#dc3545"; // Red for errors
|
||||
case "correction":
|
||||
return "#ffc107"; // Yellow for corrections
|
||||
default:
|
||||
return config.colors.primary;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<EmailLayout title={`⚠️ ${getIssueTypeLabel(issueType)} - ${ticketId}`}>
|
||||
<Section>
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: getSeverityColor(issueType) + "15",
|
||||
padding: "20px",
|
||||
borderRadius: "8px",
|
||||
marginBottom: "20px",
|
||||
border: `2px solid ${getSeverityColor(issueType)}`,
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "24px",
|
||||
color: getSeverityColor(issueType),
|
||||
marginTop: 0,
|
||||
marginBottom: "10px",
|
||||
}}
|
||||
>
|
||||
{getIssueTypeLabel(issueType)}
|
||||
</Heading>
|
||||
<Text
|
||||
style={{ fontSize: "16px", fontWeight: "bold", margin: "5px 0" }}
|
||||
>
|
||||
Ticket ID: {ticketId}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", color: "#666666", margin: "5px 0" }}>
|
||||
Issue Type: {issueType.toUpperCase()}
|
||||
</Text>
|
||||
</Section>
|
||||
|
||||
<Hr style={{ borderColor: config.colors.primary, margin: "30px 0" }} />
|
||||
|
||||
{/* Affected User Information */}
|
||||
<Section style={{ marginBottom: "25px" }}>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "20px",
|
||||
color: config.colors.text,
|
||||
marginTop: "20px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
👤 Affected User
|
||||
</Heading>
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.background,
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
>
|
||||
<Row>
|
||||
<Column>
|
||||
<Text style={{ fontSize: "14px", margin: "5px 0" }}>
|
||||
<strong>Username:</strong> {affectedUser.username}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "5px 0" }}>
|
||||
<strong>Email:</strong> {affectedUser.email}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "5px 0" }}>
|
||||
<strong>Account ID:</strong> {affectedUser.accountId}
|
||||
</Text>
|
||||
</Column>
|
||||
</Row>
|
||||
</Section>
|
||||
</Section>
|
||||
|
||||
{/* Ticket Details */}
|
||||
<Section style={{ marginBottom: "25px" }}>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "20px",
|
||||
color: config.colors.text,
|
||||
marginTop: "20px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
🎫 Ticket Details
|
||||
</Heading>
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.background,
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
>
|
||||
<Row>
|
||||
<Column style={{ width: "50%" }}>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Ticket Number:</strong>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Bet Type:</strong>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Event:</strong>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Selection:</strong>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Placed Date:</strong>
|
||||
</Text>
|
||||
</Column>
|
||||
<Column>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
{ticketDetails.ticketNumber}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
{ticketDetails.betType}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
{ticketDetails.event}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
{ticketDetails.selection}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
{formatDate(ticketDetails.placedDate)}
|
||||
</Text>
|
||||
</Column>
|
||||
</Row>
|
||||
|
||||
<Hr style={{ borderColor: "#ddd", margin: "15px 0" }} />
|
||||
|
||||
<Row>
|
||||
<Column style={{ width: "50%" }}>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Stake:</strong>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Potential Payout:</strong>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Status:</strong>
|
||||
</Text>
|
||||
</Column>
|
||||
<Column>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
color: config.colors.primary,
|
||||
margin: "8px 0",
|
||||
}}
|
||||
>
|
||||
{formatCurrency(ticketDetails.stake)}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
color: config.colors.secondary,
|
||||
margin: "8px 0",
|
||||
}}
|
||||
>
|
||||
{formatCurrency(ticketDetails.potentialPayout)}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
color: getSeverityColor(issueType),
|
||||
margin: "8px 0",
|
||||
}}
|
||||
>
|
||||
{ticketDetails.status}
|
||||
</Text>
|
||||
</Column>
|
||||
</Row>
|
||||
</Section>
|
||||
</Section>
|
||||
|
||||
{/* Issue Information */}
|
||||
<Section style={{ marginBottom: "25px" }}>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "20px",
|
||||
color: config.colors.text,
|
||||
marginTop: "20px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
📋 Issue Information
|
||||
</Heading>
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: "#fff3cd",
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
border: "1px solid #ffc107",
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
margin: "0 0 10px 0",
|
||||
}}
|
||||
>
|
||||
Reason:
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "0 0 15px 0" }}>
|
||||
{reason}
|
||||
</Text>
|
||||
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
margin: "0 0 10px 0",
|
||||
}}
|
||||
>
|
||||
Action Taken:
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "0 0 15px 0" }}>
|
||||
{actionTaken}
|
||||
</Text>
|
||||
|
||||
{resolutionNotes && (
|
||||
<>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
margin: "0 0 10px 0",
|
||||
}}
|
||||
>
|
||||
Resolution Notes:
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: 0 }}>
|
||||
{resolutionNotes}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</Section>
|
||||
</Section>
|
||||
|
||||
<Hr style={{ borderColor: config.colors.primary, margin: "30px 0" }} />
|
||||
|
||||
<Text
|
||||
style={{ fontSize: "12px", color: "#666666", fontStyle: "italic" }}
|
||||
>
|
||||
This is an automated notification from the {config.companyName}{" "}
|
||||
sportsbook system.
|
||||
<br />
|
||||
Processed by: {administrator}
|
||||
<br />
|
||||
Timestamp: {new Date().toLocaleString()}
|
||||
</Text>
|
||||
</Section>
|
||||
</EmailLayout>
|
||||
);
|
||||
};
|
||||
300
src/emails/admin/SystemAlertEmail.tsx
Normal file
300
src/emails/admin/SystemAlertEmail.tsx
Normal file
|
|
@ -0,0 +1,300 @@
|
|||
import { Section, Text, Heading, Hr, Row, Column } from "@react-email/components";
|
||||
import { EmailLayout } from "../../components/EmailLayout";
|
||||
import { useBrandingConfig } from "../../hooks/useBrandingConfig";
|
||||
import { formatDate } from "../../utils/emailHelpers";
|
||||
|
||||
interface SystemAlertEmailProps {
|
||||
alertId?: string;
|
||||
alertType?: "critical" | "warning" | "info" | "maintenance";
|
||||
alertTitle?: string;
|
||||
alertDescription?: string;
|
||||
affectedSystems?: string[];
|
||||
severity?: "high" | "medium" | "low";
|
||||
detectedAt?: Date | string;
|
||||
resolvedAt?: Date | string;
|
||||
status?: "active" | "resolved" | "investigating";
|
||||
actionRequired?: string;
|
||||
technicalDetails?: string;
|
||||
incidentNumber?: string;
|
||||
}
|
||||
|
||||
export const SystemAlertEmail = ({
|
||||
alertId = "ALERT-2024-001",
|
||||
alertType = "warning",
|
||||
alertTitle = "High Transaction Volume Detected",
|
||||
alertDescription = "Transaction processing system is experiencing higher than normal load. Response times may be slightly delayed.",
|
||||
affectedSystems = ["Payment Gateway", "Transaction Processor", "User Accounts"],
|
||||
severity = "medium",
|
||||
detectedAt = new Date(),
|
||||
resolvedAt,
|
||||
status = "investigating",
|
||||
actionRequired = "Monitor system performance and scale resources if needed.",
|
||||
technicalDetails = "CPU usage: 85%, Memory usage: 72%, Response time: 1.2s (avg)",
|
||||
incidentNumber = "INC-2024-1234",
|
||||
}: SystemAlertEmailProps) => {
|
||||
const config = useBrandingConfig();
|
||||
|
||||
const getAlertColor = (type: string) => {
|
||||
switch (type) {
|
||||
case "critical":
|
||||
return "#dc3545"; // Red
|
||||
case "warning":
|
||||
return "#ffc107"; // Yellow
|
||||
case "maintenance":
|
||||
return "#17a2b8"; // Blue
|
||||
case "info":
|
||||
return config.colors.primary;
|
||||
default:
|
||||
return config.colors.primary;
|
||||
}
|
||||
};
|
||||
|
||||
const getSeverityColor = (severity: string) => {
|
||||
switch (severity) {
|
||||
case "high":
|
||||
return "#dc3545";
|
||||
case "medium":
|
||||
return "#ffc107";
|
||||
case "low":
|
||||
return "#28a745";
|
||||
default:
|
||||
return config.colors.primary;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status) {
|
||||
case "resolved":
|
||||
return "#28a745";
|
||||
case "investigating":
|
||||
return "#ffc107";
|
||||
case "active":
|
||||
return "#dc3545";
|
||||
default:
|
||||
return config.colors.primary;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<EmailLayout title={`🔔 System Alert - ${alertTitle}`}>
|
||||
<Section>
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: getAlertColor(alertType) + "15",
|
||||
padding: "20px",
|
||||
borderRadius: "8px",
|
||||
marginBottom: "20px",
|
||||
border: `2px solid ${getAlertColor(alertType)}`,
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "24px",
|
||||
color: getAlertColor(alertType),
|
||||
marginTop: 0,
|
||||
marginBottom: "10px",
|
||||
}}
|
||||
>
|
||||
{alertTitle}
|
||||
</Heading>
|
||||
<Row>
|
||||
<Column>
|
||||
<Text style={{ fontSize: "14px", margin: "5px 0" }}>
|
||||
<strong>Alert ID:</strong> {alertId}
|
||||
</Text>
|
||||
{incidentNumber && (
|
||||
<Text style={{ fontSize: "14px", margin: "5px 0" }}>
|
||||
<strong>Incident #:</strong> {incidentNumber}
|
||||
</Text>
|
||||
)}
|
||||
</Column>
|
||||
<Column>
|
||||
<Text style={{ fontSize: "14px", margin: "5px 0" }}>
|
||||
<strong>Type:</strong> {alertType.toUpperCase()}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "5px 0" }}>
|
||||
<strong>Severity:</strong>{" "}
|
||||
<span style={{ color: getSeverityColor(severity), fontWeight: "bold" }}>
|
||||
{severity.toUpperCase()}
|
||||
</span>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "5px 0" }}>
|
||||
<strong>Status:</strong>{" "}
|
||||
<span style={{ color: getStatusColor(status), fontWeight: "bold" }}>
|
||||
{status.toUpperCase()}
|
||||
</span>
|
||||
</Text>
|
||||
</Column>
|
||||
</Row>
|
||||
</Section>
|
||||
|
||||
<Hr style={{ borderColor: config.colors.primary, margin: "30px 0" }} />
|
||||
|
||||
{/* Alert Description */}
|
||||
<Section style={{ marginBottom: "25px" }}>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "20px",
|
||||
color: config.colors.text,
|
||||
marginTop: "20px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
📋 Alert Description
|
||||
</Heading>
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.background,
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: "14px", lineHeight: "22px", margin: 0 }}>
|
||||
{alertDescription}
|
||||
</Text>
|
||||
</Section>
|
||||
</Section>
|
||||
|
||||
{/* Affected Systems */}
|
||||
{affectedSystems && affectedSystems.length > 0 && (
|
||||
<Section style={{ marginBottom: "25px" }}>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "20px",
|
||||
color: config.colors.text,
|
||||
marginTop: "20px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
🔧 Affected Systems
|
||||
</Heading>
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.background,
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
>
|
||||
<ul style={{ margin: 0, paddingLeft: "20px" }}>
|
||||
{affectedSystems.map((system, index) => (
|
||||
<li key={index} style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
{system}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Section>
|
||||
</Section>
|
||||
)}
|
||||
|
||||
{/* Timeline */}
|
||||
<Section style={{ marginBottom: "25px" }}>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "20px",
|
||||
color: config.colors.text,
|
||||
marginTop: "20px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
⏰ Timeline
|
||||
</Heading>
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.background,
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Detected At:</strong> {formatDate(detectedAt)} (
|
||||
{new Date(detectedAt).toLocaleTimeString()})
|
||||
</Text>
|
||||
{resolvedAt && (
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Resolved At:</strong> {formatDate(resolvedAt)} (
|
||||
{new Date(resolvedAt).toLocaleTimeString()})
|
||||
</Text>
|
||||
)}
|
||||
{!resolvedAt && (
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0", color: "#ffc107" }}>
|
||||
<strong>Status:</strong> Under Investigation
|
||||
</Text>
|
||||
)}
|
||||
</Section>
|
||||
</Section>
|
||||
|
||||
{/* Technical Details */}
|
||||
{technicalDetails && (
|
||||
<Section style={{ marginBottom: "25px" }}>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "20px",
|
||||
color: config.colors.text,
|
||||
marginTop: "20px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
🔍 Technical Details
|
||||
</Heading>
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: "#f8f9fa",
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
border: "1px solid #dee2e6",
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "13px",
|
||||
fontFamily: "monospace",
|
||||
lineHeight: "20px",
|
||||
margin: 0,
|
||||
whiteSpace: "pre-wrap",
|
||||
}}
|
||||
>
|
||||
{technicalDetails}
|
||||
</Text>
|
||||
</Section>
|
||||
</Section>
|
||||
)}
|
||||
|
||||
{/* Action Required */}
|
||||
{actionRequired && (
|
||||
<Section style={{ marginBottom: "25px" }}>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "20px",
|
||||
color: config.colors.text,
|
||||
marginTop: "20px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
⚡ Action Required
|
||||
</Heading>
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: "#fff3cd",
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
border: "1px solid #ffc107",
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: "14px", margin: 0 }}>{actionRequired}</Text>
|
||||
</Section>
|
||||
</Section>
|
||||
)}
|
||||
|
||||
<Hr style={{ borderColor: config.colors.primary, margin: "30px 0" }} />
|
||||
|
||||
<Text style={{ fontSize: "12px", color: "#666666", fontStyle: "italic" }}>
|
||||
This is an automated system alert from {config.companyName}.
|
||||
<br />
|
||||
Timestamp: {new Date().toLocaleString()}
|
||||
<br />
|
||||
Please monitor the system dashboard for updates.
|
||||
</Text>
|
||||
</Section>
|
||||
</EmailLayout>
|
||||
);
|
||||
};
|
||||
247
src/emails/admin/UserSuspensionEmail.tsx
Normal file
247
src/emails/admin/UserSuspensionEmail.tsx
Normal file
|
|
@ -0,0 +1,247 @@
|
|||
import { Section, Text, Heading, Hr } from "@react-email/components";
|
||||
import { EmailLayout } from "../../components/EmailLayout";
|
||||
import { useBrandingConfig } from "../../hooks/useBrandingConfig";
|
||||
import { formatDate } from "../../utils/emailHelpers";
|
||||
|
||||
interface UserSuspensionEmailProps {
|
||||
userId?: string;
|
||||
username?: string;
|
||||
email?: string;
|
||||
suspensionType?: "temporary" | "permanent" | "warning";
|
||||
suspensionReason?: string;
|
||||
suspensionDuration?: number; // in days
|
||||
suspensionStartDate?: Date | string;
|
||||
suspensionEndDate?: Date | string;
|
||||
violationDetails?: string[];
|
||||
administrator?: string;
|
||||
appealInformation?: string;
|
||||
}
|
||||
|
||||
export const UserSuspensionEmail = ({
|
||||
userId = "ACC-12345",
|
||||
username = "player123",
|
||||
email = "player@example.com",
|
||||
suspensionType = "temporary",
|
||||
suspensionReason = "Violation of terms of service - Suspicious betting patterns detected",
|
||||
suspensionDuration = 30,
|
||||
suspensionStartDate = new Date(),
|
||||
suspensionEndDate = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
|
||||
violationDetails = [
|
||||
"Multiple accounts detected",
|
||||
"Violation of betting rules",
|
||||
"Suspicious activity patterns",
|
||||
],
|
||||
administrator = "Security Team",
|
||||
appealInformation = "To appeal this decision, please contact support@example.com within 7 days.",
|
||||
}: UserSuspensionEmailProps) => {
|
||||
const config = useBrandingConfig();
|
||||
|
||||
const getSuspensionColor = (type: string) => {
|
||||
switch (type) {
|
||||
case "permanent":
|
||||
return "#dc3545"; // Red
|
||||
case "temporary":
|
||||
return "#ffc107"; // Yellow
|
||||
case "warning":
|
||||
return "#17a2b8"; // Blue
|
||||
default:
|
||||
return config.colors.primary;
|
||||
}
|
||||
};
|
||||
|
||||
const getSuspensionLabel = (type: string) => {
|
||||
switch (type) {
|
||||
case "permanent":
|
||||
return "Permanent Suspension";
|
||||
case "temporary":
|
||||
return "Temporary Suspension";
|
||||
case "warning":
|
||||
return "Account Warning";
|
||||
default:
|
||||
return "Account Action";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<EmailLayout title={`🚫 ${getSuspensionLabel(suspensionType)} - Action Required`}>
|
||||
<Section>
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: getSuspensionColor(suspensionType) + "15",
|
||||
padding: "20px",
|
||||
borderRadius: "8px",
|
||||
marginBottom: "20px",
|
||||
border: `2px solid ${getSuspensionColor(suspensionType)}`,
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "24px",
|
||||
color: getSuspensionColor(suspensionType),
|
||||
marginTop: 0,
|
||||
marginBottom: "10px",
|
||||
}}
|
||||
>
|
||||
{getSuspensionLabel(suspensionType)}
|
||||
</Heading>
|
||||
<Text style={{ fontSize: "16px", fontWeight: "bold", margin: "5px 0" }}>
|
||||
User: {username}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", color: "#666666", margin: "5px 0" }}>
|
||||
Account ID: {userId}
|
||||
</Text>
|
||||
</Section>
|
||||
|
||||
<Hr style={{ borderColor: config.colors.primary, margin: "30px 0" }} />
|
||||
|
||||
{/* User Information */}
|
||||
<Section style={{ marginBottom: "25px" }}>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "20px",
|
||||
color: config.colors.text,
|
||||
marginTop: "20px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
👤 Account Information
|
||||
</Heading>
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.background,
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Username:</strong> {username}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Email:</strong> {email}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Account ID:</strong> {userId}
|
||||
</Text>
|
||||
</Section>
|
||||
</Section>
|
||||
|
||||
{/* Suspension Details */}
|
||||
<Section style={{ marginBottom: "25px" }}>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "20px",
|
||||
color: config.colors.text,
|
||||
marginTop: "20px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
⚠️ Suspension Details
|
||||
</Heading>
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: "#fff3cd",
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
border: "1px solid #ffc107",
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: "14px", fontWeight: "bold", margin: "0 0 10px 0" }}>
|
||||
Reason:
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "0 0 20px 0" }}>{suspensionReason}</Text>
|
||||
|
||||
{suspensionType === "temporary" && (
|
||||
<>
|
||||
<Text style={{ fontSize: "14px", fontWeight: "bold", margin: "0 0 10px 0" }}>
|
||||
Suspension Period:
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "5px 0" }}>
|
||||
<strong>Start Date:</strong> {formatDate(suspensionStartDate)}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "5px 0" }}>
|
||||
<strong>End Date:</strong> {formatDate(suspensionEndDate)}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "5px 0 0 0" }}>
|
||||
<strong>Duration:</strong> {suspensionDuration} days
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
|
||||
{suspensionType === "permanent" && (
|
||||
<Text style={{ fontSize: "14px", margin: "10px 0 0 0", fontWeight: "bold" }}>
|
||||
This suspension is permanent. Your account access has been revoked indefinitely.
|
||||
</Text>
|
||||
)}
|
||||
</Section>
|
||||
</Section>
|
||||
|
||||
{/* Violation Details */}
|
||||
{violationDetails && violationDetails.length > 0 && (
|
||||
<Section style={{ marginBottom: "25px" }}>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "20px",
|
||||
color: config.colors.text,
|
||||
marginTop: "20px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
📋 Violation Details
|
||||
</Heading>
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.background,
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
>
|
||||
<ul style={{ margin: 0, paddingLeft: "20px" }}>
|
||||
{violationDetails.map((detail, index) => (
|
||||
<li key={index} style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
{detail}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Section>
|
||||
</Section>
|
||||
)}
|
||||
|
||||
{/* Appeal Information */}
|
||||
{appealInformation && (
|
||||
<Section style={{ marginBottom: "25px" }}>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "20px",
|
||||
color: config.colors.text,
|
||||
marginTop: "20px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
📞 Appeal Process
|
||||
</Heading>
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.primary + "10",
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
border: `1px solid ${config.colors.primary}`,
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: "14px", margin: 0 }}>{appealInformation}</Text>
|
||||
</Section>
|
||||
</Section>
|
||||
)}
|
||||
|
||||
<Hr style={{ borderColor: config.colors.primary, margin: "30px 0" }} />
|
||||
|
||||
<Text style={{ fontSize: "12px", color: "#666666", fontStyle: "italic" }}>
|
||||
This is an automated notification from the {config.companyName} security system.
|
||||
<br />
|
||||
Processed by: {administrator}
|
||||
<br />
|
||||
Timestamp: {new Date().toLocaleString()}
|
||||
</Text>
|
||||
</Section>
|
||||
</EmailLayout>
|
||||
);
|
||||
};
|
||||
139
src/emails/customer/AccountVerificationEmail.tsx
Normal file
139
src/emails/customer/AccountVerificationEmail.tsx
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
import { Section, Text, Heading, Hr } from "@react-email/components";
|
||||
import { EmailLayout } from "../../components/EmailLayout";
|
||||
import { Button } from "../../components/Button";
|
||||
import { useBrandingConfig } from "../../hooks/useBrandingConfig";
|
||||
|
||||
interface AccountVerificationEmailProps {
|
||||
playerName?: string;
|
||||
verificationLink?: string;
|
||||
verificationCode?: string;
|
||||
expirationTime?: number; // in hours
|
||||
supportEmail?: string;
|
||||
}
|
||||
|
||||
export const AccountVerificationEmail = ({
|
||||
playerName = "John",
|
||||
verificationLink = "https://example.com/verify?token=abc123",
|
||||
verificationCode,
|
||||
expirationTime = 24,
|
||||
supportEmail,
|
||||
}: AccountVerificationEmailProps) => {
|
||||
const config = useBrandingConfig();
|
||||
|
||||
return (
|
||||
<EmailLayout title="✅ Verify Your Account">
|
||||
<Section>
|
||||
<Text style={{ fontSize: "18px", lineHeight: "26px", marginBottom: "20px" }}>
|
||||
Hi {playerName},
|
||||
</Text>
|
||||
|
||||
<Text style={{ fontSize: "16px", lineHeight: "24px", marginBottom: "20px" }}>
|
||||
Thank you for signing up with {config.companyName}! To complete your registration and
|
||||
start playing, please verify your email address.
|
||||
</Text>
|
||||
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.primary + "10",
|
||||
padding: "20px",
|
||||
borderRadius: "8px",
|
||||
margin: "30px 0",
|
||||
border: `2px solid ${config.colors.primary}`,
|
||||
textAlign: "center" as const,
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
color: config.colors.text,
|
||||
margin: "0 0 20px 0",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
Click the button below to verify your email address:
|
||||
</Text>
|
||||
<Button href={verificationLink}>Verify Email Address</Button>
|
||||
</Section>
|
||||
|
||||
{verificationCode && (
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.background,
|
||||
padding: "20px",
|
||||
borderRadius: "8px",
|
||||
margin: "30px 0",
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
color: config.colors.text,
|
||||
margin: "0 0 10px 0",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
Or enter this verification code:
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "28px",
|
||||
fontWeight: "bold",
|
||||
color: config.colors.primary,
|
||||
letterSpacing: "4px",
|
||||
textAlign: "center" as const,
|
||||
fontFamily: "monospace",
|
||||
margin: 0,
|
||||
}}
|
||||
>
|
||||
{verificationCode}
|
||||
</Text>
|
||||
</Section>
|
||||
)}
|
||||
|
||||
<Hr style={{ borderColor: config.colors.primary, margin: "30px 0" }} />
|
||||
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: "#fff3cd",
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
border: "1px solid #ffc107",
|
||||
marginBottom: "20px",
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: "13px", color: "#856404", margin: "0 0 5px 0", fontWeight: "bold" }}>
|
||||
⏰ Important:
|
||||
</Text>
|
||||
<Text style={{ fontSize: "13px", color: "#856404", margin: 0 }}>
|
||||
This verification link will expire in {expirationTime} hours. Please verify your
|
||||
account as soon as possible.
|
||||
</Text>
|
||||
</Section>
|
||||
|
||||
<Text style={{ fontSize: "14px", lineHeight: "22px", marginBottom: "10px" }}>
|
||||
If the button doesn't work, copy and paste this link into your browser:
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: config.colors.primary,
|
||||
wordBreak: "break-all",
|
||||
marginBottom: "20px",
|
||||
}}
|
||||
>
|
||||
{verificationLink}
|
||||
</Text>
|
||||
|
||||
<Text style={{ fontSize: "14px", lineHeight: "22px", marginTop: "20px" }}>
|
||||
If you didn't create an account with {config.companyName}, please ignore this email.
|
||||
</Text>
|
||||
|
||||
{supportEmail && (
|
||||
<Text style={{ fontSize: "12px", color: "#666666", marginTop: "30px" }}>
|
||||
Need help? Contact us at {supportEmail}
|
||||
</Text>
|
||||
)}
|
||||
</Section>
|
||||
</EmailLayout>
|
||||
);
|
||||
};
|
||||
274
src/emails/customer/BetConfirmationEmail.tsx
Normal file
274
src/emails/customer/BetConfirmationEmail.tsx
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
import { Section, Text, Heading, Hr, Row, Column } from "@react-email/components";
|
||||
import { EmailLayout } from "../../components/EmailLayout";
|
||||
import { useBrandingConfig } from "../../hooks/useBrandingConfig";
|
||||
import { formatCurrency, formatDate } from "../../utils/emailHelpers";
|
||||
|
||||
interface BetConfirmationEmailProps {
|
||||
playerName?: string;
|
||||
ticketNumber?: string;
|
||||
betType?: string;
|
||||
selections?: Array<{
|
||||
event: string;
|
||||
market: string;
|
||||
selection: string;
|
||||
odds: number;
|
||||
}>;
|
||||
stake?: number;
|
||||
potentialPayout?: number;
|
||||
currency?: string;
|
||||
placedDate?: Date | string;
|
||||
status?: "pending" | "won" | "lost" | "void";
|
||||
accountLink?: string;
|
||||
}
|
||||
|
||||
export const BetConfirmationEmail = ({
|
||||
playerName = "John",
|
||||
ticketNumber = "TKT-2024-001234",
|
||||
betType = "Accumulator",
|
||||
selections = [
|
||||
{
|
||||
event: "Manchester United vs Liverpool",
|
||||
market: "Match Result",
|
||||
selection: "Manchester United Win",
|
||||
odds: 2.5,
|
||||
},
|
||||
{
|
||||
event: "Barcelona vs Real Madrid",
|
||||
market: "Total Goals",
|
||||
selection: "Over 2.5 Goals",
|
||||
odds: 1.8,
|
||||
},
|
||||
],
|
||||
stake = 50,
|
||||
potentialPayout = 225,
|
||||
currency = "USD",
|
||||
placedDate = new Date(),
|
||||
status = "pending",
|
||||
accountLink = "https://example.com/account",
|
||||
}: BetConfirmationEmailProps) => {
|
||||
const config = useBrandingConfig();
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status) {
|
||||
case "won":
|
||||
return config.colors.secondary;
|
||||
case "lost":
|
||||
return "#dc3545";
|
||||
case "void":
|
||||
return "#6c757d";
|
||||
default:
|
||||
return config.colors.primary;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusLabel = (status: string) => {
|
||||
switch (status) {
|
||||
case "won":
|
||||
return "Won";
|
||||
case "lost":
|
||||
return "Lost";
|
||||
case "void":
|
||||
return "Void";
|
||||
default:
|
||||
return "Pending";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<EmailLayout title="🎫 Bet Confirmation">
|
||||
<Section>
|
||||
<Text style={{ fontSize: "18px", lineHeight: "26px", marginBottom: "20px" }}>
|
||||
Hi {playerName},
|
||||
</Text>
|
||||
|
||||
<Text style={{ fontSize: "16px", lineHeight: "24px", marginBottom: "20px" }}>
|
||||
Your bet has been successfully placed! Good luck!
|
||||
</Text>
|
||||
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.primary + "15",
|
||||
padding: "20px",
|
||||
borderRadius: "8px",
|
||||
margin: "30px 0",
|
||||
border: `2px solid ${config.colors.primary}`,
|
||||
textAlign: "center" as const,
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "18px",
|
||||
color: config.colors.text,
|
||||
margin: "0 0 10px 0",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
Ticket Number
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "32px",
|
||||
fontWeight: "bold",
|
||||
color: config.colors.primary,
|
||||
letterSpacing: "2px",
|
||||
fontFamily: "monospace",
|
||||
margin: "5px 0",
|
||||
}}
|
||||
>
|
||||
{ticketNumber}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "16px",
|
||||
color: getStatusColor(status),
|
||||
fontWeight: "bold",
|
||||
margin: "10px 0 0 0",
|
||||
}}
|
||||
>
|
||||
Status: {getStatusLabel(status)}
|
||||
</Text>
|
||||
</Section>
|
||||
|
||||
<Hr style={{ borderColor: config.colors.primary, margin: "30px 0" }} />
|
||||
|
||||
{/* Bet Details */}
|
||||
<Section style={{ marginBottom: "25px" }}>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "20px",
|
||||
color: config.colors.text,
|
||||
marginTop: "20px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
📋 Bet Details
|
||||
</Heading>
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.background,
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
>
|
||||
<Row>
|
||||
<Column style={{ width: "50%" }}>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Bet Type:</strong>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Stake:</strong>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Potential Payout:</strong>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Placed Date:</strong>
|
||||
</Text>
|
||||
</Column>
|
||||
<Column>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>{betType}</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
color: config.colors.primary,
|
||||
margin: "8px 0",
|
||||
}}
|
||||
>
|
||||
{formatCurrency(stake, currency)}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
color: config.colors.secondary,
|
||||
margin: "8px 0",
|
||||
}}
|
||||
>
|
||||
{formatCurrency(potentialPayout, currency)}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
{formatDate(placedDate)}
|
||||
</Text>
|
||||
</Column>
|
||||
</Row>
|
||||
</Section>
|
||||
</Section>
|
||||
|
||||
{/* Selections */}
|
||||
<Section style={{ marginBottom: "25px" }}>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "20px",
|
||||
color: config.colors.text,
|
||||
marginTop: "20px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
🎯 Your Selections
|
||||
</Heading>
|
||||
{selections.map((selection, index) => (
|
||||
<Section
|
||||
key={index}
|
||||
style={{
|
||||
backgroundColor: config.colors.background,
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
marginBottom: "10px",
|
||||
borderLeft: `4px solid ${config.colors.primary}`,
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: "14px", fontWeight: "bold", margin: "0 0 5px 0" }}>
|
||||
Selection {index + 1}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "5px 0" }}>
|
||||
<strong>Event:</strong> {selection.event}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "5px 0" }}>
|
||||
<strong>Market:</strong> {selection.market}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "5px 0" }}>
|
||||
<strong>Selection:</strong> {selection.selection}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
color: config.colors.primary,
|
||||
margin: "5px 0 0 0",
|
||||
}}
|
||||
>
|
||||
Odds: {selection.odds.toFixed(2)}
|
||||
</Text>
|
||||
</Section>
|
||||
))}
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: "#d1ecf1",
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
border: "1px solid #bee5eb",
|
||||
marginBottom: "20px",
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: "13px", color: "#0c5460", margin: 0 }}>
|
||||
<strong>Note:</strong> This bet is now active. You'll receive an email notification
|
||||
once the bet is settled.
|
||||
</Text>
|
||||
</Section>
|
||||
|
||||
<Text style={{ fontSize: "14px", lineHeight: "22px", marginTop: "20px" }}>
|
||||
Good luck with your bet! We'll keep you updated on the outcome.
|
||||
</Text>
|
||||
|
||||
<Text style={{ fontSize: "14px", lineHeight: "22px", marginTop: "30px" }}>
|
||||
Best regards,
|
||||
<br />
|
||||
The {config.companyName} Team
|
||||
</Text>
|
||||
</Section>
|
||||
</EmailLayout>
|
||||
);
|
||||
};
|
||||
200
src/emails/customer/DepositConfirmationEmail.tsx
Normal file
200
src/emails/customer/DepositConfirmationEmail.tsx
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
import { Section, Text, Heading, Hr, Row, Column } from "@react-email/components";
|
||||
import { EmailLayout } from "../../components/EmailLayout";
|
||||
import { Button } from "../../components/Button";
|
||||
import { useBrandingConfig } from "../../hooks/useBrandingConfig";
|
||||
import { formatCurrency, formatDate } from "../../utils/emailHelpers";
|
||||
|
||||
interface DepositConfirmationEmailProps {
|
||||
playerName?: string;
|
||||
transactionId?: string;
|
||||
amount?: number;
|
||||
currency?: string;
|
||||
paymentMethod?: string;
|
||||
depositDate?: Date | string;
|
||||
newBalance?: number;
|
||||
bonusAmount?: number;
|
||||
bonusCode?: string;
|
||||
accountLink?: string;
|
||||
}
|
||||
|
||||
export const DepositConfirmationEmail = ({
|
||||
playerName = "John",
|
||||
transactionId = "TXN-2024-001234",
|
||||
amount = 100,
|
||||
currency = "USD",
|
||||
paymentMethod = "Credit Card",
|
||||
depositDate = new Date(),
|
||||
newBalance = 500,
|
||||
bonusAmount,
|
||||
bonusCode,
|
||||
accountLink = "https://example.com/account",
|
||||
}: DepositConfirmationEmailProps) => {
|
||||
const config = useBrandingConfig();
|
||||
|
||||
return (
|
||||
<EmailLayout title="✅ Deposit Confirmed">
|
||||
<Section>
|
||||
<Text style={{ fontSize: "18px", lineHeight: "26px", marginBottom: "20px" }}>
|
||||
Hi {playerName},
|
||||
</Text>
|
||||
|
||||
<Text style={{ fontSize: "16px", lineHeight: "24px", marginBottom: "20px" }}>
|
||||
Your deposit has been successfully processed and credited to your account!
|
||||
</Text>
|
||||
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.secondary + "15",
|
||||
padding: "25px",
|
||||
borderRadius: "8px",
|
||||
margin: "30px 0",
|
||||
border: `2px solid ${config.colors.secondary}`,
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "28px",
|
||||
color: config.colors.secondary,
|
||||
marginTop: 0,
|
||||
marginBottom: "15px",
|
||||
textAlign: "center" as const,
|
||||
}}
|
||||
>
|
||||
{formatCurrency(amount, currency)} Deposited
|
||||
</Heading>
|
||||
</Section>
|
||||
|
||||
<Hr style={{ borderColor: config.colors.primary, margin: "30px 0" }} />
|
||||
|
||||
{/* Transaction Details */}
|
||||
<Section style={{ marginBottom: "25px" }}>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "20px",
|
||||
color: config.colors.text,
|
||||
marginTop: "20px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
📋 Transaction Details
|
||||
</Heading>
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.background,
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
>
|
||||
<Row>
|
||||
<Column style={{ width: "50%" }}>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Transaction ID:</strong>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Amount:</strong>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Payment Method:</strong>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Date:</strong>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>New Balance:</strong>
|
||||
</Text>
|
||||
</Column>
|
||||
<Column>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0", fontFamily: "monospace" }}>
|
||||
{transactionId}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
color: config.colors.secondary,
|
||||
margin: "8px 0",
|
||||
}}
|
||||
>
|
||||
{formatCurrency(amount, currency)}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>{paymentMethod}</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
{formatDate(depositDate)}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
color: config.colors.primary,
|
||||
margin: "8px 0",
|
||||
}}
|
||||
>
|
||||
{formatCurrency(newBalance, currency)}
|
||||
</Text>
|
||||
</Column>
|
||||
</Row>
|
||||
</Section>
|
||||
</Section>
|
||||
|
||||
{/* Bonus Information */}
|
||||
{bonusAmount && bonusAmount > 0 && (
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.primary + "10",
|
||||
padding: "20px",
|
||||
borderRadius: "8px",
|
||||
margin: "30px 0",
|
||||
border: `2px solid ${config.colors.primary}`,
|
||||
textAlign: "center" as const,
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "18px",
|
||||
color: config.colors.primary,
|
||||
fontWeight: "bold",
|
||||
margin: "0 0 10px 0",
|
||||
}}
|
||||
>
|
||||
🎁 Bonus Added!
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "24px",
|
||||
color: config.colors.primary,
|
||||
fontWeight: "bold",
|
||||
margin: "5px 0",
|
||||
}}
|
||||
>
|
||||
+{formatCurrency(bonusAmount, currency)}
|
||||
</Text>
|
||||
{bonusCode && (
|
||||
<Text style={{ fontSize: "14px", color: "#666666", margin: "10px 0 0 0" }}>
|
||||
Applied bonus code: {bonusCode}
|
||||
</Text>
|
||||
)}
|
||||
</Section>
|
||||
)}
|
||||
|
||||
<Section style={{ textAlign: "center" as const, margin: "30px 0" }}>
|
||||
<Button href={accountLink}>View Account</Button>
|
||||
</Section>
|
||||
|
||||
<Text style={{ fontSize: "14px", lineHeight: "22px", marginTop: "20px" }}>
|
||||
Your funds are now available in your account. Start playing your favorite games and
|
||||
good luck!
|
||||
</Text>
|
||||
|
||||
<Text style={{ fontSize: "14px", lineHeight: "22px", marginTop: "30px" }}>
|
||||
Best regards,
|
||||
<br />
|
||||
The {config.companyName} Team
|
||||
</Text>
|
||||
|
||||
<Text style={{ fontSize: "12px", color: "#666666", marginTop: "20px", fontStyle: "italic" }}>
|
||||
If you did not make this deposit, please contact our support team immediately.
|
||||
</Text>
|
||||
</Section>
|
||||
</EmailLayout>
|
||||
);
|
||||
};
|
||||
151
src/emails/customer/PasswordResetEmail.tsx
Normal file
151
src/emails/customer/PasswordResetEmail.tsx
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
import { Section, Text, Heading, Hr } from "@react-email/components";
|
||||
import { EmailLayout } from "../../components/EmailLayout";
|
||||
import { Button } from "../../components/Button";
|
||||
import { useBrandingConfig } from "../../hooks/useBrandingConfig";
|
||||
|
||||
interface PasswordResetEmailProps {
|
||||
playerName?: string;
|
||||
resetLink?: string;
|
||||
resetCode?: string;
|
||||
expirationTime?: number; // in minutes
|
||||
ipAddress?: string;
|
||||
supportEmail?: string;
|
||||
}
|
||||
|
||||
export const PasswordResetEmail = ({
|
||||
playerName = "John",
|
||||
resetLink = "https://example.com/reset-password?token=abc123",
|
||||
resetCode,
|
||||
expirationTime = 30,
|
||||
ipAddress,
|
||||
supportEmail,
|
||||
}: PasswordResetEmailProps) => {
|
||||
const config = useBrandingConfig();
|
||||
|
||||
return (
|
||||
<EmailLayout title="🔐 Password Reset Request">
|
||||
<Section>
|
||||
<Text style={{ fontSize: "18px", lineHeight: "26px", marginBottom: "20px" }}>
|
||||
Hi {playerName},
|
||||
</Text>
|
||||
|
||||
<Text style={{ fontSize: "16px", lineHeight: "24px", marginBottom: "20px" }}>
|
||||
We received a request to reset your password for your {config.companyName} account. If
|
||||
you made this request, click the button below to reset your password.
|
||||
</Text>
|
||||
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.primary + "10",
|
||||
padding: "20px",
|
||||
borderRadius: "8px",
|
||||
margin: "30px 0",
|
||||
border: `2px solid ${config.colors.primary}`,
|
||||
textAlign: "center" as const,
|
||||
}}
|
||||
>
|
||||
<Button href={resetLink}>Reset Password</Button>
|
||||
</Section>
|
||||
|
||||
{resetCode && (
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.background,
|
||||
padding: "20px",
|
||||
borderRadius: "8px",
|
||||
margin: "30px 0",
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
color: config.colors.text,
|
||||
margin: "0 0 10px 0",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
Or enter this reset code:
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "28px",
|
||||
fontWeight: "bold",
|
||||
color: config.colors.primary,
|
||||
letterSpacing: "4px",
|
||||
textAlign: "center" as const,
|
||||
fontFamily: "monospace",
|
||||
margin: 0,
|
||||
}}
|
||||
>
|
||||
{resetCode}
|
||||
</Text>
|
||||
</Section>
|
||||
)}
|
||||
|
||||
<Hr style={{ borderColor: config.colors.primary, margin: "30px 0" }} />
|
||||
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: "#fff3cd",
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
border: "1px solid #ffc107",
|
||||
marginBottom: "20px",
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: "13px", color: "#856404", margin: "0 0 5px 0", fontWeight: "bold" }}>
|
||||
⏰ Important:
|
||||
</Text>
|
||||
<Text style={{ fontSize: "13px", color: "#856404", margin: 0 }}>
|
||||
This password reset link will expire in {expirationTime} minutes for security
|
||||
purposes.
|
||||
</Text>
|
||||
</Section>
|
||||
|
||||
<Text style={{ fontSize: "14px", lineHeight: "22px", marginBottom: "10px" }}>
|
||||
If the button doesn't work, copy and paste this link into your browser:
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "12px",
|
||||
color: config.colors.primary,
|
||||
wordBreak: "break-all",
|
||||
marginBottom: "20px",
|
||||
}}
|
||||
>
|
||||
{resetLink}
|
||||
</Text>
|
||||
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: "#d1ecf1",
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
border: "1px solid #bee5eb",
|
||||
marginBottom: "20px",
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: "13px", color: "#0c5460", margin: "0 0 5px 0", fontWeight: "bold" }}>
|
||||
🔒 Security Notice:
|
||||
</Text>
|
||||
<Text style={{ fontSize: "13px", color: "#0c5460", margin: 0 }}>
|
||||
If you didn't request a password reset, please ignore this email. Your password will
|
||||
remain unchanged.
|
||||
</Text>
|
||||
{ipAddress && (
|
||||
<Text style={{ fontSize: "12px", color: "#0c5460", margin: "5px 0 0 0" }}>
|
||||
Request originated from: {ipAddress}
|
||||
</Text>
|
||||
)}
|
||||
</Section>
|
||||
|
||||
{supportEmail && (
|
||||
<Text style={{ fontSize: "12px", color: "#666666", marginTop: "30px" }}>
|
||||
If you have concerns about your account security, please contact us immediately at{" "}
|
||||
{supportEmail}
|
||||
</Text>
|
||||
)}
|
||||
</Section>
|
||||
</EmailLayout>
|
||||
);
|
||||
};
|
||||
150
src/emails/customer/WelcomeEmail.tsx
Normal file
150
src/emails/customer/WelcomeEmail.tsx
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
import { Section, Text, Heading, Hr } from "@react-email/components";
|
||||
import { EmailLayout } from "../../components/EmailLayout";
|
||||
import { Button } from "../../components/Button";
|
||||
import { useBrandingConfig } from "../../hooks/useBrandingConfig";
|
||||
|
||||
interface WelcomeEmailProps {
|
||||
playerName?: string;
|
||||
username?: string;
|
||||
welcomeBonus?: number;
|
||||
bonusCode?: string;
|
||||
depositLink?: string;
|
||||
supportEmail?: string;
|
||||
}
|
||||
|
||||
export const WelcomeEmail = ({
|
||||
playerName = "John",
|
||||
username = "player123",
|
||||
welcomeBonus = 100,
|
||||
bonusCode = "WELCOME100",
|
||||
depositLink = "https://example.com/deposit",
|
||||
supportEmail,
|
||||
}: WelcomeEmailProps) => {
|
||||
const config = useBrandingConfig();
|
||||
|
||||
return (
|
||||
<EmailLayout title="🎉 Welcome to the Team!">
|
||||
<Section>
|
||||
<Text style={{ fontSize: "18px", lineHeight: "26px", marginBottom: "20px" }}>
|
||||
Hi {playerName},
|
||||
</Text>
|
||||
|
||||
<Text style={{ fontSize: "16px", lineHeight: "24px", marginBottom: "20px" }}>
|
||||
Welcome to {config.companyName}! We're thrilled to have you join our gaming community.
|
||||
Get ready for an exciting experience with top-notch games, amazing bonuses, and
|
||||
fantastic rewards.
|
||||
</Text>
|
||||
|
||||
{/* Welcome Bonus Section */}
|
||||
{welcomeBonus > 0 && (
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.secondary + "20",
|
||||
padding: "25px",
|
||||
borderRadius: "8px",
|
||||
margin: "30px 0",
|
||||
border: `2px solid ${config.colors.secondary}`,
|
||||
textAlign: "center" as const,
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "32px",
|
||||
color: config.colors.secondary,
|
||||
marginTop: 0,
|
||||
marginBottom: "10px",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
${welcomeBonus} Welcome Bonus!
|
||||
</Heading>
|
||||
{bonusCode && (
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "18px",
|
||||
color: config.colors.text,
|
||||
margin: "10px 0",
|
||||
fontWeight: "bold",
|
||||
}}
|
||||
>
|
||||
Use code: {bonusCode}
|
||||
</Text>
|
||||
)}
|
||||
<Text style={{ fontSize: "14px", color: "#666666", margin: "10px 0 0 0" }}>
|
||||
Claim your welcome bonus on your first deposit!
|
||||
</Text>
|
||||
</Section>
|
||||
)}
|
||||
|
||||
<Hr style={{ borderColor: config.colors.primary, margin: "30px 0" }} />
|
||||
|
||||
{/* Getting Started */}
|
||||
<Section style={{ marginBottom: "25px" }}>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "20px",
|
||||
color: config.colors.text,
|
||||
marginTop: "20px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
🚀 Getting Started
|
||||
</Heading>
|
||||
<Text style={{ fontSize: "14px", lineHeight: "22px", margin: "8px 0" }}>
|
||||
1. <strong>Verify your account</strong> - Check your email for verification link
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", lineHeight: "22px", margin: "8px 0" }}>
|
||||
2. <strong>Make your first deposit</strong> - Choose from our secure payment methods
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", lineHeight: "22px", margin: "8px 0" }}>
|
||||
3. <strong>Claim your bonus</strong> - Use your welcome bonus code when depositing
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", lineHeight: "22px", margin: "8px 0" }}>
|
||||
4. <strong>Start playing</strong> - Explore our games and enjoy!
|
||||
</Text>
|
||||
</Section>
|
||||
|
||||
{/* Account Info */}
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.background,
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
marginBottom: "25px",
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: "14px", margin: "5px 0" }}>
|
||||
<strong>Username:</strong> {username}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "5px 0" }}>
|
||||
<strong>Account Status:</strong> Active
|
||||
</Text>
|
||||
</Section>
|
||||
|
||||
<Section style={{ textAlign: "center" as const, margin: "30px 0" }}>
|
||||
<Button href={depositLink}>Make Your First Deposit</Button>
|
||||
</Section>
|
||||
|
||||
<Text style={{ fontSize: "14px", lineHeight: "22px", marginTop: "30px" }}>
|
||||
If you have any questions, our support team is available 24/7 to assist you.
|
||||
</Text>
|
||||
|
||||
<Text style={{ fontSize: "14px", lineHeight: "22px" }}>
|
||||
Best of luck and enjoy your gaming experience!
|
||||
</Text>
|
||||
|
||||
<Text style={{ fontSize: "14px", lineHeight: "22px", marginTop: "30px" }}>
|
||||
Best regards,
|
||||
<br />
|
||||
The {config.companyName} Team
|
||||
</Text>
|
||||
|
||||
{supportEmail && (
|
||||
<Text style={{ fontSize: "12px", color: "#666666", marginTop: "20px" }}>
|
||||
Need help? Contact us at {supportEmail}
|
||||
</Text>
|
||||
)}
|
||||
</Section>
|
||||
</EmailLayout>
|
||||
);
|
||||
};
|
||||
213
src/emails/customer/WithdrawalConfirmationEmail.tsx
Normal file
213
src/emails/customer/WithdrawalConfirmationEmail.tsx
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
import { Section, Text, Heading, Hr, Row, Column } from "@react-email/components";
|
||||
import { EmailLayout } from "../../components/EmailLayout";
|
||||
import { useBrandingConfig } from "../../hooks/useBrandingConfig";
|
||||
import { formatCurrency, formatDate } from "../../utils/emailHelpers";
|
||||
|
||||
interface WithdrawalConfirmationEmailProps {
|
||||
playerName?: string;
|
||||
transactionId?: string;
|
||||
amount?: number;
|
||||
currency?: string;
|
||||
withdrawalMethod?: string;
|
||||
requestDate?: Date | string;
|
||||
estimatedProcessingTime?: string;
|
||||
processingFee?: number;
|
||||
netAmount?: number;
|
||||
newBalance?: number;
|
||||
accountLink?: string;
|
||||
}
|
||||
|
||||
export const WithdrawalConfirmationEmail = ({
|
||||
playerName = "John",
|
||||
transactionId = "WD-2024-001234",
|
||||
amount = 500,
|
||||
currency = "USD",
|
||||
withdrawalMethod = "Bank Transfer",
|
||||
requestDate = new Date(),
|
||||
estimatedProcessingTime = "3-5 business days",
|
||||
processingFee = 5,
|
||||
netAmount,
|
||||
newBalance = 200,
|
||||
accountLink = "https://example.com/account",
|
||||
}: WithdrawalConfirmationEmailProps) => {
|
||||
const config = useBrandingConfig();
|
||||
const netWithdrawalAmount = netAmount || amount - processingFee;
|
||||
|
||||
return (
|
||||
<EmailLayout title="✅ Withdrawal Request Confirmed">
|
||||
<Section>
|
||||
<Text style={{ fontSize: "18px", lineHeight: "26px", marginBottom: "20px" }}>
|
||||
Hi {playerName},
|
||||
</Text>
|
||||
|
||||
<Text style={{ fontSize: "16px", lineHeight: "24px", marginBottom: "20px" }}>
|
||||
Your withdrawal request has been received and is being processed. We'll notify you once
|
||||
the funds have been transferred to your account.
|
||||
</Text>
|
||||
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.primary + "15",
|
||||
padding: "25px",
|
||||
borderRadius: "8px",
|
||||
margin: "30px 0",
|
||||
border: `2px solid ${config.colors.primary}`,
|
||||
}}
|
||||
>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "28px",
|
||||
color: config.colors.primary,
|
||||
marginTop: 0,
|
||||
marginBottom: "15px",
|
||||
textAlign: "center" as const,
|
||||
}}
|
||||
>
|
||||
{formatCurrency(amount, currency)} Withdrawal Requested
|
||||
</Heading>
|
||||
{processingFee > 0 && (
|
||||
<Text style={{ fontSize: "14px", color: "#666666", margin: "5px 0", textAlign: "center" as const }}>
|
||||
Processing Fee: {formatCurrency(processingFee, currency)}
|
||||
</Text>
|
||||
)}
|
||||
</Section>
|
||||
|
||||
<Hr style={{ borderColor: config.colors.primary, margin: "30px 0" }} />
|
||||
|
||||
{/* Transaction Details */}
|
||||
<Section style={{ marginBottom: "25px" }}>
|
||||
<Heading
|
||||
style={{
|
||||
fontSize: "20px",
|
||||
color: config.colors.text,
|
||||
marginTop: "20px",
|
||||
marginBottom: "15px",
|
||||
}}
|
||||
>
|
||||
📋 Withdrawal Details
|
||||
</Heading>
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: config.colors.background,
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
}}
|
||||
>
|
||||
<Row>
|
||||
<Column style={{ width: "50%" }}>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Transaction ID:</strong>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Withdrawal Amount:</strong>
|
||||
</Text>
|
||||
{processingFee > 0 && (
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Processing Fee:</strong>
|
||||
</Text>
|
||||
)}
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Net Amount:</strong>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Withdrawal Method:</strong>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Request Date:</strong>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Estimated Processing:</strong>
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
<strong>Remaining Balance:</strong>
|
||||
</Text>
|
||||
</Column>
|
||||
<Column>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0", fontFamily: "monospace" }}>
|
||||
{transactionId}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
color: config.colors.primary,
|
||||
margin: "8px 0",
|
||||
}}
|
||||
>
|
||||
{formatCurrency(amount, currency)}
|
||||
</Text>
|
||||
{processingFee > 0 && (
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
{formatCurrency(processingFee, currency)}
|
||||
</Text>
|
||||
)}
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
color: config.colors.secondary,
|
||||
margin: "8px 0",
|
||||
}}
|
||||
>
|
||||
{formatCurrency(netWithdrawalAmount, currency)}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>{withdrawalMethod}</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
{formatDate(requestDate)}
|
||||
</Text>
|
||||
<Text style={{ fontSize: "14px", margin: "8px 0" }}>
|
||||
{estimatedProcessingTime}
|
||||
</Text>
|
||||
<Text
|
||||
style={{
|
||||
fontSize: "14px",
|
||||
fontWeight: "bold",
|
||||
color: config.colors.text,
|
||||
margin: "8px 0",
|
||||
}}
|
||||
>
|
||||
{formatCurrency(newBalance, currency)}
|
||||
</Text>
|
||||
</Column>
|
||||
</Row>
|
||||
</Section>
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
style={{
|
||||
backgroundColor: "#d1ecf1",
|
||||
padding: "15px",
|
||||
borderRadius: "4px",
|
||||
border: "1px solid #bee5eb",
|
||||
marginBottom: "20px",
|
||||
}}
|
||||
>
|
||||
<Text style={{ fontSize: "13px", color: "#0c5460", margin: "0 0 5px 0", fontWeight: "bold" }}>
|
||||
ℹ️ Processing Information:
|
||||
</Text>
|
||||
<Text style={{ fontSize: "13px", color: "#0c5460", margin: 0 }}>
|
||||
Your withdrawal is being processed and will be completed within{" "}
|
||||
{estimatedProcessingTime}. You'll receive a notification email once the funds have
|
||||
been transferred.
|
||||
</Text>
|
||||
</Section>
|
||||
|
||||
<Text style={{ fontSize: "14px", lineHeight: "22px", marginTop: "20px" }}>
|
||||
If you have any questions about your withdrawal, please don't hesitate to contact our
|
||||
support team.
|
||||
</Text>
|
||||
|
||||
<Text style={{ fontSize: "14px", lineHeight: "22px", marginTop: "30px" }}>
|
||||
Best regards,
|
||||
<br />
|
||||
The {config.companyName} Team
|
||||
</Text>
|
||||
|
||||
<Text style={{ fontSize: "12px", color: "#666666", marginTop: "20px", fontStyle: "italic" }}>
|
||||
If you did not request this withdrawal, please contact our support team immediately to
|
||||
secure your account.
|
||||
</Text>
|
||||
</Section>
|
||||
</EmailLayout>
|
||||
);
|
||||
};
|
||||
|
|
@ -61,18 +61,57 @@ export const CustomizationDecorator = ({ children }: CustomizationDecoratorProps
|
|||
}
|
||||
};
|
||||
|
||||
const handleCustomEvent = (e: CustomEvent) => {
|
||||
if (e.detail?.config) {
|
||||
setConfig((prev) => ({
|
||||
...prev,
|
||||
...e.detail.config,
|
||||
colors: {
|
||||
...prev.colors,
|
||||
...(e.detail.config.colors || {}),
|
||||
},
|
||||
font: {
|
||||
...prev.font,
|
||||
...(e.detail.config.font || {}),
|
||||
},
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
if (event.data?.type === "CUSTOMIZATION_UPDATE") {
|
||||
updateConfig(event.data.config);
|
||||
}
|
||||
};
|
||||
|
||||
// Poll localStorage for changes (for same-tab updates)
|
||||
const pollInterval = setInterval(() => {
|
||||
const saved = localStorage.getItem(CUSTOMIZATION_STORAGE_KEY);
|
||||
if (saved) {
|
||||
try {
|
||||
const savedConfig = JSON.parse(saved);
|
||||
setConfig((prev) => {
|
||||
const prevStr = JSON.stringify(prev);
|
||||
if (prevStr !== saved) {
|
||||
return { ...defaultBrandingConfig, ...savedConfig };
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
} catch (e) {
|
||||
// Ignore parse errors
|
||||
}
|
||||
}
|
||||
}, 500);
|
||||
|
||||
window.addEventListener("storage", handleStorageChange);
|
||||
window.addEventListener("message", handleMessage);
|
||||
window.addEventListener("customization-update" as any, handleCustomEvent as EventListener);
|
||||
|
||||
return () => {
|
||||
clearInterval(pollInterval);
|
||||
window.removeEventListener("storage", handleStorageChange);
|
||||
window.removeEventListener("message", handleMessage);
|
||||
window.removeEventListener("customization-update" as any, handleCustomEvent as EventListener);
|
||||
};
|
||||
}, [updateConfig]);
|
||||
|
||||
|
|
|
|||
36
stories/CustomizationToolbar.stories.tsx
Normal file
36
stories/CustomizationToolbar.stories.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react";
|
||||
import { CustomizationPanel } from "../.storybook/CustomizationPanel";
|
||||
import React, { useState } from "react";
|
||||
|
||||
const meta: Meta<typeof CustomizationPanel> = {
|
||||
title: "Customization/Customization Panel",
|
||||
component: CustomizationPanel,
|
||||
parameters: {
|
||||
layout: "padded",
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof CustomizationPanel>;
|
||||
|
||||
export const Panel: Story = {
|
||||
render: () => {
|
||||
const [active, setActive] = useState(true);
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
onClick={() => setActive(!active)}
|
||||
style={{
|
||||
marginBottom: "20px",
|
||||
padding: "10px 20px",
|
||||
fontSize: "14px",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
{active ? "Hide" : "Show"} Customization Panel
|
||||
</button>
|
||||
<CustomizationPanel active={active} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user