Files
gestion_projets/index.html
Bertrand Benjamin eaca2f7813 Revoir l'ergonomie des panneaux et de la colonne des projets
Les panneaux d'édition étaient modaux et la colonne de gauche tronquait
tout ce qu'elle portait. Sept corrections, mesurées sur data/exemple.json.

Panneaux
- Le voile qui recouvrait la page disparaît : la frise reste manipulable
  pendant l'édition, et cliquer une autre barre y bascule la sélection.
  Il rendait aussi le raccourci flèches inatteignable, le focus partant
  dans le champ « Nom » sans qu'on puisse cliquer ailleurs pour en sortir.
- Un clic dans une zone inerte referme le panneau : ce que rendait le
  voile, sans le calque qui figeait la frise.
- Les paramètres d'un projet quittent le <dialog> modal pour le même
  panneau qu'une phase, avec la même édition au fil de la saisie.
  « Nouveau projet » crée donc le projet aussitôt, comme le fait déjà
  l'ajout d'une phase : plus aucune création ne passe par un formulaire.

Colonne des projets
- Le nom ne disposait que de 56 px sur 250, et « client » s'affichait
  « c. ». Les commandes passent en surimpression au survol, les tags
  cèdent en bloc derrière un compteur, et le nom cède en dernier.
- La colonne se redimensionne, largeur conservée dans localStorage.
- L'œil disparaît : il faisait doublon avec l'entrée « Masquer » du menu.
  Un « + » le remplace, l'ajout d'une phase étant l'action la plus
  fréquente, et le nom du projet devient cliquable — il portait déjà un
  curseur « main » sans aucune action derrière.
- Plus de commande « Ajouter un jalon » : un jalon est une phase dont on
  coche la case, pas un autre objet.

Frise
- Le nom d'une phase reste accroché au bord visible quand sa barre
  s'enfonce sous la colonne.
- Cliquer un nom de phase hors écran ramène la frise sur elle.
- La barre cumulative d'un projet s'affiche aussi quand il est déplié,
  amincie pour ne pas se faire passer pour une barre de phase.

Arbitrages consignés dans docs/decisions.md, sections 8 et 17 à 21.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 10:58:47 +02:00

