Commit Graph

112 Commits

Author SHA1 Message Date
soroush.asadi c360fbb068 feat(orders): recent orders view with receipt / kitchen / bar reprint
CI/CD / CI · API (dotnet build + test) (push) Successful in 41s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 3m34s
Adds a "سفارش‌ها" (Orders) nav page listing closed orders by day (date +
branch filter, paged), each with reprint actions:
- چاپ فاکتور  → customer receipt
- فیش آشپزخانه → kitchen ticket (all stations)
- one button per print station (e.g. Bar) → reprints only that station's items

Backend: the kitchen print endpoint gains an optional ?stationId= to reprint a
single station; PrintKitchenTicketAsync filters its station groups accordingly
(NO_STATION_ITEMS when that station has nothing on the order). Nav gated by
ViewOrders (visible to branch staff too). fa/en/ar strings added.

Note: local backend build couldn't run (NU1301 — NuGet restore network timeout);
dashboard typecheck is clean and the C# changes are minimal — CI builds via the
Nexus mirror.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 23:15:34 +03:30
soroush.asadi 1264606410 fix(pos): show the post-payment receipt sheet (was rendered in the wrong view)
CI/CD / CI · API (dotnet build + test) (push) Has been cancelled
CI/CD / CI · Admin API (dotnet build) (push) Has been cancelled
CI/CD / CI · Dashboard (tsc) (push) Has been cancelled
CI/CD / CI · Admin Web (tsc) (push) Has been cancelled
CI/CD / CI · Website (tsc) (push) Has been cancelled
CI/CD / CI · Koja (tsc) (push) Has been cancelled
CI/CD / Deploy · all services (push) Has been cancelled
The payment-success sheet with the "چاپ فاکتور" button lives in the order-view
return, but confirmPay called backToBoard() which switched to the board view —
so the sheet never rendered and the cashier couldn't print after paying.

Now payment clears the cart + closes the pay sheet but STAYS on the order view,
so the success sheet shows; returning to the board happens when the cashier taps
"سفارش جدید" or the backdrop. Offline/local orders still go straight to the board.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 22:17:14 +03:30
soroush.asadi cad5ba6ea3 fix(pos): make the per-item note obvious with an explicit button
CI/CD / CI · API (dotnet build + test) (push) Successful in 46s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m9s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 47s
CI/CD / CI · Koja (tsc) (push) Successful in 51s
CI/CD / Deploy · all services (push) Successful in 3m11s
The note control was an unlabeled icon next to the trash on each cart line, so
cashiers couldn't tell where to add a note. Replaced it with a clear
"افزودن یادداشت" (add note) text button under each item; clicking it reveals the
note input, which collapses again on blur when left empty. Existing notes still
show the editable field.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 22:10:29 +03:30
soroush.asadi 5596e8dbc5 chore(pos): fully remove the classic POS
CI/CD / CI · API (dotnet build + test) (push) Successful in 49s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m11s
CI/CD / CI · Admin Web (tsc) (push) Successful in 41s
CI/CD / CI · Website (tsc) (push) Successful in 47s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 2m52s
POS v2 has been the default at /pos for a while; this deletes the old classic
POS entirely:
- removed the /pos-classic route and all classic-only components
  (pos-screen, pos-pay-panel, pos-table-board, pos-queue-bar, pos-receipt-modal,
  pos-slip-modal, pos-receipt-print.css)
- relocated the two modules POS v2 still shared into the pos2 tree
  (lib/pos/submit-order → lib/pos2, components/pos/pos-customer-picker → pos2),
  so the components/pos and lib/pos folders are gone
- dropped the now-dead "نسخه کلاسیک" (classic version) button + RotateCcw import
  from the POS v2 header, and updated stale comments

POS v2 (/pos) is unchanged and fully self-contained. Typecheck clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 19:42:32 +03:30
soroush.asadi 46f962eb75 fix(pos): keep the receipt printable after paying an order
CI/CD / CI · API (dotnet build + test) (push) Successful in 56s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 31s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 39s
CI/CD / CI · Website (tsc) (push) Successful in 47s
CI/CD / CI · Koja (tsc) (push) Successful in 1m1s
CI/CD / Deploy · all services (push) Successful in 3m0s
Bug: confirming payment ran backToBoard() → clearSession(), wiping the cart's
activeOrderId, so the order vanished from the POS and the "print receipt" button
(which keys off activeOrderId) went dead — the only escape was a 4s toast.

