165 lines
7.8 KiB
PHP
165 lines
7.8 KiB
PHP
<?php
|
||
/**
|
||
* En-tête HTML commun à toutes les pages : <head>, bandeau et barre de navigation.
|
||
*
|
||
* LA NAVIGATION EST DÉFINIE À DEUX ENDROITS, à tenir cohérents :
|
||
* - $navigation ci-dessous, qui associe une clé de page à son URL et son libellé ;
|
||
* - acces_role_page en base, qui décide des pages RÉELLEMENT autorisées.
|
||
* Une page absente de $navigation n'apparaîtra jamais, même autorisée en base ;
|
||
* une page absente de la base n'apparaîtra pas non plus, même déclarée ici.
|
||
*
|
||
* $navigationGroups n'est qu'un regroupement d'affichage : une page oubliée
|
||
* dans les groupes reste atteignable via le groupe « Autres » (voir plus bas).
|
||
*
|
||
* VERSION DES ASSETS : le `?v=` de la feuille de style est à incrémenter après
|
||
* toute modification de CSS ou de JS, ICI, dans layouts/footer.php ET dans
|
||
* Views/pages/role.php, qui porte son propre <head>. Sans cela, nginx
|
||
* (expires 7d) et les navigateurs continuent de servir l'ancienne version.
|
||
*/
|
||
/** @var string $pageTitle */
|
||
/** @var string $pageSubtitle */
|
||
/** @var string $activePage */
|
||
/** @var array $allowedPages */
|
||
/** @var array $accessProfile */
|
||
/** @var string $csrfToken */
|
||
|
||
$isAgent = ($accessProfile['role'] ?? '') === 'AGENT';
|
||
// Service Enfance et DSI ouvrent leur session avec un mot de passe (V7.12) :
|
||
// eux seuls ont quelque chose à changer. La liste des rôles concernés vit en
|
||
// base (v_acces_compte.mot_de_passe_requis) ; elle est recopiée ici pour un
|
||
// simple affichage, jamais pour décider d'un droit.
|
||
$peutChangerMotDePasse = in_array($accessProfile['role'] ?? '', ['SERVICE_ENFANCE', 'DSI'], true);
|
||
|
||
$navigation = [
|
||
'dashboard' => ['dashboard.php', 'Tableau de bord'],
|
||
'planning' => ['planning.php', 'Saisie'],
|
||
'pending' => ['pending.php', 'À valider'],
|
||
'coverage' => ['coverage.php', 'Couverture'],
|
||
'structures' => ['structures.php', 'Par lieu'],
|
||
'agents' => ['agents.php', $isAgent ? 'Mon planning' : 'Par agent'],
|
||
'annual' => ['annual.php', 'Vue annuelle'],
|
||
'pta' => ['pta.php', 'PTA détaillé'],
|
||
'teams' => ['teams.php', 'Équipes'],
|
||
'vacations' => ['vacations.php', 'Vacances'],
|
||
'manage_agents' => ['manage_agents.php', 'Agents'],
|
||
'manage_structures' => ['manage_structures.php', 'Lieux d’affectation'],
|
||
'administration' => ['administration.php', 'Administration'],
|
||
'dsi' => ['dsi.php', 'Accès & journaux'],
|
||
];
|
||
|
||
/**
|
||
* Les onze onglets du Service Enfance étaient alignés à plat, sans hiérarchie :
|
||
* rien ne distinguait une tâche quotidienne d'un référentiel modifié deux fois
|
||
* par an. Ils sont désormais regroupés par intention. Les rôles qui n'ont que
|
||
* quelques pages gardent une barre simple, sans titres de groupe.
|
||
*/
|
||
$navigationGroups = [
|
||
'Piloter' => ['dashboard', 'pending', 'coverage'],
|
||
'Planifier' => ['planning', 'teams'],
|
||
'Consulter' => ['agents', 'structures', 'annual', 'pta'],
|
||
// « Management » regroupe l'administration des personnes et des lieux : ce
|
||
// qu'on crée et corrige au fil de l'eau, à distinguer des paramètres qu'on
|
||
// règle une fois pour l'année.
|
||
'Management' => ['manage_agents', 'manage_structures'],
|
||
'Paramétrer' => ['vacations', 'administration', 'dsi'],
|
||
];
|
||
|
||
$visibleGroups = [];
|
||
$grouped = [];
|
||
foreach ($navigationGroups as $groupLabel => $keys) {
|
||
$visibleKeys = array_values(array_filter(
|
||
$keys,
|
||
static fn (string $key): bool => in_array($key, $allowedPages, true) && isset($navigation[$key])
|
||
));
|
||
if ($visibleKeys !== []) {
|
||
$visibleGroups[$groupLabel] = $visibleKeys;
|
||
$grouped = array_merge($grouped, $visibleKeys);
|
||
}
|
||
}
|
||
|
||
// Filet de sécurité : une page autorisée en base mais oubliée dans les groupes
|
||
// ci-dessus reste accessible plutôt que de disparaître de la navigation.
|
||
$ungrouped = array_values(array_filter(
|
||
$allowedPages,
|
||
static fn (string $key): bool => isset($navigation[$key]) && !in_array($key, $grouped, true)
|
||
));
|
||
if ($ungrouped !== []) {
|
||
$visibleGroups['Autres'] = $ungrouped;
|
||
}
|
||
|
||
$visiblePageCount = count($grouped) + count($ungrouped);
|
||
$showGroupLabels = $visiblePageCount > 4;
|
||
|
||
/**
|
||
* Le guide n'appartient à aucun groupe d'intention : ce n'est pas une étape du
|
||
* travail mais un secours permanent. Il est volontairement absent de
|
||
* $navigation — il ne doit apparaître qu'une seule fois, ancré à droite de la
|
||
* barre, et jamais repêché par le filet « Autres ».
|
||
*/
|
||
$showHelpTab = in_array('faq', $allowedPages, true);
|
||
?>
|
||
<!doctype html>
|
||
<html lang="fr">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<title><?= htmlspecialchars($pageTitle) ?> — PTA</title>
|
||
<link rel="stylesheet" href="assets/style.css?v=7.30">
|
||
</head>
|
||
<body>
|
||
<main class="app-shell">
|
||
<header class="page-header">
|
||
<div class="page-header-identity">
|
||
<p class="eyebrow">PTA</p>
|
||
<h1><?= htmlspecialchars($pageTitle) ?></h1>
|
||
<p class="subtitle"><?= htmlspecialchars($pageSubtitle ?? '') ?></p>
|
||
</div>
|
||
<div class="header-actions">
|
||
<?php if ($activePage === 'planning'): ?>
|
||
<div id="planning-status" class="status-badge status-neutral">Nouveau planning</div>
|
||
<?php endif; ?>
|
||
<div class="access-profile-badge">
|
||
<span><?= htmlspecialchars((string) ($accessProfile['role_label'] ?? '')) ?></span>
|
||
<strong><?= htmlspecialchars((string) ($accessProfile['label'] ?? '')) ?></strong>
|
||
<?php if (!empty($accessProfile['structure_name']) && $isAgent): ?>
|
||
<small><?= htmlspecialchars((string) $accessProfile['structure_name']) ?></small>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php if ($peutChangerMotDePasse): ?>
|
||
<a class="button button-ghost switch-role-link" href="role.php?action=mot_de_passe">Mot de passe</a>
|
||
<?php endif; ?>
|
||
<a class="button button-ghost switch-role-link" href="role.php?action=reset&csrf_token=<?= htmlspecialchars(urlencode($csrfToken), ENT_QUOTES) ?>">Se déconnecter</a>
|
||
</div>
|
||
</header>
|
||
|
||
<?php if (!empty($_GET['access_denied'])): ?>
|
||
<div class="message message-warning role-access-warning">Cette page n'est pas disponible avec votre rôle actuel.</div>
|
||
<?php endif; ?>
|
||
|
||
<nav class="view-tabs<?= $showGroupLabels ? ' view-tabs-grouped' : '' ?>" aria-label="Navigation principale">
|
||
<?php foreach ($visibleGroups as $groupLabel => $keys): ?>
|
||
<div class="view-tab-group" role="group"<?= $showGroupLabels ? ' aria-label="' . htmlspecialchars($groupLabel, ENT_QUOTES) . '"' : '' ?>>
|
||
<?php if ($showGroupLabels): ?>
|
||
<span class="view-tab-group-label"><?= htmlspecialchars($groupLabel) ?></span>
|
||
<?php endif; ?>
|
||
<div class="view-tab-group-items">
|
||
<?php foreach ($keys as $key): ?>
|
||
<?php [$href, $label] = $navigation[$key]; ?>
|
||
<a class="view-tab<?= $activePage === $key ? ' is-active' : '' ?>" href="<?= htmlspecialchars($href) ?>"<?= $activePage === $key ? ' aria-current="page"' : '' ?>>
|
||
<?= htmlspecialchars($label) ?>
|
||
<?php if ($key === 'pending'): ?><span id="pending-validation-tab-badge" class="nav-count-badge" hidden>0</span><?php endif; ?>
|
||
</a>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
<?php if ($showHelpTab): ?>
|
||
<div class="view-tab-help">
|
||
<a class="view-tab view-tab-faq<?= $activePage === 'faq' ? ' is-active' : '' ?>" href="faq.php"<?= $activePage === 'faq' ? ' aria-current="page"' : '' ?>>
|
||
<span class="view-tab-faq-mark" aria-hidden="true">?</span>
|
||
FAQ
|
||
</a>
|
||
</div>
|
||
<?php endif; ?>
|
||
</nav>
|