import type { Method } from "@prisma/client"; import db from "../db.server"; export type VerticalKey = "bakery" | "florist" | "grocer"; export interface SlotTemplateInput { method: Method; weekday: number; // 0 = Sunday .. 6 = Saturday startMin: number; endMin: number; capacity: number; cutoffMin: number; leadTimeMin: number; } export interface VerticalTemplate { vertical: VerticalKey; location: { name: string; timezone: string; }; slotTemplates: SlotTemplateInput[]; widgetCopy: { pickupLabel: string; deliveryLabel: string; shippingLabel: string; }; } const HOUR = 60; // Tue-Sat, pickup + local delivery. Bakers need same-day cutoff buffer for // remaining-batch prep (strategy §3.2 resource capacity), hence the shorter // weekday window and a same-day cutoff. function bakeryTemplate(): VerticalTemplate { const weekdays = [2, 3, 4, 5, 6]; // Tue-Sat return { vertical: "bakery", location: { name: "Main Bakery", timezone: "America/Toronto" }, slotTemplates: weekdays.flatMap((weekday) => [ { method: "PICKUP" as Method, weekday, startMin: 9 * HOUR, endMin: 17 * HOUR, capacity: 20, cutoffMin: 2 * HOUR, leadTimeMin: 24 * HOUR, }, { method: "LOCAL_DELIVERY" as Method, weekday, startMin: 11 * HOUR, endMin: 16 * HOUR, capacity: 10, cutoffMin: 3 * HOUR, leadTimeMin: 24 * HOUR, }, ]), widgetCopy: { pickupLabel: "Pickup date", deliveryLabel: "Delivery date", shippingLabel: "Shipping date", }, }; } // Mon-Sat, pickup + local delivery. Shorter lead time than bakery (florists // typically make up arrangements same-day) but tighter per-slot capacity // (single arranger/driver bottleneck). function floristTemplate(): VerticalTemplate { const weekdays = [1, 2, 3, 4, 5, 6]; // Mon-Sat return { vertical: "florist", location: { name: "Main Shop", timezone: "America/Toronto" }, slotTemplates: weekdays.flatMap((weekday) => [ { method: "PICKUP" as Method, weekday, startMin: 9 * HOUR, endMin: 18 * HOUR, capacity: 15, cutoffMin: HOUR, leadTimeMin: 4 * HOUR, }, { method: "LOCAL_DELIVERY" as Method, weekday, startMin: 10 * HOUR, endMin: 17 * HOUR, capacity: 8, cutoffMin: 2 * HOUR, leadTimeMin: 4 * HOUR, }, ]), widgetCopy: { pickupLabel: "Pickup date", deliveryLabel: "Delivery date", shippingLabel: "Shipping date", }, }; } // Every day, delivery + pickup + shipping, longer hours and higher // per-slot capacity (route-based, strategy §3.2 delivery-density). function grocerTemplate(): VerticalTemplate { const weekdays = [0, 1, 2, 3, 4, 5, 6]; // every day return { vertical: "grocer", location: { name: "Main Store", timezone: "America/Toronto" }, slotTemplates: weekdays.flatMap((weekday) => [ { method: "LOCAL_DELIVERY" as Method, weekday, startMin: 8 * HOUR, endMin: 20 * HOUR, capacity: 40, cutoffMin: 2 * HOUR, leadTimeMin: 2 * HOUR, }, { method: "PICKUP" as Method, weekday, startMin: 8 * HOUR, endMin: 20 * HOUR, capacity: 60, cutoffMin: HOUR, leadTimeMin: HOUR, }, ]), widgetCopy: { pickupLabel: "Pickup date", deliveryLabel: "Delivery date", shippingLabel: "Shipping date", }, }; } const TEMPLATES: Record VerticalTemplate> = { bakery: bakeryTemplate, florist: floristTemplate, grocer: grocerTemplate, }; export function getVerticalTemplate(vertical: VerticalKey): VerticalTemplate { return TEMPLATES[vertical](); } export function listVerticalTemplates(): VerticalKey[] { return Object.keys(TEMPLATES) as VerticalKey[]; } // I/O wrapper: applies a vertical's preset by creating a Location + its // SlotTemplates and merging widgetCopy into Shop.settings, all in one // transaction. Keeps the actual preset shape (above) pure and unit-testable. export async function seedVerticalTemplate(shopDomain: string, vertical: VerticalKey) { const template = getVerticalTemplate(vertical); return db.$transaction(async (tx) => { // Only seed widgetCopy for a brand-new shop — never clobber a merchant's // existing customized settings by re-running/switching templates. await tx.shop.upsert({ where: { shopDomain }, create: { shopDomain, settings: { widgetCopy: template.widgetCopy } }, update: {}, }); const location = await tx.location.create({ data: { shopDomain, name: template.location.name, address: "", timezone: template.location.timezone, slotTemplates: { create: template.slotTemplates.map((slot) => ({ shopDomain, ...slot, })), }, }, include: { slotTemplates: true }, }); return location; }); }