loko/streetup/assets/templates/assets/buildings/publicbuilding_form.html

415 lines
22 KiB
HTML

{% extends "base.html" %}
{% load static i18n %}
{% block title %}{% translate "Modifier le bâtiment" %} {{ building.code }} | StreetUp{% endblock %}
{% block content %}
<div class="container py-4">
<!-- Breadcrumb & Title -->
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-1">
<li class="breadcrumb-item"><a href="{% url 'assets:buildings_index' %}">{% translate "Bâtiments publics" %}</a></li>
<li class="breadcrumb-item"><a href="{% url 'assets:publicbuilding_detail' building.pk %}">{{ building.code }}</a></li>
<li class="breadcrumb-item active" aria-current="page">{% translate "Modifier" %}</li>
</ol>
</nav>
<h1 class="h3 mb-0">
<i class="bi bi-pencil-square text-primary me-2"></i>{% translate "Modifier le bâtiment" %} {{ building.code }}
</h1>
</div>
<div>
<a href="{% url 'assets:publicbuilding_detail' building.pk %}" class="btn btn-outline-secondary">
<i class="bi bi-arrow-left me-1"></i>{% translate "Annuler" %}
</a>
</div>
</div>
<form method="post" action="" class="needs-validation" novalidate>
{% csrf_token %}
{% if form.errors %}
<div class="alert alert-danger alert-dismissible fade show mb-4" role="alert">
<i class="bi bi-exclamation-triangle-fill me-2"></i>
<strong>{% translate "Certains champs contiennent des erreurs." %}</strong> {% translate "Veuillez vérifier les informations ci-dessous." %}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
<!-- Card 1: Identités & Localisation -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-light fw-bold">
<i class="bi bi-info-circle me-2"></i>{% translate "Informations générales" %}
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-4">
<label for="{{ form.code.id_for_label }}" class="form-label fw-semibold">{% translate "Code du bâtiment" %} <span class="text-danger">*</span></label>
{{ form.code }}
{% if form.code.errors %}<div class="text-danger small mt-1">{{ form.code.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-4">
<label for="{{ form.name_fr.id_for_label }}" class="form-label fw-semibold">{% translate "Nom (FR)" %}</label>
{{ form.name_fr }}
{% if form.name_fr.errors %}<div class="text-danger small mt-1">{{ form.name_fr.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-4">
<label for="{{ form.name_nl.id_for_label }}" class="form-label fw-semibold">{% translate "Nom (NL)" %}</label>
{{ form.name_nl }}
{% if form.name_nl.errors %}<div class="text-danger small mt-1">{{ form.name_nl.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-4">
<label for="{{ form.location_type.id_for_label }}" class="form-label fw-semibold">{% translate "Type de localisation" %}</label>
{{ form.location_type }}
{% if form.location_type.errors %}<div class="text-danger small mt-1">{{ form.location_type.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-4">
<label for="{{ form.parent_location.id_for_label }}" class="form-label fw-semibold">{% translate "Localisation parente" %}</label>
{{ form.parent_location }}
{% if form.parent_location.errors %}<div class="text-danger small mt-1">{{ form.parent_location.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-4 d-flex align-items-end mb-2">
<div class="form-check">
{{ form.is_main_location }}
<label for="{{ form.is_main_location.id_for_label }}" class="form-check-label fw-semibold">{% translate "Localisation principale" %}</label>
</div>
</div>
<div class="col-md-12">
<label for="{{ form.address.id_for_label }}" class="form-label fw-semibold">{% translate "Adresse complète" %}</label>
{{ form.address }}
{% if form.address.errors %}<div class="text-danger small mt-1">{{ form.address.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-12">
<label for="{{ form.description.id_for_label }}" class="form-label fw-semibold">{% translate "Description" %}</label>
{{ form.description }}
{% if form.description.errors %}<div class="text-danger small mt-1">{{ form.description.errors.0 }}</div>{% endif %}
</div>
</div>
</div>
</div>
<!-- Card 2: Propriété & Gestion -->
<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é & Gestion" %}
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-4">
<label for="{{ form.owner.id_for_label }}" class="form-label fw-semibold">{% translate "Propriétaire" %}</label>
{{ form.owner }}
{% if form.owner.errors %}<div class="text-danger small mt-1">{{ form.owner.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-4">
<label for="{{ form.manager.id_for_label }}" class="form-label fw-semibold">{% translate "Gestionnaire référent" %}</label>
{{ form.manager }}
{% if form.manager.errors %}<div class="text-danger small mt-1">{{ form.manager.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-4">
<label for="{{ form.occupant_entity.id_for_label }}" class="form-label fw-semibold">{% translate "Direction / Service occupant" %}</label>
{{ form.occupant_entity }}
{% if form.occupant_entity.errors %}<div class="text-danger small mt-1">{{ form.occupant_entity.errors.0 }}</div>{% endif %}
</div>
</div>
</div>
</div>
<!-- Card 3: Usage & Caractéristiques physiques -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-light fw-bold">
<i class="bi bi-building-gear me-2"></i>{% translate "Usage & Caractéristiques physiques" %}
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-6">
<label for="{{ form.usage_type.id_for_label }}" class="form-label fw-semibold">{% translate "Type d'usage principal" %}</label>
{{ form.usage_type }}
{% if form.usage_type.errors %}<div class="text-danger small mt-1">{{ form.usage_type.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-6">
<label for="{{ form.building_status.id_for_label }}" class="form-label fw-semibold">{% translate "Statut d'occupation" %}</label>
{{ form.building_status }}
{% if form.building_status.errors %}<div class="text-danger small mt-1">{{ form.building_status.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-4">
<label for="{{ form.construction_year.id_for_label }}" class="form-label fw-semibold">{% translate "Année de construction" %}</label>
{{ form.construction_year }}
{% if form.construction_year.errors %}<div class="text-danger small mt-1">{{ form.construction_year.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-4">
<label for="{{ form.floors_count.id_for_label }}" class="form-label fw-semibold">{% translate "Nombre d'étages hors-sol" %}</label>
{{ form.floors_count }}
{% if form.floors_count.errors %}<div class="text-danger small mt-1">{{ form.floors_count.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-4">
<label for="{{ form.basement_count.id_for_label }}" class="form-label fw-semibold">{% translate "Nombre de niveaux en sous-sol" %}</label>
{{ form.basement_count }}
{% if form.basement_count.errors %}<div class="text-danger small mt-1">{{ form.basement_count.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-6">
<label for="{{ form.total_surface_m2.id_for_label }}" class="form-label fw-semibold">{% translate "Surface brute totale (m²)" %}</label>
{{ form.total_surface_m2 }}
{% if form.total_surface_m2.errors %}<div class="text-danger small mt-1">{{ form.total_surface_m2.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-6">
<label for="{{ form.usable_surface_m2.id_for_label }}" class="form-label fw-semibold">{% translate "Surface utile (m²)" %}</label>
{{ form.usable_surface_m2 }}
{% if form.usable_surface_m2.errors %}<div class="text-danger small mt-1">{{ form.usable_surface_m2.errors.0 }}</div>{% endif %}
</div>
</div>
</div>
</div>
<!-- Card 4: Réglementation & Accueil du Public (ERP / PMR) -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-light fw-bold">
<i class="bi bi-shield-check me-2"></i>{% translate "Accueil & Sécurité du Public (ERP / PMR)" %}
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-6 d-flex align-items-center">
<div class="form-check mt-3">
{{ form.is_erp }}
<label for="{{ form.is_erp.id_for_label }}" class="form-check-label fw-semibold">
{% translate "Bâtiment recevant du public (ERP / PMR)" %}
</label>
</div>
</div>
<div class="col-md-6 d-flex align-items-center">
<div class="form-check mt-3">
{{ form.pmr_accessible }}
<label for="{{ form.pmr_accessible.id_for_label }}" class="form-check-label fw-semibold">
{% translate "Accessible PMR" %}
</label>
</div>
</div>
<div class="col-md-4">
<label for="{{ form.erp_category.id_for_label }}" class="form-label fw-semibold">{% translate "Catégorie d'accueil / ERP" %}</label>
{{ form.erp_category }}
{% if form.erp_category.errors %}<div class="text-danger small mt-1">{{ form.erp_category.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-4">
<label for="{{ form.erp_type.id_for_label }}" class="form-label fw-semibold">{% translate "Type d'établissement / ERP" %}</label>
{{ form.erp_type }}
{% if form.erp_type.errors %}<div class="text-danger small mt-1">{{ form.erp_type.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-4">
<label for="{{ form.max_capacity.id_for_label }}" class="form-label fw-semibold">{% translate "Jauge maximale d'accueil du public" %}</label>
{{ form.max_capacity }}
{% if form.max_capacity.errors %}<div class="text-danger small mt-1">{{ form.max_capacity.errors.0 }}</div>{% endif %}
</div>
</div>
</div>
</div>
<!-- Card 5: Diagnostics & Énergie -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-light fw-bold">
<i class="bi bi-lightning-charge me-2"></i>{% translate "Diagnostics & Énergie" %}
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-6">
<label for="{{ form.dpe_rating.id_for_label }}" class="form-label fw-semibold">{% translate "Performance Énergétique (PEB / DPE / EPC)" %}</label>
{{ form.dpe_rating }}
{% if form.dpe_rating.errors %}<div class="text-danger small mt-1">{{ form.dpe_rating.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-6">
<label for="{{ form.ges_rating.id_for_label }}" class="form-label fw-semibold">{% translate "Émissions de Carbone & GES" %}</label>
{{ form.ges_rating }}
{% if form.ges_rating.errors %}<div class="text-danger small mt-1">{{ form.ges_rating.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-6 d-flex align-items-center">
<div class="form-check mt-2">
{{ form.has_asbestos_diag }}
<label for="{{ form.has_asbestos_diag.id_for_label }}" class="form-check-label fw-semibold">
{% translate "Diagnostic Amiante réalisé" %}
</label>
</div>
</div>
<div class="col-md-6 d-flex align-items-center">
<div class="form-check mt-2">
{{ form.has_lead_diag }}
<label for="{{ form.has_lead_diag.id_for_label }}" class="form-check-label fw-semibold">
{% translate "Diagnostic Plomb réalisé" %}
</label>
</div>
</div>
</div>
</div>
</div>
<!-- Card 6: Géolocalisation (Carte & Coordonnées) -->
<div class="card shadow-sm mb-4">
<div class="card-header bg-light fw-bold">
<i class="bi bi-geo-alt me-2"></i>{% translate "Coordonnées géographiques (GPS WGS84)" %}
</div>
<div class="card-body">
<p class="text-muted small mb-3">
<i class="bi bi-info-circle me-1"></i>{% translate "Cliquez sur la carte ou déplacez le marqueur pour ajuster la position du bâtiment. Vous pouvez également rechercher une adresse ou basculer en vue orthophoto." %}
</p>
<div id="edit-building-map" style="height: 400px; width: 100%; border-radius: 8px; border: 1px solid #dee2e6;" class="mb-3"></div>
<div class="row g-3">
<div class="col-md-6">
<label for="{{ form.lat.id_for_label }}" class="form-label fw-semibold">{% translate "Latitude" %}</label>
{{ form.lat }}
{% if form.lat.errors %}<div class="text-danger small mt-1">{{ form.lat.errors.0 }}</div>{% endif %}
</div>
<div class="col-md-6">
<label for="{{ form.lon.id_for_label }}" class="form-label fw-semibold">{% translate "Longitude" %}</label>
{{ form.lon }}
{% if form.lon.errors %}<div class="text-danger small mt-1">{{ form.lon.errors.0 }}</div>{% endif %}
</div>
</div>
</div>
</div>
<!-- Form Submit & Cancel -->
<div class="d-flex justify-content-end gap-2 mb-5">
<a href="{% url 'assets:publicbuilding_detail' building.pk %}" class="btn btn-secondary px-4">
<i class="bi bi-x-lg me-1"></i>{% translate "Annuler" %}
</a>
<button type="submit" class="btn btn-primary px-4">
<i class="bi bi-check-circle me-1"></i>{% translate "Enregistrer les modifications" %}
</button>
</div>
</form>
</div>
{% endblock content %}
{% block script %}
<script src="{% static 'common/layer-switcher-control.js' %}"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const latInput = document.getElementById("{{ form.lat.id_for_label }}");
const lonInput = document.getElementById("{{ form.lon.id_for_label }}");
let initLat = parseFloat("{{ center_lat|default_if_none:'' }}") || parseFloat(latInput.value) || 50.8477;
let initLon = parseFloat("{{ center_lon|default_if_none:'' }}") || parseFloat(lonInput.value) || 4.3572;
let hasCoords = Boolean("{{ center_lat|default_if_none:'' }}" || (latInput && latInput.value));
const map = new maplibregl.Map({
container: 'edit-building-map',
style: window.MAP_STYLE_LIGHT || 'https://tiles.openfreemap.org/styles/positron',
center: [initLon, initLat],
zoom: hasCoords ? 17 : 12,
});
// 1. Navigation & Geolocate Controls
map.addControl(new maplibregl.NavigationControl(), 'bottom-right');
map.addControl(new maplibregl.GeolocateControl({ positionOptions: { enableHighAccuracy: true }, trackUserLocation: true }), 'bottom-right');
// 2. Custom Address Search Control
if (typeof AddressSearchControl !== 'undefined') {
const searchCtrl = new AddressSearchControl({
autocompleteUrl: "{% url 'common:autocomplete_street' %}"
});
map.addControl(searchCtrl, 'top-left');
searchCtrl.addEventListener('address-selected', function(e) {
if (e.detail && e.detail.lon && e.detail.lat) {
const lon = parseFloat(e.detail.lon);
const lat = parseFloat(e.detail.lat);
map.flyTo({ center: [lon, lat], zoom: 18 });
setMarkerPosition(lat, lon);
}
});
}
// 3. Orthophotos / Satellite LayerSwitcherControl
if (typeof LayerSwitcherControl !== 'undefined') {
const layerSwitcher = new LayerSwitcherControl({
layers: [
{
id: 'ortho',
name: 'Orthophotos',
icon: 'satellite',
group: 'ortho',
layer: {
type: 'raster',
paint: { 'raster-opacity': 1 }
},
sublayers: [
{
id: 'urbis',
name: window.MAP_ORTHO_WMS_NAME || 'Urbis (Bruxelles)',
default: true,
source: {
type: 'raster',
tiles: [
window.MAP_ORTHO_WMS_URL || 'https://geoservices-urbis.irisnet.be/geoserver/urbisgrid/wms?service=WMS&version=1.3.0&request=GetMap&layers=Ortho&styles=&format=image/png&transparent=true&width=256&height=256&crs=EPSG:3857&bbox={bbox-epsg-3857}'
],
tileSize: 256,
attribution: window.MAP_ORTHO_WMS_ATTRIBUTION || '© CIRB / Urbis'
}
},
{
id: 'vlaanderen',
name: 'Vlaanderen 2023',
source: {
type: 'raster',
tiles: ['https://geo.api.vlaanderen.be/OMW/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=omwrgb23vl&STYLE=&FORMAT=image/png&TILEMATRIXSET=GoogleMapsVL&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}'],
tileSize: 256,
attribution: '© Digitaal Vlaanderen - Orthofoto 2023'
}
}
]
}
]
});
map.addControl(layerSwitcher, 'top-right');
}
// 4. Marker logic & Form synchronization
let marker = null;
if (hasCoords) {
marker = new maplibregl.Marker({ draggable: true, color: '#2B579A' })
.setLngLat([initLon, initLat])
.addTo(map);
marker.on('dragend', function() {
const lngLat = marker.getLngLat();
updateInputValues(lngLat.lat, lngLat.lng);
});
}
map.on('click', function(e) {
setMarkerPosition(e.lngLat.lat, e.lngLat.lng);
});
function setMarkerPosition(lat, lon) {
if (!marker) {
marker = new maplibregl.Marker({ draggable: true, color: '#2B579A' })
.setLngLat([lon, lat])
.addTo(map);
marker.on('dragend', function() {
const lngLat = marker.getLngLat();
updateInputValues(lngLat.lat, lngLat.lng);
});
} else {
marker.setLngLat([lon, lat]);
}
updateInputValues(lat, lon);
}
function updateInputValues(lat, lon) {
if (latInput) latInput.value = parseFloat(lat).toFixed(7);
if (lonInput) lonInput.value = parseFloat(lon).toFixed(7);
}
function syncMarkerFromInputs() {
const lat = parseFloat(latInput.value);
const lon = parseFloat(lonInput.value);
if (!isNaN(lat) && !isNaN(lon)) {
setMarkerPosition(lat, lon);
map.panTo([lon, lat]);
}
}
if (latInput) latInput.addEventListener('change', syncMarkerFromInputs);
if (lonInput) lonInput.addEventListener('change', syncMarkerFromInputs);
});
</script>
{% endblock script %}