|
|
@@ -40,6 +40,9 @@ const BRANCH_LIST_STATE = Object.freeze({
|
|
|
ERROR: "error",
|
|
|
});
|
|
|
|
|
|
+const ACTIVE_NAV_BUTTON_CLASS =
|
|
|
+ "border-blue-600 bg-blue-50 hover:bg-blue-50 dark:border-blue-900 dark:bg-blue-950 dark:hover:bg-blue-950";
|
|
|
+
|
|
|
export default function QuickNav() {
|
|
|
const router = useRouter();
|
|
|
const pathname = usePathname() || "/";
|
|
|
@@ -53,8 +56,6 @@ export default function QuickNav() {
|
|
|
|
|
|
const canRevalidate = typeof retry === "function";
|
|
|
|
|
|
- // Persisted selection for admin/dev:
|
|
|
- // - Used as the "safe fallback" when the route branch is invalid/non-existent.
|
|
|
const [selectedBranch, setSelectedBranch] = React.useState(null);
|
|
|
|
|
|
const [branchList, setBranchList] = React.useState({
|
|
|
@@ -81,33 +82,26 @@ export default function QuickNav() {
|
|
|
|
|
|
const isKnownRouteBranch = React.useMemo(() => {
|
|
|
if (!routeBranch) return false;
|
|
|
- if (!knownBranches) return false; // do not "trust" route until we know the branch list
|
|
|
+ if (!knownBranches) return false;
|
|
|
return knownBranches.includes(routeBranch);
|
|
|
}, [routeBranch, knownBranches]);
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
if (!isAuthenticated) return;
|
|
|
|
|
|
- // Branch users: fixed branch, no persisted selection needed.
|
|
|
if (isBranchUser) {
|
|
|
const own = user.branchId;
|
|
|
setSelectedBranch(own && isValidBranchParam(own) ? own : null);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // Admin/dev: initialize from localStorage only.
|
|
|
- // IMPORTANT:
|
|
|
- // We do NOT initialize from the route branch, because invalid-but-syntactically-valid
|
|
|
- // branches (e.g. NL200) would pollute state and cause thrashing.
|
|
|
const fromStorage = safeReadLocalStorageBranch(STORAGE_KEY_LAST_BRANCH);
|
|
|
-
|
|
|
if (fromStorage && fromStorage !== selectedBranch) {
|
|
|
setSelectedBranch(fromStorage);
|
|
|
}
|
|
|
}, [isAuthenticated, isBranchUser, user?.branchId, selectedBranch]);
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
- // Fetch the branch list once for admin/dev users (or when the user changes).
|
|
|
if (!isAdminDev) return;
|
|
|
|
|
|
let cancelled = false;
|
|
|
@@ -124,7 +118,6 @@ export default function QuickNav() {
|
|
|
} catch (err) {
|
|
|
if (cancelled) return;
|
|
|
|
|
|
- // Fail open: do not block navigation if validation fails.
|
|
|
console.error("[QuickNav] getBranches failed:", err);
|
|
|
setBranchList({ status: BRANCH_LIST_STATE.ERROR, branches: null });
|
|
|
}
|
|
|
@@ -136,7 +129,6 @@ export default function QuickNav() {
|
|
|
}, [isAdminDev, user?.userId]);
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
- // Once we know the branch list, keep selectedBranch valid and stable.
|
|
|
if (!isAdminDev) return;
|
|
|
if (!knownBranches || knownBranches.length === 0) return;
|
|
|
|
|
|
@@ -148,8 +140,6 @@ export default function QuickNav() {
|
|
|
}, [isAdminDev, knownBranches, selectedBranch]);
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
- // Sync selectedBranch to the current route branch ONLY when that route branch is known to exist.
|
|
|
- // This prevents the "NL200 thrash" while still keeping the dropdown in sync for valid routes.
|
|
|
if (!isAdminDev) return;
|
|
|
if (!isKnownRouteBranch) return;
|
|
|
if (!routeBranch) return;
|
|
|
@@ -164,16 +154,11 @@ export default function QuickNav() {
|
|
|
if (!isAdminDev) return;
|
|
|
if (!selectedBranch) return;
|
|
|
|
|
|
- // Keep localStorage in sync (defense-in-depth).
|
|
|
safeWriteLocalStorageBranch(STORAGE_KEY_LAST_BRANCH, selectedBranch);
|
|
|
}, [isAdminDev, selectedBranch]);
|
|
|
|
|
|
if (!isAuthenticated) return null;
|
|
|
|
|
|
- // Effective branch for navigation:
|
|
|
- // - Branch users: always their own branch
|
|
|
- // - Admin/dev: always the persisted/validated selection (NOT the route branch)
|
|
|
- // This guarantees that nav buttons still work even when the user is on /NL200.
|
|
|
const effectiveBranch = isBranchUser ? user.branchId : selectedBranch;
|
|
|
|
|
|
const canNavigate = Boolean(
|
|
|
@@ -199,15 +184,10 @@ export default function QuickNav() {
|
|
|
|
|
|
if (!nextUrl) return;
|
|
|
|
|
|
- // Trigger a session revalidation without causing content flicker.
|
|
|
if (canRevalidate) retry();
|
|
|
-
|
|
|
router.push(nextUrl);
|
|
|
}
|
|
|
|
|
|
- const explorerVariant = isExplorerActive ? "secondary" : "ghost";
|
|
|
- const searchVariant = isSearchActive ? "secondary" : "ghost";
|
|
|
-
|
|
|
return (
|
|
|
<div className="hidden items-center gap-2 md:flex">
|
|
|
{isAdminDev ? (
|
|
|
@@ -240,7 +220,6 @@ export default function QuickNav() {
|
|
|
|
|
|
setSelectedBranch(value);
|
|
|
safeWriteLocalStorageBranch(STORAGE_KEY_LAST_BRANCH, value);
|
|
|
-
|
|
|
navigateToBranchKeepingContext(value);
|
|
|
}}
|
|
|
>
|
|
|
@@ -259,10 +238,11 @@ export default function QuickNav() {
|
|
|
) : null}
|
|
|
|
|
|
<Button
|
|
|
- variant={explorerVariant}
|
|
|
+ variant="outline"
|
|
|
size="sm"
|
|
|
asChild
|
|
|
disabled={!canNavigate}
|
|
|
+ className={isExplorerActive ? ACTIVE_NAV_BUTTON_CLASS : ""}
|
|
|
>
|
|
|
<Link
|
|
|
href={canNavigate ? branchPath(effectiveBranch) : "#"}
|
|
|
@@ -274,7 +254,13 @@ export default function QuickNav() {
|
|
|
</Link>
|
|
|
</Button>
|
|
|
|
|
|
- <Button variant={searchVariant} size="sm" asChild disabled={!canNavigate}>
|
|
|
+ <Button
|
|
|
+ variant="outline"
|
|
|
+ size="sm"
|
|
|
+ asChild
|
|
|
+ disabled={!canNavigate}
|
|
|
+ className={isSearchActive ? ACTIVE_NAV_BUTTON_CLASS : ""}
|
|
|
+ >
|
|
|
<Link
|
|
|
href={canNavigate ? searchPath(effectiveBranch) : "#"}
|
|
|
title="Suche öffnen"
|