Implement booking cancellation feature and refactor booking fetching logic

This commit is contained in:
Alex Polo
2025-10-31 22:23:59 +01:00
parent b2e326c309
commit baec54814d
6 changed files with 139 additions and 499 deletions

View File

@@ -17,3 +17,15 @@
2025-10-31T20:54:03: > Ready on http://localhost:3000
2025-10-31T21:04:52: > Ready on http://localhost:3000
2025-10-31T21:07:41: > Ready on http://localhost:3000
2025-10-31T21:31:48: > Ready on http://localhost:3000
2025-10-31T21:34:11: > Ready on http://localhost:3000
2025-10-31T21:39:45: > Ready on http://localhost:3000
2025-10-31T21:40:36: > Ready on http://localhost:3000
2025-10-31T21:45:41: > Ready on http://localhost:3000
2025-10-31T21:47:51: > Ready on http://localhost:3000
2025-10-31T21:50:59: > Ready on http://localhost:3000
2025-10-31T21:58:30: > Ready on http://localhost:3000
2025-10-31T22:14:18: > Ready on http://localhost:3000
2025-10-31T22:17:17: > Ready on http://localhost:3000
2025-10-31T22:21:03: > Ready on http://localhost:3000
2025-10-31T22:22:22: > Ready on http://localhost:3000

View File

@@ -14,22 +14,54 @@ type Booking = {
menu_id?: string
package_id?: string
message?: string
status?: string
}
export default function BookingsClient() {
const [bookings, setBookings] = useState<Booking[]>([])
const [loading, setLoading] = useState(true)
const [cancelling, setCancelling] = useState<string | null>(null)
async function fetchBookings() {
try {
const r = await fetch('/api/bookings')
const data = await r.ok ? await r.json() : []
setBookings(Array.isArray(data) ? data : [])
} catch (err) {
console.error(err)
} finally {
setLoading(false)
}
}
useEffect(() => {
let mounted = true
fetch('/api/bookings')
.then(r => r.ok ? r.json() : [])
.then(data => { if (mounted) setBookings(Array.isArray(data) ? data : []) })
.catch(err => console.error(err))
.finally(() => { if (mounted) setLoading(false) })
return () => { mounted = false }
fetchBookings()
}, [])
async function cancelBooking(id: string, name: string) {
if (!confirm(`Annuller booking for ${name}?\n\nDette kan ikke fortrydes.`)) return
setCancelling(id)
try {
const res = await fetch('/api/bookings', {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id })
})
if (res.ok) {
setBookings(prev => prev.filter(b => b.id !== id))
alert(`Booking for ${name} er annulleret`)
} else {
alert('Kunne ikke annullere booking — prøv igen')
}
} catch (err) {
alert('Fejl ved annullering — prøv igen')
} finally {
setCancelling(null)
}
}
if (loading) return <div>Indlæser bookinger...</div>
if (bookings.length === 0) return <div>Ingen bookinger fundet.</div>
@@ -44,17 +76,30 @@ export default function BookingsClient() {
<th className="p-2">Kuverter</th>
<th className="p-2">Menu</th>
<th className="p-2">Note</th>
<th className="p-2">Handling</th>
</tr>
</thead>
<tbody>
{bookings.map(b => (
<tr key={b.id} className="border-t">
<tr key={b.id} className="border-t hover:bg-gray-50">
<td className="p-2 align-top">{b.booking_date}</td>
<td className="p-2 align-top">{b.start_time.slice(0,5)} - {b.end_time.slice(0,5)}</td>
<td className="p-2 align-top">{b.name}<div className="text-sm text-primary-600">{b.email}{b.phone ? ` · ${b.phone}` : ''}</div></td>
<td className="p-2 align-top">
<div>{b.name}</div>
<div className="text-sm text-primary-600">{b.email}{b.phone ? ` · ${b.phone}` : ''}</div>
</td>
<td className="p-2 align-top">{b.party_size || '-'}</td>
<td className="p-2 align-top">{b.menu_id || b.package_id || '-'}</td>
<td className="p-2 align-top">{b.message || '-'}</td>
<td className="p-2 align-top">
<button
onClick={() => cancelBooking(b.id, b.name)}
disabled={cancelling === b.id}
className="px-3 py-1 bg-red-500 text-white rounded text-sm hover:bg-red-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
>
{cancelling === b.id ? 'Annullerer...' : 'Annuller'}
</button>
</td>
</tr>
))}
</tbody>

View File

@@ -75,3 +75,24 @@ export async function POST(req: Request) {
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 })
}
}
export async function DELETE(req: Request) {
try {
const body = await req.json()
const { id } = body
if (!id) {
return NextResponse.json({ error: 'Missing booking id' }, { status: 400 })
}
const [result] = await pool.query('DELETE FROM bookings WHERE id = ?', [id])
// @ts-ignore
if (result.affectedRows === 0) {
return NextResponse.json({ error: 'Booking not found' }, { status: 404 })
}
return NextResponse.json({ ok: true })
} catch (error) {
console.error('Error deleting booking', error)
return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 })
}
}

