feat: impl accounts and channels

This commit is contained in:
2026-09-06 18:17:42 +08:00
parent aa9999a940
commit 1ccee1414c
70 changed files with 9879 additions and 31 deletions
+34
View File
@@ -3,3 +3,37 @@
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`. The available checks are `pnpm lint`, `pnpm typecheck`, and `pnpm build`; run `pnpm typecheck && pnpm build` after application changes. There is no committed test suite or CI workflow.
- Add UI primitives only through `pnpm dlx shadcn@latest add <component> --yes`. Before using a shadcn component, run `pnpm dlx shadcn@latest docs <component>` and read the referenced docs.
- Formatting is Prettier with Tailwind sorting (`pnpm format`). It uses double quotes, no semicolons, 2 spaces, LF, and 80-column wrapping.
## App And Auth
- This is a single Next.js App Router application; routes live under `app/`. Authenticated pages compose `SidebarProvider`, `AppSidebar`, and `SidebarInset` themselves.
- Next.js 16 uses `proxy.ts`, not `middleware.ts`. Keep `proxy.ts` edge-safe: it imports only `lib/auth/config.ts`, never database or password-hashing code.
- Login, registration, and `/api/auth` are public. All other routes are protected by `authConfig`; server pages and actions must still validate the session themselves.
- `lib/auth/index.ts` owns Node-side Auth.js providers and database work. OIDC is configuration-driven and disabled unless `AUTH_OIDC_ENABLED=true` with a complete issuer/client configuration.
## Database
- PostgreSQL access is Drizzle + `postgres`; the schema source of truth is `lib/db/schema.ts`. Drizzle Kit loads `DATABASE_URL` from `.env.local` and uses `drizzle.config.ts`.
- Use `pnpm drizzle-kit push --force` only when a schema change is intended; it can apply destructive changes. `pnpm tsx lib/db/reset.ts` drops `transactions`, `channels`, `accounts`, `user_accounts`, and `users` and must never be run casually.
- Business tables are user-scoped. Every read, update, or delete in `lib/actions/` must obtain `auth().user.id` and constrain the query with that `user_id`; validate that referenced account/channel IDs belong to the same user.
- Keep soft deletion (`deleted_at`) semantics in reads and mutations. Account and channel management actions already follow this model.
## UI And Copy
- The project uses shadcn `base-nova` on `@base-ui/react`, Lucide icons, Tailwind v4, and semantic CSS variables from `app/globals.css`.
- Prefer stock shadcn composition and variants over custom styling. Use `className` only for necessary layout, responsiveness, truncation, or stable dimensions; do not override component padding, margins, colors, typography, or default Dialog/Footer behavior without a verified need.
- Forms use `FieldGroup`, `Field`, and `FieldLabel`; use `FieldSet`/`FieldLegend` only when the grouping adds user-facing meaning. Put `SelectItem` inside `SelectGroup`.
- Errors and callouts use `Alert`; destructive errors use `Alert variant="destructive"` with `AlertTitle` and `AlertDescription`, never a hand-styled `div`.
- Follow the default Dialog composition: `DialogHeader`, form/content, then `DialogFooter` as a direct `DialogContent` child. Do not add `p-0`, custom negative margins, fixed heights, sticky footers, or isolated scroll containers unless the task explicitly requires them.
- Use `Button`, `Badge`, `Empty`, `Separator`, `Tooltip`, and other installed primitives instead of recreating them. Use `data-icon="inline-start"` or `data-icon="inline-end"` for icons in buttons.
- Use `gap-*`, never `space-x-*` or `space-y-*`; use semantic tokens instead of raw color palettes or manual `dark:` overrides.
- Product-facing UI copy is Chinese only. Do not add English parentheticals to menus, labels, options, or headings; retain user-entered business values such as currency codes and card brands verbatim.
- Sidebar active state must derive from `usePathname()` with exact matching for `/` and route-boundary matching for child paths. Do not add navigation links until their route exists.