deepsite-project-2u02q / live-map.html
MoShow's picture
Initial DeepSite commit
4fc5d1c verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Live Map - Staff Safety Dashboard</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
height: 100vh;
overflow: hidden;
background: #0f172a;
}
/* Neuromorphic Dark Theme Base */
.neu-dark {
background: linear-gradient(145deg, #1e293b, #0f172a);
box-shadow:
8px 8px 16px rgba(0,0,0,0.5),
-8px -8px 16px rgba(255,255,255,0.03),
inset 1px 1px 1px rgba(255,255,255,0.05);
}
.neu-dark-inset {
background: #0f172a;
box-shadow:
inset 4px 4px 8px rgba(0,0,0,0.6),
inset -4px -4px 8px rgba(255,255,255,0.02);
}
.neu-pressed {
box-shadow:
inset 4px 4px 8px rgba(0,0,0,0.5),
inset -4px -4px 8px rgba(255,255,255,0.05);
}
.neu-btn {
background: linear-gradient(145deg, #1e293b, #0f172a);
box-shadow:
4px 4px 8px rgba(0,0,0,0.4),
-4px -4px 8px rgba(255,255,255,0.03);
transition: all 0.2s ease;
}
.neu-btn:active {
box-shadow:
inset 4px 4px 8px rgba(0,0,0,0.4),
inset -4px -4px 8px rgba(255,255,255,0.03);
}
.neu-btn.active {
background: linear-gradient(145deg, #ca8a04, #eab308);
color: #0f172a;
box-shadow:
4px 4px 8px rgba(0,0,0,0.3),
-4px -4px 8px rgba(255,255,255,0.05);
}
/* Yellow Accent Utilities */
.text-accent { color: #eab308; }
.bg-accent { background-color: #eab308; }
.border-accent { border-color: rgba(234, 179, 8, 0.3); }
/* Map Container */
#map {
height: 100%;
width: 100%;
border-radius: 1rem;
z-index: 1;
background: #1e293b;
}
/* Custom Map Tiles Dark Mode */
.leaflet-layer,
.leaflet-control-zoom-in,
.leaflet-control-zoom-out,
.leaflet-control-attribution {
filter: invert(100%) hue-rotate(180deg) brightness(95%) contrast(90%);
}
/* Pulse Animation */
.status-dot {
animation: blink 2s infinite;
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
/* Scrollbar Hidden */
::-webkit-scrollbar { display: none; }
body { -ms-overflow-style: none; scrollbar-width: none; }
</style>
</head>
<body class="bg-slate-950 text-slate-200">
<!-- Sidebar -->
<aside class="fixed left-0 top-0 h-full w-64 neu-dark border-r border-slate-800/50 z-40 flex flex-col">
<div class="p-6 border-b border-slate-800/50">
<div class="flex items-center space-x-3">
<div class="w-10 h-10 neu-dark-inset rounded-lg flex items-center justify-center border border-accent">
<i data-lucide="shield-check" class="text-accent w-6 h-6"></i>
</div>
<div>
<h1 class="font-bold text-lg text-slate-100">SafeTeam</h1>
<p class="text-xs text-slate-500">Safety Management</p>
</div>
</div>
</div>
<nav class="p-4 space-y-2 flex-1">
<a href="index.html" class="flex items-center space-x-3 px-4 py-3 text-slate-400 neu-btn rounded-xl hover:text-accent transition-colors">
<i data-lucide="layout-dashboard" class="w-5 h-5"></i>
<span>Dashboard</span>
</a>
<a href="incidents.html" class="flex items-center space-x-3 px-4 py-3 text-slate-400 neu-btn rounded-xl hover:text-accent transition-colors">
<i data-lucide="alert-triangle" class="w-5 h-5"></i>
<span>Incidents</span>
<span class="ml-auto bg-red-500 text-white text-xs px-2 py-0.5 rounded-full">3</span>
</a>
<a href="training.html" class="flex items-center space-x-3 px-4 py-3 text-slate-400 neu-btn rounded-xl hover:text-accent transition-colors">
<i data-lucide="graduation-cap" class="w-5 h-5"></i>
<span>Training</span>
</a>
<a href="live-map.html" class="flex items-center space-x-3 px-4 py-3 neu-pressed text-accent rounded-xl font-medium border border-accent/30">
<i data-lucide="map" class="w-5 h-5"></i>
<span>Live Map</span>
<span class="ml-auto w-2 h-2 bg-accent rounded-full status-dot"></span>
</a>
<a href="equipment.html" class="flex items-center space-x-3 px-4 py-3 text-slate-400 neu-btn rounded-xl hover:text-accent transition-colors">
<i data-lucide="hard-hat" class="w-5 h-5"></i>
<span>Equipment</span>
</a>
<a href="reports.html" class="flex items-center space-x-3 px-4 py-3 text-slate-400 neu-btn rounded-xl hover:text-accent transition-colors">
<i data-lucide="file-text" class="w-5 h-5"></i>
<span>Reports</span>
</a>
<a href="emergency.html" class="flex items-center space-x-3 px-4 py-3 text-slate-400 neu-btn rounded-xl hover:text-red-400 transition-colors">
<i data-lucide="phone-call" class="w-5 h-5"></i>
<span>Emergency</span>
</a>
</nav>
<div class="p-4 border-t border-slate-800/50">
<div class="flex items-center space-x-3 px-4 py-3 neu-dark-inset rounded-xl border border-slate-700/50">
<img src="http://static.photos/people/100x100/42" alt="User" class="w-10 h-10 rounded-full object-cover border border-slate-600">
<div>
<p class="text-sm font-medium text-slate-200">Safety Manager</p>
<p class="text-xs text-slate-500">Admin</p>
</div>
</div>
</div>
</aside>
<!-- Main Content - Fixed Height -->
<main class="md:ml-64 h-screen flex flex-col">
<!-- Header -->
<header class="h-16 neu-dark border-b border-slate-800/50 flex items-center justify-between px-6 flex-shrink-0">
<div class="flex items-center space-x-4">
<button class="md:hidden p-2 neu-dark-inset rounded-lg text-slate-400 border border-slate-700">
<i data-lucide="menu" class="w-6 h-6"></i>
</button>
<div>
<h2 class="text-xl font-bold text-slate-100">Live Transit Tracking</h2>
<p class="text-xs text-slate-500" id="current-time">Real-time monitoring active</p>
</div>
</div>
<div class="flex items-center space-x-3">
<button class="neu-btn active px-4 py-2 rounded-lg font-semibold text-sm flex items-center space-x-2">
<i data-lucide="activity" class="w-4 h-4"></i>
<span>LIVE</span>
</button>
<div class="w-px h-8 bg-slate-700/50"></div>
<button class="p-2 neu-btn rounded-lg text-slate-400 hover:text-accent">
<i data-lucide="maximize" class="w-5 h-5"></i>
</button>
</div>
</header>
<!-- Map Viewport - No overflow -->
<div class="flex-1 p-4 relative">
<!-- Map Container -->
<div class="h-full w-full neu-dark-inset rounded-2xl border border-slate-700/50 p-2 relative">
<div id="map" class="rounded-xl border border-slate-600/30"></div>
<!-- Current Status Overlay -->
<div class="absolute top-4 left-4 z-[400] flex space-x-2">
<div class="neu-dark px-3 py-1.5 rounded-lg border border-accent/20 flex items-center space-x-2">
<div class="w-2 h-2 bg-accent rounded-full status-dot"></div>
<span class="text-xs font-medium text-slate-300">24 Staff Active</span>
</div>
<div class="neu-dark px-3 py-1.5 rounded-lg border border-green-500/20 flex items-center space-x-2">
<div class="w-2 h-2 bg-green-500 rounded-full status-dot"></div>
<span class="text-xs font-medium text-slate-300">6 Security Units</span>
</div>
</div>
<!-- Map Controls -->
<div class="absolute top-4 right-4 z-[400] flex flex-col space-y-2">
<button onclick="map.zoomIn()" class="w-10 h-10 neu-dark rounded-lg flex items-center justify-center text-slate-400 hover:text-accent border border-slate-700/50">
<i data-lucide="plus" class="w-5 h-5"></i>
</button>
<button onclick="map.zoomOut()" class="w-10 h-10 neu-dark rounded-lg flex items-center justify-center text-slate-400 hover:text-accent border border-slate-700/50">
<i data-lucide="minus" class="w-5 h-5"></i>
</button>
<button onclick="centerMap()" class="w-10 h-10 neu-dark rounded-lg flex items-center justify-center text-slate-400 hover:text-accent border border-slate-700/50">
<i data-lucide="crosshair" class="w-5 h-5"></i>
</button>
</div>
<!-- Single Active Card - Bottom Left -->
<div class="absolute bottom-4 left-4 z-[400] w-80">
<div class="neu-dark rounded-xl border border-accent/20 overflow-hidden">
<div class="p-4 border-b border-slate-700/30 flex items-center justify-between bg-slate-800/30">
<div class="flex items-center space-x-2">
<i data-lucide="navigation" class="w-4 h-4 text-accent"></i>
<span class="font-semibold text-slate-200">Active Transit</span>
</div>
<span class="text-xs font-mono text-accent">T-04</span>
</div>
<div class="p-4 space-y-4">
<div class="flex items-start space-x-3">
<div class="w-10 h-10 neu-dark-inset rounded-full flex items-center justify-center border border-slate-600 flex-shrink-0">
<span class="text-accent font-bold text-sm">LP</span>
</div>
<div class="flex-1">
<h3 class="font-bold text-slate-100">Lisa Park</h3>
<p class="text-xs text-slate-500">Site Supervisor</p>
<div class="mt-2 flex items-center space-x-2">
<span class="px-2 py-0.5 bg-green-500/10 text-green-400 text-[10px] rounded border border-green-500/20">On Track</span>
</div>
</div>
</div>
<div class="grid grid-cols-2 gap-3">
<div class="neu-dark-inset rounded-lg p-2 border border-slate-700/30">
<p class="text-[10px] text-slate-500 uppercase tracking-wider mb-1">Destination</p>
<p class="text-sm font-medium text-slate-300 truncate">Office Tower</p>
</div>
<div class="neu-dark-inset rounded-lg p-2 border border-slate-700/30">
<p class="text-[10px] text-slate-500 uppercase tracking-wider mb-1">ETA</p>
<p class="text-sm font-medium text-accent">3 min</p>
</div>
</div>
<div class="h-1.5 bg-slate-800 rounded-full overflow-hidden">
<div class="h-full bg-gradient-to-r from-accent to-yellow-600 rounded-full" style="width: 75%"></div>
</div>
<div class="flex justify-between text-[10px] text-slate-500">
<span>Distance: 0.4 mi</span>
<span>75% complete</span>
</div>
</div>
<div class="p-3 border-t border-slate-700/30 bg-slate-800/20 flex space-x-2">
<button class="flex-1 neu-btn py-2 rounded-lg text-xs font-medium text-slate-300 hover:text-accent flex items-center justify-center space-x-1">
<i data-lucide="phone" class="w-3 h-3"></i>
<span>Contact</span>
</button>
<button class="flex-1 neu-btn py-2 rounded-lg text-xs font-medium text-slate-300 hover:text-accent flex items-center justify-center space-x-1">
<i data-lucide="shield" class="w-3 h-3"></i>
<span>Escort</span>
</button>
</div>
</div>
</div>
<!-- Security Coverage Toggle - Bottom Right -->
<div class="absolute bottom-4 right-4 z-[400]">
<button onclick="toggleCoverage()" id="coverage-btn" class="neu-dark px-4 py-3 rounded-xl border border-slate-700/50 text-slate-300 hover:text-accent flex items-center space-x-2">
<i data-lucide="shield" class="w-5 h-5"></i>
<span class="text-sm font-medium">Coverage Zones</span>
</button>
</div>
</div>
</div>
</main>
<script>
lucide.createIcons();
// Update Time
function updateTime() {
const now = new Date();
document.getElementById('current-time').textContent = now.toLocaleTimeString('en-US', {
hour12: false,
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
}
setInterval(updateTime, 1000);
updateTime();
// Initialize Map
const map = L.map('map', {
zoomControl: false,
attributionControl: false
}).setView([40.7128, -74.0060], 15);
L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {
maxZoom: 19
}).addTo(map);
// Custom Yellow Icon
const yellowIcon = L.divIcon({
className: 'custom-div-icon',
html: `<div style="background-color: #eab308; width: 14px; height: 14px; border-radius: 50%; border: 3px solid #0f172a; box-shadow: 0 0 0 2px rgba(234, 179, 8, 0.4);"></div>`,
iconSize: [14, 14],
iconAnchor: [7, 7]
});
const greenIcon = L.divIcon({
className: 'custom-div-icon',
html: `<div style="background-color: #10b981; width: 12px; height: 12px; border-radius: 50%; border: 2px solid #0f172a;"></div>`,
iconSize: [12, 12],
iconAnchor: [6, 6]
});
// Active Staff Marker (Lisa Park)
const activeMarker = L.marker([40.7128, -74.006], { icon: yellowIcon }).addTo(map)
.bindPopup(`
<div style="background: #1e293b; color: #e2e8f0; padding: 8px; border-radius: 8px; min-width: 150px; border: 1px solid rgba(234, 179, 8, 0.3);">
<div style="color: #eab308; font-weight: bold; margin-bottom: 4px;">Lisa Park</div>
<div style="font-size: 12px; color: #64748b;">Site Supervisor</div>
<div style="font-size: 11px; color: #10b981; margin-top: 4px;">● On Track</div>
</div>
`, { closeButton: false });
activeMarker.openPopup();
// Other Staff Markers
const otherLocations = [
[40.714, -74.008], [40.711, -74.004], [40.715, -74.010], [40.710, -74.002]
];
otherLocations.forEach((loc, i) => {
L.marker(loc, { icon: greenIcon }).addTo(map)
.bindPopup(`<div style="color: #94a3b8; font-size: 12px;">Unit ${i + 1}</div>`, {
closeButton: false,
className: 'bg-slate-800 text-slate-200'
});
});
// Security Circles (Coverage)
const securityZones = [];
const coverageBtn = document.getElementById('coverage-btn');
let coverageEnabled = false;
function toggleCoverage() {
coverageEnabled = !coverageEnabled;
if (coverageEnabled) {
coverageBtn.classList.add('border-accent', 'text-accent');
securityZones.push(L.circle([40.714, -74.008], {
color: '#eab308',
fillColor: '#eab308',
fillOpacity: 0.05,
radius: 500,
weight: 1,
dashArray: '4, 4'
}).addTo(map));
} else {
coverageBtn.classList.remove('border-accent', 'text-accent');
securityZones.forEach(z => map.removeLayer(z));
securityZones.length = 0;
}
}
function centerMap() {
map.setView([40.7128, -74.006], 15);
}
// Mobile Menu
document.querySelector('.md\\\\:hidden').addEventListener('click', () => {
document.querySelector('aside').classList.toggle('-translate-x-full');
});
</script>
</body>
</html>