Skip to content

stats(admin): K1 — Taux de déclaration #3214

@gary-van-woerkens

Description

@gary-van-woerkens

Contexte métier

Le taux de déclaration mesure le pourcentage d'entreprises qui ont envoyé leur déclaration d'égalité professionnelle (index égalité H/F) sur une année donnée, parmi celles qui y sont légalement obligées.

Objectif pour la DGT : suivre en temps réel l'avancement de la campagne annuelle.

À quoi ça ressemble à l'écran

Une tuile DSFR (fr-tile) dans le dashboard admin. Elle affiche un gros pourcentage (la valeur de l'année en cours), et juste en dessous, en plus petit, l'écart par rapport à l'année précédente et les valeurs absolues qui ont servi au calcul.

┌──────────────────────────────────┐
│ Taux de déclaration 2026         │  ← titre
│                                  │
│           73,4 %                 │  ← valeur de l'année (gros)
│                                  │
│   ▲ +2,1 pts vs 2025             │  ← badge vert/rouge selon le sens
│   4 213 / 5 738 entreprises      │  ← numérateur / dénominateur
└──────────────────────────────────┘

Le but : qu'un agent DGT ouvre la page et comprenne en 2 secondes "où on en est" sur la campagne, et "est-ce qu'on progresse vs l'an dernier".

Page d'accueil

Page /admin/stats (à créer si elle n'existe pas). La tuile vit dans une grille de KPIs partagée avec d'autres tiles chiffrées (#3219 K8, plus tard K12).

Données à calculer

1. La valeur principale — pourcentage entre 0 et 100, 1 décimale

Élément Détail
Numérateur Nombre de déclarations soumises pour l'année. Source : table declarations, filtrée sur status = 'submitted' et year = {année sélectionnée}.
Dénominateur Nombre d'entreprises légalement obligées de déclarer cette année-là. Source : table gipMdsData (fichier annuel fourni par le GIP-MDS, l'organisme qui pré-calcule les indicateurs à partir des DSN), filtrée sur year = {année sélectionnée}, puis restreinte aux entreprises obligées.
Règle d'obligation Une fonction isObligatedForYear(size, year) à créer dans ~/modules/domain. Règles métier : moins de 50 salariés → pas obligé (déclaration volontaire) ; 50 à 99 → obligé une année sur trois (triennal) ; 100 et plus → obligé tous les ans.
Calcul (numérateur / dénominateur) × 100

2. La variation vs année précédente — badge DSFR

Élément Détail
Origine Même calcul que la valeur principale, mais sur year - 1.
Calcul valeur(année) - valeur(année - 1), exprimée en points de pourcentage (pas en %).
Affichage Badge DSFR avec flèche : ↑ +2,1 pts en vert (fr-badge--success) si positif, ↓ -2,1 pts en rouge (fr-badge--error) si négatif, = 0 pt neutre si nul. Si pas d'historique pour year-1 → on n'affiche pas le badge.

3. Le sous-texte — valeurs absolues

Élément Détail
Origine Numérateur et dénominateur déjà calculés au-dessus.
Affichage {numérateur} / {dénominateur} entreprises, avec espaces fines insécables pour les milliers (ex : 4 213 / 5 738 entreprises).

Filtres qui font varier la tuile

Filtre Composant Effet sur le calcul
Année <select> DSFR placé en barre de filtres en haut du dashboard (cette barre est mutualisée avec les autres tiles K8/K12, donc à intégrer une seule fois). Liste : année courante + 3 précédentes. Change la valeur {année sélectionnée} dans toutes les requêtes (numérateur, dénominateur, calcul de la variation N-1).
Tranche d'effectif <CompanySizeFilter> produit par la task #3212 (composant réutilisable). Valeurs : "Toutes tailles", "50-99", "100-149", "150-249", "250+". Le filtre s'applique à la fois au numérateur ET au dénominateur, pour que les deux périmètres restent cohérents (sinon le ratio n'a pas de sens).

Procédure tRPC

À ajouter au router admin.ts existant (src/server/api/routers/admin.ts) — ne pas créer un nouveau router.

admin.getCampaignStats({
  year: number,
  sizeRange?: CompanySizeRange,
})  {
  totalObligated: number,         // dénominateur (entreprises obligées)
  totalSubmitted: number,         // numérateur (déclarations soumises)
  submissionRate: number,         // pourcentage 0..100
  previousYearRate: number | null // même calcul sur year-1, null si pas d'historique
}

Procédure protégée par adminProcedure (middleware déjà existant dans src/server/api/trpc.ts).

Livrables

  • Fonction domaine isObligatedForYear dans ~/modules/domain + tests unitaires couvrant les 3 règles (volontaire, triennal, annuel)
  • Procédure tRPC admin.getCampaignStats + tests
  • Composant <AdminKpiTile title value subtitle delta /> à créer dans ~/modules/admin/ (réutilisable par stats(admin): K8 — Taux d'écart ≥ 5% #3219 K8 et plus tard K12)
  • Page /admin/stats à créer si elle n'existe pas, avec la barre de filtres (année + effectif) et la tuile K1
  • Test Playwright : la tuile s'affiche, les filtres modifient bien la valeur

Note perf

L'optimisation perf (mise en cache / vue matérialisée + cron de refresh) sera traitée dans une task devops dédiée, une fois les requêtes des KPIs stabilisées. À ce stade, la procédure tRPC requête directement les tables — pas de matérialisation.

Références

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

Status

In progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions