bmc_hub/app/modules/locations/templates/map.html

190 lines
7.8 KiB
HTML
Raw Normal View History

{% extends "shared/frontend/base.html" %}
{% block title %}Lokaliteter kort - BMC Hub{% endblock %}
{% block content %}
<div class="container-fluid px-4 py-4">
<!-- Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/" class="text-decoration-none">Hjem</a></li>
<li class="breadcrumb-item"><a href="/app/locations" class="text-decoration-none">Lokaliteter</a></li>
<li class="breadcrumb-item active">Kort</li>
</ol>
</nav>
<!-- Header Section -->
<div class="row mb-4">
<div class="col-12">
<h1 class="h2 fw-700 mb-2">Lokaliteter kort</h1>
<p class="text-muted small">Interaktivt kort over alle lokationer</p>
</div>
</div>
<!-- Filter Section -->
<div class="row mb-3">
<div class="col-md-6">
<div class="d-flex gap-2">
<select class="form-select form-select-sm" id="typeFilter" style="max-width: 200px;">
<option value="">Alle typer</option>
{% if location_types %}
{% for type_option in location_types %}
{% set option_value = type_option['value'] if type_option is mapping else type_option %}
{% set option_label = type_option['label'] if type_option is mapping else type_option %}
<option value="{{ option_value }}">
{% if option_value == 'kompleks' %}Kompleks{% elif option_value == 'bygning' %}Bygning{% elif option_value == 'etage' %}Etage{% elif option_value == 'customer_site' %}Kundesite{% elif option_value == 'rum' %}Rum{% elif option_value == 'vehicle' %}Køretøj{% else %}{{ option_label }}{% endif %}
</option>
{% endfor %}
{% endif %}
</select>
<button type="button" class="btn btn-primary btn-sm" id="filterBtn">
<i class="bi bi-funnel me-2"></i>Anvend
</button>
<a href="/app/locations" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-list me-2"></i>Listevisning
</a>
</div>
</div>
</div>
<!-- Map Container -->
<div class="card border-0" style="height: 600px; position: relative;">
<div id="map" style="width: 100%; height: 100%; border-radius: 12px;"></div>
</div>
<!-- Location Count -->
<div class="mt-3 text-muted small text-center">
<span id="locationCount">{{ locations | length }}</span> lokation(er) på kort
</div>
</div>
<!-- Leaflet CSS & JS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.min.css" />
<script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet.markercluster@1.5.1/dist/MarkerCluster.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet.markercluster@1.5.1/dist/MarkerCluster.Default.css" />
<script src="https://cdn.jsdelivr.net/npm/leaflet.markercluster@1.5.1/dist/leaflet.markercluster.js"></script>
{% endblock %}
{% block scripts %}
<script>
const locationsData = {{ locations | tojson }};
const locationTypes = {{ location_types | tojson }};
document.addEventListener('DOMContentLoaded', function() {
// Initialize map
const map = L.map('map').setView([55.7, 12.6], 6);
// Add tile layer (with dark mode support)
const isDarkMode = document.documentElement.getAttribute('data-bs-theme') === 'dark';
const tileLayer = isDarkMode
? 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
L.tileLayer(tileLayer, {
attribution: '© OpenStreetMap contributors',
maxZoom: 19,
filter: isDarkMode ? 'invert(1) hue-rotate(180deg)' : 'none'
}).addTo(map);
// Color mapping for location types
const typeColors = {
'kompleks': '#0f4c75',
'bygning': '#1abc9c',
'etage': '#3498db',
'customer_site': '#9b59b6',
'rum': '#e67e22',
'vehicle': '#8e44ad'
};
// Create marker cluster group
const markerClusterGroup = L.markerClusterGroup();
// Function to create icon
function createIcon(type) {
const color = typeColors[type] || '#6c757d';
return L.icon({
iconUrl: `https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-${getColorName(color)}.png`,
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/images/marker-shadow.png',
iconSize: [25, 41],
iconAnchor: [12, 41],
popupAnchor: [1, -34],
shadowSize: [41, 41]
});
}
function getColorName(hex) {
const colorMap = {
'#0f4c75': 'blue',
'#f39c12': 'orange',
'#2eb341': 'green',
'#9b59b6': 'violet',
'#8e44ad': 'violet'
};
return colorMap[hex] || 'blue';
}
// Add markers
function addMarkers(filter = null) {
markerClusterGroup.clearLayers();
let addedCount = 0;
locationsData.forEach(location => {
if (filter && location.location_type !== filter) return;
if (location.latitude && location.longitude) {
const marker = L.marker([location.latitude, location.longitude], {
icon: createIcon(location.location_type)
});
const typeLabel = {
'kompleks': 'Kompleks',
'bygning': 'Bygning',
'etage': 'Etage',
'customer_site': 'Kundesite',
'rum': 'Rum',
'vehicle': 'Køretøj'
}[location.location_type] || location.location_type;
const popup = `
<div class="p-2" style="min-width: 250px;">
<h6 class="fw-600 mb-2"><a href="/app/locations/${location.id}" class="text-decoration-none">${location.name}</a></h6>
<p class="small mb-2">
<span class="badge" style="background-color: ${typeColors[location.location_type] || '#6c757d'}; color: white;">${typeLabel}</span>
</p>
<p class="small mb-1"><i class="bi bi-geo-alt me-2"></i>${location.address_city || '—'}</p>
${location.phone ? `<p class="small mb-1"><i class="bi bi-telephone me-2"></i><a href="tel:${location.phone}" class="text-decoration-none">${location.phone}</a></p>` : ''}
${location.email ? `<p class="small mb-2"><i class="bi bi-envelope me-2"></i><a href="mailto:${location.email}" class="text-decoration-none">${location.email}</a></p>` : ''}
<a href="/app/locations/${location.id}" class="btn btn-sm btn-primary mt-2 w-100">Se detaljer</a>
</div>
`;
marker.bindPopup(popup);
markerClusterGroup.addLayer(marker);
addedCount++;
}
});
map.addLayer(markerClusterGroup);
document.getElementById('locationCount').textContent = addedCount;
}
// Initial load
addMarkers();
// Filter button
document.getElementById('filterBtn').addEventListener('click', function() {
const selectedType = document.getElementById('typeFilter').value;
addMarkers(selectedType || null);
});
// Filter on enter
document.getElementById('typeFilter').addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
document.getElementById('filterBtn').click();
}
});
});
</script>
{% endblock %}