View File

@@ -1,16 +1,25 @@
'use client'
import { sampleData } from '@/config/sample-data'
import { Menu } from '@/types/models'
import { Menu, Package } from '@/types/models'
import { ImagePlaceholder } from '@/components/ui/image-placeholder'
import { cn } from '@/lib/utils'
import { ArrowRight } from 'lucide-react'
export function FeaturedMenus() {
const { packages, menus } = sampleData
// Prefer menus explicitly marked as featured. Fall back to first 4 if none set.
// Force rebuild - Prefer menus explicitly marked as featured
const featuredMenus = (menus.filter((m: Menu) => (m as any).featured) as Menu[])
.slice(0, 4)
.length > 0 ? (menus.filter((m: Menu) => (m as any).featured) as Menu[]).slice(0, 4) : menus.slice(0, 4)
console.log('=== FeaturedMenus Debug v2 ===')
console.log('Total menus:', menus.length)
console.log('Featured menus:', featuredMenus.map(m => m.id))
console.log('Total packages:', packages.length)
console.log('Package menuIds:', packages.map((p: Package) => p.menuId))
return (
<section className="py-16 bg-primary-50">
<div className="container mx-auto px-4">
@@ -24,7 +33,8 @@ export function FeaturedMenus() {
</div>
<div className="grid md:grid-cols-2 gap-8 max-w-5xl mx-auto">
{featuredMenus.map((menu: Menu) => {
const basePackage = packages.find((p: { menuId: string }) => p.menuId === menu.id)
const basePackage = packages.find((p: Package) => p.menuId === menu.id)
console.log(`Menu: ${menu.id}, Package found: ${basePackage?.id}, Price: ${basePackage?.pricePerCover}`)
return (
<MenuCard
key={menu.id}

View File

@@ -1,464 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.sampleData = void 0;
exports.sampleData = {
rates: {
chefHourly: 350,
helperHourly: 200,
serverHourly: 150,
travelPerKm: 4.5,
minServerHours: 4
},
staffPool: {
helpers: [
{ id: 'h1', name: 'Ekstra køkkenhjælp', maxHours: 8 }
],
servers: [
{ id: 's1', name: 'Servering 1' },
{ id: 's2', name: 'Servering 2' }
],
minHours: 4,
autoAssignRules: {
serveringHoursFunction: 'ceil(covers / 25) * 4',
helpersThreshold: [
{ maxCovers: 35, helpers: 0 },
{ maxCovers: 60, helpers: 1 },
{ maxCovers: 80, helpers: 2 }
]
}
},
chef: {
id: 'christian-warme',
name: 'Christian Warme',
brand: 'warme.dk',
bio: 'Uddannet kok (34 år). Klassiske selskabsmenuer med ro i køkkenet og fokus på gæsternes oplevelse.',
skills: ['Klassisk dansk', 'Nordisk', 'Italiensk', 'Dessert'],
serviceAreaKm: 80,
baseCity: 'København',
photo: '/img/christian-warme.png',
contact: {
phone: '+45 12 34 56 78',
email: 'kontakt@warme.dk',
instagram: 'https://www.instagram.com/warme.dk',
facebook: 'https://www.facebook.com/warme.dk'
}
},
menus: [
{
id: 'klassisk-3-retter',
title: 'Klassisk Menu',
description: 'En traditionel dansk menu med et moderne twist. Populært valg til familiefester.',
courses: [
'Varmrøget laks med rygeostcreme, dild og radiser',
'Rosastegt kalvefilet med sæsonens grøntsager og rødvinssauce',
'Chokoladekage med bær og vaniljeis'
],
tags: ['Nordisk', 'Klassisk', 'Populær'],
featured: true,
image: '/img/klassisk.png',
baseIngredientsPerCover: 65,
ingredientsCostIndex: 1.0,
baseLabourHours: 4,
incrementalLabourPerCover: 0.05,
minCovers: 15,
leadTimeDays: 7,
basePricePerCover: 295
},
{
id: 'gron-3-retter',
title: 'Grøn Menu',
description: 'Sæsonbaseret vegetarisk menu med fokus på smag og farver. Perfekt til både vegetarer og kødelskere.',
courses: [
'Grillet asparges med hollandaise og karse',
'Svamperisotto med parmesan og urter',
'Pocherede pærer med vaniljeis og hasselnødder'
],
tags: ['Vegetarisk', 'Sæson', 'Populær'],
featured: true,
image: '/img/veg.png',
baseIngredientsPerCover: 55,
ingredientsCostIndex: 0.95,
baseLabourHours: 4,
incrementalLabourPerCover: 0.05,
minCovers: 15,
leadTimeDays: 7,
basePricePerCover: 275
},
{
id: 'italiensk-fest',
title: 'Italiensk Aften',
description: 'Det bedste fra det italienske køkken. Afslappet stemning med mad serveret "family style".',
courses: [
'Antipasti med charcuteri, oliven og bruschetta',
'Hjemmelavet pasta med tomat og basilikum',
'Tiramisu med mascarpone'
],
tags: ['Italiensk', 'Pasta', 'Afslappet'],
featured: true,
image: '/img/italiensk.png',
baseIngredientsPerCover: 55,
ingredientsCostIndex: 0.9,
baseLabourHours: 4,
incrementalLabourPerCover: 0.05,
minCovers: 15,
leadTimeDays: 7,
basePricePerCover: 250
},
{
id: 'grill-menu',
title: 'Grill Menu',
description: 'Lækker grillmenu perfekt til sommeraftener og uformelle selskaber.',
courses: [
'Grillede peberfrugter med hvidløgsdip',
'Marineret kylling og grillet flanksteak',
'Grillede ferskner med vaniljeis'
],
tags: ['Grill', 'Sommer', 'Afslappet'],
image: '/img/grill.png',
baseIngredientsPerCover: 60,
ingredientsCostIndex: 1.0,
baseLabourHours: 4,
incrementalLabourPerCover: 0.05,
minCovers: 20,
leadTimeDays: 7,
basePricePerCover: 275
},
{
id: 'nordisk-buffet',
title: 'Nordisk Buffet',
description: 'Moderne nordisk buffet med sæsonens bedste råvarer. God til større selskaber.',
courses: [
'Koldrøget laks og marinerede sild',
'Frikadeller med nye kartofler',
'Klassisk æblekage med flødeskum'
],
tags: ['Nordisk', 'Buffet', 'Traditionel'],
image: '/img/nordisk.png',
baseIngredientsPerCover: 65,
ingredientsCostIndex: 1.0,
baseLabourHours: 5,
incrementalLabourPerCover: 0.04,
minCovers: 25,
leadTimeDays: 7,
basePricePerCover: 250
},
{
id: 'brunch-klassisk',
title: 'Brunch Menu',
description: 'Klassisk brunch buffet med alt det bedste til en god start på dagen.',
courses: [
'Røræg og bacon',
'Croissanter og rundstykker',
'Yoghurt med müsli og frisk frugt'
],
tags: ['Brunch', 'Morgenmad', 'Buffet'],
image: '/img/brunch.png',
baseIngredientsPerCover: 45,
ingredientsCostIndex: 0.85,
baseLabourHours: 3,
incrementalLabourPerCover: 0.04,
minCovers: 15,
leadTimeDays: 5,
basePricePerCover: 225
},
{
id: 'tapas-menu',
title: 'Tapas Menu',
description: 'Spanske tapas og pinxtos. Perfekt til reception eller let aftensmad.',
courses: [
'Serrano skinke og manchego',
'Tortilla og patatas bravas',
'Churros med chokolade'
],
tags: ['Spansk', 'Tapas', 'Reception'],
image: '/img/tapas.png',
baseIngredientsPerCover: 50,
ingredientsCostIndex: 0.95,
baseLabourHours: 4,
incrementalLabourPerCover: 0.05,
minCovers: 20,
leadTimeDays: 7,
basePricePerCover: 265
},
{
id: 'gourmet-upgrade',
title: 'Gourmet Menu',
description: 'En eksklusiv 4-retters menu til de særlige lejligheder.',
courses: [
'Carpaccio af oksefilet med trøffelolie',
'Jordskokkesuppe med chips',
'Lammeculotte med sæsonens grønt',
'Chokoladefondant med is'
],
tags: ['Gourmet', 'Special', 'Luksus'],
featured: true,
image: '/img/gourmet.png',
baseIngredientsPerCover: 95,
ingredientsCostIndex: 1.2,
baseLabourHours: 5,
incrementalLabourPerCover: 0.06,
minCovers: 10,
leadTimeDays: 7,
basePricePerCover: 495
},
{
id: 'nordisk-signatur',
title: 'Nordisk Signatur',
description: 'En gastronomisk rejse gennem Nordens skatte med 4 serveringer.',
courses: [
'Rimmet kammusling med æble og dild',
'Jordskokkesuppe med trøffel og sprødt',
'Grillet torsk med blomkål og brunet smør',
'Brombærdessert med hvid chokolade'
],
tags: ['Nordisk', 'Fisk', 'Sæson', 'Signatur'],
image: '/img/nordisk.png',
baseIngredientsPerCover: 95,
ingredientsCostIndex: 1.2,
baseLabourHours: 5,
incrementalLabourPerCover: 0.06,
minCovers: 10,
leadTimeDays: 7,
basePricePerCover: 745
},
{
id: 'italiensk-fest',
title: 'Italiensk Fest',
description: 'Det bedste fra det italienske køkken med 4 serveringer.',
courses: [
'Antipasti med charcuteri og lokale oste',
'Hjemmelavet ravioli med ricotta og salvie',
'Braiseret kalvekød med gremolata',
'Tiramisu med mascarpone'
],
tags: ['Italiensk', 'Pasta', 'Gourmet'],
image: '/img/italiensk.png',
baseIngredientsPerCover: 85,
ingredientsCostIndex: 1.1,
baseLabourHours: 5,
incrementalLabourPerCover: 0.05,
minCovers: 10,
leadTimeDays: 7,
basePricePerCover: 695
},
{
id: 'fransk-gourmet',
title: 'Fransk Gourmet',
description: 'Klassisk fransk kogekunst med moderne touch og 5 serveringer.',
courses: [
'Østers med vinaigrette og citron',
'Foie gras med brioche og fignekompot',
'Hummerbisque med cognac',
'Confiteret andelår med pommes sarladaises',
'Crème brûlée med bær'
],
tags: ['Fransk', 'Gourmet', 'Luksus'],
image: '/img/fransk.png',
baseIngredientsPerCover: 125,
ingredientsCostIndex: 1.3,
baseLabourHours: 6,
incrementalLabourPerCover: 0.07,
minCovers: 10,
leadTimeDays: 10,
basePricePerCover: 895
},
{
id: 'tapas-deluxe',
title: 'Tapas Deluxe',
description: 'En luksuriøs aften med spanske specialiteter og 7 serveringer.',
courses: [
'Jamon Iberico de Bellota',
'Grillede rejer med hvidløg',
'Blæksprutteragout i egen blæk',
'Patatas bravas med aioli',
'Pimientos de padrón',
'Albóndigas i tomatsauce',
'Churros med chokolade'
],
tags: ['Spansk', 'Tapas', 'Deleretter', 'Luksus'],
image: '/img/tapas.png',
baseIngredientsPerCover: 110,
ingredientsCostIndex: 1.2,
baseLabourHours: 5,
incrementalLabourPerCover: 0.06,
minCovers: 10,
leadTimeDays: 7,
basePricePerCover: 745
},
{
id: 'julemenu-luksus',
title: 'Julemenu de Luxe',
description: 'En luksuriøs fortolkning af traditionel julemad med 4 serveringer.',
courses: [
'Hjemmelavet gravad laks med sennepsdressing',
'Julesuppe med and og urter',
'Confiteret andelår med rødkål og brunede kartofler',
'Ris a la mande med kirsebærsauce og sprød mandel'
],
tags: ['Jul', 'Dansk', 'Traditionel', 'Luksus'],
image: '/img/jul.png',
baseIngredientsPerCover: 95,
ingredientsCostIndex: 1.15,
baseLabourHours: 5,
incrementalLabourPerCover: 0.06,
minCovers: 10,
leadTimeDays: 14,
basePricePerCover: 695
},
{
id: 'brunch-luksus',
title: 'Luksus Brunch',
description: 'Overdådig brunch med 8 serveringer og bobler.',
courses: [
'Velkomst med mousserende vin og snacks',
'Røræg med trøffel og hjemmebagt brød',
'Røget laks med ærtepuré',
'Charcuteri og nordiske oste',
'Sprøde croissanter og wienerbrød',
'Yoghurt med granola og friske bær',
'Pandekager med ahornsirup',
'Mini-dessertanretning'
],
tags: ['Brunch', 'Morgenmad', 'Luksus'],
image: '/img/brunch.png',
baseIngredientsPerCover: 85,
ingredientsCostIndex: 1.1,
baseLabourHours: 4,
incrementalLabourPerCover: 0.05,
minCovers: 10,
leadTimeDays: 5,
basePricePerCover: 545
},
{
id: 'grill-luksus',
title: 'Gourmet Grill Deluxe',
description: 'Eksklusiv grillmenu med 5 serveringer perfekt til sommeraftener.',
courses: [
'Grillede jomfruhummere med urtemayonnaise',
'Wagyu sliders med trøffel',
'Grillet lammecarré med rosmarin',
'Grillede grøntsager med romesco',
'Grillede ferskner med mascarpone og honning'
],
tags: ['Grill', 'Sommer', 'Kød', 'Luksus'],
image: '/img/grill.png',
baseIngredientsPerCover: 120,
ingredientsCostIndex: 1.3,
baseLabourHours: 5,
incrementalLabourPerCover: 0.06,
minCovers: 10,
leadTimeDays: 7,
basePricePerCover: 845
},
{
id: 'sushi-signatur',
title: 'Signatur Sushi',
description: 'Eksklusiv omakase-inspireret sushimenu med 6 serveringer.',
courses: [
'Toro nigiri med trøffel',
'Laks tataki med ponzu',
'Kammusling nigiri med yuzu',
'Tempura rejer med wasabi-mayo',
'Udvalg af maki rolls',
'Matcha is med hvid chokolade'
],
tags: ['Japansk', 'Sushi', 'Fisk', 'Luksus'],
image: '/img/sushi.png',
baseIngredientsPerCover: 130,
ingredientsCostIndex: 1.4,
baseLabourHours: 6,
incrementalLabourPerCover: 0.07,
minCovers: 10,
leadTimeDays: 7,
basePricePerCover: 895
},
{
id: 'fusion-menu',
title: 'Asian Fusion',
description: 'Moderne asiatisk fusion menu med 5 serveringer.',
courses: [
'Tuna tartare med wasabi og sesam',
'Dumplings med and og hoisin',
'Thai inspireret suppe med kokos',
'Korean BBQ short ribs',
'Yuzu pannacotta med mango'
],
tags: ['Asiatisk', 'Fusion', 'Modern'],
image: '/img/fusion.png',
baseIngredientsPerCover: 95,
ingredientsCostIndex: 1.2,
baseLabourHours: 5,
incrementalLabourPerCover: 0.06,
minCovers: 10,
leadTimeDays: 7,
basePricePerCover: 745
}
],
packages: [
{
id: "basis",
name: "Basis Pakke",
menuId: "klassisk-3-retter",
pricePerCover: 295,
minCovers: 15,
maxCovers: 30,
includes: [
"Menu planlægning",
"Indkøb af råvarer",
"Madlavning på lokation",
"Let oprydning i køkken"
],
upsells: [
{ id: "anretning", label: "Tallerkenanretning", priceFlat: 500 },
{ id: "kaffe", label: "Kaffe/te", pricePerCover: 25 }
],
notes: "God basis løsning til familiefester. Maden serveres på fade, hvor gæsterne selv tager."
},
{
id: "service",
name: "Service Pakke",
menuId: "klassisk-3-retter",
pricePerCover: 395,
minCovers: 20,
maxCovers: 50,
includes: [
"Menu planlægning",
"Indkøb af råvarer",
"Madlavning på lokation",
"Tallerkenanretning",
"Professionel servering",
"Fuld oprydning"
],
upsells: [
{ id: "velkomst", label: "Velkomstdrink & snacks", pricePerCover: 45 },
{ id: "vinmenu", label: "Vinmenu (3 glas)", pricePerCover: 225 },
{ id: "kaffe", label: "Kaffe/te med sødt", pricePerCover: 35 }
],
notes: "Komplet løsning med personlig service og professionel servering."
},
{
id: "gourmet",
name: "Gourmet Pakke",
menuId: "gourmet-upgrade",
pricePerCover: 595,
minCovers: 10,
maxCovers: 30,
includes: [
"Menu planlægning",
"Indkøb af kvalitetsråvarer",
"Madlavning på lokation",
"Tallerkenanretning",
"Professionel servering",
"Vinmenu (3 glas)",
"Kaffe med sødt",
"Fuld oprydning"
],
upsells: [
{ id: "champagne", label: "Champagne velkomst", pricePerCover: 95 },
{ id: "ekstravin", label: "Ekstra glas vin", pricePerCover: 75 },
{ id: "natmad", label: "Let natmad", pricePerCover: 75 }
],
notes: "Eksklusiv løsning til de særlige anledninger med gourmetmenu og vinmenu inkluderet."
}
],
};

View File

@@ -347,28 +347,6 @@ export const sampleData = {
leadTimeDays: 7,
basePricePerCover: 845
},
{
id: 'sushi-signatur',
title: 'Signatur Sushi',
description: 'Eksklusiv omakase-inspireret sushimenu med 6 serveringer.',
courses: [
'Toro nigiri med trøffel',
'Laks tataki med ponzu',
'Kammusling nigiri med yuzu',
'Tempura rejer med wasabi-mayo',
'Udvalg af maki rolls',
'Matcha is med hvid chokolade'
],
tags: ['Japansk', 'Sushi', 'Fisk', 'Luksus'],
image: '/img/sushi.png',
baseIngredientsPerCover: 130,
ingredientsCostIndex: 1.4,
baseLabourHours: 6,
incrementalLabourPerCover: 0.07,
minCovers: 10,
leadTimeDays: 7,
basePricePerCover: 895
},
{
id: 'fusion-menu',
title: 'Asian Fusion',
@@ -411,6 +389,44 @@ export const sampleData = {
],
notes: "God basis løsning til familiefester. Maden serveres på fade, hvor gæsterne selv tager."
},
{
id: "basis-gron",
name: "Basis Pakke",
menuId: "gron-3-retter",
pricePerCover: 275,
minCovers: 15,
maxCovers: 30,
includes: [
"Menu planlægning",
"Indkøb af råvarer",
"Madlavning på lokation",
"Let oprydning i køkken"
],
upsells: [
{ id: "anretning", label: "Tallerkenanretning", priceFlat: 500 },
{ id: "kaffe", label: "Kaffe/te", pricePerCover: 25 }
],
notes: "God basis løsning til familiefester. Maden serveres på fade, hvor gæsterne selv tager."
},
{
id: "basis-italiensk",
name: "Basis Pakke",
menuId: "italiensk-aften",
pricePerCover: 250,
minCovers: 15,
maxCovers: 30,
includes: [
"Menu planlægning",
"Indkøb af råvarer",
"Madlavning på lokation",
"Let oprydning i køkken"
],
upsells: [
{ id: "anretning", label: "Tallerkenanretning", priceFlat: 500 },
{ id: "kaffe", label: "Kaffe/te", pricePerCover: 25 }
],
notes: "God basis løsning til familiefester. Maden serveres 'family style' på fade."
},
{
id: "service",
name: "Service Pakke",
@@ -458,4 +474,4 @@ export const sampleData = {
notes: "Eksklusiv løsning til de særlige anledninger med gourmetmenu og vinmenu inkluderet."
}
],
} as const
}