Fix: capture the just-paid order id in local state (survives the session clear)
and show a persistent payment-success sheet with a "چاپ فاکتور" button so the
cashier can print/reprint the customer receipt, then "سفارش جدید" to continue.
Shared printReceiptById() backs both the in-order button and the success sheet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 19:14:01 +03:30
soroush.asadi 6184c83fa7 feat(pos): print customer receipt from the POS page
CI/CD / CI · API (dotnet build + test) (push) Successful in 44s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 32s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m12s
CI/CD / CI · Admin Web (tsc) (push) Successful in 39s
CI/CD / CI · Website (tsc) (push) Successful in 48s
CI/CD / CI · Koja (tsc) (push) Successful in 51s
CI/CD / Deploy · all services (push) Successful in 3m0s
POS v2 auto-printed the receipt on full payment but had no manual button. Adds a
"چاپ فاکتور" (print receipt) action in the order panel that prints/reprints the
active saved order's customer receipt, plus a "print receipt" action on the
payment-success toast. Replaces the dead disabled "hold" placeholder button.
Backend print endpoint unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 14:18:20 +03:30
soroush.asadi 0c2ded4070 feat(pos): set a per-item note on each cart line in POS v2
CI/CD / CI · API (dotnet build + test) (push) Successful in 43s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 32s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m13s
CI/CD / CI · Admin Web (tsc) (push) Successful in 40s
CI/CD / CI · Website (tsc) (push) Successful in 48s
CI/CD / CI · Koja (tsc) (push) Successful in 52s
CI/CD / Deploy · all services (push) Successful in 3m9s
The cart store, order payload (create + add-items + offline), KDS ticket and
receipt already supported per-item notes — but POS v2 had no way to enter one.
Adds a note button on each cart line that toggles an inline input (e.g. "no
sugar"); the note shows highlighted when set and rides along to the kitchen/bar
ticket. No backend change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 14:05:00 +03:30
soroush.asadi 2a24798a59 feat(audit): show actor full name + role in logs, click to view details
CI/CD / CI · API (dotnet build + test) (push) Successful in 44s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 31s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m11s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 3m39s
Logs showed the raw User ID (ActorName was almost never stored) and an English
role enum. Now:

- AuditController resolves each entry's actor to the employee's CURRENT full name
  and localized role at read time (joins Employees with IgnoreQueryFilters, so it
  also names soft-deleted staff and fixes all historical rows — no migration).
- The audit table renders "Full name (Role)" with the role localized (fa/en/ar);
  the name is a button that opens an employee-details dialog.
- New EmployeeDetailsDialog: fetches the employee and shows name, role, phone,
  base salary, and an "Open in HR" link; handles removed staff gracefully.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 11:24:06 +03:30
soroush.asadi 6d71770f2e fix(auth): redirect already-signed-in users away from the register page
CI/CD / CI · API (dotnet build + test) (push) Successful in 44s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 31s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 51s
CI/CD / Deploy · all services (push) Successful in 2m55s
Mirrors the login guard: visiting /register while authenticated redirects to the
dashboard home (/) instead of showing the form. Gated on _hasHydrated; shows a
brief redirecting state. Reuses the existing auth.redirecting string.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 11:11:10 +03:30
soroush.asadi fd1f985597 fix(auth): redirect already-signed-in users away from the login page
CI/CD / CI · API (dotnet build + test) (push) Successful in 40s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 47s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 2m57s
Visiting /login while authenticated now redirects to the app (/pos) instead of
showing the login form again. Guarded on _hasHydrated so the not-yet-rehydrated
(null) session isn't misread, and renders a brief "redirecting" state instead of
flashing the form. fa/en/ar strings added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 10:39:27 +03:30
soroush.asadi d261c13175 docs(website): note KDS station tabs in the kitchen-display guide
CI/CD / CI · API (dotnet build + test) (push) Successful in 42s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 33s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m11s
CI/CD / CI · Admin Web (tsc) (push) Successful in 39s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 3m44s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 10:29:15 +03:30
soroush.asadi 958addf734 feat(kds): filter the kitchen display by station (kitchen / bar)
CI/CD / CI · API (dotnet build + test) (push) Successful in 44s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Has been cancelled
CI/CD / CI · Admin Web (tsc) (push) Has been cancelled
CI/CD / CI · Website (tsc) (push) Has been cancelled
CI/CD / CI · Koja (tsc) (push) Has been cancelled
CI/CD / Deploy · all services (push) Has been cancelled
Complements the separate kitchen/bar printers with an on-screen split. The live
order DTO now carries each item's prep station (MenuItem → Category →
KitchenStation), and the KDS shows station tabs (All / Kitchen / Bar / …) that
appear only once ≥2 stations are in play. Selecting a station shows just the
tickets — and just the items within each ticket — for that station, so bar staff
see drinks and kitchen staff see food. Single-station cafés see the board
unchanged. fa/en/ar strings added.

Note: order status is still per-order (one advance button); the split is for
viewing/printing, not per-item status.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 10:27:40 +03:30
soroush.asadi 8703e9cf87 docs(website): knowledge-base guide for printing (receipt, kitchen, bar)
CI/CD / CI · API (dotnet build + test) (push) Successful in 42s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 29s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m12s
CI/CD / CI · Admin Web (tsc) (push) Successful in 39s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 3m0s
Documents the new separate kitchen/bar print stations plus the customer
receipt (factor): how to set printer IPs, create Kitchen/Bar stations, route
menu categories to a station, and what auto-prints at send-to-kitchen vs
payment. fa/en; auto-listed on /docs and in the sitemap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 09:58:22 +03:30
soroush.asadi fb6a20eaa1 feat(print): separate kitchen & bar printers via print stations UI
CI/CD / CI · API (dotnet build + test) (push) Successful in 47s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 28s
CI/CD / CI · Dashboard (tsc) (push) Has been cancelled
CI/CD / CI · Admin Web (tsc) (push) Has been cancelled
CI/CD / CI · Website (tsc) (push) Has been cancelled
CI/CD / CI · Koja (tsc) (push) Has been cancelled
CI/CD / Deploy · all services (push) Has been cancelled
The print engine already routed items to per-station printers (MenuCategory →
KitchenStation.PrinterIp, falling back to the branch kitchen printer) and prints
the customer receipt to the receipt printer — but there was no UI to set it up.
This exposes it:

- Settings → "Kitchen & bar printers": create/edit/delete print stations, each
  with its own printer IP/port, with a per-station test print (gated by
  ManageKitchenStations).
- Menu category editor: a "Print station" dropdown to route each category to a
  station (food → Kitchen, drinks → Bar); no station = branch kitchen printer.

Result: kitchen and bar tickets print on separate printers, while the customer
factor/receipt keeps printing on the receipt printer. fa/en/ar strings added.
No backend/migration changes — purely wiring the existing capability.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 09:56:14 +03:30
soroush.asadi 97bd63015f docs(website): knowledge-base guides for notifications & roles + sitemap docs pages
CI/CD / CI · API (dotnet build + test) (push) Successful in 45s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 28s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m9s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 48s
CI/CD / Deploy · all services (push) Successful in 1m51s
The recent dashboard features shipped without knowledge-base coverage. Adds two
fa/en guides at meezi.ir/docs:
- "Notifications & sound" — bell/unread count, configurable sound (chime + volume
  + preview), desktop/Windows popups, browser-tab counter, and click-to-navigate
  to the related page.
