feat(r1.2): écrans web du référentiel — 7 écrans fidèles à maquette-r1

- Sites : liste + carte Leaflet/OSM réelle (pins maquette, positions
  PostGIS), création en modale avec position posée au clic sur la carte
- Fiche site : arbre site→zones, appareils rattachés, ajout de zone
- Ascenseurs : table dense, filtres site/statut, strie rouge à l'arrêt ;
  fiche appareil (organes ± ajout/retrait, statut, QR réel) ; création
  identité → rattachement → organes ; étiquette A6 imprimable (objet
  papier, window.print n'imprime qu'elle)
- Personnes & équipes : invitation → lien d'activation affiché à copier
  (pas d'email en R1, décision explicite), renvoi de lien, équipes ;
  page /activation (choix du mot de passe, connexion directe)
- Catégories : ajout, renommage inline, désactivation
- navigation et actions pilotées par la matrice (usePermissions) —
  l'API re-vérifie chaque requête
- e2e Playwright : recette R1 officielle rejouée intégralement (site →
  zone → appareil+organe → étiquette → invitation → activation) +
  parcours « la matrice pilote l'UI » ; purge idempotente en globalSetup
- alias Vite @siop/shared → source TS (exports nommés CJS ↯ workspace lié)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
pr-daaif
2026-07-16 13:06:45 +01:00
parent 266ffaaf1b
commit 9778629827
29 changed files with 2350 additions and 35 deletions

View File

@@ -536,6 +536,220 @@ table {
margin: 6px 4px;
}
/* ═══ R1 — Référentiel (classes extraites de maquette-r1.html validée) ═══ */
/* Statuts d'ÉQUIPEMENT (distincts des statuts OT) */
.st.arret { color: var(--danger); background: var(--prio-bloque-fond); }
.st.maintenance { color: var(--st-encours); background: var(--st-encours-fond); }
/* Carte géographique — le conteneur ; Leaflet rend à l'intérieur */
.geo {
position: relative;
border: 1px solid var(--bordure);
border-radius: var(--rayon);
overflow: hidden;
min-height: 320px;
background: var(--surface-2);
}
.geo .leaflet-container {
position: absolute;
inset: 0;
font: inherit;
}
/* Pin maquette (divIcon Leaflet) */
.pin-geo {
width: 26px;
height: 26px;
border-radius: 50% 50% 50% 0;
transform: rotate(-45deg) translate(4px, -4px);
background: var(--primaire);
border: 2px solid var(--surface);
box-shadow: var(--ombre-menu);
}
.pin-geo::after {
content: '';
position: absolute;
inset: 7px;
border-radius: 50%;
background: var(--surface);
}
.pin-geo.alerte { background: var(--prio-bloque); }
/* Arborescence des emplacements */
.arbre { list-style: none; padding: 0; font-size: 13.5px; }
.arbre li { position: relative; padding: 7px 0 7px 26px; border-bottom: 1px solid var(--bordure); }
.arbre li:last-child { border-bottom: 0; }
.arbre li::before { content: ''; position: absolute; left: 8px; top: 0; bottom: 0; width: 1px; background: var(--bordure); }
.arbre li::after { content: ''; position: absolute; left: 8px; top: 50%; width: 12px; height: 1px; background: var(--bordure); }
.arbre li.racine { padding-left: 0; }
.arbre li.racine::before, .arbre li.racine::after { display: none; }
.arbre .meta { color: var(--encre-2); font-size: 12px; margin-left: 8px; }
.arbre .fin { float: right; color: var(--encre-2); font-size: 12px; }
/* Formulaires R1 */
.form-grille { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 14px; }
@media (max-width: 900px) { .form-grille { grid-template-columns: 1fr; } }
.form-grille .pleine-l { grid-column: 1/-1; }
.champ select {
width: 100%;
padding: 9px 12px;
border: 1px solid var(--bordure-forte);
border-radius: var(--rayon-controle);
font: inherit;
background: var(--surface);
color: var(--encre);
}
.ligne-organe { display: flex; gap: 8px; align-items: center; padding: 8px 0; border-bottom: 1px solid var(--bordure); }
.ligne-organe:last-of-type { border-bottom: 0; }
.ligne-organe select, .ligne-organe input {
padding: 8px 11px;
border: 1px solid var(--bordure-forte);
border-radius: var(--rayon-controle);
font: inherit;
background: var(--surface);
color: var(--encre);
}
.btn-suppr { color: var(--encre-3); padding: 6px; border-radius: 6px; border: 0; background: none; }
.btn-suppr:hover { color: var(--danger); background: var(--prio-bloque-fond); }
/* Étiquette QR imprimable (A6 — objet papier, blanc même en thème sombre) */
.zone-impression { display: flex; gap: 24px; align-items: flex-start; flex-wrap: wrap; }
.etiquette {
width: 340px;
background: #fff;
color: #1b2534;
border: 1px solid var(--bordure-forte);
border-radius: 12px;
padding: 22px;
display: flex;
flex-direction: column;
gap: 12px;
box-shadow: var(--ombre-menu);
}
.etiquette .tete { display: flex; align-items: center; gap: 10px; border-bottom: 2px solid #1b2534; padding-bottom: 10px; }
.etiquette .tete .marque { display: flex; flex-direction: column; line-height: 0.55; font-size: 12px; color: #dd8a0b; }
.etiquette .tete b { font-size: 20px; letter-spacing: 0.04em; }
.etiquette .corps { display: flex; gap: 16px; align-items: center; }
.etiquette .qr-grand { flex: none; border: 1px solid #dce3ec; border-radius: 8px; padding: 6px; background: #fff; }
.etiquette h3 { font-size: 15px; font-weight: 700; margin: 0; }
.etiquette .ou { font-size: 12px; color: #55647a; }
.etiquette .consigne {
font-size: 12.5px;
background: #fdf3e3;
border: 1px dashed #dd8a0b;
color: #8a5a06;
border-radius: 8px;
padding: 8px 10px;
text-align: center;
font-weight: 600;
}
.etiquette .ref { font-size: 11px; color: #8494ab; text-align: center; font-variant-numeric: tabular-nums; }
@media print {
body * { visibility: hidden; }
.etiquette, .etiquette * { visibility: visible; }
.etiquette { position: fixed; inset: 0 auto auto 0; box-shadow: none; margin: 10mm; }
}
/* Modale (Radix Dialog) — style maquette */
.voile-radix {
position: fixed;
inset: 0;
background: color-mix(in srgb, var(--encre) 32%, transparent);
z-index: 70;
}
.modale {
position: fixed;
top: 10vh;
left: 50%;
transform: translateX(-50%);
width: 460px;
max-width: 94vw;
max-height: 82vh;
overflow-y: auto;
background: var(--surface);
border: 1px solid var(--bordure);
border-radius: 12px;
box-shadow: var(--ombre-menu);
padding: 20px;
display: flex;
flex-direction: column;
gap: 12px;
z-index: 71;
}
.modale h3 { font-size: 15px; font-weight: 700; }
.modale .pied { display: flex; justify-content: flex-end; gap: 8px; margin-top: 4px; }
.note-douce {
font-size: 12px;
color: var(--encre-2);
background: var(--surface-2);
border-radius: 8px;
padding: 8px 10px;
}
.lien-copiable {
font-family: var(--police-mono);
font-size: 11.5px;
word-break: break-all;
background: var(--surface-2);
border: 1px dashed var(--bordure-forte);
border-radius: 8px;
padding: 8px 10px;
}
/* Équipes */
.equipes { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
@media (max-width: 1000px) { .equipes { grid-template-columns: 1fr; } }
.equipe {
border: 1px solid var(--bordure);
border-radius: var(--rayon);
padding: 12px 14px;
display: flex;
flex-direction: column;
gap: 8px;
background: var(--surface);
}
.equipe b { font-size: 13.5px; }
.equipe .zone { font-size: 12px; color: var(--encre-2); }
/* Tables denses (liste ascenseurs / sites / personnes) */
.table { width: 100%; overflow-x: auto; }
.table table { width: 100%; font-size: 13.5px; }
.table th {
font-size: 11px;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--encre-2);
text-align: left;
padding: 9px 12px;
border-bottom: 1px solid var(--bordure);
background: var(--surface-2);
white-space: nowrap;
}
.table td { padding: 9px 12px; border-bottom: 1px solid var(--bordure); vertical-align: middle; }
.table tbody tr:hover { background: var(--primaire-doux); }
.table .ref { font-weight: 700; color: var(--primaire); }
.table .sous { color: var(--encre-2); font-size: 12px; display: block; }
.ligne-bloque td:first-child { box-shadow: inset 3px 0 0 var(--prio-bloque); }
.filajout { color: var(--encre-3); font-size: 12.5px; }
.filajout a { color: var(--encre-2); text-decoration: none; }
.filtres { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.filtres select {
border: 1px solid var(--bordure-forte);
border-radius: var(--rayon-controle);
padding: 7px 11px;
font-size: 13px;
background: var(--surface);
color: var(--encre);
}
.infos { display: grid; grid-template-columns: 130px 1fr; row-gap: 9px; font-size: 13.5px; }
.infos dt { color: var(--encre-2); }
.infos dd { font-weight: 500; }
.infos a { color: var(--primaire); text-decoration: none; font-weight: 600; }
.item-liste { display: flex; align-items: center; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--bordure); font-size: 13.5px; }
.item-liste:last-child { border-bottom: 0; }
.item-liste .fin { margin-left: auto; display: flex; align-items: center; gap: 10px; color: var(--encre-2); font-size: 12.5px; }
.avatars { display: flex; }
.avatars .avatar { width: 24px; height: 24px; font-size: 10px; border: 2px solid var(--surface); margin-left: -6px; }
.avatars .avatar:first-child { margin-left: 0; }
/* ═══ Page /design : nuanciers ═══ */
.nuancier {
display: flex;