loko/assets/forms.py \
loko/assets/admin.py \
loko/assets/templates/assets/buildings/ \
loko/assets/tests.py \
loko/assets/migrations/0028_buildingasset_contractor_name_and_more.py
git commit -m "feat(assets): extend building locations and technical equipment models
- Add comprehensive building locations (stairwells, technical rooms, boiler rooms, offices, etc.)
- Add building physical & technical fields (room numbers, floor types, heated status, dimensions)
- Add complete building envelope & technical equipment types (roofs, facades, windows, doors, boilers, heat pumps, etc.)
- Enrich admin, forms, trees, and detail templates with icons, macro-lots, and maintenance tracking
- Add migration 0028 and unit test coverage"
570 lines
34 KiB
HTML
570 lines
34 KiB
HTML
{% extends "base.html" %}
|
||
{% load static i18n documents_tags %}
|
||
|
||
{% block title %}{{ building.code }} - {{ building.get_name }} | {{ product_name }}{% endblock %}
|
||
|
||
{% block extra_head %}
|
||
<!-- MapLibre GL CSS -->
|
||
<link href="https://unpkg.com/maplibre-gl@3.6.2/dist/maplibre-gl.css" rel="stylesheet" />
|
||
<script src="https://unpkg.com/maplibre-gl@3.6.2/dist/maplibre-gl.js"></script>
|
||
{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="container-fluid py-4">
|
||
<!-- Breadcrumb & Actions -->
|
||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||
<nav aria-label="breadcrumb">
|
||
<ol class="breadcrumb mb-0">
|
||
<li class="breadcrumb-item"><a href="{% url 'assets:buildings_index' %}">{% translate "Bâtiments publics" %}</a></li>
|
||
<li class="breadcrumb-item active" aria-current="page">{{ building.code }}</li>
|
||
</ol>
|
||
</nav>
|
||
<div class="d-flex gap-2">
|
||
{% if can_edit %}
|
||
<a href="{% url 'assets:publicbuilding_edit' building.pk %}" class="btn btn-primary btn-sm">
|
||
<i class="bi bi-pencil me-1"></i>{% translate "Modifier" %}
|
||
</a>
|
||
{% endif %}
|
||
<a href="{% url 'assets:buildings_index' %}" class="btn btn-secondary btn-sm">
|
||
<i class="bi bi-arrow-left me-1"></i>{% translate "Retour à la liste" %}
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Header Card -->
|
||
<div class="card shadow-sm mb-4">
|
||
<div class="card-body">
|
||
<div class="row align-items-center">
|
||
<div class="col-md-8">
|
||
<h2 class="h4 mb-1">
|
||
<i class="bi bi-building text-primary me-2"></i>{{ building.code }} - {{ building.get_name }}
|
||
</h2>
|
||
<p class="text-muted mb-2"><i class="bi bi-geo-alt me-1"></i>{{ building.address|default:_("Adresse non renseignée") }}</p>
|
||
<div class="d-flex gap-2 flex-wrap">
|
||
<span class="badge bg-primary-subtle text-primary border">{{ building.get_usage_type_display }}</span>
|
||
<span class="badge bg-success-subtle text-success border">{{ building.get_building_status_display }}</span>
|
||
{% if building.is_erp %}
|
||
<span class="badge bg-info-subtle text-info border">{{ regional_terms.public_access_short }} (Cat. {{ building.erp_category|default:"-" }})</span>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
<div class="col-md-4 text-md-end mt-3 mt-md-0">
|
||
{% if building.dpe_rating %}
|
||
<div class="d-inline-block text-center me-3">
|
||
<span class="d-block text-muted small">{{ regional_terms.energy_diag_short }}</span>
|
||
<span class="badge bg-success fs-5 px-3 py-2">Classe {{ building.dpe_rating }}</span>
|
||
</div>
|
||
{% endif %}
|
||
{% if building.ges_rating %}
|
||
<div class="d-inline-block text-center">
|
||
<span class="d-block text-muted small">{{ regional_terms.carbon_diag_short }}</span>
|
||
<span class="badge bg-primary fs-5 px-3 py-2">Classe {{ building.ges_rating }}</span>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 3 Core Tabs Navigation -->
|
||
<ul class="nav nav-tabs mb-4" id="buildingTabs" role="tablist">
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link active" id="overview-tab" data-bs-toggle="tab" data-bs-target="#overview" type="button" role="tab">
|
||
<i class="bi bi-info-circle me-1"></i>{% translate "Vue d'ensemble" %}
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="equipments-tab" data-bs-toggle="tab" data-bs-target="#equipments" type="button" role="tab">
|
||
<i class="bi bi-gear me-1"></i>{% translate "Assets & Équipements" %} ({{ equipments|length }})
|
||
</button>
|
||
</li>
|
||
<li class="nav-item" role="presentation">
|
||
<button class="nav-link" id="interventions-tab" data-bs-toggle="tab" data-bs-target="#interventions" type="button" role="tab">
|
||
<i class="bi bi-tools me-1"></i>{% translate "Interventions" %} ({{ interventions.count }})
|
||
</button>
|
||
</li>
|
||
</ul>
|
||
|
||
<!-- Tabs Content -->
|
||
<div class="tab-content" id="buildingTabsContent">
|
||
|
||
<!-- 1. Vue d'ensemble (Dashboard avec Cards directes) -->
|
||
<div class="tab-pane fade show active" id="overview" role="tabpanel">
|
||
<div class="row g-4">
|
||
|
||
<!-- Left Column: Propriété, Caractéristiques & Arborescence Niveaux/Locaux/Équipements -->
|
||
<div class="col-md-7">
|
||
|
||
<!-- Card 1: Propriété & Responsables -->
|
||
<div class="card shadow-sm mb-4">
|
||
<div class="card-header bg-light fw-bold"><i class="bi bi-person-badge me-2"></i>{% translate "Propriété & Responsables" %}</div>
|
||
<div class="card-body">
|
||
<table class="table table-borderless mb-0">
|
||
<tr>
|
||
<th class="w-50 text-muted">{% translate "Propriétaire" %} :</th>
|
||
<td>{{ building.owner|default:"-" }}</td>
|
||
</tr>
|
||
<tr>
|
||
<th class="text-muted">{% translate "Gestionnaire référent" %} :</th>
|
||
<td>{% if building.manager %}{{ building.manager.get_full_name|default:building.manager.username }}{% else %}-{% endif %}</td>
|
||
</tr>
|
||
<tr>
|
||
<th class="text-muted">{% translate "Service occupant" %} :</th>
|
||
<td>{{ building.occupant_entity|default:"-" }}</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Card 2: Caractéristiques physiques & Normes -->
|
||
<div class="card shadow-sm mb-4">
|
||
<div class="card-header bg-light fw-bold"><i class="bi bi-rulers me-2"></i>{% translate "Caractéristiques physiques &" %} {{ regional_terms.public_access_short }}</div>
|
||
<div class="card-body">
|
||
<table class="table table-borderless mb-0">
|
||
<tr>
|
||
<th class="w-50 text-muted">{% translate "Année de construction" %} :</th>
|
||
<td>{{ building.construction_year|default:"-" }}</td>
|
||
</tr>
|
||
<tr>
|
||
<th class="text-muted">{% translate "Étages / Sous-sols" %} :</th>
|
||
<td>{{ building.floors_count }} {% translate "étage(s)" %} / {{ building.basement_count }} {% translate "sous-sol(s)" %}</td>
|
||
</tr>
|
||
<tr>
|
||
<th class="text-muted">{% translate "Surface Utile / Brut" %} :</th>
|
||
<td>{{ building.usable_surface_m2|default:"-" }} m² / {{ building.total_surface_m2|default:"-" }} m²</td>
|
||
</tr>
|
||
{% if building.room_number %}
|
||
<tr>
|
||
<th class="text-muted">{% translate "Numéro de local / porte" %} :</th>
|
||
<td><span class="badge bg-secondary-subtle text-secondary border font-monospace"><i class="bi bi-tag me-1"></i>{{ building.room_number }}</span></td>
|
||
</tr>
|
||
{% endif %}
|
||
{% if building.surface_m2 %}
|
||
<tr>
|
||
<th class="text-muted">{% translate "Surface spécifique" %} :</th>
|
||
<td>{{ building.surface_m2 }} m²</td>
|
||
</tr>
|
||
{% endif %}
|
||
{% if building.ceiling_height_m %}
|
||
<tr>
|
||
<th class="text-muted">{% translate "Hauteur sous plafond" %} :</th>
|
||
<td>{{ building.ceiling_height_m }} m</td>
|
||
</tr>
|
||
{% endif %}
|
||
<tr>
|
||
<th class="text-muted">{% translate "Chauffage du local" %} :</th>
|
||
<td>{% if building.is_heated %}<span class="text-success"><i class="bi bi-fire me-1"></i>{% translate "Chauffé" %}</span>{% else %}<span class="text-muted"><i class="bi bi-slash-circle me-1"></i>{% translate "Non chauffé" %}</span>{% endif %}</td>
|
||
</tr>
|
||
{% if building.floor_type %}
|
||
<tr>
|
||
<th class="text-muted">{% translate "Revêtement de sol" %} :</th>
|
||
<td>{{ building.get_floor_type_display }}</td>
|
||
</tr>
|
||
{% endif %}
|
||
{% if building.fire_compartment %}
|
||
<tr>
|
||
<th class="text-muted">{% translate "Compartiment coupe-feu" %} :</th>
|
||
<td>{{ building.fire_compartment }}</td>
|
||
</tr>
|
||
{% endif %}
|
||
<tr>
|
||
<th class="text-muted">{{ regional_terms.public_access_category }} :</th>
|
||
<td>{{ building.erp_category|default:"-" }}</td>
|
||
</tr>
|
||
<tr>
|
||
<th class="text-muted">{% translate "Accessibilité PMR" %} :</th>
|
||
<td>{% if building.pmr_accessible %}<span class="text-success"><i class="bi bi-check-circle-fill me-1"></i>{% translate "Oui" %}</span>{% else %}<span class="text-muted">{% translate "Non" %}</span>{% endif %}</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Card 3: Arborescence Unifiée des Localisations & Assets -->
|
||
<div class="card shadow-sm mb-4">
|
||
<div class="card-header bg-light fw-bold d-flex justify-content-between align-items-center">
|
||
<span><i class="bi bi-diagram-3 me-2"></i>{% translate "Arborescence Hiérarchique" %}</span>
|
||
<span class="badge bg-primary-subtle text-primary border">{{ location_tree.children|length }} {% translate "sous-localisation(s)" %}</span>
|
||
</div>
|
||
<div class="card-body p-3">
|
||
|
||
<!-- Localisation Parente (si existante) -->
|
||
{% if parent_location %}
|
||
<div class="card bg-light border-secondary-subtle p-2 mb-3">
|
||
<div class="d-flex align-items-center justify-content-between">
|
||
<div class="d-flex align-items-center gap-2">
|
||
<i class="bi bi-geo-alt-fill text-danger fs-5"></i>
|
||
<div>
|
||
<div class="small text-muted fw-semibold">{% translate "Localisation parente" %} :</div>
|
||
<a href="{{ parent_location.get_absolute_url }}" class="fw-bold text-primary text-decoration-underline">
|
||
{{ parent_location.code }} - {{ parent_location.get_name }}
|
||
</a>
|
||
<span class="badge bg-secondary-subtle text-dark border small ms-1">{{ parent_location.get_location_type_display }}</span>
|
||
</div>
|
||
</div>
|
||
<a href="{{ parent_location.get_absolute_url }}" class="btn btn-sm btn-outline-secondary py-0 px-2 small">
|
||
<i class="bi bi-arrow-up me-1"></i>{% translate "Voir la parente" %}
|
||
</a>
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Localisation Courante (Racine de l'affichage) -->
|
||
<div class="p-2 border rounded bg-white mb-2">
|
||
<div class="d-flex align-items-center justify-content-between">
|
||
<div class="d-flex align-items-center gap-2">
|
||
<i class="bi {{ building.get_icon_class }} text-primary fs-5"></i>
|
||
<span class="fw-bold fs-6 text-primary">{{ building.code }}</span>
|
||
{% if building.room_number %}
|
||
<span class="badge bg-secondary-subtle text-secondary border ms-1 font-monospace"><i class="bi bi-tag me-1"></i>{{ building.room_number }}</span>
|
||
{% endif %}
|
||
<span class="fw-bold fs-6">- {{ building.get_name }}</span>
|
||
<span class="badge bg-primary text-white ms-1">{{ building.get_location_type_display }}</span>
|
||
|
||
{% if location_tree.assets %}
|
||
<button class="btn btn-sm btn-outline-info py-0 px-2 small ms-2 text-nowrap"
|
||
type="button"
|
||
data-bs-toggle="collapse"
|
||
data-bs-target="#tree-assets-root"
|
||
aria-expanded="false">
|
||
<i class="bi bi-tools me-1"></i>Assets ({{ location_tree.assets|length }})
|
||
</button>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Collapsible Assets de la localisation courante (NON VISIBLES PAR DÉFAUT) -->
|
||
{% if location_tree.assets %}
|
||
<div id="tree-assets-root" class="collapse mt-2 ms-4">
|
||
<div class="card card-body bg-light border-info-subtle p-2 mb-2">
|
||
<div class="fw-bold small text-info mb-1"><i class="bi bi-tools me-1"></i>{% translate "Assets rattachés à" %} {{ building.code }} :</div>
|
||
{% include "assets/buildings/publicbuilding_asset_tree.html" with asset_nodes=location_tree.assets only %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Sous-localisations & leurs assets -->
|
||
<div class="mt-2">
|
||
{% include "assets/buildings/publicbuilding_location_tree.html" with node=location_tree depth=1 only %}
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<!-- Right Column: Localisation & Documents (sous la carte) -->
|
||
<div class="col-md-5">
|
||
|
||
<!-- Card Localisation & Carte interactive -->
|
||
<div class="card shadow-sm mb-4">
|
||
<div class="card-header bg-light fw-bold d-flex justify-content-between align-items-center">
|
||
<span><i class="bi bi-geo-alt me-2"></i>{% translate "Localisation & Carte" %}</span>
|
||
{% if center_lat and center_lon %}
|
||
<a href="https://www.google.com/maps?cbll={{ center_lat|floatformat:'6u'}},{{ center_lon|floatformat:'6u'}}&cbp=12,0,0,0,5&layer=c&panoid="
|
||
target="_blank" class="btn btn-sm btn-outline-secondary py-0 px-2">
|
||
<img src="{% static 'common/google-street-view-logo.png' %}" alt="Street View" height="14" class="me-1">
|
||
Street View
|
||
</a>
|
||
{% endif %}
|
||
</div>
|
||
<div class="card-body p-0">
|
||
<div id="building-map" style="height: 340px; width: 100%; border-radius: 0 0 4px 4px;"></div>
|
||
</div>
|
||
<div class="card-footer bg-white p-3">
|
||
<p class="mb-2 small">
|
||
<strong>{% translate "Adresse" %} :</strong> {{ building.address|default:_("Non renseignée") }}
|
||
</p>
|
||
{% if center_lat and center_lon %}
|
||
<div class="small text-muted font-monospace">
|
||
WGS84: {{ center_lat|floatformat:6 }}, {{ center_lon|floatformat:6 }}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Card Documents associés (placée sous la localisation) -->
|
||
<div class="card shadow-sm mb-4">
|
||
<div class="card-header bg-light d-flex justify-content-between align-items-center">
|
||
<span class="fw-bold"><i class="bi bi-file-earmark-text me-2"></i>{% translate "Documents associés" %} ({{ documents.count }})</span>
|
||
<a href="{% url 'admin:assets_publicbuildingdocument_add' %}?building={{ building.pk }}" class="btn btn-sm btn-outline-primary py-0 px-2">
|
||
<i class="bi bi-plus-lg me-1"></i>{% translate "Ajouter" %}
|
||
</a>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="row row-cols-1 row-cols-sm-2 g-3">
|
||
{% for doc in documents %}
|
||
<div class="col">
|
||
<div class="card h-100 border text-center p-2">
|
||
{% if doc.document.file %}
|
||
<i class="bi bi-file-earmark-pdf text-danger fs-2 mb-1"></i>
|
||
<div class="small text-truncate w-100 fw-bold" title="{{ doc.document.file.name }}">{{ doc.document.file.name }}</div>
|
||
<a href="{{ doc.document.file.url }}" target="_blank" class="btn btn-sm btn-outline-secondary mt-2">
|
||
<i class="bi bi-download me-1"></i>{% translate "Télécharger" %}
|
||
</a>
|
||
{% else %}
|
||
<i class="bi bi-file-earmark text-secondary fs-2 mb-1"></i>
|
||
<span class="text-muted small">{% translate "Fichier indisponible" %}</span>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
{% empty %}
|
||
<div class="col-12 text-center text-muted py-3">
|
||
<i class="bi bi-folder-x fs-4 d-block mb-1"></i>
|
||
{% translate "Aucun document associé." %}
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 2. Assets & Équipements Tab -->
|
||
<div class="tab-pane fade" id="equipments" role="tabpanel">
|
||
<div class="card shadow-sm">
|
||
<div class="card-header bg-light d-flex justify-content-between align-items-center">
|
||
<h5 class="card-title h6 mb-0"><i class="bi bi-tools me-2"></i>{% translate "Assets Physiques & Équipements de la Localisation et de ses sous-localisations" %}</h5>
|
||
<span class="badge bg-info-subtle text-info border">{{ equipments|length }} {% translate "asset(s)" %}</span>
|
||
</div>
|
||
<div class="card-body p-0">
|
||
<div class="table-responsive">
|
||
<table class="table table-hover align-middle mb-0">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<th>{% translate "Code / Nom" %}</th>
|
||
<th>{% translate "Lot / Type d'asset" %}</th>
|
||
<th>{% translate "Caractéristiques & Spécifications" %}</th>
|
||
<th>{% translate "Localisation spécifique" %}</th>
|
||
<th>{% translate "Asset Parent" %}</th>
|
||
<th>{% translate "Statut" %}</th>
|
||
<th>{% translate "Prochain contrôle" %}</th>
|
||
<th>{% translate "Maintenance & Prestataire" %}</th>
|
||
<th class="text-end pe-3">{% translate "Actions" %}</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for eq in equipments %}
|
||
<tr>
|
||
<td>
|
||
<a href="{{ eq.get_absolute_url }}" class="fw-bold text-decoration-none">
|
||
<i class="bi {{ eq.get_icon_class }} me-1 text-primary"></i>{{ eq.code }}
|
||
</a>
|
||
<div class="small text-muted">{{ eq.get_name }}</div>
|
||
</td>
|
||
<td>
|
||
<span class="badge bg-primary-subtle text-primary border me-1">{{ eq.get_macro_lot }}</span>
|
||
<span class="badge bg-info-subtle text-info border d-block mt-1">
|
||
{{ eq.get_asset_type_display }}
|
||
</span>
|
||
</td>
|
||
<td>
|
||
{% if eq.dimensions or eq.surface_m2 %}
|
||
<div class="small fw-semibold">{% if eq.dimensions %}{{ eq.dimensions }}{% endif %}{% if eq.surface_m2 %} ({{ eq.surface_m2 }} m²){% endif %}</div>
|
||
{% endif %}
|
||
{% if eq.power_kw or eq.energy_source %}
|
||
<div class="small text-muted">
|
||
{% if eq.power_kw %}<i class="bi bi-lightning-charge me-1"></i>{{ eq.power_kw }} kW{% endif %}
|
||
{% if eq.energy_source %}<span class="badge bg-warning-subtle text-dark border ms-1">{{ eq.get_energy_source_display }}</span>{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
{% if eq.material or eq.fire_rating or eq.glazing_type %}
|
||
<div class="small text-muted">
|
||
{% if eq.material %}{{ eq.material }}{% endif %}
|
||
{% if eq.glazing_type %} - {{ eq.get_glazing_type_display }}{% endif %}
|
||
{% if eq.fire_rating %}<span class="badge bg-danger-subtle text-danger border ms-1">{{ eq.fire_rating }}</span>{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
</td>
|
||
<td>
|
||
{% if eq.location %}
|
||
<a href="{{ eq.location.get_absolute_url }}" class="small text-dark text-decoration-none">
|
||
<i class="bi {{ eq.location.get_icon_class }} me-1"></i>{{ eq.location.code }} - {{ eq.location.get_name }}
|
||
</a>
|
||
{% else %}
|
||
<span class="small text-muted">{{ eq.building.code }}</span>
|
||
{% endif %}
|
||
</td>
|
||
<td>
|
||
{% if eq.parent_asset %}
|
||
<a href="{{ eq.parent_asset.get_absolute_url }}" class="small text-decoration-none">
|
||
<i class="bi {{ eq.parent_asset.get_icon_class }} me-1"></i>{{ eq.parent_asset.code }}
|
||
</a>
|
||
{% else %}
|
||
<span class="text-muted small">-</span>
|
||
{% endif %}
|
||
</td>
|
||
<td>
|
||
<span class="badge {% if eq.status == 'active' %}bg-success-subtle text-success border{% else %}bg-secondary-subtle text-secondary border{% endif %}">
|
||
{{ eq.get_status_display }}
|
||
</span>
|
||
</td>
|
||
<td>{{ eq.next_control_date|date:"d/m/Y"|default:"-" }}</td>
|
||
<td>
|
||
{% if eq.contractor_name %}
|
||
<div class="small fw-semibold"><i class="bi bi-building me-1"></i>{{ eq.contractor_name }}</div>
|
||
{% endif %}
|
||
{% if eq.maintenance_contract_ref %}
|
||
<div class="small text-muted font-monospace">Réf: {{ eq.maintenance_contract_ref }}</div>
|
||
{% elif not eq.contractor_name %}
|
||
<span class="text-muted small">-</span>
|
||
{% endif %}
|
||
</td>
|
||
<td class="text-end pe-3">
|
||
<a href="{{ eq.get_absolute_url }}" class="btn btn-sm btn-outline-info py-0 px-2 small text-nowrap" title="{% translate 'Détail de l’asset' %}">
|
||
<i class="bi bi-eye me-1"></i>{% translate "Détail" %}
|
||
</a>
|
||
</td>
|
||
</tr>
|
||
{% empty %}
|
||
<tr>
|
||
<td colspan="9" class="text-center text-muted py-4">{% translate "Aucun asset ou équipement technique enregistré." %}</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 3. Interventions Tab -->
|
||
<div class="tab-pane fade" id="interventions" role="tabpanel">
|
||
<div class="card shadow-sm">
|
||
<div class="card-header bg-light d-flex justify-content-between align-items-center">
|
||
<h5 class="card-title h6 mb-0"><i class="bi bi-tools me-2"></i>{% translate "Interventions & Maintenance" %}</h5>
|
||
<a href="{% url 'interventions:interventions_index' %}?asset={{ building.pk }}" class="btn btn-sm btn-outline-primary">
|
||
<i class="bi bi-plus-lg me-1"></i>{% translate "Nouvelle intervention" %}
|
||
</a>
|
||
</div>
|
||
<div class="card-body p-0">
|
||
<table class="table table-hover align-middle mb-0">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<th style="width: 50px;"></th>
|
||
<th>{% translate "Code" %}</th>
|
||
<th>{% translate "Titre / Objet" %}</th>
|
||
<th>{% translate "Date de début" %}</th>
|
||
<th>{% translate "Statut" %}</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{% for interv in interventions %}
|
||
<tr>
|
||
<td>
|
||
<a href="{% url 'interventions:interventions_index' %}{{ interv.id }}" class="btn btn-sm btn-light border" title="{% translate 'Détail' %}">
|
||
<i class="bi bi-info-circle"></i>
|
||
</a>
|
||
</td>
|
||
<td><strong>{{ interv.code }}</strong></td>
|
||
<td>{{ interv.title }}</td>
|
||
<td>{{ interv.begin_time|date:"d/m/Y H:i"|default:"-" }}</td>
|
||
<td><span class="badge bg-secondary">{{ interv.get_status_display }}</span></td>
|
||
</tr>
|
||
{% empty %}
|
||
<tr>
|
||
<td colspan="5" class="text-center text-muted py-4">
|
||
<i class="bi bi-tools fs-2 d-block mb-2 text-muted"></i>
|
||
{% translate "Aucune intervention enregistrée pour ce bâtiment." %}
|
||
</td>
|
||
</tr>
|
||
{% endfor %}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
{% endblock %}
|
||
|
||
{% block script %}
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const buildingGeom = {{ building_geojson_json|safe }};
|
||
const centerLon = {{ center_lon|default:4.3572 }};
|
||
const centerLat = {{ center_lat|default:50.8477 }};
|
||
let map = null;
|
||
|
||
function initMap() {
|
||
const container = document.getElementById('building-map');
|
||
if (!container) return;
|
||
|
||
map = new maplibregl.Map({
|
||
container: 'building-map',
|
||
style: '{{ MAP_STYLE_LIGHT|default:"https://api.maptiler.com/maps/streets-v2-light/style.json" }}',
|
||
center: [centerLon, centerLat],
|
||
zoom: 16
|
||
});
|
||
|
||
map.addControl(new maplibregl.NavigationControl());
|
||
|
||
map.on('load', function() {
|
||
if (buildingGeom) {
|
||
map.addSource('building-footprint', {
|
||
'type': 'geojson',
|
||
'data': buildingGeom
|
||
});
|
||
|
||
map.addLayer({
|
||
'id': 'building-fill',
|
||
'type': 'fill',
|
||
'source': 'building-footprint',
|
||
'paint': {
|
||
'fill-color': '#2B579A',
|
||
'fill-opacity': 0.4
|
||
}
|
||
});
|
||
|
||
map.addLayer({
|
||
'id': 'building-outline',
|
||
'type': 'line',
|
||
'source': 'building-footprint',
|
||
'paint': {
|
||
'line-color': '#2B579A',
|
||
'line-width': 3
|
||
}
|
||
});
|
||
}
|
||
});
|
||
}
|
||
|
||
initMap();
|
||
|
||
const overviewTab = document.getElementById('overview-tab');
|
||
if (overviewTab) {
|
||
overviewTab.addEventListener('shown.bs.tab', function () {
|
||
if (map) map.resize();
|
||
});
|
||
}
|
||
|
||
// Gestion du chevron plié / déplié dans l'arborescence
|
||
document.querySelectorAll('.tree-toggle-btn').forEach(function(btn) {
|
||
const targetId = btn.getAttribute('data-bs-target');
|
||
const target = document.querySelector(targetId);
|
||
if (target) {
|
||
target.addEventListener('show.bs.collapse', function (e) {
|
||
if (e.target !== target) return;
|
||
const icon = btn.querySelector('i');
|
||
if (icon) {
|
||
icon.classList.remove('bi-chevron-right');
|
||
icon.classList.add('bi-chevron-down');
|
||
}
|
||
});
|
||
target.addEventListener('hide.bs.collapse', function (e) {
|
||
if (e.target !== target) return;
|
||
const icon = btn.querySelector('i');
|
||
if (icon) {
|
||
icon.classList.remove('bi-chevron-down');
|
||
icon.classList.add('bi-chevron-right');
|
||
}
|
||
});
|
||
}
|
||
});
|
||
});
|
||
</script>
|
||
{% endblock script %}
|