- "Roles & permissions" — base roles, defining custom roles via the permission
  matrix (CRUD + sensitive actions), assigning them, and how page/action access
  is enforced.

Also fixes a standing SEO gap: the sitemap listed only /docs, never the
per-feature /docs/{slug} pages — now all guide pages (fa+en) are included so the
whole knowledge base is crawlable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 07:19:23 +03:30
soroush.asadi 3dfcb1585b feat(notifications): click a notification to jump to its related page
CI/CD / CI · API (dotnet build + test) (push) Successful in 42s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 28s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m9s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 2m45s
Every notification surface now deep-links to where the staff member needs to act:
- bell dropdown: clicking an actionable notification navigates and closes the
  dropdown (platform broadcasts still expand inline to show their text)
- notifications page: rows navigate to the right page
- in-app toast: gains a "View" action button
- desktop/Windows popup: clicking it focuses the tab and navigates

Routing is now permission-aware via a single resolver (notification-routes.ts):
a new-order alert sends a kitchen user to /kds, a cashier to /pos, and a floor
user to /tables — never to a page their role can't open; a waiter call → /tables.
This also fixes the old bug where table_call_waiter (which carries a referenceId)
wrongly routed to /kds. Toast/desktop clicks navigate client-side through a small
event bridge mounted in the dashboard shell.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 06:08:18 +03:30
soroush.asadi 2cff5051ac feat(rbac): gate pages and action buttons in the UI by permission
CI/CD / CI · API (dotnet build + test) (push) Successful in 39s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 28s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m8s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 2m45s
Nav already hides pages a role can't view (NAV_REQUIRED_PERMISSION). This wraps
the sensitive/CRUD action controls in <Can permission> so users only see what
they can do (server still enforces):

- POS/orders: void → VoidOrder, cancel → VoidOrder, transfer → EditOrder,
  pay/split → HandlePayments
- menu/inventory/coupons/customers/reservations/expenses/taxes/branches:
  add/edit/delete buttons → the matching Create/Edit/Delete permission
- reports CSV export → ExportReports; SMS send → SendSms, settings → ManageSmsSettings
- home dashboard: revenue/orders KPI queries gated on ViewReports so non-report
  roles don't 403 on the landing page

