From 1f4b4d7a16db317226259fd5dc752631928e9046 Mon Sep 17 00:00:00 2001 From: Zeeshaun Date: Tue, 5 May 2026 20:00:07 +0000 Subject: [PATCH] Restructure customer order flow around hidden cart --- client/src/App.jsx | 133 +++++++++++++++++++++++++++++------------- client/src/styles.css | 29 +++++++++ 2 files changed, 122 insertions(+), 40 deletions(-) diff --git a/client/src/App.jsx b/client/src/App.jsx index 458076d..18d17a9 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -14,8 +14,8 @@ import heroCup from "../../assets/ranaze-bubble-tea-single-cup-transparent.png"; const queueStatuses = ["new", "making", "ready"]; const orderIdleTimeoutMs = 90_000; const adminUnlockPressMs = 3_000; +const adminPassword = "admin"; const tabLabels = { - order: "Order", queue: "Order Status", menu: "Menu Admin" }; @@ -31,6 +31,7 @@ export default function App() { const adminPressTimerRef = useRef(null); const [view, setView] = useState("order"); const [orderMode, setOrderMode] = useState(null); + const [showCart, setShowCart] = useState(false); const [adminUnlocked, setAdminUnlocked] = useState(false); const [logoPressing, setLogoPressing] = useState(false); const [menu, setMenu] = useState([]); @@ -90,6 +91,12 @@ export default function App() { }; }, [view, orderMode]); + useEffect(() => { + if (!orderMode) { + setShowCart(false); + } + }, [orderMode]); + useEffect(() => { if (!settings) { return; @@ -149,7 +156,7 @@ export default function App() { return selectedCategory === "All" || item.category === selectedCategory; }); const selectedDrink = menu.find((item) => item._id === selectedDrinkId) || null; - const visibleTabs = adminUnlocked ? ["order", "queue", "menu"] : ["order"]; + const visibleTabs = adminUnlocked ? ["queue", "menu"] : []; const modeDetails = { premade: { stepLabel: "House favorite", @@ -176,7 +183,17 @@ export default function App() { window.clearTimeout(adminPressTimerRef.current); setLogoPressing(true); adminPressTimerRef.current = window.setTimeout(() => { - setAdminUnlocked((current) => !current); + if (adminUnlocked) { + setAdminUnlocked(false); + setView("order"); + } else { + const password = window.prompt("Enter admin password"); + if (password === adminPassword) { + setAdminUnlocked(true); + } else if (password !== null && password !== "") { + window.alert("Incorrect password"); + } + } setLogoPressing(false); }, adminUnlockPressMs); } @@ -186,6 +203,18 @@ export default function App() { setLogoPressing(false); } + function addLineToTicket(drink, lineBuilder) { + setTicket((current) => [ + ...current, + { + menuItemId: drink._id, + name: drink.name, + basePrice: drink.basePrice, + ...lineBuilder + } + ]); + } + return (
@@ -209,34 +238,50 @@ export default function App() {

- + )} {view === "order" && ( -
+
{orderMode && (
- -

- This screen returns to the drink menu after 90 seconds of inactivity. -

+
+ {ticket.length > 0 && ( + + )} +

+ This screen returns to the drink menu after 90 seconds of inactivity. +

+
)} @@ -276,7 +321,7 @@ export default function App() {
)} - {orderMode && ( + {orderMode && !showCart && (
@@ -286,7 +331,9 @@ export default function App() {
-
{formatCurrency(ticketTotal(ticket, settings))}
+ {ticket.length > 0 && ( + + )}
@@ -306,6 +353,10 @@ export default function App() { key={drink._id} className={`drink-card ${selectedDrinkId === drink._id ? "is-selected" : ""}`} onClick={() => { + if (orderMode === "premade") { + addLineToTicket(drink, initialBuilder(settings)); + return; + } setSelectedDrinkId(drink._id); setBuilder(initialBuilder(settings)); }} @@ -322,6 +373,16 @@ export default function App() { ))}
+ {orderMode === "premade" && ( +
+ Tap any drink to add it to your order. Use the order button whenever you are ready to review. +
+ )} +
+ )} + + {orderMode && !showCart && orderMode !== "premade" && ( +
{selectedDrink && builder ? (
@@ -398,24 +459,11 @@ export default function App() { /> )} - {orderMode === "premade" && ( -
- This option keeps the drink exactly as our signature recipe. -
- )}
) : ( -
Add menu items to start taking orders.
+
Choose a drink to customize it before adding it to your order.
)}
)} - {orderMode && ( -
+ {orderMode && showCart && ( +

Your order

Review your drinks

- +
+ + +
@@ -502,6 +553,8 @@ export default function App() { setTicket([]); setCustomerName(""); setPaymentMethod("cash"); + setShowCart(false); + setOrderMode(null); } catch (requestError) { setError(requestError.message); } diff --git a/client/src/styles.css b/client/src/styles.css index 7aaca24..fb11a99 100644 --- a/client/src/styles.css +++ b/client/src/styles.css @@ -169,6 +169,10 @@ button { gap: 1rem; } +.order-screen.is-single { + grid-template-columns: 1fr; +} + .order-toolbar { grid-column: 1 / -1; display: flex; @@ -183,6 +187,14 @@ button { font-size: 0.92rem; } +.order-toolbar-actions, +.ticket-header-actions { + display: flex; + align-items: center; + gap: 0.75rem; + flex-wrap: wrap; +} + .queue-screen, .menu-screen { display: grid; @@ -341,6 +353,11 @@ button { box-shadow: 0 12px 24px rgba(223, 162, 50, 0.28); } +.subtle-btn { + box-shadow: none; + padding-inline: 1rem; +} + .primary-btn:disabled { opacity: 0.6; cursor: not-allowed; @@ -458,6 +475,12 @@ select { border-top: 1px solid var(--line); } +.cart-panel .ticket { + margin-top: 0; + padding-top: 0; + border-top: 0; +} + .ticket-line header { display: flex; justify-content: space-between; @@ -574,6 +597,12 @@ select { flex-direction: column; } + .order-toolbar-actions, + .ticket-header-actions { + align-items: flex-start; + flex-direction: column; + } + .inline-builder-header { flex-direction: column; }