store user login
This commit is contained in:
@@ -26,6 +26,8 @@ export interface BranchScopeValue {
|
||||
scoped: TenantLocation[];
|
||||
/** The selected branch, or undefined under All. */
|
||||
current: TenantLocation | undefined;
|
||||
/** True in the Store user workspace: one branch, fixed, `select` inert. */
|
||||
isPinned: boolean;
|
||||
}
|
||||
|
||||
const BranchScopeContext = createContext<BranchScopeValue | null>(null);
|
||||
@@ -46,26 +48,50 @@ const PARAM = 'branch';
|
||||
* alone — putting the tenant id in an editable address bar would turn the one
|
||||
* boundary we control into a text field.
|
||||
*/
|
||||
export function BranchScopeProvider({ children }: { children: ReactNode }) {
|
||||
export function BranchScopeProvider({
|
||||
children,
|
||||
pin,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
/**
|
||||
* Fix the scope to one branch — the Store user workspace.
|
||||
*
|
||||
* When set, `selected` is always this id, `branches` and `scoped` hold only
|
||||
* that outlet, and `select` does nothing. The URL param is ignored rather
|
||||
* than merely defaulted: Fiesta authorises a POS or catalogue read on
|
||||
* `locationid` alone, so an address bar that can change it is not a filter,
|
||||
* it is the authorisation boundary in a text field.
|
||||
*/
|
||||
pin?: number;
|
||||
}) {
|
||||
const { user } = useAuth();
|
||||
const [params, setParams] = useSearchParams();
|
||||
const tenantid = user?.tenantid ?? 0;
|
||||
|
||||
const { data, isLoading } = useTenantLocations(tenantid || undefined);
|
||||
|
||||
const branches = useMemo(
|
||||
const all = useMemo(
|
||||
() => [...(data ?? [])].sort((a, b) => a.locationid - b.locationid),
|
||||
[data],
|
||||
);
|
||||
|
||||
// Pinned: the one outlet, and nothing else is ever in the list. Unpinned: all
|
||||
// of them, with All-branches available.
|
||||
const branches = useMemo(
|
||||
() => (pin === undefined ? all : all.filter((branch) => branch.locationid === pin)),
|
||||
[all, pin],
|
||||
);
|
||||
|
||||
const raw = params.get(PARAM);
|
||||
const parsed = raw === null || raw === 'all' ? null : Number(raw);
|
||||
// An id in the URL that this tenant does not own falls back to All rather
|
||||
// than showing an empty page — the id is user-editable, so it is untrusted.
|
||||
const selected =
|
||||
parsed !== null && Number.isFinite(parsed) && branches.some((b) => b.locationid === parsed)
|
||||
? parsed
|
||||
: null;
|
||||
pin !== undefined
|
||||
? pin
|
||||
: parsed !== null && Number.isFinite(parsed) && branches.some((b) => b.locationid === parsed)
|
||||
? parsed
|
||||
: null;
|
||||
|
||||
const value = useMemo<BranchScopeValue>(() => {
|
||||
const current = selected === null ? undefined : branches.find((b) => b.locationid === selected);
|
||||
@@ -75,15 +101,17 @@ export function BranchScopeProvider({ children }: { children: ReactNode }) {
|
||||
tenantid,
|
||||
selected,
|
||||
current,
|
||||
isPinned: pin !== undefined,
|
||||
scoped: current ? [current] : branches,
|
||||
select: (next) => {
|
||||
if (pin !== undefined) return;
|
||||
const nextParams = new URLSearchParams(params);
|
||||
if (next === null) nextParams.delete(PARAM);
|
||||
else nextParams.set(PARAM, String(next));
|
||||
setParams(nextParams, { replace: true });
|
||||
},
|
||||
};
|
||||
}, [branches, isLoading, tenantid, selected, params, setParams]);
|
||||
}, [branches, isLoading, tenantid, selected, params, setParams, pin]);
|
||||
|
||||
return <BranchScopeContext.Provider value={value}>{children}</BranchScopeContext.Provider>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user