/** * Amorçage de page — À CHARGER EN DERNIER, après core.js et tous les modules. * * Deux responsabilités : * 1. appeler `init()` sur chaque module déclaré par le PageController ; * 2. appliquer les filtres venus de l'URL (structure_id, agent_id, week, month, * focus…), que PHP a assainis et republiés dans `window.PTA_PAGE_CONFIG`. * * Ce second point est ce qui rend les LIENS PROFONDS opérants : le tableau de * bord, la file de validation et la vue annuelle renvoient tous vers un écran * pré-positionné sur le bon lieu, le bon agent et la bonne semaine. * * Les initialisations sont `async` mais lancées SANS `await` en fin de fichier : * elles concernent des écrans différents et jamais plus d'une n'agit sur une * page donnée. */ (() => { 'use strict'; const PTA = window.PTA; // core.js absent ou en erreur : rien à amorcer. if (!PTA) return; const page = window.PTA_PAGE_CONFIG || {}; const modules = Array.isArray(page.modules) ? page.modules : []; const params = page.params || {}; modules.forEach((name) => PTA.modules[name]?.init?.()); /** * Écran de saisie : positionne lieu, agent et semaine depuis l'URL. * * L'ordre des étapes est CONTRAINT : la liste des agents dépend du lieu, et * la semaine ne peut être chargée qu'une fois l'agent connu — d'où les * `await` successifs. */ async function initialisePlanning() { const planning = PTA.modules.planning; if (!planning) return; const { structureSelect, agentSelect, weekInput, validateButton } = PTA.elements; // Un lien profond qui désigne une semaine précise (file de validation, // tableau de bord, vue annuelle) doit ouvrir la saisie semaine par // semaine, et non l'éditeur de semaine type proposé par défaut. if (params.week || params.focus) { PTA.modules.weekTemplateEditor?.setMode?.('week'); } if (params.structureId && structureSelect) { structureSelect.value = String(params.structureId); await planning.loadAgents(); if (params.structureLocked) structureSelect.disabled = true; } if (params.agentId && agentSelect) { agentSelect.value = String(params.agentId); // Depuis la V7.29, un agent dont le compte d'accès est coupé sort des // listes d'affectation. Un lien profond qui le désigne (file de // validation, tableau de bord, vue annuelle) ne trouve alors aucune // option : sans ce message, l'écran resterait vide sans rien dire. if (agentSelect.value !== String(params.agentId)) { PTA.utils.showMessage( 'Cet agent n’est plus proposé à la saisie : son accès a été coupé. Rétablissez-le depuis l’onglet Agents pour reprendre son planning.', 'warning' ); } else if (weekInput) { weekInput.disabled = false; } } if (params.week && weekInput) { weekInput.disabled = !agentSelect?.value; weekInput.value = params.week; if (agentSelect?.value) await planning.handleWeekSelection(); } if (params.notice) PTA.utils.showMessage(params.notice, 'info'); if (params.focus === 'validation') { window.setTimeout(() => { document.querySelector('#editor-view .actions-bar')?.scrollIntoView({ behavior: 'smooth', block: 'center' }); validateButton?.focus({ preventScroll: true }); }, 150); } } /** Planning par lieu : pré-sélectionne le lieu et charge la semaine. */ async function initialiseStructureOverview() { if (!params.structureId) return; const select = document.querySelector('#overview-structure'); if (select) { select.value = String(params.structureId); if (params.structureLocked) select.disabled = true; } await PTA.modules.structureOverview?.load?.(); } /** * Planning par agent : pré-sélectionne l'agent et le mois. * Le chargement n'est déclenché que si un agent est désigné ; sans agent, il * n'y a rien à afficher. */ async function initialiseAgentOverview() { if (params.agentId) { const select = document.querySelector('#overview-agent'); if (select) { select.value = String(params.agentId); if (params.agentLocked) select.disabled = true; } } if (params.month) { const input = document.querySelector('#agent-overview-month'); if (input) input.value = params.month; } if (params.agentId) await PTA.modules.agentOverview?.load?.(); } /** Contrôle de couverture : pré-sélectionne le lieu et rafraîchit. */ function initialiseCoverage() { if (!params.structureId) return; const select = document.querySelector('#coverage-structure-filter'); if (select) { select.value = String(params.structureId); if (params.structureLocked) select.disabled = true; PTA.modules.coverage?.refresh?.(); } } /** * Pastille « À valider » de la barre de navigation. * * Elle n'était alimentée que sur la page « À valider » elle-même, donc * invisible là où elle sert : depuis n'importe quel écran, le service voit * maintenant si des brouillons attendent. La requête est volontairement * réduite à un compteur, et son échec est silencieux — une commodité ne * doit jamais empêcher une page de fonctionner. */ async function refreshNavigationCounters() { const badge = document.querySelector('#pending-validation-tab-badge'); if (!badge) return; // Les écrans qui chargent déjà ce compteur s'en chargent eux-mêmes. if (PTA.modules.pendingValidation || PTA.modules.dashboard) return; if (!PTA.config?.permissions?.can_view_pending) return; try { const response = await fetch('api/dashboard_summary.php?scope=badges', { headers: { Accept: 'application/json' }, }); if (!response.ok) return; const data = await PTA.utils.readJsonResponse(response); const count = Number(data?.validation?.count || 0); badge.textContent = String(count); badge.hidden = count === 0; badge.setAttribute('aria-label', `${count} planning${count > 1 ? 's' : ''} en attente de validation`); } catch (error) { badge.hidden = true; } } initialisePlanning(); initialiseStructureOverview(); initialiseAgentOverview(); initialiseCoverage(); refreshNavigationCounters(); })();