refactor: abstract site URL across email templates
- Add NEXT_PUBLIC_SITE_URL to .env.local - Update email templates to accept siteUrl prop - Update webhook handler to pass siteUrl from env var - Update create-webhooks.graphql with placeholder URL
This commit is contained in:
@@ -6,6 +6,8 @@ import { OrderShipped } from "@/emails/OrderShipped";
|
|||||||
import { OrderCancelled } from "@/emails/OrderCancelled";
|
import { OrderCancelled } from "@/emails/OrderCancelled";
|
||||||
import { OrderPaid } from "@/emails/OrderPaid";
|
import { OrderPaid } from "@/emails/OrderPaid";
|
||||||
|
|
||||||
|
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://dev.manoonoils.com";
|
||||||
|
|
||||||
interface SaleorWebhookHeaders {
|
interface SaleorWebhookHeaders {
|
||||||
"saleor-event": string;
|
"saleor-event": string;
|
||||||
"saleor-domain": string;
|
"saleor-domain": string;
|
||||||
@@ -159,6 +161,7 @@ async function handleOrderConfirmed(order: SaleorOrder) {
|
|||||||
items: parseOrderItems(order.lines, currency),
|
items: parseOrderItems(order.lines, currency),
|
||||||
total: formatPrice(order.total.gross.amount, currency),
|
total: formatPrice(order.total.gross.amount, currency),
|
||||||
shippingAddress: formatAddress(order.shippingAddress),
|
shippingAddress: formatAddress(order.shippingAddress),
|
||||||
|
siteUrl: SITE_URL,
|
||||||
}),
|
}),
|
||||||
language,
|
language,
|
||||||
idempotencyKey: `order-confirmed/${order.id}`,
|
idempotencyKey: `order-confirmed/${order.id}`,
|
||||||
@@ -175,6 +178,7 @@ async function handleOrderConfirmed(order: SaleorOrder) {
|
|||||||
items: parseOrderItems(order.lines, currency),
|
items: parseOrderItems(order.lines, currency),
|
||||||
total: formatPrice(order.total.gross.amount, currency),
|
total: formatPrice(order.total.gross.amount, currency),
|
||||||
shippingAddress: formatAddress(order.shippingAddress),
|
shippingAddress: formatAddress(order.shippingAddress),
|
||||||
|
siteUrl: SITE_URL,
|
||||||
}),
|
}),
|
||||||
eventType: "ORDER_CONFIRMED",
|
eventType: "ORDER_CONFIRMED",
|
||||||
orderId: order.id,
|
orderId: order.id,
|
||||||
@@ -219,6 +223,7 @@ async function handleOrderFulfilled(order: SaleorOrder) {
|
|||||||
items: parseOrderItems(order.lines, currency),
|
items: parseOrderItems(order.lines, currency),
|
||||||
trackingNumber,
|
trackingNumber,
|
||||||
trackingUrl,
|
trackingUrl,
|
||||||
|
siteUrl: SITE_URL,
|
||||||
}),
|
}),
|
||||||
language,
|
language,
|
||||||
idempotencyKey: `order-fulfilled/${order.id}`,
|
idempotencyKey: `order-fulfilled/${order.id}`,
|
||||||
@@ -234,6 +239,7 @@ async function handleOrderFulfilled(order: SaleorOrder) {
|
|||||||
items: parseOrderItems(order.lines, currency),
|
items: parseOrderItems(order.lines, currency),
|
||||||
trackingNumber,
|
trackingNumber,
|
||||||
trackingUrl,
|
trackingUrl,
|
||||||
|
siteUrl: SITE_URL,
|
||||||
}),
|
}),
|
||||||
eventType: "ORDER_FULFILLED",
|
eventType: "ORDER_FULFILLED",
|
||||||
orderId: order.id,
|
orderId: order.id,
|
||||||
@@ -272,6 +278,7 @@ async function handleOrderCancelled(order: SaleorOrder) {
|
|||||||
items: parseOrderItems(order.lines, currency),
|
items: parseOrderItems(order.lines, currency),
|
||||||
total: formatPrice(order.total.gross.amount, currency),
|
total: formatPrice(order.total.gross.amount, currency),
|
||||||
reason,
|
reason,
|
||||||
|
siteUrl: SITE_URL,
|
||||||
}),
|
}),
|
||||||
language,
|
language,
|
||||||
idempotencyKey: `order-cancelled/${order.id}`,
|
idempotencyKey: `order-cancelled/${order.id}`,
|
||||||
@@ -287,6 +294,7 @@ async function handleOrderCancelled(order: SaleorOrder) {
|
|||||||
items: parseOrderItems(order.lines, currency),
|
items: parseOrderItems(order.lines, currency),
|
||||||
total: formatPrice(order.total.gross.amount, currency),
|
total: formatPrice(order.total.gross.amount, currency),
|
||||||
reason,
|
reason,
|
||||||
|
siteUrl: SITE_URL,
|
||||||
}),
|
}),
|
||||||
eventType: "ORDER_CANCELLED",
|
eventType: "ORDER_CANCELLED",
|
||||||
orderId: order.id,
|
orderId: order.id,
|
||||||
@@ -316,6 +324,7 @@ async function handleOrderFullyPaid(order: SaleorOrder) {
|
|||||||
customerName,
|
customerName,
|
||||||
items: parseOrderItems(order.lines, currency),
|
items: parseOrderItems(order.lines, currency),
|
||||||
total: formatPrice(order.total.gross.amount, currency),
|
total: formatPrice(order.total.gross.amount, currency),
|
||||||
|
siteUrl: SITE_URL,
|
||||||
}),
|
}),
|
||||||
language,
|
language,
|
||||||
idempotencyKey: `order-paid/${order.id}`,
|
idempotencyKey: `order-paid/${order.id}`,
|
||||||
@@ -330,6 +339,7 @@ async function handleOrderFullyPaid(order: SaleorOrder) {
|
|||||||
customerName,
|
customerName,
|
||||||
items: parseOrderItems(order.lines, currency),
|
items: parseOrderItems(order.lines, currency),
|
||||||
total: formatPrice(order.total.gross.amount, currency),
|
total: formatPrice(order.total.gross.amount, currency),
|
||||||
|
siteUrl: SITE_URL,
|
||||||
}),
|
}),
|
||||||
eventType: "ORDER_FULLY_PAID",
|
eventType: "ORDER_FULLY_PAID",
|
||||||
orderId: order.id,
|
orderId: order.id,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ interface BaseLayoutProps {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
previewText: string;
|
previewText: string;
|
||||||
language: string;
|
language: string;
|
||||||
|
siteUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const translations: Record<string, { footer: string; company: string }> = {
|
const translations: Record<string, { footer: string; company: string }> = {
|
||||||
@@ -37,7 +38,7 @@ const translations: Record<string, { footer: string; company: string }> = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export function BaseLayout({ children, previewText, language }: BaseLayoutProps) {
|
export function BaseLayout({ children, previewText, language, siteUrl }: BaseLayoutProps) {
|
||||||
const t = translations[language] || translations.en;
|
const t = translations[language] || translations.en;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -48,7 +49,7 @@ export function BaseLayout({ children, previewText, language }: BaseLayoutProps)
|
|||||||
<Container style={styles.container}>
|
<Container style={styles.container}>
|
||||||
<Section style={styles.logoSection}>
|
<Section style={styles.logoSection}>
|
||||||
<Img
|
<Img
|
||||||
src="https://manoonoils.com/logo.png"
|
src={`${siteUrl}/logo.png`}
|
||||||
width="150"
|
width="150"
|
||||||
height="auto"
|
height="auto"
|
||||||
alt="ManoonOils"
|
alt="ManoonOils"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ interface OrderCancelledProps {
|
|||||||
items: OrderItem[];
|
items: OrderItem[];
|
||||||
total: string;
|
total: string;
|
||||||
reason?: string;
|
reason?: string;
|
||||||
|
siteUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const translations: Record<
|
const translations: Record<
|
||||||
@@ -85,11 +86,12 @@ export function OrderCancelled({
|
|||||||
items,
|
items,
|
||||||
total,
|
total,
|
||||||
reason,
|
reason,
|
||||||
|
siteUrl,
|
||||||
}: OrderCancelledProps) {
|
}: OrderCancelledProps) {
|
||||||
const t = translations[language] || translations.en;
|
const t = translations[language] || translations.en;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseLayout previewText={t.preview} language={language}>
|
<BaseLayout previewText={t.preview} language={language} siteUrl={siteUrl}>
|
||||||
<Text style={styles.title}>{t.title}</Text>
|
<Text style={styles.title}>{t.title}</Text>
|
||||||
<Text style={styles.greeting}>{t.greeting.replace("{name}", customerName)}</Text>
|
<Text style={styles.greeting}>{t.greeting.replace("{name}", customerName)}</Text>
|
||||||
<Text style={styles.text}>{t.orderCancelled}</Text>
|
<Text style={styles.text}>{t.orderCancelled}</Text>
|
||||||
@@ -124,7 +126,7 @@ export function OrderCancelled({
|
|||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section style={styles.buttonSection}>
|
<Section style={styles.buttonSection}>
|
||||||
<Button href="https://manoonoils.com" style={styles.button}>
|
<Button href={siteUrl} style={styles.button}>
|
||||||
{language === "sr" ? "Pogledajte proizvode" : "Browse Products"}
|
{language === "sr" ? "Pogledajte proizvode" : "Browse Products"}
|
||||||
</Button>
|
</Button>
|
||||||
</Section>
|
</Section>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ interface OrderConfirmationProps {
|
|||||||
items: OrderItem[];
|
items: OrderItem[];
|
||||||
total: string;
|
total: string;
|
||||||
shippingAddress?: string;
|
shippingAddress?: string;
|
||||||
|
siteUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const translations: Record<
|
const translations: Record<
|
||||||
@@ -101,11 +102,12 @@ export function OrderConfirmation({
|
|||||||
items,
|
items,
|
||||||
total,
|
total,
|
||||||
shippingAddress,
|
shippingAddress,
|
||||||
|
siteUrl,
|
||||||
}: OrderConfirmationProps) {
|
}: OrderConfirmationProps) {
|
||||||
const t = translations[language] || translations.en;
|
const t = translations[language] || translations.en;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseLayout previewText={t.preview} language={language}>
|
<BaseLayout previewText={t.preview} language={language} siteUrl={siteUrl}>
|
||||||
<Text style={styles.title}>{t.title}</Text>
|
<Text style={styles.title}>{t.title}</Text>
|
||||||
<Text style={styles.greeting}>{t.greeting.replace("{name}", customerName)}</Text>
|
<Text style={styles.greeting}>{t.greeting.replace("{name}", customerName)}</Text>
|
||||||
<Text style={styles.text}>{t.orderReceived}</Text>
|
<Text style={styles.text}>{t.orderReceived}</Text>
|
||||||
@@ -142,7 +144,7 @@ export function OrderConfirmation({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<Section style={styles.buttonSection}>
|
<Section style={styles.buttonSection}>
|
||||||
<Button href="https://manoonoils.com" style={styles.button}>
|
<Button href={siteUrl} style={styles.button}>
|
||||||
{language === "sr"
|
{language === "sr"
|
||||||
? "Pogledajte narudžbinu"
|
? "Pogledajte narudžbinu"
|
||||||
: language === "de"
|
: language === "de"
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ interface OrderPaidProps {
|
|||||||
customerName: string;
|
customerName: string;
|
||||||
items: OrderItem[];
|
items: OrderItem[];
|
||||||
total: string;
|
total: string;
|
||||||
|
siteUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const translations: Record<
|
const translations: Record<
|
||||||
@@ -92,11 +93,12 @@ export function OrderPaid({
|
|||||||
customerName,
|
customerName,
|
||||||
items,
|
items,
|
||||||
total,
|
total,
|
||||||
|
siteUrl,
|
||||||
}: OrderPaidProps) {
|
}: OrderPaidProps) {
|
||||||
const t = translations[language] || translations.en;
|
const t = translations[language] || translations.en;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseLayout previewText={t.preview} language={language}>
|
<BaseLayout previewText={t.preview} language={language} siteUrl={siteUrl}>
|
||||||
<Text style={styles.title}>{t.title}</Text>
|
<Text style={styles.title}>{t.title}</Text>
|
||||||
<Text style={styles.greeting}>{t.greeting.replace("{name}", customerName)}</Text>
|
<Text style={styles.greeting}>{t.greeting.replace("{name}", customerName)}</Text>
|
||||||
<Text style={styles.text}>{t.orderPaid}</Text>
|
<Text style={styles.text}>{t.orderPaid}</Text>
|
||||||
@@ -131,7 +133,7 @@ export function OrderPaid({
|
|||||||
</Section>
|
</Section>
|
||||||
|
|
||||||
<Section style={styles.buttonSection}>
|
<Section style={styles.buttonSection}>
|
||||||
<Button href="https://manoonoils.com" style={styles.button}>
|
<Button href={siteUrl} style={styles.button}>
|
||||||
{language === "sr" ? "Nastavite kupovinu" : "Continue Shopping"}
|
{language === "sr" ? "Nastavite kupovinu" : "Continue Shopping"}
|
||||||
</Button>
|
</Button>
|
||||||
</Section>
|
</Section>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ interface OrderShippedProps {
|
|||||||
items: OrderItem[];
|
items: OrderItem[];
|
||||||
trackingNumber?: string;
|
trackingNumber?: string;
|
||||||
trackingUrl?: string;
|
trackingUrl?: string;
|
||||||
|
siteUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const translations: Record<
|
const translations: Record<
|
||||||
@@ -80,11 +81,12 @@ export function OrderShipped({
|
|||||||
items,
|
items,
|
||||||
trackingNumber,
|
trackingNumber,
|
||||||
trackingUrl,
|
trackingUrl,
|
||||||
|
siteUrl,
|
||||||
}: OrderShippedProps) {
|
}: OrderShippedProps) {
|
||||||
const t = translations[language] || translations.en;
|
const t = translations[language] || translations.en;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseLayout previewText={t.preview} language={language}>
|
<BaseLayout previewText={t.preview} language={language} siteUrl={siteUrl}>
|
||||||
<Text style={styles.title}>{t.title}</Text>
|
<Text style={styles.title}>{t.title}</Text>
|
||||||
<Text style={styles.greeting}>{t.greeting.replace("{name}", customerName)}</Text>
|
<Text style={styles.greeting}>{t.greeting.replace("{name}", customerName)}</Text>
|
||||||
<Text style={styles.text}>{t.orderShipped}</Text>
|
<Text style={styles.text}>{t.orderShipped}</Text>
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
|
# Replace YOUR_STOREFRONT_URL with your actual storefront URL
|
||||||
|
# Dev: https://dev.manoonoils.com
|
||||||
|
# Prod: https://manoonoils.com
|
||||||
|
|
||||||
mutation CreateSaleorWebhooks {
|
mutation CreateSaleorWebhooks {
|
||||||
orderConfirmedWebhook: webhookCreate(input: {
|
orderConfirmedWebhook: webhookCreate(input: {
|
||||||
name: "Resend - Order Confirmed"
|
name: "Resend - Order Confirmed"
|
||||||
targetUrl: "https://manoonoils.com/api/webhooks/saleor"
|
targetUrl: "YOUR_STOREFRONT_URL/api/webhooks/saleor"
|
||||||
events: [ORDER_CONFIRMED]
|
events: [ORDER_CONFIRMED]
|
||||||
isActive: true
|
isActive: true
|
||||||
}) {
|
}) {
|
||||||
@@ -20,7 +24,7 @@ mutation CreateSaleorWebhooks {
|
|||||||
|
|
||||||
orderPaidWebhook: webhookCreate(input: {
|
orderPaidWebhook: webhookCreate(input: {
|
||||||
name: "Resend - Order Paid"
|
name: "Resend - Order Paid"
|
||||||
targetUrl: "https://manoonoils.com/api/webhooks/saleor"
|
targetUrl: "YOUR_STOREFRONT_URL/api/webhooks/saleor"
|
||||||
events: [ORDER_FULLY_PAID]
|
events: [ORDER_FULLY_PAID]
|
||||||
isActive: true
|
isActive: true
|
||||||
}) {
|
}) {
|
||||||
@@ -39,7 +43,7 @@ mutation CreateSaleorWebhooks {
|
|||||||
|
|
||||||
orderCancelledWebhook: webhookCreate(input: {
|
orderCancelledWebhook: webhookCreate(input: {
|
||||||
name: "Resend - Order Cancelled"
|
name: "Resend - Order Cancelled"
|
||||||
targetUrl: "https://manoonoils.com/api/webhooks/saleor"
|
targetUrl: "YOUR_STOREFRONT_URL/api/webhooks/saleor"
|
||||||
events: [ORDER_CANCELLED]
|
events: [ORDER_CANCELLED]
|
||||||
isActive: true
|
isActive: true
|
||||||
}) {
|
}) {
|
||||||
@@ -58,7 +62,7 @@ mutation CreateSaleorWebhooks {
|
|||||||
|
|
||||||
orderFulfilledWebhook: webhookCreate(input: {
|
orderFulfilledWebhook: webhookCreate(input: {
|
||||||
name: "Resend - Order Fulfilled"
|
name: "Resend - Order Fulfilled"
|
||||||
targetUrl: "https://manoonoils.com/api/webhooks/saleor"
|
targetUrl: "YOUR_STOREFRONT_URL/api/webhooks/saleor"
|
||||||
events: [ORDER_FULFILLED]
|
events: [ORDER_FULFILLED]
|
||||||
isActive: true
|
isActive: true
|
||||||
}) {
|
}) {
|
||||||
|
|||||||
Reference in New Issue
Block a user