aee0f09db8
- Datafabrik: Dockerfile fix, agentorkestrering fungerar - Vision: Identify-modell, FAISS, OCR alla testade - API: Alla 7 integrationstester passerade - Upplösare: Entitetsupplösning verifierad
1303 lines
44 KiB
HTML
1303 lines
44 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="sv">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||
<meta name="color-scheme" content="light dark">
|
||
<title>LandveX Admin — Infrastrukturportal</title>
|
||
<link rel="stylesheet" >
|
||
<style>
|
||
/* ═══════════════════════════════════════════
|
||
LANDVEX ADMIN PORTAL — iOS Native
|
||
Wavult Group · 2026
|
||
═══════════════════════════════════════════ */
|
||
|
||
* { box-sizing: border-box; margin: 0; padding: 0; -webkit-font-smoothing: antialiased; }
|
||
|
||
body {
|
||
font-family: var(--font-text);
|
||
background: var(--background-secondary);
|
||
color: var(--label-primary);
|
||
min-height: 100vh;
|
||
}
|
||
|
||
/* ── NAVIGATION BAR ── */
|
||
.nav-bar {
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 100;
|
||
height: 44px;
|
||
background: var(--glass-bg);
|
||
backdrop-filter: var(--glass-blur);
|
||
-webkit-backdrop-filter: var(--glass-blur);
|
||
border-bottom: 0.5px solid var(--separator);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 16px;
|
||
safe-area-inset-top: env(safe-area-inset-top);
|
||
}
|
||
|
||
.nav-title {
|
||
font-family: var(--font-display);
|
||
font-size: 17px;
|
||
font-weight: 600;
|
||
letter-spacing: -0.4px;
|
||
color: var(--label-primary);
|
||
}
|
||
|
||
.nav-logo {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.nav-logo .logo-icon {
|
||
width: 28px;
|
||
height: 28px;
|
||
background: linear-gradient(135deg, #007AFF 0%, #0055CC 100%);
|
||
border-radius: 7px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 14px;
|
||
color: white;
|
||
font-weight: 700;
|
||
letter-spacing: -0.5px;
|
||
}
|
||
|
||
.badge-admin {
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
padding: 3px 8px;
|
||
background: rgba(255, 149, 0, 0.15);
|
||
color: var(--ios-orange);
|
||
border-radius: 20px;
|
||
letter-spacing: 0.3px;
|
||
}
|
||
|
||
/* ── CONTENT ── */
|
||
.content {
|
||
padding: 0 16px 120px;
|
||
max-width: 1100px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
/* ── SECTION HEADER ── */
|
||
.section-header {
|
||
padding: 20px 0 8px;
|
||
font-family: var(--font-display);
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
letter-spacing: -0.4px;
|
||
color: var(--label-primary);
|
||
}
|
||
|
||
.section-header-large {
|
||
padding: 28px 0 4px;
|
||
font-family: var(--font-display);
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
letter-spacing: -0.5px;
|
||
}
|
||
|
||
.section-sub {
|
||
font-size: 15px;
|
||
color: var(--label-secondary);
|
||
padding-bottom: 12px;
|
||
}
|
||
|
||
/* ── METRICS GRID ── */
|
||
.metrics-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||
gap: 12px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.metric-card {
|
||
background: var(--background-primary);
|
||
border-radius: 16px;
|
||
padding: 16px;
|
||
border: 0.5px solid var(--separator);
|
||
}
|
||
|
||
.metric-value {
|
||
font-family: var(--font-display);
|
||
font-size: 32px;
|
||
font-weight: 700;
|
||
letter-spacing: -1px;
|
||
line-height: 1;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.metric-label {
|
||
font-size: 13px;
|
||
color: var(--label-secondary);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.metric-trend {
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
margin-top: 6px;
|
||
}
|
||
|
||
.trend-up { color: var(--ios-green); }
|
||
.trend-down { color: var(--ios-red); }
|
||
.trend-flat { color: var(--label-tertiary); }
|
||
|
||
.metric-blue .metric-value { color: var(--ios-blue); }
|
||
.metric-green .metric-value { color: var(--ios-green); }
|
||
.metric-orange .metric-value { color: var(--ios-orange); }
|
||
.metric-red .metric-value { color: var(--ios-red); }
|
||
|
||
/* ── TABLE ── */
|
||
.table-card {
|
||
background: var(--background-primary);
|
||
border-radius: 16px;
|
||
border: 0.5px solid var(--separator);
|
||
overflow: hidden;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.table-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 14px 16px;
|
||
border-bottom: 0.5px solid var(--separator);
|
||
}
|
||
|
||
.table-title {
|
||
font-size: 17px;
|
||
font-weight: 600;
|
||
letter-spacing: -0.3px;
|
||
}
|
||
|
||
.table-action {
|
||
font-size: 15px;
|
||
color: var(--ios-blue);
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
background: none;
|
||
border: none;
|
||
padding: 0;
|
||
}
|
||
|
||
.table-scroll {
|
||
overflow-x: auto;
|
||
}
|
||
|
||
table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
font-size: 14px;
|
||
}
|
||
|
||
thead th {
|
||
text-align: left;
|
||
padding: 8px 16px;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
color: var(--label-secondary);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
background: var(--background-secondary);
|
||
border-bottom: 0.5px solid var(--separator);
|
||
}
|
||
|
||
tbody tr {
|
||
border-bottom: 0.5px solid var(--separator);
|
||
transition: background 0.15s;
|
||
cursor: pointer;
|
||
}
|
||
|
||
tbody tr:last-child { border-bottom: none; }
|
||
|
||
tbody tr:hover { background: var(--fill-quaternary); }
|
||
|
||
tbody td {
|
||
padding: 12px 16px;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.td-name {
|
||
font-weight: 600;
|
||
letter-spacing: -0.2px;
|
||
}
|
||
|
||
.td-sub {
|
||
font-size: 12px;
|
||
color: var(--label-secondary);
|
||
margin-top: 2px;
|
||
}
|
||
|
||
/* ── STATUS BADGES ── */
|
||
.status-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
padding: 4px 10px;
|
||
border-radius: 20px;
|
||
}
|
||
|
||
.status-ok {
|
||
background: rgba(52, 199, 89, 0.15);
|
||
color: var(--ios-green);
|
||
}
|
||
|
||
.status-warn {
|
||
background: rgba(255, 149, 0, 0.15);
|
||
color: var(--ios-orange);
|
||
}
|
||
|
||
.status-critical {
|
||
background: rgba(255, 59, 48, 0.15);
|
||
color: var(--ios-red);
|
||
}
|
||
|
||
.status-pending {
|
||
background: rgba(142, 142, 147, 0.15);
|
||
color: var(--ios-gray);
|
||
}
|
||
|
||
.status-dot {
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 50%;
|
||
background: currentColor;
|
||
}
|
||
|
||
/* ── RISK SCORE ── */
|
||
.risk-bar-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.risk-bar {
|
||
flex: 1;
|
||
height: 4px;
|
||
background: var(--fill-tertiary);
|
||
border-radius: 2px;
|
||
overflow: hidden;
|
||
min-width: 60px;
|
||
}
|
||
|
||
.risk-fill {
|
||
height: 100%;
|
||
border-radius: 2px;
|
||
transition: width 0.3s;
|
||
}
|
||
|
||
.risk-low .risk-fill { background: var(--ios-green); }
|
||
.risk-medium .risk-fill { background: var(--ios-orange); }
|
||
.risk-high .risk-fill { background: var(--ios-red); }
|
||
|
||
.risk-label {
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
min-width: 28px;
|
||
text-align: right;
|
||
}
|
||
|
||
/* ── MAP PLACEHOLDER ── */
|
||
.map-card {
|
||
background: var(--background-primary);
|
||
border-radius: 16px;
|
||
border: 0.5px solid var(--separator);
|
||
overflow: hidden;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.map-container {
|
||
height: 320px;
|
||
background: #E8EDF2;
|
||
position: relative;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
@media (prefers-color-scheme: dark) {
|
||
.map-container { background: #1C2328; }
|
||
}
|
||
|
||
.map-overlay {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
color: var(--label-secondary);
|
||
}
|
||
|
||
.map-grid {
|
||
position: absolute;
|
||
inset: 0;
|
||
opacity: 0.3;
|
||
background-image:
|
||
linear-gradient(rgba(0,122,255,0.3) 1px, transparent 1px),
|
||
linear-gradient(90deg, rgba(0,122,255,0.3) 1px, transparent 1px);
|
||
background-size: 40px 40px;
|
||
}
|
||
|
||
.map-pin {
|
||
position: absolute;
|
||
width: 28px;
|
||
height: 28px;
|
||
border-radius: 50% 50% 50% 0;
|
||
transform: rotate(-45deg);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.25);
|
||
transition: transform 0.2s;
|
||
}
|
||
|
||
.map-pin:hover { transform: rotate(-45deg) scale(1.2); }
|
||
.map-pin-ok { background: var(--ios-green); }
|
||
.map-pin-warn { background: var(--ios-orange); }
|
||
.map-pin-critical { background: var(--ios-red); }
|
||
|
||
.map-pin-inner {
|
||
transform: rotate(45deg);
|
||
font-size: 11px;
|
||
color: white;
|
||
}
|
||
|
||
.map-legend {
|
||
position: absolute;
|
||
bottom: 12px;
|
||
right: 12px;
|
||
background: var(--glass-bg);
|
||
backdrop-filter: var(--glass-blur);
|
||
-webkit-backdrop-filter: var(--glass-blur);
|
||
border-radius: 12px;
|
||
padding: 8px 12px;
|
||
border: 0.5px solid var(--separator);
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
.legend-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
font-size: 11px;
|
||
color: var(--label-secondary);
|
||
}
|
||
|
||
.legend-dot {
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
/* ── FAB BUTTON ── */
|
||
.fab {
|
||
position: fixed;
|
||
bottom: calc(24px + env(safe-area-inset-bottom));
|
||
right: 20px;
|
||
z-index: 200;
|
||
background: var(--ios-blue);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 28px;
|
||
height: 56px;
|
||
padding: 0 20px;
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
font-family: var(--font-text);
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
box-shadow: 0 4px 20px rgba(0, 122, 255, 0.4);
|
||
transition: transform 0.15s, box-shadow 0.15s;
|
||
}
|
||
|
||
.fab:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 8px 28px rgba(0, 122, 255, 0.5);
|
||
}
|
||
|
||
.fab:active {
|
||
transform: translateY(0) scale(0.97);
|
||
box-shadow: 0 2px 12px rgba(0, 122, 255, 0.35);
|
||
}
|
||
|
||
/* ── INSPECTION CELLS ── */
|
||
.inspection-list {
|
||
background: var(--background-primary);
|
||
border-radius: 16px;
|
||
border: 0.5px solid var(--separator);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.inspection-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 14px 16px;
|
||
border-bottom: 0.5px solid var(--separator);
|
||
gap: 12px;
|
||
cursor: pointer;
|
||
transition: background 0.15s;
|
||
}
|
||
|
||
.inspection-item:last-child { border-bottom: none; }
|
||
.inspection-item:hover { background: var(--fill-quaternary); }
|
||
|
||
.inspection-icon {
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 10px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 18px;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.inspection-body { flex: 1; min-width: 0; }
|
||
|
||
.inspection-title {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
letter-spacing: -0.2px;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.inspection-meta {
|
||
font-size: 13px;
|
||
color: var(--label-secondary);
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.inspection-chevron {
|
||
font-size: 17px;
|
||
color: var(--label-tertiary);
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
/* ── MODAL ── */
|
||
.modal-overlay {
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(242,242,247,0.4);
|
||
backdrop-filter: blur(4px);
|
||
z-index: 300;
|
||
display: flex;
|
||
align-items: flex-end;
|
||
justify-content: center;
|
||
opacity: 0;
|
||
pointer-events: none;
|
||
transition: opacity 0.25s;
|
||
}
|
||
|
||
.modal-overlay.open {
|
||
opacity: 1;
|
||
pointer-events: all;
|
||
}
|
||
|
||
.modal-sheet {
|
||
background: var(--background-primary);
|
||
border-radius: 20px 20px 0 0;
|
||
width: 100%;
|
||
max-width: 640px;
|
||
padding: 8px 0 env(safe-area-inset-bottom);
|
||
transform: translateY(100%);
|
||
transition: transform 0.3s cubic-bezier(0.32, 0.72, 0, 1);
|
||
}
|
||
|
||
.modal-overlay.open .modal-sheet {
|
||
transform: translateY(0);
|
||
}
|
||
|
||
.modal-handle {
|
||
width: 36px;
|
||
height: 4px;
|
||
background: var(--fill-primary);
|
||
border-radius: 2px;
|
||
margin: 8px auto 16px;
|
||
}
|
||
|
||
.modal-title {
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
letter-spacing: -0.4px;
|
||
padding: 0 20px 16px;
|
||
border-bottom: 0.5px solid var(--separator);
|
||
}
|
||
|
||
.form-group {
|
||
padding: 16px 20px;
|
||
border-bottom: 0.5px solid var(--separator);
|
||
}
|
||
|
||
.form-label {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: var(--label-secondary);
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.form-input {
|
||
width: 100%;
|
||
padding: 10px 12px;
|
||
background: var(--background-secondary);
|
||
border: 0.5px solid var(--separator);
|
||
border-radius: 10px;
|
||
font-size: 15px;
|
||
font-family: var(--font-text);
|
||
color: var(--label-primary);
|
||
outline: none;
|
||
transition: border-color 0.2s;
|
||
}
|
||
|
||
.form-input:focus {
|
||
border-color: var(--ios-blue);
|
||
}
|
||
|
||
.form-select {
|
||
width: 100%;
|
||
padding: 10px 12px;
|
||
background: var(--background-secondary);
|
||
border: 0.5px solid var(--separator);
|
||
border-radius: 10px;
|
||
font-size: 15px;
|
||
font-family: var(--font-text);
|
||
color: var(--label-primary);
|
||
outline: none;
|
||
appearance: none;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.modal-actions {
|
||
padding: 16px 20px;
|
||
display: flex;
|
||
gap: 10px;
|
||
}
|
||
|
||
.btn-primary {
|
||
flex: 1;
|
||
height: 50px;
|
||
background: var(--ios-blue);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 14px;
|
||
font-size: 17px;
|
||
font-weight: 600;
|
||
font-family: var(--font-text);
|
||
cursor: pointer;
|
||
transition: opacity 0.15s;
|
||
}
|
||
|
||
.btn-primary:active { opacity: 0.7; }
|
||
|
||
.btn-secondary {
|
||
flex: 1;
|
||
height: 50px;
|
||
background: var(--fill-secondary);
|
||
color: var(--label-primary);
|
||
border: none;
|
||
border-radius: 14px;
|
||
font-size: 17px;
|
||
font-weight: 600;
|
||
font-family: var(--font-text);
|
||
cursor: pointer;
|
||
transition: opacity 0.15s;
|
||
}
|
||
|
||
.btn-secondary:active { opacity: 0.7; }
|
||
|
||
/* ── TAB BAR ── */
|
||
.tab-bar {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: calc(49px + env(safe-area-inset-bottom));
|
||
padding-bottom: env(safe-area-inset-bottom);
|
||
background: var(--glass-bg);
|
||
backdrop-filter: var(--glass-blur);
|
||
-webkit-backdrop-filter: var(--glass-blur);
|
||
border-top: 0.5px solid var(--separator);
|
||
display: flex;
|
||
align-items: flex-start;
|
||
justify-content: space-around;
|
||
padding-top: 8px;
|
||
z-index: 90;
|
||
}
|
||
|
||
.tab-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 2px;
|
||
cursor: pointer;
|
||
flex: 1;
|
||
padding: 0 8px;
|
||
opacity: 0.6;
|
||
transition: opacity 0.15s;
|
||
}
|
||
|
||
.tab-item.active { opacity: 1; }
|
||
|
||
.tab-icon { font-size: 22px; }
|
||
.tab-label { font-size: 10px; font-weight: 500; }
|
||
.tab-item.active .tab-label { color: var(--ios-blue); }
|
||
.tab-item.active .tab-icon { filter: none; }
|
||
|
||
/* ── DARK MODE ── */
|
||
@media (prefers-color-scheme: dark) {
|
||
.map-container { background: #1A1E23; }
|
||
}
|
||
|
||
/* ── RESPONSIVE ── */
|
||
@media (min-width: 768px) {
|
||
.metrics-grid { grid-template-columns: repeat(4, 1fr); }
|
||
.content { padding: 0 24px 80px; }
|
||
}
|
||
@media (max-width: 640px) {
|
||
.hero h1 { font-size: 2rem !important; }
|
||
.hero p { font-size: 1rem !important; }
|
||
.card { padding: 1.5rem !important; }
|
||
.grid { grid-template-columns: 1fr !important; }
|
||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||
}
|
||
</style>
|
||
|
||
<style id="wcag-contrast-enforcement">
|
||
/* AAMOS GLOBAL CONTRAST ENFORCEMENT — WCAG AA (4.5:1 minimum) */
|
||
/* Regel: Ingen text under 70% opacity. Aldrig. */
|
||
body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
|
||
/* Fix common low-contrast patterns */
|
||
[style*="rgba(60,60,67,0.4)"] { color: rgba(60,60,67,0.65) !important; }
|
||
[style*="rgba(60,60,67,0.3)"] { color: rgba(60,60,67,0.65) !important; }
|
||
[style*="rgba(0,0,0,0.4)"] { color: rgba(0,0,0,0.72) !important; }
|
||
[style*="rgba(0,0,0,0.3)"] { color: rgba(0,0,0,0.72) !important; }
|
||
[style*="opacity: 0.4"] { opacity: 0.72 !important; }
|
||
[style*="opacity: 0.3"] { opacity: 0.72 !important; }
|
||
@media (max-width: 640px) {
|
||
.hero h1 { font-size: 2rem !important; }
|
||
.hero p { font-size: 1rem !important; }
|
||
.card { padding: 1.5rem !important; }
|
||
.grid { grid-template-columns: 1fr !important; }
|
||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<!-- NAV BAR -->
|
||
<nav class="nav-bar">
|
||
<div class="nav-logo">
|
||
<div class="logo-icon">LX</div>
|
||
<span class="nav-title">LandveX Admin</span>
|
||
</div>
|
||
<div style="display:flex;align-items:center;gap:8px;">
|
||
<span class="badge-admin">ADMIN</span>
|
||
<div style="width:32px;height:32px;border-radius:50%;background:linear-gradient(135deg,#007AFF,#0055CC);display:flex;align-items:center;justify-content:center;color:white;font-size:14px;font-weight:700;">A</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<!-- MAIN CONTENT -->
|
||
<main class="content">
|
||
|
||
<!-- HEADER -->
|
||
<div class="section-header-large">Infrastrukturportal</div>
|
||
<div class="section-sub">LandveX · Administration · 18 maj 2026</div>
|
||
|
||
<!-- METRICS -->
|
||
<div class="section-header">Systemöversikt</div>
|
||
<div class="metrics-grid">
|
||
<div class="metric-card metric-blue">
|
||
<div class="metric-value">247</div>
|
||
<div class="metric-label">Registrerade objekt</div>
|
||
<div class="metric-trend trend-up">↑ 12 denna vecka</div>
|
||
</div>
|
||
<div class="metric-card metric-green">
|
||
<div class="metric-value">189</div>
|
||
<div class="metric-label">OK-status</div>
|
||
<div class="metric-trend trend-up">↑ 76%</div>
|
||
</div>
|
||
<div class="metric-card metric-orange">
|
||
<div class="metric-value">41</div>
|
||
<div class="metric-label">Kräver åtgärd</div>
|
||
<div class="metric-trend trend-flat">↔ Stabil</div>
|
||
</div>
|
||
<div class="metric-card metric-red">
|
||
<div class="metric-value">17</div>
|
||
<div class="metric-label">Kritiska</div>
|
||
<div class="metric-trend trend-down">↑ 3 sedan igår</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MAP -->
|
||
<div class="section-header">Live Operationell Karta</div>
|
||
|
||
<!-- LIVE MAP WITH ZOOMER POSITIONS -->
|
||
<div class="map-card" style="margin-bottom:12px;">
|
||
<div class="table-header">
|
||
<span class="table-title">📍 Realtids zoomers + objekt</span>
|
||
<button class="table-action" onclick="cycleZoomerPos()">↻ Uppdatera</button>
|
||
</div>
|
||
<div class="map-container" id="map" style="position:relative;min-height:260px;">
|
||
<div class="map-grid"></div>
|
||
|
||
<!-- Object pins -->
|
||
<div class="map-pin map-pin-ok" style="left:22%;top:30%;" title="Gamla Stan Bron · OK"><div class="map-pin-inner">✓</div></div>
|
||
<div class="map-pin map-pin-warn" style="left:45%;top:45%;" title="E4 Avsnitt 12 · Varning"><div class="map-pin-inner">!</div></div>
|
||
<div class="map-pin map-pin-critical" style="left:68%;top:25%;" title="E4 Älvsjö · Kritisk"><div class="map-pin-inner">✕</div></div>
|
||
<div class="map-pin map-pin-ok" style="left:35%;top:65%;" title="Arlanda Terminal 5 · OK"><div class="map-pin-inner">✓</div></div>
|
||
<div class="map-pin map-pin-warn" style="left:80%;top:55%;" title="Öresundsbron · Varning"><div class="map-pin-inner">!</div></div>
|
||
<div class="map-pin map-pin-ok" style="left:55%;top:72%;" title="Göta Älvbron · OK"><div class="map-pin-inner">✓</div></div>
|
||
|
||
<!-- Live zoomer pins -->
|
||
<div id="zoomer-lx01" style="position:absolute;left:48%;top:42%;z-index:10;transition:all 2s ease;">
|
||
<div style="background:var(--ios-blue);color: #1d1d1f;border-radius:50%;width:28px;height:28px;display:flex;align-items:center;justify-content:center;font-size:14px;box-shadow:0 2px 8px rgba(0,122,255,0.5);animation:zoomer-pulse 2s infinite;">🚁</div>
|
||
<div style="font-size:9px;font-weight:700;color:var(--ios-blue);text-align:center;margin-top:2px;background:rgba(0,0,0,0.9);border-radius:6px;padding:1px 4px;">LX-01</div>
|
||
</div>
|
||
<div id="zoomer-lx02" style="position:absolute;left:24%;top:27%;z-index:10;transition:all 2s ease;">
|
||
<div style="background:var(--ios-green);color: #1d1d1f;border-radius:50%;width:28px;height:28px;display:flex;align-items:center;justify-content:center;font-size:14px;box-shadow:0 2px 8px rgba(52,199,89,0.5);">🚁</div>
|
||
<div style="font-size:9px;font-weight:700;color:var(--ios-green);text-align:center;margin-top:2px;background:rgba(0,0,0,0.9);border-radius:6px;padding:1px 4px;">LX-02</div>
|
||
</div>
|
||
<div id="zoomer-lx03" style="position:absolute;left:72%;top:58%;z-index:10;transition:all 2s ease;">
|
||
<div style="background:var(--ios-orange);color: #1d1d1f;border-radius:50%;width:28px;height:28px;display:flex;align-items:center;justify-content:center;font-size:14px;box-shadow:0 2px 8px rgba(255,149,0,0.5);">🚁</div>
|
||
<div style="font-size:9px;font-weight:700;color:var(--ios-orange);text-align:center;margin-top:2px;background:rgba(0,0,0,0.9);border-radius:6px;padding:1px 4px;">LX-03 <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#f59e0b" stroke-width="2"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg></div>
|
||
</div>
|
||
|
||
<!-- Live indicator -->
|
||
<div style="position:absolute;top:10px;left:10px;z-index:11;display:flex;align-items:center;gap:5px;background:rgba(0,0,0,0.9);border-radius:20px;padding:4px 10px;font-size:11px;font-weight:600;color:var(--ios-green);">
|
||
<div style="width:6px;height:6px;border-radius:50%;background:var(--ios-green);animation:zoomer-pulse 2s infinite;"></div>
|
||
LIVE
|
||
</div>
|
||
|
||
<div class="map-legend">
|
||
<div class="legend-item"><div class="legend-dot" style="background:var(--ios-green)"></div>OK (189)</div>
|
||
<div class="legend-item"><div class="legend-dot" style="background:var(--ios-orange)"></div>Varning (41)</div>
|
||
<div class="legend-item"><div class="legend-dot" style="background:var(--ios-red)"></div>Kritisk (17)</div>
|
||
<div class="legend-item"><div class="legend-dot" style="background:var(--ios-blue)"></div>🚁 Zoomer (3)</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- UPPDRAGSSTATUS -->
|
||
<div class="section-header">Uppdragsstatus</div>
|
||
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:12px;">
|
||
<div class="metric-card metric-green">
|
||
<div class="metric-value">14</div>
|
||
<div class="metric-label">Pågående</div>
|
||
<div class="metric-trend trend-up">🚁 Aktiva uppdrag</div>
|
||
</div>
|
||
<div class="metric-card metric-blue">
|
||
<div class="metric-value">218</div>
|
||
<div class="metric-label">Klart idag</div>
|
||
<div class="metric-trend trend-up">↑ 23% vs plan</div>
|
||
</div>
|
||
<div class="metric-card metric-orange">
|
||
<div class="metric-value">3</div>
|
||
<div class="metric-label">Avvikelse</div>
|
||
<div class="metric-trend trend-down">↓ Kräver åtgärd</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ACTIVE MISSIONS TABLE -->
|
||
<div class="table-card" style="margin-bottom:12px;">
|
||
<div class="table-header">
|
||
<span class="table-title">⚡ Pågående uppdrag</span>
|
||
<button class="table-action" onclick="openDispatch()">Snabb-dispatch →</button>
|
||
</div>
|
||
<div class="table-scroll">
|
||
<table>
|
||
<thead><tr>
|
||
<th>Zoomer</th><th>Objekt</th><th>Status</th><th>Progress</th><th>ETA</th><th>Åtgärd</th>
|
||
</tr></thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><span style="font-weight:600;color:var(--ios-blue);">🚁 LX-01</span></td>
|
||
<td>E4 Avsnitt 12</td>
|
||
<td><span style="background:rgba(52,199,89,0.12);color:var(--ios-green);font-size:11px;font-weight:700;padding:3px 8px;border-radius:20px;">PÅGÅR</span></td>
|
||
<td>
|
||
<div style="width:80px;height:5px;background:var(--fill-tertiary);border-radius:3px;overflow:hidden;">
|
||
<div style="width:72%;height:100%;background:var(--ios-blue);border-radius:3px;"></div>
|
||
</div>
|
||
<div style="font-size:10px;color:var(--label-tertiary);margin-top:2px;">72%</div>
|
||
</td>
|
||
<td style="font-size:13px;">14 min</td>
|
||
<td><button style="font-size:12px;color:var(--ios-blue);background:none;border:none;cursor:pointer;padding:4px 0;">Följ live</button></td>
|
||
</tr>
|
||
<tr>
|
||
<td><span style="font-weight:600;color:var(--ios-green);">🚁 LX-02</span></td>
|
||
<td>Gamla Stan Bron</td>
|
||
<td><span style="background:rgba(52,199,89,0.12);color:var(--ios-green);font-size:11px;font-weight:700;padding:3px 8px;border-radius:20px;">PÅGÅR</span></td>
|
||
<td>
|
||
<div style="width:80px;height:5px;background:var(--fill-tertiary);border-radius:3px;overflow:hidden;">
|
||
<div style="width:35%;height:100%;background:var(--ios-green);border-radius:3px;"></div>
|
||
</div>
|
||
<div style="font-size:10px;color:var(--label-tertiary);margin-top:2px;">35%</div>
|
||
</td>
|
||
<td style="font-size:13px;">38 min</td>
|
||
<td><button style="font-size:12px;color:var(--ios-blue);background:none;border:none;cursor:pointer;padding:4px 0;">Följ live</button></td>
|
||
</tr>
|
||
<tr>
|
||
<td><span style="font-weight:600;color:var(--ios-orange);">🚁 LX-03</span></td>
|
||
<td>Öresundsbron S3</td>
|
||
<td><span style="background:rgba(255,149,0,0.12);color:var(--ios-orange);font-size:11px;font-weight:700;padding:3px 8px;border-radius:20px;">AVVIKELSE</span></td>
|
||
<td>
|
||
<div style="width:80px;height:5px;background:var(--fill-tertiary);border-radius:3px;overflow:hidden;">
|
||
<div style="width:58%;height:100%;background:var(--ios-orange);border-radius:3px;"></div>
|
||
</div>
|
||
<div style="font-size:10px;color:var(--label-tertiary);margin-top:2px;">58%</div>
|
||
</td>
|
||
<td style="font-size:13px;">Försenad</td>
|
||
<td><button style="font-size:12px;color:var(--ios-red);background:none;border:none;cursor:pointer;font-weight:600;padding:4px 0;" onclick="openDispatch()">Dispatch ny</button></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- KOSTNADSEFFEKTIVITET -->
|
||
<div class="section-header">Kostnadseffektivitet</div>
|
||
<div style="display:grid;grid-template-columns:repeat(2,1fr);gap:10px;margin-bottom:12px;">
|
||
<div class="metric-card metric-green">
|
||
<div class="metric-value">1 240</div>
|
||
<div class="metric-label">Pris/inspektion (SEK)</div>
|
||
<div class="metric-trend trend-up">↓ 62% vs marknad</div>
|
||
</div>
|
||
<div class="metric-card metric-blue">
|
||
<div class="metric-value">3 280</div>
|
||
<div class="metric-label">Marknadspris (SEK)</div>
|
||
<div class="metric-trend flat">→ Traditionell inspektion</div>
|
||
</div>
|
||
<div class="metric-card metric-green">
|
||
<div class="metric-value">218</div>
|
||
<div class="metric-label">Insp. idag</div>
|
||
<div class="metric-trend trend-up">Rekord <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#0066FF" stroke-width="2"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/></svg></div>
|
||
</div>
|
||
<div class="metric-card metric-orange">
|
||
<div class="metric-value">270k</div>
|
||
<div class="metric-label">Besparing idag (SEK)</div>
|
||
<div class="metric-trend trend-up">↑ vs traditionellt</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- PIPELINE -->
|
||
<div class="section-header">Inkommande beställningar</div>
|
||
<div class="table-card" style="margin-bottom:12px;">
|
||
<div class="table-header">
|
||
<span class="table-title">Pipeline · 7 väntande</span>
|
||
<button class="table-action">Hantera alla →</button>
|
||
</div>
|
||
<div class="table-scroll">
|
||
<table>
|
||
<thead><tr>
|
||
<th>Kund</th><th>Objekt</th><th>Typ</th><th>Prio</th><th>Mottagen</th><th>Åtgärd</th>
|
||
</tr></thead>
|
||
<tbody>
|
||
<tr>
|
||
<td style="font-weight:600;">Trafikverket</td><td>E4 Älvsjö · Kritisk</td><td>Fullständig</td>
|
||
<td><span style="background:rgba(255,59,48,0.12);color:var(--ios-red);font-size:11px;font-weight:700;padding:2px 7px;border-radius:20px;">KRITISK</span></td>
|
||
<td style="font-size:12px;color:var(--label-tertiary);">09:12</td>
|
||
<td><button style="font-size:12px;color: #1d1d1f;background:var(--ios-red);border:none;border-radius:12px;padding:4px 12px;cursor:pointer;font-weight:600;" onclick="openDispatch()">Dispatch nu</button></td>
|
||
</tr>
|
||
<tr>
|
||
<td style="font-weight:600;">Region Stockholm</td><td>Gamla Stan Bro</td><td>Ytinspektion</td>
|
||
<td><span style="background:rgba(255,149,0,0.12);color:var(--ios-orange);font-size:11px;font-weight:700;padding:2px 7px;border-radius:20px;">HÖG</span></td>
|
||
<td style="font-size:12px;color:var(--label-tertiary);">08:45</td>
|
||
<td><button style="font-size:12px;color: #1d1d1f;background:var(--ios-orange);border:none;border-radius:12px;padding:4px 12px;cursor:pointer;font-weight:600;" onclick="openDispatch()">Dispatch</button></td>
|
||
</tr>
|
||
<tr>
|
||
<td style="font-weight:600;">NCC Sverige</td><td>Rv 40 Borås</td><td>Strukturkontroll</td>
|
||
<td><span style="background:rgba(0,122,255,0.10);color:var(--ios-blue);font-size:11px;font-weight:700;padding:2px 7px;border-radius:20px;">NORMAL</span></td>
|
||
<td style="font-size:12px;color:var(--label-tertiary);">Igår</td>
|
||
<td><button style="font-size:12px;color:var(--ios-blue);background:none;border:1px solid var(--ios-blue);border-radius:12px;padding:4px 12px;cursor:pointer;font-weight:600;">Schemalägg</button></td>
|
||
</tr>
|
||
<tr>
|
||
<td style="font-weight:600;">Ellevio AB</td><td>Kraftledning N32</td><td>Optisk scanning</td>
|
||
<td><span style="background:rgba(0,122,255,0.10);color:var(--ios-blue);font-size:11px;font-weight:700;padding:2px 7px;border-radius:20px;">NORMAL</span></td>
|
||
<td style="font-size:12px;color:var(--label-tertiary);">Igår</td>
|
||
<td><button style="font-size:12px;color:var(--ios-blue);background:none;border:1px solid var(--ios-blue);border-radius:12px;padding:4px 12px;cursor:pointer;font-weight:600;">Schemalägg</button></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- KARTVY (original) -->
|
||
<div class="section-header" style="display:none;">Kartvy (Legacy)</div>
|
||
|
||
<!-- OBJECTS TABLE -->
|
||
<div class="section-header">Registrerade objekt</div>
|
||
<div class="table-card">
|
||
<div class="table-header">
|
||
<span class="table-title">Fastigheter & Infrastruktur</span>
|
||
<button class="table-action" onclick="openModal()">+ Nytt objekt</button>
|
||
</div>
|
||
<div class="table-scroll">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Objekt</th>
|
||
<th>Typ</th>
|
||
<th>Status</th>
|
||
<th>Risk</th>
|
||
<th>Senaste inspektion</th>
|
||
<th>Nästa inspektion</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr onclick="alert('Öppnar objektdetaljer: E4 Norrtull')">
|
||
<td><div class="td-name">E4 Norrtull Avsnitt 12</div><div class="td-sub">REF-001 · Stockholm</div></td>
|
||
<td>🛣️ Väg</td>
|
||
<td><span class="status-badge status-warn"><span class="status-dot"></span>Kräver åtgärd</span></td>
|
||
<td>
|
||
<div class="risk-bar-wrap risk-medium">
|
||
<div class="risk-bar"><div class="risk-fill" style="width:58%"></div></div>
|
||
<span class="risk-label">58</span>
|
||
</div>
|
||
</td>
|
||
<td>2026-05-10</td>
|
||
<td>2026-05-25</td>
|
||
</tr>
|
||
<tr onclick="alert('Öppnar objektdetaljer: Gamla Stan')">
|
||
<td><div class="td-name">Gamla Stan Bro</div><div class="td-sub">REF-002 · Stockholm</div></td>
|
||
<td>🌉 Bro</td>
|
||
<td><span class="status-badge status-ok"><span class="status-dot"></span>OK</span></td>
|
||
<td>
|
||
<div class="risk-bar-wrap risk-low">
|
||
<div class="risk-bar"><div class="risk-fill" style="width:22%"></div></div>
|
||
<span class="risk-label">22</span>
|
||
</div>
|
||
</td>
|
||
<td>2026-05-14</td>
|
||
<td>2026-08-14</td>
|
||
</tr>
|
||
<tr onclick="alert('Öppnar objektdetaljer: Kungliga Slottet')">
|
||
<td><div class="td-name">Kungliga Slottet Tak</div><div class="td-sub">REF-003 · Stockholm</div></td>
|
||
<td>🏛️ Byggnad</td>
|
||
<td><span class="status-badge status-critical"><span class="status-dot"></span>Kritisk</span></td>
|
||
<td>
|
||
<div class="risk-bar-wrap risk-high">
|
||
<div class="risk-bar"><div class="risk-fill" style="width:87%"></div></div>
|
||
<span class="risk-label">87</span>
|
||
</div>
|
||
</td>
|
||
<td>2026-05-01</td>
|
||
<td>🔴 Omedelbart</td>
|
||
</tr>
|
||
<tr onclick="alert('Öppnar objektdetaljer: Arlanda Terminal 5')">
|
||
<td><div class="td-name">Arlanda Terminal 5</div><div class="td-sub">REF-004 · Sigtuna</div></td>
|
||
<td>✈️ Byggnad</td>
|
||
<td><span class="status-badge status-ok"><span class="status-dot"></span>OK</span></td>
|
||
<td>
|
||
<div class="risk-bar-wrap risk-low">
|
||
<div class="risk-bar"><div class="risk-fill" style="width:15%"></div></div>
|
||
<span class="risk-label">15</span>
|
||
</div>
|
||
</td>
|
||
<td>2026-05-16</td>
|
||
<td>2026-11-16</td>
|
||
</tr>
|
||
<tr onclick="alert('Öppnar objektdetaljer: Öresundsbron')">
|
||
<td><div class="td-name">Öresundsbron Sektion 3</div><div class="td-sub">REF-005 · Malmö</div></td>
|
||
<td>🌉 Bro</td>
|
||
<td><span class="status-badge status-warn"><span class="status-dot"></span>Kräver åtgärd</span></td>
|
||
<td>
|
||
<div class="risk-bar-wrap risk-medium">
|
||
<div class="risk-bar"><div class="risk-fill" style="width:63%"></div></div>
|
||
<span class="risk-label">63</span>
|
||
</div>
|
||
</td>
|
||
<td>2026-04-28</td>
|
||
<td>2026-05-20</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- INSPECTIONS -->
|
||
<div class="section-header">Senaste inspektioner</div>
|
||
<div class="inspection-list">
|
||
<div class="inspection-item">
|
||
<div class="inspection-icon" style="background:rgba(52,199,89,0.15)">🏗️</div>
|
||
<div class="inspection-body">
|
||
<div class="inspection-title">E4 Norrtull — Ytinspektion</div>
|
||
<div class="inspection-meta">Anna Lindqvist · 2026-05-10 · <span style="color:var(--ios-green)">Godkänd</span></div>
|
||
</div>
|
||
<div class="inspection-chevron">›</div>
|
||
</div>
|
||
<div class="inspection-item">
|
||
<div class="inspection-icon" style="background:rgba(255,149,0,0.15)">🌉</div>
|
||
<div class="inspection-body">
|
||
<div class="inspection-title">Öresundsbron — Strukturkontroll</div>
|
||
<div class="inspection-meta">Marcus Holm · 2026-04-28 · <span style="color:var(--ios-orange)">Åtgärder krävs</span></div>
|
||
</div>
|
||
<div class="inspection-chevron">›</div>
|
||
</div>
|
||
<div class="inspection-item">
|
||
<div class="inspection-icon" style="background:rgba(255,59,48,0.15)">🏛️</div>
|
||
<div class="inspection-body">
|
||
<div class="inspection-title">Kungliga Slottet — Takinspektion</div>
|
||
<div class="inspection-meta">Sofia Berg · 2026-05-01 · <span style="color:var(--ios-red)">Kritisk rapport</span></div>
|
||
</div>
|
||
<div class="inspection-chevron">›</div>
|
||
</div>
|
||
<div class="inspection-item">
|
||
<div class="inspection-icon" style="background:rgba(0,122,255,0.15)">✈️</div>
|
||
<div class="inspection-body">
|
||
<div class="inspection-title">Arlanda T5 — Fasadinspektion</div>
|
||
<div class="inspection-meta">Erik Svensson · 2026-05-16 · <span style="color:var(--ios-green)">Godkänd</span></div>
|
||
</div>
|
||
<div class="inspection-chevron">›</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- STATUS REPORTS -->
|
||
<div class="section-header">Statusrapporter</div>
|
||
<div class="table-card">
|
||
<div class="table-scroll">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Rapport</th>
|
||
<th>Period</th>
|
||
<th>Objekt</th>
|
||
<th>Status</th>
|
||
<th></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><div class="td-name">Månatlig Statusrapport</div><div class="td-sub">Automatisk</div></td>
|
||
<td>Maj 2026</td>
|
||
<td>247 objekt</td>
|
||
<td><span class="status-badge status-ok"><span class="status-dot"></span>Klar</span></td>
|
||
<td><button class="table-action">Ladda ned</button></td>
|
||
</tr>
|
||
<tr>
|
||
<td><div class="td-name">Kritiska avvikelser</div><div class="td-sub">Automatisk · Alert</div></td>
|
||
<td>2026-05-17</td>
|
||
<td>3 objekt</td>
|
||
<td><span class="status-badge status-critical"><span class="status-dot"></span>Åtgärda</span></td>
|
||
<td><button class="table-action">Visa</button></td>
|
||
</tr>
|
||
<tr>
|
||
<td><div class="td-name">Kvartal Q1 2026</div><div class="td-sub">Manuell</div></td>
|
||
<td>Jan–Mar 2026</td>
|
||
<td>231 objekt</td>
|
||
<td><span class="status-badge status-ok"><span class="status-dot"></span>Klar</span></td>
|
||
<td><button class="table-action">Ladda ned</button></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
</main>
|
||
|
||
<!-- FAB -->
|
||
<button class="fab" onclick="openModal()">
|
||
<span>+</span>
|
||
<span>Ny inspektion</span>
|
||
</button>
|
||
|
||
<!-- MODAL: Ny inspektion -->
|
||
<div class="modal-overlay" id="modal" onclick="closeModal(event)">
|
||
<div class="modal-sheet" onclick="event.stopPropagation()">
|
||
<div class="modal-handle"></div>
|
||
<div class="modal-title">Skapa ny inspektion</div>
|
||
<div class="form-group">
|
||
<div class="form-label">Objekt</div>
|
||
<select class="form-select">
|
||
<option>E4 Norrtull Avsnitt 12</option>
|
||
<option>Gamla Stan Bro</option>
|
||
<option>Kungliga Slottet Tak</option>
|
||
<option>Arlanda Terminal 5</option>
|
||
<option>Öresundsbron Sektion 3</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<div class="form-label">Inspektionstyp</div>
|
||
<select class="form-select">
|
||
<option>Ytinspektion</option>
|
||
<option>Strukturkontroll</option>
|
||
<option>Fasadinspektion</option>
|
||
<option>Takinspektion</option>
|
||
<option>Fullständig inspektion</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-group">
|
||
<div class="form-label">Ansvarig inspektion</div>
|
||
<input class="form-input" type="text" placeholder="Sök inspektör...">
|
||
</div>
|
||
<div class="form-group">
|
||
<div class="form-label">Datum</div>
|
||
<input class="form-input" type="date" value="2026-05-20">
|
||
</div>
|
||
<div class="modal-actions">
|
||
<button class="btn-secondary" onclick="closeModal()">Avbryt</button>
|
||
<button class="btn-primary" onclick="submitInspection()">Skapa inspektion</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- TAB BAR -->
|
||
<nav class="tab-bar">
|
||
<div class="tab-item active">
|
||
<div class="tab-icon">🏛️</div>
|
||
<div class="tab-label">Översikt</div>
|
||
</div>
|
||
<div class="tab-item" onclick="window.location.href='landvex-admin.html#objects'">
|
||
<div class="tab-icon">📋</div>
|
||
<div class="tab-label">Objekt</div>
|
||
</div>
|
||
<div class="tab-item" onclick="window.location.href='landvex-admin.html#inspections'">
|
||
<div class="tab-icon">🔍</div>
|
||
<div class="tab-label">Inspektioner</div>
|
||
</div>
|
||
<div class="tab-item">
|
||
<div class="tab-icon"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#0066FF" stroke-width="2"><path d="M18 20V10"/><path d="M12 20V4"/><path d="M6 20v-6"/></svg></div>
|
||
<div class="tab-label">Rapporter</div>
|
||
</div>
|
||
<div class="tab-item">
|
||
<div class="tab-icon">⚙️</div>
|
||
<div class="tab-label">Inställningar</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<style>
|
||
@keyframes zoomer-pulse {
|
||
0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(0,122,255,0.4); }
|
||
50% { transform: scale(1.05); box-shadow: 0 0 0 6px rgba(0,122,255,0); }
|
||
}
|
||
@media (max-width: 640px) {
|
||
.hero h1 { font-size: 2rem !important; }
|
||
.hero p { font-size: 1rem !important; }
|
||
.card { padding: 1.5rem !important; }
|
||
.grid { grid-template-columns: 1fr !important; }
|
||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||
}
|
||
</style>
|
||
|
||
<!-- DISPATCH MODAL -->
|
||
<div id="dispatch-modal" style="position:fixed;inset:0;background: rgba(242,242,247,0.5);display:flex;align-items:flex-end;z-index:1001;opacity:0;pointer-events:none;transition:opacity 0.25s;">
|
||
<div style="background:var(--background-primary);border-radius:20px 20px 0 0;padding:20px 20px 40px;width:100%;max-height:80vh;overflow-y:auto;transform:translateY(100%);transition:transform 0.35s cubic-bezier(0.34,1.56,0.64,1);" id="dispatch-sheet">
|
||
<div style="width:36px;height:4px;background:var(--fill-secondary);border-radius:2px;margin:0 auto 16px;"></div>
|
||
<div style="font-family:var(--font-display);font-size:22px;font-weight:700;letter-spacing:-0.4px;margin-bottom:16px;">⚡ Snabb-dispatch</div>
|
||
|
||
<div style="background:var(--background-secondary);border-radius:12px;padding:14px;margin-bottom:12px;">
|
||
<div style="font-size:13px;font-weight:600;color:var(--label-secondary);text-transform:uppercase;letter-spacing:0.4px;margin-bottom:10px;">Närmaste lediga zoomer</div>
|
||
<div style="display:flex;align-items:center;gap:12px;padding:10px;background:rgba(52,199,89,0.08);border-radius:10px;border:0.5px solid rgba(52,199,89,0.3);margin-bottom:8px;">
|
||
<span style="font-size:24px;">🚁</span>
|
||
<div style="flex:1;">
|
||
<div style="font-size:16px;font-weight:600;">LX-04 · Stockholm N</div>
|
||
<div style="font-size:12px;color:var(--label-secondary);">4.2 km bort · Laddad 100% · HD + Multispektral</div>
|
||
</div>
|
||
<button onclick="confirmDispatch('LX-04')" style="background:var(--ios-green);color: #1d1d1f;border:none;border-radius:12px;padding:8px 14px;font-size:14px;font-weight:600;cursor:pointer;">Skicka</button>
|
||
</div>
|
||
<div style="display:flex;align-items:center;gap:12px;padding:10px;background:var(--fill-quaternary);border-radius:10px;margin-bottom:8px;">
|
||
<span style="font-size:24px;">🚁</span>
|
||
<div style="flex:1;">
|
||
<div style="font-size:16px;font-weight:600;">LX-07 · Nacka</div>
|
||
<div style="font-size:12px;color:var(--label-secondary);">8.7 km bort · Laddad 87% · HD</div>
|
||
</div>
|
||
<button onclick="confirmDispatch('LX-07')" style="background:var(--ios-blue);color: #1d1d1f;border:none;border-radius:12px;padding:8px 14px;font-size:14px;font-weight:600;cursor:pointer;">Skicka</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="dispatch-success" style="display:none;text-align:center;padding:16px;background:rgba(52,199,89,0.1);border-radius:14px;color:var(--ios-green);font-size:16px;font-weight:600;"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#00C853" stroke-width="3"><path d="M20 6L9 17l-5-5"/></svg> Dispatch bekräftad! Zoomer anländer inom 12 min.</div>
|
||
<button onclick="closeDispatch()" style="width:100%;padding:14px;background:var(--fill-tertiary);color:var(--label-primary);border:none;border-radius:14px;font-size:17px;font-weight:600;cursor:pointer;margin-top:8px;">Stäng</button>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
function openModal() {
|
||
document.getElementById('modal').classList.add('open');
|
||
}
|
||
|
||
function closeModal(e) {
|
||
if (!e || e.target === document.getElementById('modal')) {
|
||
document.getElementById('modal').classList.remove('open');
|
||
}
|
||
}
|
||
|
||
function submitInspection() {
|
||
document.getElementById('modal').classList.remove('open');
|
||
// Toast feedback
|
||
const toast = document.createElement('div');
|
||
toast.style.cssText = 'position:fixed;bottom:80px;left:50%;transform:translateX(-50%);background:var(--ios-green);color:white;padding:12px 20px;border-radius:20px;font-size:15px;font-weight:600;z-index:500;box-shadow:0 4px 16px rgba(52,199,89,0.4);';
|
||
toast.textContent = '✓ Inspektion skapad';
|
||
document.body.appendChild(toast);
|
||
setTimeout(() => toast.remove(), 2500);
|
||
}
|
||
|
||
// Tab switching
|
||
document.querySelectorAll('.tab-item').forEach((tab, i) => {
|
||
tab.addEventListener('click', () => {
|
||
document.querySelectorAll('.tab-item').forEach(t => t.classList.remove('active'));
|
||
tab.classList.add('active');
|
||
});
|
||
});
|
||
|
||
// Dispatch modal
|
||
function openDispatch() {
|
||
const m = document.getElementById('dispatch-modal');
|
||
const s = document.getElementById('dispatch-sheet');
|
||
document.getElementById('dispatch-success').style.display = 'none';
|
||
m.style.opacity = '1';
|
||
m.style.pointerEvents = 'all';
|
||
setTimeout(() => s.style.transform = 'translateY(0)', 10);
|
||
}
|
||
|
||
function closeDispatch() {
|
||
const m = document.getElementById('dispatch-modal');
|
||
const s = document.getElementById('dispatch-sheet');
|
||
s.style.transform = 'translateY(100%)';
|
||
setTimeout(() => { m.style.opacity = '0'; m.style.pointerEvents = 'none'; }, 300);
|
||
}
|
||
|
||
function confirmDispatch(zoomerId) {
|
||
setTimeout(() => {
|
||
document.getElementById('dispatch-success').style.display = 'block';
|
||
}, 800);
|
||
}
|
||
|
||
document.getElementById('dispatch-modal').addEventListener('click', (e) => {
|
||
if (e.target === document.getElementById('dispatch-modal')) closeDispatch();
|
||
});
|
||
|
||
// Zoomer live position animation
|
||
function cycleZoomerPos() {
|
||
const z1 = document.getElementById('zoomer-lx01');
|
||
const z2 = document.getElementById('zoomer-lx02');
|
||
const z3 = document.getElementById('zoomer-lx03');
|
||
if (z1) z1.style.left = (44 + Math.random() * 8) + '%';
|
||
if (z1) z1.style.top = (38 + Math.random() * 8) + '%';
|
||
if (z2) z2.style.left = (20 + Math.random() * 8) + '%';
|
||
if (z2) z2.style.top = (25 + Math.random() * 8) + '%';
|
||
if (z3) z3.style.left = (68 + Math.random() * 8) + '%';
|
||
if (z3) z3.style.top = (52 + Math.random() * 8) + '%';
|
||
}
|
||
|
||
// Auto-animate zoomers every 5s
|
||
setInterval(cycleZoomerPos, 5000);
|
||
</script>
|
||
</body>
|
||
</html>
|