From 59486cdf24d6a1ee966796ea7d1aadbc23b38f7c Mon Sep 17 00:00:00 2001 From: "soroush.asadi" Date: Sun, 7 Jun 2026 07:10:03 +0330 Subject: [PATCH] fix(pos2): wait for branch before fetching menu + add left category sidebar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 ( --- .../src/components/pos2/pos2-screen.tsx | 46 +++++++++++++++---- web/dashboard/src/lib/pos2/use-pos2.ts | 11 ++++- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/web/dashboard/src/components/pos2/pos2-screen.tsx b/web/dashboard/src/components/pos2/pos2-screen.tsx index 762d394..a1d5bc1 100644 --- a/web/dashboard/src/components/pos2/pos2-screen.tsx +++ b/web/dashboard/src/components/pos2/pos2-screen.tsx @@ -69,20 +69,25 @@ export function Pos2Screen() { // Resolve a VALID branch (auto-pick the first) exactly like the classic POS — // the menu/tables are branch-scoped, so a null or stale stored branchId would // otherwise load an empty menu. v2 has no branch picker, so it must self-heal. - const { data: branches = [] } = useQuery({ + // + // IMPORTANT: orderBranchId returns `undefined` while branches are still loading. + // usePos2Menu treats `undefined` as "not yet determined" and pauses the query so + // we never fire getBranchMenu(cafeId, null) which returns an empty array. + const { data: branches = [], isFetched: branchesFetched } = useQuery({ queryKey: ["branches", cafeId], queryFn: () => apiGet<{ id: string; name: string }[]>(`/api/cafes/${cafeId}/branches`), enabled: !!cafeId, }); useEffect(() => { - if (branches.length === 0) return; + if (!branchesFetched || branches.length === 0) return; const valid = branchId && branches.some((b) => b.id === branchId); if (!valid) setBranchId(branches[0]!.id); - }, [branches, branchId, setBranchId]); - const orderBranchId = useMemo(() => { + }, [branchesFetched, branches, branchId, setBranchId]); + const orderBranchId = useMemo(() => { + if (!branchesFetched) return undefined; // still loading → pause the menu query if (branchId && branches.some((b) => b.id === branchId)) return branchId; - return branches[0]?.id ?? null; - }, [branchId, branches]); + return branches[0]?.id ?? null; // null = no branches → café-wide fallback + }, [branchesFetched, branchId, branches]); const { data: categories } = usePos2Categories(cafeId); const { data: menu, isLoading: menuLoading, isError: menuError, refetch: refetchMenu } = usePos2Menu(cafeId, orderBranchId); @@ -447,8 +452,29 @@ export function Pos2Screen() {
+ {/* ── Left: vertical category sidebar (desktop) ── */} + + + {/* ── Center: menu items ── */}
-
+ {/* Horizontal chips — mobile only */} +
{catChips.map((c) => (
+ {menuLoading ? (
@@ -475,7 +502,7 @@ export function Pos2Screen() {
) : ( -
+
{visibleItems.map((it) => (
diff --git a/web/dashboard/src/lib/pos2/use-pos2.ts b/web/dashboard/src/lib/pos2/use-pos2.ts index d947a24..4ff0c77 100644 --- a/web/dashboard/src/lib/pos2/use-pos2.ts +++ b/web/dashboard/src/lib/pos2/use-pos2.ts @@ -22,7 +22,13 @@ export function usePos2Categories(cafeId?: string | null) { } /** 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. */ + * café-wide menu. Both normalize to MenuItem so the cart store can consume them. + * + * Pass `branchId = undefined` (not null) while still determining which branch to + * use — the query will pause until branchId is resolved. Once resolved: + * • string → branch-scoped menu via getBranchMenu + * • null → café-wide fallback via /menu/items + */ export function usePos2Menu(cafeId?: string | null, branchId?: string | null) { return useQuery({ queryKey: ["pos2-menu", cafeId, branchId ?? "cafe"], @@ -33,7 +39,8 @@ export function usePos2Menu(cafeId?: string | null, branchId?: string | null) { } return apiGet(`/api/cafes/${cafeId}/menu/items`); }, - enabled: !!cafeId, + // branchId === undefined means "still determining" — don't fire yet + enabled: !!cafeId && branchId !== undefined, staleTime: 30_000, }); }