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:
180
src/components/orders/actions.ts
Normal file
180
src/components/orders/actions.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
import {
|
||||
Ban,
|
||||
ExternalLink,
|
||||
MessageCircle,
|
||||
PhoneCall,
|
||||
PhoneForwarded,
|
||||
ShieldAlert,
|
||||
Smartphone,
|
||||
Voicemail,
|
||||
XCircle,
|
||||
type LucideIcon,
|
||||
} from "lucide-react";
|
||||
|
||||
import { telHref, whatsAppHref } from "@/lib/format";
|
||||
import type { Order, OrderAction, OrderStatus } from "@/lib/types";
|
||||
|
||||
export type { OrderAction };
|
||||
|
||||
/** Status, den eine Aktion auf der Bestellung setzt. Aktionen ohne Eintrag ändern nichts. */
|
||||
export const ACTION_STATUS: Partial<Record<OrderAction, OrderStatus>> = {
|
||||
uebermittelt: "uebermittelt",
|
||||
nicht_uebermittelt: "nicht_uebermittelt",
|
||||
rueckruf: "rueckruf",
|
||||
storno: "storniert",
|
||||
fake: "fake",
|
||||
stornieren: "storniert",
|
||||
sos: "storniert",
|
||||
telefonisch_uebermittelt: "telefonisch",
|
||||
};
|
||||
|
||||
/** Aktionen, nach denen der Gastronom als bestätigt gilt. */
|
||||
export const CONFIRMING_ACTIONS: OrderAction[] = ["uebermittelt", "telefonisch_uebermittelt"];
|
||||
|
||||
export const ACTION_TOAST: Partial<Record<OrderAction, string>> = {
|
||||
uebermittelt: "Als übermittelt markiert",
|
||||
gastronom_voice: "Voice-Message an Gastronom gesendet",
|
||||
nicht_uebermittelt: "Als nicht übermittelt markiert – Kunde und Gastronom informiert",
|
||||
rueckruf: "Rückrufhinweis an Kunden gesendet",
|
||||
storno: "Bestellung storniert – Kunde per E-Mail informiert",
|
||||
stornieren: "Bestellung storniert",
|
||||
fake: "Als Fake markiert",
|
||||
gastronom_anruf: "Direktanruf beim Gastronom vermerkt",
|
||||
sos: "SOS ausgelöst – Bestellung storniert, Gastronom für heute geschlossen",
|
||||
telefonisch_uebermittelt: "Als telefonisch übermittelt markiert",
|
||||
};
|
||||
|
||||
export type ActionDef = {
|
||||
key: string;
|
||||
label: string;
|
||||
/** Kurzform für die Schnellleiste auf der Karte. */
|
||||
short: string;
|
||||
icon: LucideIcon;
|
||||
/** Gesetzt bei Links (Telefon, WhatsApp, ORDER WebApp). */
|
||||
href?: string;
|
||||
external?: boolean;
|
||||
/** Reiner Telefonanschluss – steckt bereits im Anruf-Dialog. */
|
||||
phone?: boolean;
|
||||
/** Gesetzt bei Aktionen, die den Bestellstatus ändern oder eine Nachricht auslösen. */
|
||||
action?: OrderAction;
|
||||
destructive?: boolean;
|
||||
};
|
||||
|
||||
/** Kontaktwege zum Gastronomen – Buttons 1–6 der alten Oberfläche. */
|
||||
export function gastronomActions({ restaurant }: Order): ActionDef[] {
|
||||
const defs: ActionDef[] = [
|
||||
{
|
||||
key: "phone1",
|
||||
label: `Telefon 1 · ${restaurant.phone1}`,
|
||||
short: "Telefon 1",
|
||||
phone: true,
|
||||
icon: PhoneCall,
|
||||
href: telHref(restaurant.phone1),
|
||||
action: "gastronom_anruf",
|
||||
},
|
||||
];
|
||||
|
||||
if (restaurant.phone2) {
|
||||
defs.push({
|
||||
key: "phone2",
|
||||
label: `Telefon 2 · ${restaurant.phone2}`,
|
||||
short: "Telefon 2",
|
||||
phone: true,
|
||||
icon: PhoneCall,
|
||||
href: telHref(restaurant.phone2),
|
||||
});
|
||||
}
|
||||
if (restaurant.mobile) {
|
||||
defs.push({
|
||||
key: "mobile",
|
||||
label: `Mobil · ${restaurant.mobile}`,
|
||||
short: "Mobil",
|
||||
phone: true,
|
||||
icon: Smartphone,
|
||||
href: telHref(restaurant.mobile),
|
||||
});
|
||||
}
|
||||
|
||||
defs.push({
|
||||
key: "voice",
|
||||
label: "Voice-Message senden",
|
||||
short: "Voice",
|
||||
icon: Voicemail,
|
||||
action: "gastronom_voice",
|
||||
});
|
||||
|
||||
if (restaurant.mobile) {
|
||||
defs.push({
|
||||
key: "whatsapp",
|
||||
label: "WhatsApp-Message senden",
|
||||
short: "WhatsApp",
|
||||
icon: MessageCircle,
|
||||
href: whatsAppHref(restaurant.mobile),
|
||||
external: true,
|
||||
});
|
||||
}
|
||||
|
||||
defs.push({
|
||||
key: "order",
|
||||
label: "ORDER WebApp öffnen",
|
||||
short: "ORDER",
|
||||
icon: ExternalLink,
|
||||
href: restaurant.orderUrl,
|
||||
external: true,
|
||||
});
|
||||
|
||||
return defs;
|
||||
}
|
||||
|
||||
/** Bearbeitungsschritte zur Bestellung – Buttons 7–13 der alten Oberfläche. */
|
||||
export function bearbeitungActions({ customer }: Order): ActionDef[] {
|
||||
return [
|
||||
{
|
||||
key: "call_customer",
|
||||
label: `Kunde anrufen · ${customer.phone}`,
|
||||
short: "Kunde anrufen",
|
||||
phone: true,
|
||||
icon: PhoneCall,
|
||||
href: telHref(customer.phone),
|
||||
action: "kunde_anrufen",
|
||||
},
|
||||
{
|
||||
key: "phone_transfer",
|
||||
label: "Telefonisch übermittelt",
|
||||
short: "Telefonisch",
|
||||
icon: PhoneForwarded,
|
||||
action: "telefonisch_uebermittelt",
|
||||
},
|
||||
{
|
||||
key: "not_transferred",
|
||||
label: "Nicht übermittelt",
|
||||
short: "Nicht übermittelt",
|
||||
icon: Ban,
|
||||
action: "nicht_uebermittelt",
|
||||
},
|
||||
{
|
||||
key: "callback",
|
||||
label: "Rückrufhinweis senden",
|
||||
short: "Rückruf",
|
||||
icon: PhoneForwarded,
|
||||
action: "rueckruf",
|
||||
},
|
||||
{ key: "fake", label: "Als Fake markieren", short: "Fake", icon: ShieldAlert, action: "fake" },
|
||||
{
|
||||
key: "storno",
|
||||
label: "Storno (Kunde per E-Mail)",
|
||||
short: "Storno",
|
||||
icon: XCircle,
|
||||
action: "storno",
|
||||
destructive: true,
|
||||
},
|
||||
{
|
||||
key: "cancel",
|
||||
label: "Stornieren (ohne E-Mail)",
|
||||
short: "Stornieren",
|
||||
icon: Ban,
|
||||
action: "stornieren",
|
||||
destructive: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user