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, }); }