docs(claude) : refactorise CLAUDE.md en index + extrait les regles dans .claude/rules/
- CLAUDE.md devient un index concis : contexte, stack, regles absolues numerotees, pointeurs vers les fichiers de regles detaillees via references @.claude/rules/*.md - Les conventions detaillees (architecture, backend, frontend, testing, naming, git, workflow) sont extraites dans .claude/rules/ pour rester chargees a la demande sans gonfler le context du CLAUDE.md principal - Ajoute la regle absolue "Ne jamais mentionner Claude/IA dans commits ou PR" (point 10) pour garder l'historique git signe par l'utilisateur
This commit is contained in:
66
.claude/rules/architecture.md
Normal file
66
.claude/rules/architecture.md
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
# Architecture — Modular Monolith DDD
|
||||||
|
|
||||||
|
## Principe fondamental
|
||||||
|
Le **backend est la source de verite unique**. Il dicte :
|
||||||
|
- Quels modules sont actifs (`config/modules.php`)
|
||||||
|
- L'organisation de la sidebar (`config/sidebar.php`, decouplee des modules)
|
||||||
|
|
||||||
|
Le frontend scanne `modules/*/` comme layers Nuxt et consomme l'API pour la navigation. Il ne decide rien.
|
||||||
|
|
||||||
|
## Endpoints API cles
|
||||||
|
|
||||||
|
- `GET /api/version` (public) — version de l'app
|
||||||
|
- `GET /api/modules` (public) — IDs des modules actifs
|
||||||
|
- `GET /api/sidebar` (public) — sections filtrees par modules actifs + `disabledRoutes` (items dont le module owner est inactif)
|
||||||
|
- `GET /api/me` (auth) — user courant
|
||||||
|
|
||||||
|
## Arborescence minimale (detail complet : @README.md)
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
Shared/ # Noyau technique partage (Domain/, Application/Bus/, Infrastructure/ApiPlatform/)
|
||||||
|
Module/
|
||||||
|
Core/ # Module obligatoire (auth, users)
|
||||||
|
CoreModule.php # ID, LABEL, REQUIRED, permissions()
|
||||||
|
Domain/ Application/ Infrastructure/
|
||||||
|
Commercial/ # Exemple d'autre module
|
||||||
|
frontend/
|
||||||
|
app/ # Shell (layouts, middlewares)
|
||||||
|
shared/ # Code inter-modules (composables, stores, utils)
|
||||||
|
modules/ # Layers Nuxt auto-detectes
|
||||||
|
core/ commercial/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Declaration d'un module
|
||||||
|
|
||||||
|
Chaque module expose un `*Module.php` avec :
|
||||||
|
- `ID` (snake_case, ex: `commercial`, `gestion_rh`)
|
||||||
|
- `LABEL`
|
||||||
|
- `REQUIRED` (bool)
|
||||||
|
- Methode statique `permissions()` retournant les RBAC du module
|
||||||
|
|
||||||
|
## Activer / desactiver un module
|
||||||
|
|
||||||
|
Editer uniquement `config/modules.php` (commenter la ligne). Cascade automatique :
|
||||||
|
1. `/api/modules` ne retourne plus l'ID
|
||||||
|
2. `/api/sidebar` filtre les items `module: '<id>'` et supprime les sections vides
|
||||||
|
3. Middleware front `modules.global.ts` redirige toute navigation vers une route desactivee
|
||||||
|
4. Le code reste dans le bundle (layer auto-detecte) → reactivation instantanee sans rebuild
|
||||||
|
|
||||||
|
## Reorganiser la sidebar
|
||||||
|
|
||||||
|
Editer uniquement `config/sidebar.php`. Le code des modules n'est pas touche — seule la place des items change. Chaque item reference son module owner via la cle `module`.
|
||||||
|
|
||||||
|
## Communication inter-modules
|
||||||
|
|
||||||
|
**Interdit** : import direct d'une classe d'un autre module.
|
||||||
|
**Autorise** :
|
||||||
|
- Via `Shared/Domain/Contract/` (interfaces : `UserResolverInterface`, `TenantAwareInterface`...)
|
||||||
|
- Via domain events (`Shared/Domain/Event/DomainEventInterface`)
|
||||||
|
|
||||||
|
## Migrations
|
||||||
|
|
||||||
|
- **Par defaut** : `src/Module/<Module>/Infrastructure/Doctrine/Migrations/` (namespace modulaire)
|
||||||
|
- **Exception** : les migrations d'initialisation critiques (setup user, RBAC, seed de base) vivent au namespace racine `DoctrineMigrations` dans `migrations/`.
|
||||||
|
- Raison : avec plusieurs `migrations_paths`, Doctrine Migrations 3.x trie par FQCN alphabetique et non par version timestamp → ordre incorrect entre namespaces sur base vide.
|
||||||
|
- A supprimer quand un `MigrationsComparator` custom ou un upgrade Doctrine resoudra le tri.
|
||||||
55
.claude/rules/backend.md
Normal file
55
.claude/rules/backend.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# Backend — Regles PHP / Symfony / API Platform
|
||||||
|
|
||||||
|
## Structure de fichier
|
||||||
|
|
||||||
|
- Toujours `declare(strict_types=1);` en tete de tout fichier PHP
|
||||||
|
- PHP CS Fixer : regles Symfony + PSR-12 + strict types (commande : `make php-cs-fixer-allow-risky`)
|
||||||
|
- Commentaires (docblock, inline, bloc) **en francais** ; code (classes, methodes, variables) en anglais
|
||||||
|
|
||||||
|
## API Platform (pas de controllers)
|
||||||
|
|
||||||
|
- Toujours utiliser `#[ApiResource]` + Providers + Processors — pas de controllers Symfony classiques
|
||||||
|
- Routes prefixees `/api` (via `config/routes/api_platform.yaml`)
|
||||||
|
- Le login `/login_check` est **hors** prefix `/api` (nginx reecrit `REQUEST_URI` vers `/login_check`)
|
||||||
|
- **Exception** : si tu dois creer un controller custom sous `/api/`, mettre `priority: 1` sur `#[Route]` pour eviter le conflit avec API Platform `{id}`
|
||||||
|
|
||||||
|
## Repositories
|
||||||
|
|
||||||
|
- Interface : `*RepositoryInterface` dans `Domain/Repository/`
|
||||||
|
- Implementation Doctrine : `Doctrine*Repository` dans `Infrastructure/Doctrine/`
|
||||||
|
- Le domaine garde les attributs ORM (approche pragmatique)
|
||||||
|
|
||||||
|
## RBAC (permissions)
|
||||||
|
|
||||||
|
Format obligatoire : `module.resource[.subresource].action` en snake_case.
|
||||||
|
- Exemples : `core.users.view`, `commercial.clients.contacts.edit`, `core.audit_log.view`
|
||||||
|
- Declarees via la methode statique `permissions()` des `*Module.php`
|
||||||
|
- Synchronisation : `app:sync-permissions`
|
||||||
|
- Verification API Platform : `is_granted('module.resource.action')`
|
||||||
|
- Verification front : `usePermissions()`
|
||||||
|
|
||||||
|
## Roles
|
||||||
|
|
||||||
|
- Hierarchie dans `config/packages/security.yaml` : `ROLE_ADMIN`, `ROLE_USER`
|
||||||
|
- Le role ne remplace pas la permission RBAC — deux niveaux complementaires
|
||||||
|
|
||||||
|
## Audit (obligatoire)
|
||||||
|
|
||||||
|
- Toute entite metier (nouvelle ou existante) : `#[Auditable]` (de `Shared/Domain/Attribute/`)
|
||||||
|
- Champs sensibles (password, token, secret) : `#[AuditIgnore]`
|
||||||
|
- Audit ManyToMany : trace automatiquement `{fieldName: {added: [ids], removed: [ids]}}` — aucune action supplementaire
|
||||||
|
- Spec complete : @doc/audit-log.md
|
||||||
|
|
||||||
|
## Serialization
|
||||||
|
|
||||||
|
Pour embarquer une relation dans le JSON (au lieu d'un IRI Hydra), ajouter le groupe du parent sur les proprietes de l'entite cible.
|
||||||
|
|
||||||
|
Exemple : pour qu'`User.profile` soit embarque au lieu d'un lien IRI sous le groupe `user:read`, annoter `Profile.$firstName` avec `#[Groups(['user:read'])]`.
|
||||||
|
|
||||||
|
## Upload de fichiers
|
||||||
|
|
||||||
|
- Valider cote serveur avec `$file->getMimeType()` — **jamais** `getClientMimeType()` (spoofable par le client)
|
||||||
|
|
||||||
|
## PostgreSQL
|
||||||
|
|
||||||
|
- Noms de colonnes toujours en **minuscules** dans le SQL brut (commun a tous les projets MALIO)
|
||||||
69
.claude/rules/frontend.md
Normal file
69
.claude/rules/frontend.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# Frontend — Regles Nuxt 4 / Vue 3 / @malio/layer-ui
|
||||||
|
|
||||||
|
## Base
|
||||||
|
|
||||||
|
- TypeScript strict
|
||||||
|
- 4 espaces d'indentation
|
||||||
|
- Commentaires (JSDoc, inline, bloc) **en francais** ; code (variables, types) en anglais
|
||||||
|
- Chaque module front = un layer Nuxt auto-detecte (`frontend/modules/*/nuxt.config.ts` minimal)
|
||||||
|
|
||||||
|
## Appels API
|
||||||
|
|
||||||
|
- Toujours `useApi()` — jamais `$fetch`, `ofetch`, `axios` en direct
|
||||||
|
- `useApi()` gere : cookies JWT, erreurs, toasts i18n, parsing Hydra
|
||||||
|
|
||||||
|
## Stores (Pinia)
|
||||||
|
|
||||||
|
- `useAuthStore` pour l'authentification
|
||||||
|
- `useUiStore` pour l'etat UI global (sidebar, modales, etc.)
|
||||||
|
- Composables avec state singleton (refs module-level) : exposer une fonction `reset*()` et la rappeler au logout (ex: `useSidebar().resetSidebar()`)
|
||||||
|
|
||||||
|
## Middlewares globaux
|
||||||
|
|
||||||
|
- `auth.global.ts` protege les routes + charge la sidebar apres login
|
||||||
|
- `modules.global.ts` redirige si la route demandee est dans `disabledRoutes`
|
||||||
|
|
||||||
|
## i18n et sidebar
|
||||||
|
|
||||||
|
- Labels de sidebar = cles i18n `sidebar.<module>.*`, jamais du texte brut
|
||||||
|
- Le layout `default.vue` applique `t()` sur les labels retournes par `/api/sidebar`
|
||||||
|
- Traductions dans `frontend/i18n/locales/`
|
||||||
|
|
||||||
|
## Composants formulaires — @malio/layer-ui obligatoire
|
||||||
|
|
||||||
|
Tout champ de formulaire / filtre doit utiliser les composants `Malio*` plutot que `<input>` / `<select>` bruts :
|
||||||
|
|
||||||
|
- `MalioInputText`, `MalioInputNumber`, `MalioInputAmount`, `MalioInputPassword`, `MalioInputTextArea`
|
||||||
|
- `MalioSelect`, `MalioSelectCheckbox`, `MalioCheckbox`, `MalioRadioButton`
|
||||||
|
- `MalioInputUpload`, `MalioTime`
|
||||||
|
- `MalioButton`, `MalioButtonIcon`
|
||||||
|
|
||||||
|
**Exceptions autorisees** (commenter un `// TODO` pour migrer quand la lib couvrira le cas) :
|
||||||
|
1. Type non couvert : `datetime-local`, `date`, color picker, file drag & drop
|
||||||
|
2. Bug connu bloquant (ex: `MalioSelect` avec options string) — documenter le bug en commentaire
|
||||||
|
|
||||||
|
Toute autre exception requiert validation avant merge.
|
||||||
|
|
||||||
|
## Tableaux de donnees — MalioDataTable obligatoire
|
||||||
|
|
||||||
|
Tout affichage LISTE tabulaire (donnees metier paginees, CRUD admin) doit passer par `MalioDataTable` :
|
||||||
|
- Pagination integree
|
||||||
|
- Slots `#header-*` pour filtres, `#cell-*` pour rendu custom
|
||||||
|
- Pas de `<table>` brut avec pagination custom
|
||||||
|
|
||||||
|
**Exception** : tableaux purement presentationnels non paginables (diff field/old/new, grille de comparaison, matrice RBAC d'admin, etc.) peuvent rester en `<table>` HTML brut.
|
||||||
|
|
||||||
|
## Etat des tableaux — pas de persistance URL
|
||||||
|
|
||||||
|
**Interdit** de persister l'etat d'un tableau (filtres, pagination, tri par colonne, selection, ligne active, scroll) dans la query string ou de le reinjecter depuis `route.query` au montage.
|
||||||
|
|
||||||
|
- L'etat vit uniquement dans le composant (`reactive`, `ref` locales)
|
||||||
|
- Seuls les deep links "de navigation metier" (ex: ouvrir un detail precis `/users/42`) sont dans l'URL
|
||||||
|
- Exceptions autorisees **sur demande explicite** de l'utilisateur
|
||||||
|
|
||||||
|
## Interdits
|
||||||
|
|
||||||
|
- `modules-loader.ts`, `.module.ts` — le scan des layers est automatique
|
||||||
|
- Hardcode de la sidebar cote front — elle vient de `/api/sidebar`
|
||||||
|
- Edition manuelle de `extends` dans `frontend/nuxt.config.ts` — les layers sont scannes
|
||||||
|
- Import d'un module front depuis un autre module — passer par `frontend/shared/`
|
||||||
39
.claude/rules/git.md
Normal file
39
.claude/rules/git.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Git
|
||||||
|
|
||||||
|
## Commits
|
||||||
|
|
||||||
|
Format : `<type>(<scope optionnel>) : <message>`
|
||||||
|
**Espaces obligatoires** autour du `:`.
|
||||||
|
|
||||||
|
Types autorises (minuscules uniquement) : `build`, `chore`, `ci`, `docs`, `feat`, `fix`, `perf`, `refactor`, `revert`, `style`, `test`
|
||||||
|
|
||||||
|
Exemples :
|
||||||
|
- `feat : add login page`
|
||||||
|
- `fix(auth) : prevent null token crash`
|
||||||
|
- `chore : bump version to v1.2.3`
|
||||||
|
|
||||||
|
## Regles de commit
|
||||||
|
|
||||||
|
- **Jamais commit sans demande explicite** de l'utilisateur
|
||||||
|
- **Jamais force push** sans confirmation
|
||||||
|
- **Jamais modifier la config git**
|
||||||
|
- **Jamais mentionner Claude, Anthropic ou une IA** dans un commit (message, titre, body, footer, trailer) ou une PR (titre, description). Concretement, aucun des elements suivants ne doit apparaitre :
|
||||||
|
- Trailer `Co-Authored-By: Claude <...>` / `Co-Authored-By: Anthropic`
|
||||||
|
- Footer type `Generated with Claude Code`, `Generated by AI`, etc.
|
||||||
|
- Emoji robot `🤖` ou similaire en tete/fin de message
|
||||||
|
- Toute phrase attribuant la paternite du changement a une IA
|
||||||
|
Les commits sont signes **uniquement** par l'utilisateur. Si un hook ou un template ajoute ces elements automatiquement, les retirer manuellement avant push.
|
||||||
|
|
||||||
|
## Versioning & tags
|
||||||
|
|
||||||
|
La version de l'app est dans `config/version.yaml` (parametre `app.version`).
|
||||||
|
|
||||||
|
Workflow de release :
|
||||||
|
1. Bump `config/version.yaml` a la nouvelle version
|
||||||
|
2. Commit dedie : `chore : bump version to v<X.Y.Z>`
|
||||||
|
3. Tag : `git tag v<X.Y.Z>`
|
||||||
|
4. Push : `git push origin develop --tags`
|
||||||
|
|
||||||
|
**A chaque creation de tag**, toujours mettre a jour `config/version.yaml` avec la meme version — sinon l'app ne connait pas sa propre version.
|
||||||
|
|
||||||
|
CI Gitea automatise le bump sur push `develop` (cf. `.gitea/workflows/`), mais un tag manuel reste possible.
|
||||||
18
.claude/rules/naming.md
Normal file
18
.claude/rules/naming.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Nommage
|
||||||
|
|
||||||
|
| Element | Convention | Exemple |
|
||||||
|
|---------|-----------|---------|
|
||||||
|
| Module back | PascalCase | `Module/Commercial/` |
|
||||||
|
| Module front | kebab-case | `modules/commercial/` |
|
||||||
|
| Module ID (dans code/config) | snake_case | `commercial`, `gestion_rh` |
|
||||||
|
| Entity Doctrine | PascalCase singulier | `User.php` |
|
||||||
|
| Repository interface | `*RepositoryInterface` | `UserRepositoryInterface.php` |
|
||||||
|
| Repository impl Doctrine | `Doctrine*Repository` | `DoctrineUserRepository.php` |
|
||||||
|
| DTO | `*Output` / `*Input` | `UserOutput.php` |
|
||||||
|
| API Platform Resource | classe dans `Infrastructure/ApiPlatform/Resource/` | `UserResource.php` |
|
||||||
|
| API Platform Provider | `*Provider` | `MeProvider.php` |
|
||||||
|
| API Platform Processor | `*Processor` | `UserPasswordHasherProcessor.php` |
|
||||||
|
| Module declaration back | `*Module.php` | `CommercialModule.php` |
|
||||||
|
| Composable front | `use*` | `useSidebar.ts` |
|
||||||
|
| Cles i18n sidebar | `sidebar.<module>.*` | `sidebar.commercial.overview` |
|
||||||
|
| Permission RBAC | `module.resource[.subresource].action` | `core.users.view`, `commercial.clients.contacts.edit` |
|
||||||
36
.claude/rules/testing.md
Normal file
36
.claude/rules/testing.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Tests
|
||||||
|
|
||||||
|
## Trois suites
|
||||||
|
|
||||||
|
| Suite | Commande | Outil | Where |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Back | `make test` | PHPUnit | Container PHP ; fixtures dediees sous `tests/Fixtures/` |
|
||||||
|
| Front unitaire | `make nuxt-test` | Vitest (happy-dom) | Container Node ; <30s ; composables/utils/stores |
|
||||||
|
| Front E2E | `make test-e2e` | Playwright | **Host** (pas container, navigateur reel requis) |
|
||||||
|
|
||||||
|
Bootstrap E2E (une fois par poste de dev) : `make install-e2e-deps` (Chromium + libs systeme, sudo).
|
||||||
|
Re-run uniquement si `@playwright/test` upgrade majeur.
|
||||||
|
|
||||||
|
Workflow E2E : `make start && make seed-e2e && make dev-nuxt` (terminal 1), `make test-e2e` (terminal 2).
|
||||||
|
UI interactive pour debug : `make test-e2e-ui`.
|
||||||
|
|
||||||
|
## Regle d'or E2E
|
||||||
|
|
||||||
|
**Un nouveau test E2E ne s'ajoute QUE si un bug critique est passe en prod.**
|
||||||
|
|
||||||
|
Sinon, la bonne place est un test unitaire Vitest :
|
||||||
|
- Plus rapide
|
||||||
|
- Plus stable
|
||||||
|
- Moins de faux positifs
|
||||||
|
|
||||||
|
Pour etendre la couverture RBAC, etendre un **persona existant** dans `frontend/tests/e2e/_fixtures/personas.ts` plutot que de creer un nouveau test.
|
||||||
|
|
||||||
|
## Matrice RBAC — 3 endroits obligatoires
|
||||||
|
|
||||||
|
Ajouter/modifier une permission testable = toucher les 3 miroirs (sinon drift garanti) :
|
||||||
|
|
||||||
|
1. `config/sidebar.php` — attacher `permission` au bon item
|
||||||
|
2. `frontend/tests/e2e/_fixtures/personas.ts` — ajuster `permissions` + `expectedAdminLinks` d'un persona existant
|
||||||
|
3. `src/Module/Core/Infrastructure/Console/SeedE2ECommand.php` — miroir back du meme persona
|
||||||
|
|
||||||
|
Tout changement sur l'un des trois sans les deux autres = test casse ou faux positif.
|
||||||
66
.claude/rules/workflow.md
Normal file
66
.claude/rules/workflow.md
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
# Workflow
|
||||||
|
|
||||||
|
## Langue
|
||||||
|
|
||||||
|
- **UI** : francais (tout ce qui est visible utilisateur)
|
||||||
|
- **Communication avec l'utilisateur** : francais
|
||||||
|
- **Code** (noms de classes, methodes, variables, types) : anglais
|
||||||
|
- **Commentaires** (PHP, TS, Vue — docblock, inline, bloc) : **francais**. Objectif : faciliter la relecture par l'equipe FR sans polluer l'API publique du code
|
||||||
|
|
||||||
|
## Delegation Codex
|
||||||
|
|
||||||
|
Pour les taches mecaniques (generation de tests boilerplate, renommages massifs, refacto repetitif, scaffolding), **deleguer a Codex** via le plugin `codex` (skill `codex:rescue`).
|
||||||
|
|
||||||
|
- **Codex** = junior rapide et pas cher → executions mecaniques
|
||||||
|
- **Claude** = senior qui verifie et reflechit → design, review, decisions
|
||||||
|
|
||||||
|
Ratio qualite/credits optimal : Claude conserve la reflexion et la validation, Codex avale le boilerplate.
|
||||||
|
|
||||||
|
## Avant d'implementer un ticket
|
||||||
|
|
||||||
|
Les specs fonctionnelles vivent sous `docs/{rbac,sites,modules}/ticket-*-spec.md`.
|
||||||
|
|
||||||
|
Avant de coder :
|
||||||
|
1. Lire la spec correspondante en entier (elles sont longues mais exhaustives)
|
||||||
|
2. Ne pas inventer de detail non specifie — demander a l'utilisateur ou citer explicitement la section manquante
|
||||||
|
3. Si la spec contredit le code existant, poser la question avant de choisir
|
||||||
|
|
||||||
|
## Verification avant "c'est fini"
|
||||||
|
|
||||||
|
Ne jamais declarer une tache terminee sans avoir lance les verifications applicables :
|
||||||
|
|
||||||
|
| Ce qui a bouge | Commande a lancer |
|
||||||
|
|---|---|
|
||||||
|
| Fichiers PHP | `make test` + `make php-cs-fixer-allow-risky` |
|
||||||
|
| Fichiers front (Vue/TS) | `make nuxt-test` |
|
||||||
|
| Migrations | `make migration-migrate` (sur BDD fraiche ideal : `make db-reset`) |
|
||||||
|
| Sidebar / permissions | Verifier que les 3 miroirs RBAC sont alignes (cf. @.claude/rules/testing.md) |
|
||||||
|
| UI visible | Demarrer `make dev-nuxt` et verifier le golden path dans le navigateur |
|
||||||
|
|
||||||
|
Si une verification echoue ou ne peut pas etre lancee (ex : container pas demarre), le dire explicitement plutot que d'annoncer "fini".
|
||||||
|
|
||||||
|
## Time tracking Lesstime
|
||||||
|
|
||||||
|
Au demarrage de toute tache de dev sur Coltura, creer une time entry via l'API Lesstime (cf. `~/.claude/CLAUDE.md` pour la procedure complete).
|
||||||
|
- Projet : `/api/projects/6` (COLTURA)
|
||||||
|
- Tags : choisir selon le type (Backend `3`, Frontend `2`, Infra `5`, UI/UX `4`, Maintenance `6`, Gestion projet `9`, etc.)
|
||||||
|
|
||||||
|
## Fix `make cache-clear` (permissions `var/`)
|
||||||
|
|
||||||
|
Si `make cache-clear` echoue sur les permissions de `var/` :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -t -u root php-coltura-fpm chown -R www-data:www-data /var/www/html/var
|
||||||
|
docker exec -t -u www-data php-coltura-fpm php bin/console cache:clear
|
||||||
|
```
|
||||||
|
|
||||||
|
A terme : integrer ce fix dans le `makefile` lui-meme.
|
||||||
|
|
||||||
|
## Docker — references utiles
|
||||||
|
|
||||||
|
- Container PHP : `php-coltura-fpm`
|
||||||
|
- Container Nginx : `nginx-coltura` (port 8083)
|
||||||
|
- Container DB : PostgreSQL port **5437** (interne et externe)
|
||||||
|
- Config dev : `infra/dev/.env.docker` (override local : `infra/dev/.env.docker.local`)
|
||||||
|
- Config prod : `infra/prod/` (Dockerfile multi-stage, `docker-compose.prod.yml`)
|
||||||
|
- Apres modif nginx : `docker restart nginx-coltura`
|
||||||
347
CLAUDE.md
347
CLAUDE.md
@@ -1,317 +1,68 @@
|
|||||||
# Coltura
|
# Coltura
|
||||||
|
|
||||||
CRM/ERP. Monorepo Symfony 8 (API Platform 4) + Nuxt 4. **Architecture Modular Monolith DDD.**
|
## Contexte
|
||||||
|
CRM/ERP en architecture **modular monolith DDD**. Le backend est la source de verite unique (modules actifs, sidebar). Le frontend scanne `frontend/modules/*/` comme layers Nuxt et consomme l'API pour la navigation. Multi-tenant : chaque module est activable/desactivable.
|
||||||
|
|
||||||
## Architecture Modulaire
|
Doc humaine : @README.md — Spec audit : @doc/audit-log.md
|
||||||
|
|
||||||
Le projet suit une architecture **modular monolith** pilotee par le backend : chaque module metier est un bounded context autonome, activable/desactivable par tenant. Le module `Core` est obligatoire.
|
|
||||||
|
|
||||||
**Principe fondamental : le backend est la source de verite unique.**
|
|
||||||
- Le backend dicte quels modules sont actifs (`config/modules.php`).
|
|
||||||
- Le backend dicte l'organisation de la sidebar (`config/sidebar.php`), decouplee des modules eux-memes.
|
|
||||||
- Le frontend ne connait rien : il scanne automatiquement les modules comme layers Nuxt et demande la sidebar au backend.
|
|
||||||
|
|
||||||
### Backend — Organisation par module
|
|
||||||
|
|
||||||
```
|
|
||||||
src/
|
|
||||||
Kernel.php
|
|
||||||
Shared/ # Noyau technique partage
|
|
||||||
Domain/
|
|
||||||
ValueObject/ # VO de base (Email...)
|
|
||||||
Event/ # DomainEventInterface
|
|
||||||
Contract/ # Interfaces inter-modules (UserResolverInterface, TenantAwareInterface)
|
|
||||||
Application/
|
|
||||||
Bus/ # CommandBusInterface, QueryBusInterface (interfaces seules)
|
|
||||||
Infrastructure/
|
|
||||||
ApiPlatform/
|
|
||||||
Resource/ # AppVersion, ModulesResource, SidebarResource
|
|
||||||
State/ # AppVersionProvider, ModulesProvider, SidebarProvider
|
|
||||||
Module/
|
|
||||||
Core/ # Module obligatoire (auth, users)
|
|
||||||
CoreModule.php # Declaration (ID, LABEL, REQUIRED)
|
|
||||||
Domain/
|
|
||||||
Entity/ # Entites Doctrine + API Platform (User)
|
|
||||||
Repository/ # Interfaces repositories (UserRepositoryInterface)
|
|
||||||
Event/ # Domain events (UserCreated)
|
|
||||||
Application/
|
|
||||||
DTO/ # UserOutput
|
|
||||||
Infrastructure/
|
|
||||||
Doctrine/ # DoctrineUserRepository, Migrations/
|
|
||||||
ApiPlatform/
|
|
||||||
State/
|
|
||||||
Provider/ # MeProvider
|
|
||||||
Processor/ # UserPasswordHasherProcessor
|
|
||||||
Console/ # CreateUserCommand
|
|
||||||
DataFixtures/ # AppFixtures
|
|
||||||
Commercial/ # Autre module (exemple)
|
|
||||||
CommercialModule.php
|
|
||||||
config/
|
|
||||||
modules.php # Liste des modules actifs (source de verite activation)
|
|
||||||
sidebar.php # Structure de la sidebar (source de verite navigation)
|
|
||||||
version.yaml
|
|
||||||
jwt/ # Cles JWT
|
|
||||||
packages/ # Config Symfony
|
|
||||||
migrations/ # Anciennes migrations Doctrine
|
|
||||||
infra/dev/ # Docker dev
|
|
||||||
infra/prod/ # Docker prod (multi-stage)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Frontend — Organisation modulaire (auto-detectee)
|
|
||||||
|
|
||||||
```
|
|
||||||
frontend/
|
|
||||||
app/ # Shell applicatif
|
|
||||||
layouts/ # default.vue, auth.vue
|
|
||||||
middleware/ # auth.global.ts, modules.global.ts
|
|
||||||
shared/ # Code partage (hors modules)
|
|
||||||
composables/ # useApi, useAppVersion, useSidebar
|
|
||||||
components/ui/ # AppTopNav, ...
|
|
||||||
stores/ # auth, ui
|
|
||||||
services/ # auth
|
|
||||||
types/ # SidebarSection, SidebarItem, UserData
|
|
||||||
utils/ # api (Hydra)
|
|
||||||
modules/ # Modules auto-detectes comme layers Nuxt
|
|
||||||
core/
|
|
||||||
nuxt.config.ts # Marqueur layer (vide)
|
|
||||||
pages/ # index.vue, login.vue
|
|
||||||
commercial/
|
|
||||||
nuxt.config.ts
|
|
||||||
pages/ # commercial.vue
|
|
||||||
app.vue # Composant racine
|
|
||||||
nuxt.config.ts # Scanne modules/*/ automatiquement
|
|
||||||
i18n/locales/ # Traductions (cles sidebar.*, etc.)
|
|
||||||
assets/ # CSS, images
|
|
||||||
public/ # Fichiers statiques
|
|
||||||
```
|
|
||||||
|
|
||||||
### Endpoints API cles
|
|
||||||
|
|
||||||
- `GET /api/version` (public) — version de l'app
|
|
||||||
- `GET /api/modules` (public) — liste des IDs de modules actifs
|
|
||||||
- `GET /api/sidebar` (public) — sections de la sidebar + `disabledRoutes`
|
|
||||||
- Filtre automatiquement les items dont le `module` owner n'est pas actif
|
|
||||||
- Les sections vides apres filtrage sont supprimees
|
|
||||||
- `disabledRoutes` = `to` des items filtres (utilise par le middleware front)
|
|
||||||
- `GET /api/me` (auth) — user courant
|
|
||||||
|
|
||||||
### Flux d'activation/desactivation d'un module
|
|
||||||
|
|
||||||
Pour activer/desactiver un module, tu touches **uniquement** `config/modules.php` :
|
|
||||||
|
|
||||||
```php
|
|
||||||
return [
|
|
||||||
\App\Module\Core\CoreModule::class,
|
|
||||||
// \App\Module\Commercial\CommercialModule::class, // commente = desactive
|
|
||||||
];
|
|
||||||
```
|
|
||||||
|
|
||||||
Cascade automatique :
|
|
||||||
1. `GET /api/modules` ne retourne plus `commercial`
|
|
||||||
2. `GET /api/sidebar` filtre les items `module: 'commercial'` → section "Commercial" disparait, ses routes passent dans `disabledRoutes`
|
|
||||||
3. Frontend : sidebar se met a jour, middleware `modules.global.ts` redirige toute navigation vers `/commercial` ou `/commercial/*`
|
|
||||||
4. Le code du module reste dans le bundle Nuxt (layer auto-detecte) → reactivation instantanee sans rebuild
|
|
||||||
|
|
||||||
### Reorganiser la sidebar sans toucher aux modules
|
|
||||||
|
|
||||||
Pour deplacer un item (ex: "Commandes fournisseurs") d'une section a une autre, tu edites juste `config/sidebar.php` :
|
|
||||||
|
|
||||||
```php
|
|
||||||
// Avant : sous Commercial
|
|
||||||
['label' => 'sidebar.commercial.suppliers', 'to' => '/commercial/suppliers', 'module' => 'commercial'],
|
|
||||||
|
|
||||||
// Apres : sous Production (l'item reste "owned" par Commercial, seule sa place change)
|
|
||||||
[
|
|
||||||
'label' => 'sidebar.production.section',
|
|
||||||
'items' => [
|
|
||||||
['label' => 'sidebar.commercial.suppliers', 'to' => '/commercial/suppliers', 'module' => 'commercial'],
|
|
||||||
],
|
|
||||||
],
|
|
||||||
```
|
|
||||||
|
|
||||||
Le code du module Commercial n'est pas touche.
|
|
||||||
|
|
||||||
### Regles d'architecture
|
|
||||||
|
|
||||||
**Backend :**
|
|
||||||
- Le domaine (`Domain/`) peut garder les attributs ORM (approche pragmatique) mais les repositories sont des interfaces
|
|
||||||
- Communication inter-modules par `Shared/Domain/Contract/` ou domain events — jamais d'import direct entre modules
|
|
||||||
- Chaque module declare un `*Module.php` avec `ID`, `LABEL`, `REQUIRED`
|
|
||||||
- `config/modules.php` = seule source de verite pour l'activation
|
|
||||||
- `config/sidebar.php` = seule source de verite pour l'organisation de la sidebar (chaque item reference son module owner via la cle `module`)
|
|
||||||
- Migrations par module dans `src/Module/{Module}/Infrastructure/Doctrine/Migrations/`
|
|
||||||
- **Exception connue** : avec plusieurs `migrations_paths` configures, Doctrine Migrations 3.x trie les migrations par FQCN alphabetique et non par version timestamp → ordre d'execution incorrect entre namespaces sur une base vide. Tant que ce n'est pas resolu (via un `MigrationsComparator` custom ou un upgrade), les migrations d'initialisation critiques (setup user, RBAC, etc.) vivent au namespace racine `DoctrineMigrations` dans `migrations/`. Le namespace modulaire reste configure pour les futures migrations applicatives (qui dependent d'un schema deja cree).
|
|
||||||
|
|
||||||
**Frontend :**
|
|
||||||
- Chaque module est un layer Nuxt auto-detecte (`modules/*/nuxt.config.ts` minimal)
|
|
||||||
- Un module front ne doit pas importer depuis un autre module — utiliser `shared/`
|
|
||||||
- `useSidebar()` fetch `/api/sidebar` et expose `sections`, `disabledRoutes`, `isRouteDisabled()`
|
|
||||||
- Le layout `default.vue` itere sur les sections retournees par l'API, applique `t()` sur les labels
|
|
||||||
- Middleware `auth.global.ts` charge la sidebar apres authentification
|
|
||||||
- Middleware `modules.global.ts` redirige si la route demandee est dans `disabledRoutes`
|
|
||||||
- Les composables avec state singleton (refs module-level) doivent exposer une fonction `reset*()` et etre reinitialises au logout (ex: `useSidebar().resetSidebar()`)
|
|
||||||
- **Interdit** : `.module.ts`, `modules-loader.ts`, hardcode de la sidebar, edition manuelle de `extends` dans `nuxt.config.ts`
|
|
||||||
|
|
||||||
## Stack
|
## Stack
|
||||||
|
- Backend : PHP 8.4, Symfony 8, API Platform 4, Doctrine ORM, PostgreSQL 16 (port 5437)
|
||||||
|
- Frontend : Nuxt 4 (SPA), Vue 3, Pinia, Tailwind, @malio/layer-ui, @nuxtjs/i18n
|
||||||
|
- Auth : JWT HTTP-only cookie (Lexik), login a `/login_check`
|
||||||
|
- Containers : `php-coltura-fpm`, `nginx-coltura` (port 8083), dev Nuxt port **3004**
|
||||||
|
|
||||||
- **Backend** : PHP 8.4, Symfony 8.0, API Platform 4, Doctrine ORM, PostgreSQL 16
|
## Regles ABSOLUES
|
||||||
- **Frontend** : Nuxt 4 (SSR off / SPA), Vue 3, Pinia, Tailwind CSS, @malio/layer-ui, nuxt-toast, @nuxtjs/i18n, @nuxt/icon
|
|
||||||
- **Auth** : JWT HTTP-only cookie (lexik/jwt-authentication-bundle), login a `/login_check`, cookie `BEARER`
|
|
||||||
- **Docker** : PHP-FPM + Node 24, Nginx (port 8083), PostgreSQL (port 5437)
|
|
||||||
|
|
||||||
## Commandes
|
1. **Ne jamais importer d'un module a un autre** — passer par `Shared/Domain/Contract/` (interfaces) ou domain events.
|
||||||
|
2. **Toujours `declare(strict_types=1);`** en tete de tout fichier PHP.
|
||||||
```bash
|
3. **Toujours annoter `#[Auditable]`** les entites metier ; `#[AuditIgnore]` sur champs sensibles (password, token, secret).
|
||||||
make start # Demarrer les containers
|
4. **Toujours utiliser `useApi()`** pour les appels API cote front — jamais `$fetch` / `ofetch` direct.
|
||||||
make stop # Arreter les containers
|
5. **Toujours utiliser les composants `Malio*`** (@malio/layer-ui) pour formulaires/filtres ; `MalioDataTable` pour listes paginees.
|
||||||
make restart # Redemarrer les containers
|
6. **Jamais persister l'etat de tableau dans l'URL** (filtres, pagination, tri, selection) — state local uniquement.
|
||||||
make install # Install complet (composer, migrations, fixtures, build Nuxt)
|
7. **Jamais ajouter un test E2E** sauf si un bug critique est passe en prod ; preferer un test Vitest.
|
||||||
make reset # Tout supprimer et reinstaller (supprime la BDD)
|
8. **Toujours mettre a jour les 3 sources RBAC ensemble** : `config/sidebar.php`, `frontend/tests/e2e/_fixtures/personas.ts`, `src/Module/Core/Infrastructure/Console/SeedE2ECommand.php`.
|
||||||
make dev-nuxt # Dev server Nuxt (hot reload, port 3004)
|
9. **Jamais commit sans demande explicite** de l'utilisateur ; jamais force push sans confirmation.
|
||||||
make shell # Shell dans le container PHP
|
10. **Jamais mentionner Claude, Anthropic ou une IA** dans un commit (message, titre, body, footer, trailer) ou une PR (titre, description). Pas de `Co-Authored-By: Claude`, pas de `Generated with Claude Code`, pas de `🤖`, pas d'emoji robot, rien. Les commits sont signes par l'utilisateur uniquement.
|
||||||
make shell-root # Shell root dans le container PHP
|
11. **Migrations d'initialisation au namespace racine** `DoctrineMigrations` dans `migrations/` (setup user, RBAC, seed de base). Les migrations modulaires (`src/Module/*/Infrastructure/Doctrine/Migrations/`) sont reservees aux evolutions post-schema (ajout de colonnes, index) — cf. @.claude/rules/architecture.md pour la raison.
|
||||||
make cache-clear # Vider le cache Symfony
|
|
||||||
make migration-migrate # Lancer les migrations
|
|
||||||
make fixtures # Charger les fixtures
|
|
||||||
make db-reset # Reset BDD + migrations + fixtures
|
|
||||||
make test # PHPUnit
|
|
||||||
make nuxt-test # Vitest (tests unitaires frontend)
|
|
||||||
make php-cs-fixer-allow-risky # Fix code style PHP
|
|
||||||
make logs-dev # Tail logs Symfony
|
|
||||||
```
|
|
||||||
|
|
||||||
Si `make cache-clear` echoue pour cause de permissions sur `var/` :
|
|
||||||
```bash
|
|
||||||
docker exec -t -u root php-coltura-fpm chown -R www-data:www-data /var/www/html/var
|
|
||||||
docker exec -t -u www-data php-coltura-fpm php bin/console cache:clear
|
|
||||||
```
|
|
||||||
|
|
||||||
### Tests E2E (Playwright)
|
|
||||||
|
|
||||||
La suite E2E vit dans `frontend/tests/e2e/` (personas, Page Objects, specs). Elle tourne sur l'host (Playwright a besoin d'un navigateur reel, pas dans un container PHP).
|
|
||||||
|
|
||||||
**Bootstrap one-time par poste de dev :**
|
|
||||||
```bash
|
|
||||||
make install-e2e-deps # Telecharge Chromium + installe les libs systeme (sudo)
|
|
||||||
```
|
|
||||||
A relancer uniquement si `@playwright/test` upgrade de version majeure.
|
|
||||||
|
|
||||||
**Workflow nominal :**
|
|
||||||
```bash
|
|
||||||
# Terminal 1
|
|
||||||
make start # Containers up
|
|
||||||
make seed-e2e # Cree les 6 personas e2e.* (idempotent)
|
|
||||||
make dev-nuxt # Dev server sur :3004 (garder ouvert)
|
|
||||||
|
|
||||||
# Terminal 2
|
|
||||||
make test-e2e # Run la suite
|
|
||||||
# ou
|
|
||||||
make test-e2e-ui # UI interactive pour debug
|
|
||||||
```
|
|
||||||
|
|
||||||
**Regle d'or E2E** (a respecter pour garder la suite maintenable sur la duree) : un nouveau test E2E ne s'ajoute QUE quand un bug critique est passe en prod. Sinon, la bonne place est un test unitaire Vitest (plus rapide, plus stable). Cf. `frontend/tests/e2e/_fixtures/personas.ts` pour etendre la matrice RBAC via un persona existant plutot que d'ajouter un test.
|
|
||||||
|
|
||||||
**Etendre la matrice RBAC** : pour ajouter une permission testable, toucher les 3 endroits (sinon drift garanti) :
|
|
||||||
1. `config/sidebar.php` — attacher `permission` au bon item
|
|
||||||
2. `frontend/tests/e2e/_fixtures/personas.ts` — ajuster `permissions` + `expectedAdminLinks` d'un persona existant (ne pas creer de nouveau persona par reflexe)
|
|
||||||
3. `src/Module/Core/Infrastructure/Console/SeedE2ECommand.php` — miroir back du meme persona
|
|
||||||
|
|
||||||
## Conventions
|
## Conventions
|
||||||
|
@.claude/rules/architecture.md
|
||||||
|
@.claude/rules/backend.md
|
||||||
|
@.claude/rules/frontend.md
|
||||||
|
@.claude/rules/testing.md
|
||||||
|
@.claude/rules/naming.md
|
||||||
|
@.claude/rules/git.md
|
||||||
|
@.claude/rules/workflow.md
|
||||||
|
|
||||||
### Commits
|
## Commandes (liste complete dans @README.md)
|
||||||
|
|
||||||
Format : `<type>(<scope optionnel>) : <message>` (espace avant et apres `:`)
|
- Demarrer : `make start`
|
||||||
|
- Dev front (hot reload) : `make dev-nuxt` (port 3004)
|
||||||
|
- Shell PHP : `make shell`
|
||||||
|
- Tests back : `make test`
|
||||||
|
- Tests front unitaires : `make nuxt-test`
|
||||||
|
- Tests E2E : `make test-e2e` (prerequis : `make seed-e2e && make dev-nuxt`)
|
||||||
|
- Reset BDD : `make db-reset`
|
||||||
|
- Lint PHP : `make php-cs-fixer-allow-risky`
|
||||||
|
|
||||||
Types autorises (minuscules) : `build`, `chore`, `ci`, `docs`, `feat`, `fix`, `perf`, `refactor`, `revert`, `style`, `test`
|
## Activer / desactiver un module
|
||||||
|
|
||||||
Exemples : `feat : add login page`, `fix(auth) : prevent null token crash`
|
Editer uniquement `config/modules.php` (commenter la ligne). Cascade automatique via `/api/modules`, `/api/sidebar`, middleware front `modules.global.ts`. Details : @.claude/rules/architecture.md
|
||||||
|
|
||||||
### Tags & Versioning
|
## A NE PAS faire
|
||||||
|
|
||||||
- La version de l'app est dans `config/version.yaml` (parametre `app.version`)
|
- Pas de controller Symfony custom sous `/api/` sans `priority: 1` sur `#[Route]` (conflit API Platform `{id}`).
|
||||||
- A chaque creation de tag, **toujours** mettre a jour `config/version.yaml` avec la meme version
|
- Pas de `getClientMimeType()` pour valider un upload — utiliser `$file->getMimeType()` (serveur).
|
||||||
- Faire un commit separe de bump : `chore : bump version to v<X.Y.Z>`
|
- Pas de hardcode de la sidebar cote front, pas de `modules-loader.ts` ni `.module.ts`.
|
||||||
- Puis creer le tag et pusher : `git tag v<X.Y.Z> && git push origin develop --tags`
|
- Pas d'edition manuelle de `extends` dans `frontend/nuxt.config.ts` — les layers sont scannes automatiquement.
|
||||||
|
- Pas de commentaires en anglais dans le code — **commentaires en francais**, code (noms) en anglais.
|
||||||
|
- Pas d'`<input>` / `<select>` / `<table>` bruts quand un composant `@malio/layer-ui` existe (exceptions documentees dans @.claude/rules/frontend.md).
|
||||||
|
- Pas de modification de la config git.
|
||||||
|
|
||||||
### Nommage
|
## Skills projet
|
||||||
|
|
||||||
| Element | Convention | Exemple |
|
- `create-module` — scaffolder un nouveau module back+front et wirer la sidebar.
|
||||||
|---------|-----------|---------|
|
|
||||||
| Module back | PascalCase | `Module/Commercial/` |
|
|
||||||
| Module front | kebab-case | `modules/commercial/` |
|
|
||||||
| Module ID | snake_case | `commercial`, `gestion_rh` |
|
|
||||||
| Entity | PascalCase singulier | `User.php` |
|
|
||||||
| Repository interface | `*RepositoryInterface` | `UserRepositoryInterface.php` |
|
|
||||||
| Repository impl | `Doctrine*Repository` | `DoctrineUserRepository.php` |
|
|
||||||
| DTO | `*Output` / `*Input` | `UserOutput.php` |
|
|
||||||
| API Resource | classe dans `Infrastructure/ApiPlatform/Resource/` | `UserResource.php` |
|
|
||||||
| Provider | `*Provider` | `MeProvider.php` |
|
|
||||||
| Processor | `*Processor` | `UserPasswordHasherProcessor.php` |
|
|
||||||
| Module declaration back | `*Module.php` | `CommercialModule.php` |
|
|
||||||
| Composable front | `use*` | `useSidebar.ts` |
|
|
||||||
| Cles i18n sidebar | `sidebar.<module>.*` | `sidebar.commercial.overview` |
|
|
||||||
|
|
||||||
### Backend
|
## Credentials (dev)
|
||||||
|
|
||||||
- Toujours `declare(strict_types=1)` en haut des fichiers PHP
|
`admin` / `admin` (ROLE_ADMIN) ; `alice` / `alice`, `bob` / `bob` (ROLE_USER).
|
||||||
- **Commentaires en francais** : tout commentaire PHP (docblock, inline, bloc) doit etre redige en francais. Le code (noms de classes, methodes, variables) reste en anglais. Objectif : faciliter la relecture par l'equipe FR sans polluer l'API publique du code.
|
|
||||||
- API Platform : utiliser ApiResource, Providers, Processors — pas de controllers
|
|
||||||
- Routes API prefixees `/api` (via `config/routes/api_platform.yaml`)
|
|
||||||
- Le login (`/login_check`) est hors prefix `/api`, nginx reecrit `REQUEST_URI` vers `/login_check`
|
|
||||||
- PHP CS Fixer : regles Symfony + PSR-12 + strict types
|
|
||||||
- Roles : `ROLE_ADMIN`, `ROLE_USER` — hierarchie dans `security.yaml`
|
|
||||||
- **Permissions RBAC** : format obligatoire `module.resource[.subresource].action` en snake_case, ex : `core.users.view`, `commercial.clients.contacts.edit`. Declarees via la methode statique `permissions()` des `*Module.php`, synchronisees par la commande `app:sync-permissions`. Verification via `is_granted('module.resource.action')` cote API Platform et `usePermissions()` cote front.
|
|
||||||
- PostgreSQL : noms de colonnes toujours en **minuscules** dans le SQL brut
|
|
||||||
- Controllers custom sous `/api/` : ajouter `priority: 1` sur `#[Route]` pour eviter le conflit avec API Platform `{id}`
|
|
||||||
- Serialization : pour embarquer une relation (pas IRI), ajouter le groupe du parent aux proprietes de l'entite cible
|
|
||||||
- Upload fichiers : utiliser `$file->getMimeType()` (pas `getClientMimeType()`) pour valider cote serveur
|
|
||||||
- **Audit obligatoire** : toute entite (nouvelle ou existante) doit porter `#[Auditable]` (dans `Shared/Domain/Attribute/`). Les champs sensibles (password, token, secret) doivent etre annotes `#[AuditIgnore]`. Spec complete : `doc/audit-log.md`
|
|
||||||
|
|
||||||
### Frontend
|
|
||||||
|
|
||||||
- TypeScript strict
|
|
||||||
- **Commentaires en francais** : tout commentaire TS/Vue (JSDoc, inline, bloc) doit etre redige en francais. Le code reste en anglais. Meme regle que cote backend.
|
|
||||||
- Composable `useApi()` pour tous les appels API (gere cookies, erreurs, toasts, i18n)
|
|
||||||
- Stores Pinia : `useAuthStore` (auth), `useUiStore` (ui)
|
|
||||||
- Middleware global `auth.global.ts` protege les routes + charge la sidebar apres login
|
|
||||||
- Middleware global `modules.global.ts` redirige les routes des modules desactives
|
|
||||||
- Traductions dans `frontend/i18n/locales/` avec le namespace `sidebar.*` pour la nav
|
|
||||||
- 4 espaces d'indentation
|
|
||||||
- Les labels de sidebar sont des cles i18n, jamais du texte brut (le layout applique `t()` dessus)
|
|
||||||
- **Tableaux : pas de persistance URL.** Aucun etat de tableau (filtres, pagination, tri, tri par colonne, selection, ligne active...) ne doit etre persiste dans la query string ou reinjecte depuis `route.query` au montage. L'etat vit uniquement dans le composant (reactive locale). Seuls les deep links "de navigation metier" (ex: ouvrir un detail precis `/users/42`) sont dans l'URL, jamais l'etat UI du tableau. Exceptions autorisees sur demande explicite de l'utilisateur.
|
|
||||||
- **Composants formulaires : utiliser `@malio/layer-ui`.** Tout champ de formulaire / filtre doit utiliser les composants `Malio*` (MalioInputText, MalioSelect, MalioSelectCheckbox, MalioCheckbox, MalioRadioButton, MalioInputNumber, MalioInputAmount, MalioInputPassword, MalioInputTextArea, MalioInputUpload, MalioTime, MalioButton, MalioButtonIcon) plutot que des `<input>` / `<select>` bruts.
|
|
||||||
- **Exceptions autorisees** (a commenter en TODO lors du premier usage, pour pouvoir migrer quand la lib evoluera) :
|
|
||||||
1. Type de champ non couvert par la lib : `datetime-local`, `date`, `color picker`, `file drag & drop`, etc.
|
|
||||||
2. Bug connu bloquant du composant : ex. `MalioSelect` avec options a valeur string (cf. note dans le Lesstime CLAUDE.md). Documenter le bug avec un commentaire pointant la limitation.
|
|
||||||
- Toute autre exception doit etre validee par l'equipe avant merge.
|
|
||||||
- **Tableaux de donnees : utiliser `MalioDataTable`.** Tout affichage LISTE tabulaire (donnees metier paginees, CRUD admin) doit passer par `MalioDataTable` (pagination + slots `#header-*` pour filtres + `#cell-*` pour rendu custom). Pas de `<table>` brut avec pagination custom. **Exception** : les tableaux purement presentationnels non-paginables (diff field/old/new, grille de comparaison, matrice RBAC, etc.) peuvent rester en `<table>` HTML brut — MalioDataTable n'est pas adapte a ces cas.
|
|
||||||
- **Audit ManyToMany** : le listener trace les modifications de collections to-many (`permissions`, etc.) sous forme `{fieldName: {added: [ids], removed: [ids]}}`. Toute nouvelle entite `#[Auditable]` portant des ManyToMany a auditer beneficie automatiquement de cette couverture — aucune action supplementaire requise.
|
|
||||||
|
|
||||||
### Nginx
|
|
||||||
|
|
||||||
- `/api/*` -> Symfony (via try_files + index.php)
|
|
||||||
- `/api/login_check` -> location exact match, fastcgi direct avec REQUEST_URI reecrit en `/login_check`
|
|
||||||
- `/` -> SPA frontend (`frontend/dist/`)
|
|
||||||
|
|
||||||
## Docker
|
|
||||||
|
|
||||||
- Container PHP : `php-coltura-fpm`
|
|
||||||
- Container Nginx : `nginx-coltura`
|
|
||||||
- Container DB : PostgreSQL sur port **5437** (interne et externe)
|
|
||||||
- Config Docker dev : `infra/dev/.env.docker` (override local : `infra/dev/.env.docker.local`)
|
|
||||||
- Config Docker prod : `infra/prod/` (Dockerfile multi-stage, docker-compose.prod.yml)
|
|
||||||
- Apres modif nginx : `docker restart nginx-coltura`
|
|
||||||
|
|
||||||
## Fixtures
|
|
||||||
|
|
||||||
- User admin : `admin` / `admin` (ROLE_ADMIN)
|
|
||||||
- Users internes : `alice` / `alice`, `bob` / `bob` (ROLE_USER)
|
|
||||||
|
|
||||||
## Delegation Codex
|
|
||||||
|
|
||||||
Pour les taches mecaniques (tests, boilerplate, renommages, refacto repetitif), delegue a Codex via le plugin `codex`. Garde Claude pour la reflexion, l'architecture et la verification.
|
|
||||||
|
|
||||||
- **Codex** = junior dev rapide et pas cher (executions mecaniques)
|
|
||||||
- **Claude** = senior dev qui verifie et reflechit (design, review, decisions)
|
|
||||||
|
|
||||||
C'est le meilleur ratio qualite/credits.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user