Add order management UI
Rebuild the landing page as an order overview for the Speisekarte24 tool: - Two tabs: all orders and "Nicht übermittelt", the latter fed by the 15-minute rule and highlighted while it holds entries - Table, list and grid views behind an icon dropdown, plus search, filters, sorting and pagination - Escalation timeline (three transmissions, tag, voice message, direct call, SOS) rendered in the detail dialog, inline in the list view and condensed to the current step in the table - Action definitions live in one module and feed the row menu, the detail dialog and the card quick-action bar Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
109
src/lib/types.ts
Normal file
109
src/lib/types.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
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;
|
||||
orderUrl: string;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user