refactor: streamline settings navigation chrome
This commit is contained in:
parent
aea1d84c88
commit
1307095960
@ -9,7 +9,7 @@ type AdminLayoutShellProps = ComponentProps<typeof TopNavbar> & {
|
||||
};
|
||||
|
||||
function shouldHideSidebarChrome(pathname: string) {
|
||||
return pathname === "/settings";
|
||||
return pathname === "/settings" || pathname.startsWith("/settings/");
|
||||
}
|
||||
|
||||
export function AdminLayoutShell({
|
||||
|
||||
@ -1,16 +1,8 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { ArrowLeft, ChevronDown } from "lucide-react";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@ -25,7 +17,6 @@ import { Separator } from "@/components/ui/separator";
|
||||
import { ADMIN_SETTINGS_NAV_GROUPS } from "../_lib/settings-navigation";
|
||||
import type { AdminSettingsNavGroup, AdminSettingsNavItem } from "../_lib/types";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { SettingStatusBadge } from "./setting-status-badge";
|
||||
|
||||
function getActiveGroup(activeTab: string) {
|
||||
return ADMIN_SETTINGS_NAV_GROUPS.find((group) =>
|
||||
@ -33,78 +24,48 @@ function getActiveGroup(activeTab: string) {
|
||||
);
|
||||
}
|
||||
|
||||
function SettingsGroupDropdown({
|
||||
activeTab,
|
||||
function SettingsGroupTab({
|
||||
group,
|
||||
isActive,
|
||||
onTabChange,
|
||||
}: {
|
||||
activeTab: string;
|
||||
group: AdminSettingsNavGroup;
|
||||
isActive: boolean;
|
||||
onTabChange: (value: string) => void;
|
||||
}) {
|
||||
const hasActiveTab = group.items.some((item) => item.value === activeTab);
|
||||
const defaultItem = group.items[0];
|
||||
const hasHref = Boolean(defaultItem?.href);
|
||||
const className = cn(
|
||||
"h-10 w-full justify-center rounded-2xl border px-4 text-sm font-medium shadow-none transition-colors",
|
||||
isActive
|
||||
? "border-foreground/10 bg-foreground text-background hover:bg-foreground/90 hover:text-background dark:border-white/20 dark:bg-white dark:text-black dark:hover:bg-white/90"
|
||||
: "border-border/70 bg-zinc-50/80 text-muted-foreground hover:bg-background hover:text-foreground"
|
||||
);
|
||||
|
||||
if (!defaultItem) return null;
|
||||
|
||||
if (hasHref) {
|
||||
return (
|
||||
<Button
|
||||
asChild
|
||||
className={className}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
>
|
||||
<Link href={defaultItem.href!}>{group.title}</Link>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
className={cn(
|
||||
"h-9 rounded-full border px-3.5 text-sm shadow-none",
|
||||
hasActiveTab
|
||||
? "border-foreground/10 bg-foreground text-background hover:bg-foreground/90 hover:text-background dark:border-white/20 dark:bg-white dark:text-black dark:hover:bg-white/90"
|
||||
: "border-border/70 bg-background/90 text-muted-foreground hover:bg-background hover:text-foreground"
|
||||
)}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
>
|
||||
<span>{group.title}</span>
|
||||
<ChevronDown className="h-3.5 w-3.5 opacity-70" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" className="w-80">
|
||||
<DropdownMenuLabel className="space-y-1">
|
||||
<div>{group.title}</div>
|
||||
<div className="font-normal text-muted-foreground text-xs">
|
||||
{group.description}
|
||||
</div>
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
{group.items.map((item) => {
|
||||
const Icon = item.icon;
|
||||
const itemContent = (
|
||||
<>
|
||||
<Icon className="h-4 w-4 text-muted-foreground" />
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="font-medium text-sm">{item.label}</div>
|
||||
<div className="truncate text-muted-foreground text-xs">
|
||||
{item.description}
|
||||
</div>
|
||||
</div>
|
||||
{item.value === activeTab ? (
|
||||
<SettingStatusBadge tone="enabled">当前</SettingStatusBadge>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
|
||||
if (item.href) {
|
||||
return (
|
||||
<DropdownMenuItem asChild key={item.value}>
|
||||
<Link href={item.href}>{itemContent}</Link>
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
key={item.value}
|
||||
onSelect={() => onTabChange(item.value)}
|
||||
>
|
||||
{itemContent}
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<Button
|
||||
className={className}
|
||||
onClick={() => onTabChange(defaultItem.value)}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
>
|
||||
{group.title}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
@ -112,28 +73,33 @@ function SettingsQuickAction({
|
||||
activeTab,
|
||||
item,
|
||||
onTabChange,
|
||||
stretched = false,
|
||||
}: {
|
||||
activeTab: string;
|
||||
item: AdminSettingsNavItem;
|
||||
onTabChange: (value: string) => void;
|
||||
stretched?: boolean;
|
||||
}) {
|
||||
const Icon = item.icon;
|
||||
const className =
|
||||
item.value === activeTab
|
||||
? "border-foreground/10 bg-foreground text-background shadow-sm hover:bg-foreground/90 dark:border-white/20 dark:bg-white dark:text-black dark:hover:bg-white/90"
|
||||
: "border-transparent bg-transparent text-muted-foreground hover:border-border/80 hover:bg-background hover:text-foreground";
|
||||
const layoutClassName = stretched
|
||||
? "h-10 w-full min-w-0 justify-center gap-2 rounded-2xl border px-4"
|
||||
: "h-9 shrink-0 gap-2 rounded-full border px-3.5";
|
||||
|
||||
if (item.href) {
|
||||
return (
|
||||
<Button
|
||||
asChild
|
||||
className={`h-9 shrink-0 gap-2 rounded-full border px-3.5 ${className}`}
|
||||
className={`${layoutClassName} ${className}`}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
>
|
||||
<Link href={item.href}>
|
||||
<Icon className="h-4 w-4" />
|
||||
<span>{item.label}</span>
|
||||
<span className={stretched ? "truncate" : ""}>{item.label}</span>
|
||||
</Link>
|
||||
</Button>
|
||||
);
|
||||
@ -141,13 +107,13 @@ function SettingsQuickAction({
|
||||
|
||||
return (
|
||||
<Button
|
||||
className={`h-9 shrink-0 gap-2 rounded-full border px-3.5 ${className}`}
|
||||
className={`${layoutClassName} ${className}`}
|
||||
onClick={() => onTabChange(item.value)}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
>
|
||||
<Icon className="h-4 w-4" />
|
||||
<span>{item.label}</span>
|
||||
<span className={stretched ? "truncate" : ""}>{item.label}</span>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@ -160,6 +126,10 @@ export function AdminSettingsNavigation({
|
||||
onTabChange: (value: string) => void;
|
||||
}) {
|
||||
const activeGroup = getActiveGroup(activeTab);
|
||||
const groupButtons = ADMIN_SETTINGS_NAV_GROUPS.map((group) => ({
|
||||
group,
|
||||
isActive: group.items.some((item) => item.value === activeTab),
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className="rounded-2xl border bg-background px-4 py-3 shadow-sm">
|
||||
@ -229,26 +199,39 @@ export function AdminSettingsNavigation({
|
||||
|
||||
<Separator className="mx-1 h-5 shrink-0" orientation="vertical" />
|
||||
|
||||
<div className="flex min-w-0 flex-1 items-center gap-1 overflow-x-auto">
|
||||
{ADMIN_SETTINGS_NAV_GROUPS.map((group) => (
|
||||
<SettingsGroupDropdown
|
||||
activeTab={activeTab}
|
||||
group={group}
|
||||
key={group.title}
|
||||
onTabChange={onTabChange}
|
||||
/>
|
||||
))}
|
||||
<div className="flex min-w-0 flex-1 items-center gap-3 overflow-hidden">
|
||||
<div className="grid w-[420px] min-w-[420px] grid-cols-3 gap-2">
|
||||
{groupButtons.map(({ group, isActive }) => (
|
||||
<SettingsGroupTab
|
||||
group={group}
|
||||
isActive={isActive}
|
||||
key={group.title}
|
||||
onTabChange={onTabChange}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<Separator className="mx-1 h-5 shrink-0" orientation="vertical" />
|
||||
<Separator className="h-6 shrink-0" orientation="vertical" />
|
||||
|
||||
{(activeGroup?.items ?? []).map((item) => (
|
||||
<SettingsQuickAction
|
||||
activeTab={activeTab}
|
||||
item={item}
|
||||
key={item.value}
|
||||
onTabChange={onTabChange}
|
||||
/>
|
||||
))}
|
||||
<div
|
||||
className="grid min-w-0 flex-1 gap-2"
|
||||
style={{
|
||||
gridTemplateColumns: `repeat(${Math.max(
|
||||
activeGroup?.items.length || 1,
|
||||
1
|
||||
)}, minmax(0, 1fr))`,
|
||||
}}
|
||||
>
|
||||
{(activeGroup?.items ?? []).map((item) => (
|
||||
<SettingsQuickAction
|
||||
activeTab={activeTab}
|
||||
item={item}
|
||||
key={item.value}
|
||||
onTabChange={onTabChange}
|
||||
stretched
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user