gmao/app_new/templates/equipments/index.html

215 lines
15 KiB
HTML
Raw Normal View History

2026-08-14 18:02:25 +02:00
{% extends "base.html" %}
{% block title %}Équipements — GMAO Collège{% endblock %}
{% block content %}
<div class="d-flex justify-content-between align-items-center mb-3">
<h2 class="mb-0 h5"><i class="bi bi-pc-display"></i> Équipements par zone</h2>
<div>
<a href="{{ url_for('wizard.equipment_wizard') }}" class="btn btn-success btn-sm me-2">
<i class="bi bi-magic"></i> <span class="d-none d-sm-inline">Inventaire guidé</span>
2026-08-14 18:02:25 +02:00
</a>
<a href="{{ url_for('equipments.create', advanced=1) }}" class="btn btn-outline-primary btn-sm">
<i class="bi bi-plus-lg"></i> <span class="d-none d-sm-inline">Fiche unitaire avancée</span><span class="d-sm-none">+</span>
2026-08-14 18:02:25 +02:00
</a>
</div>
</div>
<!-- Filtres -->
<div class="card border-0 shadow-sm mb-3">
<div class="card-body py-2">
<form method="GET" class="row g-2 g-md-3 align-items-end">
<div class="col-6 col-md-3">
<input type="text" name="search" class="form-control form-control-sm" placeholder="Rechercher..." value="{{ filters.search or '' }}">
</div>
<div class="col-6 col-md-2">
<select name="category" class="form-select form-select-sm">
<option value="">— Catégorie —</option>
{% for cat in categories %}
<option value="{{ cat.id }}" {% if filters.category == cat.id %}selected{% endif %}>{{ cat.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-6 col-md-2">
<select name="building" class="form-select form-select-sm">
<option value="">— Bâtiment —</option>
{% for b in buildings %}
<option value="{{ b.id }}" {% if filters.building == b.id %}selected{% endif %}>{{ b.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-6 col-md-2">
<select name="status" class="form-select form-select-sm">
<option value="">— Statut —</option>
<option value="en_service" {% if filters.status == 'en_service' %}selected{% endif %}>En service</option>
<option value="hors_service" {% if filters.status == 'hors_service' %}selected{% endif %}>Hors service</option>
<option value="en_reparation" {% if filters.status == 'en_reparation' %}selected{% endif %}>En réparation</option>
<option value="remplace" {% if filters.status == 'remplace' %}selected{% endif %}>Remplacé</option>
</select>
</div>
<div class="col-12 col-md-2">
<button type="submit" class="btn btn-sm btn-outline-primary"><i class="bi bi-funnel"></i> Filtrer</button>
<a href="{{ url_for('equipments.index') }}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-x-circle"></i></a>
</div>
</form>
</div>
</div>
{% if parents %}
<div class="accordion" id="parentsAccordion">
{% for parent in parents %}
<div class="card mb-2 border-0 shadow-sm">
<div class="card-header bg-light d-flex justify-content-between align-items-center py-2" id="heading-parent-{{ parent.id }}">
<button class="btn btn-link text-decoration-none text-dark fw-bold d-flex align-items-center" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-parent-{{ parent.id }}" aria-expanded="false">
<i class="bi bi-chevron-right me-2 parent-chevron"></i>
<i class="bi bi-collection me-2 text-info"></i>
{{ parent.name }}
<span class="badge bg-info ms-2">{{ parent.individual_count }} individu{{ 'els' if parent.individual_count > 1 else 'el' }}</span>
{% if parent.category %}
<span class="badge bg-secondary ms-1">{{ parent.category.name }}</span>
{% endif %}
</button>
<div class="d-flex gap-1">
<a href="{{ url_for('equipments.detail', id=parent.id) }}" class="btn btn-sm btn-outline-info" title="Voir"><i class="bi bi-eye"></i></a>
<a href="{{ url_for('equipments.edit', id=parent.id) }}" class="btn btn-sm btn-outline-primary" title="Modifier"><i class="bi bi-pencil"></i></a>
<form action="{{ url_for('equipments.delete', id=parent.id) }}" method="POST" class="d-inline" onsubmit="return confirm('Supprimer le groupe \"{{ parent.name }}\" et ses {{ parent.individual_count }} individuels ?')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn btn-sm btn-outline-danger" title="Supprimer le groupe et ses individuels"><i class="bi bi-trash"></i></button>
</form>
</div>
</div>
<div id="collapse-parent-{{ parent.id }}" class="collapse" aria-labelledby="heading-parent-{{ parent.id }}" data-bs-parent="#parentsAccordion">
<div class="card-body p-0">
{% if parent.zones_list %}
<div class="accordion" id="zonesAccordion-{{ parent.id }}">
{% for zone in parent.zones_list %}
<div class="card border-0">
<div class="card-header py-2 d-flex justify-content-between align-items-center bg-white" id="heading-zone-{{ parent.id }}-{{ zone.zone.id if zone.zone else 'none' }}">
<button class="btn btn-link text-decoration-none text-dark ps-0 d-flex align-items-center" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-zone-{{ parent.id }}-{{ zone.zone.id if zone.zone else 'none' }}" aria-expanded="false">
<i class="bi bi-chevron-right me-2 zone-chevron"></i>
<i class="bi bi-geo-alt me-1 text-secondary"></i>
{{ zone.name }}
<span class="badge bg-secondary ms-2">{{ zone.rooms_list|length }} salle{{ 's' if zone.rooms_list|length > 1 else '' }}</span>
</button>
</div>
<div id="collapse-zone-{{ parent.id }}-{{ zone.zone.id if zone.zone else 'none' }}" class="collapse" aria-labelledby="heading-zone-{{ parent.id }}-{{ zone.zone.id if zone.zone else 'none' }}" data-bs-parent="#zonesAccordion-{{ parent.id }}">
<div class="card-body p-0">
<div class="accordion" id="roomsAccordion-{{ parent.id }}-{{ zone.zone.id if zone.zone else 'none' }}">
{% for room_data in zone.rooms_list %}
<div class="card border-0">
<div class="card-header py-2 d-flex justify-content-between align-items-center" style="background:#fafafa;" id="heading-room-{{ parent.id }}-{{ room_data.room.id }}">
<button class="btn btn-link text-decoration-none text-dark ps-4 d-flex align-items-center" type="button" data-bs-toggle="collapse" data-bs-target="#collapse-room-{{ parent.id }}-{{ room_data.room.id }}" aria-expanded="false">
<i class="bi bi-chevron-right me-2 room-chevron"></i>
<i class="bi bi-door-open me-1 text-muted"></i>
{{ room_data.name }}
<span class="badge bg-light text-dark border ms-2">{{ room_data.individuals|length }}</span>
</button>
</div>
<div id="collapse-room-{{ parent.id }}-{{ room_data.room.id }}" class="collapse" aria-labelledby="heading-room-{{ parent.id }}-{{ room_data.room.id }}" data-bs-parent="#roomsAccordion-{{ parent.id }}-{{ zone.zone.id if zone.zone else 'none' }}">
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover table-sm mb-0">
<thead class="table-light">
<tr>
<th>Code</th>
<th>Nom / N°</th>
<th>Statut</th>
<th>Curatif</th>
<th style="width:110px;">Actions</th>
</tr>
</thead>
<tbody>
{% for indiv in room_data.individuals %}
<tr {% if indiv.needs_deep_work %}class="table-danger"{% endif %}>
<td><code>{{ indiv.code or '—' }}</code></td>
<td>
<i class="bi bi-cube text-muted me-1"></i>
<a href="{{ url_for('equipments.detail', id=indiv.id) }}">{{ indiv.name }}</a>
{% if indiv.individual_number %}
<span class="badge bg-light text-dark ms-1">#{{ indiv.individual_number }}</span>
{% endif %}
</td>
<td><span class="badge {{ indiv.status|status_badge }}">{{ indiv.status|format_status }}</span></td>
<td>
{% if indiv.curative_count > 0 %}
<span class="badge {% if indiv.needs_deep_work %}bg-danger{% else %}bg-warning text-dark{% endif %}">{{ indiv.curative_count }}</span>
{% else %}
<span class="text-muted">0</span>
{% endif %}
</td>
<td>
<a href="{{ url_for('equipments.detail', id=indiv.id) }}" class="btn btn-sm btn-outline-info" title="Voir"><i class="bi bi-eye"></i></a>
<a href="{{ url_for('equipments.edit', id=indiv.id) }}" class="btn btn-sm btn-outline-primary" title="Modifier"><i class="bi bi-pencil"></i></a>
<form action="{{ url_for('equipments.delete', id=indiv.id) }}" method="POST" class="d-inline" onsubmit="return confirm('Supprimer l\'élément \"{{ indiv.name }}\" ?')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<button type="submit" class="btn btn-sm btn-outline-danger" title="Supprimer"><i class="bi bi-trash"></i></button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<p class="text-muted p-3 mb-0"><i class="bi bi-info-circle"></i> Aucun individuel dans ce groupe.</p>
{% endif %}
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="text-center py-5 text-muted">
<i class="bi bi-inbox" style="font-size:3rem;"></i>
<p class="mt-2">Aucun équipement</p>
</div>
{% endif %}
{% if pagination and pagination.pages > 1 %}
<nav aria-label="Pagination équipements" class="mt-3">
<ul class="pagination pagination-sm justify-content-center">
<li class="page-item {% if not pagination.has_prev %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('equipments.index', page=pagination.prev_num, **filters) if pagination.has_prev else '#' }}">&laquo;</a>
</li>
{% for p in pagination.iter_pages(left_edge=1, right_edge=1, left_current=2, right_current=2) %}
{% if p %}
<li class="page-item {% if p == pagination.page %}active{% endif %}">
<a class="page-link" href="{{ url_for('equipments.index', page=p, **filters) }}">{{ p }}</a>
</li>
{% else %}
<li class="page-item disabled"><span class="page-link"></span></li>
{% endif %}
{% endfor %}
<li class="page-item {% if not pagination.has_next %}disabled{% endif %}">
<a class="page-link" href="{{ url_for('equipments.index', page=pagination.next_num, **filters) if pagination.has_next else '#' }}">&raquo;</a>
</li>
</ul>
</nav>
<p class="text-center text-muted small">
Page {{ pagination.page }} / {{ pagination.pages }} — {{ pagination.total }} groupe(s) au total
</p>
{% endif %}
<script>
document.querySelectorAll('.collapse').forEach(collapse => {
collapse.addEventListener('shown.bs.collapse', function() {
const chevron = this.closest('.card').querySelector('.parent-chevron, .zone-chevron, .room-chevron');
if (chevron) chevron.classList.replace('bi-chevron-right', 'bi-chevron-down');
});
collapse.addEventListener('hidden.bs.collapse', function() {
const chevron = this.closest('.card').querySelector('.parent-chevron, .zone-chevron, .room-chevron');
if (chevron) chevron.classList.replace('bi-chevron-down', 'bi-chevron-right');
});
});
</script>
{% endblock %}