import React, { createContext, useContext, useState, useEffect } from 'react'; import type { FoodItem, Category, Stall } from '../types'; interface FoodContextType { categories: Category[]; foodItems: FoodItem[]; stalls: Stall[]; isLoading: boolean; error: string | null; refreshData: () => Promise; } const FoodContext = createContext(undefined); const API_BASE_URL = `http://${window.location.hostname}:8080/api`; const EMOJIS = ['🍦', '🍪', '🧃', '🍫', '🥨', '🥐', '🍹', '📦', '🍳', '🍱', '🍔', '🍕', '🥗', '🍲']; const COLORS = ['#FFF0F6', '#FFF7ED', '#FFFFEB', '#FDF3EF', '#F0FDF4', '#FEFCE8', '#EFF6FF']; export const FoodProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [categories, setCategories] = useState([]); const [foodItems, setFoodItems] = useState([]); const [stalls, setStalls] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const fetchData = async () => { setIsLoading(true); setError(null); try { const [baseItemsRes, productsRes, stallsRes] = await Promise.all([ fetch(`${API_BASE_URL}/base-items?size=100`, { cache: 'no-store' }), fetch(`${API_BASE_URL}/products?size=100`, { cache: 'no-store' }), fetch(`${API_BASE_URL}/stalls/active`, { cache: 'no-store' }) ]); if (!baseItemsRes.ok || !productsRes.ok || !stallsRes.ok) { throw new Error('Failed to fetch data from server'); } const baseItemsDataRaw = await baseItemsRes.json(); const productsDataRaw = await productsRes.json(); const stallsData = await stallsRes.json(); const baseItemsData = baseItemsDataRaw.content || baseItemsDataRaw; const productsData = productsDataRaw.content || productsDataRaw; // Map BaseItems to Categories const mappedCategories: Category[] = baseItemsData.map((item: any, index: number) => ({ id: item.id.toString(), name: item.name, description: item.description, emoji: EMOJIS[index % EMOJIS.length], image: item.imageData?.trim() ? (item.imageData.trim().startsWith('data:') ? item.imageData.trim() : `data:image/png;base64,${item.imageData.trim()}`) : '', color: COLORS[index % COLORS.length] })); // Map Stalls and build a product-to-stall lookup map const productToStallMap: Record = {}; const categoryToStallMap: Record = {}; const mappedStalls: Stall[] = stallsData.map((item: any) => { const stallInfo = { id: item.id.toString(), name: item.name }; // Map category/baseItems to this stall for indirect lookup if (item.baseItems) { item.baseItems.forEach((bi: any) => { if (!categoryToStallMap[bi.name.toLowerCase()]) { categoryToStallMap[bi.name.toLowerCase()] = stallInfo; } }); } // If this stall explicitly lists products, add them to map if (item.products) { item.products.forEach((p: any) => { if (!productToStallMap[p.id.toString()]) { productToStallMap[p.id.toString()] = stallInfo; } }); } return { id: stallInfo.id, name: stallInfo.name, description: item.description || 'Our delicious stall', imageData: item.imageData?.trim() ? (item.imageData.trim().startsWith('data:') ? item.imageData.trim() : `data:image/png;base64,${item.imageData.trim()}`) : '', active: item.active, temporarilyClosed: item.temporarilyClosed }; }); // Map Products to FoodItems using the lookup map as fallback const mappedFoodItems: FoodItem[] = productsData.map((item: any) => { const rawImg = item.imageData?.trim(); const finalImage = rawImg ? (rawImg.startsWith('data:') ? rawImg : `data:image/png;base64,${rawImg}`) : ''; const itemId = item.id.toString(); const stallFromBackend = item.stalls && item.stalls.length > 0 ? { id: item.stalls[0].id.toString(), name: item.stalls[0].name } : null; const stallFromMap = productToStallMap[itemId]; const stallFromCategory = item.category ? categoryToStallMap[item.category.toLowerCase()] : null; return { id: itemId, name: item.name, description: item.description || 'No description available', price: item.price || item.basePrice || 0, category: item.category, image: finalImage, isVeg: item.veg, isPopular: item.active, stock: item.stock, stallId: (stallFromBackend?.id || stallFromMap?.id || stallFromCategory?.id), stallName: (stallFromBackend?.name || stallFromMap?.name || stallFromCategory?.name) }; }); setCategories(mappedCategories); setFoodItems(mappedFoodItems); setStalls(mappedStalls); } catch (err: any) { setError(err.message); console.error('Error fetching food data:', err); } finally { setIsLoading(false); } }; useEffect(() => { fetchData(); }, []); return ( {children} ); }; export const useFood = () => { const context = useContext(FoodContext); if (context === undefined) { throw new Error('useFood must be used within a FoodProvider'); } return context; };