|
|
@@ -2,6 +2,8 @@
|
|
|
|
|
|
import React from "react";
|
|
|
import Link from "next/link";
|
|
|
+import { usePathname, useRouter } from "next/navigation";
|
|
|
+import { FolderOpen, Search as SearchIcon } from "lucide-react";
|
|
|
|
|
|
import { useAuth } from "@/components/auth/authContext";
|
|
|
import { getBranches } from "@/lib/frontend/apiClient";
|
|
|
@@ -13,6 +15,10 @@ import {
|
|
|
safeReadLocalStorageBranch,
|
|
|
safeWriteLocalStorageBranch,
|
|
|
} from "@/lib/frontend/quickNav/branchSwitch";
|
|
|
+import {
|
|
|
+ getPrimaryNavFromPathname,
|
|
|
+ PRIMARY_NAV,
|
|
|
+} from "@/lib/frontend/nav/activeRoute";
|
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
import {
|
|
|
@@ -35,6 +41,9 @@ const BRANCH_LIST_STATE = Object.freeze({
|
|
|
});
|
|
|
|
|
|
export default function QuickNav() {
|
|
|
+ const router = useRouter();
|
|
|
+ const pathname = usePathname() || "/";
|
|
|
+
|
|
|
const { status, user } = useAuth();
|
|
|
|
|
|
const isAuthenticated = status === "authenticated" && user;
|
|
|
@@ -49,26 +58,39 @@ export default function QuickNav() {
|
|
|
branches: null,
|
|
|
});
|
|
|
|
|
|
+ const activePrimaryNav = React.useMemo(() => {
|
|
|
+ return getPrimaryNavFromPathname(pathname);
|
|
|
+ }, [pathname]);
|
|
|
+
|
|
|
+ const isExplorerActive = activePrimaryNav?.active === PRIMARY_NAV.EXPLORER;
|
|
|
+ const isSearchActive = activePrimaryNav?.active === PRIMARY_NAV.SEARCH;
|
|
|
+
|
|
|
React.useEffect(() => {
|
|
|
if (!isAuthenticated) return;
|
|
|
|
|
|
+ // Branch users: selection is fixed to their own branch.
|
|
|
if (isBranchUser) {
|
|
|
const own = user.branchId;
|
|
|
setSelectedBranch(own && isValidBranchParam(own) ? own : null);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- const fromRoute = readRouteBranchFromPathname(window.location.pathname);
|
|
|
+ // Admin/dev: prefer current route branch, fallback to last-used localStorage.
|
|
|
+ const fromRoute = readRouteBranchFromPathname(pathname);
|
|
|
const fromStorage = safeReadLocalStorageBranch(STORAGE_KEY_LAST_BRANCH);
|
|
|
|
|
|
const initial = fromRoute || fromStorage || null;
|
|
|
- if (initial) setSelectedBranch(initial);
|
|
|
- }, [isAuthenticated, isBranchUser, user?.branchId]);
|
|
|
+
|
|
|
+ // Avoid unnecessary state updates.
|
|
|
+ if (initial && initial !== selectedBranch) {
|
|
|
+ setSelectedBranch(initial);
|
|
|
+ safeWriteLocalStorageBranch(STORAGE_KEY_LAST_BRANCH, initial);
|
|
|
+ }
|
|
|
+ }, [isAuthenticated, isBranchUser, user?.branchId, pathname, selectedBranch]);
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
- // B.4 fix:
|
|
|
- // - Fetch the branch list once for admin/dev users (or when the user changes),
|
|
|
- // not on every selectedBranch change.
|
|
|
+ // Fetch the branch list once for admin/dev users (or when the user changes),
|
|
|
+ // not on every selectedBranch change.
|
|
|
if (!isAdminDev) return;
|
|
|
|
|
|
let cancelled = false;
|
|
|
@@ -106,7 +128,6 @@ export default function QuickNav() {
|
|
|
: [];
|
|
|
if (branches.length === 0) return;
|
|
|
|
|
|
- // If no selection yet (or selection is no longer in the list), choose a stable default.
|
|
|
if (!selectedBranch || !branches.includes(selectedBranch)) {
|
|
|
const next = branches[0];
|
|
|
setSelectedBranch(next);
|
|
|
@@ -125,24 +146,38 @@ export default function QuickNav() {
|
|
|
|
|
|
const effectiveBranch = isBranchUser ? user.branchId : selectedBranch;
|
|
|
const canNavigate = Boolean(
|
|
|
- effectiveBranch && isValidBranchParam(effectiveBranch)
|
|
|
+ effectiveBranch && isValidBranchParam(effectiveBranch),
|
|
|
);
|
|
|
|
|
|
function navigateToBranchKeepingContext(nextBranch) {
|
|
|
- if (typeof window === "undefined") return;
|
|
|
if (!isValidBranchParam(nextBranch)) return;
|
|
|
|
|
|
+ // IMPORTANT:
|
|
|
+ // Avoid useSearchParams() here to prevent build-time prerender failures on static routes.
|
|
|
+ // We only need the current query string at click-time (client-only), so window is fine.
|
|
|
+ const currentPathname =
|
|
|
+ typeof window !== "undefined"
|
|
|
+ ? window.location.pathname || pathname
|
|
|
+ : pathname;
|
|
|
+
|
|
|
+ const currentSearch =
|
|
|
+ typeof window !== "undefined" ? window.location.search || "" : "";
|
|
|
+
|
|
|
const nextUrl = buildNextUrlForBranchSwitch({
|
|
|
- pathname: window.location.pathname || "/",
|
|
|
- search: window.location.search || "",
|
|
|
+ pathname: currentPathname,
|
|
|
+ search: currentSearch,
|
|
|
nextBranch,
|
|
|
});
|
|
|
|
|
|
if (!nextUrl) return;
|
|
|
|
|
|
- window.location.assign(nextUrl);
|
|
|
+ // Client navigation: keeps providers mounted and avoids hard reload flicker.
|
|
|
+ router.push(nextUrl);
|
|
|
}
|
|
|
|
|
|
+ const explorerVariant = isExplorerActive ? "secondary" : "ghost";
|
|
|
+ const searchVariant = isSearchActive ? "secondary" : "ghost";
|
|
|
+
|
|
|
return (
|
|
|
<div className="hidden items-center gap-2 md:flex">
|
|
|
{isAdminDev ? (
|
|
|
@@ -193,20 +228,29 @@ export default function QuickNav() {
|
|
|
</DropdownMenu>
|
|
|
) : null}
|
|
|
|
|
|
- <Button variant="outline" size="sm" asChild disabled={!canNavigate}>
|
|
|
+ <Button
|
|
|
+ variant={explorerVariant}
|
|
|
+ size="sm"
|
|
|
+ asChild
|
|
|
+ disabled={!canNavigate}
|
|
|
+ >
|
|
|
<Link
|
|
|
href={canNavigate ? branchPath(effectiveBranch) : "#"}
|
|
|
title="Explorer öffnen"
|
|
|
+ aria-current={isExplorerActive ? "page" : undefined}
|
|
|
>
|
|
|
+ <FolderOpen className="h-4 w-4" />
|
|
|
Explorer
|
|
|
</Link>
|
|
|
</Button>
|
|
|
|
|
|
- <Button variant="outline" size="sm" asChild disabled={!canNavigate}>
|
|
|
+ <Button variant={searchVariant} size="sm" asChild disabled={!canNavigate}>
|
|
|
<Link
|
|
|
href={canNavigate ? searchPath(effectiveBranch) : "#"}
|
|
|
title="Suche öffnen"
|
|
|
+ aria-current={isSearchActive ? "page" : undefined}
|
|
|
>
|
|
|
+ <SearchIcon className="h-4 w-4" />
|
|
|
Suche
|
|
|
</Link>
|
|
|
</Button>
|