Skip to content

feat(admin): referent management (CRUD, import/export, public API)#3198

Merged
LucasCharrier merged 13 commits intoalphafrom
feat/issue-3182-admin-referents
Apr 16, 2026
Merged

feat(admin): referent management (CRUD, import/export, public API)#3198
LucasCharrier merged 13 commits intoalphafrom
feat/issue-3182-admin-referents

Conversation

@LucasCharrier
Copy link
Copy Markdown
Contributor

Summary

  • Add full referent management page in admin backoffice: paginated table with search (name, region, county), sortable columns, row selection, batch delete
  • Create/edit modals with DSFR form (region → county dependency, email/URL type toggle, substitute fields)
  • JSON import (replaces all data) and JSON/CSV export
  • Public API route GET /api/public/referents-egalite-professionnelle?format=json|csv
  • Admin sidebar navigation (Accueil, Référents)
  • Shared infrastructure: DsfrTable, useDsfrModal hook, regions/counties domain dictionaries

Based on feat/issue-3178-admin-role.

Closes #3182

Note: Drizzle migration needs to be generated manually (pnpm db:generate) — drizzle-kit requires a TTY for interactive conflict resolution.

Quality gates

  • Typecheck (0 errors)
  • Tests (148 files, 1123 tests)
  • Lint / Format
  • Structural audit (16 rules)
  • RGAA accessibility audit (13 themes)
  • Security audit (OWASP Top 10)

Generated with Claude Code

@LucasCharrier LucasCharrier requested review from a team as code owners April 9, 2026 15:39
@revu-bot revu-bot Bot requested a review from revu-bot April 9, 2026 15:39
Copy link
Copy Markdown
Collaborator

@revu-bot revu-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ PR Review Skipped

1 validation issue found. Review thresholds can be adjusted in .revu.yml.

See why it was skipped and detailed metrics

Issues Found

1. This PR changes 47 files, which exceeds the limit of 25 files.

Suggestion: Consider breaking this PR into smaller, more focused changes. Large PRs are harder to review effectively and may contain unrelated changes.

PR Metrics

  • Total files changed: 47
  • Reviewable files: 47
  • Diff size: 3316 lines
  • Documentation files: 0
  • Largest file change: 280 lines
  • Addition/Deletion ratio: 417.71

This validation helps ensure the bot focuses on PRs where automated review provides the most value.

@LucasCharrier LucasCharrier changed the base branch from alpha to feat/issue-3178-admin-role April 9, 2026 15:41
Base automatically changed from feat/issue-3178-admin-role to alpha April 13, 2026 07:35
Copy link
Copy Markdown
Member

@maxgfr maxgfr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

J'ai marqué pas mal de commentaires sur les autres PR, je pense il y a un soucis de rebase

@LucasCharrier LucasCharrier force-pushed the feat/issue-3182-admin-referents branch from 694a7b3 to 7cfa7c2 Compare April 13, 2026 14:42
- Create DsfrTable wrapper component in ~/modules/shared
- Create shared useDsfrModal hook to deduplicate modal open/close logic
- Export useZodForm from shared barrel for consistent imports
- Add REGIONS, COUNTIES, REGIONS_TO_COUNTIES dictionaries to domain layer
- Create AdminNavigation component with DSFR sidemenu pattern
- Update admin layout to include sidebar + content grid
- Remove duplicate fr-container from AdminHomePage
…blic API

- Add referents table to Drizzle schema with referent_type enum
- Create adminReferents tRPC router (search, create, update, delete, import, exportAll)
- Build AdminReferentsPage with paginated table, search, sort, batch delete
- Add create/edit modals with region-dependent county select
- Add JSON import modal with validation
- Add JSON/CSV export buttons
- Create public API route /api/public/referents-egalite-professionnelle
- Add Zod schemas, types, constants for the referents module
- Add unit tests for schemas, constants, and page component
- Add E2E test for non-admin redirect