(Refund/discount/comp/cash-drawer have no UI control yet — no buttons to gate.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 05:58:56 +03:30
soroush.asadi 53d90fa357 feat(rbac): full permission catalog in the custom-role matrix UI (fa/en/ar)
CI/CD / CI · API (dotnet build + test) (push) Successful in 42s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m7s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 3m24s
Mirrors the expanded backend catalog on the client: the Permission type and the
custom-role permission matrix now expose all ~80 capabilities grouped into 16
sections (admin, branches, menu, inventory, taxes, staff, tables, orders,
register, queue/kitchen, delivery, customers, coupons, marketing, reports,
expenses), each with fa/en/ar labels. Nav visibility now maps each page to its
View permission; taxes & branches become permission-driven (managers can view),
leaving billing as the sole hard owner-only nav gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 05:49:11 +03:30
soroush.asadi 170a9aa7ac feat(dashboard): Notifications & sound settings panel (fa/en/ar)
CI/CD / CI · API (dotnet build + test) (push) Successful in 1m5s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m9s
CI/CD / CI · Admin Web (tsc) (push) Successful in 39s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 3m46s
New Settings → "Notifications & sound" leaf to make the alert channels
changeable: toggle sound (+ picker with live preview + volume slider),
enable desktop notifications (permission flow + test button), toggle the
tab unread badge and in-app toasts. Strings added for fa/en/ar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 05:08:39 +03:30
soroush.asadi 149a4d88cd feat(dashboard): configurable notification sound, desktop popups & tab unread badge
CI/CD / CI · API (dotnet build + test) (push) Has been cancelled
CI/CD / CI · Admin API (dotnet build) (push) Has been cancelled
CI/CD / CI · Dashboard (tsc) (push) Has been cancelled
CI/CD / CI · Admin Web (tsc) (push) Has been cancelled
CI/CD / CI · Website (tsc) (push) Has been cancelled
CI/CD / CI · Koja (tsc) (push) Has been cancelled
CI/CD / Deploy · all services (push) Has been cancelled
Per-device notification preferences (localStorage) drive three new alert
channels in the dashboard shell, all fed by the existing SignalR
NotificationReceived events:

- Sound: 6 selectable procedural Web Audio chimes + volume, no asset files.
- Desktop/Windows popups via the Notification API, fired only when the tab
  is backgrounded (in-app toast covers the focused case).
- Unread count on the browser tab: (N) title prefix + numbered favicon badge.

useOrderAlerts is now the single orchestrator (sound + toast + desktop),
each gated by prefs; topbar feed enableToasts disabled to avoid double toasts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 05:08:02 +03:30
soroush.asadi aebfa825cd feat: custom roles with per-permission matrix for café owners
- Owner can define named custom roles (e.g. Barista, Supervisor) with
  color, description, and a fine-grained permission set (21 permissions
  across 7 categories: admin, menu, staff, customer, reports, ops, kitchen)
- Employee assigned a custom role gets its permissions embedded in the
  JWT at login (customPerms claim) and parsed by TenantMiddleware —
  overrides the static EmployeeRole matrix for all API permission checks
- New endpoints: GET/POST/PATCH/DELETE /api/cafes/{id}/custom-roles and
  PUT /api/cafes/{id}/employees/{id}/custom-role for assignment
- Dashboard Settings → Team & Staff → Custom Roles panel with grouped
  checkbox matrix, group-level toggles, color preset picker, CRUD forms,
  and employee-count display; translations in fa/en/ar
- EF migration adds CustomRoles table + nullable CustomRoleId FK on Employees
- POS slip now shows per-item notes on both thermal print and bill preview

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 03:12:43 +03:30
soroush.asadi 73a5e5183b fix(seed): IgnoreQueryFilters on all seeder queries + sitemap invalid date guard
CI/CD / CI · API (dotnet build + test) (push) Successful in 45s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 31s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m13s
CI/CD / CI · Admin Web (tsc) (push) Successful in 40s
CI/CD / CI · Website (tsc) (push) Successful in 47s
CI/CD / CI · Koja (tsc) (push) Successful in 59s
CI/CD / Deploy · all services (push) Successful in 3m45s
DemoSeedService / DemoMenuSeeder:
  Add IgnoreQueryFilters() to every seeder lookup (Taxes, MenuCategories,
  MenuItems). Soft-deleted rows still hold their PKs; without this a second
  seed run after user-deletion throws a PK collision on the Tax or category
  that was soft-deleted but is still in the index.

sitemap.ts:
  Guard new Date(post.date) against empty / missing frontmatter date fields.
  new Date("") = Invalid Date → broken <lastmod> in sitemap XML.
  Fall back to the build-time date when the post date is absent or invalid.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 15:54:07 +03:30
soroush.asadi 24fbbcb01c fix(admin): don't prefill a fake phone on the admin login in production
CI/CD / CI · API (dotnet build + test) (push) Successful in 50s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m12s
CI/CD / CI · Admin Web (tsc) (push) Successful in 40s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 1m1s
CI/CD / Deploy · all services (push) Successful in 2m0s
The admin login OTP tab hard-coded phone "09120000001" as the initial value.
In production that placeholder belongs to no SystemAdmin, so hitting "send code"
returns NOT_FOUND → 404 (which WCDN then repaints as an HTML error page) — it
looked like the login endpoint was broken. Keep the convenience prefill in
development only; ship an empty field in production so the admin types their
real number (e.g. the registered admin phone).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 00:44:57 +03:30
soroush.asadi a967e5d211 fix(admin): keep admin panel logged in — refresh the token instead of dying at 7 days
CI/CD / CI · API (dotnet build + test) (push) Successful in 39s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 29s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m6s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
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 2m35s
Prod diag showed every /api/admin/* call returning 401 with
"IDX10223: token expired, ValidTo 06/09" — the admin access token was 6 days
dead and nothing renewed it, so cafes/tickets/integrations/settings all loaded
empty. The admin web (unlike the café dashboard) had NO refresh logic at all:
it only ever sent the access token, and its 401 handler early-returned on any
error code before the login redirect, so the admin wasn't even bounced to login
— pages just showed no data.

Client (admin-client.ts): add a silent refresh-on-401 mirroring the dashboard —
one shared in-flight POST /api/admin/auth/refresh for a burst of 401s, replay
the original request on success, force-logout only on a definitive 4xx, and
ride out a transient failure (API restarting during deploy) without logging out.

Backend (AdminAuthService): make refresh non-rotating + sliding (reuse the
presented refresh token and re-store it) instead of revoke-and-mint, so the
dashboard's many concurrent refreshes don't race the rotated token — same fix
already applied to the main API.

Also bump admin tokens 7d/30d → 30d/365d to match the main API, so the session
is long-lived even before the first refresh round-trip.

tsc clean; Admin.API builds clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 20:57:21 +03:30
soroush.asadi 82d1cf8e9e fix(auth): stop logging users out on every deploy
CI/CD / CI · API (dotnet build + test) (push) Successful in 46s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 28s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m8s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 2m50s
Diagnostic on prod confirmed the backend keeps sessions valid across deploys
(stable 64-char JWT key, 30-day access tokens, 62 refresh tokens persisting in
Redis with appendonly; redis/db never restart on deploy). The forced logout was
client-side:

1. The axios refresh path treated ANY refresh failure as "session gone" and
   nuked the tokens. During the ~30s API restart window of a deploy, the refresh
   POST gets a 502/timeout (transient) → user kicked to /login. Now refresh
   distinguishes a definitive 4xx (truly invalid/expired refresh → log out) from
   a transient network/5xx failure (reject + keep the session; retry later).
   Refresh tokens are opaque Redis GUIDs, so they survive even a key rotation —
   the only thing that was breaking sessions was this over-eager logout.

2. PWA service worker served a stale app shell after an update, pointing at JS
   chunks the new build replaced. Added skipWaiting + clientsClaim +
   cleanupOutdatedCaches and a NetworkFirst handler for navigations so the HTML
   and its chunk refs always match the live deploy; hashed static stays
   CacheFirst.

Net: a normal update no longer logs anyone out. tsc clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 20:42:38 +03:30
soroush.asadi d4d7b7e679 feat(website): full Meezi knowledge base with per-feature wireframes
CI/CD / CI · API (dotnet build + test) (push) Successful in 40s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 29s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m8s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 1m53s
Turns the static /docs page into a real help center. Every feature now has a
detail page at /docs/{slug} with a minimal wireframe mockup + concrete Persian
how-to steps (English mirror), grouped into 6 sections.

- guide-data.tsx: typed GUIDE_FEATURES (21 features — pos, tables, kds, queue,
  reservations, menu, inventory, crm, coupons, sms, reviews, reports, expenses,
  shifts, taxes, hr, branches, subscription, settings, qr-menu, koja) with
  fa/en title, tagline, 5–8 steps, tips, tier badge, group, wireframe variant.
- wireframes.tsx: 7 reusable minimal line-art variants (board/order/menu/list/
  dashboard/form/phone), brand-colored, RTL-aware.
- docs/[slug]/page.tsx: dynamic guide page (hero, wireframe + numbered steps,
  tips, prev/next, support CTA); generateStaticParams + generateMetadata; 404
  for unknown slugs.
- docs/page.tsx: module cards now sourced from GUIDE_FEATURES, grouped, linking
  to the detail pages.

Verified via SSR: index lists all 21, detail pages render titles + wireframe,
en mirror 200, unknown slug 404, tsc clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 19:00:10 +03:30
soroush.asadi d407f0b3e9 fix(brand): real Meezi icon/favicon on website + admin (was missing)
CI/CD / CI · API (dotnet build + test) (push) Successful in 40s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 29s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Has been cancelled
- web/website: manifest referenced /icon-192.png and /icon-512.png that
  didn't exist (broken favicon). Added the real transparent Meezi mark
  (32/180/192/512) + wired icons into metadata. OG image stays dynamic.
- web/admin: had no metadata or icons at all. Added title template,
  favicon/apple icons (icons/ dir), themeColor, noindex.

Dashboard + Koja already carry the real logo; web apps are now consistent.
Mobile launcher icons will be handled with the Android packaging task.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:37:21 +03:30
soroush.asadi 72ab09189c fix(brand): real transparent Meezi icon + guest-menu image placeholder
CI/CD / CI · API (dotnet build + test) (push) Successful in 42s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 29s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m9s
CI/CD / CI · Admin Web (tsc) (push) Successful in 39s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 3m24s
- Icons/favicon were a plain solid-green square (a 547B placeholder). Replaced
  with the actual Meezi mark (green rounded square + menu lines) on transparent
  corners, generated at 32/48/180/192/512 + a full-bleed green maskable-512.
  Wired 32/48 favicon + 180 apple-touch-icon into the panel and /q metadata.
  Copied the same icons to Koja for consistent branding.
- Guest QR menu showed blank muted boxes for items without a photo. Added a
  minimal themed café-cup placeholder (MenuImageFallback) across all four
  layouts so the menu looks intentional. (Admin/POS already had placeholders.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:17:52 +03:30
soroush.asadi 456a446850 feat(meta): per-page titles + favicon/app icons + PWA across the panel
CI/CD / CI · API (dotnet build + test) (push) Successful in 40s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 31s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m9s
CI/CD / CI · Admin Web (tsc) (push) Successful in 39s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 2m54s
The app had no metadata anywhere — pages showed no <title> and no favicon
or app icon. Added:
- Root metadata in [locale]/layout: title default + "%s — میزی" template,
  description, icons (favicon + apple-touch-icon → /icons), manifest link,
  appleWebApp, themeColor viewport, noindex (private panel).
- Per-page title on all 22 dashboard route pages (داشبورد, منو, گزارش‌ها, …).
- Guest menu (/q) layout: own title + icon + manifest.

PWA + favicon now use the Meezi icon everywhere. Verified via SSR: titles
render (e.g. "منو — میزی") and icon/manifest/apple-touch-icon links present.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 15:47:00 +03:30
soroush.asadi 4523c8861f feat(ui): grouped thousands separators for price/amount inputs
Price fields showed raw digits (1490000) while typing — hard to read for
Toman amounts. New shared MoneyInput groups as you type (1,490,000),
accepts Persian/Arabic digits, and reports a raw digit string so callers
keep parsing unchanged. Applied to menu item price, branch price override,
expense amount, and payment-correction replacement amount. Displays already
group via formatCurrency (incl. the QR guest-menu preview).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 15:46:25 +03:30
soroush.asadi a855cf1d80 feat(auth): admin-issued café recovery key login
CI/CD / CI · API (dotnet build + test) (push) Successful in 5m6s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 1m30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 1m0s
CI/CD / Deploy · all services (push) Successful in 5m31s
Platform admins can generate a permanent recovery key per café (admin
panel → Cafés). The café Owner uses it to sign in when OTP access is lost;
once authenticated, all server-side data syncs as normal (data is per-café
on the server, the device only caches it).

Backend:
- Cafe.RecoveryKeyHash (SHA-256, unique index) + RecoveryKeyCreatedAt; migration
- RecoveryKeyGenerator util: MZ-XXXXX-XXXXX-XXXXX-XXXXX, ~190-bit entropy,
  stored as SHA-256 (API-token pattern — raw key shown once, never retrievable)
- Admin: POST/DELETE /api/admin/cafes/{id}/recovery-key (key returned once);
  café list now reports HasRecoveryKey + RecoveryKeyCreatedAt
- Login: POST /api/auth/login-key → exact-hash lookup → resolves café Owner →
  issues normal JWT; rate-limited (auth-otp), suspended/no-owner guarded, logged

Admin UI: per-café generate / regenerate / revoke with one-time reveal + copy.
Dashboard login: discreet "ورود با کلید بازیابی" link → key field. fa/en/ar.

86 tests pass; all tsc clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 15:10:11 +03:30
soroush.asadi 76d4434581 fix(qr): guest menu 500 (SSR) + remove café discovery from owner panel
1. The /q/{code} guest menu returned HTTP 500 on every load. Root cause:
   menu-item-model-viewer.tsx did a top-level `import "@google/model-viewer"`,
   a browser-only lib that touches `self` at module evaluation. Next pulled
   it into the server module graph (page → qr-guest-menu → qr-menu-3d-sheet →
   model-viewer) and SSR crashed with "self is not defined". Now the library
   is imported lazily inside useEffect (client-only); a poster placeholder
   shows until the custom element registers. Verified /q/* now returns 200.

2. Removed the "discover" (browse other cafés) item from the café owner
   sidebar — café discovery belongs in Koja, not the owner panel. The owner
   still manages their OWN Koja listing from Settings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 15:08:48 +03:30
soroush.asadi 00649d0248 feat(sms): bring-your-own-provider — cafés use their own SMS account
CI/CD / CI · API (dotnet build + test) (push) Successful in 40s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m8s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 5m16s
The platform no longer sells SMS. Each café saves its OWN Kavenegar API
key + sender line (new Cafes columns + migration) and campaigns are sent
and billed through that account.

Backend:
- GET/PUT /sms/settings (Manager/Owner; key echoed masked, verified
  against the provider before saving)
- campaign + balance use the café's credentials; SMS_NOT_CONFIGURED
  error when missing; plan-tier SMS gating removed everywhere
  (PlanLimitChecker, SmsMarketingService, billing status)
- platform Kavenegar config stays ONLY for login OTPs (env/DB)
- design-time DbContext factory so `dotnet ef migrations add` works
  without booting the host

Dashboard:
- SMS screen: provider-settings card, not-configured callout, campaign
  form disabled until configured; quota bar removed (usage stays as info)
- subscription screen + plan comparison no longer show SMS limits

Admin panel:
- Kavenegar/SMS section removed from integrations (request field now
  optional; stored OTP config untouched)
- SMS limit field removed from the plan editor
- nav label "درگاه و پیامک" → "درگاه پرداخت و AI"

fa/en/ar translations. 86 tests pass; all tsc clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 09:23:50 +03:30
soroush.asadi 615d5348de fix(subscription): plan comparison + checkout read the live plan catalog
CI/CD / CI · API (dotnet build + test) (push) Successful in 42s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 47s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 3m27s
The merchant plan page hard-coded 4 tiers, prices and a feature matrix
that drifted from the admin-editable platform catalog (Starter tier
missing, stale prices/features). PlanComparison and CheckoutScreen now
consume /platform/plans + new /platform/features-catalog:

- columns = active plans by SortOrder (incl. Starter), names from
  DisplayNameFa/En, prices from MonthlyPriceToman
- limit rows from PlanLimitsData (int.MaxValue → "نامحدود")
- feature rows from the feature catalog, ticked via FeatureKeys
- checkout validates the ?plan= param against isBillableOnline and
  prices from the catalog — no more client-side price constants

fa/en/ar limit-row labels added.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 08:16:29 +03:30
soroush.asadi 74f46a4781 fix(dashboard): Set spread → Array.from for CI tsconfig target
CI/CD / CI · API (dotnet build + test) (push) Successful in 41s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 39s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 51s
CI/CD / Deploy · all services (push) Successful in 3m41s
Local tsconfig.json has uncommitted target changes, so `[...voidIds]`
passed locally but failed CI's tsc (TS2802, target < es2015).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 01:30:21 +03:30
soroush.asadi c47922414a feat: اصلاح سند payment corrections + audit-log & daily P&L views
CI/CD / CI · API (dotnet build + test) (push) Successful in 51s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 31s
CI/CD / CI · Dashboard (tsc) (push) Failing after 1m12s
CI/CD / CI · Admin Web (tsc) (push) Successful in 40s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 51s
CI/CD / Deploy · all services (push) Has been skipped
Backend:
- POST /orders/{id}/payments/corrections (Manager/Owner): void wrong
  payments (marked Refunded, never deleted) and/or record replacements
  atomically; mandatory reason; requires an open register shift; full
  before/after written to the immutable audit trail.
- GET /orders/closed?date= — closed orders of one Iran-calendar day,
  paged, the browsing surface for corrections.
- CalculateExpectedCash now subtracts cash refunds so corrections keep
  the drawer expectation honest.

Dashboard (reports screen now has three tabs):
- عملکرد و سود: existing KPIs/charts + new day-by-day breakdown table
  (orders, revenue, expenses, net profit per Jalali day).
- اصلاح سند: closed-orders browser with payment chips + correction
  dialog (void checkboxes, replacement rows, live balance, reason).
- گزارش عملیات: filterable audit-log viewer (category, Jalali range,
  branch) with expandable structured details.

fa/en/ar translations included. 86 backend tests pass; dashboard tsc clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 01:24:19 +03:30
soroush.asadi 2a4cf1d20b feat(dashboard): Jalali date pickers + mobile/tablet responsive shell
CI/CD / CI · API (dotnet build + test) (push) Successful in 51s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 38s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 2m41s
Full Persian calendar:
- New JalaliDateField — Shamsi popover picker (Saturday-first weeks,
  Persian digits, امروز shortcut); wire format stays ISO Gregorian
  YYYY-MM-DD. Falls back to the native input for the en locale.
- Replaces all 5 native type="date" inputs (Gregorian-only pickers):
  reservations, expenses from/to, reports from/to.
- Reservations list date now renders Jalali instead of the raw ISO
  string; branches purge timestamp now formats with fa-IR.

Responsive shell (mobile + tablet):
- New MobileNav: hamburger in the topbar (< md) opening an RTL-aware
  slide-over drawer with all nav destinations, permission-filtered,
  Escape/backdrop close and body scroll lock.
- Desktop sidebar hidden below md; header center cluster (clock/plan)
  hidden below md; language switcher hidden below sm.
- Main content padding scales p-3 → p-4 → p-6.
- Verified at 375px and 768px: no horizontal overflow, drawer and
  Jalali picker fully functional.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 23:10:38 +03:30
soroush.asadi d811b7d6d5 feat(dashboard): simplify navigation — frequency-based IA
CI/CD / CI · API (dotnet build + test) (push) Successful in 47s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 28s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m6s
CI/CD / CI · Admin Web (tsc) (push) Successful in 40s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 2m43s
The sidebar had 22 items in 5 accordion groups, all defaulting closed:
first visit showed five vague headers and zero destinations, there was
no Dashboard/Home link at all, and rare pages (taxes, subscription) had
equal weight with POS. Restructured around usage frequency:

- Flat primary (always visible, no header): Dashboard, POS, Tables,
  Kitchen, Queue, Reservations, Menu, Reports
- Two collapsible groups: Customers & marketing (crm, coupons, sms,
  reviews, discover) and Café management (inventory, expenses, shifts,
  taxes, hr, branches)
- Footer utility icons: settings, subscription, support
- Removed "notifications" from the nav (duplicate of the topbar bell)

Other fixes folded in:
- Deleted [locale]/page.tsx which redirected "/" to /pos — it made the
  POS exit button a no-op loop and left OverviewScreen unreachable.
  "/" now renders the overview home; login still lands on /pos.
- Branch gating moved from group-level to an item whitelist
  (BRANCH_ALLOWED_NAV_KEYS) — also closes the hole where branch
  accounts could deep-link to /reports etc. past the RouteGuard.
- RouteGuard now checks footer items too (subscription stays gated).
- revalidate=300 on the locale layout: Next emitted s-maxage=31536000
  and the WCDN edge kept serving year-old HTML shells after deploys.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 22:06:59 +03:30
soroush.asadi 82145b0d21 feat(pos2): add dashboard exit button to POS board header
CI/CD / CI · API (dotnet build + test) (push) Failing after 3m19s
CI/CD / CI · Admin API (dotnet build) (push) Failing after 3m19s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 48s
CI/CD / Deploy · all services (push) Has been cancelled
POS runs in the (fullscreen) layout which strips the sidebar.
Adds a Home → داشبورد button at the top-left of the table board so
users can navigate back to the dashboard without being stuck.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 07:23:14 +03:30
soroush.asadi 59486cdf24 fix(pos2): wait for branch before fetching menu + add left category sidebar
CI/CD / CI · API (dotnet build + test) (push) Failing after 3m20s
CI/CD / CI · Admin API (dotnet build) (push) Failing after 3m19s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 41s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 52s
CI/CD / Deploy · all services (push) Has been cancelled
Race fix: orderBranchId now returns `undefined` (not null) while the /branches
query is in flight. usePos2Menu treats undefined as "not yet determined" and
skips the fetch, preventing getBranchMenu(cafeId, null) → empty array.
Once branchesFetched=true, orderBranchId resolves to the correct branchId
(or null for café-wide fallback).

Layout: desktop order screen now shows a left vertical category sidebar
(116 px, md+) instead of horizontal chips, giving the classic POS sidebar
feel. Horizontal chips kept for mobile (<md). Menu grid columns adjusted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 07:10:03 +03:30
soroush.asadi f02f78a97c fix(pos): POS v2 menu empty — resolve a valid branch like classic POS
CI/CD / CI · API (dotnet build + test) (push) Successful in 42s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 31s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m6s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 2m37s
The menu/tables are branch-scoped. v2 used the raw stored branchId, which is
null or stale for users who never opened the classic POS (it has no branch
picker), so getBranchMenu returned an empty menu. Now v2 fetches /branches,
auto-selects the first valid branch (self-healing the stored id), and loads the
branch menu + tables + order submission against that resolved branch — matching
the classic POS exactly. Also adds a visible "menu failed to load / retry"
state instead of a silent empty grid.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 06:17:37 +03:30
soroush.asadi cc0933c514 fix(auth): don't log out fullscreen routes (POS/queue) on refresh
CI/CD / CI · API (dotnet build + test) (push) Successful in 42s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 31s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m4s
CI/CD / CI · Admin Web (tsc) (push) Successful in 36s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 48s
CI/CD / Deploy · all services (push) Successful in 2m35s
The (fullscreen) layout redirected to /login whenever user.accessToken was
falsy — but on a page refresh that fires before Zustand finishes rehydrating
the persisted auth from localStorage, so an authenticated user was bounced to
login on every refresh. Gate the redirect on _hasHydrated (and show a loader
while rehydrating), matching RouteGuard. Tokens themselves are already long
(30d access / 365d refresh), so sessions now survive refreshes as expected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 05:56:11 +03:30
soroush.asadi 7c35984096 feat(pos): default the pay sheet to Card
CI/CD / CI · API (dotnet build + test) (push) Successful in 40s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 31s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m6s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 44s
CI/CD / CI · Koja (tsc) (push) Successful in 48s
CI/CD / Deploy · all services (push) Successful in 2m42s
Card is now the pre-selected payment method (and split rows default to Card),
matching Iran's card-dominant payments. Card already sits first in the selector.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 05:38:13 +03:30
soroush.asadi bf0ca68fa6 feat(pos): show Card first in pay sheet, keep Cash as default
CI/CD / CI · API (dotnet build + test) (push) Successful in 43s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 31s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m6s
CI/CD / CI · Admin Web (tsc) (push) Has been cancelled
CI/CD / CI · Website (tsc) (push) Has been cancelled
CI/CD / CI · Koja (tsc) (push) Has been cancelled
CI/CD / Deploy · all services (push) Has been cancelled
Reorder the payment method tabs to کارت / نقدی / تقسیم (Card first, most common
in Iran) while keeping Cash as the pre-selected default method.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 05:32:26 +03:30
soroush.asadi 6778c32028 feat(pos): POS v2 feature parity + promote to default /pos
CI/CD / CI · API (dotnet build + test) (push) Successful in 45s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 32s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m7s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 2m46s
Completes the four POS v2 roadmap items:

1. Real split payments — split tab records N separate payment rows (equal split,
   last row takes the remainder), each row toggles Cash/Card; posts payments[].
2. Card-terminal push — confirmPay sums Card amounts and calls requestPosPayment
   (POS device) before recording; surfaces POS_DEVICE_* errors.
3. Customer + coupons + loyalty — reuses PosCustomerPicker (attach/search/create)
   and validates coupons via /coupons/validate (discount in totals). Pay sheet
   offers loyalty redemption (1 point = 100 toman) when a customer is attached.
4. Promote to default — /pos now renders POS v2 (full-screen, café-themed); the
   classic terminal moves to /pos-classic with its sidebar+topbar chrome. The
   "نسخه کلاسیک" link points there.

Order submission already carried customerId/guestName/guestPhone/couponId via the
shared cart store, so customer + coupon flow straight through send + pay.
tsc --noEmit clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 05:16:52 +03:30
soroush.asadi 75a0a1c834 feat(pos): wire POS v2 to live data (board, orders, payments)
CI/CD / CI · API (dotnet build + test) (push) Successful in 43s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 32s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 2m42s
POS v2 is now a real, working point of sale at /[locale]/pos2 (was a static
mock). It reuses the existing data layer so it shares the React Query cache and
offline pipeline with the classic POS:

- Table board ← fetchCafeTableBoard (Free/Busy/Reserved/Cleaning, live totals,
  guest-QR badge); polls every 15s. Open a free table to start an order; open a
  busy table to hydrate its existing order (GET order → cart hydrateFromOrder).
- Order screen ← real branch/café menu + categories, bound to useCartStore
  (add/qty/remove). Send via submitOrderToApi (online + offline outbox) then
  re-hydrate; "ارسال (n)" shows the pending (unsynced) line count.
- Pay sheet ← POST /orders/{id}/payments. Cash (numpad + change), Card, and a
  Split helper (records the full amount; split is cashier guidance for now).
- Online/offline badge, loading/empty states, toasts, busy overlay, and a
  "نسخه کلاسیک" link back to /pos.

The static design mock stays at /[locale]/pos2-preview (dev-only, 404 in prod).
tsc --noEmit clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 00:16:37 +03:30
soroush.asadi 5078af2dd7 feat(pos): clickable POS v2 redesign prototype at /pos2 (static, no backend)
CI/CD / CI · API (dotnet build + test) (push) Failing after 3m18s
CI/CD / CI · Admin API (dotnet build) (push) Failing after 3m17s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m9s
CI/CD / CI · Admin Web (tsc) (push) Successful in 39s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 1m2s
CI/CD / Deploy · all services (push) Has been skipped
Responsive RTL big-touch reimagining of the POS order screen for judging the
redesign on real devices before decomposing the 1568-line pos-screen.tsx + wiring
real logic. Self-contained: mock menu + local cart, no API/store/SignalR.

- 3 zones: category chips + item grid · order ticket · sticky action bar.
- lg+ side-panel ticket; smaller screens get a "view order" bar + slide-over
  (covers landscape tablet, portrait tablet, phone).
- Big-touch (56px primary / 44px qty), brand green #0F6E56, Toman totals.
  Send/Pay are mock toasts. tsc clean.
2026-06-03 17:23:58 +03:30
soroush.asadi 55e0c9499d content(website): reflect latest features across all pages
CI/CD / CI · API (dotnet build + test) (push) Successful in 44s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 34s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m5s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 1m49s
Full pass over the marketing site so every page reflects the current product:

- Features page: +"Works Offline" and +"Get Discovered on Koja" cards (NEW badges);
  rewrote "Real-time Notifications" to describe the dashboard sound+toast alert.
- Solutions: added offline / Koja / real-time-alert bullets across cafés,
  restaurants, chains, and cloud kitchens.
- Tour: POS step now mentions offline + auto-sync; kitchen step describes the
  sound+toast new-order alert on any screen.
- Docs: +Offline Mode and +Koja Discovery module guides (fa/en).
- appPromo: waiter-app "new order" alert notes sound.
- privacy/terms: "Last updated" June 2025 → June 2026.

Website build clean.
2026-06-03 08:37:42 +03:30
soroush.asadi dcdb0d5747 feat(realtime): global guest-order alert on the dashboard
CI/CD / CI · API (dotnet build + test) (push) Successful in 47s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 32s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m4s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
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 3m9s
Guest orders from the QR/digital menu already notified via SignalR, but only
screens that were open (KDS/POS/tables) reacted — and silently (a data refresh,
no alert). So staff on any other screen never knew a menu order arrived.

- Add a global useOrderAlerts() mounted in the dashboard shell: connects to
  /hubs/kds, joins the café group, and on a new GUEST order plays a chime + shows
  a toast (localized fa/en/ar) + nudges order/KDS/POS lists to refresh — on every
  screen.
- Filter to guest QR-menu orders only (not staff POS orders): LiveOrderDto now
  carries Source, set in MapLiveOrder (+ the delivery/snappfood mappers).

86 API tests pass; dashboard tsc + build clean.
2026-06-03 02:42:29 +03:30
soroush.asadi 9b2f15151d feat(website): reflect new features + 5-tier pricing
CI/CD / CI · API (dotnet build + test) (push) Successful in 50s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 45s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m4s
CI/CD / CI · Admin Web (tsc) (push) Successful in 36s
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 3m18s
- Pricing: add the Starter tier (now Free·Starter·Pro·Business·Enterprise),
  fix currency ₺ (Turkish Lira) → Toman, and rewrite every plan's bullets to the
  agreed matrix (Free: 6 tables/30 orders/Koja/offline + watermark; Starter:
  watermark-removal/custom-styling/review-reply; Pro: CRM/reports/taxes/payroll/
  delivery/3 branches; Business: 3D + AI-3D + unlimited; Enterprise: API/white-label/
  SLA/24-7). 5-column responsive grid.
- Features: add two headliner cards that were missing — "Works offline" and
  "Get discovered on Koja". fa/en.

Website tsc + build clean.
2026-06-03 02:20:16 +03:30