Évolution du thème existant "étiquette d'inventaire" plutôt que remplacement : couche de tokens sémantiques + échelles espacement/ typographie, mode sombre via prefers-color-scheme, hiérarchie de boutons (primaire/secondaire/danger), icônes SVG et état actif dans la nav, fil d'Ariane et compteur de résultats sur /materiels, badges de stock bas réutilisés depuis /scan, formulaire de planification regroupé en carte sur /destinataires, et correctif de contraste en mode sombre (inputs natifs + boutons qui restaient rendus en clair). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EEC2W1xtjmpHkEYgua7gWC
150 lines
6.2 KiB
HTML
150 lines
6.2 KiB
HTML
{% extends "base.html" %}
|
|
{% block titre %}Matériels - Gestion de stock IT{% endblock %}
|
|
|
|
{#
|
|
Macro pour une ligne de matériel : utilisée à la fois en affichage plat
|
|
et en affichage groupé par catégorie (tri=categorie), pour ne pas
|
|
dupliquer le HTML entre les deux.
|
|
|
|
Chaque ligne est repliée par défaut (juste le résumé, pas les champs
|
|
d'édition) : avec beaucoup de matériels, afficher 5 <input> par ligne en
|
|
permanence rend la liste illisible et pousse le contenu hors de l'écran.
|
|
On ne dépense la largeur d'un vrai formulaire que sur la ligne qu'on
|
|
déplie. Voir aussi CLAUDE.md pour le contexte de ce choix.
|
|
#}
|
|
{% macro ligne_materiel(materiel, categories_aplaties) %}
|
|
{% set sous_seuil = materiel.seuil_alerte is not none and materiel.quantite < materiel.seuil_alerte %}
|
|
<li>
|
|
<details class="ligne-materiel">
|
|
<summary>
|
|
<span class="noeud-chevron" aria-hidden="true"></span>
|
|
<span class="materiel-nom">{{ materiel.nom }}</span>
|
|
<span class="materiel-categorie">{{ materiel.categorie.chemin() }}</span>
|
|
<span class="materiel-code">{{ materiel.code_barre or "—" }}</span>
|
|
<span class="materiel-quantite {{ 'quantite-basse' if sous_seuil else '' }}">
|
|
{{ materiel.quantite }}
|
|
{% if sous_seuil %}<span class="badge badge-bas"><span class="statut-puce" aria-hidden="true"></span>bas</span>{% endif %}
|
|
</span>
|
|
</summary>
|
|
|
|
<form method="post" action="/materiels/{{ materiel.id }}/modifier" class="formulaire-edition">
|
|
<label>Nom
|
|
<input type="text" name="nom" value="{{ materiel.nom }}" required>
|
|
</label>
|
|
<label>Catégorie
|
|
<select name="categorie_id" required>
|
|
{% for profondeur, categorie in categories_aplaties %}
|
|
<option value="{{ categorie.id }}" {{ "selected" if categorie.id == materiel.categorie_id else "" }}>
|
|
{{ "— " * profondeur }}{{ categorie.nom }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
</label>
|
|
<label>Code-barre
|
|
<input type="text" name="code_barre" value="{{ materiel.code_barre or '' }}">
|
|
</label>
|
|
<label>Quantité
|
|
<input type="number" name="quantite" value="{{ materiel.quantite }}" min="0" required>
|
|
</label>
|
|
<label>Seuil d'alerte
|
|
<input type="number" name="seuil_alerte" value="{{ materiel.seuil_alerte if materiel.seuil_alerte is not none else '' }}" min="0">
|
|
</label>
|
|
<div class="formulaire-actions">
|
|
<button type="submit">Enregistrer</button>
|
|
<button type="submit" formaction="/materiels/{{ materiel.id }}/supprimer" class="bouton-danger">Supprimer</button>
|
|
</div>
|
|
</form>
|
|
</details>
|
|
</li>
|
|
{% endmacro %}
|
|
|
|
{% block contenu %}
|
|
<h1>Matériels</h1>
|
|
|
|
<form method="get" action="/materiels" class="barre-filtres">
|
|
<input type="search" name="q" value="{{ q }}" placeholder="Rechercher par nom ou code-barre…">
|
|
|
|
<select name="categorie_id">
|
|
<option value="">Toutes les catégories</option>
|
|
{% for profondeur, categorie in categories_aplaties %}
|
|
<option value="{{ categorie.id }}" {{ "selected" if categorie_id == categorie.id|string else "" }}>
|
|
{{ "— " * profondeur }}{{ categorie.nom }}
|
|
</option>
|
|
{% endfor %}
|
|
</select>
|
|
|
|
<select name="tri">
|
|
<option value="nom_asc" {{ "selected" if tri == "nom_asc" else "" }}>Nom (A → Z)</option>
|
|
<option value="nom_desc" {{ "selected" if tri == "nom_desc" else "" }}>Nom (Z → A)</option>
|
|
<option value="quantite_asc" {{ "selected" if tri == "quantite_asc" else "" }}>Quantité (croissant)</option>
|
|
<option value="quantite_desc" {{ "selected" if tri == "quantite_desc" else "" }}>Quantité (décroissant)</option>
|
|
<option value="categorie" {{ "selected" if tri == "categorie" else "" }}>Regrouper par catégorie</option>
|
|
</select>
|
|
|
|
<label class="case-stock-bas">
|
|
<input type="checkbox" name="stock_bas" value="1" {{ "checked" if stock_bas else "" }}>
|
|
Stock bas uniquement
|
|
</label>
|
|
|
|
<button type="submit">Filtrer</button>
|
|
{% if q or categorie_id or stock_bas or tri != "nom_asc" %}
|
|
<a href="/materiels" class="lien-effacer">Réinitialiser</a>
|
|
{% endif %}
|
|
|
|
<span class="compteur-resultats">{{ materiels|length }} matériel(s)</span>
|
|
</form>
|
|
|
|
{#
|
|
Le select de catégorie ci-dessus ne montre plus, une fois soumis, quel
|
|
sous-arbre est réellement affiché (juste son nom dans la liste
|
|
déroulante). Ce fil d'Ariane le rappelle explicitement, avec un lien
|
|
pour revenir à "toutes les catégories".
|
|
#}
|
|
{% if categorie_selectionnee %}
|
|
<p class="fil-ariane-categorie">
|
|
Filtré sur <span class="chemin-categorie">{{ categorie_selectionnee.chemin() }}</span>
|
|
<a href="/materiels{{ '?q=' + q if q else '' }}" class="lien-effacer">(retirer)</a>
|
|
</p>
|
|
{% endif %}
|
|
|
|
<ul class="liste-materiels">
|
|
{% if groupes %}
|
|
{% for chemin, groupe in groupes %}
|
|
<li class="groupe-categorie-entete">{{ chemin }}</li>
|
|
{% for materiel in groupe %}
|
|
{{ ligne_materiel(materiel, categories_aplaties) }}
|
|
{% endfor %}
|
|
{% endfor %}
|
|
{% else %}
|
|
{% for materiel in materiels %}
|
|
{{ ligne_materiel(materiel, categories_aplaties) }}
|
|
{% endfor %}
|
|
{% endif %}
|
|
{% if not materiels %}
|
|
<li class="liste-vide">
|
|
{% if q or categorie_id or stock_bas %}Aucun matériel ne correspond à ces filtres.{% else %}Aucun matériel pour l'instant.{% endif %}
|
|
</li>
|
|
{% endif %}
|
|
</ul>
|
|
|
|
<h2>Ajouter un matériel</h2>
|
|
{% if categories_aplaties %}
|
|
<form method="post" action="/materiels" class="formulaire-grille">
|
|
<label>Nom <input type="text" name="nom" required></label>
|
|
<label>Catégorie
|
|
<select name="categorie_id" required>
|
|
{% for profondeur, categorie in categories_aplaties %}
|
|
<option value="{{ categorie.id }}">{{ "— " * profondeur }}{{ categorie.nom }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</label>
|
|
<label>Code-barre <input type="text" name="code_barre"></label>
|
|
<label>Quantité initiale <input type="number" name="quantite" value="0" min="0"></label>
|
|
<label>Seuil d'alerte <input type="number" name="seuil_alerte" min="0"></label>
|
|
<button type="submit" class="bouton-primaire">Créer</button>
|
|
</form>
|
|
{% else %}
|
|
<p>Créez d'abord une <a href="/categories">catégorie</a> avant d'ajouter un matériel.</p>
|
|
{% endif %}
|
|
{% endblock %}
|