refactor: tighten admin settings toolbar
This commit is contained in:
parent
ffccdecc90
commit
de70a23ff6
@ -24,6 +24,7 @@ import {
|
|||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { ADMIN_SETTINGS_NAV_GROUPS } from "../_lib/settings-navigation";
|
import { ADMIN_SETTINGS_NAV_GROUPS } from "../_lib/settings-navigation";
|
||||||
import type { AdminSettingsNavGroup, AdminSettingsNavItem } from "../_lib/types";
|
import type { AdminSettingsNavGroup, AdminSettingsNavItem } from "../_lib/types";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
import { SettingStatusBadge } from "./setting-status-badge";
|
import { SettingStatusBadge } from "./setting-status-badge";
|
||||||
|
|
||||||
function getActiveGroup(activeTab: string) {
|
function getActiveGroup(activeTab: string) {
|
||||||
@ -53,16 +54,26 @@ function SettingsGroupDropdown({
|
|||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
className="h-10 rounded-full px-4"
|
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"
|
size="sm"
|
||||||
variant={hasActiveTab ? "default" : "outline"}
|
variant="ghost"
|
||||||
>
|
>
|
||||||
<span>{group.title}</span>
|
<span>{group.title}</span>
|
||||||
<ChevronDown className="h-4 w-4" />
|
<ChevronDown className="h-3.5 w-3.5 opacity-70" />
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="start" className="w-80">
|
<DropdownMenuContent align="start" className="w-80">
|
||||||
<DropdownMenuLabel>{group.title}</DropdownMenuLabel>
|
<DropdownMenuLabel className="space-y-1">
|
||||||
|
<div>{group.title}</div>
|
||||||
|
<div className="font-normal text-muted-foreground text-xs">
|
||||||
|
{group.description}
|
||||||
|
</div>
|
||||||
|
</DropdownMenuLabel>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
{group.items.map((item) => {
|
{group.items.map((item) => {
|
||||||
const Icon = item.icon;
|
const Icon = item.icon;
|
||||||
@ -115,14 +126,14 @@ function SettingsQuickAction({
|
|||||||
const Icon = item.icon;
|
const Icon = item.icon;
|
||||||
const className =
|
const className =
|
||||||
item.value === activeTab
|
item.value === activeTab
|
||||||
? "border-primary bg-primary text-primary-foreground hover:bg-primary/90"
|
? "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-border bg-background text-foreground hover:bg-accent hover:text-accent-foreground";
|
: "border-transparent bg-transparent text-muted-foreground hover:border-border/80 hover:bg-background hover:text-foreground";
|
||||||
|
|
||||||
if (item.href) {
|
if (item.href) {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
asChild
|
asChild
|
||||||
className={`h-10 shrink-0 gap-2 rounded-full border px-4 ${className}`}
|
className={`h-9 shrink-0 gap-2 rounded-full border px-3.5 ${className}`}
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
>
|
>
|
||||||
@ -136,7 +147,7 @@ function SettingsQuickAction({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
className={`h-10 shrink-0 gap-2 rounded-full border px-4 ${className}`}
|
className={`h-9 shrink-0 gap-2 rounded-full border px-3.5 ${className}`}
|
||||||
onClick={() => onTabChange(item.value)}
|
onClick={() => onTabChange(item.value)}
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@ -159,114 +170,134 @@ export function AdminSettingsNavigation({
|
|||||||
const ActiveIcon = activeItem?.icon;
|
const ActiveIcon = activeItem?.icon;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 rounded-2xl border bg-background px-5 py-4 shadow-sm">
|
<div className="rounded-2xl border bg-background px-4 py-3 shadow-sm">
|
||||||
<div className="flex flex-col gap-4 xl:flex-row xl:items-start xl:justify-between">
|
<div className="flex flex-col gap-2.5">
|
||||||
<div className="space-y-2">
|
<div className="flex flex-col gap-2 lg:flex-row lg:items-center lg:justify-between">
|
||||||
<div className="font-semibold text-muted-foreground text-xs uppercase tracking-[0.16em]">
|
<div className="min-w-0 space-y-1.5">
|
||||||
Admin Console
|
<div className="flex flex-wrap items-center gap-2 text-[11px] text-muted-foreground uppercase tracking-[0.18em]">
|
||||||
</div>
|
<span className="font-semibold">Admin Console</span>
|
||||||
<div className="flex flex-wrap items-center gap-3">
|
|
||||||
<h1 className="font-semibold text-2xl tracking-tight">
|
|
||||||
系统设置中心
|
|
||||||
</h1>
|
|
||||||
{activeGroup ? (
|
|
||||||
<SettingStatusBadge tone="enabled">{activeGroup.title}</SettingStatusBadge>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
<p className="max-w-4xl text-muted-foreground text-sm leading-6">
|
|
||||||
页面已改成横向工作区:顶部收拢分类入口,正文区域尽量留给配置表单和数据内容。
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Button asChild className="h-10 rounded-full px-4" size="sm" variant="outline">
|
|
||||||
<Link href="/">
|
|
||||||
<ArrowLeft className="h-4 w-4" />
|
|
||||||
返回工作台
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="rounded-2xl border bg-muted/20 p-2">
|
|
||||||
<div className="flex flex-col gap-3 md:hidden">
|
|
||||||
<Select onValueChange={onTabChange} value={activeTab}>
|
|
||||||
<SelectTrigger className="h-11 border-0 bg-background shadow-sm">
|
|
||||||
<SelectValue placeholder="选择设置项" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{ADMIN_SETTINGS_NAV_GROUPS.map((group, groupIndex) => (
|
|
||||||
<SelectGroup key={group.title}>
|
|
||||||
<SelectLabel>{group.title}</SelectLabel>
|
|
||||||
{group.items
|
|
||||||
.filter((item) => !item.href)
|
|
||||||
.map((item) => (
|
|
||||||
<SelectItem key={item.value} value={item.value}>
|
|
||||||
{item.label}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
{groupIndex < ADMIN_SETTINGS_NAV_GROUPS.length - 1 ? (
|
|
||||||
<SelectSeparator />
|
|
||||||
) : null}
|
|
||||||
</SelectGroup>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
|
|
||||||
<div className="flex flex-wrap gap-2">
|
|
||||||
{(activeGroup?.items ?? []).map((item) => (
|
|
||||||
<SettingsQuickAction
|
|
||||||
activeTab={activeTab}
|
|
||||||
item={item}
|
|
||||||
key={item.value}
|
|
||||||
onTabChange={onTabChange}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="hidden items-center gap-2 md:flex">
|
|
||||||
{ADMIN_SETTINGS_NAV_GROUPS.map((group) => (
|
|
||||||
<SettingsGroupDropdown
|
|
||||||
activeTab={activeTab}
|
|
||||||
group={group}
|
|
||||||
key={group.title}
|
|
||||||
onTabChange={onTabChange}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<Separator className="mx-1 h-7" orientation="vertical" />
|
|
||||||
|
|
||||||
<div className="flex min-w-0 flex-1 items-center gap-2 overflow-x-auto pb-1">
|
|
||||||
{(activeGroup?.items ?? []).map((item) => (
|
|
||||||
<SettingsQuickAction
|
|
||||||
activeTab={activeTab}
|
|
||||||
item={item}
|
|
||||||
key={item.value}
|
|
||||||
onTabChange={onTabChange}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{activeItem && ActiveIcon ? (
|
|
||||||
<div className="flex items-start gap-3 rounded-2xl border bg-muted/10 px-4 py-3">
|
|
||||||
<div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-2xl bg-background shadow-sm">
|
|
||||||
<ActiveIcon className="h-5 w-5 text-muted-foreground" />
|
|
||||||
</div>
|
|
||||||
<div className="min-w-0 flex-1">
|
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
|
||||||
<h2 className="font-semibold text-base">{activeItem.label}</h2>
|
|
||||||
{activeGroup ? (
|
{activeGroup ? (
|
||||||
<span className="text-muted-foreground text-xs">
|
<>
|
||||||
{activeGroup.title}
|
<span className="h-1 w-1 rounded-full bg-border" />
|
||||||
</span>
|
<span className="tracking-normal normal-case text-[12px]">
|
||||||
|
{activeGroup.title}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-wrap items-center gap-x-3 gap-y-2">
|
||||||
|
<h1 className="font-semibold text-[26px] leading-none tracking-tight">
|
||||||
|
系统设置中心
|
||||||
|
</h1>
|
||||||
|
{activeItem && ActiveIcon ? (
|
||||||
|
<div className="inline-flex min-w-0 items-center gap-2 rounded-full border border-border/70 bg-muted/10 px-2.5 py-1.5">
|
||||||
|
<div className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-background text-muted-foreground shadow-sm">
|
||||||
|
<ActiveIcon className="h-3.5 w-3.5" />
|
||||||
|
</div>
|
||||||
|
<div className="min-w-0">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="truncate font-medium text-[13px] text-foreground">
|
||||||
|
{activeItem.label}
|
||||||
|
</span>
|
||||||
|
<SettingStatusBadge tone="enabled">当前</SettingStatusBadge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
asChild
|
||||||
|
className="h-8 rounded-full border-border/70 px-3 text-[13px]"
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
<Link href="/">
|
||||||
|
<ArrowLeft className="h-3.5 w-3.5" />
|
||||||
|
返回工作台
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="border-border/70 border-t pt-2.5">
|
||||||
|
<div className="flex flex-col gap-2 md:hidden">
|
||||||
|
<Select onValueChange={onTabChange} value={activeTab}>
|
||||||
|
<SelectTrigger className="h-9 border-border/70 bg-background shadow-none">
|
||||||
|
<SelectValue placeholder="选择设置项" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{ADMIN_SETTINGS_NAV_GROUPS.map((group, groupIndex) => (
|
||||||
|
<SelectGroup key={group.title}>
|
||||||
|
<SelectLabel>{group.title}</SelectLabel>
|
||||||
|
{group.items
|
||||||
|
.filter((item) => !item.href)
|
||||||
|
.map((item) => (
|
||||||
|
<SelectItem key={item.value} value={item.value}>
|
||||||
|
{item.label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
{groupIndex < ADMIN_SETTINGS_NAV_GROUPS.length - 1 ? (
|
||||||
|
<SelectSeparator />
|
||||||
|
) : null}
|
||||||
|
</SelectGroup>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
|
||||||
|
{activeItem ? (
|
||||||
|
<p className="truncate text-muted-foreground text-xs">
|
||||||
|
{activeItem.description}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<div className="flex flex-wrap gap-1">
|
||||||
|
{(activeGroup?.items ?? []).map((item) => (
|
||||||
|
<SettingsQuickAction
|
||||||
|
activeTab={activeTab}
|
||||||
|
item={item}
|
||||||
|
key={item.value}
|
||||||
|
onTabChange={onTabChange}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="hidden gap-3 md:flex md:items-start">
|
||||||
|
<div className="flex shrink-0 items-center gap-2">
|
||||||
|
{ADMIN_SETTINGS_NAV_GROUPS.map((group) => (
|
||||||
|
<SettingsGroupDropdown
|
||||||
|
activeTab={activeTab}
|
||||||
|
group={group}
|
||||||
|
key={group.title}
|
||||||
|
onTabChange={onTabChange}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Separator className="mt-1 h-6" orientation="vertical" />
|
||||||
|
|
||||||
|
<div className="min-w-0 flex-1">
|
||||||
|
<div className="flex min-w-0 items-center gap-1 overflow-x-auto pb-1">
|
||||||
|
{(activeGroup?.items ?? []).map((item) => (
|
||||||
|
<SettingsQuickAction
|
||||||
|
activeTab={activeTab}
|
||||||
|
item={item}
|
||||||
|
key={item.value}
|
||||||
|
onTabChange={onTabChange}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
{activeItem ? (
|
||||||
|
<p className="truncate px-1 pt-1 text-muted-foreground text-xs">
|
||||||
|
{activeItem.description}
|
||||||
|
</p>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-1 text-muted-foreground text-sm">
|
|
||||||
{activeItem.description}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user