refactor: Remove email functionality - migrated to core-extensions app

Removed:
- Webhook handlers (src/app/api/webhooks/saleor/)
- Email templates (src/emails/)
- OrderNotificationService (src/lib/services/)

Emails now handled by saleor-core-extensions service
Manifest: https://core-extensions.manoonoils.com/api/manifest
This commit is contained in:
Unchained
2026-03-26 08:50:58 +02:00
parent a6ebcf408c
commit 09294fd752
9 changed files with 0 additions and 1838 deletions

View File

@@ -1,357 +0,0 @@
import { sendEmailToCustomer, sendEmailToAdmin } from "@/lib/resend";
import { OrderConfirmation } from "@/emails/OrderConfirmation";
import { OrderShipped } from "@/emails/OrderShipped";
import { OrderCancelled } from "@/emails/OrderCancelled";
import { OrderPaid } from "@/emails/OrderPaid";
import { formatPrice } from "@/app/api/webhooks/saleor/utils";
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL || "https://dev.manoonoils.com";
const DASHBOARD_URL = process.env.DASHBOARD_URL || "https://dashboard.manoonoils.com";
// Translation helper for email subjects
function getOrderConfirmationSubject(language: string, orderNumber: string): string {
const subjects: Record<string, string> = {
sr: `Potvrda narudžbine #${orderNumber}`,
de: `Bestellbestätigung #${orderNumber}`,
fr: `Confirmation de commande #${orderNumber}`,
en: `Order Confirmation #${orderNumber}`,
};
return subjects[language] || subjects.en;
}
function getOrderShippedSubject(language: string, orderNumber: string): string {
const subjects: Record<string, string> = {
sr: `Vaša narudžbina #${orderNumber} je poslata!`,
de: `Ihre Bestellung #${orderNumber} wurde versendet!`,
fr: `Votre commande #${orderNumber} a été expédiée!`,
en: `Your Order #${orderNumber} Has Shipped!`,
};
return subjects[language] || subjects.en;
}
function getOrderCancelledSubject(language: string, orderNumber: string): string {
const subjects: Record<string, string> = {
sr: `Vaša narudžbina #${orderNumber} je otkazana`,
de: `Ihre Bestellung #${orderNumber} wurde storniert`,
fr: `Votre commande #${orderNumber} a été annulée`,
en: `Your Order #${orderNumber} Has Been Cancelled`,
};
return subjects[language] || subjects.en;
}
function getOrderPaidSubject(language: string, orderNumber: string): string {
const subjects: Record<string, string> = {
sr: `Plaćanje za narudžbinu #${orderNumber} je primljeno!`,
de: `Zahlung für Bestellung #${orderNumber} erhalten!`,
fr: `Paiement reçu pour la commande #${orderNumber}!`,
en: `Payment Received for Order #${orderNumber}!`,
};
return subjects[language] || subjects.en;
}
// Interfaces
interface OrderItem {
id: string;
productName: string;
variantName?: string;
quantity: number;
totalPrice: {
gross: {
amount: number;
currency: string;
};
};
}
interface OrderAddress {
firstName?: string;
lastName?: string;
streetAddress1?: string;
streetAddress2?: string;
city?: string;
postalCode?: string;
country?: string;
phone?: string;
}
interface Order {
id: string;
number: string;
userEmail: string;
user?: {
firstName?: string;
lastName?: string;
};
billingAddress?: OrderAddress;
shippingAddress?: OrderAddress;
lines: OrderItem[];
total: {
gross: {
amount: number;
currency: string;
};
};
languageCode?: string;
metadata?: Array<{ key: string; value: string }>;
}
interface OrderEmailItem {
id: string;
name: string;
quantity: number;
price: string;
}
class OrderNotificationService {
private static instance: OrderNotificationService;
static getInstance(): OrderNotificationService {
if (!OrderNotificationService.instance) {
OrderNotificationService.instance = new OrderNotificationService();
}
return OrderNotificationService.instance;
}
private parseOrderItems(lines: OrderItem[], currency: string): OrderEmailItem[] {
return lines.map((line) => ({
id: line.id,
name: line.variantName ? `${line.productName} (${line.variantName})` : line.productName,
quantity: line.quantity,
price: formatPrice(line.totalPrice.gross.amount, currency),
}));
}
private formatAddress(address?: OrderAddress): string {
if (!address) return "";
const parts = [
address.firstName,
address.lastName,
address.streetAddress1,
address.streetAddress2,
address.city,
address.postalCode,
address.country,
].filter(Boolean);
return parts.join(", ");
}
private getCustomerName(order: Order): string {
if (order.user?.firstName || order.user?.lastName) {
return `${order.user.firstName || ""} ${order.user.lastName || ""}`.trim();
}
if (order.shippingAddress?.firstName || order.shippingAddress?.lastName) {
return `${order.shippingAddress.firstName || ""} ${order.shippingAddress.lastName || ""}`.trim();
}
return "Customer";
}
private getCustomerLanguage(order: Order): string {
const LANGUAGE_CODE_MAP: Record<string, string> = {
SR: "sr",
EN: "en",
DE: "de",
FR: "fr",
};
if (order.languageCode && LANGUAGE_CODE_MAP[order.languageCode]) {
return LANGUAGE_CODE_MAP[order.languageCode];
}
if (order.metadata) {
const langMeta = order.metadata.find((m) => m.key === "language");
if (langMeta && LANGUAGE_CODE_MAP[langMeta.value.toUpperCase()]) {
return LANGUAGE_CODE_MAP[langMeta.value.toUpperCase()];
}
}
return "en";
}
async sendOrderConfirmation(order: Order): Promise<void> {
const language = this.getCustomerLanguage(order);
const currency = order.total.gross.currency;
const customerName = this.getCustomerName(order);
const customerEmail = order.userEmail;
const phone = order.shippingAddress?.phone || order.billingAddress?.phone;
await sendEmailToCustomer({
to: customerEmail,
subject: getOrderConfirmationSubject(language, order.number),
react: OrderConfirmation({
language,
orderId: order.id,
orderNumber: order.number,
customerEmail,
customerName,
items: this.parseOrderItems(order.lines, currency),
total: formatPrice(order.total.gross.amount, currency),
shippingAddress: this.formatAddress(order.shippingAddress),
siteUrl: SITE_URL,
}),
language,
idempotencyKey: `order-confirmed/${order.id}`,
});
}
async sendOrderConfirmationToAdmin(order: Order): Promise<void> {
const currency = order.total.gross.currency;
const customerName = this.getCustomerName(order);
const customerEmail = order.userEmail;
const phone = order.shippingAddress?.phone || order.billingAddress?.phone;
await sendEmailToAdmin({
subject: `🎉 New Order #${order.number} - ${formatPrice(order.total.gross.amount, currency)}`,
react: OrderConfirmation({
language: "en",
orderId: order.id,
orderNumber: order.number,
customerEmail,
customerName,
items: this.parseOrderItems(order.lines, currency),
total: formatPrice(order.total.gross.amount, currency),
shippingAddress: this.formatAddress(order.shippingAddress),
billingAddress: this.formatAddress(order.billingAddress),
phone,
siteUrl: SITE_URL,
dashboardUrl: DASHBOARD_URL,
isAdmin: true,
}),
eventType: "ORDER_CONFIRMED",
orderId: order.id,
});
}
async sendOrderShipped(order: Order, trackingNumber?: string, trackingUrl?: string): Promise<void> {
const language = this.getCustomerLanguage(order);
const currency = order.total.gross.currency;
const customerName = this.getCustomerName(order);
const customerEmail = order.userEmail;
await sendEmailToCustomer({
to: customerEmail,
subject: getOrderShippedSubject(language, order.number),
react: OrderShipped({
language,
orderId: order.id,
orderNumber: order.number,
customerName,
items: this.parseOrderItems(order.lines, currency),
trackingNumber,
trackingUrl,
siteUrl: SITE_URL,
}),
language,
idempotencyKey: `order-fulfilled/${order.id}`,
});
}
async sendOrderShippedToAdmin(order: Order, trackingNumber?: string, trackingUrl?: string): Promise<void> {
const currency = order.total.gross.currency;
const customerName = this.getCustomerName(order);
await sendEmailToAdmin({
subject: `Order Shipped #${order.number} - ${customerName}`,
react: OrderShipped({
language: "en",
orderId: order.id,
orderNumber: order.number,
customerName,
items: this.parseOrderItems(order.lines, currency),
trackingNumber,
trackingUrl,
siteUrl: SITE_URL,
}),
eventType: "ORDER_FULFILLED",
orderId: order.id,
});
}
async sendOrderCancelled(order: Order, reason?: string): Promise<void> {
const language = this.getCustomerLanguage(order);
const currency = order.total.gross.currency;
const customerName = this.getCustomerName(order);
const customerEmail = order.userEmail;
await sendEmailToCustomer({
to: customerEmail,
subject: getOrderCancelledSubject(language, order.number),
react: OrderCancelled({
language,
orderId: order.id,
orderNumber: order.number,
customerName,
items: this.parseOrderItems(order.lines, currency),
total: formatPrice(order.total.gross.amount, currency),
reason,
siteUrl: SITE_URL,
}),
language,
idempotencyKey: `order-cancelled/${order.id}`,
});
}
async sendOrderCancelledToAdmin(order: Order, reason?: string): Promise<void> {
const currency = order.total.gross.currency;
const customerName = this.getCustomerName(order);
await sendEmailToAdmin({
subject: `Order Cancelled #${order.number} - ${customerName}`,
react: OrderCancelled({
language: "en",
orderId: order.id,
orderNumber: order.number,
customerName,
items: this.parseOrderItems(order.lines, currency),
total: formatPrice(order.total.gross.amount, currency),
reason,
siteUrl: SITE_URL,
}),
eventType: "ORDER_CANCELLED",
orderId: order.id,
});
}
async sendOrderPaid(order: Order): Promise<void> {
const language = this.getCustomerLanguage(order);
const currency = order.total.gross.currency;
const customerName = this.getCustomerName(order);
const customerEmail = order.userEmail;
await sendEmailToCustomer({
to: customerEmail,
subject: getOrderPaidSubject(language, order.number),
react: OrderPaid({
language,
orderId: order.id,
orderNumber: order.number,
customerName,
items: this.parseOrderItems(order.lines, currency),
total: formatPrice(order.total.gross.amount, currency),
siteUrl: SITE_URL,
}),
language,
idempotencyKey: `order-paid/${order.id}`,
});
}
async sendOrderPaidToAdmin(order: Order): Promise<void> {
const currency = order.total.gross.currency;
const customerName = this.getCustomerName(order);
await sendEmailToAdmin({
subject: `Payment Received #${order.number} - ${customerName} - ${formatPrice(order.total.gross.amount, currency)}`,
react: OrderPaid({
language: "en",
orderId: order.id,
orderNumber: order.number,
customerName,
items: this.parseOrderItems(order.lines, currency),
total: formatPrice(order.total.gross.amount, currency),
siteUrl: SITE_URL,
}),
eventType: "ORDER_FULLY_PAID",
orderId: order.id,
});
}
}
export const orderNotificationService = OrderNotificationService.getInstance();
export default orderNotificationService;