update
This commit is contained in:
@@ -51,10 +51,10 @@ window.PTA_PAGE_CONFIG = <?= json_encode([
|
||||
'params' => $pageConfig,
|
||||
], JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE) ?>;
|
||||
</script>
|
||||
<script src="assets/js/core.js?v=7.16"></script>
|
||||
<script src="assets/js/core.js?v=7.23"></script>
|
||||
<?php foreach ($scripts as $script): ?>
|
||||
<script src="assets/js/<?= htmlspecialchars($script) ?>?v=7.16"></script>
|
||||
<script src="assets/js/<?= htmlspecialchars($script) ?>?v=7.23"></script>
|
||||
<?php endforeach; ?>
|
||||
<script src="assets/js/page-bootstrap.js?v=7.16"></script>
|
||||
<script src="assets/js/page-bootstrap.js?v=7.23"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
/** @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'],
|
||||
@@ -36,7 +41,10 @@ $navigation = [
|
||||
'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'],
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -49,7 +57,11 @@ $navigationGroups = [
|
||||
'Piloter' => ['dashboard', 'pending', 'coverage'],
|
||||
'Planifier' => ['planning', 'teams'],
|
||||
'Consulter' => ['agents', 'structures', 'annual', 'pta'],
|
||||
'Paramétrer' => ['vacations', 'administration'],
|
||||
// « 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 = [];
|
||||
@@ -92,7 +104,7 @@ $showHelpTab = in_array('faq', $allowedPages, true);
|
||||
<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.16">
|
||||
<link rel="stylesheet" href="assets/style.css?v=7.23">
|
||||
</head>
|
||||
<body>
|
||||
<main class="app-shell">
|
||||
@@ -113,12 +125,15 @@ $showHelpTab = in_array('faq', $allowedPages, true);
|
||||
<small><?= htmlspecialchars((string) $accessProfile['structure_name']) ?></small>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<a class="button button-ghost switch-role-link" href="role.php?action=reset&csrf_token=<?= htmlspecialchars(urlencode($csrfToken), ENT_QUOTES) ?>">Changer de rôle</a>
|
||||
<?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>
|
||||
<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">
|
||||
|
||||
9
app/Views/pages/dsi.php
Normal file
9
app/Views/pages/dsi.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Vue de page : assemble l'en-tête commun, le partiel de contenu et le pied.
|
||||
* Toutes les variables utilisées viennent de PageController::renderPage(), qui
|
||||
* les a extraites dans la portée de ce fichier.
|
||||
*/
|
||||
require dirname(__DIR__) . '/layouts/header.php';
|
||||
require dirname(__DIR__) . '/partials/dsi.php';
|
||||
require dirname(__DIR__) . '/layouts/footer.php';
|
||||
9
app/Views/pages/manage_agents.php
Normal file
9
app/Views/pages/manage_agents.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Vue de page : assemble l'en-tête commun, le partiel de contenu et le pied.
|
||||
* Toutes les variables utilisées viennent de PageController::renderPage(), qui
|
||||
* les a extraites dans la portée de ce fichier.
|
||||
*/
|
||||
require dirname(__DIR__) . '/layouts/header.php';
|
||||
require dirname(__DIR__) . '/partials/manage_agents.php';
|
||||
require dirname(__DIR__) . '/layouts/footer.php';
|
||||
9
app/Views/pages/manage_structures.php
Normal file
9
app/Views/pages/manage_structures.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
/**
|
||||
* Vue de page : assemble l'en-tête commun, le partiel de contenu et le pied.
|
||||
* Toutes les variables utilisées viennent de PageController::renderPage(), qui
|
||||
* les a extraites dans la portée de ce fichier.
|
||||
*/
|
||||
require dirname(__DIR__) . '/layouts/header.php';
|
||||
require dirname(__DIR__) . '/partials/manage_structures.php';
|
||||
require dirname(__DIR__) . '/layouts/footer.php';
|
||||
@@ -1,131 +1,202 @@
|
||||
<?php
|
||||
/**
|
||||
* Sélecteur de rôle — SEULE vue à ne pas passer par layouts/header.php.
|
||||
* Écran de connexion — SEULE vue à ne pas passer par layouts/header.php.
|
||||
*
|
||||
* Elle s'affiche avant qu'un rôle soit choisi, donc avant tout contrôle
|
||||
* d'accès, et ne peut pas afficher la barre de navigation qui en dépend. Elle
|
||||
* porte donc son propre <head> : c'est le TROISIÈME endroit où incrémenter le
|
||||
* `?v=` des assets, avec layouts/header.php et layouts/footer.php.
|
||||
* Elle s'affiche avant qu'une session existe, donc avant tout contrôle d'accès,
|
||||
* et ne peut pas afficher la barre de navigation qui en dépend. Elle porte donc
|
||||
* son propre <head> : c'est le TROISIÈME endroit où incrémenter le `?v=` des
|
||||
* assets, avec layouts/header.php et layouts/footer.php.
|
||||
*
|
||||
* Rappel : sélection n'est pas authentification. L'utilisateur choisit
|
||||
* librement son identité (voir App\Core\Access::select()).
|
||||
* Depuis la V7.11, ce n'est plus un sélecteur de rôle mais une AUTHENTIFICATION :
|
||||
* l'utilisateur présente le jeton que la DSI lui a remis, et c'est son compte qui
|
||||
* détermine son rôle et son périmètre.
|
||||
*
|
||||
* La V7.12 ajoute un second facteur pour les comptes Service Enfance et DSI.
|
||||
* D'où les quatre états de cet écran, portés par $etape :
|
||||
* « jeton » — saisie du jeton ;
|
||||
* « definition » — premier accès d'un compte privilégié : choisir son mot de passe ;
|
||||
* « verification » — accès suivants : le saisir ;
|
||||
* « changement » — session ouverte, le titulaire remplace son mot de passe.
|
||||
* Un seul formulaire est affiché à la fois : les étapes sont exclusives.
|
||||
*/
|
||||
/** @var string $csrfToken */
|
||||
/** @var array $agents */
|
||||
/** @var array $structures */
|
||||
/** @var string $error */
|
||||
/** @var string $notice */
|
||||
/** @var string $etape */
|
||||
/** @var string $titulaire */
|
||||
/** @var int $longueurMinimale */
|
||||
/** @var array|null $currentProfile */
|
||||
/** @var string $homeUrl */
|
||||
/** @var string $cspNonce */
|
||||
|
||||
$titres = [
|
||||
'jeton' => 'Connexion',
|
||||
'definition' => 'Choisissez votre mot de passe',
|
||||
'verification' => 'Mot de passe',
|
||||
'changement' => 'Changer de mot de passe',
|
||||
];
|
||||
$titre = $titres[$etape] ?? 'Connexion';
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="fr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Choisir votre accès — PTA</title>
|
||||
<link rel="stylesheet" href="assets/style.css?v=7.16">
|
||||
<title><?= htmlspecialchars($titre) ?> — PTA</title>
|
||||
<link rel="stylesheet" href="assets/style.css?v=7.23">
|
||||
</head>
|
||||
<body class="role-page-body">
|
||||
<main class="role-page-shell">
|
||||
<section class="role-intro">
|
||||
<p class="eyebrow">PTA</p>
|
||||
<h1>Quel est votre rôle ?</h1>
|
||||
<p>Votre choix détermine les pages visibles et les actions autorisées pendant cette session.</p>
|
||||
<?php if ($currentProfile): ?>
|
||||
<h1><?= htmlspecialchars($titre) ?></h1>
|
||||
|
||||
<?php if ($etape === 'jeton'): ?>
|
||||
<p>Saisissez le jeton d’accès qui vous a été remis. Il détermine votre rôle, les pages visibles et les actions autorisées.</p>
|
||||
<?php elseif ($etape === 'definition'): ?>
|
||||
<p>
|
||||
Votre compte <strong><?= htmlspecialchars($titulaire) ?></strong> engage l’application :
|
||||
il demande un second secret, connu de vous seul. Choisissez-le maintenant, il vous sera demandé à chaque connexion.
|
||||
</p>
|
||||
<?php elseif ($etape === 'verification'): ?>
|
||||
<p>Jeton reconnu. Saisissez le mot de passe de <strong><?= htmlspecialchars($titulaire) ?></strong> pour ouvrir la session.</p>
|
||||
<?php else: ?>
|
||||
<p>Votre mot de passe actuel vous est demandé : sans lui, un poste laissé ouvert suffirait à confisquer votre compte.</p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($currentProfile && $etape === 'jeton'): ?>
|
||||
<div class="role-current-profile">
|
||||
Profil actuel : <strong><?= htmlspecialchars((string) $currentProfile['role_label']) ?></strong>
|
||||
Session ouverte : <strong><?= htmlspecialchars((string) $currentProfile['role_label']) ?></strong>
|
||||
— <?= htmlspecialchars((string) $currentProfile['label']) ?>
|
||||
<a class="button button-primary role-resume-link" href="<?= htmlspecialchars($homeUrl) ?>">Reprendre</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if ($notice !== ''): ?>
|
||||
<div class="message message-success"><?= htmlspecialchars($notice) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php if ($error !== ''): ?>
|
||||
<div class="message message-error"><?= htmlspecialchars($error) ?></div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<form method="post" class="role-selection-form" id="role-selection-form">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrfToken) ?>">
|
||||
|
||||
<div class="role-choice-grid">
|
||||
<label class="role-choice-card">
|
||||
<input type="radio" name="role" value="AGENT" required>
|
||||
<span class="role-choice-content">
|
||||
<strong>Agent</strong>
|
||||
<small>Lecture de mon planning et du planning de mon lieu de rattachement.</small>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label class="role-choice-card">
|
||||
<input type="radio" name="role" value="RESPONSABLE_STRUCTURE" required>
|
||||
<span class="role-choice-content">
|
||||
<strong>Responsable de structure</strong>
|
||||
<small>Saisie et modification des horaires et motifs de mon lieu, uniquement en brouillon.</small>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label class="role-choice-card">
|
||||
<input type="radio" name="role" value="SERVICE_ENFANCE" required>
|
||||
<span class="role-choice-content">
|
||||
<strong>Service Enfance</strong>
|
||||
<small>Contrôle complet, prévisionnel N+1, administration et validation des plannings.</small>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="agent-context" class="role-context-card" hidden>
|
||||
<?php if ($etape === 'jeton'): ?>
|
||||
<form method="post" class="role-selection-form role-token-form" id="role-token-form">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrfToken) ?>">
|
||||
<label class="field">
|
||||
<span>Je suis l’agent</span>
|
||||
<select name="agent_id" id="role-agent-select">
|
||||
<option value="">Choisir mon identité</option>
|
||||
<?php foreach ($agents as $agent): ?>
|
||||
<option value="<?= (int) $agent['id_agent'] ?>">
|
||||
<?= htmlspecialchars($agent['prenom'] . ' ' . $agent['nom'] . ' (' . $agent['matricule'] . ')') ?>
|
||||
<?= !empty($agent['structure_nom']) ? ' — ' . htmlspecialchars((string) $agent['structure_nom']) : '' ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<span>Jeton d’accès</span>
|
||||
<?php /* autocomplete="off" : un jeton n'a pas à être retenu par le navigateur d'un poste partagé. */ ?>
|
||||
<input
|
||||
type="text"
|
||||
name="token"
|
||||
id="role-token-input"
|
||||
class="role-token-input"
|
||||
placeholder="PTA-XXXXXX-XXXXXX-XXXXXX-XXXXXX"
|
||||
autocomplete="off"
|
||||
autocapitalize="characters"
|
||||
spellcheck="false"
|
||||
required
|
||||
autofocus>
|
||||
</label>
|
||||
</div>
|
||||
<div class="role-form-actions">
|
||||
<button type="submit" class="button button-primary">Continuer</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php elseif ($etape === 'definition' || $etape === 'verification'): ?>
|
||||
<form method="post" class="role-selection-form role-token-form">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrfToken) ?>">
|
||||
<input type="hidden" name="etape" value="mot_de_passe">
|
||||
|
||||
<div id="responsable-context" class="role-context-card" hidden>
|
||||
<label class="field">
|
||||
<span>Je suis responsable du lieu</span>
|
||||
<select name="structure_id" id="role-structure-select">
|
||||
<option value="">Choisir le lieu d’affectation</option>
|
||||
<?php foreach ($structures as $structure): ?>
|
||||
<option value="<?= (int) $structure['id_structure'] ?>">
|
||||
<?= htmlspecialchars((string) $structure['nom']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<span><?= $etape === 'definition' ? 'Nouveau mot de passe' : 'Mot de passe' ?></span>
|
||||
<input
|
||||
type="password"
|
||||
name="mot_de_passe"
|
||||
autocomplete="<?= $etape === 'definition' ? 'new-password' : 'current-password' ?>"
|
||||
minlength="<?= (int) $longueurMinimale ?>"
|
||||
required
|
||||
autofocus>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="role-form-actions">
|
||||
<button type="submit" class="button button-primary">Accéder à PTA</button>
|
||||
</div>
|
||||
</form>
|
||||
<?php if ($etape === 'definition'): ?>
|
||||
<label class="field">
|
||||
<span>Confirmation</span>
|
||||
<input type="password" name="mot_de_passe_confirmation" autocomplete="new-password" minlength="<?= (int) $longueurMinimale ?>" required>
|
||||
</label>
|
||||
<p class="hint">
|
||||
Au moins <?= (int) $longueurMinimale ?> caractères. Aucune règle de composition :
|
||||
une phrase dont vous vous souvenez protège mieux qu’un mot court hérissé de symboles.
|
||||
Il ne sera affiché nulle part — la DSI elle-même ne peut que l’effacer, jamais le lire.
|
||||
</p>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="role-form-actions">
|
||||
<button type="submit" class="button button-primary"><?= $etape === 'definition' ? 'Enregistrer et se connecter' : 'Se connecter' ?></button>
|
||||
</div>
|
||||
<p class="hint role-step-back">
|
||||
<a href="role.php?action=reset&csrf_token=<?= htmlspecialchars(urlencode($csrfToken), ENT_QUOTES) ?>">Recommencer avec un autre jeton</a>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<?php else: ?>
|
||||
<form method="post" class="role-selection-form role-token-form">
|
||||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrfToken) ?>">
|
||||
<input type="hidden" name="etape" value="changement">
|
||||
|
||||
<label class="field">
|
||||
<span>Mot de passe actuel</span>
|
||||
<input type="password" name="mot_de_passe_actuel" autocomplete="current-password" required autofocus>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Nouveau mot de passe</span>
|
||||
<input type="password" name="mot_de_passe" autocomplete="new-password" minlength="<?= (int) $longueurMinimale ?>" required>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Confirmation</span>
|
||||
<input type="password" name="mot_de_passe_confirmation" autocomplete="new-password" minlength="<?= (int) $longueurMinimale ?>" required>
|
||||
</label>
|
||||
<p class="hint">Au moins <?= (int) $longueurMinimale ?> caractères.</p>
|
||||
|
||||
<div class="role-form-actions">
|
||||
<button type="submit" class="button button-primary">Changer le mot de passe</button>
|
||||
</div>
|
||||
<p class="hint role-step-back">
|
||||
<a href="<?= htmlspecialchars($homeUrl) ?>">Revenir à l’application</a>
|
||||
</p>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
<aside class="role-security-note">
|
||||
<strong>Version de démonstration :</strong> ce sélecteur organise les droits dans l’application, mais ne remplace pas une authentification nominative. En production, le rôle devra être associé au compte connecté.
|
||||
<?php if ($etape === 'changement'): ?>
|
||||
<strong>Un mot de passe oublié ne se retrouve pas :</strong> la DSI l’efface, et vous en choisissez un nouveau à votre connexion suivante, avec votre jeton.
|
||||
<?php else: ?>
|
||||
<strong>Jeton perdu ou compromis ?</strong> Contactez la DSI : elle en génère un nouveau, ce qui révoque immédiatement l’ancien.
|
||||
Un jeton vaut pour une personne — il ne se prête pas, puisque toutes les actions sont journalisées à son nom.
|
||||
Les comptes Service Enfance et DSI y ajoutent un mot de passe, que vous seul connaissez.
|
||||
<?php endif; ?>
|
||||
</aside>
|
||||
</main>
|
||||
<script nonce="<?= htmlspecialchars($cspNonce, ENT_QUOTES) ?>">
|
||||
(() => {
|
||||
const agentContext = document.querySelector('#agent-context');
|
||||
const responsableContext = document.querySelector('#responsable-context');
|
||||
const agentSelect = document.querySelector('#role-agent-select');
|
||||
const structureSelect = document.querySelector('#role-structure-select');
|
||||
const input = document.querySelector('#role-token-input');
|
||||
if (!input) return;
|
||||
|
||||
function refresh() {
|
||||
const role = document.querySelector('input[name="role"]:checked')?.value || '';
|
||||
agentContext.hidden = role !== 'AGENT';
|
||||
responsableContext.hidden = role !== 'RESPONSABLE_STRUCTURE';
|
||||
agentSelect.required = role === 'AGENT';
|
||||
structureSelect.required = role === 'RESPONSABLE_STRUCTURE';
|
||||
}
|
||||
|
||||
document.querySelectorAll('input[name="role"]').forEach((input) => input.addEventListener('change', refresh));
|
||||
refresh();
|
||||
/**
|
||||
* Met en forme la saisie au fil de la frappe : majuscules et suppression des
|
||||
* espaces. Les jetons sont recopiés depuis un courriel ou un papier, et un
|
||||
* copier-coller ramène presque toujours une espace ou un retour à la ligne.
|
||||
* Le serveur normalise de toute façon (CompteModel::normaliser) ; ceci n'est
|
||||
* qu'un confort de saisie — et cela ne concerne QUE le jeton, jamais le mot
|
||||
* de passe, dont chaque caractère compte tel qu'il est frappé.
|
||||
*/
|
||||
input.addEventListener('input', () => {
|
||||
const position = input.selectionStart;
|
||||
const nettoye = input.value.toUpperCase().replace(/\s+/g, '');
|
||||
if (nettoye !== input.value) {
|
||||
input.value = nettoye;
|
||||
input.setSelectionRange(position, position);
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -1,192 +1,19 @@
|
||||
<?php
|
||||
/**
|
||||
* Référentiels : agents, lieux d'affectation, postes, horaires de couverture.
|
||||
* Modules JS : `administration` et `coverage`.
|
||||
* Administration : horaires de couverture attendus des lieux.
|
||||
* Module JS : `coverage`.
|
||||
*
|
||||
* Partiel de contenu inclus par la vue de page homonyme. Les identifiants HTML
|
||||
* posés ici sont les points d'ancrage du module JS : les renommer casse le
|
||||
* module sans aucune erreur visible.
|
||||
*
|
||||
* V7.14 : les agents et les lieux d'affectation ont quitté cet écran pour la
|
||||
* page « Management ». Ne restent ici que les règles de couverture, qui ne
|
||||
* décrivent ni une personne ni un lieu mais une EXIGENCE DE PRÉSENCE — le
|
||||
* paramètre que contrôle l'onglet « Couverture ».
|
||||
*/
|
||||
?>
|
||||
<section id="assignment-view" class="app-view page-content">
|
||||
<div class="admin-grid">
|
||||
<section class="card admin-card">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2>Ajouter un lieu d’affectation</h2>
|
||||
<p class="section-subtitle">Chaque lieu possède son propre type : périscolaire ou extrascolaire.</p>
|
||||
</div>
|
||||
</div>
|
||||
<form id="create-structure-form" class="admin-form">
|
||||
<div class="admin-form-grid">
|
||||
<label class="field">
|
||||
<span>Code <strong>*</strong></span>
|
||||
<input id="new-structure-code" name="code" type="text" maxlength="50" placeholder="Ex. CRECHE_MATUSALEME" required>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Nom <strong>*</strong></span>
|
||||
<input id="new-structure-name" name="nom" type="text" maxlength="150" placeholder="Ex. Crèche Matusalème" required>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Type d’affectation <strong>*</strong></span>
|
||||
<select id="new-structure-type" name="type_affectation" required>
|
||||
<option value="PERISCOLAIRE">Périscolaire</option>
|
||||
<option value="EXTRASCOLAIRE">Extrascolaire</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field admin-field-wide">
|
||||
<span>Adresse</span>
|
||||
<input id="new-structure-address" name="adresse" type="text" maxlength="255" placeholder="Adresse du lieu">
|
||||
</label>
|
||||
</div>
|
||||
<div class="admin-form-actions">
|
||||
<div id="create-structure-message" class="message" role="status" aria-live="polite"></div>
|
||||
<button class="button button-primary" type="submit">Ajouter le lieu</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="card admin-card">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2>Ajouter un agent</h2>
|
||||
<p class="section-subtitle">L’agent reçoit un lieu principal à sa création, mais peut être intégré aussi bien aux équipes périscolaires qu’extrascolaires et travailler ponctuellement sur d’autres lieux.</p>
|
||||
</div>
|
||||
</div>
|
||||
<form id="create-agent-form" class="admin-form">
|
||||
<div class="admin-form-grid">
|
||||
<label class="field">
|
||||
<span>Matricule <strong>*</strong></span>
|
||||
<input id="new-agent-matricule" name="matricule" type="text" maxlength="50" required>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Poste PTA <strong>*</strong></span>
|
||||
<select id="new-agent-post" name="id_poste" required>
|
||||
<option value="">Choisir un poste</option>
|
||||
<?php foreach ($posts as $post): ?>
|
||||
<option value="<?= (int) $post['id_poste'] ?>"><?= htmlspecialchars($post['libelle']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Type de contrat <strong>*</strong></span>
|
||||
<select id="new-agent-contract-type" required>
|
||||
<option value="PERMANENT">Permanent — soumis au PTA</option>
|
||||
<option value="TEMPORAIRE">Temporaire — hors périmètre PTA</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Lieu d’affectation principal <strong>*</strong></span>
|
||||
<select id="new-agent-structure" name="structure_id" required>
|
||||
<option value="">Choisir un lieu d’affectation</option>
|
||||
<?php foreach ($structures as $structure): ?>
|
||||
<option value="<?= (int) $structure['id_structure'] ?>">
|
||||
<?= htmlspecialchars($structure['nom']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Nom <strong>*</strong></span>
|
||||
<input id="new-agent-name" name="nom" type="text" maxlength="100" required>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Prénom <strong>*</strong></span>
|
||||
<input id="new-agent-firstname" name="prenom" type="text" maxlength="100" required>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>E-mail</span>
|
||||
<input id="new-agent-email" name="email" type="email" maxlength="255" placeholder="prenom.nom@exemple.fr">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Téléphone</span>
|
||||
<input id="new-agent-phone" name="telephone" type="tel" maxlength="30" placeholder="Ex. 06 12 34 56 78">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Qualification</span>
|
||||
<select id="new-agent-qualified" name="est_diplome">
|
||||
<option value="0">Non diplômé</option>
|
||||
<option value="1">Diplômé</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Diplôme</span>
|
||||
<input id="new-agent-diploma" name="diplome_libelle" type="text" maxlength="150" placeholder="Ex. BAFA, CAP AEPE…" disabled>
|
||||
</label>
|
||||
<label class="field admin-field-wide">
|
||||
<span>Adresse personnelle</span>
|
||||
<textarea id="new-agent-address" name="adresse" maxlength="255" rows="2" placeholder="Adresse de l’agent"></textarea>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Quotité de travail <strong>*</strong></span>
|
||||
<div class="input-suffix">
|
||||
<input id="new-agent-rate" name="quotite_travail" type="number" min="1" max="100" step="0.01" value="100" required>
|
||||
<span>%</span>
|
||||
</div>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Date de début du contrat <strong>*</strong></span>
|
||||
<input id="new-agent-contract-start" name="date_debut_contrat" type="date" value="<?= date('Y-m-d') ?>" required>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Date de fin du contrat</span>
|
||||
<input id="new-agent-contract-end" name="date_fin_contrat" type="date">
|
||||
</label>
|
||||
<label class="check-field admin-field-wide">
|
||||
<input id="new-agent-training-right" type="checkbox" checked>
|
||||
<span>Droit à l’enveloppe de formation de 14 h. Décoché, le motif « Formation » ne pourra pas être affecté à cet agent lors de la saisie des horaires.</span>
|
||||
</label>
|
||||
<label class="field admin-field-wide">
|
||||
<span>Observations utiles à la construction du PTA</span>
|
||||
<textarea id="new-agent-pta-comment" maxlength="1000" rows="3" placeholder="Disponibilités particulières, organisation connue…"></textarea>
|
||||
</label>
|
||||
</div>
|
||||
<p class="hint">Le quota PTA est suivi sur l’année civile, du 1er janvier au 31 décembre. À 100 %, la référence est de 1 607 h. La date de début du contrat reste une information RH mais ne décale pas l’année de référence.</p>
|
||||
<div class="admin-form-actions">
|
||||
<div id="create-agent-message" class="message" role="status" aria-live="polite"></div>
|
||||
<button class="button button-primary" type="submit">Ajouter l’agent</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<section class="card">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2>Paramètres des lieux d’affectation</h2>
|
||||
<p class="section-subtitle">Le type d’affectation est défini au niveau du lieu : par exemple « Crèche Matusalème — Périscolaire ».</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="structure-settings-message" class="message" role="status" aria-live="polite"></div>
|
||||
<div class="table-scroll">
|
||||
<table class="planning-table assignment-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Lieu</th>
|
||||
<th>Code</th>
|
||||
<th>Type d’affectation</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="structure-settings-body">
|
||||
<?php foreach ($structures as $structure): ?>
|
||||
<tr data-structure-id="<?= (int) $structure['id_structure'] ?>">
|
||||
<td><strong><?= htmlspecialchars($structure['nom']) ?></strong></td>
|
||||
<td><?= htmlspecialchars($structure['code']) ?></td>
|
||||
<td>
|
||||
<select class="structure-type-select" aria-label="Type d’affectation de <?= htmlspecialchars($structure['nom']) ?>">
|
||||
<option value="PERISCOLAIRE" <?= ($structure['type_affectation'] ?? 'PERISCOLAIRE') === 'PERISCOLAIRE' ? 'selected' : '' ?>>Périscolaire</option>
|
||||
<option value="EXTRASCOLAIRE" <?= ($structure['type_affectation'] ?? '') === 'EXTRASCOLAIRE' ? 'selected' : '' ?>>Extrascolaire</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><button type="button" class="button button-secondary save-structure-type">Enregistrer</button></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card coverage-settings-card">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
@@ -216,151 +43,4 @@
|
||||
<button id="save-coverage-rules" class="button button-primary" type="button" disabled>Enregistrer les horaires de couverture</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2>Informations des agents</h2>
|
||||
<p class="section-subtitle">Consultez la qualification, les coordonnées et le lieu principal de chaque agent. Le bouton Modifier ouvre la fiche complète.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="assignment-message" class="message" role="status" aria-live="polite"></div>
|
||||
<div class="table-scroll">
|
||||
<table class="planning-table assignment-table agent-information-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Agent</th>
|
||||
<th>Matricule</th>
|
||||
<th>Poste</th>
|
||||
<th>Qualification</th>
|
||||
<th>Téléphone</th>
|
||||
<th>Début contrat</th>
|
||||
<th>Lieu principal</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="agent-assignment-body">
|
||||
<?php foreach ($agents as $agent): ?>
|
||||
<tr
|
||||
data-agent-id="<?= (int) $agent['id_agent'] ?>"
|
||||
data-email="<?= htmlspecialchars((string) ($agent['email'] ?? ''), ENT_QUOTES) ?>"
|
||||
data-telephone="<?= htmlspecialchars((string) ($agent['telephone'] ?? ''), ENT_QUOTES) ?>"
|
||||
data-adresse="<?= htmlspecialchars((string) ($agent['adresse'] ?? ''), ENT_QUOTES) ?>"
|
||||
data-est-diplome="<?= !empty($agent['est_diplome']) ? '1' : '0' ?>"
|
||||
data-diplome-libelle="<?= htmlspecialchars((string) ($agent['diplome_libelle'] ?? ''), ENT_QUOTES) ?>"
|
||||
data-date-debut-contrat="<?= htmlspecialchars((string) ($agent['date_debut_contrat'] ?? ''), ENT_QUOTES) ?>"
|
||||
data-date-fin-contrat="<?= htmlspecialchars((string) ($agent['date_fin_contrat'] ?? ''), ENT_QUOTES) ?>"
|
||||
data-id-poste="<?= (int) ($agent['id_poste'] ?? 0) ?>"
|
||||
data-type-contrat="<?= htmlspecialchars((string) ($agent['type_contrat'] ?? 'PERMANENT'), ENT_QUOTES) ?>"
|
||||
data-droit-enveloppe-formation="<?= !isset($agent['droit_enveloppe_formation']) || !empty($agent['droit_enveloppe_formation']) ? '1' : '0' ?>"
|
||||
data-commentaire-pta="<?= htmlspecialchars((string) ($agent['commentaire_pta'] ?? ''), ENT_QUOTES) ?>"
|
||||
data-structure-id="<?= (int) ($agent['id_structure'] ?? 0) ?>"
|
||||
>
|
||||
<td><strong class="agent-row-name"><?= htmlspecialchars($agent['prenom'] . ' ' . $agent['nom']) ?></strong></td>
|
||||
<td><?= htmlspecialchars($agent['matricule']) ?></td>
|
||||
<td class="agent-row-post"><?= htmlspecialchars((string) (($agent['poste_libelle'] ?? '') ?: 'Non renseigné')) ?></td>
|
||||
<td class="agent-row-qualification">
|
||||
<?= !empty($agent['est_diplome'])
|
||||
? 'Diplômé' . (!empty($agent['diplome_libelle']) ? ' — ' . htmlspecialchars($agent['diplome_libelle']) : '')
|
||||
: 'Non diplômé' ?>
|
||||
</td>
|
||||
<td class="agent-row-phone"><?= htmlspecialchars((string) (($agent['telephone'] ?? '') ?: 'Non renseigné')) ?></td>
|
||||
<td class="agent-row-contract-start"><?= htmlspecialchars((string) (($agent['date_debut_contrat'] ?? '') ?: 'Non renseignée')) ?></td>
|
||||
<td class="agent-row-structure"><?= htmlspecialchars((string) (($agent['structure_nom'] ?? '') ?: 'Sans lieu principal')) ?></td>
|
||||
<td><button type="button" class="button button-secondary edit-agent-profile">Modifier</button></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<div id="agent-profile-modal" class="modal-backdrop" hidden>
|
||||
<section class="modal-card" role="dialog" aria-modal="true" aria-labelledby="agent-profile-modal-title">
|
||||
<div class="section-heading modal-heading">
|
||||
<div>
|
||||
<h2 id="agent-profile-modal-title">Modifier les informations de l’agent</h2>
|
||||
<p id="agent-profile-modal-subtitle" class="section-subtitle"></p>
|
||||
</div>
|
||||
<button id="close-agent-profile-modal" type="button" class="button button-ghost modal-close" aria-label="Fermer">×</button>
|
||||
</div>
|
||||
<form id="agent-profile-form" class="admin-form">
|
||||
<input id="edit-agent-profile-id" type="hidden">
|
||||
<div class="admin-form-grid">
|
||||
<label class="field">
|
||||
<span>Poste PTA <strong>*</strong></span>
|
||||
<select id="edit-agent-profile-post" required>
|
||||
<option value="">Choisir un poste</option>
|
||||
<?php foreach ($posts as $post): ?>
|
||||
<option value="<?= (int) $post['id_poste'] ?>"><?= htmlspecialchars($post['libelle']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Type de contrat <strong>*</strong></span>
|
||||
<select id="edit-agent-profile-contract-type" required>
|
||||
<option value="PERMANENT">Permanent</option>
|
||||
<option value="TEMPORAIRE">Temporaire</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>E-mail</span>
|
||||
<input id="edit-agent-profile-email" type="email" maxlength="255">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Téléphone</span>
|
||||
<input id="edit-agent-profile-phone" type="tel" maxlength="30" placeholder="Ex. 06 12 34 56 78">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Qualification</span>
|
||||
<select id="edit-agent-profile-qualified">
|
||||
<option value="0">Non diplômé</option>
|
||||
<option value="1">Diplômé</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Diplôme</span>
|
||||
<input id="edit-agent-profile-diploma" type="text" maxlength="150" placeholder="Ex. BAFA, CAP AEPE…">
|
||||
</label>
|
||||
<label class="field admin-field-wide">
|
||||
<span>Adresse personnelle</span>
|
||||
<textarea id="edit-agent-profile-address" maxlength="255" rows="3"></textarea>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Date de début du contrat <strong>*</strong></span>
|
||||
<input id="edit-agent-profile-contract-start" type="date" required>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Date de fin du contrat</span>
|
||||
<input id="edit-agent-profile-contract-end" type="date">
|
||||
</label>
|
||||
<label class="check-field admin-field-wide">
|
||||
<input id="edit-agent-profile-training-right" type="checkbox">
|
||||
<span>Droit à l’enveloppe de formation de 14 h</span>
|
||||
</label>
|
||||
<label class="field admin-field-wide">
|
||||
<span>Observations PTA</span>
|
||||
<textarea id="edit-agent-profile-pta-comment" maxlength="1000" rows="3"></textarea>
|
||||
</label>
|
||||
<label class="field admin-field-wide">
|
||||
<span>Lieu d’affectation principal</span>
|
||||
<select id="edit-agent-profile-structure">
|
||||
<option value="">Sans lieu principal</option>
|
||||
<?php foreach ($structures as $structure): ?>
|
||||
<option value="<?= (int) $structure['id_structure'] ?>">
|
||||
<?= htmlspecialchars($structure['nom']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div id="agent-profile-message" class="message" role="status" aria-live="polite"></div>
|
||||
<div class="admin-form-actions modal-actions">
|
||||
<button id="cancel-agent-profile" type="button" class="button button-secondary">Annuler</button>
|
||||
<button type="submit" class="button button-primary">Enregistrer les informations</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
<span class="dashboard-tile-label">Lieux à paramétrer</span>
|
||||
<strong class="dashboard-tile-value" id="dashboard-setup-count">—</strong>
|
||||
<small class="dashboard-tile-detail" id="dashboard-setup-detail">Sans horaires de couverture attendus</small>
|
||||
<span class="dashboard-tile-cta">Ouvrir l’administration</span>
|
||||
<span class="dashboard-tile-cta">Ouvrir l'administration</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
</div>
|
||||
<a class="button button-secondary" href="pending.php">Tout voir</a>
|
||||
</div>
|
||||
<div id="dashboard-pending-empty" class="empty-state" hidden>Aucun planning n’attend de validation.</div>
|
||||
<div id="dashboard-pending-empty" class="empty-state" hidden>Aucun planning n'attend de validation.</div>
|
||||
<ul class="dashboard-list" id="dashboard-pending-list"></ul>
|
||||
</section>
|
||||
|
||||
@@ -154,9 +154,9 @@
|
||||
<strong>Ouvrir un PTA détaillé</strong>
|
||||
<small>Souhaits, contraintes et répartition.</small>
|
||||
</a>
|
||||
<a class="dashboard-shortcut" href="administration.php">
|
||||
<strong>Gérer les référentiels</strong>
|
||||
<small>Agents, lieux, postes, couverture.</small>
|
||||
<a class="dashboard-shortcut" href="manage_agents.php">
|
||||
<strong>Gérer les agents</strong>
|
||||
<small>Créer, corriger une fiche, changer un rattachement.</small>
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
229
app/Views/partials/dsi.php
Normal file
229
app/Views/partials/dsi.php
Normal file
@@ -0,0 +1,229 @@
|
||||
<?php
|
||||
/**
|
||||
* Espace DSI : comptes d'accès, jetons et journal d'activité.
|
||||
* Module JS : `dsi`.
|
||||
*
|
||||
* Partiel de contenu inclus par la vue de page homonyme. Les identifiants HTML
|
||||
* posés ici sont les points d'ancrage du module JS : les renommer casse le
|
||||
* module sans aucune erreur visible.
|
||||
*
|
||||
* Aucune donnée n'est rendue côté PHP : la page est entièrement alimentée par
|
||||
* api/dsi_comptes.php et api/dsi_journal.php. C'est ce qui permet de rafraîchir
|
||||
* le journal sans recharger l'écran, et d'éviter que le HTML servi porte la
|
||||
* moindre information sur les comptes.
|
||||
*/
|
||||
?>
|
||||
<section class="page-content dsi-page">
|
||||
<div id="dsi-installation-warning" class="message message-error dsi-installation-warning" role="alert" hidden>
|
||||
Le compte DSI utilise encore le jeton d'installation, publié en clair dans la migration SQL :
|
||||
<strong>régénérez-le maintenant</strong>, il donne un accès complet à cette page.
|
||||
</div>
|
||||
|
||||
<!--
|
||||
Panneau des jetons fraîchement produits. C'est la SEULE occasion de les
|
||||
lire : la base n'en conserve qu'une empreinte. Il reste ouvert tant que
|
||||
la DSI ne l'a pas explicitement refermé, pour laisser le temps de les
|
||||
recopier ou de les imprimer.
|
||||
-->
|
||||
<section id="dsi-token-panel" class="card dsi-token-panel" hidden>
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2>Jetons à transmettre</h2>
|
||||
<p class="section-subtitle">Ces jetons ne seront plus jamais affichés : la base n'en garde qu'une empreinte. Copiez-les, transmettez-les à leurs titulaires, puis refermez ce panneau.</p>
|
||||
</div>
|
||||
<div class="button-group">
|
||||
<button id="dsi-token-copy" class="button button-secondary" type="button">Tout copier</button>
|
||||
<button id="dsi-token-close" class="button button-secondary" type="button">Fermer</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="dsi-token-list" class="dsi-token-list"></div>
|
||||
<div id="dsi-token-message" class="message" role="status" aria-live="polite"></div>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2>Comptes d'accès</h2>
|
||||
<p class="section-subtitle">Un compte par utilisateur : agents, responsables de lieu, administrateurs du Service Enfance et DSI. Chacun se connecte avec son jeton, il n’y a aucun mot de passe dans PTA.</p>
|
||||
</div>
|
||||
<button id="dsi-generate-missing" class="button button-secondary" type="button">
|
||||
Générer les jetons manquants <span id="dsi-missing-count" class="nav-count-badge" hidden>0</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form id="dsi-account-form" class="admin-form">
|
||||
<div class="admin-form-grid">
|
||||
<label class="field">
|
||||
<span>Type de compte</span>
|
||||
<select id="dsi-account-role" required>
|
||||
<option value="SERVICE_ENFANCE">Service Enfance (administrateur)</option>
|
||||
<option value="DSI">DSI</option>
|
||||
<option value="AGENT">Agent</option>
|
||||
<option value="RESPONSABLE_STRUCTURE">Responsable de structure</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field" id="dsi-account-agent-field" hidden>
|
||||
<span>Agent titulaire</span>
|
||||
<select id="dsi-account-agent"><option value="">Choisir un agent sans compte</option></select>
|
||||
</label>
|
||||
<label class="field" id="dsi-account-structure-field" hidden>
|
||||
<span>Lieu titulaire</span>
|
||||
<select id="dsi-account-structure"><option value="">Choisir un lieu sans compte</option></select>
|
||||
</label>
|
||||
<label class="field" id="dsi-account-label-field">
|
||||
<span>Nom du titulaire</span>
|
||||
<input id="dsi-account-label" type="text" maxlength="180" placeholder="Ex. Service Enfance — Camille Roux">
|
||||
</label>
|
||||
<label class="field admin-field-wide">
|
||||
<span>Commentaire (facultatif)</span>
|
||||
<input id="dsi-account-comment" type="text" maxlength="255" placeholder="Ex. remplacement congé maternité, jusqu'en septembre">
|
||||
</label>
|
||||
</div>
|
||||
<p id="dsi-account-hint" class="hint"></p>
|
||||
<div class="admin-form-actions">
|
||||
<div id="dsi-account-message" class="message" role="status" aria-live="polite"></div>
|
||||
<button class="button button-primary" type="submit">Créer le compte et générer son jeton</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="selection-grid overview-filters">
|
||||
<label class="field">
|
||||
<span>Type</span>
|
||||
<select id="dsi-filter-role">
|
||||
<option value="">Tous les types</option>
|
||||
<option value="DSI">DSI</option>
|
||||
<option value="SERVICE_ENFANCE">Service Enfance</option>
|
||||
<option value="RESPONSABLE_STRUCTURE">Responsable de structure</option>
|
||||
<option value="AGENT">Agent</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>État</span>
|
||||
<select id="dsi-filter-state">
|
||||
<option value="">Tous</option>
|
||||
<option value="1">Actifs</option>
|
||||
<option value="0">Désactivés</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Rechercher</span>
|
||||
<input id="dsi-filter-search" type="search" maxlength="80" placeholder="Nom, matricule ou début de jeton">
|
||||
</label>
|
||||
<div class="overview-filter-actions">
|
||||
<button id="dsi-accounts-refresh" class="button button-secondary filter-button" type="button">Actualiser</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="dsi-accounts-message" class="message" role="status" aria-live="polite"></div>
|
||||
<div class="table-scroll">
|
||||
<table class="planning-table dsi-accounts-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Titulaire</th>
|
||||
<th>Type</th>
|
||||
<th>Jeton</th>
|
||||
<th>Dernière connexion</th>
|
||||
<th>État</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="dsi-accounts-body">
|
||||
<tr><td colspan="6">Chargement des comptes…</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2>Journal d'activité</h2>
|
||||
<p class="section-subtitle">Connexions, écritures en base et refus d'accès. Les lectures ne sont pas tracées : elles noieraient le journal sans rien apprendre.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="dsi-journal-stats" class="summary-grid compact-summary"></div>
|
||||
|
||||
<div class="selection-grid overview-filters dsi-journal-filters">
|
||||
<label class="field">
|
||||
<span>Catégorie</span>
|
||||
<select id="dsi-journal-category">
|
||||
<option value="">Toutes</option>
|
||||
<option value="CONNEXION">Connexions</option>
|
||||
<option value="ECRITURE">Écritures</option>
|
||||
<option value="ACCES_REFUSE">Refus d'accès</option>
|
||||
<option value="ADMINISTRATION">Administration</option>
|
||||
<option value="SYSTEME">Système</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Résultat</span>
|
||||
<select id="dsi-journal-result">
|
||||
<option value="">Tous</option>
|
||||
<option value="SUCCES">Succès</option>
|
||||
<option value="REFUS">Refus</option>
|
||||
<option value="ERREUR">Erreur</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Rôle</span>
|
||||
<select id="dsi-journal-role">
|
||||
<option value="">Tous</option>
|
||||
<option value="DSI">DSI</option>
|
||||
<option value="SERVICE_ENFANCE">Service Enfance</option>
|
||||
<option value="RESPONSABLE_STRUCTURE">Responsable de structure</option>
|
||||
<option value="AGENT">Agent</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Du</span>
|
||||
<input id="dsi-journal-from" type="date">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Au</span>
|
||||
<input id="dsi-journal-to" type="date">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Rechercher</span>
|
||||
<input id="dsi-journal-search" type="search" maxlength="80" placeholder="Acteur, action, adresse IP">
|
||||
</label>
|
||||
<div class="overview-filter-actions">
|
||||
<button id="dsi-journal-refresh" class="button button-secondary filter-button" type="button">Actualiser</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="dsi-journal-message" class="message" role="status" aria-live="polite"></div>
|
||||
<div class="table-scroll">
|
||||
<table class="planning-table dsi-journal-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Catégorie</th>
|
||||
<th>Acteur</th>
|
||||
<th>Action</th>
|
||||
<th>Détail</th>
|
||||
<th>Origine</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="dsi-journal-body">
|
||||
<tr><td colspan="6">Chargement du journal…</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="dsi-pagination">
|
||||
<button id="dsi-journal-previous" class="button button-secondary" type="button" disabled>Page précédente</button>
|
||||
<span id="dsi-journal-page-label">Page 1</span>
|
||||
<button id="dsi-journal-next" class="button button-secondary" type="button" disabled>Page suivante</button>
|
||||
</div>
|
||||
|
||||
<form id="dsi-purge-form" class="inline-form dsi-purge-form">
|
||||
<label class="field">
|
||||
<span>Purger les entrées de plus de</span>
|
||||
<input id="dsi-purge-days" type="number" min="30" max="3650" value="365" required>
|
||||
</label>
|
||||
<span class="hint">jours (30 minimum : une purge trop courte effacerait les traces d'un incident avant qu'il soit constaté).</span>
|
||||
<button class="button button-danger" type="submit">Purger</button>
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
@@ -19,16 +19,16 @@ $roleActuel = (string) ($accessProfile['role'] ?? '');
|
||||
* droit, plutôt que de masquer les étapes inaccessibles.
|
||||
*/
|
||||
$sommaire = [
|
||||
'faq-parcours' => 'Le parcours en un coup d’œil',
|
||||
'faq-roles' => 'Qui fait quoi : les trois rôles',
|
||||
'faq-lieux' => '1. Ajouter un lieu d’affectation',
|
||||
'faq-parcours' => 'Le parcours en un coup d\'œil',
|
||||
'faq-roles' => 'Qui fait quoi : les quatre rôles',
|
||||
'faq-lieux' => '1. Ajouter un lieu d\'affectation',
|
||||
'faq-agents' => '2. Ajouter un agent',
|
||||
'faq-couverture' => '3. Définir les horaires d’ouverture',
|
||||
'faq-couverture' => '3. Définir les horaires d\'ouverture',
|
||||
'faq-vacances' => '4. Renseigner les vacances scolaires',
|
||||
'faq-saisie' => '5. Saisir un planning pour un agent',
|
||||
'faq-validation' => '6. Contrôler et valider un planning',
|
||||
'faq-equipes' => '7. Composer les équipes',
|
||||
'faq-suivi' => '8. Suivre l’année et éditer les PDF',
|
||||
'faq-suivi' => '8. Suivre l\'année et éditer les PDF',
|
||||
'faq-quota' => 'Comprendre le quota annuel et les motifs',
|
||||
'faq-questions' => 'Questions fréquentes',
|
||||
];
|
||||
@@ -73,8 +73,8 @@ $sommaire = [
|
||||
<article class="card faq-section" id="faq-roles">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2>Qui fait quoi : les trois rôles</h2>
|
||||
<p class="section-subtitle">Votre rôle détermine les onglets visibles et les actions autorisées. Il se choisit à l’ouverture de l’application et se change à tout moment par le bouton « Changer de rôle », en haut à droite.</p>
|
||||
<h2>Qui fait quoi : les quatre rôles</h2>
|
||||
<p class="section-subtitle">Votre rôle détermine les onglets visibles et les actions autorisées. Il ne se choisit pas : il est porté par le jeton d’accès que la DSI vous a remis, et que vous saisissez à l’ouverture de l’application. Le bouton « Se déconnecter », en haut à droite, referme la session.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-scroll">
|
||||
@@ -99,24 +99,31 @@ $sommaire = [
|
||||
</tr>
|
||||
<tr<?= $roleActuel === 'SERVICE_ENFANCE' ? ' class="faq-row-current"' : '' ?>>
|
||||
<td><strong>Service Enfance</strong></td>
|
||||
<td>Tous les lieux, tous les agents, tous les écrans.</td>
|
||||
<td>Tous les lieux, tous les agents, tous les écrans métier.</td>
|
||||
<td>Paramétrer, saisir, composer les équipes, <strong>valider</strong> et rouvrir les plannings.</td>
|
||||
</tr>
|
||||
<tr<?= $roleActuel === 'DSI' ? ' class="faq-row-current"' : '' ?>>
|
||||
<td><strong>DSI</strong></td>
|
||||
<td>Uniquement les comptes d’accès et le journal d’activité. Aucun planning, aucun agent.</td>
|
||||
<td>Créer, désactiver et régénérer les jetons ; consulter les connexions, les écritures et les refus d’accès.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<p class="hint">Un planning validé n’est plus modifiable par un responsable de structure : il faut demander au Service Enfance de le rouvrir, ce qui le repasse en brouillon.</p>
|
||||
<p class="hint"><strong>Comptes Service Enfance et DSI :</strong> le jeton ne suffit pas. Un mot de passe, que vous choisissez à votre première connexion et que personne d’autre ne connaît, vous est demandé ensuite. Le bouton « Mot de passe », en haut à droite, permet d’en changer ; en cas d’oubli, la DSI l’efface et vous en choisissez un nouveau.</p>
|
||||
<p class="hint"><strong>Votre jeton vaut votre signature :</strong> toutes vos écritures sont journalisées à votre nom. Il ne se prête pas. Jeton perdu ou divulgué, la DSI en génère un nouveau, ce qui rend l’ancien inutilisable sur-le-champ.</p>
|
||||
</article>
|
||||
|
||||
<article class="card faq-section" id="faq-lieux">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2>1. Ajouter un lieu d’affectation</h2>
|
||||
<p class="section-subtitle">Onglet <strong>Administration</strong> — réservé au Service Enfance. Un lieu doit exister avant qu’un agent puisse y être rattaché ou planifié.</p>
|
||||
<p class="section-subtitle">Groupe <strong>Management</strong>, onglet <strong>Lieux d’affectation</strong> — réservé au Service Enfance. Un lieu doit exister avant qu’un agent puisse y être rattaché ou planifié.</p>
|
||||
</div>
|
||||
</div>
|
||||
<ol class="faq-steps">
|
||||
<li>Ouvrez <strong>Administration</strong>, encadré « Ajouter un lieu d’affectation ».</li>
|
||||
<li>Ouvrez <strong>Management → Lieux d’affectation</strong>, encadré « Ajouter un lieu d’affectation ».</li>
|
||||
<li>Saisissez le <strong>code</strong> : un identifiant court et stable, sans espace, par exemple <code>CRECHE_MATUSALEME</code>. Il ne s’affiche que dans les tableaux techniques.</li>
|
||||
<li>Saisissez le <strong>nom</strong> tel qu’il doit apparaître partout ailleurs, par exemple « Crèche Matusalème ».</li>
|
||||
<li>Choisissez le <strong>type d’affectation</strong> : <em>Périscolaire</em> (autour du temps scolaire) ou <em>Extrascolaire</em> (mercredis et vacances). Ce choix appartient au lieu, pas à l’agent.</li>
|
||||
@@ -132,7 +139,7 @@ $sommaire = [
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2>2. Ajouter un agent</h2>
|
||||
<p class="section-subtitle">Onglet <strong>Administration</strong> — réservé au Service Enfance. La fiche agent détermine son quota annuel et les motifs qu’on pourra lui affecter.</p>
|
||||
<p class="section-subtitle">Groupe <strong>Management</strong>, onglet <strong>Agents</strong> — réservé au Service Enfance. La fiche agent détermine son quota annuel et les motifs qu’on pourra lui affecter.</p>
|
||||
</div>
|
||||
</div>
|
||||
<ol class="faq-steps">
|
||||
@@ -147,7 +154,8 @@ $sommaire = [
|
||||
<li><strong>Observations utiles à la construction du PTA</strong> : disponibilités particulières, organisation connue… Ces notes se retrouvent sur l’écran « PTA détaillé ».</li>
|
||||
<li>Cliquez sur <strong>Ajouter l’agent</strong>.</li>
|
||||
</ol>
|
||||
<p class="hint">Pour corriger une fiche existante, utilisez le tableau « Informations des agents » en bas de la page : le bouton <strong>Modifier</strong> ouvre la fiche complète, lieu principal compris.</p>
|
||||
<p class="hint"><strong>Dates de contrat :</strong> elles bornent la planification. Aucune affectation — pas même un congé — ne peut être posée avant la date de début ou après la date de fin. Un contrat sans date de fin est considéré comme à durée indéterminée. Raccourcir un contrat est refusé tant qu’il reste des affectations au-delà : supprimez-les d’abord.</p>
|
||||
<p class="hint">Pour corriger une fiche existante, utilisez le tableau « Informations des agents » du même onglet : le bouton <strong>Modifier</strong> ouvre la fiche complète, nom, prénom et lieu principal compris. Seuls le <strong>matricule</strong> et la <strong>quotité de travail</strong> restent hors d’atteinte : le premier sert de clé avec les outils RH, la seconde détermine le quota déjà consommé de l’année.</p>
|
||||
</article>
|
||||
|
||||
<article class="card faq-section" id="faq-couverture">
|
||||
@@ -388,7 +396,7 @@ $sommaire = [
|
||||
</div>
|
||||
<dl class="faq-qa">
|
||||
<dt>Je ne vois pas l’onglet dont on me parle.</dt>
|
||||
<dd>Les onglets dépendent du rôle sélectionné. Vérifiez le badge en haut à droite et, si nécessaire, utilisez « Changer de rôle ». Si l’onglet reste absent, votre rôle n’y a pas droit.</dd>
|
||||
<dd>Les onglets dépendent du rôle porté par votre jeton. Vérifiez le badge en haut à droite : si l’onglet attendu n’apparaît pas, votre rôle n’y a pas droit, et c’est la DSI qui délivre un autre accès.</dd>
|
||||
|
||||
<dt>Impossible de choisir un agent dans l’écran de saisie.</dt>
|
||||
<dd>Il faut d’abord choisir un lieu d’affectation : la liste des agents en dépend. La semaine ne se débloque qu’une fois l’agent choisi.</dd>
|
||||
|
||||
278
app/Views/partials/manage_agents.php
Normal file
278
app/Views/partials/manage_agents.php
Normal file
@@ -0,0 +1,278 @@
|
||||
<?php
|
||||
/**
|
||||
* Management — Agents : création, liste et fiche complète des agents.
|
||||
* Module JS : `administration`.
|
||||
*
|
||||
* Partiel de contenu inclus par la vue de page homonyme. Les identifiants HTML
|
||||
* posés ici sont les points d'ancrage du module JS : les renommer casse le
|
||||
* module sans aucune erreur visible.
|
||||
*
|
||||
* V7.14 : ce contenu vivait dans le partiel « administration », qui mêlait les
|
||||
* agents, les lieux et les horaires de couverture. Chaque objet a désormais son
|
||||
* onglet, sous le groupe « Management » de la barre de navigation.
|
||||
*
|
||||
* L'ordre est celui du travail réel : on crée un agent, puis on consulte ou
|
||||
* corrige sa fiche dans le tableau qui suit.
|
||||
*/
|
||||
?>
|
||||
<section id="manage-agents-view" class="app-view page-content">
|
||||
<section class="card admin-card">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2>Ajouter un agent</h2>
|
||||
<p class="section-subtitle">L’agent reçoit un lieu principal à sa création, mais peut être intégré aussi bien aux équipes périscolaires qu’extrascolaires et travailler ponctuellement sur d’autres lieux.</p>
|
||||
</div>
|
||||
</div>
|
||||
<form id="create-agent-form" class="admin-form">
|
||||
<div class="admin-form-grid">
|
||||
<label class="field">
|
||||
<span>Matricule <strong>*</strong></span>
|
||||
<input id="new-agent-matricule" name="matricule" type="text" maxlength="50" required>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Poste PTA <strong>*</strong></span>
|
||||
<select id="new-agent-post" name="id_poste" required>
|
||||
<option value="">Choisir un poste</option>
|
||||
<?php foreach ($posts as $post): ?>
|
||||
<option value="<?= (int) $post['id_poste'] ?>"><?= htmlspecialchars($post['libelle']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Type de contrat <strong>*</strong></span>
|
||||
<select id="new-agent-contract-type" required>
|
||||
<option value="PERMANENT">Permanent — soumis au PTA</option>
|
||||
<option value="TEMPORAIRE">Temporaire — hors périmètre PTA</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Lieu d’affectation principal <strong>*</strong></span>
|
||||
<select id="new-agent-structure" name="structure_id" required>
|
||||
<option value="">Choisir un lieu d’affectation</option>
|
||||
<?php foreach ($structures as $structure): ?>
|
||||
<option value="<?= (int) $structure['id_structure'] ?>">
|
||||
<?= htmlspecialchars($structure['nom']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Nom <strong>*</strong></span>
|
||||
<input id="new-agent-name" name="nom" type="text" maxlength="100" required>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Prénom <strong>*</strong></span>
|
||||
<input id="new-agent-firstname" name="prenom" type="text" maxlength="100" required>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>E-mail</span>
|
||||
<input id="new-agent-email" name="email" type="email" maxlength="255" placeholder="prenom.nom@exemple.fr">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Téléphone</span>
|
||||
<input id="new-agent-phone" name="telephone" type="tel" maxlength="30" placeholder="Ex. 06 12 34 56 78">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Qualification</span>
|
||||
<select id="new-agent-qualified" name="est_diplome">
|
||||
<option value="0">Non diplômé</option>
|
||||
<option value="1">Diplômé</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Diplôme</span>
|
||||
<input id="new-agent-diploma" name="diplome_libelle" type="text" maxlength="150" placeholder="Ex. BAFA, CAP AEPE…" disabled>
|
||||
</label>
|
||||
<label class="field admin-field-wide">
|
||||
<span>Adresse personnelle</span>
|
||||
<textarea id="new-agent-address" name="adresse" maxlength="255" rows="2" placeholder="Adresse de l’agent"></textarea>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Quotité de travail <strong>*</strong></span>
|
||||
<div class="input-suffix">
|
||||
<input id="new-agent-rate" name="quotite_travail" type="number" min="1" max="100" step="0.01" value="100" required>
|
||||
<span>%</span>
|
||||
</div>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Date de début du contrat <strong>*</strong></span>
|
||||
<input id="new-agent-contract-start" name="date_debut_contrat" type="date" value="<?= date('Y-m-d') ?>" required>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Date de fin du contrat</span>
|
||||
<input id="new-agent-contract-end" name="date_fin_contrat" type="date">
|
||||
</label>
|
||||
<label class="check-field admin-field-wide">
|
||||
<input id="new-agent-training-right" type="checkbox" checked>
|
||||
<span>Droit à l’enveloppe de formation de 14 h. Décoché, le motif « Formation » ne pourra pas être affecté à cet agent lors de la saisie des horaires.</span>
|
||||
</label>
|
||||
<label class="field admin-field-wide">
|
||||
<span>Observations utiles à la construction du PTA</span>
|
||||
<textarea id="new-agent-pta-comment" maxlength="1000" rows="3" placeholder="Disponibilités particulières, organisation connue…"></textarea>
|
||||
</label>
|
||||
</div>
|
||||
<p class="hint">Le quota PTA est suivi sur l’année civile, du 1er janvier au 31 décembre. À 100 %, la référence est de 1 607 h. La date de début du contrat reste une information RH mais ne décale pas l’année de référence.</p>
|
||||
<div class="admin-form-actions">
|
||||
<div id="create-agent-message" class="message" role="status" aria-live="polite"></div>
|
||||
<button class="button button-primary" type="submit">Ajouter l’agent</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2>Informations des agents</h2>
|
||||
<p class="section-subtitle">Consultez la qualification, les coordonnées et le lieu principal de chaque agent. Le bouton Modifier ouvre la fiche complète.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="assignment-message" class="message" role="status" aria-live="polite"></div>
|
||||
<div class="table-scroll">
|
||||
<table class="planning-table assignment-table agent-information-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Agent</th>
|
||||
<th>Matricule</th>
|
||||
<th>Poste</th>
|
||||
<th>Qualification</th>
|
||||
<th>Téléphone</th>
|
||||
<th>Début contrat</th>
|
||||
<th>Lieu principal</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="agent-assignment-body">
|
||||
<?php foreach ($agents as $agent): ?>
|
||||
<tr
|
||||
data-agent-id="<?= (int) $agent['id_agent'] ?>"
|
||||
data-prenom="<?= htmlspecialchars((string) $agent['prenom'], ENT_QUOTES) ?>"
|
||||
data-nom="<?= htmlspecialchars((string) $agent['nom'], ENT_QUOTES) ?>"
|
||||
data-email="<?= htmlspecialchars((string) ($agent['email'] ?? ''), ENT_QUOTES) ?>"
|
||||
data-telephone="<?= htmlspecialchars((string) ($agent['telephone'] ?? ''), ENT_QUOTES) ?>"
|
||||
data-adresse="<?= htmlspecialchars((string) ($agent['adresse'] ?? ''), ENT_QUOTES) ?>"
|
||||
data-est-diplome="<?= !empty($agent['est_diplome']) ? '1' : '0' ?>"
|
||||
data-diplome-libelle="<?= htmlspecialchars((string) ($agent['diplome_libelle'] ?? ''), ENT_QUOTES) ?>"
|
||||
data-date-debut-contrat="<?= htmlspecialchars((string) ($agent['date_debut_contrat'] ?? ''), ENT_QUOTES) ?>"
|
||||
data-date-fin-contrat="<?= htmlspecialchars((string) ($agent['date_fin_contrat'] ?? ''), ENT_QUOTES) ?>"
|
||||
data-id-poste="<?= (int) ($agent['id_poste'] ?? 0) ?>"
|
||||
data-type-contrat="<?= htmlspecialchars((string) ($agent['type_contrat'] ?? 'PERMANENT'), ENT_QUOTES) ?>"
|
||||
data-droit-enveloppe-formation="<?= !isset($agent['droit_enveloppe_formation']) || !empty($agent['droit_enveloppe_formation']) ? '1' : '0' ?>"
|
||||
data-commentaire-pta="<?= htmlspecialchars((string) ($agent['commentaire_pta'] ?? ''), ENT_QUOTES) ?>"
|
||||
data-structure-id="<?= (int) ($agent['id_structure'] ?? 0) ?>"
|
||||
>
|
||||
<td><strong class="agent-row-name"><?= htmlspecialchars($agent['prenom'] . ' ' . $agent['nom']) ?></strong></td>
|
||||
<td><?= htmlspecialchars($agent['matricule']) ?></td>
|
||||
<td class="agent-row-post"><?= htmlspecialchars((string) (($agent['poste_libelle'] ?? '') ?: 'Non renseigné')) ?></td>
|
||||
<td class="agent-row-qualification">
|
||||
<?= !empty($agent['est_diplome'])
|
||||
? 'Diplômé' . (!empty($agent['diplome_libelle']) ? ' — ' . htmlspecialchars($agent['diplome_libelle']) : '')
|
||||
: 'Non diplômé' ?>
|
||||
</td>
|
||||
<td class="agent-row-phone"><?= htmlspecialchars((string) (($agent['telephone'] ?? '') ?: 'Non renseigné')) ?></td>
|
||||
<td class="agent-row-contract-start"><?= htmlspecialchars((string) (($agent['date_debut_contrat'] ?? '') ?: 'Non renseignée')) ?></td>
|
||||
<td class="agent-row-structure"><?= htmlspecialchars((string) (($agent['structure_nom'] ?? '') ?: 'Sans lieu principal')) ?></td>
|
||||
<td><button type="button" class="button button-secondary edit-agent-profile">Modifier</button></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<div id="agent-profile-modal" class="modal-backdrop" hidden>
|
||||
<section class="modal-card" role="dialog" aria-modal="true" aria-labelledby="agent-profile-modal-title">
|
||||
<div class="section-heading modal-heading">
|
||||
<div>
|
||||
<h2 id="agent-profile-modal-title">Modifier les informations de l’agent</h2>
|
||||
<p id="agent-profile-modal-subtitle" class="section-subtitle"></p>
|
||||
</div>
|
||||
<button id="close-agent-profile-modal" type="button" class="button button-ghost modal-close" aria-label="Fermer">×</button>
|
||||
</div>
|
||||
<form id="agent-profile-form" class="admin-form">
|
||||
<input id="edit-agent-profile-id" type="hidden">
|
||||
<div class="admin-form-grid">
|
||||
<?php /* Le matricule reste absent : c'est la clé de rapprochement avec les outils RH. */ ?>
|
||||
<label class="field">
|
||||
<span>Prénom <strong>*</strong></span>
|
||||
<input id="edit-agent-profile-firstname" type="text" maxlength="100" required>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Nom <strong>*</strong></span>
|
||||
<input id="edit-agent-profile-lastname" type="text" maxlength="100" required>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Poste PTA <strong>*</strong></span>
|
||||
<select id="edit-agent-profile-post" required>
|
||||
<option value="">Choisir un poste</option>
|
||||
<?php foreach ($posts as $post): ?>
|
||||
<option value="<?= (int) $post['id_poste'] ?>"><?= htmlspecialchars($post['libelle']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Type de contrat <strong>*</strong></span>
|
||||
<select id="edit-agent-profile-contract-type" required>
|
||||
<option value="PERMANENT">Permanent</option>
|
||||
<option value="TEMPORAIRE">Temporaire</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>E-mail</span>
|
||||
<input id="edit-agent-profile-email" type="email" maxlength="255">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Téléphone</span>
|
||||
<input id="edit-agent-profile-phone" type="tel" maxlength="30" placeholder="Ex. 06 12 34 56 78">
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Qualification</span>
|
||||
<select id="edit-agent-profile-qualified">
|
||||
<option value="0">Non diplômé</option>
|
||||
<option value="1">Diplômé</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Diplôme</span>
|
||||
<input id="edit-agent-profile-diploma" type="text" maxlength="150" placeholder="Ex. BAFA, CAP AEPE…">
|
||||
</label>
|
||||
<label class="field admin-field-wide">
|
||||
<span>Adresse personnelle</span>
|
||||
<textarea id="edit-agent-profile-address" maxlength="255" rows="3"></textarea>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Date de début du contrat <strong>*</strong></span>
|
||||
<input id="edit-agent-profile-contract-start" type="date" required>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Date de fin du contrat</span>
|
||||
<input id="edit-agent-profile-contract-end" type="date">
|
||||
</label>
|
||||
<label class="check-field admin-field-wide">
|
||||
<input id="edit-agent-profile-training-right" type="checkbox">
|
||||
<span>Droit à l’enveloppe de formation de 14 h</span>
|
||||
</label>
|
||||
<label class="field admin-field-wide">
|
||||
<span>Observations PTA</span>
|
||||
<textarea id="edit-agent-profile-pta-comment" maxlength="1000" rows="3"></textarea>
|
||||
</label>
|
||||
<label class="field admin-field-wide">
|
||||
<span>Lieu d’affectation principal</span>
|
||||
<select id="edit-agent-profile-structure">
|
||||
<option value="">Sans lieu principal</option>
|
||||
<?php foreach ($structures as $structure): ?>
|
||||
<option value="<?= (int) $structure['id_structure'] ?>">
|
||||
<?= htmlspecialchars($structure['nom']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<div id="agent-profile-message" class="message" role="status" aria-live="polite"></div>
|
||||
<div class="admin-form-actions modal-actions">
|
||||
<button id="cancel-agent-profile" type="button" class="button button-secondary">Annuler</button>
|
||||
<button type="submit" class="button button-primary">Enregistrer les informations</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
87
app/Views/partials/manage_structures.php
Normal file
87
app/Views/partials/manage_structures.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/**
|
||||
* Management — Lieux d'affectation : création et paramètres des lieux.
|
||||
* Module JS : `administration`.
|
||||
*
|
||||
* Partiel de contenu inclus par la vue de page homonyme. Les identifiants HTML
|
||||
* posés ici sont les points d'ancrage du module JS : les renommer casse le
|
||||
* module sans aucune erreur visible.
|
||||
*
|
||||
* Les HORAIRES DE COUVERTURE d'un lieu ne sont pas ici mais dans l'onglet
|
||||
* « Administration » : ils ne décrivent pas le lieu, ils décrivent une exigence
|
||||
* de présence, contrôlée par l'onglet « Couverture ».
|
||||
*/
|
||||
?>
|
||||
<section id="manage-structures-view" class="app-view page-content">
|
||||
<section class="card admin-card">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2>Ajouter un lieu d’affectation</h2>
|
||||
<p class="section-subtitle">Chaque lieu possède son propre type : périscolaire ou extrascolaire.</p>
|
||||
</div>
|
||||
</div>
|
||||
<form id="create-structure-form" class="admin-form">
|
||||
<div class="admin-form-grid">
|
||||
<label class="field">
|
||||
<span>Code <strong>*</strong></span>
|
||||
<input id="new-structure-code" name="code" type="text" maxlength="50" placeholder="Ex. CRECHE_MATUSALEME" required>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Nom <strong>*</strong></span>
|
||||
<input id="new-structure-name" name="nom" type="text" maxlength="150" placeholder="Ex. Crèche Matusalème" required>
|
||||
</label>
|
||||
<label class="field">
|
||||
<span>Type d’affectation <strong>*</strong></span>
|
||||
<select id="new-structure-type" name="type_affectation" required>
|
||||
<option value="PERISCOLAIRE">Périscolaire</option>
|
||||
<option value="EXTRASCOLAIRE">Extrascolaire</option>
|
||||
</select>
|
||||
</label>
|
||||
<label class="field admin-field-wide">
|
||||
<span>Adresse</span>
|
||||
<input id="new-structure-address" name="adresse" type="text" maxlength="255" placeholder="Adresse du lieu">
|
||||
</label>
|
||||
</div>
|
||||
<div class="admin-form-actions">
|
||||
<div id="create-structure-message" class="message" role="status" aria-live="polite"></div>
|
||||
<button class="button button-primary" type="submit">Ajouter le lieu</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<div class="section-heading">
|
||||
<div>
|
||||
<h2>Paramètres des lieux d’affectation</h2>
|
||||
<p class="section-subtitle">Le type d’affectation est défini au niveau du lieu : par exemple « Crèche Matusalème — Périscolaire ».</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="structure-settings-message" class="message" role="status" aria-live="polite"></div>
|
||||
<div class="table-scroll">
|
||||
<table class="planning-table assignment-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Lieu</th>
|
||||
<th>Code</th>
|
||||
<th>Type d’affectation</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="structure-settings-body">
|
||||
<?php foreach ($structures as $structure): ?>
|
||||
<tr data-structure-id="<?= (int) $structure['id_structure'] ?>">
|
||||
<td><strong><?= htmlspecialchars($structure['nom']) ?></strong></td>
|
||||
<td><?= htmlspecialchars($structure['code']) ?></td>
|
||||
<td>
|
||||
<select class="structure-type-select" aria-label="Type d’affectation de <?= htmlspecialchars($structure['nom']) ?>">
|
||||
<option value="PERISCOLAIRE" <?= ($structure['type_affectation'] ?? 'PERISCOLAIRE') === 'PERISCOLAIRE' ? 'selected' : '' ?>>Périscolaire</option>
|
||||
<option value="EXTRASCOLAIRE" <?= ($structure['type_affectation'] ?? '') === 'EXTRASCOLAIRE' ? 'selected' : '' ?>>Extrascolaire</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><button type="button" class="button button-secondary save-structure-type">Enregistrer</button></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
Reference in New Issue
Block a user