site-panzai/server/utils/store.ts
2026-11-13 13:57:04 +01:00

166 lines
7.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Base de données en mémoire (démo). Tout est remis à zéro au redémarrage du serveur.
// En production : remplacer par Supabase / Postgres.
export type Size = 'L' | 'XL'
export type Category = 'wok' | 'baguette' | 'boissons' | 'desserts'
export type Status = 'new' | 'prep' | 'ready' | 'done' | 'refused'
export type Payment = 'carte' | 'apple' | 'google' | 'especes'
export interface Dish {
id: string
name: string
desc: string
cat: Category
prices: Partial<Record<Size, number | null>> // centimes ; null = prix à définir
meats: string[]
sauces?: string[]
badge?: { label: string, tone: 'best' | 'spicy' | 'veggie' }
img?: string
available: boolean
}
export interface Extra { id: string, label: string, price: number }
export interface OrderItem {
dishId: string
name: string
size: Size
meat: string
sauce?: string
extras: string[]
note?: string
qty: number
unit: number
}
export interface Order {
id: string
num: string
createdAt: number
client: { name: string, phone: string }
mode: 'livraison' | 'retrait'
address?: string
slot?: string
items: OrderItem[]
subtotal: number
fee: number
total: number
payment: Payment
paid: boolean
status: Status
prepMinutes?: number
readyBy?: number
refunded?: boolean
history: { at: number, label: string }[]
}
export interface DayHours { day: string, open: boolean, midi: string, soir: string }
export interface Settings {
open: boolean
wait: number
deliveryFee: number
freeFrom: number
minOrder: number
radiusKm: number
hours: DayHours[]
closures: { date: string, label: string }[]
}
interface DB {
dishes: Dish[]
extras: Extra[]
orders: Order[]
settings: Settings
seq: number
}
const MEATS = ['Bœuf', 'Crevettes', 'Poulet']
function seedOrders(): Order[] {
const now = Date.now()
const m = (min: number) => now - min * 60000
const mk = (n: number, min: number, name: string, mode: Order['mode'], items: OrderItem[], status: Status, payment: Payment = 'carte', extra: Partial<Order> = {}): Order => {
const subtotal = items.reduce((a, i) => a + i.unit * i.qty, 0)
const fee = mode === 'livraison' ? 250 : 0
const createdAt = m(min)
return {
id: 'seed' + n, num: 'PZ-0' + n, createdAt, client: { name, phone: '06 12 34 56 78' }, mode,
address: mode === 'livraison' ? '12 rue des Arts, 74200 Thonon-les-Bains' : undefined,
slot: mode === 'retrait' ? 'Dès que possible' : undefined,
items, subtotal, fee, total: subtotal + fee, payment, paid: payment !== 'especes', status,
history: [
{ at: createdAt, label: 'Commande reçue' },
...(status !== 'new' ? [{ at: createdAt + 60000, label: 'Acceptée · en préparation au wok' }] : []),
...(status === 'ready' || status === 'done' ? [{ at: createdAt + 18 * 60000, label: 'Prête' }] : []),
...(status === 'done' ? [{ at: createdAt + 25 * 60000, label: mode === 'livraison' ? 'Remise au livreur' : 'Récupérée par le client' }] : [])
],
...extra
}
}
const it = (dishId: string, name: string, size: Size, meat: string, unit: number, qty = 1, sauce?: string, note?: string): OrderItem =>
({ dishId, name, size, meat, unit, qty, sauce, note, extras: [] })
return [
mk(412, 380, 'Emma T.', 'retrait', [it('pad', 'Le Pad Thaï', 'L', 'Poulet', 1350)], 'done'),
mk(413, 350, 'Hugo L.', 'livraison', [it('crousty', 'Le Crousty', 'XL', 'Poulet', 1350, 2, 'BBQ')], 'done', 'google'),
mk(414, 320, 'Nadia K.', 'livraison', [it('nouilles', 'Les Nouilles', 'XL', 'Bœuf', 1500), it('crousty', 'Le Crousty', 'L', 'Poulet', 1100, 1, 'Chili thaï')], 'done'),
mk(415, 60, 'Karim D.', 'livraison', [it('crousty', 'Le Crousty', 'L', 'Poulet', 1100, 1, 'BBQ'), it('nouilles', 'Les Nouilles', 'XL', 'Bœuf', 1500)], 'done', 'apple'),
mk(416, 22, 'Lina M.', 'retrait', [it('crousty', 'Le Crousty', 'XL', 'Bœuf', 1350, 1, 'Aigre-douce'), it('baguette', 'La Baguette', 'XL', 'Poulet', 1000)], 'ready', 'carte', { prepMinutes: 20 }),
mk(417, 12, 'Sami B.', 'livraison', [it('pad', 'Le Pad Thaï', 'XL', 'Crevettes', 1590, 1, undefined, 'sans oignons frits'), it('nouilles', 'Les Nouilles', 'L', 'Poulet', 1250)], 'prep', 'apple', { prepMinutes: 20, readyBy: m(12) + 20 * 60000 })
]
}
function seed(): DB {
return {
seq: 418,
extras: [
{ id: 'sauce', label: 'Sauce en plus', price: 50 },
{ id: 'oignons', label: 'Oignons frits en plus', price: 50 },
{ id: 'viande', label: 'Double viande', price: 300 }
],
dishes: [
{ id: 'crousty', name: 'Le Crousty', desc: 'Poulet crousty, riz, sauce wow, oignons frits.', cat: 'wok', prices: { L: 1100, XL: 1350 }, meats: MEATS, sauces: ['BBQ', 'Chili thaï', 'Aigre-douce'], badge: { label: 'Best-seller', tone: 'best' }, img: '/img/crousty.png', available: true },
{ id: 'nouilles', name: 'Les Nouilles', desc: 'Nouilles, légumes, herbes thaï, oignons frits.', cat: 'wok', prices: { L: 1250, XL: 1500 }, meats: MEATS, img: '/img/nouilles.png', available: true },
{ id: 'riz', name: 'Le Riz Thaï', desc: 'Riz sauté, légumes, sauce soja, herbes thaï, oignons frits.', cat: 'wok', prices: { L: 1150, XL: 1400 }, meats: MEATS, img: '/img/riz-thai.png', available: true },
{ id: 'pad', name: 'Le Pad Thaï', desc: 'Nouilles de riz, herbes thaï, soja, oignons frits.', cat: 'wok', prices: { L: 1350, XL: 1590 }, meats: MEATS, badge: { label: 'Épicé 🌶️', tone: 'spicy' }, img: '/img/pad-thai.png', available: true },
{ id: 'veggie', name: 'Le Veggie', desc: 'Riz, légumes (brocolis), herbes thaï, oignons frits.', cat: 'wok', prices: { L: null, XL: null }, meats: [], badge: { label: 'Veggie', tone: 'veggie' }, available: true },
{ id: 'baguette', name: 'La Baguette', desc: 'Bœuf ou poulet, légumes, sauce, frites.', cat: 'baguette', prices: { XL: 1000 }, meats: ['Bœuf', 'Poulet'], available: true }
],
orders: seedOrders(),
settings: {
open: true,
wait: 20,
deliveryFee: 250,
freeFrom: 3500,
minOrder: 1500,
radiusKm: 5,
hours: [
{ day: 'Lundi', open: true, midi: '11h30 – 14h00', soir: '18h30 – 22h00' },
{ day: 'Mardi', open: true, midi: '11h30 – 14h00', soir: '18h30 – 22h00' },
{ day: 'Mercredi', open: true, midi: '11h30 – 14h00', soir: '18h30 – 22h00' },
{ day: 'Jeudi', open: true, midi: '11h30 – 14h00', soir: '18h30 – 22h00' },
{ day: 'Vendredi', open: true, midi: '11h30 – 14h00', soir: '18h30 – 23h00' },
{ day: 'Samedi', open: true, midi: '11h30 – 14h30', soir: '18h30 – 23h00' },
{ day: 'Dimanche', open: true, midi: 'Fermé', soir: '18h30 – 22h00' }
],
closures: [
{ date: '24–25 déc.', label: 'Fêtes de fin d\'année' },
{ date: '1 janv.', label: 'Jour de l\'an' }
]
}
}
}
const g = globalThis as unknown as { __panzaiDb?: DB }
if (!g.__panzaiDb) g.__panzaiDb = seed()
export const db = g.__panzaiDb
export function resetDb() {
Object.assign(db, seed())
}
export function findOrder(id: string) {
const o = db.orders.find(x => x.id === id)
if (!o) throw createError({ statusCode: 404, statusMessage: 'Commande introuvable' })
return o
}