Files
Lesstime/docs/superpowers/specs/2026-05-22-employee-management-reorg-design.md
Matthieu 2a0b202d32 feat(absences) : avancement module absences + suppression du portail client
Deux lots regroupés sur la branche feat/absence-management.

Suppression complète du portail client :
- retire ROLE_CLIENT (security.yaml) ; User::getRoles() ajoute toujours ROLE_USER
- supprime l'entité ClientTicket (+ repo, states, relations), User.client et
  User.allowedProjects, NotificationService, ProjectAllowedExtension, le bloc
  ROLE_CLIENT de MailAccessChecker
- front : pages /portal, layout portal, composants client-ticket/,
  AdminClientTicketTab, services/dto/i18n/docs associés
- fixtures : retire les users client-liot / client-acme
- migration Version20260522110000 (drop client_ticket, user_allowed_projects,
  colonnes liées ; task_document.task_id -> NOT NULL)
- tests : retire les cas obsolètes testant le blocage des clients sur le mail

Module gestion des absences (WIP) :
- entités / migrations (Version20260521160000, Version20260522090000)
- pages absences.vue / team-absences.vue, composants frontend/components/absence/
- services front, AccrueLeaveCommand, PublicHolidayController

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 11:31:31 +02:00

5.7 KiB

Réorganisation de la gestion des employés (module Absences)

Date : 2026-05-22 Branche : feat/absence-management Statut : design approuvé, prêt pour plan d'implémentation

Contexte

Aujourd'hui, le UserDrawer (admin → utilisateurs) porte deux responsabilités mélangées :

  1. l'administration du compte (nom, mot de passe, rôles, client, projets) ;
  2. tout le détail RH/employé : case « Employé » + un bloc de champs (date d'embauche, date de sortie, type de contrat, situation familiale, temps de travail, CP annuels, début de période de référence, solde CP initial, nombre d'enfants).

Ces champs existent déjà sur l'entité User (backend) et dans le DTO UserData/UserWrite (frontend). La persistance se fait via l'API user (PATCH /api/users/{id}).

On veut séparer ces deux préoccupations : le UserDrawer ne décide plus que si un utilisateur est un employé ; l'édition des informations RH se fait dans un espace dédié, dans le module Absences.

Objectifs

  • UserDrawer : ne conserver que la case à cocher « Employé ».
  • team-absences : ajouter un onglet « Employés » (la page est déjà middleware: ["admin"], donc admin-only).
  • L'onglet liste les utilisateurs marqués isEmployee avec leurs soldes de congés.
  • Un drawer dédié permet d'éditer les informations RH d'un employé.

Hors périmètre : création d'utilisateur (reste dans l'admin), modification du flag isEmployee ailleurs que dans le UserDrawer, backend (déjà en place).

Composants

1. frontend/components/user/UserDrawer.vue

  • Supprimer le bloc détaillé employé (hireDate, endDate, contractType, familySituation, workTimeRatio, annualLeaveDays, referencePeriodStart, initialLeaveBalance, nbChildren).
  • Conserver uniquement la case isEmployee (« Employé (soumis à la gestion des absences) »).
  • Le payload de sauvegarde du user n'envoie plus les champs détaillés, pour ne pas les écraser. Il continue d'envoyer isEmployee et les champs de compte existants.
  • Nettoyer l'état du formulaire et les imports devenus inutiles (options contrat / situation familiale si elles ne servent plus que là).

2. frontend/pages/team-absences.vue — onglet « Employés »

  • Ajouter un 4ᵉ onglet dans tabs : { key: 'employees', label: t('absences.admin.tabs.employees'), icon: 'mdi:account-group' }.
  • Slot #employees : MalioDataTable avec les colonnes Nom · Contrat · CP pris · CP restants.
  • Clic sur une ligne → ouvre EmployeeDrawer avec l'utilisateur sélectionné (cohérent avec l'onglet Demandes qui ouvre le détail au clic ligne).
  • Chargement des données :
    • usersService.getAll() filtré sur isEmployee === true ;
    • absenceService.getBalances({ type: 'cp' }) → map par user.id pour récupérer taken (CP pris) et available (CP restants) ;
    • fusion en lignes de tableau (taken/available à si pas de solde CP pour l'employé).
  • Recharger la liste après saved du drawer.

3. frontend/components/absence/EmployeeDrawer.vue (nouveau)

  • Props : modelValue: boolean, user: UserData | null.
  • Events : update:modelValue, saved.
  • Formulaire en composants Malio :
    • MalioDate : hireDate, endDate (valeurs ISO YYYY-MM-DD) ;
    • MalioSelect : contractType (CDI/CDD/Stage/Alternance/Autre), familySituation (Célibataire/Marié/Pacsé/Divorcé/Veuf) ;
    • MalioInputText : workTimeRatio, annualLeaveDays, referencePeriodStart (MM-DD), initialLeaveBalance, nbChildren.
  • À l'ouverture, initialiser le formulaire depuis props.user ; remettre à jour si user change.
  • Sauvegarde : usersService.update(user.id, { …champs employé }) ; à la réussite, émettre saved et fermer.
  • En-tête : nom de l'employé.

4. i18n (frontend/i18n/locales/fr.json)

Nouvelles clés regroupées sous absences.admin.employees.* (et l'onglet sous absences.admin.tabs.employees) :

  • onglet : absences.admin.tabs.employees = « Employés » ;
  • colonnes liste : absences.admin.employees.columns.{name,contract,cpTaken,cpRemaining} ;
  • drawer : absences.admin.employees.drawer.title + libellés de champs sous absences.admin.employees.fields.*.

Les libellés de contrat et de situation familiale (CDI/CDD/…, Célibataire/Marié/…) actuellement codés en dur dans UserDrawer sont déplacés avec leur drawer ; on les passe en clés i18n à cette occasion.

Flux de données

UserDrawer  --PATCH isEmployee-->  User (backend)
                                      |
team-absences (onglet Employés)       |
   getAll() ∩ isEmployee  <-----------+
   getBalances({type:'cp'})  --> map user.id -> {taken, available}
   => lignes tableau (nom, contrat, CP pris, CP restants)
        |
        clic ligne
        v
EmployeeDrawer(user)  --usersService.update--> User (backend)
        |
        @saved --> recharge liste

Découpage / responsabilités

  • UserDrawer : compte + flag employé. Ne connaît plus le détail RH.
  • Onglet Employés : vue dérivée en lecture (jointure users ⋈ soldes), aiguille vers le drawer.
  • EmployeeDrawer : seule unité qui édite les champs RH ; interface claire (user en entrée, saved en sortie), testable isolément.

Vérification

  • UserDrawer : ouvrir un user, vérifier qu'il ne reste que la case « Employé », cocher/décocher et sauvegarder sans perte des autres champs RH (qui ne sont plus envoyés).
  • Onglet Employés : la liste affiche les users isEmployee avec CP pris/restants cohérents avec l'onglet Soldes.
  • EmployeeDrawer : éditer un employé (dates en JJ/MM/AAAA, selects FR), sauvegarder, vérifier la persistance (recharge) et l'absence d'erreurs console.
  • Vérification navigateur via Chrome DevTools sur les trois écrans.