import { useEffect, useState } from 'react' import { useNavigate, useParams, useSearchParams } from 'react-router-dom' import ProductPicker from '../components/ProductPicker' import OrderDrawer from '../components/OrderDrawer' import client from '../api/client' import { useProductCache } from '../hooks/useProductCache' export default function AddItemsPage() { const { tableId } = useParams() const [searchParams] = useSearchParams() const isNewTable = searchParams.get('new') === '1' const navigate = useNavigate() const { products, categories } = useProductCache() const [cart, setCart] = useState([]) const [orderId, setOrderId] = useState(null) const [sending, setSending] = useState(false) const [retrying, setRetrying] = useState(false) const [error, setError] = useState('') const [printAck, setPrintAck] = useState(null) const [cartOpen, setCartOpen] = useState(false) const [editItem, setEditItem] = useState(null) // { cartKey, product, drawerState } const [viewAllOpen, setViewAllOpen] = useState(false) const [searchOpen, setSearchOpen] = useState(false) const [searchQuery, setSearchQuery] = useState('') useEffect(() => { async function load() { const statusRes = await client.get(`/api/tables/${tableId}/status`) setOrderId(statusRes.data.active_order_id) // Pre-populate cart from "order again" if present const stored = sessionStorage.getItem('orderAgainItems') if (stored) { sessionStorage.removeItem('orderAgainItems') try { const items = JSON.parse(stored) const initialCart = items.map(it => ({ ...it, _key: Date.now() + Math.random(), })) setCart(initialCart) } catch {} } } load() }, [tableId]) // Back button: if this was a new table and nothing was added, leave the table FREE function handleBack() { if (isNewTable && cart.length === 0) { navigate('/tables', { replace: true }) } else { navigate(`/tables/${tableId}`) } } function addToCart(item) { setCart(prev => { // Try to find an identical item already in the cart to stack onto. // Two items are identical when every meaningful field matches exactly. const { _key: _k, _drawerState: _ds, ...newCore } = item const matchIdx = prev.findIndex(existing => { const { _key, _drawerState, ...existCore } = existing return JSON.stringify(existCore) === JSON.stringify(newCore) }) if (matchIdx !== -1) { const next = [...prev] next[matchIdx] = { ...next[matchIdx], quantity: next[matchIdx].quantity + (item.quantity ?? 1) } return next } return [...prev, { ...item, _key: Date.now() + Math.random() }] }) } function removeFromCart(key) { setCart(prev => prev.filter(i => i._key !== key)) } function changeCartQty(key, newQty) { if (newQty <= 0) { removeFromCart(key) } else { setCart(prev => prev.map(i => i._key === key ? { ...i, quantity: newQty } : i)) } } function openEditDrawer(cartItem) { const product = products.find(p => p.id === cartItem.product_id) if (!product) return setCartOpen(false) setEditItem({ cartKey: cartItem._key, product, drawerState: cartItem._drawerState }) } function handleEditSave(updatedItem) { setCart(prev => prev.map(i => i._key === editItem.cartKey ? { ...updatedItem, _key: i._key } : i )) setEditItem(null) } async function sendOrder() { if (cart.length === 0) return setSending(true) setError('') setPrintAck(null) setCartOpen(false) try { // For new (free) tables, open the order now — lazily let activeOrderId = orderId if (!activeOrderId) { const { data: newOrder } = await client.post('/api/orders/', { table_id: Number(tableId) }) activeOrderId = newOrder.id setOrderId(activeOrderId) } const res = await client.post(`/api/orders/${activeOrderId}/items`, { items: cart.map(({ _key, _drawerState, ...item }) => item), }) const printResults = res.data.print_results ?? [] const allOk = printResults.length === 0 || printResults.every(r => r.success) setPrintAck({ allOk, results: printResults }) if (allOk) setTimeout(() => navigate('/tables'), 1200) } catch (err) { setError(err.response?.data?.detail || 'Σφάλμα αποστολής — η παραγγελία δεν στάλθηκε') } finally { setSending(false) } } async function retryNow() { if (!orderId) return setRetrying(true) try { const res = await client.post(`/api/orders/${orderId}/retry-print`) const printResults = res.data.print_results ?? [] const allOk = printResults.length === 0 || printResults.every(r => r.success) setPrintAck({ allOk, results: printResults }) if (allOk) setTimeout(() => navigate('/tables'), 1200) } catch { } finally { setRetrying(false) } } function saveAsDraft() { navigate(`/tables/${tableId}`, { replace: true }) } function leaveAndContinue() { navigate(`/tables/${tableId}`, { replace: true }) } function getProduct(id) { return products.find(p => p.id === id) } // Returns structured sections for the expanded cart view function buildItemSections(item, product) { const sections = [] if (item.selected_options?.length) { const prefIds = new Set( (product?.preference_sets || []).flatMap(ps => ps.choices.map(c => c.id)) ) // Build a map: prefChoiceId → preference set name const prefSetByChoiceId = {} ;(product?.preference_sets || []).forEach(ps => { ps.choices.forEach(c => { prefSetByChoiceId[c.id] = ps.name }) }) const quickNames = new Set((product?.quick_options || []).map(o => o.name)) const extraIds = new Set((product?.options || []).map(o => o.id)) // Group prefs: { prefSetName, choiceName, subName } const prefGroups = [] // Group extras: { name, subName, qty } — one entry per unique (id) const extraGroups = [] const quickLines = [] let i = 0 const opts = item.selected_options while (i < opts.length) { const o = opts[i] if (prefIds.has(o.id)) { // Collect sub immediately following (id === null) const setName = prefSetByChoiceId[o.id] ?? '' let subName = null if (i + 1 < opts.length && opts[i + 1].id == null) { subName = opts[i + 1].name i += 2 } else { i++ } // Merge into existing prefGroup for same setName, or create new const existing = prefGroups.find(g => g.setName === setName) if (existing) { // multiple choices from same set (shouldn't normally happen, but handle gracefully) existing.values.push(subName ? `${o.name} · ${subName}` : o.name) } else { prefGroups.push({ setName, values: [subName ? `${o.name} · ${subName}` : o.name] }) } } else if (o.id != null && extraIds.has(o.id)) { // Collect sub immediately following let subName = null if (i + 1 < opts.length && opts[i + 1].id == null) { subName = opts[i + 1].name i += 2 } else { i++ } // Merge duplicates const existing = extraGroups.find(g => g.id === o.id && g.subName === subName) if (existing) existing.qty++ else extraGroups.push({ id: o.id, name: o.name, subName, qty: 1 }) } else if (quickNames.has(o.name)) { quickLines.push(o) i++ } else { i++ } } // Deduplicate quick lines: multiple entries of same name → single entry with qty const quickDeduped = [] quickLines.forEach(o => { const existing = quickDeduped.find(x => x.name === o.name) if (existing) existing._qty = (existing._qty || 1) + 1 else quickDeduped.push({ ...o, _qty: 1 }) }) if (prefGroups.length > 0) sections.push({ type: 'prefs', lines: prefGroups }) if (quickDeduped.length > 0) sections.push({ type: 'quick', lines: quickDeduped }) if (extraGroups.length > 0) sections.push({ type: 'extras', lines: extraGroups }) } if (item.removed_ingredients?.length) { sections.push({ type: 'removed', lines: item.removed_ingredients.map(n => ({ name: n })) }) } if (item.notes) { sections.push({ type: 'note', lines: [{ name: item.notes }] }) } return sections } // Simple flat summary for the collapsed one-liner function buildItemSummary(item) { const lines = [] if (item.selected_options?.length) { item.selected_options.forEach(o => { if (o.price_delta && o.price_delta !== 0) lines.push(`${o.name} (${o.price_delta > 0 ? '+' : ''}${o.price_delta.toFixed(2)} €)`) else lines.push(o.name) }) } if (item.removed_ingredients?.length) lines.push(`Χωρίς: ${item.removed_ingredients.join(', ')}`) if (item.notes) lines.push(item.notes) return lines } // Print-failure dialog if (printAck && !printAck.allOk) { return (
⚠ Η παραγγελία αποθηκεύτηκε
Ένας ή περισσότεροι εκτυπωτές δεν ανταποκρίθηκαν.
{r.printer_name}
{!r.success &&Εκτυπωτής μη προσβάσιμος
}Επιλέξτε πώς να συνεχίσετε:
{error}
}Η παραγγελία είναι κενή.
) : (Πληκτρολογήστε για αναζήτηση…
) : results.length === 0 ? (Δεν βρέθηκαν προϊόντα για «{query}»
) : results.map(p => { const initials = p.name.trim().split(/\s+/).slice(0, 2).map(w => w[0]).join('').toUpperCase() return ( ) })}