Build and Deploy / build (push) Has been cancelled
- Add Resend email integration with @react-email/render - Create email templates: OrderConfirmation, OrderShipped, OrderCancelled, OrderPaid - Implement webhook handler for ORDER_CREATED and other events - Add multi-language support for customer emails - Admin emails in English with order details - Update checkout page with auto-scroll on order completion - Configure DASHBOARD_URL environment variable
395 lines
11 KiB
TypeScript
395 lines
11 KiB
TypeScript
import { Button, Hr, Section, Text } from "@react-email/components";
|
|
import { BaseLayout } from "./BaseLayout";
|
|
|
|
interface OrderItem {
|
|
id: string;
|
|
name: string;
|
|
quantity: number;
|
|
price: string;
|
|
}
|
|
|
|
interface OrderConfirmationProps {
|
|
language: string;
|
|
orderId: string;
|
|
orderNumber: string;
|
|
customerEmail: string;
|
|
customerName: string;
|
|
items: OrderItem[];
|
|
total: string;
|
|
shippingAddress?: string;
|
|
billingAddress?: string;
|
|
phone?: string;
|
|
siteUrl: string;
|
|
dashboardUrl?: string;
|
|
isAdmin?: boolean;
|
|
}
|
|
|
|
const translations: Record<
|
|
string,
|
|
{
|
|
title: string;
|
|
preview: string;
|
|
greeting: string;
|
|
orderReceived: string;
|
|
orderNumber: string;
|
|
items: string;
|
|
quantity: string;
|
|
total: string;
|
|
shippingTo: string;
|
|
questions: string;
|
|
thankYou: string;
|
|
adminTitle: string;
|
|
adminPreview: string;
|
|
adminGreeting: string;
|
|
adminMessage: string;
|
|
customerLabel: string;
|
|
customerEmailLabel: string;
|
|
billingAddressLabel: string;
|
|
phoneLabel: string;
|
|
viewDashboard: string;
|
|
}
|
|
> = {
|
|
sr: {
|
|
title: "Potvrda narudžbine",
|
|
preview: "Vaša narudžbina je potvrđena",
|
|
greeting: "Poštovani {name},",
|
|
orderReceived: "Zahvaljujemo se na Vašoj narudžbini! Primili smo je i sada je u pripremi.",
|
|
orderNumber: "Broj narudžbine",
|
|
items: "Artikli",
|
|
quantity: "Količina",
|
|
total: "Ukupno",
|
|
shippingTo: "Adresa za dostavu",
|
|
questions: "Imate pitanja? Pišite nam na [email protected]",
|
|
thankYou: "Hvala Vam što kupujete kod nas!",
|
|
adminTitle: "Nova narudžbina!",
|
|
adminPreview: "Nova narudžbina je primljena",
|
|
adminGreeting: "Čestitamo na prodaji!",
|
|
adminMessage: "Nova narudžbina je upravo primljena. Detalji su ispod:",
|
|
customerLabel: "Kupac",
|
|
customerEmailLabel: "Email kupca",
|
|
billingAddressLabel: "Adresa za naplatu",
|
|
phoneLabel: "Telefon",
|
|
viewDashboard: "Pogledaj u Dashboardu",
|
|
},
|
|
en: {
|
|
title: "Order Confirmation",
|
|
preview: "Your order has been confirmed",
|
|
greeting: "Dear {name},",
|
|
orderReceived:
|
|
"Thank you for your order! We have received it and it is now being processed.",
|
|
orderNumber: "Order number",
|
|
items: "Items",
|
|
quantity: "Quantity",
|
|
total: "Total",
|
|
shippingTo: "Shipping address",
|
|
questions: "Questions? Email us at [email protected]",
|
|
thankYou: "Thank you for shopping with us!",
|
|
adminTitle: "New Order! 🎉",
|
|
adminPreview: "A new order has been received",
|
|
adminGreeting: "Congratulations on the sale!",
|
|
adminMessage: "A new order has just been placed. Details below:",
|
|
customerLabel: "Customer",
|
|
customerEmailLabel: "Customer Email",
|
|
billingAddressLabel: "Billing Address",
|
|
phoneLabel: "Phone",
|
|
viewDashboard: "View in Dashboard",
|
|
},
|
|
de: {
|
|
title: "Bestellungsbestätigung",
|
|
preview: "Ihre Bestellung wurde bestätigt",
|
|
greeting: "Sehr geehrte/r {name},",
|
|
orderReceived:
|
|
"Vielen Dank für Ihre Bestellung! Wir haben sie erhalten und sie wird nun bearbeitet.",
|
|
orderNumber: "Bestellnummer",
|
|
items: "Artikel",
|
|
quantity: "Menge",
|
|
total: "Gesamt",
|
|
shippingTo: "Lieferadresse",
|
|
questions: "Fragen? Schreiben Sie uns an [email protected]",
|
|
thankYou: "Vielen Dank für Ihren Einkauf!",
|
|
adminTitle: "Neue Bestellung! 🎉",
|
|
adminPreview: "Eine neue Bestellung wurde erhalten",
|
|
adminGreeting: "Glückwunsch zum Verkauf!",
|
|
adminMessage: "Eine neue Bestellung wurde soeben aufgegeben. Details unten:",
|
|
customerLabel: "Kunde",
|
|
customerEmailLabel: "Kunden-E-Mail",
|
|
billingAddressLabel: "Rechnungsadresse",
|
|
phoneLabel: "Telefon",
|
|
viewDashboard: "Im Dashboard anzeigen",
|
|
},
|
|
fr: {
|
|
title: "Confirmation de commande",
|
|
preview: "Votre commande a été confirmée",
|
|
greeting: "Cher(e) {name},",
|
|
orderReceived:
|
|
"Merci pour votre commande! Nous l'avons reçue et elle est en cours de traitement.",
|
|
orderNumber: "Numéro de commande",
|
|
items: "Articles",
|
|
quantity: "Quantité",
|
|
total: "Total",
|
|
shippingTo: "Adresse de livraison",
|
|
questions: "Questions? Écrivez-nous à [email protected]",
|
|
thankYou: "Merci d'avoir Magasiné avec nous!",
|
|
adminTitle: "Nouvelle commande! 🎉",
|
|
adminPreview: "Une nouvelle commande a été reçue",
|
|
adminGreeting: "Félicitations pour la vente!",
|
|
adminMessage: "Une nouvelle commande vient d'être passée. Détails ci-dessous:",
|
|
customerLabel: "Client",
|
|
customerEmailLabel: "Email du client",
|
|
billingAddressLabel: "Adresse de facturation",
|
|
phoneLabel: "Téléphone",
|
|
viewDashboard: "Voir dans le Dashboard",
|
|
},
|
|
};
|
|
|
|
export function OrderConfirmation({
|
|
language = "en",
|
|
orderId,
|
|
orderNumber,
|
|
customerEmail,
|
|
customerName,
|
|
items,
|
|
total,
|
|
shippingAddress,
|
|
billingAddress,
|
|
phone,
|
|
siteUrl,
|
|
dashboardUrl,
|
|
isAdmin = false,
|
|
}: OrderConfirmationProps) {
|
|
const t = translations[language] || translations.en;
|
|
|
|
// For admin emails, always use English
|
|
const adminT = translations["en"];
|
|
|
|
if (isAdmin) {
|
|
return (
|
|
<BaseLayout previewText={adminT.adminPreview} language="en" siteUrl={siteUrl}>
|
|
<Text style={styles.title}>{adminT.adminTitle}</Text>
|
|
<Text style={styles.greeting}>{adminT.adminGreeting}</Text>
|
|
<Text style={styles.text}>{adminT.adminMessage}</Text>
|
|
|
|
<Section style={styles.orderInfo}>
|
|
<Text style={styles.orderNumber}>
|
|
<strong>{adminT.orderNumber}:</strong> {orderNumber}
|
|
</Text>
|
|
<Text style={styles.customerInfo}>
|
|
<strong>{adminT.customerLabel}:</strong> {customerName}
|
|
</Text>
|
|
<Text style={styles.customerInfo}>
|
|
<strong>{adminT.customerEmailLabel}:</strong> {customerEmail}
|
|
</Text>
|
|
{phone && (
|
|
<Text style={styles.customerInfo}>
|
|
<strong>{adminT.phoneLabel}:</strong> {phone}
|
|
</Text>
|
|
)}
|
|
</Section>
|
|
|
|
<Section style={styles.itemsSection}>
|
|
<Text style={styles.sectionTitle}>{adminT.items}</Text>
|
|
<Hr style={styles.hr} />
|
|
{items.map((item) => (
|
|
<Section key={item.id} style={styles.itemRow}>
|
|
<Text style={styles.itemName}>
|
|
{item.quantity}x {item.name}
|
|
</Text>
|
|
<Text style={styles.itemPrice}>{item.price}</Text>
|
|
</Section>
|
|
))}
|
|
<Hr style={styles.hr} />
|
|
<Section style={styles.totalRow}>
|
|
<Text style={styles.totalLabel}>{adminT.total}:</Text>
|
|
<Text style={styles.totalValue}>{total}</Text>
|
|
</Section>
|
|
</Section>
|
|
|
|
{shippingAddress && (
|
|
<Section style={styles.shippingSection}>
|
|
<Text style={styles.sectionTitle}>{adminT.shippingTo}</Text>
|
|
<Text style={styles.shippingAddress}>{shippingAddress}</Text>
|
|
</Section>
|
|
)}
|
|
|
|
{billingAddress && (
|
|
<Section style={styles.shippingSection}>
|
|
<Text style={styles.sectionTitle}>{adminT.billingAddressLabel}</Text>
|
|
<Text style={styles.shippingAddress}>{billingAddress}</Text>
|
|
</Section>
|
|
)}
|
|
|
|
<Section style={styles.buttonSection}>
|
|
<Button href={`${dashboardUrl}/orders/${orderId}`} style={styles.button}>
|
|
{adminT.viewDashboard}
|
|
</Button>
|
|
</Section>
|
|
</BaseLayout>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<BaseLayout previewText={t.preview} language={language} siteUrl={siteUrl}>
|
|
<Text style={styles.title}>{t.title}</Text>
|
|
<Text style={styles.greeting}>{t.greeting.replace("{name}", customerName)}</Text>
|
|
<Text style={styles.text}>{t.orderReceived}</Text>
|
|
|
|
<Section style={styles.orderInfo}>
|
|
<Text style={styles.orderNumber}>
|
|
<strong>{t.orderNumber}:</strong> {orderNumber}
|
|
</Text>
|
|
</Section>
|
|
|
|
<Section style={styles.itemsSection}>
|
|
<Text style={styles.sectionTitle}>{t.items}</Text>
|
|
<Hr style={styles.hr} />
|
|
{items.map((item) => (
|
|
<Section key={item.id} style={styles.itemRow}>
|
|
<Text style={styles.itemName}>
|
|
{item.quantity}x {item.name}
|
|
</Text>
|
|
<Text style={styles.itemPrice}>{item.price}</Text>
|
|
</Section>
|
|
))}
|
|
<Hr style={styles.hr} />
|
|
<Section style={styles.totalRow}>
|
|
<Text style={styles.totalLabel}>{t.total}:</Text>
|
|
<Text style={styles.totalValue}>{total}</Text>
|
|
</Section>
|
|
</Section>
|
|
|
|
{shippingAddress && (
|
|
<Section style={styles.shippingSection}>
|
|
<Text style={styles.sectionTitle}>{t.shippingTo}</Text>
|
|
<Text style={styles.shippingAddress}>{shippingAddress}</Text>
|
|
</Section>
|
|
)}
|
|
|
|
<Section style={styles.buttonSection}>
|
|
<Button href={siteUrl} style={styles.button}>
|
|
{language === "sr"
|
|
? "Pogledajte narudžbinu"
|
|
: language === "de"
|
|
? "Bestellung ansehen"
|
|
: language === "fr"
|
|
? "Voir la commande"
|
|
: "View Order"}
|
|
</Button>
|
|
</Section>
|
|
|
|
<Text style={styles.questions}>{t.questions}</Text>
|
|
<Text style={styles.thankYou}>{t.thankYou}</Text>
|
|
</BaseLayout>
|
|
);
|
|
}
|
|
|
|
const styles = {
|
|
title: {
|
|
fontSize: "24px",
|
|
fontWeight: "bold" as const,
|
|
color: "#1a1a1a",
|
|
marginBottom: "20px",
|
|
},
|
|
greeting: {
|
|
fontSize: "16px",
|
|
color: "#333333",
|
|
marginBottom: "10px",
|
|
},
|
|
text: {
|
|
fontSize: "14px",
|
|
color: "#666666",
|
|
marginBottom: "20px",
|
|
},
|
|
orderInfo: {
|
|
backgroundColor: "#f9f9f9",
|
|
padding: "15px",
|
|
borderRadius: "8px",
|
|
marginBottom: "20px",
|
|
},
|
|
orderNumber: {
|
|
fontSize: "14px",
|
|
color: "#333333",
|
|
margin: "0 0 8px 0",
|
|
},
|
|
customerInfo: {
|
|
fontSize: "14px",
|
|
color: "#333333",
|
|
margin: "0 0 4px 0",
|
|
},
|
|
itemsSection: {
|
|
marginBottom: "20px",
|
|
},
|
|
sectionTitle: {
|
|
fontSize: "16px",
|
|
fontWeight: "bold" as const,
|
|
color: "#1a1a1a",
|
|
marginBottom: "10px",
|
|
},
|
|
hr: {
|
|
borderColor: "#e0e0e0",
|
|
margin: "10px 0",
|
|
},
|
|
itemRow: {
|
|
display: "flex" as const,
|
|
justifyContent: "space-between" as const,
|
|
padding: "8px 0",
|
|
},
|
|
itemName: {
|
|
fontSize: "14px",
|
|
color: "#333333",
|
|
margin: "0",
|
|
},
|
|
itemPrice: {
|
|
fontSize: "14px",
|
|
color: "#333333",
|
|
margin: "0",
|
|
},
|
|
totalRow: {
|
|
display: "flex" as const,
|
|
justifyContent: "space-between" as const,
|
|
padding: "8px 0",
|
|
},
|
|
totalLabel: {
|
|
fontSize: "16px",
|
|
fontWeight: "bold" as const,
|
|
color: "#1a1a1a",
|
|
margin: "0",
|
|
},
|
|
totalValue: {
|
|
fontSize: "16px",
|
|
fontWeight: "bold" as const,
|
|
color: "#1a1a1a",
|
|
margin: "0",
|
|
},
|
|
shippingSection: {
|
|
marginBottom: "20px",
|
|
},
|
|
shippingAddress: {
|
|
fontSize: "14px",
|
|
color: "#666666",
|
|
margin: "0",
|
|
},
|
|
buttonSection: {
|
|
textAlign: "center" as const,
|
|
marginBottom: "20px",
|
|
},
|
|
button: {
|
|
backgroundColor: "#000000",
|
|
color: "#ffffff",
|
|
padding: "12px 30px",
|
|
borderRadius: "4px",
|
|
fontSize: "14px",
|
|
fontWeight: "bold" as const,
|
|
textDecoration: "none",
|
|
},
|
|
questions: {
|
|
fontSize: "14px",
|
|
color: "#666666",
|
|
marginBottom: "10px",
|
|
},
|
|
thankYou: {
|
|
fontSize: "14px",
|
|
fontWeight: "bold" as const,
|
|
color: "#1a1a1a",
|
|
},
|
|
};
|