Add top nav bar and Heimservice page
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.
This commit is contained in:
21
src/app/heimservice/page.tsx
Normal file
21
src/app/heimservice/page.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { RestaurantPanel } from "@/components/heimservice/restaurant-panel";
|
||||||
|
import { restaurants } from "@/lib/data";
|
||||||
|
|
||||||
|
export default function HeimservicePage() {
|
||||||
|
return (
|
||||||
|
<main className="mx-auto w-full max-w-[1600px] flex-1 px-6 py-8">
|
||||||
|
<header className="mb-6">
|
||||||
|
<h1 className="text-2xl font-semibold tracking-tight">Heimservice</h1>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Zeitfenster, Zugangsdaten und Rufnummern je Gastronom verwalten.
|
||||||
|
</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||||
|
{restaurants.map((restaurant) => (
|
||||||
|
<RestaurantPanel key={restaurant.name} restaurant={restaurant} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import { Geist, Geist_Mono } from "next/font/google";
|
|||||||
|
|
||||||
import { Toaster } from "@/components/ui/sonner";
|
import { Toaster } from "@/components/ui/sonner";
|
||||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||||
|
import { TopNav } from "@/components/top-nav";
|
||||||
|
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
|
||||||
@@ -28,7 +29,10 @@ export default function RootLayout({ children }: LayoutProps<"/">) {
|
|||||||
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
|
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
|
||||||
>
|
>
|
||||||
<body className="min-h-full flex flex-col bg-background text-foreground">
|
<body className="min-h-full flex flex-col bg-background text-foreground">
|
||||||
<TooltipProvider delay={300}>{children}</TooltipProvider>
|
<TooltipProvider delay={300}>
|
||||||
|
<TopNav />
|
||||||
|
{children}
|
||||||
|
</TooltipProvider>
|
||||||
{/* App läuft bewusst nur im Light Mode – Systemtheme nicht übernehmen. */}
|
{/* App läuft bewusst nur im Light Mode – Systemtheme nicht übernehmen. */}
|
||||||
<Toaster theme="light" />
|
<Toaster theme="light" />
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
328
src/components/heimservice/restaurant-panel.tsx
Normal file
328
src/components/heimservice/restaurant-panel.tsx
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
import {
|
||||||
|
Check,
|
||||||
|
ExternalLink,
|
||||||
|
KeyRound,
|
||||||
|
Pencil,
|
||||||
|
Phone,
|
||||||
|
PhoneCall,
|
||||||
|
Power,
|
||||||
|
Smartphone,
|
||||||
|
Voicemail,
|
||||||
|
type LucideIcon,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogAction,
|
||||||
|
AlertDialogCancel,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogTitle,
|
||||||
|
} from "@/components/ui/alert-dialog";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogClose,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label";
|
||||||
|
import { telHref } from "@/lib/format";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import type { Restaurant } from "@/lib/types";
|
||||||
|
|
||||||
|
type Props = { restaurant: Restaurant };
|
||||||
|
|
||||||
|
/** Ein Kontroll-Panel je Gastronom – Nachbau der 10 Buttons der alten Heimservice-Oberfläche. */
|
||||||
|
export function RestaurantPanel({ restaurant }: Props) {
|
||||||
|
const [windowOn, setWindowOn] = React.useState<[boolean, boolean]>([true, true]);
|
||||||
|
const [resetOpen, setResetOpen] = React.useState(false);
|
||||||
|
const [editOpen, setEditOpen] = React.useState(false);
|
||||||
|
const [callOpen, setCallOpen] = React.useState(false);
|
||||||
|
const [mobile1, setMobile1] = React.useState(restaurant.mobile ?? "");
|
||||||
|
const [mobile2, setMobile2] = React.useState(restaurant.mobile2 ?? "");
|
||||||
|
|
||||||
|
const numbers = [
|
||||||
|
{ label: "Telefon 1", phone: restaurant.phone1, icon: PhoneCall },
|
||||||
|
restaurant.phone2 && { label: "Telefon 2", phone: restaurant.phone2, icon: PhoneCall },
|
||||||
|
mobile1 && { label: "Mobil 1", phone: mobile1, icon: Smartphone },
|
||||||
|
mobile2 && { label: "Mobil 2", phone: mobile2, icon: Smartphone },
|
||||||
|
].filter((n): n is { label: string; phone: string; icon: LucideIcon } => Boolean(n));
|
||||||
|
|
||||||
|
const [window1, window2] = restaurant.heimserviceWindows;
|
||||||
|
|
||||||
|
function toggleWindow(index: 0 | 1) {
|
||||||
|
setWindowOn((prev) => {
|
||||||
|
const next: [boolean, boolean] = [...prev];
|
||||||
|
next[index] = !next[index];
|
||||||
|
toast.success(
|
||||||
|
next[index] ? `Zeitfenster ${index + 1} aktiviert` : `Zeitfenster ${index + 1} deaktiviert`,
|
||||||
|
{ description: restaurant.name },
|
||||||
|
);
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetPassword() {
|
||||||
|
setResetOpen(false);
|
||||||
|
toast.success("Passwort zurückgesetzt", {
|
||||||
|
description: `Neue Zugangsdaten für ${restaurant.name} wurden versendet.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveMobiles() {
|
||||||
|
setEditOpen(false);
|
||||||
|
toast.success("Mobilrufnummern aktualisiert", { description: restaurant.name });
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendVoiceTest() {
|
||||||
|
toast.success("Voice-Message an Telefon 1 gesendet", { description: restaurant.name });
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="text-left text-base">{restaurant.name}</CardTitle>
|
||||||
|
<p className="mt-0.5 text-left text-sm text-muted-foreground">{restaurant.city}</p>
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
|
<CardContent className="flex flex-col gap-4">
|
||||||
|
<div className="flex flex-wrap gap-1.5 text-xs text-muted-foreground">
|
||||||
|
<span>
|
||||||
|
Zeitfenster 1: <span className="tabular-nums">{window1}</span>
|
||||||
|
</span>
|
||||||
|
{window2 && (
|
||||||
|
<>
|
||||||
|
<span aria-hidden>·</span>
|
||||||
|
<span>
|
||||||
|
Zeitfenster 2: <span className="tabular-nums">{window2}</span>
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Section label="Verfügbarkeit">
|
||||||
|
<WindowToggle
|
||||||
|
index={0}
|
||||||
|
time={window1}
|
||||||
|
on={windowOn[0]}
|
||||||
|
onToggle={() => toggleWindow(0)}
|
||||||
|
/>
|
||||||
|
<WindowToggle
|
||||||
|
index={1}
|
||||||
|
time={window2}
|
||||||
|
on={windowOn[1]}
|
||||||
|
onToggle={() => toggleWindow(1)}
|
||||||
|
disabled={!window2}
|
||||||
|
/>
|
||||||
|
<Tile
|
||||||
|
icon={ExternalLink}
|
||||||
|
label="ORDER WebApp"
|
||||||
|
sublabel="Öffnen"
|
||||||
|
nativeButton={false}
|
||||||
|
render={<a href={restaurant.orderUrl} target="_blank" rel="noreferrer" />}
|
||||||
|
/>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<Section label="Zugang">
|
||||||
|
<Tile
|
||||||
|
icon={KeyRound}
|
||||||
|
label="Benutzer / Passwort"
|
||||||
|
sublabel="Zurücksetzen"
|
||||||
|
onClick={() => setResetOpen(true)}
|
||||||
|
/>
|
||||||
|
<Tile
|
||||||
|
icon={Pencil}
|
||||||
|
label="Mobilrufnummern"
|
||||||
|
sublabel="Bearbeiten"
|
||||||
|
onClick={() => setEditOpen(true)}
|
||||||
|
/>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<Section label="Kontakt">
|
||||||
|
<Tile
|
||||||
|
icon={PhoneCall}
|
||||||
|
label="Anrufen"
|
||||||
|
sublabel={`${numbers.length} ${numbers.length === 1 ? "Nummer" : "Nummern"}`}
|
||||||
|
onClick={() => setCallOpen(true)}
|
||||||
|
/>
|
||||||
|
<Tile
|
||||||
|
icon={Voicemail}
|
||||||
|
label="Voice-Message"
|
||||||
|
sublabel={`Telefon 1 · ${restaurant.phone1}`}
|
||||||
|
onClick={sendVoiceTest}
|
||||||
|
/>
|
||||||
|
</Section>
|
||||||
|
</CardContent>
|
||||||
|
|
||||||
|
<Dialog open={callOpen} onOpenChange={setCallOpen}>
|
||||||
|
<DialogContent className="sm:max-w-md">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Anrufen</DialogTitle>
|
||||||
|
<DialogDescription>{restaurant.name}</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
{numbers.map(({ label, phone, icon: Icon }) => (
|
||||||
|
<Button
|
||||||
|
key={label}
|
||||||
|
variant="outline"
|
||||||
|
className="justify-start"
|
||||||
|
nativeButton={false}
|
||||||
|
render={<a href={telHref(phone)} />}
|
||||||
|
>
|
||||||
|
<Icon />
|
||||||
|
<span className="truncate text-left">
|
||||||
|
{label}
|
||||||
|
<span className="ml-1.5 text-muted-foreground tabular-nums">{phone}</span>
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
|
<AlertDialog open={resetOpen} onOpenChange={setResetOpen}>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>Passwort zurücksetzen?</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription>
|
||||||
|
Die ORDER-Zugangsdaten von {restaurant.name} werden neu vergeben und an den
|
||||||
|
Gastronomen versendet.
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel>Abbrechen</AlertDialogCancel>
|
||||||
|
<AlertDialogAction onClick={resetPassword}>Zurücksetzen</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
|
|
||||||
|
<Dialog open={editOpen} onOpenChange={setEditOpen}>
|
||||||
|
<DialogContent className="sm:max-w-md">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Mobilrufnummern bearbeiten</DialogTitle>
|
||||||
|
<DialogDescription>{restaurant.name}</DialogDescription>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<div className="flex flex-col gap-3">
|
||||||
|
<div className="flex flex-col gap-1.5">
|
||||||
|
<Label htmlFor={`mobile1-${restaurant.name}`}>Mobil 1</Label>
|
||||||
|
<Input
|
||||||
|
id={`mobile1-${restaurant.name}`}
|
||||||
|
value={mobile1}
|
||||||
|
onChange={(e) => setMobile1(e.target.value)}
|
||||||
|
placeholder="+49 …"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-1.5">
|
||||||
|
<Label htmlFor={`mobile2-${restaurant.name}`}>Mobil 2</Label>
|
||||||
|
<Input
|
||||||
|
id={`mobile2-${restaurant.name}`}
|
||||||
|
value={mobile2}
|
||||||
|
onChange={(e) => setMobile2(e.target.value)}
|
||||||
|
placeholder="+49 …"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DialogFooter>
|
||||||
|
<DialogClose render={<Button variant="outline" />}>Abbrechen</DialogClose>
|
||||||
|
<Button onClick={saveMobiles}>
|
||||||
|
<Check />
|
||||||
|
Speichern
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Section({ label, children }: { label: string; children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<p className="text-xs font-medium text-muted-foreground uppercase">{label}</p>
|
||||||
|
<div className="grid grid-cols-2 gap-2">{children}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function WindowToggle({
|
||||||
|
index,
|
||||||
|
time,
|
||||||
|
on,
|
||||||
|
onToggle,
|
||||||
|
disabled,
|
||||||
|
}: {
|
||||||
|
index: 0 | 1;
|
||||||
|
time: string | undefined;
|
||||||
|
on: boolean;
|
||||||
|
onToggle: () => void;
|
||||||
|
disabled?: boolean;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={onToggle}
|
||||||
|
className={cn(
|
||||||
|
"h-auto w-full flex-col items-start gap-0.5 py-2 whitespace-normal",
|
||||||
|
!disabled && on && "border-emerald-500/40 bg-emerald-500/10 hover:bg-emerald-500/15",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span className="flex w-full items-center gap-1.5 text-left text-sm font-medium">
|
||||||
|
<Power className={on && !disabled ? "text-emerald-600" : undefined} />
|
||||||
|
Zeitfenster {index + 1}
|
||||||
|
</span>
|
||||||
|
<span className="w-full text-left text-xs font-normal text-muted-foreground tabular-nums">
|
||||||
|
{disabled ? "kein Zeitfenster" : `${time} · ${on ? "aktiv" : "pausiert"}`}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Tile({
|
||||||
|
icon: Icon,
|
||||||
|
label,
|
||||||
|
sublabel,
|
||||||
|
onClick,
|
||||||
|
nativeButton,
|
||||||
|
render,
|
||||||
|
}: {
|
||||||
|
icon: typeof Phone;
|
||||||
|
label: string;
|
||||||
|
sublabel: string;
|
||||||
|
onClick?: () => void;
|
||||||
|
nativeButton?: boolean;
|
||||||
|
render?: React.ReactElement;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
className="h-auto w-full flex-col items-start gap-0.5 py-2 whitespace-normal"
|
||||||
|
onClick={onClick}
|
||||||
|
nativeButton={nativeButton}
|
||||||
|
render={render}
|
||||||
|
>
|
||||||
|
<span className="flex w-full items-center gap-1.5 text-left text-sm font-medium">
|
||||||
|
<Icon />
|
||||||
|
{label}
|
||||||
|
</span>
|
||||||
|
<span className="w-full truncate text-left text-xs font-normal text-muted-foreground">
|
||||||
|
{sublabel}
|
||||||
|
</span>
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
55
src/components/top-nav.tsx
Normal file
55
src/components/top-nav.tsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
|
import { usePathname } from "next/navigation";
|
||||||
|
import { LogOut } from "lucide-react";
|
||||||
|
import { toast } from "sonner";
|
||||||
|
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
const NAV_ITEMS = [
|
||||||
|
{ href: "/", label: "Bestellübersicht" },
|
||||||
|
{ href: "/heimservice", label: "Heimservice" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export function TopNav() {
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<header className="border-b bg-background">
|
||||||
|
<div className="mx-auto flex h-14 w-full max-w-[1600px] items-center gap-6 px-6">
|
||||||
|
<span className="text-sm font-semibold tracking-tight">Speisekarte24</span>
|
||||||
|
|
||||||
|
<nav className="flex items-center gap-1">
|
||||||
|
{NAV_ITEMS.map((item) => {
|
||||||
|
const active = pathname === item.href;
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={item.href}
|
||||||
|
href={item.href}
|
||||||
|
className={cn(
|
||||||
|
"rounded-md px-3 py-1.5 text-sm font-medium transition-colors",
|
||||||
|
active
|
||||||
|
? "bg-muted text-foreground"
|
||||||
|
: "text-muted-foreground hover:bg-muted hover:text-foreground",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{item.label}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
className="ml-auto"
|
||||||
|
onClick={() => toast.success("Abgemeldet")}
|
||||||
|
>
|
||||||
|
<LogOut />
|
||||||
|
Abmelden
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
type Restaurant,
|
type Restaurant,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
const restaurants: Restaurant[] = [
|
export const restaurants: Restaurant[] = [
|
||||||
{
|
{
|
||||||
name: "Muster Ristorante",
|
name: "Muster Ristorante",
|
||||||
city: "Taben-Rodt",
|
city: "Taben-Rodt",
|
||||||
@@ -15,7 +15,9 @@ const restaurants: Restaurant[] = [
|
|||||||
phone1: "+49 6582 123456",
|
phone1: "+49 6582 123456",
|
||||||
phone2: "+49 6582 123457",
|
phone2: "+49 6582 123457",
|
||||||
mobile: "+49 171 2223344",
|
mobile: "+49 171 2223344",
|
||||||
|
mobile2: "+49 171 2223345",
|
||||||
orderUrl: "https://order.speisekarte24.de/muster-ristorante",
|
orderUrl: "https://order.speisekarte24.de/muster-ristorante",
|
||||||
|
heimserviceWindows: ["10:00 bis 14:30", "17:00 bis 23:00"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Pizzeria Da Vinci",
|
name: "Pizzeria Da Vinci",
|
||||||
@@ -24,6 +26,7 @@ const restaurants: Restaurant[] = [
|
|||||||
phone1: "+49 651 998877",
|
phone1: "+49 651 998877",
|
||||||
mobile: "+49 160 5544332",
|
mobile: "+49 160 5544332",
|
||||||
orderUrl: "https://order.speisekarte24.de/da-vinci",
|
orderUrl: "https://order.speisekarte24.de/da-vinci",
|
||||||
|
heimserviceWindows: ["11:00 bis 14:30", "17:30 bis 22:30"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Sushi Yama",
|
name: "Sushi Yama",
|
||||||
@@ -32,7 +35,9 @@ const restaurants: Restaurant[] = [
|
|||||||
phone1: "+49 681 445566",
|
phone1: "+49 681 445566",
|
||||||
phone2: "+49 681 445567",
|
phone2: "+49 681 445567",
|
||||||
mobile: "+49 175 1112233",
|
mobile: "+49 175 1112233",
|
||||||
|
mobile2: "+49 175 1112234",
|
||||||
orderUrl: "https://order.speisekarte24.de/sushi-yama",
|
orderUrl: "https://order.speisekarte24.de/sushi-yama",
|
||||||
|
heimserviceWindows: ["12:00 bis 15:00", "18:00 bis 23:00"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Burger Station",
|
name: "Burger Station",
|
||||||
@@ -41,6 +46,7 @@ const restaurants: Restaurant[] = [
|
|||||||
phone1: "+49 6501 334455",
|
phone1: "+49 6501 334455",
|
||||||
mobile: "+49 152 9988776",
|
mobile: "+49 152 9988776",
|
||||||
orderUrl: "https://order.speisekarte24.de/burger-station",
|
orderUrl: "https://order.speisekarte24.de/burger-station",
|
||||||
|
heimserviceWindows: ["11:30 bis 15:00", "17:00 bis 21:00"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Athen Grill",
|
name: "Athen Grill",
|
||||||
@@ -48,6 +54,8 @@ const restaurants: Restaurant[] = [
|
|||||||
hours: "17:00 bis 23:30",
|
hours: "17:00 bis 23:30",
|
||||||
phone1: "+49 6571 220011",
|
phone1: "+49 6571 220011",
|
||||||
orderUrl: "https://order.speisekarte24.de/athen-grill",
|
orderUrl: "https://order.speisekarte24.de/athen-grill",
|
||||||
|
// Nur Abendschicht, kein zweites Zeitfenster.
|
||||||
|
heimserviceWindows: ["17:00 bis 23:30", undefined],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,10 @@ export type Restaurant = {
|
|||||||
phone1: string;
|
phone1: string;
|
||||||
phone2?: string;
|
phone2?: string;
|
||||||
mobile?: string;
|
mobile?: string;
|
||||||
|
mobile2?: string;
|
||||||
orderUrl: string;
|
orderUrl: string;
|
||||||
|
/** Zeitfenster für den Heimservice, z. B. Mittags- und Abendschicht. Zweites Fenster optional. */
|
||||||
|
heimserviceWindows: [string, string | undefined];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Customer = {
|
export type Customer = {
|
||||||
|
|||||||
Reference in New Issue
Block a user