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:
parent
906af4feb1
commit
38400366a1
2 changed files with 83 additions and 15 deletions
|
|
@ -124,6 +124,7 @@ export default async function RootLayout(props: { children: React.ReactNode }) {
|
||||||
hasLogistics={isLogistics}
|
hasLogistics={isLogistics}
|
||||||
showCallsign={user.preferences?.display?.showCallsign ?? false}
|
showCallsign={user.preferences?.display?.showCallsign ?? false}
|
||||||
canImpersonate={await isSuperuser(payload, user)}
|
canImpersonate={await isSuperuser(payload, user)}
|
||||||
|
preferences={user.preferences}
|
||||||
className="border-r"
|
className="border-r"
|
||||||
/>
|
/>
|
||||||
<SidebarInset>
|
<SidebarInset>
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,13 @@ import {
|
||||||
Search,
|
Search,
|
||||||
Send,
|
Send,
|
||||||
Shield,
|
Shield,
|
||||||
|
ShoppingBag,
|
||||||
|
TrendingUp,
|
||||||
UserCog,
|
UserCog,
|
||||||
Users,
|
Users,
|
||||||
Warehouse,
|
Warehouse,
|
||||||
|
Maximize,
|
||||||
|
Minimize,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
import { NavCore } from "@/components/frontend/blocks/NavCore";
|
import { NavCore } from "@/components/frontend/blocks/NavCore";
|
||||||
|
|
@ -31,7 +35,9 @@ import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import XPDisplay from "@/components/frontend/blocks/XPDisplay";
|
import XPDisplay from "@/components/frontend/blocks/XPDisplay";
|
||||||
import type { ResolvedLevel } from "@/utils/xp/resolveLevel";
|
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 = {
|
const data = {
|
||||||
navMain: [
|
navMain: [
|
||||||
|
|
@ -45,6 +51,8 @@ const data = {
|
||||||
url: "/roster",
|
url: "/roster",
|
||||||
icon: Users,
|
icon: Users,
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
navHelpdesk: [
|
||||||
{
|
{
|
||||||
title: "Helpdesk",
|
title: "Helpdesk",
|
||||||
url: "/helpdesk",
|
url: "/helpdesk",
|
||||||
|
|
@ -108,22 +116,19 @@ const data = {
|
||||||
title: "Deployed Vehicles",
|
title: "Deployed Vehicles",
|
||||||
url: "/logistics/game-vehicles",
|
url: "/logistics/game-vehicles",
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
navCommerce: [
|
||||||
{
|
{
|
||||||
title: "Banking",
|
title: "Banking",
|
||||||
url: "/logistics/banking",
|
url: "/logistics/banking",
|
||||||
|
icon: TrendingUp,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Market",
|
title: "Market",
|
||||||
url: "/logistics/market",
|
url: "/logistics/market",
|
||||||
},
|
icon: ShoppingBag,
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
navSecondary: [
|
|
||||||
{
|
|
||||||
title: "Feedback",
|
|
||||||
url: "https://onyxsimple.youtrack.cloud",
|
|
||||||
icon: Send,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
navPersonal: [
|
navPersonal: [
|
||||||
|
|
@ -138,6 +143,13 @@ const data = {
|
||||||
icon: UserCog,
|
icon: UserCog,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
navSecondary: [
|
||||||
|
{
|
||||||
|
title: "Feedback",
|
||||||
|
url: "https://onyxsimple.youtrack.cloud",
|
||||||
|
icon: Send,
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
export function AppSidebar({
|
export function AppSidebar({
|
||||||
|
|
@ -148,6 +160,7 @@ export function AppSidebar({
|
||||||
hasLogistics = true,
|
hasLogistics = true,
|
||||||
showCallsign = false,
|
showCallsign = false,
|
||||||
canImpersonate = false,
|
canImpersonate = false,
|
||||||
|
preferences,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
user: {
|
user: {
|
||||||
|
|
@ -161,7 +174,36 @@ export function AppSidebar({
|
||||||
hasLogistics?: boolean;
|
hasLogistics?: boolean;
|
||||||
showCallsign?: boolean;
|
showCallsign?: boolean;
|
||||||
canImpersonate?: boolean;
|
canImpersonate?: boolean;
|
||||||
|
preferences?: NonNullable<User["preferences"]>;
|
||||||
} & React.ComponentProps<typeof Sidebar>) {
|
} & 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 (
|
return (
|
||||||
<Sidebar variant="inset" {...props}>
|
<Sidebar variant="inset" {...props}>
|
||||||
<SidebarHeader>
|
<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} />
|
<NavCore label="Personal" items={data.navPersonal} />
|
||||||
{/*<NavProjects projects={data.projects} />*/}
|
{/*<NavProjects projects={data.projects} />*/}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -226,9 +270,32 @@ export function AppSidebar({
|
||||||
}
|
}
|
||||||
className="mt-auto"
|
className="mt-auto"
|
||||||
/>
|
/>
|
||||||
<XPDisplay xpLevel={xpLevel} />
|
|
||||||
</SidebarContent>
|
</SidebarContent>
|
||||||
<SidebarFooter>
|
<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
|
<NavUser
|
||||||
user={{
|
user={{
|
||||||
displayName: user?.displayName
|
displayName: user?.displayName
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue