feat: Add Saleor webhook handler with Resend email integration
- Add Resend SDK for transactional emails - Create React Email templates for order events: - OrderConfirmation - OrderShipped - OrderCancelled - OrderPaid - Multi-language support (SR, EN, DE, FR) - Customer emails in their language - Admin emails in English to me@hytham.me and tamara@hytham.me - Webhook handler at /api/webhooks/saleor - Supports: ORDER_CONFIRMED, ORDER_FULLY_PAID, ORDER_CANCELLED, ORDER_FULFILLED - Add GraphQL mutation to create webhooks in Saleor - Add Resend API key to .env.local
This commit is contained in:
191
src/emails/OrderShipped.tsx
Normal file
191
src/emails/OrderShipped.tsx
Normal file
@@ -0,0 +1,191 @@
|
||||
import { Button, Hr, Section, Text } from "@react-email/components";
|
||||
import { BaseLayout } from "./BaseLayout";
|
||||
|
||||
interface OrderItem {
|
||||
id: string;
|
||||
name: string;
|
||||
quantity: number;
|
||||
price: string;
|
||||
}
|
||||
|
||||
interface OrderShippedProps {
|
||||
language: string;
|
||||
orderId: string;
|
||||
orderNumber: string;
|
||||
customerName: string;
|
||||
items: OrderItem[];
|
||||
trackingNumber?: string;
|
||||
trackingUrl?: string;
|
||||
}
|
||||
|
||||
const translations: Record<
|
||||
string,
|
||||
{
|
||||
title: string;
|
||||
preview: string;
|
||||
greeting: string;
|
||||
orderShipped: string;
|
||||
tracking: string;
|
||||
items: string;
|
||||
questions: string;
|
||||
}
|
||||
> = {
|
||||
sr: {
|
||||
title: "Vaša narudžbina je poslata!",
|
||||
preview: "Vaša narudžbina je na putu",
|
||||
greeting: "Poštovani {name},",
|
||||
orderShipped:
|
||||
"Odlične vesti! Vaša narudžbina je poslata i uskoro će stići na vašu adresu.",
|
||||
tracking: "Praćenje pošiljke",
|
||||
items: "Artikli",
|
||||
questions: "Imate pitanja? Pišite nam na support@manoonoils.com",
|
||||
},
|
||||
en: {
|
||||
title: "Your Order Has Shipped!",
|
||||
preview: "Your order is on its way",
|
||||
greeting: "Dear {name},",
|
||||
orderShipped:
|
||||
"Great news! Your order has been shipped and will arrive at your address soon.",
|
||||
tracking: "Track your shipment",
|
||||
items: "Items",
|
||||
questions: "Questions? Email us at support@manoonoils.com",
|
||||
},
|
||||
de: {
|
||||
title: "Ihre Bestellung wurde versendet!",
|
||||
preview: "Ihre Bestellung ist unterwegs",
|
||||
greeting: "Sehr geehrte/r {name},",
|
||||
orderShipped:
|
||||
"Großartige Neuigkeiten! Ihre Bestellung wurde versandt und wird in Kürze bei Ihnen eintreffen.",
|
||||
tracking: "Sendung verfolgen",
|
||||
items: "Artikel",
|
||||
questions: "Fragen? Schreiben Sie uns an support@manoonoils.com",
|
||||
},
|
||||
fr: {
|
||||
title: "Votre commande a été expédiée!",
|
||||
preview: "Votre commande est en route",
|
||||
greeting: "Cher(e) {name},",
|
||||
orderShipped:
|
||||
"Bonne nouvelle! Votre commande a été expédiée et arrivera bientôt à votre adresse.",
|
||||
tracking: "Suivre votre envoi",
|
||||
items: "Articles",
|
||||
questions: "Questions? Écrivez-nous à support@manoonoils.com",
|
||||
},
|
||||
};
|
||||
|
||||
export function OrderShipped({
|
||||
language = "en",
|
||||
orderId,
|
||||
orderNumber,
|
||||
customerName,
|
||||
items,
|
||||
trackingNumber,
|
||||
trackingUrl,
|
||||
}: OrderShippedProps) {
|
||||
const t = translations[language] || translations.en;
|
||||
|
||||
return (
|
||||
<BaseLayout previewText={t.preview} language={language}>
|
||||
<Text style={styles.title}>{t.title}</Text>
|
||||
<Text style={styles.greeting}>{t.greeting.replace("{name}", customerName)}</Text>
|
||||
<Text style={styles.text}>{t.orderShipped}</Text>
|
||||
|
||||
{trackingNumber && (
|
||||
<Section style={styles.trackingSection}>
|
||||
<Text style={styles.sectionTitle}>{t.tracking}</Text>
|
||||
{trackingUrl ? (
|
||||
<Button href={trackingUrl} style={styles.trackingButton}>
|
||||
{trackingNumber}
|
||||
</Button>
|
||||
) : (
|
||||
<Text style={styles.trackingNumber}>{trackingNumber}</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>
|
||||
))}
|
||||
</Section>
|
||||
|
||||
<Text style={styles.questions}>{t.questions}</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",
|
||||
},
|
||||
trackingSection: {
|
||||
backgroundColor: "#f9f9f9",
|
||||
padding: "15px",
|
||||
borderRadius: "8px",
|
||||
marginBottom: "20px",
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: "16px",
|
||||
fontWeight: "bold" as const,
|
||||
color: "#1a1a1a",
|
||||
marginBottom: "10px",
|
||||
},
|
||||
trackingNumber: {
|
||||
fontSize: "14px",
|
||||
color: "#333333",
|
||||
margin: "0",
|
||||
},
|
||||
trackingButton: {
|
||||
backgroundColor: "#000000",
|
||||
color: "#ffffff",
|
||||
padding: "10px 20px",
|
||||
borderRadius: "4px",
|
||||
fontSize: "14px",
|
||||
textDecoration: "none",
|
||||
},
|
||||
itemsSection: {
|
||||
marginBottom: "20px",
|
||||
},
|
||||
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",
|
||||
},
|
||||
questions: {
|
||||
fontSize: "14px",
|
||||
color: "#666666",
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user