- Implemented tests for debugging browser cache vs server response. - Verified calendar displays correct dates for November 2025. - Added visual inspection screenshot for calendar layout. - Created tests to ensure calendar fits within its container. - Checked for correct styling of available and blocked dates. - Debugged login form behavior and cookie handling. - Added tests for forced browser refresh and cache verification. - Conducted visual tests to compare actual calendar layout with expected. - Enhanced logging for better traceability during test execution.
582 lines
25 KiB
JavaScript
582 lines
25 KiB
JavaScript
import mysql from 'mysql2/promise';
|
|
import dotenv from 'dotenv';
|
|
|
|
dotenv.config();
|
|
|
|
const connection = await mysql.createConnection({
|
|
host: process.env.DB_HOST,
|
|
user: process.env.DB_USER,
|
|
password: process.env.DB_PASSWORD,
|
|
database: process.env.DB_NAME
|
|
});
|
|
|
|
console.log('=== Seeding All Menus from data.ts ===\n');
|
|
|
|
// All 16 menus with complete data
|
|
const allMenus = [
|
|
{
|
|
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'],
|
|
sides: [],
|
|
tags: ['Nordisk', 'Klassisk', 'Populær'],
|
|
image: '/img/klassisk.png',
|
|
baseIngredientsPerCover: 65,
|
|
ingredientsCostIndex: 1.0,
|
|
baseLabourHours: 4,
|
|
incrementalLabourPerCover: 0.05,
|
|
minCovers: 15,
|
|
leadTimeDays: 7,
|
|
basePricePerCover: 295,
|
|
ingredients: [
|
|
{ name: 'Varmrøget laks', quantity: '100g', perCover: true },
|
|
{ name: 'Rygeost', quantity: '30g', perCover: true },
|
|
{ name: 'Dild, frisk', quantity: '5g', perCover: true },
|
|
{ name: 'Radiser', quantity: '3 stk', perCover: true },
|
|
{ name: 'Kalvefilet', quantity: '200g', perCover: true },
|
|
{ name: 'Kartofler', quantity: '150g', perCover: true },
|
|
{ name: 'Gulerødder', quantity: '50g', perCover: true },
|
|
{ name: 'Rødvin til sauce', quantity: '100ml', perCover: false },
|
|
{ name: 'Chokolade, mørk', quantity: '80g', perCover: true },
|
|
{ name: 'Bær, blandede', quantity: '50g', perCover: true },
|
|
{ name: 'Vaniljeis', quantity: '100g', perCover: true },
|
|
{ name: 'Fløde', quantity: '500ml', perCover: false },
|
|
{ name: 'Smør', quantity: '200g', perCover: false }
|
|
]
|
|
},
|
|
{
|
|
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'],
|
|
sides: [],
|
|
tags: ['Vegetarisk', 'Sæson', 'Populær'],
|
|
image: '/img/veg.png',
|
|
baseIngredientsPerCover: 55,
|
|
ingredientsCostIndex: 0.95,
|
|
baseLabourHours: 4,
|
|
incrementalLabourPerCover: 0.05,
|
|
minCovers: 15,
|
|
leadTimeDays: 7,
|
|
basePricePerCover: 275,
|
|
ingredients: [
|
|
{ name: 'Asparges', quantity: '150g', perCover: true },
|
|
{ name: 'Æg til hollandaise', quantity: '2 stk', perCover: false },
|
|
{ name: 'Smør', quantity: '100g', perCover: false },
|
|
{ name: 'Karse', quantity: '5g', perCover: true },
|
|
{ name: 'Risottoris', quantity: '80g', perCover: true },
|
|
{ name: 'Blandede svampe', quantity: '100g', perCover: true },
|
|
{ name: 'Parmesan', quantity: '30g', perCover: true },
|
|
{ name: 'Løg', quantity: '50g', perCover: false },
|
|
{ name: 'Hvidvin', quantity: '200ml', perCover: false },
|
|
{ name: 'Pærer', quantity: '1 stk', perCover: true },
|
|
{ name: 'Hasselnødder', quantity: '20g', perCover: true },
|
|
{ name: 'Vaniljeis', quantity: '100g', perCover: true }
|
|
]
|
|
},
|
|
{
|
|
id: 'italiensk-aften',
|
|
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'],
|
|
sides: [],
|
|
tags: ['Italiensk', 'Pasta', 'Afslappet'],
|
|
image: '/img/italiensk.png',
|
|
baseIngredientsPerCover: 55,
|
|
ingredientsCostIndex: 0.9,
|
|
baseLabourHours: 4,
|
|
incrementalLabourPerCover: 0.05,
|
|
minCovers: 15,
|
|
leadTimeDays: 7,
|
|
basePricePerCover: 250,
|
|
ingredients: [
|
|
{ name: 'Prosciutto', quantity: '50g', perCover: true },
|
|
{ name: 'Salami', quantity: '40g', perCover: true },
|
|
{ name: 'Oliven', quantity: '30g', perCover: true },
|
|
{ name: 'Brød til bruschetta', quantity: '100g', perCover: true },
|
|
{ name: 'Tomater', quantity: '150g', perCover: true },
|
|
{ name: 'Pasta (frisk)', quantity: '150g', perCover: true },
|
|
{ name: 'Basilikum', quantity: '10g', perCover: true },
|
|
{ name: 'Parmesan', quantity: '30g', perCover: true },
|
|
{ name: 'Hvidløg', quantity: '2 fed', perCover: false },
|
|
{ name: 'Mascarpone', quantity: '80g', perCover: true },
|
|
{ name: 'Kaffe (espresso)', quantity: '50ml', perCover: true },
|
|
{ name: 'Amaretto', quantity: '20ml', perCover: true },
|
|
{ name: 'Æg', quantity: '2 stk', perCover: false },
|
|
{ name: 'Kakao', quantity: '10g', perCover: true }
|
|
]
|
|
},
|
|
{
|
|
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'],
|
|
sides: [],
|
|
tags: ['Grill', 'Sommer', 'Afslappet'],
|
|
image: '/img/grill.png',
|
|
baseIngredientsPerCover: 60,
|
|
ingredientsCostIndex: 1.0,
|
|
baseLabourHours: 4,
|
|
incrementalLabourPerCover: 0.05,
|
|
minCovers: 20,
|
|
leadTimeDays: 7,
|
|
basePricePerCover: 275,
|
|
ingredients: [
|
|
{ name: 'Peberfrugter', quantity: '150g', perCover: true },
|
|
{ name: 'Hvidløg', quantity: '3 fed', perCover: false },
|
|
{ name: 'Græsk yoghurt', quantity: '50g', perCover: true },
|
|
{ name: 'Kyllingebryst', quantity: '200g', perCover: true },
|
|
{ name: 'Flanksteak', quantity: '200g', perCover: true },
|
|
{ name: 'Marinade (olie, krydderier)', quantity: '50ml', perCover: false },
|
|
{ name: 'Salat, blandet', quantity: '50g', perCover: true },
|
|
{ name: 'Tomater', quantity: '100g', perCover: true },
|
|
{ name: 'Ferskner', quantity: '1 stk', perCover: true },
|
|
{ name: 'Vaniljeis', quantity: '100g', perCover: true },
|
|
{ name: 'Honning', quantity: '20ml', perCover: false }
|
|
]
|
|
},
|
|
{
|
|
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'],
|
|
sides: [],
|
|
tags: ['Nordisk', 'Buffet', 'Traditionel'],
|
|
image: '/img/nordisk.png',
|
|
baseIngredientsPerCover: 65,
|
|
ingredientsCostIndex: 1.0,
|
|
baseLabourHours: 5,
|
|
incrementalLabourPerCover: 0.04,
|
|
minCovers: 25,
|
|
leadTimeDays: 7,
|
|
basePricePerCover: 250,
|
|
ingredients: [
|
|
{ name: 'Koldrøget laks', quantity: '80g', perCover: true },
|
|
{ name: 'Marinerede sild', quantity: '60g', perCover: true },
|
|
{ name: 'Rugbrød', quantity: '100g', perCover: true },
|
|
{ name: 'Kalve-/svinehakket', quantity: '120g', perCover: true },
|
|
{ name: 'Æg', quantity: '1 stk', perCover: false },
|
|
{ name: 'Løg', quantity: '50g', perCover: false },
|
|
{ name: 'Nye kartofler', quantity: '200g', perCover: true },
|
|
{ name: 'Smør', quantity: '200g', perCover: false },
|
|
{ name: 'Æbler', quantity: '150g', perCover: true },
|
|
{ name: 'Makroner', quantity: '30g', perCover: true },
|
|
{ name: 'Flødeskum', quantity: '100ml', perCover: true }
|
|
]
|
|
},
|
|
{
|
|
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'],
|
|
sides: [],
|
|
tags: ['Brunch', 'Morgenmad', 'Buffet'],
|
|
image: '/img/brunch.png',
|
|
baseIngredientsPerCover: 45,
|
|
ingredientsCostIndex: 0.85,
|
|
baseLabourHours: 3,
|
|
incrementalLabourPerCover: 0.04,
|
|
minCovers: 15,
|
|
leadTimeDays: 5,
|
|
basePricePerCover: 225,
|
|
ingredients: [
|
|
{ name: 'Æg', quantity: '3 stk', perCover: true },
|
|
{ name: 'Bacon', quantity: '50g', perCover: true },
|
|
{ name: 'Croissanter', quantity: '2 stk', perCover: true },
|
|
{ name: 'Rundstykker', quantity: '2 stk', perCover: true },
|
|
{ name: 'Smør', quantity: '20g', perCover: true },
|
|
{ name: 'Marmelade', quantity: '30g', perCover: true },
|
|
{ name: 'Yoghurt, naturel', quantity: '150g', perCover: true },
|
|
{ name: 'Müsli', quantity: '50g', perCover: true },
|
|
{ name: 'Friske bær', quantity: '80g', perCover: true },
|
|
{ name: 'Appelsin juice', quantity: '200ml', perCover: true },
|
|
{ name: 'Kaffe/te', quantity: '1 kop', perCover: true }
|
|
]
|
|
},
|
|
{
|
|
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'],
|
|
sides: [],
|
|
tags: ['Spansk', 'Tapas', 'Reception'],
|
|
image: '/img/tapas.png',
|
|
baseIngredientsPerCover: 50,
|
|
ingredientsCostIndex: 0.95,
|
|
baseLabourHours: 4,
|
|
incrementalLabourPerCover: 0.05,
|
|
minCovers: 20,
|
|
leadTimeDays: 7,
|
|
basePricePerCover: 265,
|
|
ingredients: [
|
|
{ name: 'Serrano skinke', quantity: '60g', perCover: true },
|
|
{ name: 'Manchego ost', quantity: '50g', perCover: true },
|
|
{ name: 'Oliven', quantity: '40g', perCover: true },
|
|
{ name: 'Kartofler', quantity: '200g', perCover: true },
|
|
{ name: 'Æg', quantity: '2 stk', perCover: false },
|
|
{ name: 'Løg', quantity: '100g', perCover: false },
|
|
{ name: 'Tomatsauce', quantity: '50ml', perCover: true },
|
|
{ name: 'Aioli', quantity: '30g', perCover: true },
|
|
{ name: 'Chorizo', quantity: '50g', perCover: true },
|
|
{ name: 'Churros (frossen)', quantity: '100g', perCover: true },
|
|
{ name: 'Chokolade sauce', quantity: '50ml', perCover: true }
|
|
]
|
|
},
|
|
{
|
|
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'],
|
|
sides: [],
|
|
tags: ['Gourmet', 'Special', 'Luksus'],
|
|
image: '/img/gourmet.png',
|
|
baseIngredientsPerCover: 95,
|
|
ingredientsCostIndex: 1.2,
|
|
baseLabourHours: 5,
|
|
incrementalLabourPerCover: 0.06,
|
|
minCovers: 10,
|
|
leadTimeDays: 7,
|
|
basePricePerCover: 495,
|
|
ingredients: [
|
|
{ name: 'Oksefilet', quantity: '100g', perCover: true },
|
|
{ name: 'Trøffelolie', quantity: '5ml', perCover: true },
|
|
{ name: 'Rucola', quantity: '20g', perCover: true },
|
|
{ name: 'Parmesan', quantity: '20g', perCover: true },
|
|
{ name: 'Jordskokker', quantity: '150g', perCover: true },
|
|
{ name: 'Fløde', quantity: '100ml', perCover: false },
|
|
{ name: 'Lammeculotte', quantity: '250g', perCover: true },
|
|
{ name: 'Sæsongrønt', quantity: '150g', perCover: true },
|
|
{ name: 'Kartofler', quantity: '150g', perCover: true },
|
|
{ name: 'Chokolade fondant', quantity: '120g', perCover: true },
|
|
{ name: 'Vaniljeis', quantity: '80g', perCover: true }
|
|
]
|
|
}
|
|
// Continue with remaining 8 menus...
|
|
];
|
|
|
|
// Add remaining 8 menus (nordisk-signatur through fusion-menu)
|
|
// I'll add them separately due to length
|
|
|
|
const remainingMenus = [
|
|
{
|
|
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'],
|
|
sides: [],
|
|
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,
|
|
ingredients: [
|
|
{ name: 'Kammusling', quantity: '3 stk', perCover: true },
|
|
{ name: 'Æbler', quantity: '50g', perCover: true },
|
|
{ name: 'Dild', quantity: '5g', perCover: true },
|
|
{ name: 'Jordskokker', quantity: '150g', perCover: true },
|
|
{ name: 'Trøffel', quantity: '5g', perCover: false },
|
|
{ name: 'Fløde', quantity: '100ml', perCover: false },
|
|
{ name: 'Torsk', quantity: '200g', perCover: true },
|
|
{ name: 'Blomkål', quantity: '150g', perCover: true },
|
|
{ name: 'Smør', quantity: '50g', perCover: false },
|
|
{ name: 'Brombær', quantity: '80g', perCover: true },
|
|
{ name: 'Hvid chokolade', quantity: '50g', perCover: true }
|
|
]
|
|
},
|
|
{
|
|
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'],
|
|
sides: [],
|
|
tags: ['Italiensk', 'Pasta', 'Gourmet'],
|
|
image: '/img/italiensk.png',
|
|
baseIngredientsPerCover: 85,
|
|
ingredientsCostIndex: 1.1,
|
|
baseLabourHours: 5,
|
|
incrementalLabourPerCover: 0.05,
|
|
minCovers: 10,
|
|
leadTimeDays: 7,
|
|
basePricePerCover: 695,
|
|
ingredients: [
|
|
{ name: 'Charcuteri mix', quantity: '80g', perCover: true },
|
|
{ name: 'Lokale oste', quantity: '60g', perCover: true },
|
|
{ name: 'Brød', quantity: '50g', perCover: true },
|
|
{ name: 'Pasta dej', quantity: '150g', perCover: true },
|
|
{ name: 'Ricotta', quantity: '80g', perCover: true },
|
|
{ name: 'Salvie', quantity: '5g', perCover: true },
|
|
{ name: 'Kalvekød', quantity: '200g', perCover: true },
|
|
{ name: 'Gremolata', quantity: '20g', perCover: true },
|
|
{ name: 'Mascarpone', quantity: '80g', perCover: true },
|
|
{ name: 'Kaffe (espresso)', quantity: '50ml', perCover: true },
|
|
{ name: 'Amaretto', quantity: '20ml', perCover: true },
|
|
{ name: 'Kakao', quantity: '10g', perCover: true }
|
|
]
|
|
},
|
|
{
|
|
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'],
|
|
sides: [],
|
|
tags: ['Fransk', 'Gourmet', 'Luksus'],
|
|
image: '/img/fransk.png',
|
|
baseIngredientsPerCover: 125,
|
|
ingredientsCostIndex: 1.3,
|
|
baseLabourHours: 6,
|
|
incrementalLabourPerCover: 0.07,
|
|
minCovers: 10,
|
|
leadTimeDays: 10,
|
|
basePricePerCover: 895,
|
|
ingredients: [
|
|
{ name: 'Østers', quantity: '3 stk', perCover: true },
|
|
{ name: 'Vinaigrette', quantity: '20ml', perCover: true },
|
|
{ name: 'Citron', quantity: '1/4 stk', perCover: true },
|
|
{ name: 'Foie gras', quantity: '80g', perCover: true },
|
|
{ name: 'Brioche', quantity: '50g', perCover: true },
|
|
{ name: 'Fignekompot', quantity: '30g', perCover: true },
|
|
{ name: 'Hummer', quantity: '150g', perCover: false },
|
|
{ name: 'Fløde', quantity: '200ml', perCover: false },
|
|
{ name: 'Cognac', quantity: '50ml', perCover: false },
|
|
{ name: 'Andelår', quantity: '250g', perCover: true },
|
|
{ name: 'Kartofler', quantity: '200g', perCover: true },
|
|
{ name: 'Gåsefed', quantity: '50g', perCover: false },
|
|
{ name: 'Fløde til brûlée', quantity: '150ml', perCover: true },
|
|
{ name: 'Bær', quantity: '50g', perCover: true }
|
|
]
|
|
},
|
|
{
|
|
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'],
|
|
sides: [],
|
|
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,
|
|
ingredients: [
|
|
{ name: 'Jamon Iberico', quantity: '60g', perCover: true },
|
|
{ name: 'Rejer', quantity: '100g', perCover: true },
|
|
{ name: 'Hvidløg', quantity: '3 fed', perCover: false },
|
|
{ name: 'Blæksprutte', quantity: '120g', perCover: true },
|
|
{ name: 'Blæksprutte blæk', quantity: '10g', perCover: false },
|
|
{ name: 'Kartofler til bravas', quantity: '150g', perCover: true },
|
|
{ name: 'Aioli', quantity: '30g', perCover: true },
|
|
{ name: 'Pimientos de padrón', quantity: '80g', perCover: true },
|
|
{ name: 'Hakkekød til albóndigas', quantity: '100g', perCover: true },
|
|
{ name: 'Tomatsauce', quantity: '50ml', perCover: true },
|
|
{ name: 'Churros', quantity: '100g', perCover: true },
|
|
{ name: 'Chokolade sauce', quantity: '50ml', perCover: true }
|
|
]
|
|
},
|
|
{
|
|
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'],
|
|
sides: [],
|
|
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,
|
|
ingredients: [
|
|
{ name: 'Laks', quantity: '150g', perCover: true },
|
|
{ name: 'Sennep', quantity: '20g', perCover: false },
|
|
{ name: 'Dild', quantity: '10g', perCover: false },
|
|
{ name: 'Andelår', quantity: '250g', perCover: true },
|
|
{ name: 'Rødkål', quantity: '100g', perCover: true },
|
|
{ name: 'Kartofler', quantity: '200g', perCover: true },
|
|
{ name: 'Sukker til brunede kartofler', quantity: '50g', perCover: false },
|
|
{ name: 'Ris', quantity: '80g', perCover: true },
|
|
{ name: 'Mandler', quantity: '30g', perCover: true },
|
|
{ name: 'Kirsebærsauce', quantity: '50ml', perCover: true },
|
|
{ name: 'Fløde', quantity: '100ml', perCover: false }
|
|
]
|
|
},
|
|
{
|
|
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'],
|
|
sides: [],
|
|
tags: ['Brunch', 'Morgenmad', 'Luksus'],
|
|
image: '/img/brunch.png',
|
|
baseIngredientsPerCover: 85,
|
|
ingredientsCostIndex: 1.1,
|
|
baseLabourHours: 4,
|
|
incrementalLabourPerCover: 0.05,
|
|
minCovers: 10,
|
|
leadTimeDays: 5,
|
|
basePricePerCover: 545,
|
|
ingredients: [
|
|
{ name: 'Mousserende vin', quantity: '100ml', perCover: true },
|
|
{ name: 'Snacks', quantity: '50g', perCover: true },
|
|
{ name: 'Æg', quantity: '3 stk', perCover: true },
|
|
{ name: 'Trøffel', quantity: '5g', perCover: false },
|
|
{ name: 'Hjemmebagt brød', quantity: '100g', perCover: true },
|
|
{ name: 'Røget laks', quantity: '80g', perCover: true },
|
|
{ name: 'Ærter', quantity: '50g', perCover: true },
|
|
{ name: 'Charcuteri', quantity: '80g', perCover: true },
|
|
{ name: 'Nordiske oste', quantity: '60g', perCover: true },
|
|
{ name: 'Croissanter', quantity: '2 stk', perCover: true },
|
|
{ name: 'Wienerbrød', quantity: '2 stk', perCover: true },
|
|
{ name: 'Yoghurt', quantity: '150g', perCover: true },
|
|
{ name: 'Granola', quantity: '50g', perCover: true },
|
|
{ name: 'Friske bær', quantity: '80g', perCover: true },
|
|
{ name: 'Pandekager', quantity: '3 stk', perCover: true },
|
|
{ name: 'Ahornsirup', quantity: '30ml', perCover: true },
|
|
{ name: 'Mini-desserter', quantity: '3 stk', perCover: true }
|
|
]
|
|
},
|
|
{
|
|
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'],
|
|
sides: [],
|
|
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,
|
|
ingredients: [
|
|
{ name: 'Jomfruhummere', quantity: '3 stk', perCover: true },
|
|
{ name: 'Urtemayonnaise', quantity: '50g', perCover: true },
|
|
{ name: 'Wagyu beef', quantity: '120g', perCover: true },
|
|
{ name: 'Brioche boller', quantity: '2 stk', perCover: true },
|
|
{ name: 'Trøffel', quantity: '5g', perCover: false },
|
|
{ name: 'Lammecarré', quantity: '250g', perCover: true },
|
|
{ name: 'Rosmarin', quantity: '5g', perCover: true },
|
|
{ name: 'Grøntsager, blandede', quantity: '200g', perCover: true },
|
|
{ name: 'Romesco sauce', quantity: '50ml', perCover: true },
|
|
{ name: 'Ferskner', quantity: '1 stk', perCover: true },
|
|
{ name: 'Mascarpone', quantity: '50g', perCover: true },
|
|
{ name: 'Honning', quantity: '20ml', perCover: true }
|
|
]
|
|
},
|
|
{
|
|
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'],
|
|
sides: [],
|
|
tags: ['Asiatisk', 'Fusion', 'Modern'],
|
|
image: '/img/fusion.png',
|
|
baseIngredientsPerCover: 95,
|
|
ingredientsCostIndex: 1.2,
|
|
baseLabourHours: 5,
|
|
incrementalLabourPerCover: 0.06,
|
|
minCovers: 10,
|
|
leadTimeDays: 7,
|
|
basePricePerCover: 745,
|
|
ingredients: [
|
|
{ name: 'Tun', quantity: '100g', perCover: true },
|
|
{ name: 'Wasabi', quantity: '5g', perCover: false },
|
|
{ name: 'Sesam', quantity: '10g', perCover: true },
|
|
{ name: 'And', quantity: '150g', perCover: true },
|
|
{ name: 'Dumpling dej', quantity: '100g', perCover: true },
|
|
{ name: 'Hoisin sauce', quantity: '30ml', perCover: true },
|
|
{ name: 'Kokosmælk', quantity: '200ml', perCover: false },
|
|
{ name: 'Thai krydderier', quantity: '20g', perCover: false },
|
|
{ name: 'Short ribs', quantity: '300g', perCover: true },
|
|
{ name: 'Korean BBQ marinade', quantity: '50ml', perCover: true },
|
|
{ name: 'Fløde til pannacotta', quantity: '150ml', perCover: true },
|
|
{ name: 'Yuzu juice', quantity: '20ml', perCover: true },
|
|
{ name: 'Mango', quantity: '80g', perCover: true }
|
|
]
|
|
}
|
|
];
|
|
|
|
const allMenusComplete = [...allMenus, ...remainingMenus];
|
|
|
|
try {
|
|
console.log(`Inserting ${allMenusComplete.length} menus...\n`);
|
|
|
|
for (const menu of allMenusComplete) {
|
|
await connection.execute(`
|
|
INSERT INTO menus (
|
|
id, title, description, courses, sides, tags, image,
|
|
base_ingredients_per_cover, ingredients_cost_index, base_labour_hours,
|
|
incremental_labour_per_cover, min_covers, lead_time_days, base_price_per_cover
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
title = VALUES(title),
|
|
description = VALUES(description),
|
|
courses = VALUES(courses),
|
|
sides = VALUES(sides),
|
|
tags = VALUES(tags),
|
|
image = VALUES(image),
|
|
base_ingredients_per_cover = VALUES(base_ingredients_per_cover),
|
|
ingredients_cost_index = VALUES(ingredients_cost_index),
|
|
base_labour_hours = VALUES(base_labour_hours),
|
|
incremental_labour_per_cover = VALUES(incremental_labour_per_cover),
|
|
min_covers = VALUES(min_covers),
|
|
lead_time_days = VALUES(lead_time_days),
|
|
base_price_per_cover = VALUES(base_price_per_cover)
|
|
`, [
|
|
menu.id,
|
|
menu.title,
|
|
menu.description,
|
|
JSON.stringify(menu.courses),
|
|
JSON.stringify(menu.sides),
|
|
JSON.stringify(menu.tags),
|
|
menu.image,
|
|
menu.baseIngredientsPerCover,
|
|
menu.ingredientsCostIndex,
|
|
menu.baseLabourHours,
|
|
menu.incrementalLabourPerCover,
|
|
menu.minCovers,
|
|
menu.leadTimeDays,
|
|
menu.basePricePerCover
|
|
]);
|
|
|
|
// Delete and re-insert ingredients
|
|
await connection.execute('DELETE FROM menu_ingredients WHERE menu_id = ?', [menu.id]);
|
|
|
|
for (let i = 0; i < menu.ingredients.length; i++) {
|
|
const ing = menu.ingredients[i];
|
|
await connection.execute(`
|
|
INSERT INTO menu_ingredients (menu_id, name, quantity, per_cover, sort_order)
|
|
VALUES (?, ?, ?, ?, ?)
|
|
`, [menu.id, ing.name, ing.quantity, ing.perCover, i]);
|
|
}
|
|
|
|
console.log(`✓ ${menu.title} (${menu.ingredients.length} ingredienser)`);
|
|
}
|
|
|
|
// Show summary
|
|
const [menuCount] = await connection.execute('SELECT COUNT(*) as count FROM menus');
|
|
const [ingredientCount] = await connection.execute('SELECT COUNT(*) as count FROM menu_ingredients');
|
|
|
|
console.log('\n=== Database Summary ===');
|
|
console.log(`Menus: ${menuCount[0].count}`);
|
|
console.log(`Ingredients: ${ingredientCount[0].count}`);
|
|
console.log('\n✅ Seed completed successfully!');
|
|
|
|
} catch (error) {
|
|
console.error('❌ Seed failed:', error);
|
|
process.exit(1);
|
|
} finally {
|
|
await connection.end();
|
|
}
|