| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100 |
- "use client";
- import React from "react";
- import { Loader2 } from "lucide-react";
- import { useAuth } from "@/components/auth/authContext";
- import { getBranches } from "@/lib/frontend/apiClient";
- import {
- decideBranchUi,
- BRANCH_UI_DECISION,
- } from "@/lib/frontend/rbac/branchUiDecision";
- import ForbiddenView from "@/components/system/ForbiddenView";
- import NotFoundView from "@/components/system/NotFoundView";
- const BRANCH_LIST_STATE = Object.freeze({
- IDLE: "idle",
- LOADING: "loading",
- READY: "ready",
- ERROR: "error",
- });
- function BranchValidationLoading() {
- return (
- <div className="flex items-center gap-3 text-sm text-muted-foreground">
- <Loader2 className="h-4 w-4 animate-spin" />
- <span>Niederlassung wird geprüft…</span>
- </div>
- );
- }
- export default function BranchGuard({ branch, children }) {
- const { status, user } = useAuth();
- const isAuthenticated = status === "authenticated" && user;
- const needsExistenceCheck =
- isAuthenticated && (user.role === "admin" || user.role === "dev");
- const [branchList, setBranchList] = React.useState({
- status: BRANCH_LIST_STATE.IDLE,
- branches: null,
- });
- React.useEffect(() => {
- if (!needsExistenceCheck) return;
- let cancelled = false;
- setBranchList({ status: BRANCH_LIST_STATE.LOADING, branches: null });
- (async () => {
- try {
- const res = await getBranches();
- if (cancelled) return;
- const branches = Array.isArray(res?.branches) ? res.branches : [];
- setBranchList({ status: BRANCH_LIST_STATE.READY, branches });
- } catch (err) {
- if (cancelled) return;
- // Fail open: do not block navigation if validation fails.
- console.error("[BranchGuard] getBranches failed:", err);
- setBranchList({ status: BRANCH_LIST_STATE.ERROR, branches: null });
- }
- })();
- return () => {
- cancelled = true;
- };
- // IMPORTANT:
- // - depend only on user identity + needsExistenceCheck
- // - do NOT depend on branchList.status (would cancel itself when setting LOADING)
- }, [needsExistenceCheck, user?.userId]);
- if (!isAuthenticated) return children;
- if (needsExistenceCheck && branchList.status === BRANCH_LIST_STATE.LOADING) {
- return <BranchValidationLoading />;
- }
- const allowedBranches =
- branchList.status === BRANCH_LIST_STATE.READY ? branchList.branches : null;
- const decision = decideBranchUi({
- user,
- branch,
- allowedBranches,
- });
- if (decision === BRANCH_UI_DECISION.FORBIDDEN) {
- return <ForbiddenView attemptedBranch={branch} />;
- }
- if (decision === BRANCH_UI_DECISION.NOT_FOUND) {
- return <NotFoundView />;
- }
- return children;
- }
|