additional-email-templates
This commit is contained in:
parent
e232c50e52
commit
9d889e6209
|
|
@ -13,14 +13,26 @@ export const CustomizationPanel: React.FC<CustomizationPanelProps> = ({ active }
|
||||||
const [showBackgroundPicker, setShowBackgroundPicker] = useState(false);
|
const [showBackgroundPicker, setShowBackgroundPicker] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Send initial config to preview
|
// Save to localStorage for persistence and cross-tab sync
|
||||||
window.postMessage(
|
if (typeof window !== "undefined") {
|
||||||
{
|
localStorage.setItem("email-branding-config", JSON.stringify(config));
|
||||||
type: "CUSTOMIZATION_UPDATE",
|
|
||||||
config: config,
|
// Send message for immediate updates
|
||||||
},
|
window.postMessage(
|
||||||
"*"
|
{
|
||||||
);
|
type: "CUSTOMIZATION_UPDATE",
|
||||||
|
config: config,
|
||||||
|
},
|
||||||
|
"*"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Dispatch custom event for same-tab updates
|
||||||
|
window.dispatchEvent(
|
||||||
|
new CustomEvent("customization-update", {
|
||||||
|
detail: { config },
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}, [config]);
|
}, [config]);
|
||||||
|
|
||||||
const handleColorChange = (colorType: "primary" | "secondary" | "background") => (
|
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 { CustomizationDecorator } from "../stories/CustomizationDecorator";
|
||||||
import React from "react";
|
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 = {
|
const preview: Preview = {
|
||||||
parameters: {
|
parameters: {
|
||||||
actions: { argTypesRegex: "^on[A-Z].*" },
|
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) => {
|
const handleMessage = (event: MessageEvent) => {
|
||||||
if (event.data?.type === "CUSTOMIZATION_UPDATE") {
|
if (event.data?.type === "CUSTOMIZATION_UPDATE") {
|
||||||
updateConfig(event.data.config);
|
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("storage", handleStorageChange);
|
||||||
window.addEventListener("message", handleMessage);
|
window.addEventListener("message", handleMessage);
|
||||||
|
window.addEventListener("customization-update" as any, handleCustomEvent as EventListener);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
clearInterval(pollInterval);
|
||||||
window.removeEventListener("storage", handleStorageChange);
|
window.removeEventListener("storage", handleStorageChange);
|
||||||
window.removeEventListener("message", handleMessage);
|
window.removeEventListener("message", handleMessage);
|
||||||
|
window.removeEventListener("customization-update" as any, handleCustomEvent as EventListener);
|
||||||
};
|
};
|
||||||
}, [updateConfig]);
|
}, [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