Files
pta/assets/js/page-bootstrap.js
PTA b68ac15de5 V7.9 — Tableau de bord et navigation regroupée pour le Service Enfance
Le Service Enfance ouvrait onze onglets à plat et atterrissait sur l'écran de
saisie, alors que son travail commence par le contrôle. Les chiffres qui
déclenchent une action vivaient sur trois écrans distincts.

- Nouvelle page dashboard.php, réservée au Service Enfance et devenue sa page
  d'atterrissage : quatre tuiles d'action, les brouillons les plus anciens avec
  ouverture directe sur la bonne semaine, les plages de couverture les plus
  déficitaires, et le suivi des quotas annuels par agent.
- Navigation regroupée par intention (Piloter / Planifier / Consulter /
  Paramétrer). Les rôles à quelques pages gardent la barre simple.
- Sous-titre propre à chaque page : le bandeau générique n'apprenait rien.
- Pastille « À valider » alimentée depuis toutes les pages, et non plus
  seulement depuis la page qu'elle annonce.

Aucune règle métier n'est modifiée : le tableau de bord relit ce que
PlanningModel, CoverageModel et QuotaModel calculent déjà, et son endpoint est
gardé par requireService(). Le droit d'accès à la page vient de la migration
V7.9 ; sans elle, l'onglet n'apparaît pas et homeUrl() se replie sur planning.

Vérifié sur la base réelle : les chiffres de quota du tableau de bord sont
identiques à ceux de api/annual_overview.php, les trois rôles rendent leurs
écrans sans erreur, le rendu JS a été exécuté dans un DOM avec la réponse réelle
de l'API, et la page ne produit aucun attribut style inline (CSP).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 16:48:50 +02:00

113 lines
4.3 KiB
JavaScript

(() => {
'use strict';
const PTA = window.PTA;
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?.());
async function initialisePlanning() {
const planning = PTA.modules.planning;
if (!planning) return;
const { structureSelect, agentSelect, weekInput, validateButton } = PTA.elements;
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);
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);
}
}
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?.();
}
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?.();
}
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();
})();