diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 26cfc26..fa8890f 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -26,7 +26,8 @@ "Bash(pip3 install:*)", "Bash(find:*)", "Bash(git add:*)", - "Bash(git commit:*)" + "Bash(git commit:*)", + "Bash(python3:*)" ] } } diff --git a/CLAUDE.md b/CLAUDE.md index d022603..79edb61 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,6 +2,7 @@ ## Mandatory Rules - Any functional change MUST update `doc/` in the same intervention +- Any functional change MUST update the in-app documentation (`frontend/data/documentation-content.ts`) in the same intervention - At the end of every feature addition or functional modification, update this CLAUDE.md to reflect new patterns, rules, or conventions introduced ## Commands @@ -84,6 +85,16 @@ - Keep backend PHP DTOs aligned with frontend TS DTOs (`frontend/services/dto/*`) - Update unit tests when constructor/service signatures change +## In-App Documentation +- Content: `frontend/data/documentation-content.ts` — structured TypeScript data with all user-facing documentation +- Types: `frontend/types/documentation.ts` — DocSection, DocArticle, DocBlock +- Composable: `frontend/composables/useDocumentation.ts` — role-based filtering (employee < site_manager < admin) +- Components: `frontend/components/documentation/` — DocumentationPage, DocumentationSection, DocumentationArticle +- Page: `frontend/pages/documentation.vue` +- 3 access levels: `employee` (ROLE_SELF), `site_manager` (ROLE_USER), `admin` (ROLE_ADMIN) — cumulative (admin sees everything) +- Each section/article has a `requiredLevel` that controls visibility +- When adding or modifying a feature, update the corresponding section in `documentation-content.ts` + ## Language - UI is in French - User communicates in French diff --git a/frontend/components/documentation/DocumentationArticle.vue b/frontend/components/documentation/DocumentationArticle.vue new file mode 100644 index 0000000..8e93211 --- /dev/null +++ b/frontend/components/documentation/DocumentationArticle.vue @@ -0,0 +1,26 @@ + + + diff --git a/frontend/components/documentation/DocumentationPage.vue b/frontend/components/documentation/DocumentationPage.vue new file mode 100644 index 0000000..f7cc1a3 --- /dev/null +++ b/frontend/components/documentation/DocumentationPage.vue @@ -0,0 +1,67 @@ + + + diff --git a/frontend/components/documentation/DocumentationSection.vue b/frontend/components/documentation/DocumentationSection.vue new file mode 100644 index 0000000..03f1c20 --- /dev/null +++ b/frontend/components/documentation/DocumentationSection.vue @@ -0,0 +1,23 @@ + + + diff --git a/frontend/composables/useDocumentation.ts b/frontend/composables/useDocumentation.ts new file mode 100644 index 0000000..086d272 --- /dev/null +++ b/frontend/composables/useDocumentation.ts @@ -0,0 +1,39 @@ +import { documentationSections } from '~/data/documentation-content' +import type { DocAccessLevel, DocSection } from '~/types/documentation' + +const LEVEL_HIERARCHY: Record = { + employee: 0, + site_manager: 1, + admin: 2, +} + +function getUserLevel(roles: string[]): number { + if (roles.includes('ROLE_ADMIN') || roles.includes('ROLE_SUPER_ADMIN')) return 2 + if (roles.includes('ROLE_USER')) return 1 + return 0 +} + +export function useDocumentation() { + const auth = useAuthStore() + const userLevel = computed(() => getUserLevel(auth.user?.roles ?? [])) + + const visibleSections = computed(() => { + return documentationSections + .filter(s => LEVEL_HIERARCHY[s.requiredLevel] <= userLevel.value) + .map(s => ({ + ...s, + articles: s.articles.filter(a => LEVEL_HIERARCHY[a.requiredLevel] <= userLevel.value), + })) + .filter(s => s.articles.length > 0) + }) + + const activeArticleId = ref(null) + + const scrollToArticle = (articleId: string) => { + activeArticleId.value = articleId + const el = document.getElementById(`doc-${articleId}`) + el?.scrollIntoView({ behavior: 'smooth', block: 'start' }) + } + + return { visibleSections, activeArticleId, scrollToArticle } +} diff --git a/frontend/data/documentation-content.ts b/frontend/data/documentation-content.ts new file mode 100644 index 0000000..ae33c2b --- /dev/null +++ b/frontend/data/documentation-content.ts @@ -0,0 +1,568 @@ +import type { DocSection } from '~/types/documentation' + +export const documentationSections: DocSection[] = [ + // ============================================================ + // EMPLOYEE LEVEL + // ============================================================ + { + id: 'connexion', + title: 'Connexion et navigation', + requiredLevel: 'employee', + icon: 'mdi:login', + articles: [ + { + id: 'login', + title: 'Se connecter', + requiredLevel: 'employee', + blocks: [ + { type: 'paragraph', content: 'Pour accéder à l\'application, rendez-vous sur la page de connexion et saisissez vos identifiants.' }, + { type: 'list', content: 'Saisissez votre nom d\'utilisateur\nSaisissez votre mot de passe\nCliquez sur le bouton "Connexion"' }, + { type: 'note', content: 'Si vous ne parvenez pas à vous connecter, contactez votre administrateur RH. Votre compte a peut-être été verrouillé.' }, + ], + }, + { + id: 'navigation', + title: 'Naviguer dans la vue jour', + requiredLevel: 'employee', + blocks: [ + { type: 'paragraph', content: 'La vue jour est votre écran principal. Elle affiche les heures de travail pour une date donnée.' }, + { type: 'list', content: 'Boutons "Hier" / "Aujourd\'hui" / "Demain" pour naviguer rapidement\nSélecteur de date pour choisir une date spécifique\nFiltrage par site si vous avez accès à plusieurs sites' }, + { type: 'paragraph', content: 'Seuls les employés ayant un contrat actif à la date sélectionnée sont affichés.' }, + ], + }, + { + id: 'perimetre', + title: 'Périmètre d\'accès', + requiredLevel: 'employee', + blocks: [ + { type: 'paragraph', content: 'Votre accès dépend du rôle qui vous a été attribué par l\'administrateur.' }, + { type: 'list', content: 'Employé : accès à la saisie de ses propres heures uniquement\nChef de site : accès aux heures des employés de ses sites autorisés + validation\nAdministrateur : accès complet à toutes les fonctionnalités' }, + ], + }, + ], + }, + { + id: 'saisie-heures', + title: 'Saisie des heures', + requiredLevel: 'employee', + icon: 'mdi:clock-time-four-outline', + articles: [ + { + id: 'saisie-time', + title: 'Mode horaire (TIME)', + requiredLevel: 'employee', + blocks: [ + { type: 'paragraph', content: 'En mode horaire, vous saisissez vos heures via des créneaux matin, après-midi et soir.' }, + { type: 'list', content: 'Matin : heure de début et heure de fin\nAprès-midi : heure de début et heure de fin\nSoir : heure de début et heure de fin' }, + { type: 'paragraph', content: 'Le sélecteur de temps fonctionne par tranches de 15 minutes (00, 15, 30, 45). La saisie libre est possible mais sera corrigée automatiquement.' }, + { type: 'note', content: 'Les calculs sont mis à jour automatiquement : heures de jour (06:00–21:00), heures de nuit (00:00–06:00 et 21:00–24:00) et total.' }, + ], + }, + { + id: 'saisie-presence', + title: 'Mode présence (PRESENCE)', + requiredLevel: 'employee', + blocks: [ + { type: 'paragraph', content: 'En mode présence (contrats forfait), vous indiquez simplement si vous étiez présent le matin et/ou l\'après-midi.' }, + { type: 'list', content: 'Cochez "Présent matin" pour indiquer une demi-journée de travail le matin\nCochez "Présent après-midi" pour indiquer une demi-journée l\'après-midi\nChaque demi-journée cochée compte pour 0.5 jour de présence' }, + ], + }, + { + id: 'comprendre-calculs', + title: 'Comprendre les calculs affichés', + requiredLevel: 'employee', + blocks: [ + { type: 'paragraph', content: 'Les colonnes de calcul sont mises à jour automatiquement en fonction de votre saisie.' }, + { type: 'list', content: 'Jour : total des heures dans la plage 06:00–21:00\nNuit : total des heures dans les plages 00:00–06:00 et 21:00–24:00\nTotal : somme des heures de jour et de nuit' }, + ], + }, + ], + }, + { + id: 'saisie-conducteurs', + title: 'Saisie conducteurs', + requiredLevel: 'employee', + icon: 'mdi:truck-outline', + articles: [ + { + id: 'conducteur-heures', + title: 'Saisie des heures conducteur', + requiredLevel: 'employee', + blocks: [ + { type: 'paragraph', content: 'Les conducteurs disposent d\'un écran dédié accessible via le menu "Heures Conducteurs". Ils n\'apparaissent pas sur l\'écran classique des heures.' }, + { type: 'list', content: 'Heures de jour : durée au format HH:MM\nHeures de nuit : durée au format HH:MM\nHeures atelier : durée au format HH:MM\nTotal : calculé automatiquement (jour + nuit + atelier)' }, + ], + }, + { + id: 'conducteur-indemnites', + title: 'Indemnités conducteur', + requiredLevel: 'employee', + blocks: [ + { type: 'paragraph', content: 'En plus des heures, vous pouvez cocher les indemnités correspondant à votre journée.' }, + { type: 'list', content: 'Petit déjeuner\nDéjeuner\nDîner\nNuitée' }, + { type: 'paragraph', content: 'La même logique de validation s\'applique que pour les heures classiques.' }, + ], + }, + ], + }, + { + id: 'absences-validations', + title: 'Absences et validations', + requiredLevel: 'employee', + icon: 'mdi:information-outline', + articles: [ + { + id: 'comprendre-absences', + title: 'Comprendre les absences affichées', + requiredLevel: 'employee', + blocks: [ + { type: 'paragraph', content: 'Quand une absence est posée sur votre journée, elle apparaît dans la colonne dédiée avec un fond coloré selon le type d\'absence.' }, + { type: 'list', content: 'Absence du matin (AM) : verrouille le créneau matin\nAbsence de l\'après-midi (PM) : verrouille les créneaux après-midi et soir\nAbsence journée complète : verrouille tous les créneaux' }, + { type: 'note', content: 'Vous ne pouvez pas modifier les créneaux horaires verrouillés par une absence. Seul un administrateur peut retirer ou modifier l\'absence.' }, + ], + }, + { + id: 'comprendre-validations', + title: 'Comprendre les validations', + requiredLevel: 'employee', + blocks: [ + { type: 'paragraph', content: 'Vos heures passent par un processus de double validation avant d\'être définitivement enregistrées.' }, + { type: 'list', content: 'Validation chef de site : votre chef de site vérifie et valide vos heures. La ligne est alors verrouillée pour vous.\nValidation RH : l\'administrateur RH valide définitivement. La ligne est complètement verrouillée.' }, + { type: 'paragraph', content: 'Une fois validée, vous ne pouvez plus modifier la ligne. Si une correction est nécessaire, contactez votre chef de site ou l\'administrateur RH.' }, + { type: 'note', content: 'Toute vraie modification effectuée par un administrateur remet automatiquement les deux validations à zéro.' }, + ], + }, + ], + }, + + // ============================================================ + // SITE MANAGER LEVEL + // ============================================================ + { + id: 'validation-site', + title: 'Validation de site', + requiredLevel: 'site_manager', + icon: 'mdi:check-decagram-outline', + articles: [ + { + id: 'role-chef-site', + title: 'Rôle du chef de site', + requiredLevel: 'site_manager', + blocks: [ + { type: 'paragraph', content: 'En tant que chef de site, vous êtes responsable de la vérification et de la validation des heures saisies par les employés de votre site.' }, + { type: 'paragraph', content: 'Le workflow de validation suit un circuit en 3 étapes : l\'employé saisit ses heures → le chef de site valide → l\'admin RH valide définitivement.' }, + ], + }, + { + id: 'validation-individuelle', + title: 'Validation individuelle', + requiredLevel: 'site_manager', + blocks: [ + { type: 'paragraph', content: 'Pour valider une ligne d\'heures individuellement :' }, + { type: 'list', content: 'Cochez la case de validation site sur la ligne de l\'employé\nLa ligne est immédiatement verrouillée pour l\'employé\nL\'administrateur RH peut toujours corriger une ligne que vous avez validée' }, + ], + }, + { + id: 'validation-masse', + title: 'Validation en masse', + requiredLevel: 'site_manager', + blocks: [ + { type: 'paragraph', content: 'Pour gagner du temps, vous pouvez valider toutes les lignes en une seule action.' }, + { type: 'list', content: 'Cliquez sur le bouton de validation en masse\nToutes les lignes de la date affichée sont validées d\'un coup\nUtile quand toutes les saisies sont correctes' }, + { type: 'note', content: 'Quand toutes les lignes de votre site sont validées pour une date donnée, les administrateurs RH reçoivent automatiquement une notification.' }, + ], + }, + { + id: 'difference-validations', + title: 'Validation site vs validation RH', + requiredLevel: 'site_manager', + blocks: [ + { type: 'paragraph', content: 'Il est important de comprendre la différence entre les deux niveaux de validation.' }, + { type: 'list', content: 'Validation site : verrouille la ligne pour les employés, mais l\'admin RH peut encore modifier\nValidation RH : verrouillage complet, seul l\'admin peut retirer cette validation\nLe chef de site ne voit pas et ne peut pas agir sur la validation RH' }, + ], + }, + ], + }, + + // ============================================================ + // ADMIN LEVEL + // ============================================================ + { + id: 'administration', + title: 'Administration', + requiredLevel: 'admin', + icon: 'mdi:cog-outline', + articles: [ + { + id: 'gestion-sites', + title: 'Gestion des sites', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Les sites organisent les employés et les accès dans l\'application. Chaque site possède un nom et une couleur utilisée dans toute l\'interface.' }, + { type: 'list', content: 'Créer, modifier ou supprimer un site depuis le menu "Sites"\nL\'ordre d\'affichage est modifiable par glisser-déposer\nLa couleur du site est utilisée pour identifier visuellement les employés' }, + ], + }, + { + id: 'gestion-types-absence', + title: 'Gestion des types d\'absence', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Les types d\'absence définissent les catégories disponibles lors de la pose d\'une absence.' }, + { type: 'list', content: 'Code : identifiant court (max 10 caractères), ex: C, M, AT\nLibellé : nom affiché, ex: Congé, Maladie, Accident du travail\nCouleur : code couleur pour le calendrier et la vue jour\nOption "Compté comme travaillé" : si activé, l\'absence crédite des heures en mode TIME' }, + { type: 'note', content: 'L\'option "Compté comme travaillé" impacte le calcul des heures supplémentaires. En mode TIME, les minutes sont créditées selon le contrat. En mode PRESENCE, aucun crédit n\'est appliqué.' }, + ], + }, + { + id: 'gestion-utilisateurs', + title: 'Gestion des utilisateurs', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Chaque personne qui se connecte à l\'application a un compte utilisateur distinct de sa fiche employé.' }, + { type: 'list', content: 'Nom d\'utilisateur : unique, sert de login\nMot de passe : défini à la création, modifiable\nRôle : Admin (accès complet), User (chef de site), Self (employé)\nSites autorisés : pour les chefs de site, définit leur périmètre\nAssociation employé : lie le compte à une fiche employé\nVerrouillage : un compte verrouillé ne peut plus se connecter' }, + { type: 'note', content: 'Il n\'est pas possible de supprimer un utilisateur (sécurité). Pour bloquer l\'accès, utilisez le verrouillage de compte.' }, + ], + }, + { + id: 'taches-automatiques', + title: 'Tâches automatiques (crons)', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Deux tâches automatiques s\'exécutent quotidiennement pour gérer le report des compteurs.' }, + { type: 'list', content: 'Report congés (02h10) : déclenche le report des congés payés le 1er juin (CDI/CDD) et le 1er janvier (forfait)\nReport RTT (02h15) : déclenche le report du solde RTT le 1er juin' }, + { type: 'note', content: 'Ces tâches sont idempotentes : si elles s\'exécutent plusieurs fois, aucun doublon n\'est créé.' }, + ], + }, + ], + }, + { + id: 'employes-contrats', + title: 'Employés et contrats', + requiredLevel: 'admin', + icon: 'mdi:account-group-outline', + articles: [ + { + id: 'liste-employes', + title: 'Liste et recherche d\'employés', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'La page Employés affiche tous les employés sous forme de cartes.' }, + { type: 'list', content: 'Recherche par nom\nFiltrage par site (multi-sélection)\nFiltrage par statut de contrat : "Avec contrat" (défaut), "Sans contrat", "Tous"\n"Avec contrat" = employés ayant une période de contrat active à la date du jour' }, + ], + }, + { + id: 'creation-employe', + title: 'Créer un employé', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'La création d\'un employé se fait via le drawer d\'ajout.' }, + { type: 'list', content: 'Champs : prénom, nom, site\nNature du contrat : CDI, CDD ou INTERIM\nType de contrat / temps de travail (Forfait, 35h, 39h, etc.)\nDate de début (obligatoire)\nDate de fin (obligatoire pour CDD et INTERIM)' }, + ], + }, + { + id: 'types-contrat', + title: 'Types de contrat', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Le type de contrat détermine le mode de suivi et les règles de calcul appliquées.' }, + { type: 'list', content: 'FORFAIT : suivi en jours (mode PRESENCE), base 218 jours/an\n35 HEURES : suivi horaire (mode TIME), 35h/semaine\n39 HEURES : suivi horaire (mode TIME), 39h/semaine\nCUSTOM : heures personnalisées (ex: 4h, 20h), 1h sup = 1h récup sans bonus\nINTERIM : travail temporaire, pas de récupération ni de congés gérés' }, + { type: 'note', content: 'Le mode de suivi (TIME ou PRESENCE) est lié au type de contrat et ne peut pas être modifié indépendamment.' }, + ], + }, + { + id: 'suivi-contrat', + title: 'Suivi contrat et historique', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'L\'onglet "Suivi contrat" sur la fiche employé affiche l\'historique complet des périodes de contrat.' }, + { type: 'list', content: 'Chaque ligne : nature (CDI/CDD/INTERIM), type de contrat, date début, date fin ou "En cours"\nAjouter un contrat : disponible uniquement si le contrat en cours est clôturé\nClôturer un contrat : définir la date de fin + option "Solde de tout compte"\nSuspension : ajouter une période de suspension avec dates et commentaire' }, + { type: 'note', content: 'La case "Soldé dans le solde de tout compte" remet le report des congés à 0 pour l\'exercice suivant.' }, + ], + }, + { + id: 'statut-conducteur', + title: 'Statut conducteur', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Le statut conducteur est un flag activé sur une période de contrat. Un employé peut changer de statut conducteur selon la période.' }, + { type: 'paragraph', content: 'Un employé conducteur apparaît uniquement sur l\'écran "Heures Conducteurs" et non sur l\'écran "Heures" classique.' }, + ], + }, + ], + }, + { + id: 'double-validation', + title: 'Saisie et double validation', + requiredLevel: 'admin', + icon: 'mdi:shield-check-outline', + articles: [ + { + id: 'validation-rh', + title: 'Validation RH', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'La validation RH est le niveau de validation le plus élevé, réservé aux administrateurs.' }, + { type: 'list', content: 'Verrouille complètement la ligne (heures et absences)\nSeul un administrateur peut retirer cette validation\nPeut être appliquée individuellement ou en masse' }, + ], + }, + { + id: 'regles-reinitialisation', + title: 'Règles de réinitialisation', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Les validations sont automatiquement réinitialisées dans certaines conditions.' }, + { type: 'list', content: 'Toute vraie modification d\'une ligne remet les deux validations (site et RH) à faux\nUn enregistrement sans changement réel préserve les validations existantes\nLa date de modification est mise à jour uniquement quand un employé modifie ses propres heures' }, + { type: 'note', content: 'La date de modification est visible uniquement par les administrateurs, sous le nom de l\'employé dans la vue jour.' }, + ], + }, + ], + }, + { + id: 'vue-semaine-hs', + title: 'Vue semaine et heures supplémentaires', + requiredLevel: 'admin', + icon: 'mdi:calendar-week', + articles: [ + { + id: 'vue-semaine', + title: 'Vue semaine', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'La vue semaine est réservée aux administrateurs. Elle affiche une synthèse hebdomadaire par employé avec les heures supplémentaires calculées.' }, + { type: 'list', content: 'Filtrage par site et par employé\nDétail par jour avec totaux hebdomadaires\nColonnes de calcul : base, heures sup 25%, heures sup 50%, total récupération' }, + ], + }, + { + id: 'calcul-hs', + title: 'Calcul des heures supplémentaires', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Les règles de calcul des heures supplémentaires dépendent du type de contrat.' }, + { type: 'list', content: 'Contrats ≤ 35h : +25% de 35h à 43h, +50% au-delà de 43h\nContrats ≥ 39h : +25% de 39h à 43h, +50% au-delà de 43h\nContrats CUSTOM (4h, 25h, etc.) : 1h supplémentaire = 1h de récupération, pas de bonus\nINTERIM : aucune récupération, aucun bonus' }, + { type: 'note', content: 'En cas de déficit hebdomadaire (heures travaillées < heures contrat), le déficit est déduit du cumul RTT : d\'abord des heures à 50%, puis des heures à 25%.' }, + ], + }, + { + id: 'vue-semaine-conducteurs', + title: 'Vue semaine conducteurs', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'La vue semaine conducteurs affiche des colonnes spécifiques.' }, + { type: 'list', content: 'Totaux jour / nuit / atelier par jour et par semaine\nPanier de nuit (PN) : affiché quand heures nuit > heures jour OU nuit ≥ 4h\nCompteurs hebdomadaires : petit déjeuner, déjeuner, dîner, nuitée\nRTT calculé sur jour + nuit + atelier (au lieu des créneaux classiques)' }, + ], + }, + ], + }, + { + id: 'absences-calendrier', + title: 'Absences et calendrier', + requiredLevel: 'admin', + icon: 'mdi:calendar-blank', + articles: [ + { + id: 'poser-absence', + title: 'Poser une absence', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Les absences peuvent être posées depuis la vue jour des heures ou depuis le calendrier.' }, + { type: 'list', content: 'Journée complète : efface toutes les plages horaires\nDemi-journée matin (AM) : efface le créneau matin\nDemi-journée après-midi (PM) : efface les créneaux après-midi et soir' }, + { type: 'paragraph', content: 'Les absences sont stockées par jour : une absence de plusieurs jours est automatiquement découpée en entrées quotidiennes.' }, + ], + }, + { + id: 'effet-absences-heures', + title: 'Effet sur les heures', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'L\'impact d\'une absence sur les heures dépend du type d\'absence et du mode de suivi.' }, + { type: 'list', content: 'Standard : efface les créneaux horaires correspondants\nSi "Compté comme travaillé" en mode TIME : crédite des minutes selon le contrat actif\nSi "Compté comme travaillé" en mode PRESENCE : aucun crédit (seules les cases cochées comptent)' }, + { type: 'note', content: 'Les absences comptées comme travaillées impactent le calcul des heures supplémentaires et du RTT.' }, + ], + }, + { + id: 'calendrier-mensuel', + title: 'Calendrier mensuel', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Le calendrier offre une vue d\'ensemble mensuelle des absences de tous les employés.' }, + { type: 'list', content: 'Code couleur par type d\'absence\nDemi-journée : affichage en dégradé diagonal\nJournée complète : fond plein\nJours fériés : fond bleu clair\nFiltres par site et par employé\nNavigation par mois (précédent / suivant)' }, + ], + }, + ], + }, + { + id: 'conges-payes', + title: 'Congés payés', + requiredLevel: 'admin', + icon: 'mdi:umbrella-beach-outline', + articles: [ + { + id: 'regles-cdi-cdd', + title: 'Règles CDI/CDD non-forfait', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Pour les contrats CDI et CDD (hors forfait), l\'exercice de congés va du 1er juin (N-1) au 31 mai (N).' }, + { type: 'list', content: 'Acquisition annuelle : 25 jours + 5 samedis\nAcquisition mensuelle : 2,08 jours + 0,42 samedi par mois\nProratisation en cas de début/fin ou suspension en cours de mois\nContrat 4h : 10 jours annuels, 0 samedi, 0,83 jour/mois' }, + ], + }, + { + id: 'regles-forfait', + title: 'Règles FORFAIT', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Pour les contrats forfait, l\'exercice suit l\'année civile (1er janvier au 31 décembre).' }, + { type: 'list', content: 'Calcul : jours ouvrés de l\'année − 218 + bonus weekend/férié\nBonus : 1 jour par jour travaillé un weekend ou jour férié (0.5 si demi-journée)\nPas de samedis\nPas de jours en cours d\'acquisition' }, + ], + }, + { + id: 'maladie-longue', + title: 'Arrêt maladie long', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'En cas d\'arrêt maladie de plus d\'un mois, les règles d\'acquisition sont modifiées.' }, + { type: 'list', content: 'Premier mois de maladie : acquisition normale\nAprès le premier mois : acquisition réduite (facteur 0,80)\nDétection automatique à partir des absences MALADIE consécutives (tolérance de gap ≤ 3 jours)' }, + ], + }, + { + id: 'report-conges', + title: 'Report annuel et rollover', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Le reliquat de congés de l\'exercice précédent est automatiquement reporté dans les acquis du nouvel exercice.' }, + { type: 'list', content: 'Report automatique le 1er juin (CDI/CDD non-forfait) ou 1er janvier (forfait)\nSi "Solde de tout compte" coché sur le contrat clôturé : report remis à 0\nJours fractionnés : saisie manuelle par la RH, ajoutés aux acquis' }, + ], + }, + { + id: 'consommation-conges', + title: 'Règle de consommation', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Les absences s\'imputent selon un ordre précis.' }, + { type: 'list', content: 'D\'abord sur les acquis (report N-1)\nPuis sur les jours en cours d\'acquisition\nEn cours d\'acquisition peut devenir négatif temporairement (se reconstitue avec les acquisitions suivantes)' }, + { type: 'paragraph', content: 'Compteurs visibles sur l\'onglet Congé de la fiche employé : acquis, en cours d\'acquisition, pris, restant.' }, + ], + }, + ], + }, + { + id: 'rtt', + title: 'RTT (Récupération de Temps de Travail)', + requiredLevel: 'admin', + icon: 'mdi:timer-sand', + articles: [ + { + id: 'rtt-principe', + title: 'Principe et exercice RTT', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Le RTT correspond aux heures supplémentaires accumulées, converties en temps de récupération. L\'exercice RTT va du 1er juin (N-1) au 31 mai (N).' }, + { type: 'paragraph', content: 'L\'onglet RTT sur la fiche employé affiche le détail hebdomadaire regroupé par mois, avec un compteur global en heures (1 jour = 7h = 420 minutes).' }, + ], + }, + { + id: 'rtt-compteurs', + title: 'Compteurs RTT', + requiredLevel: 'admin', + blocks: [ + { type: 'list', content: 'Report N-1 : solde de l\'exercice précédent\nAcquis : cumul des heures supplémentaires de l\'exercice en cours\nDisponible : report + acquis − payé\nPayé : RTT convertis en salaire (soustraits du disponible)' }, + { type: 'note', content: 'Les contrats INTERIM et le mode PRESENCE n\'accumulent pas de RTT (affiché à 0).' }, + ], + }, + { + id: 'rtt-paiement', + title: 'Paiement RTT', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'L\'administrateur RH peut enregistrer un paiement RTT depuis l\'onglet RTT de la fiche employé.' }, + { type: 'list', content: 'Saisie : mois, nombre de minutes, taux (25% ou 50%)\nPlusieurs paiements possibles par mois\nLes heures payées sont soustraites du solde disponible' }, + ], + }, + { + id: 'rtt-semaines-mois', + title: 'Attribution des semaines aux mois', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Chaque semaine ISO est attribuée à un seul mois dans le tableau RTT.' }, + { type: 'list', content: 'Une semaine est attribuée au mois qui contient son samedi\nSi le samedi tombe en début de mois suivant, la semaine est dans ce mois suivant' }, + ], + }, + ], + }, + { + id: 'frais-primes-observations', + title: 'Frais, primes et observations', + requiredLevel: 'admin', + icon: 'mdi:account-cash-outline', + articles: [ + { + id: 'frais', + title: 'Onglet Frais', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'L\'onglet Frais sur la fiche employé permet de saisir les frais kilométriques et les montants associés.' }, + { type: 'list', content: 'Mois : obligatoire\nKilomètres : nombre de km (optionnel)\nMontant : en euros (optionnel)\nCommentaire : optionnel\nDeux justificatifs PDF distincts : un pour les km, un pour le montant' }, + { type: 'note', content: 'Au moins un des deux champs (kilomètres ou montant) doit être supérieur à 0. Un seul enregistrement par mois par employé.' }, + ], + }, + { + id: 'primes', + title: 'Onglet Prime', + requiredLevel: 'admin', + blocks: [ + { type: 'list', content: 'Mois : obligatoire\nMontant en euros : obligatoire\nCommentaire : optionnel\nUne seule prime par mois par employé' }, + ], + }, + { + id: 'observations', + title: 'Onglet Observation', + requiredLevel: 'admin', + blocks: [ + { type: 'list', content: 'Mois : obligatoire\nTexte d\'observation : obligatoire\nUne seule observation par mois par employé\nNote libre pour le suivi RH' }, + ], + }, + ], + }, + { + id: 'exports', + title: 'Exports et impressions', + requiredLevel: 'admin', + icon: 'mdi:file-pdf-box', + articles: [ + { + id: 'export-recap-conges', + title: 'Export récap. congés', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Génère un PDF A4 portrait récapitulant les congés de tous les employés actifs.' }, + { type: 'list', content: 'Accessible depuis la page Employés (bouton "Export récap. congés")\nGénère un PDF à la date du jour\nDonnées groupées par site\nColonnes : nom, contrat, CP N-1 restant, samedi restant, CP N, RTT' }, + ], + }, + { + id: 'export-recap-salaire', + title: 'Récapitulatif salaire', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Génère un PDF A4 paysage avec le détail mensuel pour la paie.' }, + { type: 'list', content: 'Sélecteur de mois (défaut = mois courant)\nDonnées groupées par site\nColonnes : nom, base contrat, jours de présence cadre, heures de nuit, panier de nuit, heures RTT payées, congés (nombre + dates), maladie/AT (nombre + dates), primes conducteur (PDJ, repas, nuitée, samedi), observations' }, + ], + }, + { + id: 'impression-absences', + title: 'Impression absences', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Génère un PDF A3 paysage du calendrier d\'absences avec des filtres.' }, + { type: 'list', content: 'Filtres : période (du/au), sites, nature de contrat, type de contrat\nTous les filtres sont cochés par défaut\nCalendrier coloré par type d\'absence' }, + ], + }, + { + id: 'export-heures-annuelles', + title: 'Export heures annuelles', + requiredLevel: 'admin', + blocks: [ + { type: 'paragraph', content: 'Génère un PDF par employé avec le détail jour par jour de ses heures sur une année.' }, + { type: 'list', content: 'Accessible depuis la fiche employé (bouton imprimante)\nChoix de l\'année civile (janvier à décembre)\nColonnes adaptées au mode de suivi (TIME, PRESENCE, conducteur)\nSections séparées en cas de changement de contrat en cours d\'année' }, + ], + }, + ], + }, +] diff --git a/frontend/layouts/default.vue b/frontend/layouts/default.vue index 613a12f..c5d37df 100644 --- a/frontend/layouts/default.vue +++ b/frontend/layouts/default.vue @@ -95,6 +95,16 @@

