返回模板目录
dashboardkokonutui-dashboard

KokonutUI 财务面板

现代财务管理仪表盘,包含账户概览、交易记录和事件追踪。

独立打开

完整模板源代码

查看 1254 行代码
"use client";

export const dynamic = "force-static";

import { useState } from "react";
import { TemplateBackLink } from "@/components/templates/TemplateBackLink";
import {
  Home,
  BarChart2,
  Building2,
  Folder,
  Wallet,
  Receipt,
  CreditCard,
  Users2,
  Shield,
  MessagesSquare,
  Video,
  Settings,
  HelpCircle,
  Menu,
  Bell,
  ChevronRight,
  ArrowUpRight,
  ArrowDownLeft,
  SendHorizontal,
  QrCode,
  Plus,
  ArrowRight,
  ShoppingCart,
  PiggyBank,
  TrendingUp,
  Calendar,
  CheckCircle2,
  Timer,
  AlertCircle,
} from "@/components/IconShim";

/* ── Sidebar Data ── */
const sidebarSections = [
  {
    title: "Overview",
    items: [
      { icon: Home, label: "Dashboard" },
      { icon: BarChart2, label: "Analytics" },
      { icon: Building2, label: "Organization" },
      { icon: Folder, label: "Projects" },
    ],
  },
  {
    title: "Finance",
    items: [
      { icon: Wallet, label: "Transactions" },
      { icon: Receipt, label: "Invoices" },
      { icon: CreditCard, label: "Payments" },
    ],
  },
  {
    title: "Team",
    items: [
      { icon: Users2, label: "Members" },
      { icon: Shield, label: "Permissions" },
      { icon: MessagesSquare, label: "Chat" },
      { icon: Video, label: "Meetings" },
    ],
  },
];

type KokonutPage =
  | "Dashboard"
  | "Analytics"
  | "Organization"
  | "Projects"
  | "Transactions"
  | "Invoices"
  | "Payments"
  | "Members"
  | "Permissions"
  | "Chat"
  | "Meetings"
  | "Settings"
  | "Help";

const kokonutPageSummary: Record<KokonutPage, string> = {
  Dashboard:
    "Overview of account balances, recent activity, and upcoming financial milestones.",
  Analytics:
    "Track trends, category performance, and month-over-month financial movement.",
  Organization: "Manage teams, roles, and workspace-level finance permissions.",
  Projects: "Review budget usage and expense allocation by active projects.",
  Transactions: "Inspect transaction streams, statuses, and reconciliations.",
  Invoices: "Review outgoing invoices, payment status, and due schedules.",
  Payments: "Manage payment methods, payout flows, and transfer history.",
  Members: "View team members, access levels, and finance responsibilities.",
  Permissions: "Control security policy and role-based permission settings.",
  Chat: "Collaborate with team members on approvals and financial decisions.",
  Meetings: "Track upcoming finance reviews and planning sessions.",
  Settings:
    "Configure workspace preferences, billing settings, and notifications.",
  Help: "Get support resources, troubleshooting steps, and contact channels.",
};

/* ── Accounts (List01) ── */
const initialAccounts = [
  {
    id: "1",
    title: "Main Savings",
    description: "Personal savings",
    balance: 8459.45,
    type: "savings" as const,
  },
  {
    id: "2",
    title: "Checking Account",
    description: "Daily expenses",
    balance: 2850,
    type: "checking" as const,
  },
  {
    id: "3",
    title: "Investment Portfolio",
    description: "Stock & ETFs",
    balance: 15230.8,
    type: "investment" as const,
  },
  {
    id: "4",
    title: "Credit Card",
    description: "Pending charges",
    balance: 1200,
    type: "debt" as const,
  },
  {
    id: "5",
    title: "Savings Account",
    description: "Emergency fund",
    balance: 3000,
    type: "savings" as const,
  },
];

/* ── Transactions (List02) ── */
const initialTransactions = [
  {
    id: "1",
    title: "Apple Store Purchase",
    amount: 999,
    type: "outgoing" as const,
    icon: ShoppingCart,
    timestamp: "Today, 2:45 PM",
    status: "completed",
  },
  {
    id: "2",
    title: "Salary Deposit",
    amount: 4500,
    type: "incoming" as const,
    icon: Wallet,
    timestamp: "Today, 9:00 AM",
    status: "completed",
  },
  {
    id: "3",
    title: "Netflix Subscription",
    amount: 15.99,
    type: "outgoing" as const,
    icon: CreditCard,
    timestamp: "Yesterday",
    status: "pending",
  },
  {
    id: "4",
    title: "Apple Store Purchase",
    amount: 999,
    type: "outgoing" as const,
    icon: ShoppingCart,
    timestamp: "Today, 2:45 PM",
    status: "completed",
  },
  {
    id: "5",
    title: "Supabase Subscription",
    amount: 15.99,
    type: "outgoing" as const,
    icon: CreditCard,
    timestamp: "Yesterday",
    status: "pending",
  },
  {
    id: "6",
    title: "Vercel Subscription",
    amount: 15.99,
    type: "outgoing" as const,
    icon: CreditCard,
    timestamp: "Yesterday",
    status: "pending",
  },
];

/* ── Events (List03) ── */
const events = [
  {
    id: "1",
    title: "Emergency Fund",
    subtitle: "3 months of expenses saved",
    icon: PiggyBank,
    date: "Target: Dec 2024",
    amount: "$15,000",
    status: "in-progress" as const,
    progress: 65,
  },
  {
    id: "2",
    title: "Stock Portfolio",
    subtitle: "Tech sector investment plan",
    icon: TrendingUp,
    date: "Target: Jun 2024",
    amount: "$50,000",
    status: "pending" as const,
    progress: 30,
  },
  {
    id: "3",
    title: "Debt Repayment",
    subtitle: "Student loan payoff plan",
    icon: CreditCard,
    date: "Target: Mar 2025",
    amount: "$25,000",
    status: "in-progress" as const,
    progress: 45,
  },
];

