metatroncubeswdev e56182e0e3 chore: initial Datewise app (rebranded from SlotSure prototype)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:06:49 -04:00

167 lines
4.7 KiB
TypeScript

import type { Weekday, TimeRange, LocationConfig, PeakDay, ProductRule, ShopSettings, CartItem } from "../types";
import { zonedToUtc } from "../time";
export const TZ = "America/Toronto";
/** Local Toronto wall time → epoch ms */
export const at = (date: string, time: string) => zonedToUtc(date, time, TZ);
const h = () => [{ start: "10:00", end: "18:00" }];
const monSat: Partial<Record<Weekday, TimeRange[]>> = { 1: h(), 2: h(), 3: h(), 4: h(), 5: h(), 6: h() };
const deliverySlots = [
{ start: "10:00", end: "13:00" },
{ start: "13:00", end: "16:00" },
{ start: "16:00", end: "19:00", surcharge: 300 },
];
export function kitchener(): LocationConfig {
return {
id: "kit",
name: "Kitchener",
address: "900 Guelph St, Kitchener, ON N2H 5Z6",
timezone: TZ,
lat: 43.4643,
lng: -80.4844,
enabled: true,
priority: 1,
exceptions: [],
zones: [
{ id: "z1", name: "Kitchener-Waterloo", postalPatterns: ["N2H*", "N2G*", "N2J..N2M"], fee: 799, freeOver: 7500, minOrder: 2500 },
{ id: "z2", name: "15 km radius", radiusKm: 15, fee: 1299 },
],
pickupInstructions: "Unit 7 — ring the bell",
methods: {
pickup: {
enabled: true,
weeklyHours: { ...monSat },
slotMode: "generated",
slotMinutes: 60,
prepMinutes: 60,
prepMode: "elapsed",
sameDayCutoff: "14:00",
horizonDays: 30,
slotCapacity: 4,
},
delivery: {
enabled: true,
weeklyHours: { 0: [{ start: "10:00", end: "19:00" }], 2: [{ start: "10:00", end: "19:00" }], 3: [{ start: "10:00", end: "19:00" }], 4: [{ start: "10:00", end: "19:00" }], 5: [{ start: "10:00", end: "19:00" }], 6: [{ start: "10:00", end: "19:00" }] },
slotMode: "explicit",
slots: { 0: deliverySlots, 2: deliverySlots, 3: deliverySlots, 4: deliverySlots, 5: deliverySlots, 6: deliverySlots },
prepMinutes: 120,
prepMode: "elapsed",
sameDayCutoff: "12:00",
horizonDays: 21,
slotCapacity: 5,
dailyCapacity: 12,
},
shipping: {
enabled: true,
weeklyHours: { ...monSat },
slotMode: "dateOnly",
prepMinutes: 0,
prepMode: "operating",
sameDayCutoff: "14:00",
horizonDays: 14,
},
},
};
}
export function waterloo(): LocationConfig {
const l = kitchener();
return {
...l,
id: "wat",
name: "Waterloo",
priority: 2,
lat: 43.4723,
lng: -80.5449,
zones: [{ id: "zw", name: "Waterloo", postalPatterns: ["N2L*", "N2J*"], fee: 599 }],
};
}
export const settings: ShopSettings = {
mixedPrepPolicy: "longest",
holdMinutes: 10,
weekStartsOn: 0,
reschedule: { enabled: true, minHoursBefore: 24, maxChanges: 2, allowMethodChange: false },
reminders: [
{ id: "r1", channel: "sms", minutesBefore: 24 * 60, template: "Hi {{name}}, your {{method}} is {{date}} {{time}}", enabled: true },
{ id: "r2", channel: "email", minutesBefore: 120, template: "Reminder {{order}}", enabled: true },
],
};
export const marigold: CartItem = {
productId: "p_marigold",
variantId: "v_marigold",
title: "Fresh Marigold Garland",
quantity: 2,
price: 1999,
collectionIds: ["c_flowers"],
vendor: "C2C Agricorp",
productType: "Fresh Flowers",
tags: ["perishable"],
};
export const pakku: CartItem = {
productId: "p_pakku",
variantId: "v_pakku",
title: "பாக்கு (Pākkŭ) 200g",
quantity: 1,
price: 699,
collectionIds: ["c_puja"],
vendor: "Clicks to Cart",
productType: "Puja",
};
export const saree: CartItem = {
productId: "p_saree",
variantId: "v_saree",
title: "Silk Saree",
quantity: 1,
price: 15999,
collectionIds: ["c_apparel"],
productType: "Apparel",
};
export const rules: ProductRule[] = [
{
id: "r_fresh_noship",
name: "Fresh flowers can't ship",
enabled: true,
priority: 1,
target: { type: "productType", values: ["Fresh Flowers"] },
action: { type: "prohibitMethods", methods: ["shipping"] },
message: "Fresh flowers are pickup or local delivery only",
},
{
id: "r_fresh_prep",
name: "Garland stringing time",
enabled: true,
priority: 2,
target: { type: "tag", values: ["perishable"] },
action: { type: "addPrep", minutes: 240 },
},
{
id: "r_saree_prep",
name: "Saree fall & pico",
enabled: true,
priority: 3,
target: { type: "collection", values: ["c_apparel"] },
action: { type: "addPrep", minutes: 60 * 24 },
},
];
export const diwali: PeakDay = {
id: "pk_diwali",
name: "Diwali",
dates: ["2026-11-08"],
methods: ["delivery", "pickup"],
dailyCapacity: 30,
slotCapacity: 10,
extraPrepMinutes: 60,
cutoff: { daysBefore: 2, time: "18:00" },
surcharge: 500,
waitlist: true,
message: "Diwali orders close Friday 6 PM",
};