1
0
Fork 0

feat(sidebar): regroup commerce nav and add XP mini toggle

Move Banking and Market out of Logistics into a commerce group beside Personal, and add a quick XP mini-mode toggle synced to the saved display preference via the layout-provided preferences prop.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
Jason Fraley 2026-08-23 18:52:18 -04:00
parent 906af4feb1
commit 38400366a1
2 changed files with 83 additions and 15 deletions

View file

@ -124,6 +124,7 @@ export default async function RootLayout(props: { children: React.ReactNode }) {
hasLogistics={isLogistics}
showCallsign={user.preferences?.display?.showCallsign ?? false}
canImpersonate={await isSuperuser(payload, user)}
preferences={user.preferences}
className="border-r"
/>
<SidebarInset>

View file

@ -10,9 +10,13 @@ import {
Search,
Send,
Shield,
ShoppingBag,
TrendingUp,
UserCog,
Users,
Warehouse,
Maximize,
Minimize,
} from "lucide-react";
import { NavCore } from "@/components/frontend/blocks/NavCore";
@ -31,7 +35,9 @@ 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 { Rank } from "@/payload-types";
import { Rank, User } from "@/payload-types";
import { Progress } from "@/components/ui/progress";
import { updatePreferences } from "@/app/(frontend)/account/actions";
const data = {
navMain: [
@ -45,6 +51,8 @@ const data = {
url: "/roster",
icon: Users,
},
],
navHelpdesk: [
{
title: "Helpdesk",
url: "/helpdesk",
@ -108,22 +116,19 @@ const data = {
title: "Deployed Vehicles",
url: "/logistics/game-vehicles",
},
],
},
],
navCommerce: [
{
title: "Banking",
url: "/logistics/banking",
icon: TrendingUp,
},
{
title: "Market",
url: "/logistics/market",
},
],
},
],
navSecondary: [
{
title: "Feedback",
url: "https://onyxsimple.youtrack.cloud",
icon: Send,
icon: ShoppingBag,
},
],
navPersonal: [
@ -138,6 +143,13 @@ const data = {
icon: UserCog,
},
],
navSecondary: [
{
title: "Feedback",
url: "https://onyxsimple.youtrack.cloud",
icon: Send,
},
],
};
export function AppSidebar({
@ -148,6 +160,7 @@ export function AppSidebar({
hasLogistics = true,
showCallsign = false,
canImpersonate = false,
preferences,
...props
}: {
user: {
@ -161,7 +174,36 @@ export function AppSidebar({
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,
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>
@ -208,6 +250,8 @@ export function AppSidebar({
)}
/>
)}
<NavCore label="Helpdesk" items={data.navHelpdesk} />
<NavCore label="Commerce" items={data.navCommerce} />
<NavCore label="Personal" items={data.navPersonal} />
{/*<NavProjects projects={data.projects} />*/}
</div>
@ -226,9 +270,32 @@ export function AppSidebar({
}
className="mt-auto"
/>
<XPDisplay xpLevel={xpLevel} />
</SidebarContent>
<SidebarFooter>
<div className="flex flex-col">
<div className="flex items-center justify-between">
{isMiniMode ? (
<div className="flex items-center gap-2">
<div className="text-xs text-muted-foreground uppercase">
{xpLevel.levelName}
</div>
<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