Closes #3182
@LucasCharrier LucasCharrier force-pushed the feat/issue-3182-admin-referents branch from 7cfa7c2 to f159e73 Compare April 14, 2026 07:18
Server files imported shared constants through the `~/modules/shared`
barrel, which also re-exports client-only hooks (useZodForm, useDsfrModal,
useFileUploadForm). Turbopack pulled those hooks into the RSC bundle and
failed on useRef/useForm. Import directly from uploadConfig instead.
The test assumed a non-admin logged-in user but Playwright shares a single
AUTH_FILE (admin). Rewrite to follow admin-declarations.e2e.ts:
happy-path admin access + unauthenticated redirect to /login.
Adds unit tests for all adminReferentsRouter procedures (search, create,
update, delete, import, exportAll, auth guard) and for the public
referents-egalite-professionnelle route (JSON default, CSV formatting and
headers). Increases Sonar new-code coverage above the 60% gate.
…ort)

Also fix lint errors: unused CountyCode import in ReferentFormFields,
unused formatShortDate in DeclarationsSection, and replace the then-property
in the router test mock with a Proxy to satisfy biome/noThenProperty.
Schema was pushed in CI via db:push but no migration file existed, so the
table would not be created in envs that use db:migrate (prod/preprod).
Add 0026_add_referents_table.sql with the referent_type enum, the
app_referent table and the region index.
Without fr-btn--icon-left, DSFR treats the button as icon-only and hides
the text, making the action unclear.
DSFR expects the `fr-icon-X` class to come before `fr-btn--icon-left`. When
the modifier is placed before the icon class, some DSFR rules treat the
button as icon-only and hide the text.
DSFR applies max-width:2rem;overflow:hidden to buttons with fr-icon-*
inside a fr-btns-group unless the group itself has fr-btns-group--icon-left
(or -right). Without this, the button text is visually hidden even when
the button has fr-btn--icon-left.
@Viczei
Copy link
Copy Markdown
Contributor

Viczei commented Apr 14, 2026

J'ai marqué pas mal de commentaires sur les autres PR, je pense il y a un soucis de rebase

Oui ca fait beaucoup de fichiers modifié pour une page de referents

@LucasCharrier
Copy link
Copy Markdown
Contributor Author

J'ai marqué pas mal de commentaires sur les autres PR, je pense il y a un soucis de rebase

Oui ca fait beaucoup de fichiers modifié pour une page de referents

Le commentaire de Max c'était hier, mais normarlement là tout est bien rebase. C'est pas juste une page avec les referents, il y a un systeme de recherche, d'ajout, d'import de csv, il y la page d'admin et d'affichage publique. C'est vrai qu'il y a beaucoup de fichiers mais comme on subdivise tout en petit composants par fichier, ça fait rapidement plein de fichier au total.

Comment thread packages/app/.env.example
CLAMAV_HOST="localhost"
CLAMAV_PORT="3310"
ADMIN_EMAILS="test@fia1.fr"
EGAPRO_AUDIT_CLEANUP_TOKEN="change-me-to-a-32-chars-minimum-token"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah oui bien vu 👍

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pourquoi on met pas ça dans un router trpc ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Le CSV bloque : tRPC renvoie du JSON enveloppé, pas moyen de setter Content-Disposition pour le téléchargement. Et pour un endpoint /api/public/ consommé par des clients externes (curl, Python, data.gouv), l'enveloppe tRPC + URL couplée au nom de procédure c'est pas le bon contrat. tRPC serait pertinent pour un usage interne.

Comment thread packages/app/.env.example
CLAMAV_HOST="localhost"
CLAMAV_PORT="3310"
ADMIN_EMAILS="test@fia1.fr"
EGAPRO_AUDIT_CLEANUP_TOKEN="change-me-to-a-32-chars-minimum-token"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

à quoi ca sert concretementt ? et pouquoi on l'a pas register dans le env.js ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ou alors, c'est une variable qu'on avait oublié d'ajouter :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C'est juste bizarre d'avoir une variable clean_up comme ça, on pourra en discuter une next time

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oui c'est plus un oubli, c'est un token pour sécuriser l'api de nettoyage des logs d'audit

@tokenbureau
Copy link
Copy Markdown

tokenbureau Bot commented Apr 16, 2026

@LucasCharrier LucasCharrier merged commit 0a889e8 into alpha Apr 16, 2026
15 checks passed
@LucasCharrier LucasCharrier deleted the feat/issue-3182-admin-referents branch April 16, 2026 08:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Backoffice - Liste des référents

4 participants