/* ── Profile ── */
const statusConfig = {
  pending: {
    icon: Timer,
    class: "text-amber-600 dark:text-amber-400",
    bg: "bg-amber-100 dark:bg-amber-900/30",
  },
  "in-progress": {
    icon: AlertCircle,
    class: "text-blue-600 dark:text-blue-400",
    bg: "bg-blue-100 dark:bg-blue-900/30",
  },
  completed: {
    icon: CheckCircle2,
    class: "text-emerald-600 dark:text-emerald-400",
    bg: "bg-emerald-100 dark:bg-emerald-900/30",
  },
};

function accountIconColor(type: string) {
  if (type === "savings") return "bg-emerald-100 dark:bg-emerald-900/30";
  if (type === "checking") return "bg-blue-100 dark:bg-blue-900/30";
  if (type === "investment") return "bg-purple-100 dark:bg-purple-900/30";
  return "bg-red-100 dark:bg-red-900/30";
}

function AccountIcon({ type }: { type: string }) {
  if (type === "savings")
    return (
      <Wallet className="w-3.5 h-3.5 text-emerald-600 dark:text-emerald-400" />
    );
  if (type === "checking")
    return <QrCode className="w-3.5 h-3.5 text-blue-600 dark:text-blue-400" />;
  if (type === "investment")
    return (
      <ArrowUpRight className="w-3.5 h-3.5 text-purple-600 dark:text-purple-400" />
    );
  return <CreditCard className="w-3.5 h-3.5 text-red-600 dark:text-red-400" />;
}

const currency = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
});

type AccountAction = "add" | "send" | "top-up";

