6989a98d75
- Arkitektur: docs/auth/passwordless-architecture.md - Backend: iom/quixzoom-auth-service/ (FastAPI + Redis) - Webb: quixzoom-market-pages/se/login/ (QR-kod + polling) - App: iom/quixzoom-app/src/features/auth/ (push + deep links) Flöde: QR-kod → app-godkännande → webb-inloggad
140 lines
4.3 KiB
HTML
140 lines
4.3 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>LIFE Weather Intelligence</title>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif; background: #000; color: #fff; }
|
|
.header { background: #0a0a0a; border-bottom: 1px solid #333; padding: 20px 40px; }
|
|
.header h1 { font-size: 1.5rem; color: #00d4ff; }
|
|
.main { padding: 40px; max-width: 1400px; margin: 0 auto; }
|
|
.metrics { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px; margin-bottom: 32px; }
|
|
.metric { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 20px; }
|
|
.metric-value { font-size: 2.5rem; font-weight: 700; color: #00d4ff; }
|
|
.metric-label { font-size: 0.85rem; color: #888; margin-top: 4px; }
|
|
.section { background: #111; border: 1px solid #333; border-radius: var(--radius-md); padding: 24px; margin-bottom: 24px; }
|
|
.section h2 { font-size: 1.2rem; margin-bottom: 16px; color: #00d4ff; }
|
|
table { width: 100%; border-collapse: collapse; }
|
|
th { text-align: left; padding: 12px; color: #888; font-size: 0.8rem; }
|
|
td { padding: 12px; border-top: 1px solid #222; font-size: 0.9rem; }
|
|
.status { display: inline-block; padding: 4px 8px; border-radius: var(--radius-sm); font-size: 0.75rem; }
|
|
.status-normal { background: #1a3a1a; color: #4ade80; }
|
|
.status-warning { background: #3a3a1a; color: #facc15; }
|
|
.status-critical { background: #3a1a1a; color: #f87171; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="header">
|
|
<h1>LIFE Weather Intelligence</h1>
|
|
</div>
|
|
|
|
<div class="main">
|
|
|
|
<div class="metrics">
|
|
<div class="metric">
|
|
<div class="metric-value" id="total-roads">—</div>
|
|
<div class="metric-label">Roads Monitored</div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-value" id="active-risks">—</div>
|
|
<div class="metric-label">Active Risks</div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-value" id="active-events">—</div>
|
|
<div class="metric-label">Active Events</div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-value" id="missions-waiting">—</div>
|
|
<div class="metric-label">Missions Waiting</div>
|
|
</div>
|
|
<div class="metric">
|
|
<div class="metric-value" id="reality-latency">—</div>
|
|
<div class="metric-label">Reality Latency (min)</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<h2>Latest Weather</h2>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Road</th>
|
|
<th>Temperature</th>
|
|
<th>Precipitation</th>
|
|
<th>Wind</th>
|
|
<th>Risk</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="weather-table">
|
|
<tr><td colspan="5" style="text-align:center;color:#666;">Loading...</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<h2>Reality DNA</h2>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Road ID</th>
|
|
<th>Type</th>
|
|
<th>Observations</th>
|
|
<th>Last Update</th>
|
|
<th>Status</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="dna-table">
|
|
<tr><td colspan="5" style="text-align:center;color:#666;">Loading...</td></tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
// Simulera data för nu
|
|
const roads = [
|
|
{ id: 1, name: "E4", type: "motorway", temp: 22, precip: 3.1, wind: 5.0, risk: "normal" },
|
|
{ id: 2, name: "E6", type: "motorway", temp: 21, precip: 2.5, wind: 4.5, risk: "normal" },
|
|
{ id: 3, name: "E18", type: "motorway", temp: 20, precip: 1.0, wind: 3.0, risk: "normal" },
|
|
];
|
|
|
|
function updateDashboard() {
|
|
document.getElementById('total-roads').textContent = '181';
|
|
document.getElementById('active-risks').textContent = '0';
|
|
document.getElementById('active-events').textContent = '0';
|
|
document.getElementById('missions-waiting').textContent = '0';
|
|
document.getElementById('reality-latency').textContent = '60';
|
|
|
|
const tbody = document.getElementById('weather-table');
|
|
tbody.innerHTML = roads.map(r => `
|
|
<tr>
|
|
<td>${r.name}</td>
|
|
<td>${r.temp}°C</td>
|
|
<td>${r.precip}mm</td>
|
|
<td>${r.wind} m/s</td>
|
|
<td><span class="status status-${r.risk}">${r.risk.toUpperCase()}</span></td>
|
|
</tr>
|
|
`).join('');
|
|
|
|
const dnaBody = document.getElementById('dna-table');
|
|
dnaBody.innerHTML = roads.map(r => `
|
|
<tr>
|
|
<td>${r.id}</td>
|
|
<td>${r.type}</td>
|
|
<td>3</td>
|
|
<td>2026-07-04 15:32</td>
|
|
<td><span class="status status-normal">NORMAL</span></td>
|
|
</tr>
|
|
`).join('');
|
|
}
|
|
|
|
updateDashboard();
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|