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
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>
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
"use client";
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// POS v2 data hooks — thin wrappers over the EXISTING data layer so the new POS
|
||||
// UI reuses the same endpoints/query keys as the classic POS (cache-shared).
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { apiGet } from "@/lib/api/client";
|
||||
import { getBranchMenu, branchMenuItemToMenuItem } from "@/lib/api/branch-menu";
|
||||
import { fetchCafeTableBoard } from "@/lib/api/branch-tables";
|
||||
import type { MenuCategory, MenuItem, TableBoardItem } from "@/lib/api/types";
|
||||
|
||||
export function usePos2Categories(cafeId?: string | null) {
|
||||
return useQuery({
|
||||
queryKey: ["menu-categories", cafeId],
|
||||
queryFn: () => apiGet<MenuCategory[]>(`/api/cafes/${cafeId}/menu/categories`),
|
||||
enabled: !!cafeId,
|
||||
staleTime: 60_000,
|
||||
});
|
||||
}
|
||||
|
||||
/** Branch-scoped menu (effective prices) when a branch is selected; otherwise the
|
||||
* café-wide menu. Both normalize to MenuItem so the cart store can consume them. */
|
||||
export function usePos2Menu(cafeId?: string | null, branchId?: string | null) {
|
||||
return useQuery({
|
||||
queryKey: ["pos2-menu", cafeId, branchId ?? "cafe"],
|
||||
queryFn: async (): Promise<MenuItem[]> => {
|
||||
if (branchId) {
|
||||
const rows = await getBranchMenu(cafeId as string, branchId);
|
||||
return rows.map(branchMenuItemToMenuItem);
|
||||
}
|
||||
return apiGet<MenuItem[]>(`/api/cafes/${cafeId}/menu/items`);
|
||||
},
|
||||
enabled: !!cafeId,
|
||||
staleTime: 30_000,
|
||||
});
|
||||
}
|
||||
|
||||
export function usePos2Tables(cafeId?: string | null, branchId?: string | null) {
|
||||
return useQuery({
|
||||
queryKey: ["tables-board", cafeId, branchId, "pos"],
|
||||
queryFn: () => fetchCafeTableBoard(cafeId as string, branchId ?? undefined),
|
||||
enabled: !!cafeId,
|
||||
refetchInterval: 15_000,
|
||||
});
|
||||
}
|
||||
|
||||
export function useMenuById(items: MenuItem[] | undefined): Map<string, MenuItem> {
|
||||
return useMemo(() => {
|
||||
const m = new Map<string, MenuItem>();
|
||||
for (const it of items ?? []) m.set(it.id, it);
|
||||
return m;
|
||||
}, [items]);
|
||||
}
|
||||
|
||||
export type { MenuCategory, MenuItem, TableBoardItem };
|
||||
Reference in New Issue
Block a user