Commit Graph

6 Commits

Author SHA1 Message Date
edbe54cb8a fix(admin) : recherche insensible a la casse + label permissions dans filtres
SearchFilter partial etait case-sensitive en PostgreSQL : taper "ad" ne
trouvait pas "Administrateur". Passage en strategy `ipartial` (ILIKE) sur
tous les champs texte filtrables :
- Role.label / Role.code
- Site.name / Site.city / Site.postalCode
- User.username

Les filtres exacts sur relations (rbacRoles.code, sites.name,
permissions.code) restent en `exact` — alimentes par des <select> donc
casse maitrisee.

Test de non-regression ajoute : chercher "ad" (minuscule) trouve bien
"Administrateur" (A majuscule) sur /api/roles?label=ad.

UI select permissions du filtre /admin/roles : affiche `perm.label`
("Gerer les roles et permissions") au lieu de `perm.code`
("core.roles.manage"). Value reste sur `code` pour le backend. Tri
par label pour coherence alphabetique avec l'affichage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 17:28:01 +02:00
1550f46b23 Merge remote-tracking branch 'origin/feat/module-site-backend' into feat/admin-tables-filter-pagination
# Conflicts:
#	frontend/modules/sites/pages/admin/sites.vue
2026-04-20 17:04:04 +02:00
cb6d2d72ec feat(admin) : filtres + pagination serveur sur /admin/users/sites/roles
Ajoute le filtrage par colonne et la pagination negociee via query params
sur les 3 DataTables admin existantes. Tout est cote serveur (API Platform
SearchFilter + BooleanFilter) pour scaler naturellement.

Backend :
- api_platform.yaml : scan du mapping Sites + pagination_client_items_per_page
  (avec borne max 100 pour proteger contre les payloads exagerement grands).
- User : SearchFilter username (partial), rbacRoles.code (exact),
  sites.name (exact) + BooleanFilter isAdmin.
- Site : SearchFilter name/city/postalCode (partial).
- Role : SearchFilter label/code (partial), permissions.code (exact).
  (BooleanFilter isSystem deja present.)

Frontend :
- Composable useDataTableServerState (shared) : singleton de page/perPage/
  filters avec debounce 300ms sur les filters, fetch immediat sur page/
  perPage, reset page=1 au changement filter, token anti-race-condition.
- Pages admin : chaque filtre dans un slot #header-{key} (input text avec
  debounce, select mono-selection pour les relations). Font-size 20px sur
  les inputs de filtre.
- /admin/users : colonne Sites + filtre Sites conditionnes par
  useModules().isModuleActive('sites') — preserve l'invariant "module
  desactivable sans casse".

Tests : 215/215 PHPUnit (14 nouveaux filtres/pagination) + 48/48 Vitest
(8 nouveaux useDataTableServerState).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 17:00:34 +02:00
Matthieu
fd5d3fe36f refactor(sites) : decouple module Sites via SiteInterface + leaks groupes user:list
- Introduit Shared/Domain/Contract/SiteInterface que Site implemente
- SiteAwareInterface + User.php typent contre SiteInterface (plus d'import
  direct Core -> Sites, respect regle CLAUDE.md 138)
- Exception SiteNotAuthorizedException deplacee dans Shared/, alias
  retrocompat dans le module
- Retire `sites` et `currentSite` des groupes `user:list` et `user:rbac:write`
  (info leak via /api/users, escalade core.users.manage -> sites.manage)
- User::$sites et User::$currentSite en fetch LAZY (N+1 sur /api/users paginee)
2026-04-20 16:46:27 +02:00
d137828919 feat(sites) : API CRUD + rattachement User<->Site + admin (ticket 2/4)
Exposition de Site via API Platform (5 operations RBAC sites.view/sites.manage),
relation User.sites (M2M user_site EAGER) + User.currentSite (M2O nullable,
ON DELETE SET NULL). Endpoint PATCH /api/me/current-site via ressource
virtuelle + processor (SiteNotAuthorizedException → 403). UserRbacProcessor
etendu avec gardes post-persist : auto-reset si currentSite retire, auto-select
premier site si null + sites non vide.

Page /admin/sites (DataTable + drawer creation/edition + modale suppression).
UserRbacDrawer etendu avec section "Sites autorises". Colonne "Sites" ajoutee
dans la table /admin/users (liste des noms separes par virgule). Sidebar
entree Sites (module: sites, permission: sites.view).

Refactor adresse : split full_address en street + complement (nullable) + getter
computed Site::getFullAddress() multi-lignes. Migration ALTER dediee pour
compat devs ayant deja joue le ticket 1. Fixtures avec vraies adresses
(Chatellerault/Fontenet/Pommevic).

Doctrine : inversedBy synchrone User.sites <-> Site.users pour maintenir la
collection inverse en memoire. User::switchCurrentSite() porte la garde
domaine (throw SiteNotAuthorizedException), aligne sur Role::ensureDeletable.
Helper skipIfSitesModuleDisabled centralise dans AbstractApiTestCase.

Tests : 182/182 (182/182 aussi module desactive, 2 skipped). 29 nouveaux tests
PHPUnit (CRUD API, switch currentSite, cascade DB, /api/me enrichi, extension
/rbac, gardes structurelles fullAddress/currentSite ignores, anti-cycle
Site.users). 11 tests Vitest sur la validation hex couleur.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 10:09:05 +02:00
8590e3e850 feat(sites) : brique fondatrice du module Sites (ticket 1/4)
Module Sites optionnel et desactivable via config/modules.php.
Entite Site (nom unique, ville, CP FR, couleur hex, adresse),
repository + impl Doctrine, migration racine (namespace DoctrineMigrations
conforme exception CLAUDE.md), fixtures idempotentes (Chatellerault,
Saint-Jean, Pommevic), permissions RBAC sites.view/sites.manage.
Tests unitaires + validation via KernelTestCase (UniqueEntity, regex
hex et CP, NotBlank, Length).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 15:45:33 +02:00