feat(r6): mobile ouvert à tous les rôles — maquette + socle R6.1

R4 avait délibérément fermé le mobile au Technicien. Le référent demande
l'ouverture à tous les rôles avec les mêmes droits qu'au web — nouvelle
release R6 (R4 est close, R4.1/4.2/4.3 déjà pris par le socle mobile
technicien d'origine, pas de réouverture ni de collision de numérotation).

Maquette (docs/02-design/maquettes/maquette-mobile-tous-roles.html, 7
écrans) validée par le référent : D1 barre d'onglets adaptative par rôle
(Technicien/Technicien limité inchangés + onglet Menu ajouté) ; D2 le Menu
reprend à l'identique les 4 groupes du web, même matrice de permissions ;
D3 groupe sans lien visible masqué en entier ; D4 le mobile porte les
actions courantes par famille, pas les flux de gestion les plus denses ;
D5 aucune logique de permission propre au mobile.

R6.1 — socle : usePermissions() mobile calqué sur le web ; barre d'onglets
adaptative (ongletsVisibles par rôle, href:null masque sans retirer du
navigateur) ; Accueil (dashboard réel pour Administrateur/Gestionnaire/
Dispatcher/Vue seule, aucun chiffre inventé) ; OT (liste complète,
viewOther déjà géré serveur, réutilise la fiche OT R4 telle quelle) ; Menu
(groupes filtrés, vide masqué) ; Demandes (PanneauDemandes — un seul
composant pour tous les rôles : création/suivi Demandeur, approbation/
rejet Gestionnaire/Dispatcher/Administrateur, lecture Vue seule).
Familles pas encore portées : écran « à venir » plutôt qu'un lien mort.

Typecheck propre, 17 tests Jest, lint 5/5 paquets.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
pr-daaif
2026-08-02 11:19:44 +01:00
parent 3e9e708116
commit ab0f260d0f
17 changed files with 1857 additions and 13 deletions

View File

@@ -0,0 +1,764 @@
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SIOP V2 — Maquettes HD · Mobile ouvert à tous les rôles</title>
</head>
<body>
<!--
SIOP V2 — Maquette HD « Mobile ouvert à tous les rôles » (design-first).
À VALIDER PAR LE RÉFÉRENT AVANT TOUT CODE EXPO (principe n°1).
R4 avait délibérément fermé le mobile au Technicien (périmètre R4 D3). Ici :
ouvrir l'app à TOUS les rôles, avec les MÊMES droits qu'au web (aucune
nouvelle logique de permission — le mobile lit la même matrice, ADR-003).
Tokens et gabarit téléphone repris tels quels de maquette-r4.html.
Les 4 écrans terrain déjà livrés et recettés (Ma journée, Scanner, Préventif,
Synchro) restent INCHANGÉS pour le Technicien — cette maquette ajoute une
couche autour, elle ne les remplace pas.
Décisions proposées à validation (récapitulées aussi dans le journal) :
D1 — Barre d'onglets ADAPTATIVE selon le rôle connecté (pas un seul jeu
d'onglets pour tout le monde) ; Technicien/Technicien limité gardent
exactement leurs onglets terrain actuels + un nouvel onglet « Menu ».
D2 — Le Menu mobile reprend EXACTEMENT les 4 groupes du web (Exploitation /
Parc / Ressources / Pilotage), dans le même ordre, filtrés par la même
matrice de permissions déjà en base — aucune règle de droit nouvelle.
D3 — Contrairement au web (le groupe reste affiché même vide), un groupe
SANS AUCUN lien visible est masqué en entier sur mobile — l'écran est
petit, un intitulé de groupe vide n'aide personne.
D4 — Le mobile porte les actions COURANTES de chaque famille (consulter,
créer une pièce/BC simple, approuver/rejeter, inviter) mais pas les
flux de gestion les plus denses (BC multi-lignes détaillé, revue pixel
de document) qui restent réservés au grand écran pour l'instant.
D5 — Le mobile n'acquiert AUCUNE logique de permission propre : un hook
`usePermissions()` calqué sur celui du web, lisant `me.permissions`
(déjà renvoyé par `/users/me`, déjà mis en cache par l'app).
-->
<style>
/* ═══ Tokens (répliques de ../tokens.css) ═══ */
:root{
--primaire:#1f4fb8;--primaire-actif:#1a439c;--primaire-doux:#eaf0fb;
--safran:#dd8a0b;--safran-doux:#fdf3e3;
--fond:#f5f7fa;--surface:#ffffff;--surface-2:#eef2f7;
--bordure:#dce3ec;--bordure-forte:#b9c4d4;
--encre:#1b2534;--encre-2:#55647a;--encre-3:#8494ab;
--st-ouvert:#3d6fe0;--st-ouvert-fond:#e9effc;--st-encours:#6d5bd8;--st-encours-fond:#efecfa;
--st-attente:#b96f07;--st-attente-fond:#fbf1df;--st-termine:#178a50;--st-termine-fond:#e6f5ec;
--st-annule:#68788f;--st-annule-fond:#edf0f4;
--prio-bloque:#d92626;--prio-bloque-fond:#fdeaea;--prio-haute:#d92626;
--prio-moyenne:#b96f07;--prio-basse:#178a50;--prio-aucune:#8494ab;
--succes:#178a50;--alerte:#b96f07;--danger:#d92626;--info:#3d6fe0;
--graf-1:#3b69d6;--graf-2:#dd8a0b;--graf-3:#1f9e8e;--graf-4:#8a63d2;--graf-grille:#e6ebf2;
--nav-fond:#16233b;--nav-encre:#c6d2e4;--nav-encre-vif:#ffffff;--nav-bordure:#24344f;
--police-ui:"Manrope","Avenir Next","Segoe UI Variable",system-ui,sans-serif;
--rayon:10px;--rayon-controle:8px;--ombre-menu:0 8px 24px rgb(16 30 54/.14);
--transition:140ms ease-out;
}
@media (prefers-color-scheme:dark){:root{
--primaire:#6e92e8;--primaire-actif:#8facf0;--primaire-doux:#1d2c4c;
--safran:#e89a1f;--safran-doux:#33270f;
--fond:#0f1622;--surface:#182234;--surface-2:#1e2a40;
--bordure:#2b3850;--bordure-forte:#3d4d6b;
--encre:#e8edf5;--encre-2:#a7b4c8;--encre-3:#6d7d96;
--st-ouvert:#7da2ee;--st-ouvert-fond:#1c2a47;--st-encours:#a394ec;--st-encours-fond:#262040;
--st-attente:#e0a33c;--st-attente-fond:#322510;--st-termine:#4bc084;--st-termine-fond:#12301f;
--st-annule:#93a3ba;--st-annule-fond:#222d3f;
--prio-bloque:#f26d6d;--prio-bloque-fond:#3a1414;--prio-haute:#f26d6d;
--prio-moyenne:#e0a33c;--prio-basse:#4bc084;--prio-aucune:#6d7d96;
--succes:#4bc084;--alerte:#e0a33c;--danger:#f26d6d;--info:#7da2ee;
--graf-1:#5580e4;--graf-2:#c7820a;--graf-3:#26a896;--graf-4:#9673de;--graf-grille:#24304a;
--nav-fond:#0c1524;--nav-encre:#a7b4c8;--nav-encre-vif:#f2f6fc;--nav-bordure:#1e2c44;
--ombre-menu:0 8px 24px rgb(0 0 0/.45);
}}
:root[data-theme="light"]{
--primaire:#1f4fb8;--primaire-actif:#1a439c;--primaire-doux:#eaf0fb;
--safran:#dd8a0b;--safran-doux:#fdf3e3;
--fond:#f5f7fa;--surface:#ffffff;--surface-2:#eef2f7;
--bordure:#dce3ec;--bordure-forte:#b9c4d4;
--encre:#1b2534;--encre-2:#55647a;--encre-3:#8494ab;
--st-ouvert:#3d6fe0;--st-ouvert-fond:#e9effc;--st-encours:#6d5bd8;--st-encours-fond:#efecfa;
--st-attente:#b96f07;--st-attente-fond:#fbf1df;--st-termine:#178a50;--st-termine-fond:#e6f5ec;
--st-annule:#68788f;--st-annule-fond:#edf0f4;
--prio-bloque:#d92626;--prio-bloque-fond:#fdeaea;--prio-haute:#d92626;
--prio-moyenne:#b96f07;--prio-basse:#178a50;--prio-aucune:#8494ab;
--succes:#178a50;--alerte:#b96f07;--danger:#d92626;--info:#3d6fe0;
--graf-1:#3b69d6;--graf-2:#dd8a0b;--graf-3:#1f9e8e;--graf-4:#8a63d2;--graf-grille:#e6ebf2;
--nav-fond:#16233b;--nav-encre:#c6d2e4;--nav-encre-vif:#ffffff;--nav-bordure:#24344f;
--ombre-menu:0 8px 24px rgb(16 30 54/.14);
}
:root[data-theme="dark"]{
--primaire:#6e92e8;--primaire-actif:#8facf0;--primaire-doux:#1d2c4c;
--safran:#e89a1f;--safran-doux:#33270f;
--fond:#0f1622;--surface:#182234;--surface-2:#1e2a40;
--bordure:#2b3850;--bordure-forte:#3d4d6b;
--encre:#e8edf5;--encre-2:#a7b4c8;--encre-3:#6d7d96;
--st-ouvert:#7da2ee;--st-ouvert-fond:#1c2a47;--st-encours:#a394ec;--st-encours-fond:#262040;
--st-attente:#e0a33c;--st-attente-fond:#322510;--st-termine:#4bc084;--st-termine-fond:#12301f;
--st-annule:#93a3ba;--st-annule-fond:#222d3f;
--prio-bloque:#f26d6d;--prio-bloque-fond:#3a1414;--prio-haute:#f26d6d;
--prio-moyenne:#e0a33c;--prio-basse:#4bc084;--prio-aucune:#6d7d96;
--succes:#4bc084;--alerte:#e0a33c;--danger:#f26d6d;--info:#7da2ee;
--graf-1:#5580e4;--graf-2:#c7820a;--graf-3:#26a896;--graf-4:#9673de;--graf-grille:#24304a;
--nav-fond:#0c1524;--nav-encre:#a7b4c8;--nav-encre-vif:#f2f6fc;--nav-bordure:#1e2c44;
--ombre-menu:0 8px 24px rgb(0 0 0/.45);
}
/* ═══ Base ═══ */
*{box-sizing:border-box;margin:0}
html,body{background:var(--fond)}
body{font-family:var(--police-ui);color:var(--encre);font-size:14px;line-height:1.5}
button{font:inherit;cursor:pointer;border:0;background:none;color:inherit}
button:focus-visible,a:focus-visible{outline:2px solid var(--primaire);outline-offset:2px;border-radius:4px}
.num{font-variant-numeric:tabular-nums}
/* ═══ Barre de revue (outil maquette, pas l'app) ═══ */
.revue{position:sticky;top:0;z-index:50;display:flex;align-items:center;gap:12px;flex-wrap:wrap;
padding:10px 16px;background:var(--nav-fond);color:var(--nav-encre);font-size:13px}
.revue b{color:var(--nav-encre-vif);letter-spacing:.02em}
.revue .onglets{display:flex;gap:4px;flex-wrap:wrap}
.revue .onglets button{padding:6px 10px;border-radius:6px;color:var(--nav-encre)}
.revue .onglets button:hover{background:rgb(255 255 255/.08);color:var(--nav-encre-vif)}
.revue .onglets button[aria-selected="true"]{background:rgb(255 255 255/.12);color:var(--nav-encre-vif);
box-shadow:inset 3px 0 0 var(--safran)}
.revue .droite{margin-left:auto;display:flex;align-items:center;gap:8px}
.revue .theme{padding:6px 10px;border:1px solid var(--nav-bordure);border-radius:6px}
.note-ecran{max-width:1120px;margin:14px auto 0;padding:0 20px;color:var(--encre-2);font-size:13px}
.note-ecran b{color:var(--encre)}
/* ═══ Écrans ═══ */
.ecran{display:none;max-width:1120px;margin:18px auto 44px;padding:0 20px}
.ecran.actif{display:block}
.tels{display:flex;gap:28px;justify-content:center;flex-wrap:wrap}
.tel{width:310px;border:1px solid var(--bordure-forte);border-radius:28px;overflow:hidden;background:var(--fond);
box-shadow:var(--ombre-menu);display:flex;flex-direction:column}
.tel .encoche{height:26px;background:var(--nav-fond);display:flex;justify-content:center;align-items:center;flex:none}
.tel .encoche i{width:70px;height:7px;border-radius:999px;background:rgb(255 255 255/.18)}
.tel .legende{flex:none;text-align:center;font-size:11.5px;font-weight:700;letter-spacing:.05em;
text-transform:uppercase;color:var(--encre-3);padding:8px;background:var(--surface-2)}
.ecran-tel{padding:14px;display:flex;flex-direction:column;gap:10px;min-height:560px}
/* Entête app */
.entete-tel{display:flex;align-items:center;gap:8px}
.entete-tel .flamme{color:var(--safran);font-size:16px}
.entete-tel b{font-size:16px;letter-spacing:.03em}
.entete-tel small{color:var(--encre-3);font-size:10.5px;margin-top:3px}
.entete-tel .fin{margin-left:auto;display:flex;align-items:center;gap:6px}
.avatar-tel{width:26px;height:26px;border-radius:50%;background:var(--st-encours);color:#fff;
display:flex;align-items:center;justify-content:center;font-size:10.5px;font-weight:700}
.sync{font-size:10.5px;font-weight:700;color:var(--st-termine);background:var(--st-termine-fond);
padding:2px 8px;border-radius:999px;white-space:nowrap}
.sync.off{color:var(--st-attente);background:var(--st-attente-fond)}
.titre-tel{font-size:17px;font-weight:800;display:flex;align-items:center;gap:8px}
.titre-tel .filet{margin-left:auto;font-size:11px;color:var(--encre-3);font-weight:600}
/* Bandeaux */
.bandeau-off{display:flex;align-items:center;gap:8px;background:var(--st-attente-fond);color:var(--st-attente);
border:1px solid currentColor;border-radius:10px;padding:8px 10px;font-size:12px;font-weight:700}
.bandeau-urgence{display:flex;align-items:center;gap:8px;background:var(--prio-bloque-fond);color:var(--prio-bloque);
border-radius:10px;padding:8px 10px;font-size:12px;font-weight:800}
.point-pulse{width:8px;height:8px;border-radius:50%;background:currentColor;flex:none}
/* Cartes OT */
.carte-ot-tel{background:var(--surface);border:1px solid var(--bordure);border-radius:12px;padding:11px 12px;
border-left:4px solid transparent}
.carte-ot-tel.bloque{border-left-color:var(--prio-bloque)}
.carte-ot-tel.haute{border-left-color:var(--prio-haute)}
.carte-ot-tel.moyenne{border-left-color:var(--prio-moyenne)}
.carte-ot-tel.basse{border-left-color:var(--prio-basse)}
.carte-ot-tel .type-t{font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--encre-2);
font-weight:700;display:flex;justify-content:space-between;align-items:center;gap:6px}
.carte-ot-tel b{display:block;margin:3px 0 2px;font-size:14px}
.carte-ot-tel .ou{color:var(--encre-2);font-size:12px}
.st{font-size:10.5px;font-weight:700;padding:2px 8px;border-radius:999px;white-space:nowrap}
.st.ouvert{color:var(--st-ouvert);background:var(--st-ouvert-fond)}
.st.encours{color:var(--st-encours);background:var(--st-encours-fond)}
.st.attente{color:var(--st-attente);background:var(--st-attente-fond)}
.st.termine{color:var(--st-termine);background:var(--st-termine-fond)}
.st.annule{color:var(--st-annule);background:var(--st-annule-fond)}
.pastille-file{font-size:10.5px;font-weight:700;color:var(--st-attente);background:var(--st-attente-fond);
padding:2px 8px;border-radius:999px}
/* Boutons */
.btn-tel{display:flex;justify-content:center;align-items:center;gap:8px;padding:12px;border-radius:10px;
font-weight:700;font-size:14.5px;background:var(--primaire);color:#fff;width:100%}
.btn-tel.vert{background:var(--succes)}
.btn-tel.rouge{background:var(--danger)}
.btn-tel.gris{background:var(--bordure-forte);color:var(--surface)}
.btn-tel.contour{background:var(--surface);color:var(--primaire);border:1.5px solid var(--primaire)}
.paire-boutons{display:flex;gap:8px}
.paire-boutons .btn-tel{padding:10px;font-size:13px}
/* Fiche : cartes d'info */
.carte-tel{background:var(--surface);border:1px solid var(--bordure);border-radius:12px;padding:12px;
display:flex;flex-direction:column;gap:8px}
.carte-tel h4{font-size:10.5px;letter-spacing:.07em;text-transform:uppercase;color:var(--encre-2)}
.ligne-info{display:flex;justify-content:space-between;gap:10px;font-size:12.5px}
.ligne-info span:first-child{color:var(--encre-2)}
.ligne-info b{text-align:right}
.garde{font-size:11.5px;color:var(--alerte);font-weight:600}
/* Tabbar */
.tabbar{margin-top:auto;display:flex;border-top:1px solid var(--bordure);background:var(--surface);
padding:6px 4px 8px}
.tabbar a{flex:1;display:flex;flex-direction:column;align-items:center;gap:2px;font-size:9.5px;
font-weight:700;color:var(--encre-3);text-decoration:none;position:relative}
.tabbar a.actif{color:var(--primaire)}
.tabbar a .pic{font-size:17px;line-height:1.2}
.tabbar .badge-tab{position:absolute;top:-3px;right:calc(50% - 16px);background:var(--st-attente);
color:#fff;font-size:9px;font-weight:800;padding:0 5px;border-radius:999px}
.tabbar .badge-tab.rouge{background:var(--prio-bloque)}
/* ═══ Nouveautés propres à cette maquette ═══ */
/* KPI mini-cartes (Accueil / Statistiques) */
.kpis-tel{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.kpi-tel{background:var(--surface);border:1px solid var(--bordure);border-radius:10px;padding:9px 10px}
.kpi-tel .val{font-size:19px;font-weight:800;color:var(--encre)}
.kpi-tel .lbl{font-size:10.5px;color:var(--encre-2);font-weight:600}
.kpi-tel.accent .val{color:var(--primaire)}
/* Raccourcis (Accueil) */
.raccourci-tel{display:flex;align-items:center;gap:10px;background:var(--surface);border:1px solid var(--bordure);
border-radius:10px;padding:10px 11px}
.raccourci-tel .ico{width:28px;height:28px;border-radius:8px;background:var(--primaire-doux);color:var(--primaire);
display:flex;align-items:center;justify-content:center;font-size:14px;flex:none}
.raccourci-tel .txt{flex:1;display:flex;flex-direction:column}
.raccourci-tel b{font-size:12.5px}
.raccourci-tel small{color:var(--encre-2);font-size:11px}
.raccourci-tel .chev{color:var(--encre-3)}
.raccourci-tel .num{font-size:10.5px;font-weight:800;color:var(--st-attente);background:var(--st-attente-fond);
padding:2px 7px;border-radius:999px}
/* Menu groupé (mirroir de la nav web) */
.menu-groupe{display:flex;flex-direction:column;gap:6px}
.menu-groupe .titre-groupe{font-size:10.5px;font-weight:800;letter-spacing:.07em;text-transform:uppercase;
color:var(--encre-3);padding:4px 2px 0}
.menu-item{display:flex;align-items:center;gap:10px;background:var(--surface);border:1px solid var(--bordure);
border-radius:10px;padding:10px 11px}
.menu-item .ico{width:24px;text-align:center;font-size:14px;color:var(--primaire);flex:none}
.menu-item b{flex:1;font-size:12.5px;font-weight:700}
.menu-item .chev{color:var(--encre-3);font-size:13px}
.menu-item .num{font-size:10.5px;font-weight:800;color:var(--st-attente);background:var(--st-attente-fond);
padding:2px 7px;border-radius:999px}
.menu-vide{color:var(--encre-3);font-size:12px;text-align:center;padding:20px 8px;font-weight:600}
/* Formulaire mobile */
.champ-tel{display:flex;flex-direction:column;gap:4px}
.champ-tel label{font-size:11px;font-weight:700;color:var(--encre-2)}
.champ-tel .valeur-champ{border:1.5px solid var(--bordure-forte);border-radius:9px;background:var(--surface);
padding:9px 10px;font-size:13px;font-weight:600;color:var(--encre)}
.champ-tel textarea.valeur-champ{min-height:56px}
/* Tableau de synthèse (hors gabarit téléphone) */
.table-roles{width:100%;border-collapse:collapse;background:var(--surface);border:1px solid var(--bordure);
border-radius:12px;overflow:hidden;font-size:12.5px}
.table-roles th,.table-roles td{padding:10px 12px;text-align:left;border-bottom:1px solid var(--bordure)}
.table-roles thead th{background:var(--surface-2);font-size:10.5px;letter-spacing:.05em;text-transform:uppercase;
color:var(--encre-2)}
.table-roles tbody tr:last-child td{border-bottom:0}
.table-roles td.role{font-weight:800}
.table-roles .oui{color:var(--st-termine);font-weight:800}
.table-roles .non{color:var(--encre-3)}
.chip-onglet{display:inline-block;background:var(--primaire-doux);color:var(--primaire);font-weight:700;
font-size:11px;padding:2px 8px;border-radius:999px;margin:2px 3px 2px 0}
.chip-onglet.terrain{background:var(--safran-doux);color:var(--safran)}
</style>
<header class="revue">
<b>SIOP · Maquette — Mobile ouvert à tous les rôles</b>
<nav class="onglets" role="tablist" aria-label="Écrans">
<button role="tab" aria-selected="true" data-cible="synthese">1 · Synthèse par rôle</button>
<button role="tab" aria-selected="false" data-cible="accueil">2 · Accueil adaptatif</button>
<button role="tab" aria-selected="false" data-cible="menu">3 · Menu groupé</button>
<button role="tab" aria-selected="false" data-cible="ot-dispatch">4 · OT — Dispatcher</button>
<button role="tab" aria-selected="false" data-cible="demandes">5 · Demandes</button>
<button role="tab" aria-selected="false" data-cible="stock">6 · Stock & achats</button>
<button role="tab" aria-selected="false" data-cible="pilotage">7 · Personnes & statistiques</button>
</nav>
<div class="droite"><button class="theme" id="btn-theme">Thème : auto</button></div>
</header>
<!-- ═══ 1 · SYNTHÈSE PAR RÔLE ═══ -->
<p class="note-ecran" data-note="synthese"><b>Intention :</b> avant le détail écran par écran,
le tableau ci-dessous résume la <b>décision D1/D2</b> pour les 7 rôles : quels onglets la barre
adaptative affiche, et quels groupes du Menu restent visibles (mêmes droits qu'au web, rien de
nouveau dans la matrice). Les 4 onglets terrain du Technicien (encadré safran) sont ceux déjà
livrés en R4 — inchangés.</p>
<section class="ecran actif" id="synthese">
<div style="overflow-x:auto">
<table class="table-roles">
<thead><tr><th>Rôle (compte démo)</th><th>Onglets de la barre</th><th>Groupes du Menu visibles</th></tr></thead>
<tbody>
<tr>
<td class="role">Administrateur<br><small style="font-weight:500;color:var(--encre-3)">Yasmine Alaoui</small></td>
<td><span class="chip-onglet">Accueil</span><span class="chip-onglet">OT</span><span class="chip-onglet">Menu</span></td>
<td>Exploitation · Parc · Ressources · Pilotage <span style="color:var(--encre-3)">(tout)</span></td>
</tr>
<tr>
<td class="role">Gestionnaire<br><small style="font-weight:500;color:var(--encre-3)">Nadia Berrada</small></td>
<td><span class="chip-onglet">Accueil</span><span class="chip-onglet">OT</span><span class="chip-onglet">Menu</span></td>
<td>Exploitation · Parc · Ressources · Pilotage <span style="color:var(--encre-3)">(stats en lecture)</span></td>
</tr>
<tr>
<td class="role">Dispatcher<br><small style="font-weight:500;color:var(--encre-3)">Salma Idrissi</small></td>
<td><span class="chip-onglet">Accueil</span><span class="chip-onglet">OT</span><span class="chip-onglet">Menu</span></td>
<td>Exploitation · Parc (lecture) <span style="color:var(--encre-3)">— Ressources/Pilotage restreints</span></td>
</tr>
<tr style="box-shadow:inset 3px 0 0 var(--safran)">
<td class="role">Technicien<br><small style="font-weight:500;color:var(--encre-3)">Ahmed Benali</small></td>
<td><span class="chip-onglet terrain">Ma journée</span><span class="chip-onglet terrain">Scanner</span><span class="chip-onglet terrain">Préventif</span><span class="chip-onglet terrain">Synchro</span><span class="chip-onglet">Menu</span></td>
<td>Menu quasi vide (ses OT + parc en lecture) — <b>inchangé depuis R4</b></td>
</tr>
<tr style="box-shadow:inset 3px 0 0 var(--safran)">
<td class="role">Technicien limité<br><small style="font-weight:500;color:var(--encre-3)">Youssef Tazi</small></td>
<td><span class="chip-onglet terrain">Ma journée</span><span class="chip-onglet terrain">Scanner</span><span class="chip-onglet terrain">Préventif</span><span class="chip-onglet terrain">Synchro</span><span class="chip-onglet">Menu</span></td>
<td>Menu quasi vide (ses OT uniquement) — <b>inchangé depuis R4</b></td>
</tr>
<tr>
<td class="role">Demandeur<br><small style="font-weight:500;color:var(--encre-3)">Karim Doukkali</small></td>
<td><span class="chip-onglet">Accueil</span><span class="chip-onglet">Nouvelle demande</span></td>
<td>Exploitation → Demandes uniquement <span style="color:var(--encre-3)">(Menu quasi vide, D3)</span></td>
</tr>
<tr>
<td class="role">Vue seule<br><small style="font-weight:500;color:var(--encre-3)">Rachid Bennis</small></td>
<td><span class="chip-onglet">Accueil</span><span class="chip-onglet">Menu</span></td>
<td>Exploitation · Parc · Ressources · Pilotage <span style="color:var(--encre-3)">(lecture seule, aucun bouton d'action)</span></td>
</tr>
</tbody>
</table>
</div>
</section>
<!-- ═══ 2 · ACCUEIL ADAPTATIF ═══ -->
<p class="note-ecran" data-note="accueil" hidden><b>Intention :</b> le Technicien garde « Ma
journée » telle quelle (rien à revalider). Les autres rôles reçoivent un <b>Accueil</b> propre à
leur travail : le Gestionnaire voit ses signaux de pilotage et des raccourcis vers ce qui demande
une action ; le Demandeur voit directement ses demandes et le geste qu'il fait le plus (en
créer une nouvelle) — jamais un écran vide générique.</p>
<section class="ecran" id="accueil">
<div class="tels">
<div class="tel">
<div class="encoche"><i></i></div>
<div class="legende">Technicien — inchangé (R4)</div>
<div class="ecran-tel">
<div class="entete-tel">
<span class="flamme"></span><b>SIOP</b><small>Technicien</small>
<span class="fin"><span class="sync">Synchro à jour</span><span class="avatar-tel">AB</span></span>
</div>
<div class="titre-tel">Ma journée <span class="filet">jeudi 1 août</span></div>
<div class="bandeau-urgence"><span class="point-pulse"></span>1 personne bloquée — B2 · Tour Atlas</div>
<div class="carte-ot-tel bloque">
<div class="type-t">OT-2026-0342 · Dépannage <span class="st ouvert">Ouvert</span></div>
<b>Personne bloquée en cabine</b>
<div class="ou">Asc. B2 — Tour Atlas · Aïn Diab</div>
</div>
<div class="carte-ot-tel haute">
<div class="type-t">OT-2026-0341 · Dépannage <span class="st encours">En cours</span></div>
<b>Bruit anormal en gaine</b>
<div class="ou">Asc. A1 — Résidence Al Manar</div>
</div>
<nav class="tabbar">
<a class="actif" href="#"><span class="pic"></span>Ma journée</a>
<a href="#"><span class="pic"></span>Scanner</a>
<a href="#"><span class="pic"></span>Préventif</a>
<a href="#"><span class="pic"></span>Synchro</a>
<a href="#"><span class="pic"></span>Menu</a>
</nav>
</div>
</div>
<div class="tel">
<div class="encoche"><i></i></div>
<div class="legende">Gestionnaire — nouveau</div>
<div class="ecran-tel">
<div class="entete-tel">
<span class="flamme"></span><b>SIOP</b><small>Gestionnaire</small>
<span class="fin"><span class="sync">Synchro à jour</span><span class="avatar-tel">NB</span></span>
</div>
<div class="titre-tel">Accueil <span class="filet">jeudi 1 août</span></div>
<div class="kpis-tel">
<div class="kpi-tel accent"><div class="val">6</div><div class="lbl">OT en cours</div></div>
<div class="kpi-tel"><div class="val">3</div><div class="lbl">Stock sous seuil</div></div>
<div class="kpi-tel"><div class="val">78 %</div><div class="lbl">Préventif du mois</div></div>
<div class="kpi-tel"><div class="val">2</div><div class="lbl">Demandes reçues</div></div>
</div>
<div class="raccourci-tel"><span class="ico"></span>
<span class="txt"><b>Stock sous seuil</b><small>3 pièces à commander</small></span>
<span class="num">3</span><span class="chev"></span></div>
<div class="raccourci-tel"><span class="ico"></span>
<span class="txt"><b>Demandes reçues</b><small>en attente d'approbation</small></span>
<span class="num">2</span><span class="chev"></span></div>
<div class="raccourci-tel"><span class="ico"></span>
<span class="txt"><b>Invitations en attente</b><small>lien envoyé, pas encore activé</small></span>
<span class="chev"></span></div>
<nav class="tabbar">
<a class="actif" href="#"><span class="pic"></span>Accueil</a>
<a href="#"><span class="pic"></span>OT</a>
<a href="#"><span class="pic"></span>Menu</a>
</nav>
</div>
</div>
<div class="tel">
<div class="encoche"><i></i></div>
<div class="legende">Demandeur — nouveau</div>
<div class="ecran-tel">
<div class="entete-tel">
<span class="flamme"></span><b>SIOP</b><small>Demandeur</small>
<span class="fin"><span class="sync">Synchro à jour</span><span class="avatar-tel">KD</span></span>
</div>
<div class="titre-tel">Mes demandes</div>
<div class="carte-ot-tel">
<div class="type-t">DEM-2026-0089 <span class="st attente">Reçue</span></div>
<b>Éclairage cabine défaillant</b>
<div class="ou">Asc. A2 — Résidence Al Manar</div>
</div>
<div class="carte-ot-tel">
<div class="type-t">DEM-2026-0081 <span class="st termine">Traitée</span></div>
<b>Bruit au démarrage</b>
<div class="ou">Asc. A2 — Résidence Al Manar → OT-2026-0336</div>
</div>
<div style="margin-top:auto">
<button class="btn-tel">+ Nouvelle demande</button>
</div>
<nav class="tabbar">
<a class="actif" href="#"><span class="pic"></span>Accueil</a>
<a href="#"><span class="pic">+</span>Nouvelle demande</a>
</nav>
</div>
</div>
</div>
</section>
<!-- ═══ 3 · MENU GROUPÉ ═══ -->
<p class="note-ecran" data-note="menu" hidden><b>Intention :</b> le Menu reprend, à l'identique,
les 4 groupes et l'ordre du sidebar web — <b>Exploitation / Parc / Ressources / Pilotage</b> — et
les mêmes permissions (`can(catégorie, 'view')`). Un rôle qui n'a droit à rien dans un groupe ne
voit pas le groupe du tout (D3) : comparez Administrateur (tout) et Demandeur (presque rien) sur
le MÊME écran, sans aucune règle de plus dans le code.</p>
<section class="ecran" id="menu">
<div class="tels">
<div class="tel">
<div class="encoche"><i></i></div>
<div class="legende">Administrateur — tout visible</div>
<div class="ecran-tel">
<div class="entete-tel">
<span class="flamme"></span><b>SIOP</b><small>Administrateur</small>
<span class="fin"><span class="avatar-tel">YA</span></span>
</div>
<div class="titre-tel">Menu</div>
<div class="menu-groupe">
<div class="titre-groupe">Exploitation</div>
<div class="menu-item"><span class="ico"></span><b>Tableau de bord</b><span class="chev"></span></div>
<div class="menu-item"><span class="ico"></span><b>Ordres de travail</b><span class="num">1</span><span class="chev"></span></div>
<div class="menu-item"><span class="ico"></span><b>Demandes</b><span class="num">2</span><span class="chev"></span></div>
<div class="menu-item"><span class="ico"></span><b>Préventif</b><span class="chev"></span></div>
</div>
<div class="menu-groupe">
<div class="titre-groupe">Parc</div>
<div class="menu-item"><span class="ico"></span><b>Ascenseurs</b><span class="chev"></span></div>
<div class="menu-item"><span class="ico"></span><b>Sites</b><span class="chev"></span></div>
</div>
<div class="menu-groupe">
<div class="titre-groupe">Ressources</div>
<div class="menu-item"><span class="ico"></span><b>Stock & achats</b><span class="num">3</span><span class="chev"></span></div>
<div class="menu-item"><span class="ico"></span><b>Tiers</b><span class="chev"></span></div>
</div>
<div class="menu-groupe">
<div class="titre-groupe">Pilotage</div>
<div class="menu-item"><span class="ico"></span><b>Statistiques</b><span class="chev"></span></div>
<div class="menu-item"><span class="ico"></span><b>Personnes & équipes</b><span class="chev"></span></div>
</div>
<nav class="tabbar">
<a href="#"><span class="pic"></span>Accueil</a>
<a href="#"><span class="pic"></span>OT</a>
<a class="actif" href="#"><span class="pic"></span>Menu</a>
</nav>
</div>
</div>
<div class="tel">
<div class="encoche"><i></i></div>
<div class="legende">Demandeur — presque vide (D3)</div>
<div class="ecran-tel">
<div class="entete-tel">
<span class="flamme"></span><b>SIOP</b><small>Demandeur</small>
<span class="fin"><span class="avatar-tel">KD</span></span>
</div>
<div class="titre-tel">Menu</div>
<div class="menu-groupe">
<div class="titre-groupe">Exploitation</div>
<div class="menu-item"><span class="ico"></span><b>Mes demandes</b><span class="chev"></span></div>
</div>
<div class="menu-vide">Parc, Ressources et Pilotage ne sont pas accessibles à ce rôle —<br>groupes masqués, pas de section vide affichée.</div>
<nav class="tabbar">
<a class="actif" href="#"><span class="pic"></span>Accueil</a>
<a href="#"><span class="pic">+</span>Nouvelle demande</a>
</nav>
</div>
</div>
</div>
</section>
<!-- ═══ 4 · OT — DISPATCHER ═══ -->
<p class="note-ecran" data-note="ot-dispatch" hidden><b>Intention :</b> contrairement au
Technicien (qui ne voit que SES OT), le Dispatcher a `viewOther` — il voit tous les OT et peut en
créer/assigner. Le mobile porte cette différence : liste complète, bouton « + » et une action
rapide « Assigner » par carte (bottom-sheet, même pattern que le sélecteur de bilan codé de R4).</p>
<section class="ecran" id="ot-dispatch">
<div class="tels">
<div class="tel">
<div class="encoche"><i></i></div>
<div class="legende">Dispatcher — tous les OT</div>
<div class="ecran-tel">
<div class="entete-tel">
<span class="flamme"></span><b>SIOP</b><small>Dispatcher</small>
<span class="fin"><span class="sync">Synchro à jour</span><span class="avatar-tel">SI</span></span>
</div>
<div class="titre-tel">Ordres de travail <span class="filet">14 en cours</span></div>
<div class="carte-ot-tel bloque">
<div class="type-t">OT-2026-0342 · Dépannage <span class="st ouvert">Ouvert</span></div>
<b>Personne bloquée en cabine</b>
<div class="ou">Asc. B2 — Tour Atlas · non assigné</div>
</div>
<div class="carte-ot-tel haute">
<div class="type-t">OT-2026-0341 · Dépannage <span class="st encours">En cours</span></div>
<b>Bruit anormal en gaine</b>
<div class="ou">Asc. A1 — Résidence Al Manar · Ahmed Benali</div>
</div>
<div class="carte-ot-tel moyenne">
<div class="type-t">OT-2026-0336 · Préventif <span class="st ouvert">Ouvert</span></div>
<b>Contrôle mensuel — juillet</b>
<div class="ou">Asc. A2 — Résidence Al Manar · non assigné</div>
</div>
<div style="margin-top:auto">
<button class="btn-tel">+ Nouvel OT</button>
</div>
<nav class="tabbar">
<a href="#"><span class="pic"></span>Accueil</a>
<a class="actif" href="#"><span class="pic"></span>OT</a>
<a href="#"><span class="pic"></span>Menu</a>
</nav>
</div>
</div>
<div class="tel">
<div class="encoche"><i></i></div>
<div class="legende">Assigner — feuille au pouce</div>
<div class="ecran-tel" style="justify-content:flex-end;padding:0">
<div style="flex:1;background:rgb(9 14 22/.45)"></div>
<div style="background:var(--surface);border-radius:16px 16px 0 0;padding:14px;display:flex;flex-direction:column;gap:10px">
<div style="font-weight:800;font-size:15px">Assigner OT-2026-0342</div>
<div class="champ-tel">
<label>Technicien</label>
<div class="valeur-champ">Ahmed Benali ▾</div>
</div>
<button class="btn-tel">Assigner</button>
<button class="btn-tel contour">Annuler</button>
</div>
</div>
</div>
</div>
</section>
<!-- ═══ 5 · DEMANDES ═══ -->
<p class="note-ecran" data-note="demandes" hidden><b>Intention :</b> le Demandeur crée sa demande
en mobilité (site, ascenseur si connu, description, urgence) exactement comme au web mais sur un
formulaire au pouce ; le Gestionnaire/Dispatcher approuve ou rejette (motif obligatoire au rejet,
comme au web) directement depuis la carte.</p>
<section class="ecran" id="demandes">
<div class="tels">
<div class="tel">
<div class="encoche"><i></i></div>
<div class="legende">Demandeur — nouvelle demande</div>
<div class="ecran-tel">
<div class="entete-tel">
<span class="flamme"></span><b>SIOP</b><small>Demandeur</small>
<span class="fin"><span class="avatar-tel">KD</span></span>
</div>
<div class="titre-tel">Nouvelle demande</div>
<div class="champ-tel"><label>Site</label><div class="valeur-champ">Résidence Al Manar ▾</div></div>
<div class="champ-tel"><label>Ascenseur (si connu)</label><div class="valeur-champ">Asc. A2 ▾</div></div>
<div class="champ-tel"><label>Description</label>
<div class="valeur-champ" style="min-height:56px">Odeur de brûlé au démarrage, depuis ce matin.</div>
</div>
<div class="champ-tel"><label>Urgence</label><div class="valeur-champ">Personne bloquée — Non</div></div>
<div style="margin-top:auto">
<button class="btn-tel">Envoyer la demande</button>
</div>
<nav class="tabbar">
<a href="#"><span class="pic"></span>Accueil</a>
<a class="actif" href="#"><span class="pic">+</span>Nouvelle demande</a>
</nav>
</div>
</div>
<div class="tel">
<div class="encoche"><i></i></div>
<div class="legende">Gestionnaire — approbation</div>
<div class="ecran-tel">
<div class="entete-tel">
<span class="flamme"></span><b>SIOP</b><small>Gestionnaire</small>
<span class="fin"><span class="avatar-tel">NB</span></span>
</div>
<div class="titre-tel">Demandes reçues <span class="filet">2</span></div>
<div class="carte-ot-tel">
<div class="type-t">DEM-2026-0089 <span class="st attente">Reçue</span></div>
<b>Éclairage cabine défaillant</b>
<div class="ou">Asc. A2 — Résidence Al Manar · Karim Doukkali</div>
<div class="paire-boutons" style="margin-top:8px">
<button class="btn-tel vert">Approuver</button>
<button class="btn-tel rouge">Rejeter</button>
</div>
</div>
<div class="carte-ot-tel">
<div class="type-t">DEM-2026-0090 <span class="st attente">Reçue</span></div>
<b>Porte palière qui force</b>
<div class="ou">Asc. C1 — Anfa Place · Karim Doukkali</div>
<div class="paire-boutons" style="margin-top:8px">
<button class="btn-tel vert">Approuver</button>
<button class="btn-tel rouge">Rejeter</button>
</div>
</div>
<nav class="tabbar">
<a href="#"><span class="pic"></span>Accueil</a>
<a class="actif" href="#"><span class="pic"></span>OT</a>
<a href="#"><span class="pic"></span>Menu</a>
</nav>
</div>
</div>
</div>
</section>
<!-- ═══ 6 · STOCK & ACHATS ═══ -->
<p class="note-ecran" data-note="stock" hidden><b>Intention :</b> le Gestionnaire consulte le
stock en mobilité et déclenche l'action courante — commander une pièce sous seuil — sans passer
par le grand écran. La fiche pièce reprend les lignes déjà connues (quantité, seuil, dernier
prix, fournisseur). Le bon de commande détaillé multi-lignes reste au web (D4).</p>
<section class="ecran" id="stock">
<div class="tels">
<div class="tel">
<div class="encoche"><i></i></div>
<div class="legende">Gestionnaire — stock</div>
<div class="ecran-tel">
<div class="entete-tel">
<span class="flamme"></span><b>SIOP</b><small>Gestionnaire</small>
<span class="fin"><span class="avatar-tel">NB</span></span>
</div>
<div class="titre-tel">Stock & achats <span class="filet">3 sous seuil</span></div>
<div class="carte-tel">
<h4>Câble de traction 8 mm</h4>
<div class="ligne-info"><span>Quantité</span><b style="color:var(--danger)">2</b></div>
<div class="ligne-info"><span>Seuil</span><b>5</b></div>
<div class="ligne-info"><span>Fournisseur</span><b>Ascentech Maroc</b></div>
<button class="btn-tel" style="margin-top:4px">Commander</button>
</div>
<div class="carte-tel">
<h4>Galet de guidage</h4>
<div class="ligne-info"><span>Quantité</span><b style="color:var(--danger)">1</b></div>
<div class="ligne-info"><span>Seuil</span><b>4</b></div>
<div class="ligne-info"><span>Fournisseur</span><b>SchindlerParts</b></div>
<button class="btn-tel" style="margin-top:4px">Commander</button>
</div>
<nav class="tabbar">
<a href="#"><span class="pic"></span>Accueil</a>
<a href="#"><span class="pic"></span>OT</a>
<a class="actif" href="#"><span class="pic"></span>Menu</a>
</nav>
</div>
</div>
<div class="tel">
<div class="encoche"><i></i></div>
<div class="legende">BC pré-rempli depuis l'alerte</div>
<div class="ecran-tel">
<div class="entete-tel">
<span class="flamme"></span><b>SIOP</b><small>Gestionnaire</small>
<span class="fin"><span class="avatar-tel">NB</span></span>
</div>
<div class="titre-tel">Nouveau BC</div>
<div class="champ-tel"><label>Fournisseur</label><div class="valeur-champ">Ascentech Maroc</div></div>
<div class="champ-tel"><label>Pièce</label><div class="valeur-champ">Câble de traction 8 mm</div></div>
<div class="champ-tel"><label>Quantité</label><div class="valeur-champ">10</div></div>
<div class="garde">Le détail multi-lignes se termine sur le grand écran (D4).</div>
<div style="margin-top:auto">
<button class="btn-tel">Créer le BC</button>
</div>
</div>
</div>
</div>
</section>
<!-- ═══ 7 · PERSONNES & STATISTIQUES ═══ -->
<p class="note-ecran" data-note="pilotage" hidden><b>Intention :</b> deux écrans « Pilotage » côte
à côte — les Statistiques en lecture (mêmes chiffres que `/analytics/summary`, en cartes plutôt
qu'en graphes denses) et Personnes & équipes, avec l'action d'invitation déjà connue du web
(lien 7 jours) portée sur mobile.</p>
<section class="ecran" id="pilotage">
<div class="tels">
<div class="tel">
<div class="encoche"><i></i></div>
<div class="legende">Administrateur — statistiques</div>
<div class="ecran-tel">
<div class="entete-tel">
<span class="flamme"></span><b>SIOP</b><small>Administrateur</small>
<span class="fin"><span class="avatar-tel">YA</span></span>
</div>
<div class="titre-tel">Statistiques <span class="filet">12 derniers mois</span></div>
<div class="kpis-tel">
<div class="kpi-tel accent"><div class="val">505 MAD</div><div class="lbl">Coût du mois</div></div>
<div class="kpi-tel"><div class="val">78 %</div><div class="lbl">Taux préventif</div></div>
</div>
<div class="carte-tel">
<h4>Pannes par organe — top 3</h4>
<div class="ligne-info"><span>Portes palières</span><b>9</b></div>
<div class="ligne-info"><span>Câblage</span><b>6</b></div>
<div class="ligne-info"><span>Éclairage cabine</span><b>4</b></div>
</div>
<nav class="tabbar">
<a href="#"><span class="pic"></span>Accueil</a>
<a href="#"><span class="pic"></span>OT</a>
<a class="actif" href="#"><span class="pic"></span>Menu</a>
</nav>
</div>
</div>
<div class="tel">
<div class="encoche"><i></i></div>
<div class="legende">Gestionnaire — personnes & équipes</div>
<div class="ecran-tel">
<div class="entete-tel">
<span class="flamme"></span><b>SIOP</b><small>Gestionnaire</small>
<span class="fin"><span class="avatar-tel">NB</span></span>
</div>
<div class="titre-tel">Personnes <span class="filet">12</span></div>
<div class="menu-item"><span class="ico"></span><b>Ahmed Benali</b><span style="color:var(--encre-3);font-size:11px">Technicien</span></div>
<div class="menu-item"><span class="ico"></span><b>Youssef Tazi</b><span style="color:var(--encre-3);font-size:11px">Technicien limité</span></div>
<div class="menu-item"><span class="ico"></span><b>Salma Idrissi</b><span style="color:var(--encre-3);font-size:11px">Dispatcher</span></div>
<div style="margin-top:auto">
<button class="btn-tel">+ Inviter</button>
</div>
</div>
</div>
</div>
</section>
<script>
(function(){
var onglets=document.querySelectorAll('.revue .onglets button');
var ecrans=document.querySelectorAll('.ecran');
var notes=document.querySelectorAll('.note-ecran');
onglets.forEach(function(b){b.addEventListener('click',function(){
onglets.forEach(function(x){x.setAttribute('aria-selected','false')});
b.setAttribute('aria-selected','true');
var cible=b.dataset.cible;
ecrans.forEach(function(e){e.classList.toggle('actif',e.id===cible)});
notes.forEach(function(n){n.hidden=(n.dataset.note!==cible)});
window.scrollTo({top:0});
})});
var ordre=['auto','light','dark'];var i=0;
var bt=document.getElementById('btn-theme');
bt.addEventListener('click',function(){
i=(i+1)%3;var m=ordre[i];
if(m==='auto'){document.documentElement.removeAttribute('data-theme')}
else{document.documentElement.setAttribute('data-theme',m)}
bt.textContent='Thème : '+(m==='auto'?'auto':(m==='light'?'clair':'sombre'));
});
})();
</script>
</body>
</html>