/** * Mock data for standalone HTML testing. * Intercepts fetch() calls to api.bessa.app and returns realistic dummy data. * Injected BEFORE kantine.js in standalone builds only. */ (function () { 'use strict'; // Generate dates for this week and next week (Mon-Fri) function getWeekDates(weekOffset) { const dates = []; const now = new Date(); const dayOfWeek = now.getDay(); // 0=Sun, 1=Mon const monday = new Date(now); monday.setDate(now.getDate() - (dayOfWeek === 0 ? 6 : dayOfWeek - 1) + (weekOffset * 7)); for (let i = 0; i < 5; i++) { const d = new Date(monday); d.setDate(monday.getDate() + i); dates.push(d.toISOString().split('T')[0]); } return dates; } const thisWeekDates = getWeekDates(0); const nextWeekDates = getWeekDates(1); const allDates = [...thisWeekDates, ...nextWeekDates]; // Realistic German canteen menu items per day const menuPool = [ [ { id: 101, name: 'Wiener Schnitzel mit Kartoffelsalat', description: 'Paniertes Schweineschnitzel mit hausgemachtem Kartoffelsalat', price: '6.90', available_amount: '15', amount_tracking: true }, { id: 102, name: 'Gemüse-Curry mit Basmatireis', description: 'Veganes Curry mit saisonalem Gemüse und Kokosmilch', price: '5.50', available_amount: '0', amount_tracking: true }, { id: 103, name: 'Rindergulasch mit Spätzle', description: 'Geschmortes Rindfleisch in Paprikasauce mit Eierspätzle', price: '7.20', available_amount: '8', amount_tracking: true }, { id: 104, name: 'Tagessuppe: Tomatencremesuppe', description: 'Cremige Tomatensuppe mit Croutons', price: '3.20', available_amount: '0', amount_tracking: false }, ], [ { id: 201, name: 'Hähnchenbrust mit Pilzrahmsauce', description: 'Gebratene Hähnchenbrust mit Champignon-Rahmsauce und Reis', price: '6.50', available_amount: '12', amount_tracking: true }, { id: 202, name: 'Vegetarische Lasagne', description: 'Lasagne mit Spinat, Ricotta und Tomatensauce', price: '5.80', available_amount: '10', amount_tracking: true }, { id: 203, name: 'Bratwurst mit Sauerkraut', description: 'Thüringer Bratwurst mit Sauerkraut und Kartoffelpüree', price: '5.90', available_amount: '0', amount_tracking: true }, { id: 204, name: 'Caesar Salad mit Hähnchen', description: 'Römersalat mit gegrilltem Hähnchen, Parmesan und Croutons', price: '6.10', available_amount: '0', amount_tracking: false }, ], [ { id: 301, name: 'Spaghetti Bolognese', description: 'Klassische Bolognese mit frischen Spaghetti', price: '5.20', available_amount: '20', amount_tracking: true }, { id: 302, name: 'Gebratener Lachs mit Dillsauce', description: 'Lachsfilet auf Blattspinat mit Senf-Dill-Sauce', price: '8.50', available_amount: '5', amount_tracking: true }, { id: 303, name: 'Kartoffelgratin mit Salat', description: 'Überbackene Kartoffeln mit Sahne und Käse, dazu gemischter Salat', price: '5.00', available_amount: '0', amount_tracking: false }, { id: 304, name: 'Chili con Carne', description: 'Pikantes Chili mit Hackfleisch, Bohnen und Reis', price: '5.80', available_amount: '9', amount_tracking: true }, ], [ { id: 401, name: 'Schweinebraten mit Knödel', description: 'Bayerischer Schweinebraten mit Semmelknödel und Bratensauce', price: '7.00', available_amount: '7', amount_tracking: true }, { id: 402, name: 'Falafel-Bowl mit Hummus', description: 'Knusprige Falafel mit Hummus, Tabouleh und Fladenbrot', price: '5.90', available_amount: '0', amount_tracking: false }, { id: 403, name: 'Putengeschnetzeltes mit Nudeln', description: 'Putenstreifen in Champignon-Sahnesauce mit Bandnudeln', price: '6.30', available_amount: '11', amount_tracking: true }, { id: 404, name: 'Tagessuppe: Erbsensuppe', description: 'Deftige Erbsensuppe mit Wiener Würstchen', price: '3.50', available_amount: '0', amount_tracking: false }, ], [ { id: 501, name: 'Backfisch mit Remoulade', description: 'Paniertes Seelachsfilet mit Remouladensauce und Bratkartoffeln', price: '6.80', available_amount: '6', amount_tracking: true }, { id: 502, name: 'Käsespätzle mit Röstzwiebeln', description: 'Allgäuer Käsespätzle mit karamellisierten Zwiebeln und Salat', price: '5.50', available_amount: '14', amount_tracking: true }, { id: 503, name: 'Schnitzel Wiener Art mit Pommes', description: 'Paniertes Hähnchenschnitzel mit knusprigen Pommes Frites', price: '6.20', available_amount: '0', amount_tracking: true }, { id: 504, name: 'Griechischer Bauernsalat', description: 'Frischer Salat mit Feta, Oliven, Gurke und Tomaten', price: '5.30', available_amount: '0', amount_tracking: false }, ], ]; // Build mock responses for each date const dateResponses = {}; allDates.forEach((date, i) => { const menuIndex = i % menuPool.length; dateResponses[date] = { results: [{ id: 1, name: 'Mittagsmenü', items: menuPool[menuIndex].map(item => ({ ...item, // Ensure unique IDs per date id: item.id + (i * 1000) })) }] }; }); // Mock some orders for today (to show "Bestellt" badges) const todayStr = new Date().toISOString().split('T')[0]; const todayMenu = dateResponses[todayStr]; const mockOrders = []; let nextOrderId = 9001; if (todayMenu) { const firstItem = todayMenu.results[0].items[0]; mockOrders.push({ id: nextOrderId++, article: firstItem.id, article_name: firstItem.name, date: todayStr, venue: 591, status: 'confirmed', created: new Date().toISOString() }); } // Pre-seed a mock auth session so flag/order buttons render sessionStorage.setItem('kantine_authToken', 'mock-token-for-testing'); sessionStorage.setItem('kantine_currentUser', '12345'); sessionStorage.setItem('kantine_firstName', 'Test'); sessionStorage.setItem('kantine_lastName', 'User'); // Intercept fetch const originalFetch = window.fetch; window.fetch = function (url, options) { const urlStr = typeof url === 'string' ? url : url.toString(); // Menu dates endpoint if (urlStr.includes('/menu/dates/')) { console.log('[MOCK] Returning mock dates data'); return Promise.resolve(new Response(JSON.stringify({ results: allDates.map(date => ({ date, orders: [] })) }), { status: 200, headers: { 'Content-Type': 'application/json' } })); } // Menu detail for a specific date const dateMatch = urlStr.match(/\/menu\/\d+\/(\d{4}-\d{2}-\d{2})\//); if (dateMatch) { const date = dateMatch[1]; const data = dateResponses[date] || { results: [] }; console.log(`[MOCK] Returning mock menu for ${date}`); return Promise.resolve(new Response(JSON.stringify(data), { status: 200, headers: { 'Content-Type': 'application/json' } })); } // Orders endpoint if (urlStr.includes('/user/orders/') && (!options || options.method === 'GET' || !options.method)) { console.log('[MOCK] Returning mock orders'); // Formatter for history mapping const mappedOrders = mockOrders.map(o => ({ id: o.id, date: `${o.date}T10:00:00Z`, order_state: o.status === 'cancelled' ? 9 : 5, total: o.price || '6.50', items: [{ article: o.article, name: o.article_name, price: o.price || '6.50', amount: 1 }] })); // Handle lazy load / pagination if requesting full history if (urlStr.includes('limit=50')) { return Promise.resolve(new Response(JSON.stringify({ count: mappedOrders.length, next: null, results: mappedOrders }), { status: 200, headers: { 'Content-Type': 'application/json' } })); } return Promise.resolve(new Response(JSON.stringify({ results: mappedOrders }), { status: 200, headers: { 'Content-Type': 'application/json' } })); } // Auth user endpoint if (urlStr.includes('/auth/user/')) { console.log('[MOCK] Returning mock user'); return Promise.resolve(new Response(JSON.stringify({ pk: 12345, username: 'testuser', email: 'test@example.com', first_name: 'Test', last_name: 'User' }), { status: 200, headers: { 'Content-Type': 'application/json' } })); } // Order create (POST to /user/orders/) if (urlStr.includes('/user/orders/') && options && options.method === 'POST') { const body = JSON.parse(options.body || '{}'); const newOrder = { id: nextOrderId++, article: body.article, article_name: 'Mock Order', date: body.date, venue: 591, status: 'confirmed', created: new Date().toISOString() }; mockOrders.push(newOrder); console.log('[MOCK] Created order:', newOrder); return Promise.resolve(new Response(JSON.stringify(newOrder), { status: 201, headers: { 'Content-Type': 'application/json' } })); } // Order cancel (POST to /user/orders/{id}/cancel/) const cancelMatch = urlStr.match(/\/user\/orders\/(\d+)\/cancel\//); if (cancelMatch) { const orderId = parseInt(cancelMatch[1]); const idx = mockOrders.findIndex(o => o.id === orderId); if (idx >= 0) mockOrders.splice(idx, 1); console.log('[MOCK] Cancelled order:', orderId); return Promise.resolve(new Response('{}', { status: 200, headers: { 'Content-Type': 'application/json' } })); } // Fallback to real fetch for other URLs (fonts, etc.) return originalFetch.apply(this, arguments); }; console.log('[MOCK] 🧪 Mock data active – using dummy canteen menus for UI testing'); })();