feat(orders): per-item kitchen/bar notes (POS + QR app + KDS)
CI/CD / CI · API (dotnet build + test) (push) Successful in 57s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 57s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m6s
CI/CD / CI · Admin Web (tsc) (push) Successful in 35s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 2m43s
CI/CD / CI · API (dotnet build + test) (push) Successful in 57s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 57s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m6s
CI/CD / CI · Admin Web (tsc) (push) Successful in 35s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 2m43s
Lets the POS agent and the QR/app customer attach a free-text note to each order line (e.g. "no tomato", "extra hot") that reaches the kitchen/bar. - Backend already supported it (OrderItem.Notes persists; CreateOrderItemRequest and OrderItemDto carry Notes; LiveOrderDto items include it) — this wires the UI. - cart.store: add setNotes(menuItemId, notes); notes already travel in getPendingLines and round-trip via hydrateFromOrder. - POS pos-screen: a note input under each cart line. - QR guest menu: a note input under each cart line (QrCartLine.note). - KDS: render the note prominently under each item so kitchen/bar sees it. - i18n: pos.itemNotePlaceholder + qrMenu.itemNote (fa/ar/en). Note: notes are captured on items being added; editing a note on an already-submitted line is out of scope (no pending delta to re-send). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -407,29 +407,44 @@ export function QrGuestMenu({ code }: QrGuestMenuProps) {
|
||||
{cart.map((c) => (
|
||||
<div
|
||||
key={c.item.id}
|
||||
className="flex items-center justify-between gap-3 border-b px-3 py-3 last:border-0"
|
||||
className="flex flex-col gap-2 border-b px-3 py-3 last:border-0"
|
||||
>
|
||||
<div className="min-w-0 flex-1">
|
||||
<MenuItemLabels item={c.item} lines={1} primaryClassName="text-sm" />
|
||||
<p className="text-sm font-medium" style={{ color: primary }}>
|
||||
{formatCurrency(effectiveLinePrice(c.item), "fa-IR")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<QtyButton
|
||||
label="−"
|
||||
onClick={() => removeFromCart(c.item.id)}
|
||||
variant="outline"
|
||||
color={primary}
|
||||
/>
|
||||
<span className="min-w-6 text-center font-semibold">{c.qty}</span>
|
||||
<QtyButton
|
||||
label="+"
|
||||
onClick={() => addToCart(c.item)}
|
||||
variant="filled"
|
||||
color={primary}
|
||||
/>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="min-w-0 flex-1">
|
||||
<MenuItemLabels item={c.item} lines={1} primaryClassName="text-sm" />
|
||||
<p className="text-sm font-medium" style={{ color: primary }}>
|
||||
{formatCurrency(effectiveLinePrice(c.item), "fa-IR")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<QtyButton
|
||||
label="−"
|
||||
onClick={() => removeFromCart(c.item.id)}
|
||||
variant="outline"
|
||||
color={primary}
|
||||
/>
|
||||
<span className="min-w-6 text-center font-semibold">{c.qty}</span>
|
||||
<QtyButton
|
||||
label="+"
|
||||
onClick={() => addToCart(c.item)}
|
||||
variant="filled"
|
||||
color={primary}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
value={c.note ?? ""}
|
||||
onChange={(e) =>
|
||||
setCart((prev) =>
|
||||
prev.map((l) =>
|
||||
l.item.id === c.item.id ? { ...l, note: e.target.value } : l
|
||||
)
|
||||
)
|
||||
}
|
||||
placeholder={t("itemNote")}
|
||||
className="w-full rounded-md border qr-border bg-transparent px-2 py-1.5 text-xs placeholder:opacity-60 focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user