IT-Stock/app/templates/materiels.html

150 lines
6.2 KiB
HTML
Raw Normal View History

{% 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 %}