Files
fluxent-web/AGENTS.md
T
SerinaNya c33857ffac 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
2026-09-06 23:23:29 +08:00

43 lines
4.1 KiB
Markdown

<!-- BEGIN:nextjs-agent-rules -->
# This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
<!-- END:nextjs-agent-rules -->
# Fluxent Web
## Commands
- Use `pnpm`. Primary checks are `pnpm lint`, `pnpm typecheck`, and `pnpm build`; run `pnpm typecheck && pnpm build` to verify changes. There is no committed test suite.
- Add UI primitives only via `pnpm dlx shadcn@latest add <component> --yes`. Read the referenced docs via `pnpm dlx shadcn@latest docs <component>` before using.
- Format with `pnpm format` (Prettier + Tailwind plugin: double quotes, no semicolons, 2 spaces, LF, 80-column wrap).
## App And Auth
- Next.js 16 App Router application under `app/`. Authenticated pages compose `SidebarProvider`, `AppSidebar`, and `SidebarInset` themselves.
- Next.js 16 uses `proxy.ts`, NOT `middleware.ts`. Keep `proxy.ts` edge-safe: import only `lib/auth/config.ts`, never database or password-hashing code.
- Public routes: `/login`, `/register`, `/api/auth/*`. All other routes are protected by `proxy.ts`; server pages and server actions must still validate sessions via `auth()`.
- `lib/auth/index.ts` owns Node-side Auth.js providers and database operations. OIDC is disabled unless `AUTH_OIDC_ENABLED=true` with full issuer/client credentials configured.
- Sidebar active state must derive from `usePathname()` (`/` exact match, child routes prefix match). Do not add navigation links until their route exists.
## Database And Ledger Architecture
- PostgreSQL access via Drizzle ORM + `postgres`; schema truth is `lib/db/schema.ts`. Drizzle Kit loads `DATABASE_URL` from `.env.local` via `drizzle.config.ts`.
- Schema sync: `pnpm drizzle-kit push --force`. Never execute `pnpm tsx lib/db/reset.ts` unless explicitly instructed (drops all data tables).
- Multi-tenancy & safety: Every action in `lib/actions/` must enforce `userId = session.user.id` and filter out soft-deleted records with `isNull(table.deletedAt)`. Verify that referenced accounts or channels belong to the same user.
- Two-tier transaction ledger:
- Fast/draft entry stores records in `transactions_dirty`.
- Cleansing action (`cleanseTransactionsAction` in `lib/actions/bookkeeping.ts`) performs atomic zero-tolerance validation before moving items into `transactions`.
- Card brands: Stored as lowercase machine keys (`visa`, `mastercard`, `unionpay`, `amex`, `diners`, `discover`, `jcb`). Always use `normalizeCardBrand()` from `lib/payment/card-brand.ts` and local SVGs under `/payment-logos/`.
## UI And Copy
- Tech stack: shadcn `base-nova` on `@base-ui/react`, Lucide icons, Tailwind v4, semantic CSS variables from `app/globals.css`.
- Base UI Select & Combobox:
- `<SelectValue />`: When `<SelectItem>` contains complex JSX/icons and no plain string children, Base UI falls back to rendering raw values (e.g. UUIDs). Provide plain text `children` or explicit formatting logic.
- `<Combobox>`: When `items` is an array of objects, always provide both `itemToStringValue` (string serialization for search matching and value keying) and `itemToStringLabel` (input display string) to avoid `[object Object]` display bugs.
- Form composition: Use `FieldGroup`, `Field`, and `FieldLabel`. Put `SelectItem` inside `SelectGroup`. Use `FieldSet`/`FieldLegend` only when grouping adds visible user meaning.
- Dialog composition: Follow standard structure (`DialogHeader`, form/content, `DialogFooter` directly inside `DialogContent`). Do not add `p-0`, negative margins, sticky footers, or manual scroll wrappers without a verified requirement.
- Styling: Use `gap-*`, NEVER `space-x-*` or `space-y-*`. Use semantic color tokens, avoid hardcoded palettes or manual `dark:` overrides. Buttons with icons use `data-icon="inline-start"` or `data-icon="inline-end"`.
- Product copy is Chinese only: Do not add English parentheticals to labels, menus, options, or headings (e.g., no "账户 (Accounts)"). Retain user-entered values and standard business tokens (e.g., currency codes `CNY`, card brand names `Visa`) verbatim.