feat(web): refine operator navigation IA
All checks were successful
CD Pipeline / tests (push) Successful in 1m26s
Code Review / ai-code-review (push) Successful in 13s
CD Pipeline / build-and-deploy (push) Successful in 4m10s
CD Pipeline / post-deploy-checks (push) Successful in 1m59s

This commit is contained in:
Your Name
2026-06-04 14:49:42 +08:00
parent 02cadee63e
commit 973fc7a455
7 changed files with 493 additions and 75 deletions

View File

@@ -8,8 +8,9 @@
import { AppLayout } from "@/components/layout";
import { Link, usePathname } from "@/i18n/routing";
import { Activity, BrainCircuit, Building2, ClipboardList, FileText, ShieldCheck } from "lucide-react";
import { Activity, BrainCircuit, Building2, ClipboardList, FileText, LayoutDashboard, ShieldCheck } from "lucide-react";
import { cn } from "@/lib/utils";
import { useTranslations } from "next-intl";
// =============================================================================
// 導航設定
@@ -17,30 +18,36 @@ import { cn } from "@/lib/utils";
const navItems = [
{
label: "工作鏈路",
labelKey: "overview",
href: "/awooop" as const,
icon: LayoutDashboard,
exact: true,
},
{
labelKey: "workItems",
href: "/awooop/work-items" as const,
icon: ClipboardList,
},
{
label: "租戶管理",
href: "/awooop/tenants" as const,
icon: Building2,
},
{
label: "合約儀表板",
href: "/awooop/contracts" as const,
icon: FileText,
},
{
label: "執行監控",
labelKey: "runs",
href: "/awooop/runs" as const,
icon: Activity,
},
{
label: "審批佇列",
labelKey: "approvals",
href: "/awooop/approvals" as const,
icon: ShieldCheck,
},
{
labelKey: "contracts",
href: "/awooop/contracts" as const,
icon: FileText,
},
{
labelKey: "tenants",
href: "/awooop/tenants" as const,
icon: Building2,
},
];
// =============================================================================
@@ -55,6 +62,7 @@ export default function AwoooPLayout({
params: { locale: string };
}) {
const pathname = usePathname();
const t = useTranslations("awooop.shell");
return (
<AppLayout locale={params.locale} showBackground={false}>
@@ -67,31 +75,31 @@ export default function AwoooPLayout({
</span>
<div>
<h1 className="text-lg font-semibold tracking-normal text-[#141413]">
AwoooP
{t("title")}
</h1>
<div className="mt-1 flex items-center gap-2 text-xs text-[#77736a]">
<span></span>
<span>{t("subtitle")}</span>
<span className="h-1 w-1 rounded-full bg-[#d97757]" />
<span></span>
<span>{t("mode")}</span>
</div>
</div>
</div>
<span className="inline-flex items-center gap-2 border border-[#d8d3c7] bg-white px-3 py-1.5 text-xs font-semibold text-[#141413]">
<span className="h-1.5 w-1.5 rounded-full bg-[#22c55e]" />
{t("operator")}
</span>
</div>
<nav
className="mt-4 flex flex-wrap gap-1"
className="mt-4 flex max-w-full gap-1 overflow-x-auto pb-1"
role="navigation"
aria-label="AwoooP 主要導航"
aria-label={t("navLabel")}
>
{navItems.map((item) => {
const Icon = item.icon;
const isActive =
pathname === item.href ||
pathname?.startsWith(item.href + "/");
(!item.exact && pathname?.startsWith(item.href + "/"));
return (
<Link
@@ -99,14 +107,14 @@ export default function AwoooPLayout({
href={item.href}
aria-current={isActive ? "page" : undefined}
className={cn(
"inline-flex items-center gap-2 border px-3 py-2 text-sm font-medium transition-colors",
"inline-flex shrink-0 items-center gap-2 border px-3 py-2 text-sm font-medium transition-colors",
isActive
? "border-[#d97757] bg-white text-[#141413]"
: "border-transparent text-[#77736a] hover:border-[#d8d3c7] hover:bg-white hover:text-[#141413]"
)}
>
<Icon className="h-4 w-4" aria-hidden="true" />
{item.label}
<span>{t(`nav.${item.labelKey}`)}</span>
</Link>
);
})}