branch crud and intergration

This commit is contained in:
2026-09-08 12:13:49 +05:30
parent bebec10eb3
commit f664961fce
113 changed files with 41598 additions and 174 deletions
@@ -9,10 +9,53 @@ import { createContext, useContext, useEffect, useState } from "react"
import { authApi } from "@/lib/api/auth"
import { MeResponse } from "@/types/rbac"
interface ActiveBranch {
id: number | null
name: string | null
}
/** Admin's last-picked header branch, kept per-browser (docs/BRANCH_ISOLATION_PLAN.md
* §Frontend impact: "a current branch selector... persisted per session"). Purely a
* display/filter convenience — never trusted server-side. */
const ACTIVE_BRANCH_KEY = "erpcore.activeBranch"
function readStoredActiveBranch(): ActiveBranch | null {
if (typeof window === "undefined") return null
const raw = window.localStorage.getItem(ACTIVE_BRANCH_KEY)
if (!raw) return null
try {
return JSON.parse(raw) as ActiveBranch
} catch {
window.localStorage.removeItem(ACTIVE_BRANCH_KEY)
return null
}
}
interface AuthContextValue {
roleCode: string | null
roleName: string | null
navCodes: string[]
/** True for the Admin role — the only role that may pick any branch (others are locked to their own). */
isAdmin: boolean
/** The caller's own branch, via their linked Employee record — null if unlinked. */
branchId: number | null
branchName: string | null
/**
* The branch currently being "worked in": for non-admins this always mirrors their own
* branch; for an Admin it's whichever branch they last picked in the header (null = "All
* Branches"), persisted per-browser. Screens that want to filter by "the branch the user
* is currently viewing" should read this, not `branchId`.
*/
activeBranchId: number | null
activeBranchName: string | null
/** Admin-only — switches the header's active branch. A no-op for any other role. */
setActiveBranch: (id: number | null, name: string | null) => void
/**
* The deployment-wide toggle (Settings → Configuration → Branch Configuration). False
* means this deployment doesn't use branches at all — every branch picker/badge in the
* app should be hidden entirely, not merely restricted by role.
*/
branchingEnabled: boolean
/** True until the first `/auth/me` response lands. */
loading: boolean
}
@@ -21,11 +64,19 @@ const AuthContext = createContext<AuthContextValue>({
roleCode: null,
roleName: null,
navCodes: [],
isAdmin: false,
branchId: null,
branchName: null,
activeBranchId: null,
activeBranchName: null,
setActiveBranch: () => {},
branchingEnabled: true,
loading: true,
})
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [me, setMe] = useState<MeResponse | null>(null)
const [activeBranch, setActiveBranchState] = useState<ActiveBranch | null>(null)
useEffect(() => {
let cancelled = false
@@ -37,19 +88,39 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
.catch(() => {
// Unauthenticated/unreachable — fall back to "no permissions" rather than
// crash the shell; proxy.ts already redirects unauthenticated users to /login.
if (!cancelled) setMe({ roleCode: null, roleName: null, navCodes: [] })
if (!cancelled) setMe({ roleCode: null, roleName: null, navCodes: [], isAdmin: false, branchId: null, branchName: null, branchingEnabled: true })
})
return () => {
cancelled = true
}
}, [])
useEffect(() => {
if (me === null) return
// Non-admins are always pinned to their own branch — never their own past picks.
setActiveBranchState(me.isAdmin ? (readStoredActiveBranch() ?? { id: null, name: null }) : { id: me.branchId, name: me.branchName })
}, [me])
function setActiveBranch(id: number | null, name: string | null) {
if (!me?.isAdmin) return
const next = { id, name }
setActiveBranchState(next)
if (typeof window !== "undefined") window.localStorage.setItem(ACTIVE_BRANCH_KEY, JSON.stringify(next))
}
return (
<AuthContext.Provider
value={{
roleCode: me?.roleCode ?? null,
roleName: me?.roleName ?? null,
navCodes: me?.navCodes ?? [],
isAdmin: me?.isAdmin ?? false,
branchId: me?.branchId ?? null,
branchName: me?.branchName ?? null,
activeBranchId: activeBranch?.id ?? null,
activeBranchName: activeBranch?.name ?? null,
setActiveBranch,
branchingEnabled: me?.branchingEnabled ?? true,
loading: me === null,
}}
>