bricksoffice-projects — agent rules¶
Always-on guidance for the Projects back-office app. Loads for Claude Code (via the projects/bricksoffice-projects/CLAUDE.md symlink) and Cursor (glob-scoped).
Canonical source is this file at
.cursor/rules/bo/.projects/bricksoffice-projects/CLAUDE.mdand everything under.claude/are symlinks — edit the original, not the symlink.Root rules apply. See
../claude-root.mdc. This file only restates the BO-specific delta.
Topical rules¶
- Shared BO lib —
@bricks-common/boarchitecture, public surface,#-imports, router/auth wiring, build/deploy - Forms — idtlt pattern, primitives, discriminated unions, runtime rules, branded submit
- Tables — DataTable providers, pinning, toolbar, URL state, CSV/XLSX
- Modals —
modules/<domain>/modals/, Dialog/AlertDialog, drawer+actions, disabled-with-tooltip - Styling — tokens, cva, topical DS folders
- Data fetching —
appQueryClient,processHttpResult, admin API error pipeline - i18n — paraglide, dotted-key bracket access, BO instance compile
Scaffolding & workflows:
- Scaffold a list page → .cursor/skills/bo-projects-create-page-table/SKILL.md
- Publish a changelog entry → /bo-projects-changelog-entry
- Audit rules for drift → /rules-hygiene (see .cursor/skills/rules-hygiene/)
BO-specific non-negotiables¶
These refine the root rules — don't restate them.
- Default-export exception: TanStack Router files in
src/routes/(file-based routing requires it). - Logging: no logger wired yet — surface errors via React Query / form state.
console.warn|error|infoallowed by Biome. - No
React.memoreflex: small SPA, React Compiler isn't wired here. - Import aliases (per-app):
@/→src/*,@core/→src/core/*,@shared/→src/shared/*. Workspace imports via@bricks-common/bo/.... - Root cause over symptoms: no suppressions, defensive guards, or swallowed errors. If the real fix is bigger than expected, surface options.
- No
Intl.NumberFormatfor currency. UseformatCentsFull/formatCentsShortfrom@bricks-common/bo/core; euro inputs parse viaparseEurosInputToCents. Missing helper → lift into BO core, don't roll local. - No direct
window.localStorage. UsereadLocalStorage/writeLocalStorage/removeLocalStorage/useLocalStorageStatefrom@bricks-common/bo/core(SSR guard, JSON, runtime validation). Keys namespacedbo:<feature>:<detail>. - English identifiers, French UI. All code (files, components, hooks, types,
OpenModalkindliterals, i18n key slugs) in English. UI strings in French. API fields that mirror the contract (soldeDeToutCompteDoneAt) stay French; surrounding artifacts use the English equivalent (ProjectFinalSettlementModal,useMarkProjectFinalSettlementDone). - Scaffolded folders (
.gitkeepstubs undersrc/modules/projects/) are intentional — don't fill without specs.
BO rule-file mapping¶
Per Keep rules in sync with code — change BO code, edit the matching rule in the same PR:
- Form pattern, primitive, validator →
./form.mdc - Table convention (sizing, toolbar, URL state, CSV) →
./table.mdc - Modal placement, drawer+actions, disabled-reason helpers →
./modal.mdc - Tokens, theme vars, DS topical folders, cva →
./styling.mdc appQueryClient, error handling,processHttpResult→./data-fetching.mdc- Paraglide setup, key naming, compile →
./i18n.mdc - BO public surface,
#-imports, router/auth, build pruning →./@bricks-common-bo.mdc - BO-specific non-negotiable or stack item → this file. Monorepo-wide →
../claude-root.mdc.
What this app is¶
Back-office for the Projects domain, sibling to bricksoffice-invest. Operational SPA used by ops/admin to manage obligation & royalty projects: payment schedules (échéancier), construction budgets, management fees, incoming wires, lifecycle (publish, funds transfer, final settlement).
UI strings: French only. Static SPA built by Vite, served by nginx on Railway. Build/deploy gotchas → ./@bricks-common-bo.mdc.
Stack (one-liners)¶
- Vite 7.2.2 + React 19 (StrictMode, no SSR)
- TanStack Router file-based (
autoCodeSplitting: true) —src/routeTree.gen.tsis generated, never hand-edit - TanStack Query — single
appQueryClientfrom@bricks-common/bo/core, exposed via router context - Tailwind v4 via
@tailwindcss/vite— config in CSS@import, notailwind.config.* - Design system:
@bricks-common/bo— shadcnnew-york/zinc,radix-ui+cva - Forms:
react-hook-form+<Form>wrapper — see./form.mdc - i18n:
@inlang/paraglide-js,fronly — see./i18n.mdc - Dates:
dayjs(localefr) — useformatDate/formatDateTimefrom@bricks-common/bo/core - Tests: Vitest 4 + RTL + jsdom (none wired yet)
- Lint/format: Biome 2.4 — root config authoritative
Commands¶
| Command | What |
|---|---|
pnpm dev |
Vite dev server (port 3000) |
pnpm build |
tsc -b && vite build to dist/ |
pnpm preview |
Preview built bundle |
pnpm type-check |
tsc --noEmit |
pnpm lint |
Biome check (scoped) |
pnpm format |
Biome format --write (scoped) |
No test script yet; run pnpm exec vitest once tests exist.
Project layout¶
src/
├── main.tsx # mounts <App />
├── App.tsx # QueryClientProvider → RouterProvider
├── styles.css # @import '@bricks-common/bo/styles.css'
├── routeTree.gen.ts # GENERATED — never hand-edit
├── core/{config,i18n,router,providers,layout}
├── modules/<domain>/ # see topical rules
│ ├── components/ # tables, cells, drawers, panels
│ ├── modals/ # Dialog/AlertDialog only
│ ├── pages/
│ ├── services/ # queryKeys + hooks
│ ├── store/ # URL search state
│ ├── types/
│ └── utils/
└── routes/ # file-based, __root.tsx + devtools
Module map¶
14 active modules. Routes auto-generated under src/routes/_authenticated/ unless noted.
| Module | Route(s) | Purpose |
|---|---|---|
auth |
/login |
Admin login (wraps shared AdminLoginPage) |
projects |
/projects/obligations, /projects/financing-simulation |
Obligation list, ProjectDrawer, simulation, 24+ lifecycle modals |
royalties |
/projects/royalties |
Royalty properties, yearly financial update, resale |
constructionBudget |
/projects/budget-chantier |
PDP wire approve/decline, transfer to échéance |
paymentsByPeriod |
/suivi-echeances |
Cross-project payment monitoring per month |
managementFees |
/suivi-frais-gestion |
Overdue fee tracking, transfer / mark-executed |
wiresToBeAssigned |
/paiements-a-assigner |
Match incoming wires to project/échéance |
wiresAssignedArchived |
/paiements-assignes-archives |
Read-only history of assigned/archived wires |
pendingPayouts |
/paiements-en-attente |
Pending versement queue |
capitalRepayments |
/remboursement-capital-impaye |
Unpaid capital repayment tracking |
spvsWallets |
/spvs-wallets |
SPV wallet ledger |
appBanner |
/app-invest/banner |
Cross-app banner editor |
homeNews |
/app-invest/home-news |
Home-page news editor |
changelog |
/changelog |
"Quoi de neuf ?" release timeline (unread badge in sidebar) |
componentStories |
/component-stories/* |
Dev-only DS gallery (gated on import.meta.env.DEV) |
Cross-cutting patterns¶
- ProjectDrawer — URL-driven (
?selectedProject=<uuid>&selectedProjectTab=overview|echeancier) shared acrossprojects,constructionBudget,managementFees,paymentsByPeriod. Side panel without dedicated route; échéancier Actions include a read-only prorogation preview. - ProjectModalActionsProvider — global modal provider at
_authenticatedlayout level for project lifecycle actions. - URL search state — each table page persists search/filter/pagination in URL via
store/use*ListSearch.ts+ routevalidateSearch.