site-panzai/server/utils/store.ts

167 lines
7.1 KiB
TypeScript
Raw Normal View History

2026-11-13 13:57:04 +01:00
// 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
}