209 lines
7.5 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="fr">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Frise multi-projets</title>
<link rel="stylesheet" href="css/style.css" />
<link
rel="icon"
href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect width='16' height='16' rx='3' fill='%233b82f6'/><rect x='3' y='4' width='9' height='2' rx='1' fill='white'/><rect x='5' y='7' width='8' height='2' rx='1' fill='white'/><rect x='3' y='10' width='6' height='2' rx='1' fill='white'/></svg>"
/>
</head>
<body>
<header class="outils">
<h1>Frise multi-projets</h1>
<div class="outils__groupe">
<button type="button" id="aller-aujourdhui">Aujourd'hui</button>
<button type="button" id="nouveau-projet" class="bouton--principal">
Nouveau projet
</button>
</div>
<p class="outils__etat" id="etat-sauvegarde" role="status" aria-live="polite"></p>
</header>
<!--
Barre de filtres, masquée tant qu'aucun projet ne porte de tag : elle
n'apparaît que le jour où elle sert à quelque chose.
-->
<div class="filtres" id="filtres" hidden role="group" aria-label="Filtrer par tag">
<span class="filtres__titre">Filtrer</span>
<div class="tags tags--filtres" id="filtres-liste"></div>
<button type="button" id="effacer-filtres" hidden>Tout afficher</button>
</div>
<!--
Un seul conteneur défilant, découpé en grille 2×2 : l'en-tête de dates est
collé en haut (`sticky top`), la colonne des libellés collée à gauche
(`sticky left`), et le coin aux deux. Le navigateur gère ainsi le
défilement des deux axes sans aucune synchronisation en JavaScript.
-->
<main class="frise" id="frise">
<div class="grille">
<div class="grille__coin">
<!--
Poignée de largeur de la colonne. Placée dans le coin, seul endroit
de la colonne qui reste visible quel que soit le défilement.
-->
<div
class="poignee-colonne"
id="poignee-colonne"
role="separator"
aria-orientation="vertical"
aria-label="Largeur de la colonne des projets"
tabindex="0"
title="Glisser pour élargir la colonne — double-clic pour la réinitialiser"
></div>
</div>
<div class="grille__entete" id="entete"></div>
<div class="grille__libelles" id="libelles"></div>
<div class="grille__couloirs" id="couloirs"></div>
</div>
</main>
<p class="vide" id="message-vide" hidden>
Aucun projet pour l'instant.<br />
<button type="button" id="premier-projet" class="bouton--principal">
Créer un premier projet
</button>
</p>
<p class="vide" id="message-filtre" hidden>
Aucun projet ne porte tous les tags sélectionnés.<br />
<button type="button" id="effacer-filtres-vide">Tout afficher</button>
</p>
<!--
Panneau de détail, ouvert au clic sur une phase. Volontairement **non
modal** : la frise reste vivante derrière lui, on passe d'une phase à
l'autre en cliquant sa barre. `tabindex="-1"` permet d'y poser le focus à
l'ouverture sans le placer dans un champ de saisie, ce qui laisse les
flèches piloter la phase sélectionnée.
-->
<aside class="panneau" id="panneau" hidden tabindex="-1" aria-label="Détail de la phase">
<div class="panneau__entete">
<h2 id="panneau-titre">Phase</h2>
<button type="button" id="fermer-panneau" aria-label="Fermer">&times;</button>
</div>
<form class="panneau__corps" id="formulaire-phase">
<label>
Nom
<input type="text" name="name" required autocomplete="off" />
</label>
<div class="champ-double">
<label>
Début
<input type="date" name="start" required />
</label>
<label id="champ-fin">
Fin
<input type="date" name="end" required />
</label>
</div>
<label>
Statut
<select name="status">
<option value="todo">À venir</option>
<option value="doing">En cours</option>
<option value="done">Terminé</option>
<option value="blocked">Bloqué</option>
</select>
</label>
<label class="case">
<input type="checkbox" name="milestone" />
Jalon (une seule date, affichée en losange)
</label>
<label>
Notes
<textarea name="notes" rows="4"></textarea>
</label>
<p class="panneau__erreur" id="erreur-phase" role="alert" hidden></p>
<div class="panneau__actions">
<button type="button" id="supprimer-phase" class="bouton--danger">Supprimer</button>
</div>
</form>
</aside>
<!-- Menu contextuel, positionné à l'ouverture près de son point d'ancrage. -->
<div class="menu" id="menu" hidden role="menu"></div>
<!--
Paramètres d'un projet. Même panneau, mêmes gestes et même édition en
direct que le détail d'une phase : renommer un projet et renommer une
phase sont la même opération à un niveau près, elles n'ont pas à se
présenter différemment.
-->
<aside
class="panneau"
id="panneau-projet"
hidden
tabindex="-1"
aria-label="Paramètres du projet"
>
<div class="panneau__entete">
<h2 id="panneau-projet-titre">Projet</h2>
<button type="button" id="fermer-panneau-projet" aria-label="Fermer">&times;</button>
</div>
<form class="panneau__corps" id="formulaire-projet">
<label>
Nom
<input type="text" name="name" required autocomplete="off" />
</label>
<fieldset class="couleurs">
<legend>Couleur du couloir</legend>
<div id="panneau-projet-couleurs"></div>
</fieldset>
<label>
Tags, séparés par des virgules
<input type="text" name="tags" autocomplete="off" placeholder="client, refonte" />
</label>
<!-- Masqué tant qu'aucun tag n'existe ailleurs dans le planning. -->
<fieldset class="couleurs" id="panneau-projet-suggestions-bloc" hidden>
<legend>Tags déjà utilisés</legend>
<div class="tags tags--suggestions" id="panneau-projet-suggestions"></div>
</fieldset>
<p class="panneau__erreur" id="erreur-projet" role="alert" hidden></p>
<div class="panneau__actions">
<button type="button" id="supprimer-projet" class="bouton--danger">
Supprimer le projet
</button>
</div>
</form>
</aside>
<!-- Confirmation d'une action irréversible. -->
<dialog class="dialogue" id="dialogue-confirmation">
<form method="dialog">
<h2 id="dialogue-confirmation-titre">Confirmer</h2>
<p id="dialogue-confirmation-message"></p>
<menu class="dialogue__actions">
<button type="submit" value="annuler">Annuler</button>
<button type="submit" value="ok" class="bouton--principal" id="dialogue-confirmation-ok">
Confirmer
</button>
</menu>
</form>
</dialog>
<!-- Bandeau d'erreur bloquante : fichier corrompu, serveur injoignable. -->
<div class="alerte" id="alerte" hidden role="alert">
<p id="alerte-message"></p>
</div>
<script type="module" src="js/app.js"></script>
</body>
</html>