Aller au contenu

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.md and 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/bo architecture, 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
  • Modalsmodules/<domain>/modals/, Dialog/AlertDialog, drawer+actions, disabled-with-tooltip
  • Styling — tokens, cva, topical DS folders
  • Data fetchingappQueryClient, 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|info allowed by Biome.
  • No React.memo reflex: 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.NumberFormat for currency. Use formatCentsFull / formatCentsShort from @bricks-common/bo/core; euro inputs parse via parseEurosInputToCents. Missing helper → lift into BO core, don't roll local.
  • No direct window.localStorage. Use readLocalStorage / writeLocalStorage / removeLocalStorage / useLocalStorageState from @bricks-common/bo/core (SSR guard, JSON, runtime validation). Keys namespaced bo:<feature>:<detail>.
  • English identifiers, French UI. All code (files, components, hooks, types, OpenModal kind literals, 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 (.gitkeep stubs under src/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.ts is generated, never hand-edit
  • TanStack Query — single appQueryClient from @bricks-common/bo/core, exposed via router context
  • Tailwind v4 via @tailwindcss/vite — config in CSS @import, no tailwind.config.*
  • Design system: @bricks-common/bo — shadcn new-york / zinc, radix-ui + cva
  • Forms: react-hook-form + <Form> wrapper — see ./form.mdc
  • i18n: @inlang/paraglide-js, fr only — see ./i18n.mdc
  • Dates: dayjs (locale fr) — use formatDate / formatDateTime from @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 across projects, constructionBudget, managementFees, paymentsByPeriod. Side panel without dedicated route; échéancier Actions include a read-only prorogation preview.
  • ProjectModalActionsProvider — global modal provider at _authenticated layout level for project lifecycle actions.
  • URL search state — each table page persists search/filter/pagination in URL via store/use*ListSearch.ts + route validateSearch.