Replace nested section submenus in Intelligence, Infrastructure, and Personnel with flat per-page links carrying icons; drop the defensive item copying in AppSidebar. Ultraworked with [Sisyphus](https://github.com/code-yeongju/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
207 lines
6.9 KiB
TypeScript
207 lines
6.9 KiB
TypeScript
"use client";
|
|
|
|
import * as React from "react";
|
|
import {
|
|
ArrowLeftRight,
|
|
Bolt,
|
|
Shield,
|
|
Users,
|
|
Maximize,
|
|
Minimize,
|
|
SparkleIcon,
|
|
} from "lucide-react";
|
|
|
|
import { NavCore } from "@/components/frontend/blocks/NavCore";
|
|
import { NavSecondary } from "@/components/frontend/blocks/NavSecondary";
|
|
import { NavUser } from "@/components/frontend/blocks/NavUser";
|
|
import {
|
|
Sidebar,
|
|
SidebarContent,
|
|
SidebarFooter,
|
|
SidebarHeader,
|
|
SidebarMenu,
|
|
SidebarMenuButton,
|
|
SidebarMenuItem,
|
|
} from "@/components/ui/sidebar";
|
|
import Image from "next/image";
|
|
import Link from "next/link";
|
|
import XPDisplay from "@/components/frontend/blocks/XPDisplay";
|
|
import type { ResolvedLevel } from "@/utils/xp/resolveLevel";
|
|
import type { User } from "@/payload-types";
|
|
import { Progress } from "@/components/ui/progress";
|
|
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
|
import { updatePreferences } from "@/app/(frontend)/account/actions";
|
|
import { sidebarData } from "@/components/frontend/blocks/sidebarData";
|
|
|
|
const data = {
|
|
navMain: [
|
|
{
|
|
title: "Dashboard",
|
|
url: "/",
|
|
icon: Bolt,
|
|
},
|
|
{
|
|
title: "Roster",
|
|
url: "/roster",
|
|
icon: Users,
|
|
},
|
|
{
|
|
title: "Transfers",
|
|
url: "/transfers",
|
|
icon: ArrowLeftRight,
|
|
},
|
|
],
|
|
...sidebarData,
|
|
};
|
|
|
|
export function AppSidebar({
|
|
user,
|
|
xpLevel,
|
|
adminUrl,
|
|
hasIntelligence = true,
|
|
hasLogistics = true,
|
|
showCallsign = false,
|
|
canImpersonate = false,
|
|
preferences,
|
|
...props
|
|
}: {
|
|
user: {
|
|
displayName: string;
|
|
username: string;
|
|
rank: string;
|
|
};
|
|
xpLevel: ResolvedLevel;
|
|
adminUrl?: string;
|
|
hasIntelligence?: boolean;
|
|
hasLogistics?: boolean;
|
|
showCallsign?: boolean;
|
|
canImpersonate?: boolean;
|
|
preferences?: NonNullable<User["preferences"]>;
|
|
} & React.ComponentProps<typeof Sidebar>) {
|
|
const [isMiniMode, setIsMiniMode] = React.useState<boolean>(
|
|
preferences?.display?.xpDisplayMini ?? false,
|
|
);
|
|
|
|
// Re-sync when the saved preference changes (e.g. account page save + router.refresh()).
|
|
const savedXpDisplayMini = preferences?.display?.xpDisplayMini ?? false;
|
|
React.useEffect(() => {
|
|
setIsMiniMode(savedXpDisplayMini);
|
|
}, [savedXpDisplayMini]);
|
|
|
|
const handleToggleXPMode = React.useCallback(async () => {
|
|
try {
|
|
await updatePreferences({
|
|
mutedAll: preferences?.notifications?.mutedAll ?? false,
|
|
mutedTypes: preferences?.notifications?.mutedTypes ?? [],
|
|
showCallsign: preferences?.display?.showCallsign ?? false,
|
|
showVersionOverlay: preferences?.display?.showVersionOverlay ?? true,
|
|
showBackgroundTexture: preferences?.display?.showBackgroundTexture ?? true,
|
|
leadOrActual: preferences?.display?.leadOrActual ?? "lead",
|
|
discordEnabled: preferences?.discord?.enabled ?? false,
|
|
discordMutedTypes: preferences?.discord?.mutedTypes ?? [],
|
|
xpDisplayMini: !isMiniMode,
|
|
});
|
|
setIsMiniMode(!isMiniMode);
|
|
} catch (error) {
|
|
console.error("Failed to update XP display preference:", error);
|
|
}
|
|
}, [isMiniMode, preferences]);
|
|
|
|
return (
|
|
<Sidebar variant="inset" {...props}>
|
|
<SidebarHeader>
|
|
<SidebarMenu>
|
|
<SidebarMenuItem>
|
|
<SidebarMenuButton size="lg" asChild>
|
|
<Link href="/">
|
|
{/*<div className="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
|
|
<Command className="size-4" />
|
|
</div>
|
|
<div className="grid flex-1 text-left text-sm leading-tight">
|
|
<span className="truncate font-medium">Polaris Task Force</span>
|
|
<span className="truncate text-xs">Enterprise</span>
|
|
</div>*/}
|
|
<div>
|
|
<Image
|
|
src="/images/logo.webp"
|
|
alt="Polaris Task Force"
|
|
width={1372}
|
|
height={164}
|
|
/>
|
|
</div>
|
|
</Link>
|
|
</SidebarMenuButton>
|
|
</SidebarMenuItem>
|
|
</SidebarMenu>
|
|
</SidebarHeader>
|
|
<SidebarContent className="overflow-hidden">
|
|
<div className="flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto group-data-[collapsible=icon]:overflow-hidden">
|
|
<NavCore label="Platform" items={data.navMain} />
|
|
{hasIntelligence && (
|
|
<NavCore label="Intelligence" items={data.navIntelligence} />
|
|
)}
|
|
{hasLogistics && (
|
|
<NavCore label="Infrastructure" items={data.navLogistics} />
|
|
)}
|
|
<NavCore label="Helpdesk" items={data.navHelpdesk} />
|
|
<NavCore label="Commerce" items={data.navCommerce} />
|
|
<NavCore label="Personnel" items={data.navPersonnel} />
|
|
<NavCore label="Personal" items={data.navPersonal} />
|
|
{/*<NavProjects projects={data.projects} />*/}
|
|
</div>
|
|
<hr />
|
|
<NavSecondary
|
|
items={
|
|
adminUrl
|
|
? [...data.navSecondary, { title: "Admin", url: adminUrl, icon: Shield }]
|
|
: data.navSecondary
|
|
}
|
|
className="mt-auto"
|
|
/>
|
|
</SidebarContent>
|
|
<SidebarFooter>
|
|
<div className="flex flex-col">
|
|
<div className="flex items-center justify-between">
|
|
{isMiniMode ? (
|
|
<div className="flex items-center gap-2">
|
|
<Tooltip>
|
|
<TooltipTrigger>
|
|
<div className="text-xs text-muted-foreground uppercase">
|
|
{xpLevel.levelName}
|
|
</div>
|
|
</TooltipTrigger>
|
|
<TooltipContent>
|
|
<p>{xpLevel.levelDesc}</p>
|
|
</TooltipContent>
|
|
</Tooltip>
|
|
<SparkleIcon className="w-4 h-4" />
|
|
<span className="text-xs font-bold">{xpLevel.xp.toLocaleString()}</span>
|
|
<Progress value={xpLevel.progress} className="w-20 h-1" />
|
|
</div>
|
|
) : (
|
|
<XPDisplay xpLevel={xpLevel} />
|
|
)}
|
|
<button
|
|
onClick={handleToggleXPMode}
|
|
className="p-1 rounded-md hover:bg-sidebar-accent hover:text-sidebar-accent-foreground transition-colors border border-transparent hover:border-sidebar-border"
|
|
aria-label={isMiniMode ? "Expand XP Display" : "Minimize XP Display"}
|
|
>
|
|
{isMiniMode ? <Maximize size={16} /> : <Minimize size={16} />}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<NavUser
|
|
user={{
|
|
displayName: user?.displayName
|
|
? [user?.rank ? `(${user?.rank}.)` : null, user?.displayName].join(" ")
|
|
: "Not logged in",
|
|
username: user?.username ?? "",
|
|
avatar: "",
|
|
}}
|
|
showCallsign={showCallsign}
|
|
canImpersonate={canImpersonate}
|
|
/>
|
|
</SidebarFooter>
|
|
</Sidebar>
|
|
);
|
|
}
|