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

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:
soroush.asadi
2026-06-02 09:37:59 +03:30
parent 2203ecbdaf
commit 24da1e0522
7 changed files with 70 additions and 22 deletions
@@ -255,6 +255,7 @@ export function PosScreen() {
addItem,
removeItem,
updateQty,
setNotes,
couponCode,
appliedCoupon,
setCouponCode,
@@ -1210,10 +1211,11 @@ export function PosScreen() {
<div
key={line.orderItemId ?? line.menuItem.id}
className={cn(
"flex items-center gap-2 rounded-lg border border-border p-2",
"flex flex-col gap-1.5 rounded-lg border border-border p-2",
line.isVoided && "opacity-60"
)}
>
<div className="flex items-center gap-2">
<div className="min-w-0 flex-1">
<MenuItemLabels
item={line.menuItem}
@@ -1291,6 +1293,18 @@ export function PosScreen() {
</>
) : null}
</div>
</div>
{!line.isVoided && (
<input
type="text"
value={line.notes ?? ""}
onChange={(e) => setNotes(line.menuItem.id, e.target.value)}
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
placeholder={t("itemNotePlaceholder")}
className="w-full rounded-md border border-border/70 bg-background px-2 py-1 text-[11px] placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-primary/40"
/>
)}
</div>
))
)}