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
901 lines
27 KiB
HTML
901 lines
27 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 — Inspektörsportal</title>
|
||
<link rel="stylesheet" >
|
||
<link rel="stylesheet" href="/assets/css/landvex-complete.css">
|
||
<style>
|
||
/*
|
||
LANDVEX INSPEKTÖRSPORTAL — iOS Native
|
||
*/
|
||
|
||
* { box-sizing: border-box; margin: 0; padding: 0; -webkit-font-smoothing: antialiased; }
|
||
|
||
body {
|
||
font-family: var(--font-text);
|
||
background: var(--grouped-background-primary);
|
||
color: var(--label-primary);
|
||
min-height: 100vh;
|
||
padding-bottom: calc(60px + env(safe-area-inset-bottom));
|
||
}
|
||
|
||
/* NAV */
|
||
.nav-bar {
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 100;
|
||
background: var(--glass-bg);
|
||
backdrop-filter: var(--glass-blur);
|
||
-webkit-backdrop-filter: var(--glass-blur);
|
||
border-bottom: 0.5px solid var(--separator);
|
||
padding: 0 16px;
|
||
}
|
||
|
||
.nav-top {
|
||
height: 44px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.nav-brand {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
}
|
||
|
||
.brand-icon {
|
||
width: 28px;
|
||
height: 28px;
|
||
background: linear-gradient(135deg, #0055CC, #007AFF);
|
||
border-radius: var(--radius-sm);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: white;
|
||
font-size: 12px;
|
||
font-weight: 800;
|
||
}
|
||
|
||
.brand-label {
|
||
font-size: 17px;
|
||
font-weight: 600;
|
||
letter-spacing: -0.4px;
|
||
}
|
||
|
||
.badge-inspector {
|
||
font-size: 10px;
|
||
font-weight: 700;
|
||
padding: 24px;
|
||
background: rgba(88,86,214,0.15);
|
||
color: var(--ios-indigo);
|
||
border-radius: var(--radius-xl);
|
||
letter-spacing: 0.3px;
|
||
}
|
||
|
||
/* DATE HEADER */
|
||
.date-header {
|
||
padding: 24px 4px;
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: var(--label-secondary);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
.day-title {
|
||
font-family: var(--font-display);
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
letter-spacing: -0.5px;
|
||
padding: 24px 16px;
|
||
}
|
||
|
||
/* MISSION CARD */
|
||
.mission-card {
|
||
margin: 0 16px 12px;
|
||
background: var(--grouped-background-secondary);
|
||
border-radius: var(--radius-lg);
|
||
border: 0.5px solid var(--separator);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.mission-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 24px;
|
||
border-bottom: 0.5px solid var(--separator);
|
||
cursor: pointer;
|
||
transition: background 0.15s;
|
||
}
|
||
|
||
.mission-header:active { background: var(--fill-quaternary); }
|
||
|
||
.mission-title {
|
||
font-size: 17px;
|
||
font-weight: 600;
|
||
letter-spacing: -0.3px;
|
||
}
|
||
|
||
.mission-sub {
|
||
font-size: 13px;
|
||
color: var(--label-secondary);
|
||
margin-top: 2px;
|
||
}
|
||
|
||
.mission-number {
|
||
width: 44px;
|
||
height: 44px;
|
||
border-radius: var(--radius-full);
|
||
background: linear-gradient(135deg, #007AFF, #0055CC);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: white;
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.mission-body {
|
||
padding: 0 16px;
|
||
}
|
||
|
||
.mission-row {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
padding: 10px 0;
|
||
border-bottom: 0.5px solid var(--separator);
|
||
font-size: 15px;
|
||
}
|
||
|
||
.mission-row:last-child { border-bottom: none; }
|
||
|
||
.mission-row-icon {
|
||
font-size: 18px;
|
||
width: 24px;
|
||
text-align: center;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.mission-row-label { flex: 1; }
|
||
|
||
.mission-row-value {
|
||
font-size: 15px;
|
||
color: var(--label-secondary);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.mission-actions {
|
||
padding: 24px;
|
||
display: flex;
|
||
gap: 16px;
|
||
border-top: 0.5px solid var(--separator);
|
||
}
|
||
|
||
.btn-start {
|
||
flex: 2;
|
||
height: 44px;
|
||
background: var(--ios-green);
|
||
color: white;
|
||
border: none;
|
||
border-radius: var(--radius-md);
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
font-family: var(--font-text);
|
||
cursor: pointer;
|
||
transition: opacity 0.15s;
|
||
}
|
||
|
||
.btn-start:active { opacity: 0.8; }
|
||
|
||
.btn-nav {
|
||
flex: 1;
|
||
height: 44px;
|
||
background: var(--fill-secondary);
|
||
color: var(--ios-blue);
|
||
border: none;
|
||
border-radius: var(--radius-md);
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
font-family: var(--font-text);
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* FORM SECTIONS */
|
||
.form-section {
|
||
margin: 24px 16px 0;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 20px;
|
||
font-weight: 700;
|
||
letter-spacing: -0.4px;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.form-card {
|
||
background: var(--grouped-background-secondary);
|
||
border-radius: var(--radius-lg);
|
||
border: 0.5px solid var(--separator);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.form-row {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 24px;
|
||
border-bottom: 0.5px solid var(--separator);
|
||
gap: 16px;
|
||
}
|
||
|
||
.form-row:last-child { border-bottom: none; }
|
||
|
||
.form-row-label {
|
||
font-size: 15px;
|
||
font-weight: 500;
|
||
flex: 1;
|
||
}
|
||
|
||
.form-row-input {
|
||
text-align: right;
|
||
flex: 1;
|
||
background: none;
|
||
border: none;
|
||
outline: none;
|
||
font-size: 15px;
|
||
font-family: var(--font-text);
|
||
color: var(--label-secondary);
|
||
min-width: 0;
|
||
}
|
||
|
||
.form-row-input::placeholder { color: var(--label-quaternary); }
|
||
|
||
.form-row-select {
|
||
flex: 1;
|
||
text-align: right;
|
||
background: none;
|
||
border: none;
|
||
outline: none;
|
||
font-size: 15px;
|
||
font-family: var(--font-text);
|
||
color: var(--label-secondary);
|
||
cursor: pointer;
|
||
appearance: none;
|
||
}
|
||
|
||
.form-textarea-wrap {
|
||
padding: 24px;
|
||
}
|
||
|
||
.form-textarea {
|
||
width: 100%;
|
||
min-height: 100px;
|
||
background: none;
|
||
border: none;
|
||
outline: none;
|
||
font-size: 15px;
|
||
font-family: var(--font-text);
|
||
color: var(--label-primary);
|
||
resize: vertical;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.form-textarea::placeholder { color: var(--label-quaternary); }
|
||
|
||
/* CHECKLIST */
|
||
.checklist-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 24px;
|
||
border-bottom: 0.5px solid var(--separator);
|
||
gap: 16px;
|
||
cursor: pointer;
|
||
transition: background 0.15s;
|
||
}
|
||
|
||
.checklist-item:last-child { border-bottom: none; }
|
||
.checklist-item:active { background: var(--fill-quaternary); }
|
||
|
||
.check-box {
|
||
width: 24px;
|
||
height: 24px;
|
||
border-radius: var(--radius-full);
|
||
border: 2px solid var(--ios-blue);
|
||
flex-shrink: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.check-box.checked {
|
||
background: var(--ios-blue);
|
||
border-color: var(--ios-blue);
|
||
}
|
||
|
||
.check-mark {
|
||
color: white;
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
opacity: 0;
|
||
transition: opacity 0.2s;
|
||
}
|
||
|
||
.check-box.checked .check-mark { opacity: 1; }
|
||
|
||
.checklist-text { flex: 1; font-size: 15px; }
|
||
|
||
.checklist-text.checked { color: var(--label-secondary); text-decoration: line-through; }
|
||
|
||
/* PHOTO UPLOAD */
|
||
.photo-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 16px;
|
||
padding: 24px;
|
||
}
|
||
|
||
.photo-thumb {
|
||
aspect-ratio: 1;
|
||
border-radius: var(--radius-md);
|
||
overflow: hidden;
|
||
background: var(--fill-secondary);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
position: relative;
|
||
transition: opacity 0.15s;
|
||
}
|
||
|
||
.photo-thumb:active { opacity: 0.7; }
|
||
|
||
.photo-thumb.add-photo {
|
||
border: 2px dashed var(--separator);
|
||
background: transparent;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
}
|
||
|
||
.photo-thumb.add-photo .add-icon {
|
||
font-size: 28px;
|
||
color: var(--ios-blue);
|
||
}
|
||
|
||
.photo-thumb.add-photo .add-label {
|
||
font-size: 11px;
|
||
color: var(--ios-blue);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.photo-placeholder {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 32px;
|
||
background: var(--fill-secondary);
|
||
}
|
||
|
||
/* SUBMIT AREA */
|
||
.submit-area {
|
||
padding: 24px 32px;
|
||
}
|
||
|
||
.submit-btn {
|
||
width: 100%;
|
||
height: 54px;
|
||
background: linear-gradient(135deg, #007AFF, #0055CC);
|
||
color: white;
|
||
border: none;
|
||
border-radius: var(--radius-lg);
|
||
font-size: 17px;
|
||
font-weight: 700;
|
||
font-family: var(--font-display);
|
||
cursor: pointer;
|
||
letter-spacing: -0.3px;
|
||
transition: opacity 0.15s, transform 0.1s;
|
||
box-shadow: 0 4px 20px rgba(0,122,255,0.35);
|
||
}
|
||
|
||
.submit-btn:active {
|
||
opacity: 0.85;
|
||
transform: scale(0.98);
|
||
}
|
||
|
||
.submit-note {
|
||
text-align: center;
|
||
font-size: 12px;
|
||
color: var(--label-tertiary);
|
||
margin-top: 10px;
|
||
}
|
||
|
||
/* PROGRESS BAR */
|
||
.progress-wrap {
|
||
padding: 0 16px 16px;
|
||
}
|
||
|
||
.progress-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
font-size: 13px;
|
||
color: var(--label-secondary);
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.progress-track {
|
||
height: 4px;
|
||
background: var(--fill-tertiary);
|
||
border-radius: var(--radius-sm);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.progress-fill {
|
||
height: 100%;
|
||
border-radius: var(--radius-sm);
|
||
background: var(--ios-blue);
|
||
transition: width 0.4s;
|
||
}
|
||
|
||
/* 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: 16px;
|
||
cursor: pointer;
|
||
flex: 1;
|
||
opacity: 0.55;
|
||
transition: opacity 0.15s;
|
||
}
|
||
|
||
.tab-item.active { opacity: 1; }
|
||
.tab-icon { font-size: 22px; }
|
||
.tab-label { font-size: 10px; font-weight: 500; color: var(--label-secondary); }
|
||
.tab-item.active .tab-label { color: var(--ios-blue); }
|
||
|
||
/* NOTIFICATION DOT */
|
||
.notif-dot {
|
||
position: relative;
|
||
}
|
||
|
||
.notif-dot::after {
|
||
content: '3';
|
||
position: absolute;
|
||
top: -4px;
|
||
right: -8px;
|
||
background: var(--ios-red);
|
||
color: white;
|
||
font-size: 9px;
|
||
font-weight: 700;
|
||
width: 16px;
|
||
height: 16px;
|
||
border-radius: var(--radius-md);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: 2px solid var(--grouped-background-primary);
|
||
}
|
||
@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 -->
|
||
<nav class="nav-bar" aria-label="Main navigation">
|
||
<div class="nav-top">
|
||
<div class="nav-brand">
|
||
<div class="brand-icon">LX</div>
|
||
<span class="brand-label">LandveX</span>
|
||
</div>
|
||
<span class="badge-inspector">INSPEKTÖR</span>
|
||
<div>M</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<!-- DAILY MISSIONS -->
|
||
<div class="date-header">Måndag 18 maj 2026</div>
|
||
<div class="day-title">Dagens uppdrag</div>
|
||
|
||
<!-- MISSION 1 -->
|
||
<div class="mission-card">
|
||
<div class="mission-header">
|
||
<div>
|
||
<div class="mission-title">E4 Norrtull Avsnitt 12</div>
|
||
<div class="mission-sub"> Ytinspektion · Stockholm</div>
|
||
</div>
|
||
<div class="mission-number">1</div>
|
||
</div>
|
||
<div class="mission-body">
|
||
<div class="mission-row">
|
||
<div class="mission-row-icon"></div>
|
||
<div class="mission-row-label">Starttid</div>
|
||
<div class="mission-row-value">09:00</div>
|
||
</div>
|
||
<div class="mission-row">
|
||
<div class="mission-row-icon">⏱</div>
|
||
<div class="mission-row-label">Beräknad tid</div>
|
||
<div class="mission-row-value">90 min</div>
|
||
</div>
|
||
<div class="mission-row">
|
||
<div class="mission-row-icon"></div>
|
||
<div class="mission-row-label">Plats</div>
|
||
<div class="mission-row-value">E4, km 164–166</div>
|
||
</div>
|
||
<div class="mission-row">
|
||
<div class="mission-row-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div>
|
||
<div class="mission-row-label">Typ</div>
|
||
<div class="mission-row-value">Standard ytinspektion</div>
|
||
</div>
|
||
</div>
|
||
<div class="mission-actions">
|
||
<button class="btn-start" onclick="startMission(1)"> Starta uppdrag</button>
|
||
<button class="btn-nav" onclick="navigate('E4 Norrtull')"> Navigera</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MISSION 2 -->
|
||
<div class="mission-card">
|
||
<div class="mission-header">
|
||
<div>
|
||
<div class="mission-title">Kungliga Slottet Tak</div>
|
||
<div class="mission-sub"> Takinspektion · Kritisk · Stockholm</div>
|
||
</div>
|
||
<div class="mission-number">2</div>
|
||
</div>
|
||
<div class="mission-body">
|
||
<div class="mission-row">
|
||
<div class="mission-row-icon"></div>
|
||
<div class="mission-row-label">Starttid</div>
|
||
<div class="mission-row-value">13:00</div>
|
||
</div>
|
||
<div class="mission-row">
|
||
<div class="mission-row-icon">⏱</div>
|
||
<div class="mission-row-label">Beräknad tid</div>
|
||
<div class="mission-row-value">120 min</div>
|
||
</div>
|
||
<div class="mission-row">
|
||
<div class="mission-row-icon"></div>
|
||
<div class="mission-row-label">Plats</div>
|
||
<div class="mission-row-value">Kungliga Slottet, Gamla Stan</div>
|
||
</div>
|
||
<div class="mission-row">
|
||
<div class="mission-row-icon"><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 class="mission-row-label">Prioritet</div>
|
||
<div class="mission-row-value">KRITISK</div>
|
||
</div>
|
||
</div>
|
||
<div class="mission-actions">
|
||
<button class="btn-start" onclick="startMission(2)"> Starta uppdrag</button>
|
||
<button class="btn-nav" onclick="navigate('Kungliga Slottet')"> Navigera</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- PROGRESS -->
|
||
<div>
|
||
<div class="progress-wrap">
|
||
<div class="progress-header">
|
||
<span>Daglig progress</span>
|
||
<span>0 / 2 uppdrag klara</span>
|
||
</div>
|
||
<div class="progress-track">
|
||
<div class="progress-fill" id="dayProgress"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- INSPECTION FORM -->
|
||
<div class="form-section" id="inspectionForm">
|
||
<div class="section-title"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> Inspektionsprotokoll</div>
|
||
|
||
<div class="form-card">
|
||
<div class="form-row">
|
||
<div class="form-row-label">Objekt</div>
|
||
<input class="form-row-input" type="text" id="objektNamn" placeholder="Välj objekt..." readonly>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-row-label">Inspektionstyp</div>
|
||
<select class="form-row-select" id="inspType">
|
||
<option>Ytinspektion</option>
|
||
<option>Strukturkontroll</option>
|
||
<option>Takinspektion</option>
|
||
<option>Fasadinspektion</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-row-label">Datum</div>
|
||
<input class="form-row-input" type="date" id="inspDate" value="2026-05-18">
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-row-label">Väder</div>
|
||
<select class="form-row-select">
|
||
<option> Klart</option>
|
||
<option> Molnigt</option>
|
||
<option> Regn</option>
|
||
<option> Snö</option>
|
||
<option> Blåsigt</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-row-label">Riskbedömning</div>
|
||
<select class="form-row-select" id="riskLevel">
|
||
<option value="low"><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> Låg (0–30)</option>
|
||
<option value="medium"><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> Medium (31–65)</option>
|
||
<option value="high"> Hög (66–100)</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- CHECKLIST -->
|
||
<div>Checklista</div>
|
||
<div class="form-card">
|
||
<div class="checklist-item" onclick="toggleCheck(this)">
|
||
<div class="check-box"><span class="check-mark"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span></div>
|
||
<div class="checklist-text">Yttre skador dokumenterade</div>
|
||
</div>
|
||
<div class="checklist-item" onclick="toggleCheck(this)">
|
||
<div class="check-box"><span class="check-mark"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span></div>
|
||
<div class="checklist-text">Strukturell integritet kontrollerad</div>
|
||
</div>
|
||
<div class="checklist-item" onclick="toggleCheck(this)">
|
||
<div class="check-box"><span class="check-mark"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span></div>
|
||
<div class="checklist-text">Dränering och avlopp inspekterade</div>
|
||
</div>
|
||
<div class="checklist-item" onclick="toggleCheck(this)">
|
||
<div class="check-box"><span class="check-mark"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span></div>
|
||
<div class="checklist-text">Korrosion och fuktskador noterade</div>
|
||
</div>
|
||
<div class="checklist-item" onclick="toggleCheck(this)">
|
||
<div class="check-box"><span class="check-mark"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span></div>
|
||
<div class="checklist-text">Säkerhetsrisker bedömda</div>
|
||
</div>
|
||
<div class="checklist-item" onclick="toggleCheck(this)">
|
||
<div class="check-box"><span class="check-mark"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span></div>
|
||
<div class="checklist-text">Jämförelse med föregående inspektion</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- PHOTOS -->
|
||
<div>Foto (min 3 st)</div>
|
||
<div class="form-card">
|
||
<div class="photo-grid">
|
||
<div class="photo-thumb" onclick="addPhoto(this)">
|
||
<div class="photo-placeholder"></div>
|
||
</div>
|
||
<div class="photo-thumb" onclick="addPhoto(this)">
|
||
<div class="photo-placeholder"></div>
|
||
</div>
|
||
<div class="photo-thumb add-photo" onclick="takePhoto()">
|
||
<div class="add-icon">+</div>
|
||
<div class="add-label">Lägg till</div>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
Foton lagras säkert och bifogas rapporten automatiskt.
|
||
</div>
|
||
</div>
|
||
|
||
<!-- NOTES -->
|
||
<div>Observationer & anteckningar</div>
|
||
<div class="form-card">
|
||
<div class="form-textarea-wrap">
|
||
<textarea class="form-textarea" placeholder="Beskriv observationer, avvikelser och rekommenderade åtgärder...
|
||
|
||
Exempel:
|
||
- Sprickor identifierade på nordvästra hörn (20 cm lång, 2–3 mm bred)
|
||
- Fuktinträngning synlig under bro-sektionens ände
|
||
- Rekommendation: Täta sprickor inom 14 dagar, följ upp om 30 dagar"></textarea>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- SUBMIT -->
|
||
<div class="submit-area">
|
||
<button class="submit-btn" onclick="submitReport()">
|
||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Skicka inspektionsrapport
|
||
</button>
|
||
<div class="submit-note">Rapporten skickas till LandveX och objektägaren. Du kan ej ändra efter inskickning.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- PLACEHOLDER WHEN NO FORM -->
|
||
<div id="formPlaceholder">
|
||
<div><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div>
|
||
<div>Protokollformulär</div>
|
||
<div>Starta ett uppdrag ovan för att öppna inspektionsformuläret.</div>
|
||
</div>
|
||
|
||
<!-- TAB BAR -->
|
||
<nav class="tab-bar" aria-label="Main navigation">
|
||
<div class="tab-item active">
|
||
<div class="tab-icon"></div>
|
||
<div class="tab-label">Uppdrag</div>
|
||
</div>
|
||
<div class="tab-item" onclick="showForm()">
|
||
<div class="tab-icon"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg></div>
|
||
<div class="tab-label">Protokoll</div>
|
||
</div>
|
||
<div class="tab-item">
|
||
<div class="tab-icon notif-dot"></div>
|
||
<div class="tab-label">Notiser</div>
|
||
</div>
|
||
<div class="tab-item">
|
||
<div class="tab-icon"></div>
|
||
<div class="tab-label">Arkiv</div>
|
||
</div>
|
||
<div class="tab-item">
|
||
<div class="tab-icon"></div>
|
||
<div class="tab-label">Profil</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<script>
|
||
let completedMissions = 0;
|
||
|
||
function startMission(n) {
|
||
const missions = {
|
||
1: 'E4 Norrtull Avsnitt 12',
|
||
2: 'Kungliga Slottet Tak'
|
||
};
|
||
|
||
const form = document.getElementById('inspectionForm');
|
||
const placeholder = document.getElementById('formPlaceholder');
|
||
|
||
document.getElementById('objektNamn').value = missions[n];
|
||
form.style.display = 'block';
|
||
placeholder.style.display = 'none';
|
||
|
||
// Scroll to form
|
||
setTimeout(() => form.scrollIntoView({ behavior: 'smooth' }), 100);
|
||
|
||
// Feedback
|
||
const toast = document.createElement('div');
|
||
toast.style.cssText = 'position:fixed;top:60px;left:50%;transform:translateX(-50%);background:#F2F2F7;color:white;padding: 24px;border-radius: var(--radius-xl);font-size:14px;font-weight:600;z-index:500;';
|
||
toast.textContent = ` Uppdrag ${n} startat`;
|
||
document.body.appendChild(toast);
|
||
setTimeout(() => toast.remove(), 2000);
|
||
}
|
||
|
||
function navigate(place) {
|
||
alert(`Öppnar navigation till:\n${place}\n\n(Integreras med Maps/Waze i produktion)`);
|
||
}
|
||
|
||
function toggleCheck(item) {
|
||
const box = item.querySelector('.check-box');
|
||
const text = item.querySelector('.checklist-text');
|
||
box.classList.toggle('checked');
|
||
text.classList.toggle('checked');
|
||
updateProgress();
|
||
}
|
||
|
||
function updateProgress() {
|
||
const checked = document.querySelectorAll('.check-box.checked').length;
|
||
const total = document.querySelectorAll('.check-box').length;
|
||
const pct = total > 0 ? Math.round((checked / total) * 100) : 0;
|
||
document.getElementById('dayProgress').style.width = pct + '%';
|
||
}
|
||
|
||
function addPhoto(thumb) {
|
||
// Simulate photo capture
|
||
const emojis = ['','<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14H14M4 14V8L8 4L12 8V14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 14V10H10V14" stroke="#666" stroke-width="1.5"/></svg>','<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>','','',''];
|
||
thumb.innerHTML = `<div>${emojis[Math.floor(Math.random()*emojis.length)]}</div>`;
|
||
thumb.style.background = 'var(--fill-tertiary)';
|
||
}
|
||
|
||
function takePhoto() {
|
||
const grid = document.querySelector('.photo-grid');
|
||
const addBtn = grid.querySelector('.add-photo');
|
||
|
||
const newThumb = document.createElement('div');
|
||
newThumb.className = 'photo-thumb';
|
||
newThumb.onclick = function() { addPhoto(this); };
|
||
const emojis = ['','<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14H14M4 14V8L8 4L12 8V14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 14V10H10V14" stroke="#666" stroke-width="1.5"/></svg>','','','<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="5" stroke="#666" stroke-width="2"/><path d="M11 11L14 14" stroke="#666" stroke-width="2" stroke-linecap="round"/></svg>',''];
|
||
newThumb.innerHTML = `<div>${emojis[Math.floor(Math.random()*emojis.length)]}</div>`;
|
||
newThumb.style.background = 'var(--fill-secondary)';
|
||
|
||
grid.insertBefore(newThumb, addBtn);
|
||
}
|
||
|
||
function showForm() {
|
||
document.getElementById('inspectionForm').style.display = 'block';
|
||
document.getElementById('formPlaceholder').style.display = 'none';
|
||
document.getElementById('inspectionForm').scrollIntoView({ behavior: 'smooth' });
|
||
}
|
||
|
||
function submitReport() {
|
||
const objekt = document.getElementById('objektNamn').value;
|
||
if (!objekt) {
|
||
alert('Välj ett objekt och starta uppdrag först.');
|
||
return;
|
||
}
|
||
|
||
const confirmed = confirm(`Skicka inspektionsrapport?\n\nObjekt: ${objekt}\nDatum: ${document.getElementById('inspDate').value}\n\nRapporten kan inte ändras efter inskickning.`);
|
||
|
||
if (confirmed) {
|
||
completedMissions++;
|
||
|
||
// Update progress
|
||
const pct = Math.min(completedMissions / 2 * 100, 100);
|
||
document.getElementById('dayProgress').style.width = pct + '%';
|
||
|
||
// Hide form
|
||
document.getElementById('inspectionForm').style.display = 'none';
|
||
document.getElementById('formPlaceholder').style.display = 'block';
|
||
|
||
// 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: 24px;border-radius: var(--radius-xl);font-size:15px;font-weight:600;z-index:500;box-shadow:0 4px 20px rgba(52,199,89,0.4);text-align:center;';
|
||
toast.innerHTML = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Rapport inskickad<br><span>${objekt}</span>`;
|
||
document.body.appendChild(toast);
|
||
setTimeout(() => toast.remove(), 3000);
|
||
|
||
// Scroll top
|
||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||
}
|
||
}
|
||
|
||
// Tab switching
|
||
document.querySelectorAll('.tab-item').forEach(tab => {
|
||
tab.addEventListener('click', () => {
|
||
document.querySelectorAll('.tab-item').forEach(t => t.classList.remove('active'));
|
||
tab.classList.add('active');
|
||
});
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|