changes
This commit is contained in:
@@ -3,7 +3,18 @@
|
||||
import Link from "next/link";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useEffect, useMemo, useState, useTransition } from "react";
|
||||
import { BarChart3, Bell, DollarSign, LayoutGrid, Loader2, LogOut, Settings, Wrench, X } from "lucide-react";
|
||||
import {
|
||||
BarChart3,
|
||||
Bell,
|
||||
DollarSign,
|
||||
LayoutGrid,
|
||||
Loader2,
|
||||
LogOut,
|
||||
Settings,
|
||||
Sunrise,
|
||||
Wrench,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import { useI18n } from "@/lib/i18n/useI18n";
|
||||
import { useScreenlessMode } from "@/lib/ui/screenlessMode";
|
||||
@@ -24,10 +35,10 @@ const items: NavItem[] = [
|
||||
{ href: "/reports", labelKey: "nav.reports", icon: BarChart3 },
|
||||
{ href: "/alerts", labelKey: "nav.alerts", icon: Bell },
|
||||
{ href: "/financial", labelKey: "nav.financial", icon: DollarSign, ownerOnly: true },
|
||||
{ href: "/settings", labelKey: "nav.settings", icon: Settings },
|
||||
{ href: "/downtime", labelKey: "nav.downtime", icon: BarChart3 },
|
||||
|
||||
{ href: "/recap", labelKey: "nav.recap", icon: Sunrise },
|
||||
];
|
||||
const settingsItem: NavItem = { href: "/settings", labelKey: "nav.settings", icon: Settings };
|
||||
|
||||
type SidebarProps = {
|
||||
variant?: "desktop" | "drawer";
|
||||
@@ -97,16 +108,7 @@ export function Sidebar({ variant = "desktop", onNavigate, onClose }: SidebarPro
|
||||
}
|
||||
}, [screenlessMode, pathname, router]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pendingHref) return;
|
||||
if (pathname === pendingHref || pathname.startsWith(`${pendingHref}/`)) {
|
||||
setPendingHref(null);
|
||||
} else if (!isPending) {
|
||||
setPendingHref(null);
|
||||
}
|
||||
}, [pathname, pendingHref, isPending]);
|
||||
|
||||
const markNavStart = (href: string) => {
|
||||
const markNavStart = (href: string, ts: number) => {
|
||||
if (!PERF_ENABLED) return;
|
||||
try {
|
||||
sessionStorage.setItem(
|
||||
@@ -114,7 +116,7 @@ export function Sidebar({ variant = "desktop", onNavigate, onClose }: SidebarPro
|
||||
JSON.stringify({
|
||||
href,
|
||||
from: pathname,
|
||||
ts: Date.now(),
|
||||
ts,
|
||||
})
|
||||
);
|
||||
} catch {
|
||||
@@ -128,6 +130,58 @@ export function Sidebar({ variant = "desktop", onNavigate, onClose }: SidebarPro
|
||||
"relative z-20 flex flex-col border-r border-white/10 bg-black/40 shrink-0",
|
||||
variant === "desktop" ? "hidden md:flex h-screen w-64" : "flex h-full w-72 max-w-[85vw]",
|
||||
].join(" ");
|
||||
const navLocked = isPending;
|
||||
|
||||
const renderNavItem = (it: NavItem) => {
|
||||
const isCurrent = pathname === it.href;
|
||||
const active = isCurrent || pathname.startsWith(it.href + "/");
|
||||
const isPendingItem = isPending && pendingHref === it.href;
|
||||
const Icon = it.icon;
|
||||
return (
|
||||
<Link
|
||||
key={it.href}
|
||||
href={it.href}
|
||||
prefetch={false}
|
||||
aria-disabled={navLocked}
|
||||
onClick={(event) => {
|
||||
if (
|
||||
navLocked ||
|
||||
event.defaultPrevented ||
|
||||
event.button !== 0 ||
|
||||
event.metaKey ||
|
||||
event.altKey ||
|
||||
event.ctrlKey ||
|
||||
event.shiftKey
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (isCurrent) {
|
||||
onNavigate?.();
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
markNavStart(it.href, Math.round(performance.timeOrigin + event.timeStamp));
|
||||
setPendingHref(it.href);
|
||||
startTransition(() => {
|
||||
router.push(it.href);
|
||||
});
|
||||
onNavigate?.();
|
||||
}}
|
||||
className={[
|
||||
"flex items-center gap-3 rounded-xl px-3 py-2 text-sm transition",
|
||||
active
|
||||
? "bg-emerald-500/15 text-emerald-300 border border-emerald-500/20"
|
||||
: "text-zinc-300 hover:bg-white/5 hover:text-white",
|
||||
navLocked ? "pointer-events-none" : "",
|
||||
navLocked && !isPendingItem ? "opacity-60" : "",
|
||||
].join(" ")}
|
||||
>
|
||||
<Icon className="h-4 w-4" />
|
||||
<span>{t(it.labelKey)}</span>
|
||||
{isPendingItem ? <Loader2 className="ml-auto h-4 w-4 animate-spin text-emerald-300" /> : null}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<aside className={shellClass} aria-label={t("sidebar.productTitle")}>
|
||||
@@ -148,58 +202,9 @@ export function Sidebar({ variant = "desktop", onNavigate, onClose }: SidebarPro
|
||||
)}
|
||||
</div>
|
||||
|
||||
<nav className="px-3 py-2 flex-1 space-y-1">
|
||||
{visibleItems.map((it) => {
|
||||
const isCurrent = pathname === it.href;
|
||||
const active = isCurrent || pathname.startsWith(it.href + "/");
|
||||
const isPendingItem = isPending && pendingHref === it.href;
|
||||
const navLocked = isPending;
|
||||
const Icon = it.icon;
|
||||
return (
|
||||
<Link
|
||||
key={it.href}
|
||||
href={it.href}
|
||||
prefetch={false}
|
||||
aria-disabled={navLocked}
|
||||
onClick={(event) => {
|
||||
if (
|
||||
navLocked ||
|
||||
event.defaultPrevented ||
|
||||
event.button !== 0 ||
|
||||
event.metaKey ||
|
||||
event.altKey ||
|
||||
event.ctrlKey ||
|
||||
event.shiftKey
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (isCurrent) {
|
||||
onNavigate?.();
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
markNavStart(it.href);
|
||||
setPendingHref(it.href);
|
||||
startTransition(() => {
|
||||
router.push(it.href);
|
||||
});
|
||||
onNavigate?.();
|
||||
}}
|
||||
className={[
|
||||
"flex items-center gap-3 rounded-xl px-3 py-2 text-sm transition",
|
||||
active
|
||||
? "bg-emerald-500/15 text-emerald-300 border border-emerald-500/20"
|
||||
: "text-zinc-300 hover:bg-white/5 hover:text-white",
|
||||
navLocked ? "pointer-events-none" : "",
|
||||
navLocked && !isPendingItem ? "opacity-60" : "",
|
||||
].join(" ")}
|
||||
>
|
||||
<Icon className="h-4 w-4" />
|
||||
<span>{t(it.labelKey)}</span>
|
||||
{isPendingItem ? <Loader2 className="ml-auto h-4 w-4 animate-spin text-emerald-300" /> : null}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
<nav className="px-3 py-2 flex-1 flex flex-col gap-2">
|
||||
<div className="space-y-1">{visibleItems.map(renderNavItem)}</div>
|
||||
<div className="mt-auto space-y-1 border-t border-white/10 pt-2">{renderNavItem(settingsItem)}</div>
|
||||
</nav>
|
||||
|
||||
<div className="px-5 py-4 border-t border-white/10 space-y-3">
|
||||
|
||||
Reference in New Issue
Block a user