Stream linining
This commit is contained in:
+44
-38
@@ -12,6 +12,11 @@ import logo from "../../assets/ranaze-logo.png";
|
||||
|
||||
const queueStatuses = ["new", "making", "ready"];
|
||||
const orderIdleTimeoutMs = 90_000;
|
||||
const tabLabels = {
|
||||
order: "Order",
|
||||
queue: "Order Status",
|
||||
menu: "Menu Admin"
|
||||
};
|
||||
|
||||
const initialBuilder = (settings) => ({
|
||||
size: settings.sizeOptions[0]?.name ?? "Regular",
|
||||
@@ -132,22 +137,22 @@ export default function App() {
|
||||
const selectedDrink = menu.find((item) => item._id === selectedDrinkId) || null;
|
||||
const modeDetails = {
|
||||
premade: {
|
||||
stepLabel: "Quick pick",
|
||||
title: "Pick a house favorite",
|
||||
description: "Choose a ready-to-go drink and add it to the ticket in one tap.",
|
||||
buttonLabel: "Add this drink"
|
||||
stepLabel: "House favorite",
|
||||
title: selectedDrink?.name || "Pick a house favorite",
|
||||
description: "Choose one of our ready-to-enjoy drinks and add it in one tap.",
|
||||
buttonLabel: "Add to my order"
|
||||
},
|
||||
simple: {
|
||||
stepLabel: "Quick customize",
|
||||
title: selectedDrink?.name || "Choose a drink",
|
||||
description: "Make a few easy choices and keep the line moving.",
|
||||
buttonLabel: "Add customized drink"
|
||||
title: selectedDrink?.name || "Choose your drink",
|
||||
description: "Pick your drink, then make a few simple choices like size and sweetness.",
|
||||
buttonLabel: "Add to my order"
|
||||
},
|
||||
detailed: {
|
||||
stepLabel: "Full customize",
|
||||
title: selectedDrink?.name || "Choose a drink",
|
||||
description: "Adjust every detail, including toppings and special instructions.",
|
||||
buttonLabel: "Add fully customized drink"
|
||||
stepLabel: "Build it your way",
|
||||
title: selectedDrink?.name || "Build your drink",
|
||||
description: "Customize every detail, including ice, toppings, and special requests.",
|
||||
buttonLabel: "Add to my order"
|
||||
}
|
||||
};
|
||||
const activeMode = orderMode ? modeDetails[orderMode] : null;
|
||||
@@ -161,9 +166,9 @@ export default function App() {
|
||||
</div>
|
||||
<div className="brand-copy">
|
||||
<p className="eyebrow">Ranaze Bubble Tea & Slush</p>
|
||||
<h1>Tablet POS</h1>
|
||||
<h1>Choose your drink</h1>
|
||||
<p className="muted">
|
||||
Fast ordering for house favorites, easy edits, and detailed custom drinks.
|
||||
Fresh bubble tea, fruit slushes, and easy customizations made just the way you like them.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -174,7 +179,7 @@ export default function App() {
|
||||
className={`tab ${view === tab ? "is-active" : ""}`}
|
||||
onClick={() => setView(tab)}
|
||||
>
|
||||
{titleCase(tab)}
|
||||
{tabLabels[tab]}
|
||||
</button>
|
||||
))}
|
||||
</nav>
|
||||
@@ -185,10 +190,10 @@ export default function App() {
|
||||
{orderMode && (
|
||||
<div className="order-toolbar">
|
||||
<button className="text-btn" onClick={() => setOrderMode(null)}>
|
||||
Back to order options
|
||||
Back to drink options
|
||||
</button>
|
||||
<p className="muted order-toolbar-note">
|
||||
This screen returns to the start menu after 90 seconds of inactivity.
|
||||
This screen returns to the drink menu after 90 seconds of inactivity.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -196,27 +201,27 @@ export default function App() {
|
||||
{!orderMode && (
|
||||
<section className="panel mode-launcher">
|
||||
<div className="mode-launcher-copy">
|
||||
<p className="eyebrow">Start a new order</p>
|
||||
<h2>How would you like to order today?</h2>
|
||||
<p className="eyebrow">Welcome to Ranaze</p>
|
||||
<h2>How would you like to choose your drink?</h2>
|
||||
<p className="muted">
|
||||
Pick the path that feels easiest for your guest. You can switch anytime during the order.
|
||||
Start with a signature favorite, make a few quick changes, or fully customize your drink.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mode-grid">
|
||||
<button className="mode-card" onClick={() => setOrderMode("premade")}>
|
||||
<p className="eyebrow">Fastest</p>
|
||||
<h3>Pre-Made Recipes</h3>
|
||||
<p className="muted">Best for guests who want a house favorite with no extra decisions.</p>
|
||||
<h3>House Favorites</h3>
|
||||
<p className="muted">Perfect if you want one of our signature drinks with no extra steps.</p>
|
||||
</button>
|
||||
<button className="mode-card" onClick={() => setOrderMode("simple")}>
|
||||
<p className="eyebrow">Easy</p>
|
||||
<h3>Simple Customization</h3>
|
||||
<p className="muted">Choose a drink, pick a size and sweetness level, and place it quickly.</p>
|
||||
<h3>Quick Customization</h3>
|
||||
<p className="muted">Choose your drink, then adjust size and sweetness in just a few taps.</p>
|
||||
</button>
|
||||
<button className="mode-card" onClick={() => setOrderMode("detailed")}>
|
||||
<p className="eyebrow">Flexible</p>
|
||||
<h3>Detailed Customization</h3>
|
||||
<p className="muted">Fine-tune ice, toppings, and special instructions for exact orders.</p>
|
||||
<h3>Build It Your Way</h3>
|
||||
<p className="muted">Fine-tune ice, toppings, and special instructions for the exact drink you want.</p>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
@@ -267,7 +272,7 @@ export default function App() {
|
||||
<div className="inline-builder">
|
||||
<div className="inline-builder-header">
|
||||
<div>
|
||||
<p className="eyebrow">Selected drink</p>
|
||||
<p className="eyebrow">Your drink</p>
|
||||
<h3>{activeMode.title}</h3>
|
||||
</div>
|
||||
<div className="line-price">
|
||||
@@ -341,7 +346,7 @@ export default function App() {
|
||||
)}
|
||||
{orderMode === "premade" && (
|
||||
<div className="empty-state mode-note">
|
||||
This version uses the house recipe exactly as listed on the menu.
|
||||
This option keeps the drink exactly as our signature recipe.
|
||||
</div>
|
||||
)}
|
||||
<div className="builder-actions">
|
||||
@@ -375,14 +380,14 @@ export default function App() {
|
||||
<section className="ticket">
|
||||
<div className="ticket-header">
|
||||
<div>
|
||||
<p className="eyebrow">Current order</p>
|
||||
<h3>Current ticket</h3>
|
||||
<p className="eyebrow">Your order</p>
|
||||
<h3>Review your drinks</h3>
|
||||
</div>
|
||||
<button className="text-btn" onClick={() => setTicket([])}>Clear</button>
|
||||
<button className="text-btn" onClick={() => setTicket([])}>Clear all</button>
|
||||
</div>
|
||||
|
||||
<div className="ticket-lines">
|
||||
{ticket.length === 0 && <div className="empty-state">No items in the ticket.</div>}
|
||||
{ticket.length === 0 && <div className="empty-state">Your order is empty.</div>}
|
||||
{ticket.map((line, index) => (
|
||||
<article key={`${line.menuItemId}-${index}`} className="ticket-line">
|
||||
<header>
|
||||
@@ -397,10 +402,10 @@ export default function App() {
|
||||
</button>
|
||||
</header>
|
||||
<p>{line.size} • {line.sweetness} • {line.ice}</p>
|
||||
<p className="muted">{line.toppings.length ? line.toppings.join(", ") : "No toppings"}</p>
|
||||
{line.notes && <p className="muted">Note: {line.notes}</p>}
|
||||
<p className="muted">{line.toppings.length ? line.toppings.join(", ") : "No extra toppings"}</p>
|
||||
{line.notes && <p className="muted">Special request: {line.notes}</p>}
|
||||
<div className="line-meta">
|
||||
<span>Line total</span>
|
||||
<span>Drink total</span>
|
||||
<strong>{formatCurrency(lineTotal({ basePrice: line.basePrice }, line, settings))}</strong>
|
||||
</div>
|
||||
</article>
|
||||
@@ -409,21 +414,22 @@ export default function App() {
|
||||
|
||||
<div className="checkout-form">
|
||||
<label className="field">
|
||||
<span className="field-label">Customer name</span>
|
||||
<span className="field-label">Name for the order</span>
|
||||
<input
|
||||
placeholder="Optional"
|
||||
value={customerName}
|
||||
maxLength="40"
|
||||
onChange={(event) => setCustomerName(event.target.value)}
|
||||
/>
|
||||
</label>
|
||||
<label className="field">
|
||||
<span className="field-label">Payment</span>
|
||||
<span className="field-label">How would you like to pay?</span>
|
||||
<select
|
||||
value={paymentMethod}
|
||||
onChange={(event) => setPaymentMethod(event.target.value)}
|
||||
>
|
||||
<option value="cash">Cash</option>
|
||||
<option value="tab">House Tab</option>
|
||||
<option value="tab">Store Tab</option>
|
||||
<option value="unpaid">Pay Later</option>
|
||||
</select>
|
||||
</label>
|
||||
@@ -447,7 +453,7 @@ export default function App() {
|
||||
}
|
||||
}}
|
||||
>
|
||||
Place order
|
||||
Place my order
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -95,6 +95,11 @@ button {
|
||||
gap: 0.2rem;
|
||||
}
|
||||
|
||||
.brand-copy h1 {
|
||||
font-size: clamp(2rem, 3vw, 3rem);
|
||||
line-height: 0.98;
|
||||
}
|
||||
|
||||
.brand-copy p:last-child {
|
||||
margin: 0;
|
||||
max-width: 30rem;
|
||||
@@ -123,6 +128,13 @@ button {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
padding: 0.4rem;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.55);
|
||||
border: 1px solid rgba(16, 16, 16, 0.06);
|
||||
}
|
||||
|
||||
.screen {
|
||||
display: grid;
|
||||
grid-template-columns: 1.15fr 0.85fr;
|
||||
@@ -140,6 +152,7 @@ button {
|
||||
|
||||
.order-toolbar-note {
|
||||
margin: 0;
|
||||
font-size: 0.92rem;
|
||||
}
|
||||
|
||||
.queue-screen,
|
||||
@@ -170,6 +183,13 @@ button {
|
||||
|
||||
.mode-launcher-copy {
|
||||
max-width: 48rem;
|
||||
display: grid;
|
||||
gap: 0.45rem;
|
||||
}
|
||||
|
||||
.mode-launcher-copy h2 {
|
||||
font-size: clamp(2rem, 3.4vw, 3.3rem);
|
||||
line-height: 0.96;
|
||||
}
|
||||
|
||||
.mode-grid {
|
||||
@@ -181,7 +201,9 @@ button {
|
||||
.mode-card {
|
||||
display: grid;
|
||||
gap: 0.65rem;
|
||||
align-content: start;
|
||||
padding: 1.25rem;
|
||||
min-height: 200px;
|
||||
border: 1px solid rgba(16, 16, 16, 0.08);
|
||||
border-radius: 24px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
@@ -240,6 +262,7 @@ button {
|
||||
background: var(--accent);
|
||||
color: #fff6e8;
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 8px 18px rgba(16, 16, 16, 0.18);
|
||||
}
|
||||
|
||||
.text-btn {
|
||||
@@ -286,6 +309,12 @@ button {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.drink-card h3,
|
||||
.ticket-line strong,
|
||||
.queue-card h3 {
|
||||
line-height: 1.05;
|
||||
}
|
||||
|
||||
.drink-card.is-selected {
|
||||
border-color: rgba(246, 179, 62, 0.7);
|
||||
box-shadow:
|
||||
@@ -350,6 +379,7 @@ select {
|
||||
|
||||
.builder-intro {
|
||||
max-width: 28rem;
|
||||
margin-top: 0.35rem;
|
||||
}
|
||||
|
||||
.inline-builder {
|
||||
|
||||
Reference in New Issue
Block a user