Adds a Bestellübersicht/Heimservice/Abmelden nav bar to the layout, and builds out the Heimservice page with a per-restaurant control panel (availability windows, ORDER WebApp link, password reset, mobile-number editor, and a consolidated call popup) mirroring the old interface's button layout in the current design system.
113 lines
3.1 KiB
TypeScript
113 lines
3.1 KiB
TypeScript
export type OrderStatus =
|
|
| "neu"
|
|
| "uebermittelt"
|
|
| "telefonisch"
|
|
| "nicht_uebermittelt"
|
|
| "rueckruf"
|
|
| "storniert"
|
|
| "fake";
|
|
|
|
export type OrderType = "vorbestellung" | "direktanruf";
|
|
|
|
/** Alles, was eine Bearbeiterin an einer Bestellung auslösen kann. */
|
|
export type OrderAction =
|
|
| "uebermittelt"
|
|
| "gastronom_voice"
|
|
| "gastronom_anruf"
|
|
| "kunde_anrufen"
|
|
| "nicht_uebermittelt"
|
|
| "rueckruf"
|
|
| "storno"
|
|
| "fake"
|
|
| "stornieren"
|
|
| "sos"
|
|
| "telefonisch_uebermittelt";
|
|
|
|
/** Protokolleintrag einer ausgelösten Aktion, treibt die Timeline. */
|
|
export type OrderEvent = { type: OrderAction; at: string };
|
|
|
|
export type PaymentMethod = "bar" | "ec" | "paypal" | "kreditkarte";
|
|
|
|
export type Restaurant = {
|
|
name: string;
|
|
city: string;
|
|
/** Öffnungszeiten, z. B. "10:00 bis 23:00" */
|
|
hours: string;
|
|
phone1: string;
|
|
phone2?: string;
|
|
mobile?: string;
|
|
mobile2?: string;
|
|
orderUrl: string;
|
|
/** Zeitfenster für den Heimservice, z. B. Mittags- und Abendschicht. Zweites Fenster optional. */
|
|
heimserviceWindows: [string, string | undefined];
|
|
};
|
|
|
|
export type Customer = {
|
|
name: string;
|
|
email: string;
|
|
phone: string;
|
|
};
|
|
|
|
export type Order = {
|
|
id: string;
|
|
/** Fortlaufende Bestellnummer, wird in der Liste als erste Spalte gezeigt */
|
|
number: number;
|
|
status: OrderStatus;
|
|
type: OrderType;
|
|
restaurant: Restaurant;
|
|
customer: Customer;
|
|
/** Zeitpunkt des Bestelleingangs, ISO-String */
|
|
receivedAt: string;
|
|
/** Gewünschte Liefer- bzw. Abholzeit, ISO-String */
|
|
dueAt: string;
|
|
total: number;
|
|
itemCount: number;
|
|
payment: PaymentMethod;
|
|
pdfUrl: string;
|
|
/** Bestellung im externen Backoffice, öffnet in neuem Tab. */
|
|
externalUrl: string;
|
|
note?: string;
|
|
/** Zeitpunkt, zu dem der Gastronom die Bestellung bestätigt hat. */
|
|
confirmedAt?: string;
|
|
/** Manuell ausgelöste Schritte, ältester zuerst. */
|
|
events: OrderEvent[];
|
|
};
|
|
|
|
export const STATUS_LABEL: Record<OrderStatus, string> = {
|
|
neu: "Neu",
|
|
uebermittelt: "Übermittelt",
|
|
telefonisch: "Telefonisch übermittelt",
|
|
nicht_uebermittelt: "Nicht übermittelt",
|
|
rueckruf: "Rückrufhinweis",
|
|
storniert: "Storniert",
|
|
fake: "Fake",
|
|
};
|
|
|
|
export const TYPE_LABEL: Record<OrderType, string> = {
|
|
vorbestellung: "Vorbestellung",
|
|
direktanruf: "Direktanruf",
|
|
};
|
|
|
|
export const PAYMENT_LABEL: Record<PaymentMethod, string> = {
|
|
bar: "Bar",
|
|
ec: "EC-Karte",
|
|
paypal: "PayPal",
|
|
kreditkarte: "Kreditkarte",
|
|
};
|
|
|
|
/** Abstand zwischen den drei automatischen Übermittlungsversuchen. */
|
|
export const ATTEMPT_INTERVAL_MS = 5 * 60 * 1000;
|
|
export const ATTEMPT_COUNT = 3;
|
|
|
|
/** Nach dieser Zeit ohne Bearbeitung gilt eine Bestellung als nicht übermittelt. */
|
|
export const UNDELIVERED_AFTER_MS = 15 * 60 * 1000;
|
|
|
|
/**
|
|
* Eine unbearbeitete Bestellung kippt nach {@link UNDELIVERED_AFTER_MS} automatisch auf
|
|
* `nicht_uebermittelt`. Sobald jemand eine Aktion ausgelöst hat, bleibt der echte Status stehen.
|
|
*/
|
|
export function effectiveStatus(order: Order, now: number): OrderStatus {
|
|
const overdue = now - new Date(order.receivedAt).getTime() >= UNDELIVERED_AFTER_MS;
|
|
return order.status === "neu" && overdue ? "nicht_uebermittelt" : order.status;
|
|
}
|