Journal

+ + +

Documentation

+
diff --git a/frontend/pages/documentation.vue b/frontend/pages/documentation.vue new file mode 100644 index 0000000..9ca3886 --- /dev/null +++ b/frontend/pages/documentation.vue @@ -0,0 +1,9 @@ + + + diff --git a/frontend/types/documentation.ts b/frontend/types/documentation.ts new file mode 100644 index 0000000..eebbeec --- /dev/null +++ b/frontend/types/documentation.ts @@ -0,0 +1,21 @@ +export type DocAccessLevel = 'employee' | 'site_manager' | 'admin' + +export interface DocBlock { + type: 'paragraph' | 'list' | 'note' + content: string +} + +export interface DocArticle { + id: string + title: string + requiredLevel: DocAccessLevel + blocks: DocBlock[] +} + +export interface DocSection { + id: string + title: string + requiredLevel: DocAccessLevel + icon: string + articles: DocArticle[] +} diff --git a/src/State/AuditLogProvider.php b/src/State/AuditLogProvider.php index 9d28354..bd8be04 100644 --- a/src/State/AuditLogProvider.php +++ b/src/State/AuditLogProvider.php @@ -8,6 +8,7 @@ use ApiPlatform\Metadata\Operation; use ApiPlatform\State\ProviderInterface; use App\Repository\AuditLogRepository; use DateTimeImmutable; +use DateTimeZone; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\RequestStack; @@ -59,7 +60,7 @@ class AuditLogProvider implements ProviderInterface 'description' => $log->getDescription(), 'changes' => $log->getChanges(), 'affectedDate' => $log->getAffectedDate()?->format('Y-m-d'), - 'createdAt' => $log->getCreatedAt()->format('Y-m-d H:i:s'), + 'createdAt' => $log->getCreatedAt()->setTimezone(new DateTimeZone('Europe/Paris'))->format('Y-m-d H:i:s'), ]; }