export default function KokonutuiDashboardPage() {
  const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
  const [profileOpen, setProfileOpen] = useState(false);
  const [notificationsOpen, setNotificationsOpen] = useState(false);
  const [notificationsRead, setNotificationsRead] = useState(false);
  const [activePage, setActivePage] = useState<KokonutPage>("Dashboard");
  const [accountList, setAccountList] = useState(initialAccounts);
  const [transactionList, setTransactionList] = useState(initialTransactions);
  const [accountAction, setAccountAction] = useState<AccountAction | null>(
    null,
  );
  const [actionTitle, setActionTitle] = useState("");
  const [actionAmount, setActionAmount] = useState("");
  const [actionResult, setActionResult] = useState("");
  const [selectedEvent, setSelectedEvent] = useState<
    (typeof events)[number] | null
  >(null);

  const totalBalance = accountList.reduce(
    (total, account) =>
      total + (account.type === "debt" ? -account.balance : account.balance),
    0,
  );

  const openAccountAction = (action: AccountAction) => {
    setAccountAction(action);
    setActionTitle("");
    setActionAmount("");
    setActionResult("");
  };

  const submitAccountAction = () => {
    const amount = Number(actionAmount);
    if (
      !accountAction ||
      !actionTitle.trim() ||
      !Number.isFinite(amount) ||
      amount <= 0
    ) {
      setActionResult("Enter a name and an amount greater than zero.");
      return;
    }

    if (accountAction === "add") {
      setAccountList((current) => [
        ...current,
        {
          id: crypto.randomUUID(),
          title: actionTitle.trim(),
          description: "New account",
          balance: amount,
          type: "checking" as const,
        },
      ]);
      setActionResult(
        `${actionTitle.trim()} was added with ${currency.format(amount)}.`,
      );
      return;
    }

    if (accountAction === "send" && amount > accountList[0].balance) {
      setActionResult("The transfer is larger than the Main Savings balance.");
      return;
    }

    const isTopUp = accountAction === "top-up";
    setAccountList((current) =>
      current.map((account, index) =>
        index === 0
          ? {
              ...account,
              balance: account.balance + (isTopUp ? amount : -amount),
            }
          : account,
      ),
    );
    setTransactionList((current) => [
      {
        id: crypto.randomUUID(),
        title: actionTitle.trim(),
        amount,
        type: isTopUp ? ("incoming" as const) : ("outgoing" as const),
        icon: isTopUp ? Wallet : SendHorizontal,
        timestamp: "Just now",
        status: "completed",
      },
      ...current,
    ]);
    setActionResult(
      `${isTopUp ? "Top-up" : "Transfer"} of ${currency.format(amount)} was applied.`,
    );
  };

  const renderModuleContent = () => {
    switch (activePage) {
      case "Analytics":
        return (
          <div className="space-y-6">
            <section className="grid grid-cols-1 md:grid-cols-3 gap-4">
              {[
                { label: "Net Cash Flow", value: "+$12,430", trend: "+8.4%" },
                { label: "Expense Ratio", value: "34.2%", trend: "-2.1%" },
                { label: "Forecast Accuracy", value: "92.6%", trend: "+1.7%" },
              ].map((item) => (
                <div
                  key={item.label}
                  className="rounded-xl border border-gray-200 dark:border-[#1F1F23] bg-white dark:bg-zinc-900/70 p-4"
                >
                  <p className="text-xs text-zinc-500 dark:text-zinc-400">
                    {item.label}
                  </p>
                  <p className="mt-1 text-2xl font-semibold text-zinc-900 dark:text-zinc-100">
                    {item.value}
                  </p>
                  <p className="mt-1 text-sm text-emerald-600 dark:text-emerald-400">
                    {item.trend}
                  </p>
                </div>
              ))}
            </section>
            <section className="rounded-xl border border-gray-200 dark:border-[#1F1F23] bg-white dark:bg-zinc-900/70 p-6">
              <h3 className="text-base font-semibold text-zinc-900 dark:text-zinc-100">
                Cash Flow Trend
              </h3>
              <div className="mt-4 flex items-end gap-2 h-44">
                {[38, 55, 62, 49, 71, 66, 82, 76, 88, 73, 92, 85].map(
                  (h, index) => (
                    <div
                      key={index}
                      className="flex-1 rounded-t-md bg-zinc-900/70 dark:bg-zinc-100/80"
                      style={{ height: `${h}%` }}
                    />
                  ),
                )}
              </div>
            </section>
          </div>
        );
      case "Organization":
        return (
          <div className="space-y-6">
            <section className="rounded-xl border border-gray-200 dark:border-[#1F1F23] bg-white dark:bg-zinc-900/70 p-6">
              <h3 className="text-base font-semibold text-zinc-900 dark:text-zinc-100">
                Department Ownership
              </h3>
              <div className="mt-4 grid grid-cols-1 md:grid-cols-2 gap-4">
                {[
                  {
                    team: "Finance Ops",
                    owner: "Eugene An",
                    members: 8,
                    budget: "$320K",
                  },
                  {
                    team: "Revenue Team",
                    owner: "Maria Garcia",
                    members: 12,
                    budget: "$540K",
                  },
                  {
                    team: "Compliance",
                    owner: "Alex Smith",
                    members: 5,
                    budget: "$180K",
                  },
                  {
                    team: "Procurement",
                    owner: "Luna Park",
                    members: 7,
                    budget: "$210K",
                  },
                ].map((team) => (
                  <div
                    key={team.team}
                    className="rounded-lg border border-zinc-200 dark:border-zinc-800 p-4"
                  >
                    <p className="text-sm font-semibold text-zinc-900 dark:text-zinc-100">
                      {team.team}
                    </p>
                    <p className="mt-1 text-xs text-zinc-500 dark:text-zinc-400">
                      Owner: {team.owner}
                    </p>
                    <p className="mt-1 text-xs text-zinc-500 dark:text-zinc-400">
                      Members: {team.members}
                    </p>
                    <p className="mt-2 text-sm text-zinc-700 dark:text-zinc-300">
                      Budget: {team.budget}
                    </p>
                  </div>
                ))}
              </div>
            </section>
            <section className="rounded-xl border border-gray-200 dark:border-[#1F1F23] bg-white dark:bg-zinc-900/70 p-6">
              <h3 className="text-base font-semibold text-zinc-900 dark:text-zinc-100">
                Organization Notes
              </h3>
              <p className="mt-2 text-sm text-zinc-600 dark:text-zinc-400">
                Centralized ownership is enabled for all finance workflows. Role
                inheritance applies from department leads.
              </p>
            </section>
          </div>
        );
      case "Projects":
        return (
          <div className="space-y-6">
            <section className="grid grid-cols-1 md:grid-cols-2 gap-4">
              {[
                { name: "ERP Migration", budget: "$420K", spent: 74 },
                { name: "AP Automation", budget: "$180K", spent: 48 },
                { name: "Risk Dashboard", budget: "$95K", spent: 63 },
                { name: "Vendor Portal", budget: "$150K", spent: 29 },
              ].map((project) => (
                <div
                  key={project.name}
                  className="rounded-xl border border-gray-200 dark:border-[#1F1F23] bg-white dark:bg-zinc-900/70 p-5"
                >
                  <p className="text-sm font-semibold text-zinc-900 dark:text-zinc-100">
                    {project.name}
                  </p>
                  <p className="mt-1 text-xs text-zinc-500 dark:text-zinc-400">
                    Budget {project.budget}
                  </p>
                  <div className="mt-4 h-2 bg-zinc-200 dark:bg-zinc-800 rounded-full overflow-hidden">
                    <div
                      className="h-full bg-zinc-900 dark:bg-zinc-100 rounded-full"
                      style={{ width: `${project.spent}%` }}
                    />
                  </div>
                  <p className="mt-2 text-xs text-zinc-600 dark:text-zinc-400">
                    {project.spent}% utilized
                  </p>
                </div>
              ))}
            </section>
          </div>
        );
      case "Transactions":
      case "Invoices":
      case "Payments":
        return (
          <div className="space-y-6">
            <section className="rounded-xl border border-gray-200 dark:border-[#1F1F23] bg-white dark:bg-zinc-900/70 p-6">
              <h3 className="text-base font-semibold text-zinc-900 dark:text-zinc-100">
                {activePage} Center
              </h3>
              <div className="mt-4 space-y-3">
                {transactionList.map((tx) => (
                  <div
                    key={tx.id}
                    className="flex items-center justify-between rounded-lg border border-zinc-200 dark:border-zinc-800 p-3"
                  >
                    <div>
                      <p className="text-sm font-medium text-zinc-900 dark:text-zinc-100">
                        {tx.title}
                      </p>
                      <p className="text-xs text-zinc-500 dark:text-zinc-400">
                        {tx.timestamp}
                      </p>
                    </div>
                    <div className="text-right">
                      <p className="text-sm text-zinc-700 dark:text-zinc-300">
                        {tx.amount}
                      </p>
                      <p className="text-xs text-zinc-500 dark:text-zinc-400">
                        {tx.status}
                      </p>
                    </div>
                  </div>
                ))}
              </div>
            </section>
          </div>
        );
      case "Members":
      case "Permissions":
      case "Chat":
      case "Meetings":
        return (
          <div className="space-y-6">
            <section className="rounded-xl border border-gray-200 dark:border-[#1F1F23] bg-white dark:bg-zinc-900/70 p-6">
              <h3 className="text-base font-semibold text-zinc-900 dark:text-zinc-100">
                {activePage} Workspace
              </h3>
              <div className="mt-4 grid grid-cols-1 md:grid-cols-2 gap-4">
                {[
                  { name: "Alex Smith", role: "Finance Lead" },
                  { name: "Maria Garcia", role: "Ops Manager" },
                  { name: "John Doe", role: "Analyst" },
                  { name: "Luna Park", role: "Coordinator" },
                ].map((member) => (
                  <div
                    key={member.name}
                    className="rounded-lg border border-zinc-200 dark:border-zinc-800 p-4"
                  >
                    <p className="text-sm font-medium text-zinc-900 dark:text-zinc-100">
                      {member.name}
                    </p>
                    <p className="mt-1 text-xs text-zinc-500 dark:text-zinc-400">
                      {member.role}
                    </p>
                  </div>
                ))}
              </div>
            </section>
          </div>
        );
      case "Settings":
        return (
          <div className="space-y-6">
            <section className="rounded-xl border border-gray-200 dark:border-[#1F1F23] bg-white dark:bg-zinc-900/70 p-6">
              <h3 className="text-base font-semibold text-zinc-900 dark:text-zinc-100">
                Preference Controls
              </h3>
              <div className="mt-4 space-y-4">
                {[
                  "Enable payout notifications",
                  "Require double approval for transfers",
                  "Weekly analytics digest by email",
                ].map((item) => (
                  <label
                    key={item}
                    className="flex items-center justify-between rounded-lg border border-zinc-200 dark:border-zinc-800 p-3"
                  >
                    <span className="text-sm text-zinc-700 dark:text-zinc-300">
                      {item}
                    </span>
                    <input type="checkbox" className="h-4 w-4" defaultChecked />
                  </label>
                ))}
              </div>
            </section>
          </div>
        );
      case "Help":
        return (
          <div className="space-y-6">
            <section className="rounded-xl border border-gray-200 dark:border-[#1F1F23] bg-white dark:bg-zinc-900/70 p-6">
              <h3 className="text-base font-semibold text-zinc-900 dark:text-zinc-100">
                Support Center
              </h3>
              <div className="mt-4 space-y-3">
                {[
                  "How to reconcile multi-account transactions?",
                  "How to set role-based approval flows?",
                  "How to export monthly finance audits?",
                ].map((question) => (
                  <div
                    key={question}
                    className="rounded-lg border border-zinc-200 dark:border-zinc-800 p-4"
                  >
                    <p className="text-sm text-zinc-700 dark:text-zinc-300">
                      {question}
                    </p>
                  </div>
                ))}
              </div>
            </section>
          </div>
        );
      default:
        return (
          <section className="rounded-xl border border-gray-200 dark:border-[#1F1F23] bg-white dark:bg-zinc-900/70 p-6">
            <p className="text-sm text-zinc-600 dark:text-zinc-400">
              {kokonutPageSummary[activePage]}
            </p>
          </section>
        );
    }
  };

  return (
    <>
      <TemplateBackLink />
      <div className="min-h-screen bg-white dark:bg-[#0F0F12]">
        {/* Mobile menu button */}
        <button
          type="button"
          aria-controls="kokonut-navigation"
          aria-expanded={mobileMenuOpen}
          aria-label={mobileMenuOpen ? "Close navigation" : "Open navigation"}
          className="lg:hidden fixed top-4 left-4 z-[70] p-2 rounded-lg bg-white dark:bg-[#0F0F12] shadow-md"
          onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
        >
          <Menu className="h-5 w-5 text-gray-600 dark:text-gray-300" />
        </button>

        {/* Sidebar */}
        <nav
          id="kokonut-navigation"
          aria-label="Primary navigation"
          className={`fixed inset-y-0 left-0 z-[70] w-64 bg-white dark:bg-[#0F0F12] border border-gray-200 dark:border-[#1F1F23] ${mobileMenuOpen ? "flex" : "hidden"} lg:flex`}
        >
          <div className="h-full flex flex-col">
            <div className="h-16 px-6 flex items-center border border-gray-200 dark:border-[#1F1F23]">
              <div className="flex items-center gap-3">
                <div className="w-8 h-8 rounded-lg bg-zinc-900 dark:bg-zinc-100 flex items-center justify-center text-white dark:text-zinc-900 text-sm font-bold">
                  K
                </div>
                <span className="text-lg font-semibold text-gray-900 dark:text-white">
                  KokonutUI
                </span>
              </div>
            </div>
            <div className="flex-1 overflow-y-auto py-4 px-4">
              <div className="space-y-6">
                {sidebarSections.map((section) => (
                  <div key={section.title}>
                    <div className="px-3 mb-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400">
                      {section.title}
                    </div>
                    <div className="space-y-1">
                      {section.items.map((item) => (
                        <button
                          key={item.label}
                          type="button"
                          onClick={() => {
                            setActivePage(item.label as KokonutPage);
                            setMobileMenuOpen(false);
                          }}
                          className={`w-full flex items-center px-3 py-2 text-sm rounded-md transition-colors ${
                            activePage === item.label
                              ? "bg-zinc-900 text-white dark:bg-zinc-50 dark:text-zinc-900"
                              : "text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-[#1F1F23]"
                          }`}
                        >
                          <item.icon className="h-4 w-4 mr-3 flex-shrink-0" />
                          {item.label}
                        </button>
                      ))}
                    </div>
                  </div>
                ))}
              </div>
            </div>
            <div className="px-4 py-4 border border-gray-200 dark:border-[#1F1F23]">
              <div className="space-y-1">
                <button
                  type="button"
                  onClick={() => {
                    setActivePage("Settings");
                    setMobileMenuOpen(false);
                  }}
                  className={`w-full flex items-center px-3 py-2 text-sm rounded-md transition-colors ${
                    activePage === "Settings"
                      ? "bg-zinc-900 text-white dark:bg-zinc-50 dark:text-zinc-900"
                      : "text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-[#1F1F23]"
                  }`}
                >
                  <Settings className="h-4 w-4 mr-3" />
                  Settings
                </button>
                <button
                  type="button"
                  onClick={() => {
                    setActivePage("Help");
                    setMobileMenuOpen(false);
                  }}
                  className={`w-full flex items-center px-3 py-2 text-sm rounded-md transition-colors ${
                    activePage === "Help"
                      ? "bg-zinc-900 text-white dark:bg-zinc-50 dark:text-zinc-900"
                      : "text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-50 dark:hover:bg-[#1F1F23]"
                  }`}
                >
                  <HelpCircle className="h-4 w-4 mr-3" />
                  Help
                </button>
              </div>
            </div>
          </div>
        </nav>

        {/* Mobile overlay */}
        {mobileMenuOpen && (
          <button
            type="button"
            aria-label="Close navigation"
            className="fixed inset-0 bg-black/50 z-[65] lg:hidden"
            onClick={() => setMobileMenuOpen(false)}
          />
        )}
        {/* Main content area */}
        <div className="flex min-h-screen min-w-0 flex-col lg:ml-64">
          {/* Top Nav */}
          <header className="h-16 border border-gray-200 dark:border-[#1F1F23]">
            <nav className="px-3 sm:px-6 flex items-center justify-between bg-white dark:bg-[#0F0F12] h-full">
              <div className="font-medium text-sm hidden sm:flex items-center space-x-1">
                <span className="text-gray-700 dark:text-gray-300">
                  kokonutUI
                </span>
                <ChevronRight className="h-4 w-4 text-gray-500 dark:text-gray-400 mx-1" />
                <span className="text-gray-900 dark:text-gray-100">
                  {activePage}
                </span>
              </div>
              <div className="flex items-center gap-2 sm:gap-4 ml-auto sm:ml-0">
                <div className="relative">
                  <button
                    type="button"
                    aria-label="Notifications"
                    aria-expanded={notificationsOpen}
                    onClick={() => {
                      setNotificationsOpen((open) => !open);
                      setProfileOpen(false);
                    }}
                    className="relative p-1.5 sm:p-2 hover:bg-gray-100 dark:hover:bg-[#1F1F23] rounded-full transition-colors"
                  >
                    <Bell className="h-4 w-4 sm:h-5 sm:w-5 text-gray-600 dark:text-gray-300" />
                    {!notificationsRead && (
                      <span className="absolute right-1 top-1 h-2 w-2 rounded-full bg-red-500" />
                    )}
                  </button>
                  {notificationsOpen && (
                    <div className="absolute right-0 top-11 z-50 w-[min(20rem,calc(100vw-2rem))] rounded-2xl border border-zinc-200 bg-white p-4 shadow-lg dark:border-zinc-800 dark:bg-zinc-900">
                      <div className="flex items-center justify-between gap-4">
                        <h2 className="text-sm font-semibold text-zinc-900 dark:text-zinc-100">
                          Notifications
                        </h2>
                        <button
                          type="button"
                          onClick={() => setNotificationsRead(true)}
                          className="rounded-lg px-2 py-1 text-xs font-medium text-zinc-600 hover:bg-zinc-100 hover:text-zinc-900 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-zinc-100"
                        >
                          Mark all read
                        </button>
                      </div>
                      <div className="mt-3 space-y-2">
                        {[
                          "Invoice AC-204 was paid.",
                          "Monthly finance review starts tomorrow.",
                          "The ERP Migration budget reached 74%.",
                        ].map((notification) => (
                          <p
                            key={notification}
                            className={`rounded-lg p-3 text-xs ${notificationsRead ? "bg-zinc-50 text-zinc-500 dark:bg-zinc-800/50 dark:text-zinc-400" : "bg-zinc-100 text-zinc-800 dark:bg-zinc-800 dark:text-zinc-200"}`}
                          >
                            {notification}
                          </p>
                        ))}
                      </div>
                    </div>
                  )}
                </div>
                {/* Profile dropdown */}
                <div className="relative">
                  <button
                    type="button"
                    onClick={() => {
                      setProfileOpen(!profileOpen);
                      setNotificationsOpen(false);
                    }}
                    className="w-8 h-8 rounded-full bg-zinc-200 dark:bg-zinc-700 flex items-center justify-center text-sm font-medium text-zinc-700 dark:text-zinc-200 ring-2 ring-gray-200 dark:ring-[#2B2B30]"
                  >
                    EA
                  </button>
                  {profileOpen && (
                    <>
                      <button
                        type="button"
                        aria-label="Close profile"
                        className="fixed inset-0 z-40"
                        onClick={() => setProfileOpen(false)}
                      />
                      <div className="absolute right-0 top-10 z-50 w-72 sm:w-80 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-2xl shadow-lg overflow-hidden">
                        <div className="px-6 pt-8 pb-6">
                          <div className="flex items-center gap-4 mb-6">
                            <div className="relative shrink-0">
                              <div className="w-16 h-16 rounded-full bg-zinc-200 dark:bg-zinc-700 flex items-center justify-center text-xl font-semibold text-zinc-700 dark:text-zinc-200 ring-4 ring-white dark:ring-zinc-900">
                                EA
                              </div>
                              <div className="absolute bottom-0 right-0 w-4 h-4 rounded-full bg-emerald-500 ring-2 ring-white dark:ring-zinc-900" />
                            </div>
                            <div className="flex-1">
                              <h2 className="text-xl font-semibold text-zinc-900 dark:text-zinc-100">
                                Eugene An
                              </h2>
                              <p className="text-zinc-600 dark:text-zinc-400">
                                Prompt Engineer
                              </p>
                            </div>
                          </div>
                          <button
                            type="button"
                            onClick={() => {
                              setActivePage("Settings");
                              setProfileOpen(false);
                            }}
                            className="mt-2 flex w-full items-center gap-2 rounded-lg p-3 text-sm font-medium text-zinc-900 hover:bg-zinc-100 dark:text-zinc-100 dark:hover:bg-zinc-800"
                          >
                            <Settings className="h-4 w-4" />
                            Settings
                          </button>
                        </div>
                      </div>
                    </>
                  )}
                </div>
              </div>
            </nav>
          </header>
          {/* Dashboard Content */}
          <main className="flex-1 overflow-auto bg-white p-4 dark:bg-[#0F0F12] sm:p-6">
            {activePage === "Dashboard" ? (
              <div className="space-y-4">
                <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
                  {/* Accounts (List01) */}
                  <div className="bg-white dark:bg-[#0F0F12] rounded-xl p-6 flex flex-col border border-gray-200 dark:border-[#1F1F23]">
                    <h2 className="text-lg font-bold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
                      <Wallet className="w-3.5 h-3.5 text-zinc-900 dark:text-zinc-50" />
                      Accounts
                    </h2>
                    <div className="flex-1 bg-white dark:bg-zinc-900/70 border border-zinc-100 dark:border-zinc-800 rounded-xl shadow-sm backdrop-blur-xl">
                      <div className="p-4 border border-zinc-100 dark:border-zinc-800">
                        <p className="text-xs text-zinc-600 dark:text-zinc-400">
                          Total Balance
                        </p>
                        <h3 className="text-2xl font-semibold text-zinc-900 dark:text-zinc-50">
                          {currency.format(totalBalance)}
                        </h3>
                      </div>
                      <div className="p-3">
                        <div className="flex items-center justify-between mb-2">
                          <h4 className="text-xs font-medium text-zinc-900 dark:text-zinc-100">
                            Your Accounts
                          </h4>
                        </div>
                        <div className="space-y-1">
                          {accountList.map((account) => (
                            <div
                              key={account.id}
                              className="group flex items-center justify-between p-2 rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-800/50 transition-all duration-200"
                            >
                              <div className="flex min-w-0 items-center gap-2">
                                <div
                                  className={`p-1.5 rounded-lg ${accountIconColor(account.type)}`}
                                >
                                  <AccountIcon type={account.type} />
                                </div>
                                <div className="min-w-0">
                                  <h5 className="truncate text-xs font-medium text-zinc-900 dark:text-zinc-100">
                                    {account.title}
                                  </h5>
                                  <p className="truncate text-[11px] text-zinc-600 dark:text-zinc-400">
                                    {account.description}
                                  </p>
                                </div>
                              </div>
                              <span className="shrink-0 pl-2 text-xs font-medium text-zinc-900 dark:text-zinc-100">
                                {currency.format(account.balance)}
                              </span>
                            </div>
                          ))}
                        </div>
                      </div>
                      <div className="p-2 border border-zinc-100 dark:border-zinc-800">
                        <div className="grid grid-cols-3 gap-2">
                          {[
                            {
                              icon: Plus,
                              label: "Add",
                              action: "add" as const,
                            },
                            {
                              icon: SendHorizontal,
                              label: "Send",
                              action: "send" as const,
                            },
                            {
                              icon: ArrowDownLeft,
                              label: "Top-up",
                              action: "top-up" as const,
                            },
                          ].map((btn) => (
                            <button
                              key={btn.label}
                              type="button"
                              aria-pressed={accountAction === btn.action}
                              onClick={() => openAccountAction(btn.action)}
                              className="flex items-center justify-center gap-2 py-2 px-3 rounded-lg text-xs font-medium bg-zinc-900 dark:bg-zinc-50 text-zinc-50 dark:text-zinc-900 hover:bg-zinc-800 dark:hover:bg-zinc-200 shadow-sm transition-all duration-200"
                            >
                              <btn.icon className="w-3.5 h-3.5" />
                              <span>{btn.label}</span>
                            </button>
                          ))}
                        </div>
                        {accountAction && (
                          <form
                            className="mt-3 rounded-xl bg-zinc-100 p-3 dark:bg-zinc-800"
                            onSubmit={(event) => {
                              event.preventDefault();
                              submitAccountAction();
                            }}
                          >
                            <div className="grid gap-3 sm:grid-cols-2">
                              <label className="text-xs font-medium text-zinc-700 dark:text-zinc-300">
                                {accountAction === "add"
                                  ? "Account name"
                                  : "Recipient or note"}
                                <input
                                  value={actionTitle}
                                  onChange={(event) =>
                                    setActionTitle(event.target.value)
                                  }
                                  className="mt-1 min-h-10 w-full rounded-lg border border-zinc-300 bg-white px-3 text-sm text-zinc-900 outline-none focus:border-zinc-900 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100 dark:focus:border-zinc-100"
                                  required
                                />
                              </label>
                              <label className="text-xs font-medium text-zinc-700 dark:text-zinc-300">
                                Amount
                                <input
                                  type="number"
                                  min="0.01"
                                  step="0.01"
                                  value={actionAmount}
                                  onChange={(event) =>
                                    setActionAmount(event.target.value)
                                  }
                                  className="mt-1 min-h-10 w-full rounded-lg border border-zinc-300 bg-white px-3 text-sm text-zinc-900 outline-none focus:border-zinc-900 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100 dark:focus:border-zinc-100"
                                  required
                                />
                              </label>
                            </div>
                            <div className="mt-3 flex flex-wrap items-center gap-2">
                              <button
                                type="submit"
                                className="min-h-10 rounded-lg bg-zinc-900 px-4 text-xs font-semibold text-white hover:bg-zinc-700 dark:bg-zinc-100 dark:text-zinc-900 dark:hover:bg-white"
                              >
                                {accountAction === "add"
                                  ? "Add account"
                                  : accountAction === "send"
                                    ? "Send transfer"
                                    : "Apply top-up"}
                              </button>
                              <button
                                type="button"
                                onClick={() => setAccountAction(null)}
                                className="min-h-10 rounded-lg px-3 text-xs font-medium text-zinc-600 hover:bg-zinc-200 dark:text-zinc-400 dark:hover:bg-zinc-700"
                              >
                                Cancel
                              </button>
                            </div>
                            {actionResult && (
                              <p
                                role="status"
                                className="mt-3 text-xs text-zinc-700 dark:text-zinc-300"
                              >
                                {actionResult}
                              </p>
                            )}
                          </form>
                        )}
                      </div>
                    </div>
                  </div>

                  {/* Transactions (List02) */}
                  <div className="bg-white dark:bg-[#0F0F12] rounded-xl p-6 flex flex-col border border-gray-200 dark:border-[#1F1F23]">
                    <h2 className="text-lg font-bold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
                      <CreditCard className="w-3.5 h-3.5 text-zinc-900 dark:text-zinc-50" />
                      Recent Transactions
                    </h2>
                    <div className="flex-1 bg-white dark:bg-zinc-900/70 border border-zinc-100 dark:border-zinc-800 rounded-xl shadow-sm backdrop-blur-xl">
                      <div className="p-4">
                        <div className="mb-3 flex flex-wrap items-center justify-between gap-2">
                          <h4 className="text-sm font-semibold text-zinc-900 dark:text-zinc-100">
                            Recent Activity
                            <span className="text-xs font-normal text-zinc-600 dark:text-zinc-400 ml-1">
                              ({transactionList.length} transactions)
                            </span>
                          </h4>
                          <span className="text-xs text-zinc-600 dark:text-zinc-400">
                            This Month
                          </span>
                        </div>
                        <div className="space-y-1">
                          {transactionList.map((tx) => (
                            <div
                              key={tx.id}
                              className="group flex items-center gap-3 p-2 rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-800/50 transition-all duration-200"
                            >
                              <div className="p-2 rounded-lg bg-zinc-100 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700">
                                <tx.icon className="w-4 h-4 text-zinc-900 dark:text-zinc-100" />
                              </div>
                              <div className="flex min-w-0 flex-1 items-center justify-between gap-2">
                                <div className="min-w-0 space-y-0.5">
                                  <h5 className="truncate text-xs font-medium text-zinc-900 dark:text-zinc-100">
                                    {tx.title}
                                  </h5>
                                  <p className="text-[11px] text-zinc-600 dark:text-zinc-400">
                                    {tx.timestamp}
                                  </p>
                                </div>
                                <div className="flex shrink-0 items-center gap-1.5">
                                  <span
                                    className={`text-xs font-medium ${tx.type === "incoming" ? "text-emerald-600 dark:text-emerald-400" : "text-red-600 dark:text-red-400"}`}
                                  >
                                    {tx.type === "incoming" ? "+" : "-"}
                                    {currency.format(tx.amount)}
                                  </span>
                                  {tx.type === "incoming" ? (
                                    <ArrowDownLeft className="w-3.5 h-3.5 text-emerald-600 dark:text-emerald-400" />
                                  ) : (
                                    <ArrowUpRight className="w-3.5 h-3.5 text-red-600 dark:text-red-400" />
                                  )}
                                </div>
                              </div>
                            </div>
                          ))}
                        </div>
                      </div>
                      <div className="p-2 border border-zinc-100 dark:border-zinc-800">
                        <button
                          type="button"
                          onClick={() => setActivePage("Transactions")}
                          className="w-full flex items-center justify-center gap-2 py-2 px-3 rounded-lg text-xs font-medium bg-gradient-to-r from-zinc-900 to-zinc-800 dark:from-zinc-50 dark:to-zinc-200 text-zinc-50 dark:text-zinc-900 hover:from-zinc-800 hover:to-zinc-700 dark:hover:from-zinc-200 dark:hover:to-zinc-300 shadow-sm transition-all duration-200"
                        >
                          <span>View All Transactions</span>
                          <ArrowRight className="w-3.5 h-3.5" />
                        </button>
                      </div>
                    </div>
                  </div>
                </div>
                {/* Events (List03) */}
                <div className="bg-white dark:bg-[#0F0F12] rounded-xl p-6 flex flex-col items-start justify-start border border-gray-200 dark:border-[#1F1F23]">
                  <h2 className="text-lg font-bold text-gray-900 dark:text-white mb-4 flex items-center gap-2">
                    <Calendar className="w-3.5 h-3.5 text-zinc-900 dark:text-zinc-50" />
                    Upcoming Events
                  </h2>
                  <div className="grid w-full grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3">
                    {events.map((item) => {
                      const sc = statusConfig[item.status];
                      const StatusIcon = sc.icon;
                      return (
                        <div
                          key={item.id}
                          className="flex min-w-0 flex-col bg-white dark:bg-zinc-900/70 rounded-xl border border-zinc-100 dark:border-zinc-800 hover:border-zinc-200 dark:hover:border-zinc-700 transition-all duration-200 shadow-sm backdrop-blur-xl"
                        >
                          <div className="p-4 space-y-3">
                            <div className="flex items-start justify-between">
                              <div className="p-2 rounded-lg bg-zinc-100 dark:bg-zinc-800">
                                <item.icon className="w-4 h-4 text-zinc-900 dark:text-zinc-100" />
                              </div>
                              <div
                                className={`px-2 py-1 rounded-full text-xs font-medium flex items-center gap-1.5 ${sc.bg} ${sc.class}`}
                              >
                                <StatusIcon className="w-3.5 h-3.5" />
                                {item.status.charAt(0).toUpperCase() +
                                  item.status.slice(1)}
                              </div>
                            </div>
                            <div>
                              <h5 className="text-sm font-medium text-zinc-900 dark:text-zinc-100 mb-1">
                                {item.title}
                              </h5>
                              <p className="text-xs text-zinc-600 dark:text-zinc-400 line-clamp-2">
                                {item.subtitle}
                              </p>
                            </div>
                            <div className="space-y-1.5">
                              <div className="flex items-center justify-between text-xs">
                                <span className="text-zinc-600 dark:text-zinc-400">
                                  Progress
                                </span>
                                <span className="text-zinc-900 dark:text-zinc-100">
                                  {item.progress}%
                                </span>
                              </div>
                              <div className="h-1.5 bg-zinc-100 dark:bg-zinc-800 rounded-full overflow-hidden">
                                <div
                                  className="h-full bg-zinc-900 dark:bg-zinc-100 rounded-full"
                                  style={{ width: `${item.progress}%` }}
                                />
                              </div>
                            </div>
                            <div className="flex items-center gap-1.5">
                              <span className="text-sm font-medium text-zinc-900 dark:text-zinc-100">
                                {item.amount}
                              </span>
                              <span className="text-xs text-zinc-600 dark:text-zinc-400">
                                target
                              </span>
                            </div>
                            <div className="flex items-center text-xs text-zinc-600 dark:text-zinc-400">
                              <Calendar className="w-3.5 h-3.5 mr-1.5" />
                              <span>{item.date}</span>
                            </div>
                          </div>
                          <div className="mt-auto border border-zinc-100 dark:border-zinc-800">
                            <button
                              type="button"
                              onClick={() => setSelectedEvent(item)}
                              className="w-full flex items-center justify-center gap-2 py-2.5 px-3 text-xs font-medium text-zinc-600 dark:text-zinc-400 hover:text-zinc-900 dark:hover:text-zinc-100 hover:bg-zinc-100 dark:hover:bg-zinc-800/50 transition-colors duration-200"
                            >
                              View Details
                              <ArrowRight className="w-3.5 h-3.5" />
                            </button>
                          </div>
                        </div>
                      );
                    })}
                  </div>
                </div>
              </div>
            ) : (
              <div className="space-y-6">
                <section className="rounded-xl border border-gray-200 dark:border-[#1F1F23] bg-white dark:bg-zinc-900/70 p-6">
                  <h2 className="text-2xl font-semibold text-zinc-900 dark:text-zinc-100">
                    {activePage}
                  </h2>
                  <p className="mt-2 text-sm text-zinc-600 dark:text-zinc-400">
                    {kokonutPageSummary[activePage]}
                  </p>
                </section>
                {renderModuleContent()}
              </div>
            )}
          </main>
        </div>
      </div>
      {selectedEvent && (
        <div
          className="fixed inset-0 z-[80] grid place-items-center bg-black/55 p-4"
          role="dialog"
          aria-modal="true"
          aria-labelledby="event-detail-title"
        >
          <section className="w-full max-w-md rounded-2xl bg-white p-6 text-zinc-900 shadow-xl dark:bg-zinc-900 dark:text-zinc-100">
            <div className="flex items-start justify-between gap-4">
              <div>
                <p className="text-xs font-medium uppercase tracking-wider text-zinc-500 dark:text-zinc-400">
                  {selectedEvent.status}
                </p>
                <h2
                  id="event-detail-title"
                  className="mt-1 text-xl font-semibold"
                >
                  {selectedEvent.title}
                </h2>
              </div>
              <button
                type="button"
                onClick={() => setSelectedEvent(null)}
                className="rounded-lg px-3 py-2 text-sm font-medium text-zinc-600 hover:bg-zinc-100 hover:text-zinc-900 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-zinc-100"
              >
                Close
              </button>
            </div>
            <p className="mt-4 text-sm text-zinc-600 dark:text-zinc-300">
              {selectedEvent.subtitle}
            </p>
            <dl className="mt-5 grid grid-cols-2 gap-4 rounded-xl bg-zinc-100 p-4 text-sm dark:bg-zinc-800">
              <div>
                <dt className="text-zinc-500 dark:text-zinc-400">Target</dt>
                <dd className="mt-1 font-semibold">{selectedEvent.amount}</dd>
              </div>
              <div>
                <dt className="text-zinc-500 dark:text-zinc-400">Progress</dt>
                <dd className="mt-1 font-semibold">
                  {selectedEvent.progress}%
                </dd>
              </div>
              <div className="col-span-2">
                <dt className="text-zinc-500 dark:text-zinc-400">Schedule</dt>
                <dd className="mt-1 font-semibold">{selectedEvent.date}</dd>
              </div>
            </dl>
          </section>
        </div>
      )}
    </>
  );
}