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>
This commit is contained in:
Matthieu
2026-05-22 11:31:31 +02:00
parent de98924fd3
commit 2a0b202d32
109 changed files with 3918 additions and 3656 deletions

View File

@@ -351,63 +351,6 @@
"error": "Erreur de connexion à Gitea.",
"notConfigured": "Gitea non configuré pour ce projet."
},
"portal": {
"title": "Portail client",
"projects": "Vos projets",
"noProjects": "Aucun projet disponible.",
"openTickets": "tickets ouverts",
"newTicket": "Nouveau ticket",
"ticketDetail": "Détail du ticket",
"backToProject": "Retour au projet",
"submitTicket": "Soumettre le ticket",
"ticketCreated": "Ticket soumis avec succès."
},
"clientTicket": {
"title": "Tickets",
"new": "Nouveau ticket",
"created": "Ticket créé avec succès.",
"deleted": "Ticket supprimé avec succès.",
"updated": "Ticket mis à jour avec succès.",
"statusUpdated": "Statut du ticket mis à jour.",
"type": {
"bug": "Bug",
"improvement": "Amélioration",
"other": "Autre"
},
"status": {
"new": "Nouveau",
"in_progress": "En cours",
"done": "Terminé",
"rejected": "Rejeté"
},
"fields": {
"title": "Titre",
"description": "Description",
"url": "URL de la page",
"urlPlaceholder": "https://example.com/page-concernee",
"type": "Type",
"project": "Projet"
},
"confirmDelete": "Êtes-vous sûr de vouloir supprimer ce ticket ?",
"rejectComment": "Commentaire de rejet",
"rejectCommentRequired": "Un commentaire est requis pour rejeter un ticket.",
"linkedTicket": "Lié au ticket client CT-{number}",
"description": "Description",
"url": "URL (page concernée)",
"statusComment": "Commentaire de statut",
"statusChanged": "Statut mis à jour",
"confirmDeleteMessage": "Êtes-vous sûr de vouloir supprimer ce ticket ? Cette action est irréversible.",
"linkedTooltip": "Lié au ticket client {number}",
"rejectionRequired": "Un commentaire est requis pour rejeter un ticket",
"noTickets": "Aucun ticket.",
"allStatuses": "Tous les statuts",
"allProjects": "Tous les projets",
"submittedBy": "Soumis par",
"createdAt": "Créé le",
"adminTab": "Tickets client",
"selectType": "Type de ticket",
"changeStatus": "Changer le statut"
},
"notification": {
"title": "Notifications",
"markAllRead": "Tout marquer comme lu",
@@ -614,5 +557,195 @@
"hasAttachments": "Pièces jointes",
"unread": "non lu | non lus",
"remoteImagesBlocked": "Les images distantes sont masquées pour votre sécurité."
},
"absences": {
"title": "Mes absences",
"teamTitle": "Absences de l'équipe",
"newRequest": "Nouvelle demande",
"daySingular": "jour",
"daysPlural": "jours",
"noBalance": "Aucun solde à afficher.",
"noRequests": "Aucune demande.",
"remaining": "restants",
"acquired": "acquis",
"acquiredN1": "Acquis (N-1)",
"acquiringN": "En cours d'acquisition (N)",
"acquiringHint": "posables par anticipation",
"taken": "pris",
"pending": "en attente",
"available": "disponible",
"types": {
"cp": "Congés payés",
"mariage_pacs": "Mariage / PACS",
"conge_parental": "Congé parental",
"deces": "Décès proche",
"maladie": "Arrêt maladie"
},
"status": {
"pending": "En attente",
"approved": "Approuvée",
"rejected": "Refusée",
"cancelled": "Annulée"
},
"halfDay": {
"matin": "Matin",
"apres_midi": "Après-midi"
},
"table": {
"type": "Type",
"period": "Période",
"days": "Jours",
"status": "Statut",
"employee": "Salarié",
"year": "Année",
"requestedAt": "Demandé le",
"actions": "Actions"
},
"filters": {
"allStatuses": "Tous les statuts",
"allTypes": "Tous les types",
"allYears": "Toutes les années",
"allEmployees": "Tous les salariés"
},
"form": {
"type": "Type d'absence",
"startDate": "Date de début",
"endDate": "Date de fin",
"startHalfDay": "Demi-journée (début)",
"endHalfDay": "Demi-journée (fin)",
"halfDayCheckbox": "Demi-journée",
"reason": "Motif",
"reasonPlaceholder": "Précisez le motif si nécessaire…",
"justification": "Justificatif",
"computed": "{days} décompté(s)",
"balanceAfter": "Solde restant après cette demande : {value}",
"negativeWarning": "Cette demande dépasse votre solde disponible.",
"noticeWarning": "Le délai de prévenance ({days} jours) n'est pas respecté.",
"submit": "Soumettre la demande",
"justificationRequired": "Un justificatif est requis pour ce type d'absence.",
"fullDay": "Journée entière",
"balanceAt": "Solde au {date}",
"balanceAfterValidation": "Solde après validation",
"duration": "Durée de la demande",
"commentPlaceholder": "Écrire un commentaire…",
"serverError": "La demande n'a pas pu être enregistrée.",
"errors": {
"typeRequired": "Veuillez choisir un type d'absence.",
"startRequired": "Veuillez indiquer une date de début.",
"endRequired": "Veuillez indiquer une date de fin.",
"endBeforeStart": "La date de fin doit être après la date de début.",
"zeroDays": "La période sélectionnée ne décompte aucun jour.",
"justificationRequired": "Un justificatif est obligatoire pour ce type d'absence."
}
},
"detail": {
"title": "Détail de la demande",
"timeline": "Historique",
"created": "Demande créée",
"reviewed": "Traitée par {name}",
"rejectionReason": "Motif du refus",
"downloadJustification": "Télécharger le justificatif",
"cancel": "Annuler ma demande",
"cancelConfirm": "Annuler cette demande ?"
},
"review": {
"approve": "Valider",
"reject": "Refuser",
"rejectTitle": "Refuser la demande",
"rejectReasonLabel": "Motif du refus",
"rejectReasonPlaceholder": "Expliquez la raison du refus…",
"confirm": "Confirmer"
},
"admin": {
"tabs": {
"requests": "Demandes",
"calendar": "Calendrier",
"balances": "Soldes",
"employees": "Employés"
},
"kpis": {
"pending": "En attente",
"todayAbsent": "Absents aujourd'hui",
"weekAbsent": "Absents cette semaine"
},
"balancesTable": {
"employee": "Salarié",
"type": "Type",
"period": "Période",
"acquired": "Acquis (N-1)",
"acquiring": "En cours (N)",
"taken": "Pris",
"pending": "En attente",
"available": "Disponible",
"adjust": "Ajuster"
},
"adjust": {
"title": "Ajuster le solde",
"acquired": "Acquis (N-1)",
"acquiring": "En cours d'acquisition (N)",
"taken": "Pris",
"save": "Enregistrer"
},
"employees": {
"columns": {
"name": "Nom",
"contract": "Contrat",
"cpTaken": "CP pris",
"cpRemaining": "CP restants"
},
"empty": "Aucun employé. Cochez « Employé » sur un utilisateur dans l'administration.",
"noContract": "—",
"drawer": {
"title": "Informations employé",
"save": "Enregistrer"
},
"fields": {
"hireDate": "Date d'embauche",
"endDate": "Date de sortie",
"contractType": "Type de contrat",
"familySituation": "Situation familiale",
"workTimeRatio": "Temps de travail (ex : 1.0)",
"annualLeaveDays": "CP annuels (jours)",
"referencePeriodStart": "Début période réf. (MM-DD)",
"initialLeaveBalance": "Solde CP initial",
"nbChildren": "Nombre d'enfants"
},
"contract": {
"cdi": "CDI",
"cdd": "CDD",
"stage": "Stage",
"alternance": "Alternance",
"autre": "Autre"
},
"family": {
"celibataire": "Célibataire",
"marie": "Marié(e)",
"pacse": "Pacsé(e)",
"divorce": "Divorcé(e)",
"veuf": "Veuf(ve)"
}
}
},
"policies": {
"title": "Politiques d'absence",
"subtitle": "Réglez les défauts par type d'absence (convention collective).",
"type": "Type",
"daysPerYear": "Jours / an",
"daysPerEvent": "Jours / événement",
"justificationRequired": "Justificatif requis",
"noticeDays": "Délai prévenance (j)",
"countWorkingDaysOnly": "Jours ouvrés",
"active": "Actif",
"save": "Enregistrer"
},
"toast": {
"created": "Demande d'absence créée.",
"approved": "Demande validée.",
"rejected": "Demande refusée.",
"cancelled": "Demande annulée.",
"justificationUploaded": "Justificatif ajouté.",
"balanceAdjusted": "Solde ajusté.",
"policyUpdated": "Politique mise à jour."
}
}
}