import { useState } from 'react';
import { Monitor, QrCode, Store, UserCog, UserRound, Users } from 'lucide-react';
import { StoreQrDrawer } from './StoreQrDrawer';
import { AppShell, IconButton, type MenuEntry, type NavEntry } from '@/components/shell/AppShell';
import { useAuth } from '@/auth/AuthContext';
import { useTenantLocations } from '@/queries/hooks';
import { BranchScopeProvider, useBranchScope } from '@/features/store-admin/BranchScope';
/**
* Store user — one branch, and only that branch.
*
* The same four destinations as the Store Admin, for the same reason: the
* questions a shop asks in a day are what did we take, what do we sell, what is
* on the shelf, and how did the period go. What changes is the scope, not the
* shape — so the pages are the merchant's pages with the branch fixed, rather
* than a second set that drifts.
*
* The old console gave this role five nav entries over a six-tab hub, and one
* of them (Store Console) rendered another one (Products) inside itself. Four
* pages, each owning one question, replaces it.
*/
const NAV: readonly NavEntry[] = [
{ to: '/store/console', label: 'Console' },
{ to: '/store/products', label: 'Products' },
{ to: '/store/sales', label: 'Sales' },
{ to: '/store/reports', label: 'Reports' },
];
/**
* The account menu.
*
* Everything here is either about this person, or about the shop's fittings —
* reached when something needs checking, not worked from hour to hour. The
* merchant's "New branch" entry is deliberately absent: a shop does not
* commission outlets, and `createtenantlocation` also spawns a login.
*
* Customers sits here rather than in the nav because the four nav slots each
* answer a question about the trading day, and "who orders from us" is a
* lookup. Move it up if the shop uses it hourly.
*
* The store QR is NOT here — it is an icon in the header, because it opens in
* place and closes again. A destination is for somewhere you work; this is
* something you glance at and print.
*/
const MANAGE: readonly MenuEntry[] = [
{
to: '/store/customers',
label: 'Customers',
icon: ,
note: 'Who orders from this shop',
},
{
to: '/store/terminals',
label: 'Terminals',
icon: ,
note: 'Till health at this counter',
},
{
to: '/store/staff',
label: 'Counter staff',
icon: ,
note: 'Who can open the till',
},
{
to: '/store/account',
label: 'My account',
icon: ,
note: 'Profile and store binding',
},
];
/**
* Resolve the outlet this account is bound to.
*
* Two steps, and no third. `app_users.locationid` is the binding; a tenant with
* exactly one outlet needs no id at all. The old console had four more
* fallbacks below these — including matching the email's local part against a
* store's address, and falling back to `applocationid` — and those are guesses
* at an authorisation boundary. `applocationid` is a CITY id: passing one to a
* POS read returns an empty board, or another shop's takings if the numbers
* happen to collide.
*/
function useResolvedOutlet(): { locationid: number | null; isLoading: boolean } {
const { user } = useAuth();
const { data, isLoading } = useTenantLocations(user?.tenantid || undefined);
const branches = data ?? [];
if (user?.locationid && user.locationid > 0) return { locationid: user.locationid, isLoading };
if (isLoading) return { locationid: null, isLoading: true };
if (branches.length === 1 && branches[0]) return { locationid: branches[0].locationid, isLoading };
return { locationid: null, isLoading: false };
}
export function StoreUserShell() {
const { locationid, isLoading } = useResolvedOutlet();
const [isQrOpen, setQrOpen] = useState(false);
if (isLoading) return ;
if (locationid === null) return ;
return (
}
manageItems={MANAGE}
headerActions={
setQrOpen(true)}>
}
/>
{isQrOpen ? setQrOpen(false)} /> : null}
);
}
/**
* Where the merchant has a branch selector, the shop has a label.
*
* Same slot, same size, same position — so the two workspaces read as one
* product — but not a control, because there is nothing to choose. A disabled
* dropdown would say "you may not switch"; a label says "this is your shop",
* which is the true statement.
*/
function BranchPill() {
const { current, isLoading } = useBranchScope();
const label = isLoading ? 'Loading…' : (current?.locationname ?? 'Your store');
return (
{label}
);
}
function Resolving() {
return (
Finding your store…
);
}
/**
* The honest refusal.
*
* Every read on every page below is authorised by `locationid` alone, so a page
* that guessed one would not fail loudly — it would quietly show another shop's
* takings, or an empty board that reads as a quiet day.
*/
function NoStore() {
const { signOut } = useAuth();
return (
No store assigned
Your account is not linked to a branch yet, so there is nothing to show. Ask your store
administrator to allocate you to one.