feat(ledger): add bookkeeping workbench and transaction views

- Add transactions_dirty table and zero-tolerance cleansing action

- Implement bookkeeping workbench (/bookkeeping) with searchable Combobox channel selector and card suffix / account identifier

- Implement transaction ledger (/transactions) with date groupings, multi-currency metrics, and filters

- Update AGENTS.md guidelines for Base UI Select and Combobox bindings
This commit is contained in:
2026-09-06 23:23:29 +08:00
parent 1ccee1414c
commit c33857ffac
42 changed files with 3329 additions and 598 deletions
+26 -14
View File
@@ -1,16 +1,18 @@
"use client";
"use client"
import * as React from "react";
import Link from "next/link";
import * as React from "react"
import Link from "next/link"
import {
CreditCardIcon,
LayoutDashboardIcon,
PenToolIcon,
ReceiptTextIcon,
WalletCardsIcon,
} from "lucide-react";
} from "lucide-react"
import { FluxentLogo } from "@/components/fluxent-logo";
import { NavMain, type NavMainItem } from "@/components/nav-main";
import { NavUser, type NavUserData } from "@/components/nav-user";
import { FluxentLogo } from "@/components/fluxent-logo"
import { NavMain, type NavMainItem } from "@/components/nav-main"
import { NavUser, type NavUserData } from "@/components/nav-user"
import {
Sidebar,
SidebarContent,
@@ -20,7 +22,7 @@ import {
SidebarMenuButton,
SidebarMenuItem,
useSidebar,
} from "@/components/ui/sidebar";
} from "@/components/ui/sidebar"
const navMainItems: NavMainItem[] = [
{
@@ -28,6 +30,16 @@ const navMainItems: NavMainItem[] = [
url: "/",
icon: LayoutDashboardIcon,
},
{
title: "流水记账",
url: "/bookkeeping",
icon: PenToolIcon,
},
{
title: "交易记录",
url: "/transactions",
icon: ReceiptTextIcon,
},
{
title: "资金账户",
url: "/accounts",
@@ -38,21 +50,21 @@ const navMainItems: NavMainItem[] = [
url: "/channels",
icon: CreditCardIcon,
},
];
]
export function AppSidebar({
user,
...props
}: {
user: NavUserData;
user: NavUserData
} & React.ComponentProps<typeof Sidebar>) {
const { isMobile, setOpenMobile } = useSidebar();
const { isMobile, setOpenMobile } = useSidebar()
const handleLogoNavigation = () => {
if (isMobile) {
setOpenMobile(false);
setOpenMobile(false)
}
};
}
return (
<Sidebar variant="inset" {...props}>
@@ -89,5 +101,5 @@ export function AppSidebar({
<NavUser user={user} />
</SidebarFooter>
</Sidebar>
);
)
}