const { Button, Card, PillTag, TextLink } = window.WaraqEnabDesignSystem_f0e2a0; const { useState, useEffect } = React; const REVIEW_CFG = window.WE_CONFIG || {}; const REVIEW_LOCAL = "we-reviews-v1"; /* Reviews live on the server. Until the api folder is uploaded they are kept in this browser so the flow can still be used and demonstrated. */ const reviews = { readLocal() { try { return JSON.parse(localStorage.getItem(REVIEW_LOCAL)) || []; } catch (e) { return []; } }, writeLocal(list) { try { localStorage.setItem(REVIEW_LOCAL, JSON.stringify(list)); } catch (e) {} }, async list() { if (REVIEW_CFG.reviewsEndpoint) { try { /* the server may not be uploaded yet — never let the page wait on it */ const ctrl = new AbortController(); const bail = setTimeout(() => ctrl.abort(), 4000); const res = await fetch(REVIEW_CFG.reviewsEndpoint, { signal: ctrl.signal }); clearTimeout(bail); const d = await res.json(); if (Array.isArray(d.reviews)) return d.reviews; } catch (e) { /* fall through to the local copy */ } } return reviews.readLocal(); }, async add(review) { const record = Object.assign({ id: String(Date.now()), at: new Date().toISOString() }, review); if (REVIEW_CFG.reviewsEndpoint) { try { const ctrl = new AbortController(); const bail = setTimeout(() => ctrl.abort(), 8000); const res = await fetch(REVIEW_CFG.reviewsEndpoint, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(Object.assign({ company: "" }, review)), signal: ctrl.signal }); clearTimeout(bail); const d = await res.json().catch(() => ({})); if (res.ok && d.ok) return { ok: true }; if (d.error) return { error: d.error }; } catch (e) { /* fall through to the local copy */ } } const list = reviews.readLocal(); if (review.ref && list.some(r => r.ref === review.ref)) return { error: t("This order has already been reviewed.") }; reviews.writeLocal([record].concat(list)); return { ok: true }; }, /* has this device already reviewed this order? */ reviewed(ref) { return reviews.readLocal().some(r => r.ref === ref); } }; /* Five stars. Read-only unless onChange is given. */ function Stars({ value, onChange, size = 22, label }) { const [hover, setHover] = useState(0); const shown = hover || value || 0; if (!onChange) { return ( {[1, 2, 3, 4, 5].map(n => )} ); } return ( {[1, 2, 3, 4, 5].map(n => ( ))} ); } function ratingSummary(list) { if (!list.length) return { avg: 0, count: 0 }; const sum = list.reduce((n, r) => n + (Number(r.rating) || 0), 0); return { avg: Math.round((sum / list.length) * 10) / 10, count: list.length }; } function useReviews(dishId) { const pick = (all) => dishId ? all.filter(r => (r.dishes || []).includes(dishId)) : all; const [list, setList] = useState(() => pick(reviews.readLocal())); useEffect(() => { let stop = false; reviews.list().then(all => { if (!stop) setList(pick(all)); }); return () => { stop = true; }; }, [dishId]); return list; } function ReviewCard({ review }) { const when = new Date(review.at); const named = (review.dishes || []).map(id => { let hit = null; MENU.forEach(s => s.items.forEach(i => { if (i.id === id) hit = i; })); return hit; }).filter(Boolean); return (
{when.toLocaleDateString()}
{review.comment &&

{review.comment}

}
{review.name || t("Guest")} {named.length > 0 && ( {named.slice(0, 3).map(i => {tn(i)})} )}
); } /* The form shown on the order page once the food has arrived. */ function ReviewForm({ order }) { const [rating, setRating] = useState(0); const [comment, setComment] = useState(""); const [name, setName] = useState(order.name || ""); const [picked, setPicked] = useState([]); const [busy, setBusy] = useState(false); const [err, setErr] = useState(""); const [done, setDone] = useState(() => reviews.reviewed(order.ref)); /* the dishes on this order, resolved back to menu items */ const dishes = []; (order.lines || []).forEach(l => { const id = String(l.id).split("--")[0]; if (dishes.some(d => d.id === id)) return; MENU.forEach(s => s.items.forEach(i => { if (i.id === id) dishes.push(i); })); }); const toggle = (id) => setPicked(p => p.includes(id) ? p.filter(x => x !== id) : p.concat(id)); const submit = async () => { setBusy(true); setErr(""); const r = await reviews.add({ ref: order.ref, rating, comment: comment.trim(), name: name.trim(), dishes: picked }); setBusy(false); if (r.ok) setDone(true); else setErr(r.error); }; if (done) { return (

{t("Thank you for the review.")}

{t("It helps the kitchen and it helps the next customer choose.")}

{t("Read all reviews")}
); } return (

{t("How was the food?")}

{t("Your review appears on the dish pages for other customers to read.")}

{dishes.length > 0 && (
{dishes.map(d => ( ))}
)}