bae705aa97
- Add NFC ePassport roadmap (ICAO 9303, eIDAS) - Add TensorFlow.js edge face detection (BlazeFace) - Add structured audit logger (GDPR-compliant) - Risk scoring support Part of KYC Apple Native UX v1.1.0
1200 lines
46 KiB
HTML
1200 lines
46 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="sv">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Bolagsonboarding — AAMOS</title>
|
||
<style>
|
||
/* ── iOS 26 Native Design System ── */
|
||
:root {
|
||
--ios-bg: #F2F2F7;
|
||
--ios-surface: #FFFFFF;
|
||
--ios-surface-2: #F2F2F7;
|
||
--ios-grouped: rgba(255,255,255,0.72);
|
||
--ios-label: #000000;
|
||
--ios-label-2: rgba(60,60,67,0.60);
|
||
--ios-label-3: rgba(60,60,67,0.30);
|
||
--ios-label-4: rgba(60,60,67,0.18);
|
||
--ios-separator: rgba(60,60,67,0.29);
|
||
--ios-fill: rgba(120,120,128,0.12);
|
||
--ios-fill-2: rgba(120,120,128,0.16);
|
||
--ios-blue: #007AFF;
|
||
--ios-blue-dark: #0056CC;
|
||
--ios-green: #34C759;
|
||
--ios-orange: #FF9500;
|
||
--ios-red: #FF3B30;
|
||
--ios-purple: #AF52DE;
|
||
--ios-teal: #5AC8FA;
|
||
--ios-indigo: #5856D6;
|
||
--ios-gray-6: #F2F2F7;
|
||
--ios-corner-large: 16px;
|
||
--ios-corner-med: 12px;
|
||
--ios-corner-small: 8px;
|
||
--ios-shadow: 0 2px 20px rgba(0,0,0,0.08);
|
||
--ios-shadow-lg: 0 8px 40px rgba(0,0,0,0.12);
|
||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', sans-serif;
|
||
}
|
||
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
body {
|
||
background: var(--ios-bg);
|
||
color: var(--ios-label);
|
||
min-height: 100vh;
|
||
-webkit-font-smoothing: antialiased;
|
||
overflow-x: hidden;
|
||
}
|
||
|
||
/* ── Navigation Bar ── */
|
||
.nav-bar {
|
||
position: sticky; top: 0; z-index: 100;
|
||
background: rgba(242,242,247,0.88);
|
||
backdrop-filter: blur(20px) saturate(180%);
|
||
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
||
border-bottom: 0.5px solid var(--ios-separator);
|
||
padding: 0 20px;
|
||
height: 56px;
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
}
|
||
.nav-title {
|
||
font-size: 17px; font-weight: 600;
|
||
letter-spacing: -0.4px;
|
||
}
|
||
.nav-logo {
|
||
font-size: 15px; font-weight: 500; color: var(--ios-blue);
|
||
text-decoration: none;
|
||
}
|
||
.nav-badge {
|
||
background: var(--ios-blue); color: #fff;
|
||
font-size: 11px; font-weight: 600;
|
||
padding: 2px 8px; border-radius: 10px;
|
||
}
|
||
|
||
/* ── Hero ── */
|
||
.hero {
|
||
background: linear-gradient(145deg, #007AFF 0%, #5856D6 60%, #AF52DE 100%);
|
||
padding: 56px 24px 48px;
|
||
text-align: center;
|
||
color: #fff;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.hero::before {
|
||
content: '';
|
||
position: absolute; inset: 0;
|
||
background: radial-gradient(ellipse at 30% 60%, rgba(255,255,255,0.15) 0%, transparent 60%);
|
||
}
|
||
.hero-eyebrow {
|
||
font-size: 13px; font-weight: 600;
|
||
letter-spacing: 0.8px; text-transform: uppercase;
|
||
opacity: 0.8; margin-bottom: 12px;
|
||
}
|
||
.hero-title {
|
||
font-size: 34px; font-weight: 700;
|
||
letter-spacing: -1.2px; line-height: 1.1;
|
||
margin-bottom: 12px;
|
||
}
|
||
.hero-sub {
|
||
font-size: 17px; opacity: 0.85;
|
||
font-weight: 400; line-height: 1.5;
|
||
max-width: 340px; margin: 0 auto 24px;
|
||
}
|
||
.hero-badge {
|
||
display: inline-flex; align-items: center; gap: 6px;
|
||
background: rgba(255,255,255,0.2);
|
||
backdrop-filter: blur(10px);
|
||
border: 1px solid rgba(255,255,255,0.3);
|
||
border-radius: 100px;
|
||
padding: 6px 16px;
|
||
font-size: 14px; font-weight: 600;
|
||
}
|
||
|
||
/* ── Container ── */
|
||
.container {
|
||
max-width: 520px; margin: 0 auto;
|
||
padding: 0 16px 80px;
|
||
}
|
||
|
||
/* ── Step Indicator ── */
|
||
.steps-bar {
|
||
display: flex; align-items: center; justify-content: center;
|
||
gap: 0; margin: 24px 0;
|
||
}
|
||
.step-dot {
|
||
width: 28px; height: 28px; border-radius: 50%;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 12px; font-weight: 700;
|
||
background: var(--ios-fill);
|
||
color: var(--ios-label-2);
|
||
transition: all 0.3s ease;
|
||
position: relative; z-index: 1;
|
||
}
|
||
.step-dot.active {
|
||
background: var(--ios-blue); color: #fff;
|
||
box-shadow: 0 4px 12px rgba(0,122,255,0.35);
|
||
}
|
||
.step-dot.done {
|
||
background: var(--ios-green); color: #fff;
|
||
}
|
||
.step-line {
|
||
flex: 1; height: 2px; max-width: 48px;
|
||
background: var(--ios-fill);
|
||
transition: background 0.3s;
|
||
}
|
||
.step-line.done { background: var(--ios-green); }
|
||
|
||
/* ── Card ── */
|
||
.card {
|
||
background: var(--ios-surface);
|
||
border-radius: var(--ios-corner-large);
|
||
box-shadow: var(--ios-shadow);
|
||
overflow: hidden;
|
||
margin-bottom: 16px;
|
||
}
|
||
.card-header {
|
||
padding: 16px 20px 12px;
|
||
border-bottom: 0.5px solid var(--ios-separator);
|
||
display: flex; align-items: center; gap: 12px;
|
||
}
|
||
.card-icon {
|
||
width: 36px; height: 36px; border-radius: 8px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 18px;
|
||
flex-shrink: 0;
|
||
}
|
||
.card-icon.blue { background: rgba(0,122,255,0.12); }
|
||
.card-icon.green { background: rgba(52,199,89,0.12); }
|
||
.card-icon.purple { background: rgba(175,82,222,0.12); }
|
||
.card-title { font-size: 17px; font-weight: 600; letter-spacing: -0.4px; }
|
||
.card-sub { font-size: 13px; color: var(--ios-label-2); margin-top: 2px; }
|
||
.card-body { padding: 16px 20px; }
|
||
|
||
/* ── Form fields ── */
|
||
.field-group { margin-bottom: 16px; }
|
||
.field-label {
|
||
font-size: 13px; font-weight: 500;
|
||
color: var(--ios-label-2);
|
||
margin-bottom: 6px;
|
||
letter-spacing: -0.1px;
|
||
display: flex; align-items: center; gap: 6px;
|
||
}
|
||
.field-required { color: var(--ios-red); }
|
||
.field-input {
|
||
width: 100%;
|
||
background: var(--ios-fill);
|
||
border: none; outline: none;
|
||
border-radius: var(--ios-corner-med);
|
||
padding: 13px 16px;
|
||
font-size: 17px; font-weight: 400;
|
||
color: var(--ios-label);
|
||
font-family: inherit;
|
||
transition: all 0.2s;
|
||
-webkit-appearance: none;
|
||
}
|
||
.field-input:focus {
|
||
background: var(--ios-surface);
|
||
box-shadow: 0 0 0 2px var(--ios-blue), var(--ios-shadow);
|
||
}
|
||
.field-input.valid { box-shadow: 0 0 0 2px var(--ios-green); }
|
||
.field-input.error { box-shadow: 0 0 0 2px var(--ios-red); }
|
||
.field-hint {
|
||
font-size: 12px; color: var(--ios-label-3);
|
||
margin-top: 6px;
|
||
}
|
||
|
||
/* ── Company preview card ── */
|
||
.company-preview {
|
||
background: linear-gradient(135deg, rgba(52,199,89,0.08) 0%, rgba(0,122,255,0.06) 100%);
|
||
border: 1px solid rgba(52,199,89,0.3);
|
||
border-radius: var(--ios-corner-med);
|
||
padding: 14px 16px;
|
||
margin-top: 12px;
|
||
display: none;
|
||
}
|
||
.company-preview.show { display: block; animation: fadeIn 0.3s ease; }
|
||
.company-preview-name {
|
||
font-size: 17px; font-weight: 600; letter-spacing: -0.5px;
|
||
color: var(--ios-label);
|
||
}
|
||
.company-preview-row {
|
||
font-size: 13px; color: var(--ios-label-2);
|
||
margin-top: 4px; display: flex; align-items: center; gap: 6px;
|
||
}
|
||
.company-preview-badge {
|
||
display: inline-flex; align-items: center;
|
||
background: rgba(52,199,89,0.15); color: #1a7a30;
|
||
border-radius: 100px; padding: 2px 8px;
|
||
font-size: 11px; font-weight: 600; letter-spacing: 0.2px;
|
||
}
|
||
|
||
/* ── Plan selector ── */
|
||
.plans-grid {
|
||
display: grid; grid-template-columns: 1fr 1fr 1fr;
|
||
gap: 10px; margin-bottom: 4px;
|
||
}
|
||
.plan-card {
|
||
border: 1.5px solid var(--ios-fill-2);
|
||
border-radius: var(--ios-corner-med);
|
||
padding: 12px 10px;
|
||
cursor: pointer; text-align: center;
|
||
transition: all 0.2s;
|
||
background: var(--ios-surface);
|
||
position: relative;
|
||
}
|
||
.plan-card:hover { border-color: var(--ios-blue); background: rgba(0,122,255,0.04); }
|
||
.plan-card.selected {
|
||
border-color: var(--ios-blue);
|
||
background: rgba(0,122,255,0.08);
|
||
box-shadow: 0 4px 16px rgba(0,122,255,0.15);
|
||
}
|
||
.plan-name { font-size: 14px; font-weight: 600; margin-top: 4px; }
|
||
.plan-price { font-size: 12px; color: var(--ios-label-2); margin-top: 2px; }
|
||
.plan-emoji { font-size: 20px; }
|
||
.plan-check {
|
||
position: absolute; top: 6px; right: 6px;
|
||
width: 16px; height: 16px; border-radius: 50%;
|
||
background: var(--ios-blue); color: #fff;
|
||
font-size: 10px;
|
||
display: none; align-items: center; justify-content: center;
|
||
}
|
||
.plan-card.selected .plan-check { display: flex; }
|
||
|
||
/* ── CTA Button ── */
|
||
.btn-primary {
|
||
width: 100%;
|
||
background: var(--ios-blue);
|
||
color: #fff;
|
||
border: none; outline: none;
|
||
border-radius: var(--ios-corner-large);
|
||
padding: 16px;
|
||
font-size: 17px; font-weight: 600;
|
||
font-family: inherit;
|
||
cursor: pointer;
|
||
letter-spacing: -0.4px;
|
||
transition: all 0.15s;
|
||
box-shadow: 0 4px 16px rgba(0,122,255,0.35);
|
||
-webkit-tap-highlight-color: transparent;
|
||
}
|
||
.btn-primary:hover { background: var(--ios-blue-dark); box-shadow: 0 6px 24px rgba(0,122,255,0.4); }
|
||
.btn-primary:active { transform: scale(0.98); }
|
||
.btn-primary:disabled { background: var(--ios-label-3); box-shadow: none; cursor: not-allowed; }
|
||
|
||
.btn-secondary {
|
||
background: var(--ios-fill);
|
||
color: var(--ios-blue);
|
||
border: none; outline: none;
|
||
border-radius: var(--ios-corner-large);
|
||
padding: 14px 24px;
|
||
font-size: 15px; font-weight: 600;
|
||
font-family: inherit;
|
||
cursor: pointer;
|
||
transition: all 0.15s;
|
||
-webkit-tap-highlight-color: transparent;
|
||
}
|
||
.btn-secondary:hover { background: var(--ios-fill-2); }
|
||
|
||
/* ── Progress bar ── */
|
||
.progress-wrap {
|
||
background: var(--ios-fill);
|
||
border-radius: 100px; height: 6px;
|
||
overflow: hidden; margin: 12px 0;
|
||
}
|
||
.progress-bar {
|
||
height: 100%; border-radius: 100px;
|
||
background: linear-gradient(90deg, var(--ios-blue) 0%, var(--ios-indigo) 100%);
|
||
transition: width 0.5s ease;
|
||
box-shadow: 0 2px 8px rgba(0,122,255,0.35);
|
||
}
|
||
|
||
/* ── Step items ── */
|
||
.step-items { display: flex; flex-direction: column; gap: 8px; padding: 4px 0; }
|
||
.step-item {
|
||
display: flex; align-items: center; gap: 12px;
|
||
padding: 10px 0;
|
||
border-bottom: 0.5px solid var(--ios-separator);
|
||
animation: fadeIn 0.4s ease;
|
||
}
|
||
.step-item:last-child { border-bottom: none; }
|
||
.step-status-icon {
|
||
width: 28px; height: 28px; border-radius: 50%;
|
||
display: flex; align-items: center; justify-content: center;
|
||
flex-shrink: 0; font-size: 14px;
|
||
}
|
||
.step-status-icon.pending { background: var(--ios-fill); color: var(--ios-label-3); }
|
||
.step-status-icon.running { background: rgba(0,122,255,0.12); color: var(--ios-blue); }
|
||
.step-status-icon.done { background: rgba(52,199,89,0.12); color: var(--ios-green); }
|
||
.step-status-icon.error { background: rgba(255,59,48,0.12); color: var(--ios-red); }
|
||
.step-item-text { flex: 1; }
|
||
.step-item-label { font-size: 15px; font-weight: 500; }
|
||
.step-item-sub { font-size: 12px; color: var(--ios-label-2); margin-top: 2px; }
|
||
.spinner { animation: spin 1s linear infinite; display: inline-block; }
|
||
|
||
/* ── Checklist ── */
|
||
.checklist-category { margin-bottom: 20px; }
|
||
.checklist-cat-header {
|
||
display: flex; align-items: center; gap: 8px;
|
||
font-size: 13px; font-weight: 600;
|
||
color: var(--ios-label-2); text-transform: uppercase;
|
||
letter-spacing: 0.5px; margin-bottom: 8px;
|
||
}
|
||
.checklist-item {
|
||
display: flex; align-items: flex-start; gap: 12px;
|
||
padding: 10px 0;
|
||
border-bottom: 0.5px solid var(--ios-separator);
|
||
cursor: pointer;
|
||
transition: opacity 0.2s;
|
||
}
|
||
.checklist-item:last-child { border-bottom: none; }
|
||
.checklist-item.completed { opacity: 0.5; }
|
||
.checklist-item.completed .checklist-title { text-decoration: line-through; }
|
||
.checklist-checkbox {
|
||
width: 22px; height: 22px; border-radius: 50%;
|
||
border: 2px solid var(--ios-separator);
|
||
display: flex; align-items: center; justify-content: center;
|
||
flex-shrink: 0; margin-top: 1px;
|
||
transition: all 0.2s;
|
||
}
|
||
.checklist-item.completed .checklist-checkbox {
|
||
background: var(--ios-green); border-color: var(--ios-green);
|
||
}
|
||
.checklist-item.auto .checklist-checkbox {
|
||
background: var(--ios-blue); border-color: var(--ios-blue);
|
||
}
|
||
.checklist-title { font-size: 15px; font-weight: 500; }
|
||
.checklist-meta { display: flex; align-items: center; gap: 8px; margin-top: 4px; }
|
||
.checklist-time { font-size: 12px; color: var(--ios-label-2); }
|
||
.priority-badge {
|
||
font-size: 10px; font-weight: 600; letter-spacing: 0.3px;
|
||
padding: 2px 6px; border-radius: 4px;
|
||
text-transform: uppercase;
|
||
}
|
||
.priority-high { background: rgba(255,59,48,0.1); color: var(--ios-red); }
|
||
.priority-medium { background: rgba(255,149,0,0.1); color: var(--ios-orange); }
|
||
.priority-low { background: rgba(120,120,128,0.1); color: var(--ios-label-2); }
|
||
|
||
/* ── Success state ── */
|
||
.success-hero {
|
||
text-align: center; padding: 32px 20px 24px;
|
||
}
|
||
.success-icon {
|
||
width: 80px; height: 80px; border-radius: 50%;
|
||
background: linear-gradient(135deg, var(--ios-green), #28a745);
|
||
margin: 0 auto 20px;
|
||
display: flex; align-items: center; justify-content: center;
|
||
font-size: 36px;
|
||
box-shadow: 0 8px 32px rgba(52,199,89,0.35);
|
||
animation: popIn 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||
}
|
||
.success-title {
|
||
font-size: 28px; font-weight: 700; letter-spacing: -0.8px;
|
||
margin-bottom: 8px;
|
||
}
|
||
.success-sub {
|
||
font-size: 15px; color: var(--ios-label-2); line-height: 1.5;
|
||
}
|
||
|
||
/* ── Stats row ── */
|
||
.stats-row {
|
||
display: grid; grid-template-columns: 1fr 1fr 1fr;
|
||
gap: 12px; margin-bottom: 16px;
|
||
}
|
||
.stat-card {
|
||
background: var(--ios-surface);
|
||
border-radius: var(--ios-corner-med);
|
||
padding: 14px;
|
||
text-align: center;
|
||
box-shadow: var(--ios-shadow);
|
||
}
|
||
.stat-value { font-size: 24px; font-weight: 700; letter-spacing: -0.5px; }
|
||
.stat-label { font-size: 11px; color: var(--ios-label-2); font-weight: 500; margin-top: 2px; }
|
||
|
||
/* ── Hidden states ── */
|
||
.step-view { display: none; }
|
||
.step-view.active { display: block; }
|
||
|
||
/* ── Animations ── */
|
||
@keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
|
||
@keyframes popIn { from { transform: scale(0.5); opacity: 0; } to { transform: scale(1); opacity: 1; } }
|
||
@keyframes spin { to { transform: rotate(360deg); } }
|
||
|
||
.fade-in { animation: fadeIn 0.4s ease forwards; }
|
||
|
||
/* ── Utility ── */
|
||
.text-center { text-align: center; }
|
||
.mt-8 { margin-top: 8px; }
|
||
.mt-12 { margin-top: 12px; }
|
||
.mt-16 { margin-top: 16px; }
|
||
.gap-8 { gap: 8px; }
|
||
.flex { display: flex; }
|
||
.flex-col { flex-direction: column; }
|
||
.items-center { align-items: center; }
|
||
.justify-between { justify-content: space-between; }
|
||
.w-full { width: 100%; }
|
||
.section-header {
|
||
font-size: 22px; font-weight: 700;
|
||
letter-spacing: -0.6px; margin-bottom: 4px;
|
||
}
|
||
.section-sub {
|
||
font-size: 14px; color: var(--ios-label-2); margin-bottom: 16px; line-height: 1.5;
|
||
}
|
||
.tag {
|
||
display: inline-flex; align-items: center;
|
||
font-size: 12px; font-weight: 600;
|
||
padding: 3px 10px; border-radius: 100px;
|
||
}
|
||
.tag-blue { background: rgba(0,122,255,0.12); color: var(--ios-blue); }
|
||
.tag-green { background: rgba(52,199,89,0.12); color: #1a7a30; }
|
||
.tag-orange { background: rgba(255,149,0,0.12); color: #cc7700; }
|
||
|
||
/* Divider */
|
||
.divider { height: 0.5px; background: var(--ios-separator); margin: 12px 0; }
|
||
</style>
|
||
<link rel="stylesheet" href="/_design-system/apple-native/tokens.css">
|
||
<link rel="stylesheet" href="/_design-system/apple-native/universal-fix.css">
|
||
<link rel="stylesheet" href="/_design-system/apple-native/typography.css">
|
||
|
||
|
||
<link rel="stylesheet" href="/_design-system/apple-native/components.css">
|
||
|
||
<style>
|
||
|
||
/* ── Ouroboros OS Layout Inject ── */
|
||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||
:root{
|
||
--bg:#F2F2F7;--bg2:#FFFFFF;--bg3:#E5E5EA;
|
||
--accent:#007AFF;--accent2:#0051D4;
|
||
--text:#000000;--text2:rgba(60,60,67,.8);--text3:rgba(60,60,67,.6);
|
||
--card:#FFFFFF;--border:rgba(60,60,67,.13);
|
||
--green:#34C759;--red:#FF3B30;--orange:#FF9500;
|
||
--blue:#007AFF;--purple:#AF52DE;--teal:#30B0C7;--gold:#C8763A;
|
||
--shadow:0 1px 3px rgba(0,0,0,.08),0 4px 16px rgba(0,0,0,.04);
|
||
}
|
||
@media(prefers-color-scheme:dark){:root{
|
||
--bg:#000;--bg2:#1C1C1E;--bg3:#2C2C2E;
|
||
--text:#fff;--text2:rgba(235,235,245,.8);--text3:rgba(235,235,245,.5);
|
||
--card:#1C1C1E;--border:rgba(255,255,255,.12);
|
||
--green:#30D158;--red:#FF453A;--orange:#FF9F0A;
|
||
--blue:#0A84FF;--purple:#BF5AF2;--shadow:0 2px 12px rgba(0,0,0,.4);
|
||
}}
|
||
html,body{height:100%;overflow:hidden}
|
||
body{font-family:-apple-system,'SF Pro Text',system-ui,sans-serif;-webkit-font-smoothing:antialiased;background:var(--bg);color:var(--text)}
|
||
|
||
/* ── Layout ── */
|
||
.os-layout{display:flex;height:100vh;overflow:hidden}
|
||
|
||
/* ── Sidebar ── */
|
||
.os-sidebar{width:240px;background:var(--bg2);border-right:.5px solid var(--border);display:flex;flex-direction:column;flex-shrink:0;overflow-y:auto;transition:transform .3s}
|
||
.os-sidebar-logo{padding:18px 18px 14px;border-bottom:.5px solid var(--border)}
|
||
.os-logo-brand{font-size:10px;font-weight:700;letter-spacing:.12em;color:var(--gold);text-transform:uppercase;margin-bottom:4px}
|
||
.os-logo-name{font-size:17px;font-weight:800;color:var(--text);letter-spacing:-.02em}
|
||
.os-logo-sub{font-size:11px;color:var(--text3);margin-top:2px}
|
||
.os-sidebar-back{padding:8px 14px;border-bottom:.5px solid var(--border)}
|
||
.os-sidebar-back a{color:var(--blue);font-size:13px;text-decoration:none;display:flex;align-items:center;gap:4px}
|
||
.os-sidebar-back a:hover{opacity:.7}
|
||
.os-sidebar-section{padding:12px 10px 4px}
|
||
.os-sidebar-label{font-size:10px;font-weight:700;letter-spacing:.1em;color:var(--text3);text-transform:uppercase;padding:0 8px;margin-bottom:6px}
|
||
.os-nav-item{display:flex;align-items:center;gap:8px;padding:9px 10px;border-radius:10px;cursor:pointer;color:var(--text2);font-size:13px;font-weight:500;transition:all .15s;border:none;background:none;width:100%;text-align:left;text-decoration:none}
|
||
.os-nav-item:hover{background:var(--bg3);color:var(--text)}
|
||
.os-nav-item.active{background:var(--blue);color:#fff}
|
||
.os-nav-icon{font-size:15px;width:20px;text-align:center}
|
||
.os-sidebar-footer{margin-top:auto;padding:14px;border-top:.5px solid var(--border);font-size:11px;color:var(--text3)}
|
||
|
||
/* ── Main + Topbar ── */
|
||
.os-main{flex:1;display:flex;flex-direction:column;overflow:hidden}
|
||
.os-topbar{background:rgba(242,242,247,.85);backdrop-filter:saturate(180%) blur(20px);-webkit-backdrop-filter:saturate(180%) blur(20px);border-bottom:.5px solid var(--border);padding:0 20px;height:52px;display:flex;align-items:center;justify-content:space-between;position:relative;z-index:100;flex-shrink:0}
|
||
@media(prefers-color-scheme:dark){.os-topbar{background:rgba(28,28,30,.85)}}
|
||
.os-topbar-title{font-size:15px;font-weight:700;color:var(--text)}
|
||
.os-topbar-breadcrumb{font-size:12px;color:var(--text3);display:flex;align-items:center;gap:6px}
|
||
.os-topbar-actions{display:flex;gap:8px;align-items:center}
|
||
.os-topbar-org{font-size:12px;color:var(--text3);background:var(--bg3);padding:3px 9px;border-radius:8px;font-weight:600}
|
||
.os-topbar-live{display:inline-flex;align-items:center;gap:5px;background:rgba(52,199,89,.12);border:.5px solid rgba(52,199,89,.3);border-radius:20px;padding:3px 10px;font-size:11px;font-weight:700;color:var(--green)}
|
||
.os-live-dot{width:6px;height:6px;border-radius:50%;background:var(--green);animation:osPulse 1.5s infinite}
|
||
@keyframes osPulse{0%,100%{opacity:1}50%{opacity:.3}}
|
||
|
||
/* ── Content area ── */
|
||
.os-content{flex:1;overflow-y:auto;min-height:0}
|
||
|
||
/* ── Mobile ── */
|
||
@media(max-width:768px){.os-sidebar{display:none}.os-layout{flex-direction:column}}
|
||
|
||
</style>
|
||
|
||
<style>
|
||
/* iOS Perfection Fix — hides duplicate nav, fixes stats, improves typography */
|
||
.nav-bar{display:none!important}
|
||
.large-title{font-size:28px!important;font-weight:700!important;letter-spacing:-.5px!important;margin-bottom:3px!important}
|
||
.large-subtitle{font-size:14px!important;color:rgba(60,60,67,.55)!important;font-weight:400!important}
|
||
.large-title-area,.large-title-section{padding:4px 20px 8px!important}
|
||
.mini-stat{background:#fff!important;border-radius:14px!important;padding:14px 12px!important;border:none!important;box-shadow:0 1px 3px rgba(0,0,0,.05),0 2px 8px rgba(0,0,0,.04)!important}
|
||
.mini-stat-value{font-size:26px!important;font-weight:800!important;letter-spacing:-.03em!important;color:#007AFF!important;display:block!important;margin-bottom:5px!important}
|
||
.mini-stat-label{font-size:11px!important;font-weight:500!important;color:rgba(60,60,67,.55)!important;display:block!important}
|
||
.stats-row{gap:10px!important;padding:8px 16px 12px!important}
|
||
.search-area{padding:4px 16px 8px!important}
|
||
.search-wrap{height:38px!important;border-radius:11px!important;background:rgba(120,120,128,.11)!important}
|
||
.section-title-text,.section-title{font-size:20px!important;font-weight:700!important;letter-spacing:-.3px!important}
|
||
.kpi-card{background:#fff!important;border-radius:16px!important;border:none!important;box-shadow:0 1px 3px rgba(0,0,0,.05),0 2px 8px rgba(0,0,0,.04)!important}
|
||
.kpi-value{font-weight:800!important;letter-spacing:-.03em!important}
|
||
@media(prefers-color-scheme:dark){
|
||
.mini-stat,.kpi-card{background:#1C1C1E!important}
|
||
.mini-stat-value{color:#0A84FF!important}
|
||
.large-title{color:#fff!important}
|
||
.large-subtitle{color:rgba(235,235,245,.55)!important}
|
||
}
|
||
</style>
|
||
|
||
<style id="ouroboros-apple-system">
|
||
/* OUROBOROS GLOBAL SYSTEM CSS — Apple iOS 18 baseline */
|
||
*, *::before, *::after { box-sizing: border-box; }
|
||
html { -webkit-text-size-adjust: 100%; }
|
||
body {
|
||
-webkit-font-smoothing: antialiased;
|
||
-moz-osx-font-smoothing: grayscale;
|
||
text-rendering: optimizeLegibility;
|
||
}
|
||
/* System font stack — SF Pro on Apple, Inter elsewhere */
|
||
body, input, select, textarea, button {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'SF Pro Display',
|
||
'Helvetica Neue', Arial, system-ui, sans-serif;
|
||
}
|
||
/* Fix any broken button appearances */
|
||
button { -webkit-appearance: none; appearance: none; cursor: pointer; }
|
||
select { -webkit-appearance: none; appearance: none; }
|
||
input[type="number"] { -moz-appearance: textfield; }
|
||
input[type="number"]::-webkit-inner-spin-button,
|
||
input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; }
|
||
/* Remove blue outline on focus (replace with subtle ring) */
|
||
:focus-visible {
|
||
outline: 2.5px solid rgba(0, 122, 255, 0.6);
|
||
outline-offset: 2px;
|
||
border-radius: 6px;
|
||
}
|
||
/* Language buttons must ALWAYS look like pills */
|
||
.lang-btn {
|
||
padding: 6px 12px !important;
|
||
border: 1.5px solid rgba(60,60,67,.15) !important;
|
||
border-radius: 20px !important;
|
||
background: #fff !important;
|
||
color: rgba(60,60,67,.6) !important;
|
||
font-size: 12px !important;
|
||
font-weight: 700 !important;
|
||
-webkit-appearance: none !important;
|
||
box-shadow: 0 1px 2px rgba(0,0,0,.06) !important;
|
||
}
|
||
.lang-btn.active {
|
||
background: #007AFF !important;
|
||
border-color: #007AFF !important;
|
||
color: #fff !important;
|
||
}
|
||
/* Tap targets minimum 44×44pt (iOS HIG requirement) */
|
||
@media (pointer: coarse) {
|
||
button, .btn-primary, .btn-ghost, .option-card, .lang-btn {
|
||
min-height: 44px;
|
||
}
|
||
}
|
||
</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(255,255,255,0.4)"] { color: rgba(255,255,255,0.72) !important; }
|
||
[style*="rgba(255,255,255,0.3)"] { color: rgba(255,255,255,0.72) !important; }
|
||
[style*="opacity: 0.4"] { opacity: 0.72 !important; }
|
||
[style*="opacity: 0.3"] { opacity: 0.72 !important; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="os-layout">
|
||
<!-- ── Sidebar ── -->
|
||
<nav class="os-sidebar">
|
||
<div class="os-sidebar-logo">
|
||
<div class="os-logo-brand">AAMOS · Ouroboros</div>
|
||
<div class="os-logo-name">Bolagsonboarding</div>
|
||
<div class="os-logo-sub">AAMOS Ouroboros</div>
|
||
</div>
|
||
<div class="os-sidebar-back">
|
||
<a href="/ouroboros/">← Ouroboros Hub</a>
|
||
</div>
|
||
<div class="os-sidebar-section">
|
||
<div class="os-sidebar-label">Navigation</div>
|
||
<a class="os-nav-item active" href="#">
|
||
<span class="os-nav-icon">🏛️</span> Bolagsonboarding
|
||
</a>
|
||
<a class="os-nav-item" href="/ouroboros/cockpit.html">
|
||
<span class="os-nav-icon">🚀</span> Cockpit
|
||
</a>
|
||
<a class="os-nav-item" href="/ouroboros/finance-hub.html">
|
||
<span class="os-nav-icon">💰</span> Finance Hub
|
||
</a>
|
||
<a class="os-nav-item" href="/ouroboros/compliance-hub.html">
|
||
<span class="os-nav-icon">⚖️</span> Compliance Hub
|
||
</a>
|
||
<a class="os-nav-item" href="/ouroboros/analytics-hub.html">
|
||
<span class="os-nav-icon">📊</span> Analytics Hub
|
||
</a>
|
||
</div>
|
||
<div class="os-sidebar-footer">
|
||
<div>AAMOS Ouroboros OS</div>
|
||
<div style="margin-top:4px;opacity:.6">wavult-group</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<!-- ── Main ── -->
|
||
<div class="os-main">
|
||
<div class="os-topbar">
|
||
<div class="os-topbar-breadcrumb">
|
||
<a href="/ouroboros/" style="color:var(--blue);text-decoration:none">Ouroboros</a>
|
||
<span>›</span>
|
||
<span class="os-topbar-title">Bolagsonboarding</span>
|
||
</div>
|
||
<div class="os-topbar-actions">
|
||
<span class="os-topbar-live"><span class="os-live-dot"></span>LIVE</span>
|
||
<span class="os-topbar-org">wavult-group</span>
|
||
</div>
|
||
</div>
|
||
<div class="os-content">
|
||
|
||
|
||
<!-- Navigation -->
|
||
<nav class="nav-bar">
|
||
<a href="/ouroboros" class="nav-logo">← AAMOS</a>
|
||
<span class="nav-title">Bolagsonboarding</span>
|
||
<span class="nav-badge">BETA</span>
|
||
</nav>
|
||
|
||
<!-- Hero -->
|
||
<div class="hero">
|
||
<div class="hero-eyebrow">AAMOS · OUROBOROS</div>
|
||
<h1 class="hero-title">Starta ett bolag<br>på 15 minuter.</h1>
|
||
<p class="hero-sub">Automatisk provisioning, bankintegration, skatteregistrering och checklista — på direkten.</p>
|
||
<div class="hero-badge">⚡ Powered by AI · Bolagsverket · GECL</div>
|
||
</div>
|
||
|
||
<!-- Main content -->
|
||
<div class="container">
|
||
|
||
<!-- Step indicator -->
|
||
<div class="steps-bar">
|
||
<div class="step-dot active" id="dot1">1</div>
|
||
<div class="step-line" id="line1"></div>
|
||
<div class="step-dot" id="dot2">2</div>
|
||
<div class="step-line" id="line2"></div>
|
||
<div class="step-dot" id="dot3">3</div>
|
||
<div class="step-line" id="line3"></div>
|
||
<div class="step-dot" id="dot4">4</div>
|
||
</div>
|
||
|
||
<!-- ── STEP 1: Org-nummer ── -->
|
||
<div class="step-view active" id="step1">
|
||
<div class="section-header">Vilket bolag gäller det?</div>
|
||
<p class="section-sub">Ange organisationsnummer — vi hämtar resten från Bolagsverket automatiskt.</p>
|
||
|
||
<div class="card fade-in">
|
||
<div class="card-header">
|
||
<div class="card-icon blue">🏢</div>
|
||
<div>
|
||
<div class="card-title">Organisationsnummer</div>
|
||
<div class="card-sub">10 siffror, format XXXXXX-XXXX</div>
|
||
</div>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="field-group">
|
||
<div class="field-label">
|
||
Org-nummer <span class="field-required">*</span>
|
||
</div>
|
||
<input
|
||
type="text"
|
||
id="orgNrInput"
|
||
class="field-input"
|
||
placeholder="556000-0000"
|
||
maxlength="11"
|
||
autocomplete="off"
|
||
autocorrect="off"
|
||
spellcheck="false"
|
||
>
|
||
<div class="field-hint" id="orgNrHint">Ange bolagets registrerade organisationsnummer</div>
|
||
</div>
|
||
|
||
<!-- Company preview -->
|
||
<div class="company-preview" id="companyPreview">
|
||
<div class="flex items-center justify-between">
|
||
<div class="company-preview-name" id="previewName">—</div>
|
||
<div class="company-preview-badge" id="previewStatus">✓ FOUND</div>
|
||
</div>
|
||
<div class="company-preview-row" id="previewAddress">—</div>
|
||
<div class="company-preview-row">
|
||
<span id="previewLegalForm">—</span>
|
||
·
|
||
<span id="previewSni">—</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mt-16">
|
||
<button class="btn-primary" id="step1Next" disabled onclick="goStep2()">
|
||
Fortsätt →
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── STEP 2: Välj plan & kontakt ── -->
|
||
<div class="step-view" id="step2">
|
||
<div class="section-header">Välj plan</div>
|
||
<p class="section-sub">Alla planer inkluderar automatisk skatteregistrering och bankintegration.</p>
|
||
|
||
<div class="card fade-in">
|
||
<div class="card-header">
|
||
<div class="card-icon purple">✨</div>
|
||
<div>
|
||
<div class="card-title">Abonnemangsplan</div>
|
||
<div class="card-sub">Kan ändras när som helst</div>
|
||
</div>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="plans-grid">
|
||
<div class="plan-card" onclick="selectPlan('starter', this)" data-plan="starter">
|
||
<div class="plan-check">✓</div>
|
||
<div class="plan-emoji">🌱</div>
|
||
<div class="plan-name">Starter</div>
|
||
<div class="plan-price">990 kr/mån</div>
|
||
</div>
|
||
<div class="plan-card selected" onclick="selectPlan('professional', this)" data-plan="professional">
|
||
<div class="plan-check">✓</div>
|
||
<div class="plan-emoji">🚀</div>
|
||
<div class="plan-name">Professional</div>
|
||
<div class="plan-price">2 990 kr/mån</div>
|
||
</div>
|
||
<div class="plan-card" onclick="selectPlan('enterprise', this)" data-plan="enterprise">
|
||
<div class="plan-check">✓</div>
|
||
<div class="plan-emoji">🏛️</div>
|
||
<div class="plan-name">Enterprise</div>
|
||
<div class="plan-price">9 900 kr/mån</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="divider mt-12"></div>
|
||
|
||
<div class="field-group mt-12">
|
||
<div class="field-label">Admin-namn <span class="field-required">*</span></div>
|
||
<input type="text" id="adminName" class="field-input" placeholder="Anna Andersson" autocomplete="name">
|
||
</div>
|
||
|
||
<div class="field-group">
|
||
<div class="field-label">E-postadress <span class="field-required">*</span></div>
|
||
<input type="email" id="adminEmail" class="field-input" placeholder="anna@bolaget.se" autocomplete="email">
|
||
<div class="field-hint">Välkomstmail och dashboard-länk skickas hit</div>
|
||
</div>
|
||
|
||
<div class="field-group">
|
||
<div class="field-label">Lösenord <span class="field-required">*</span></div>
|
||
<input type="password" id="adminPassword" class="field-input" placeholder="Minst 8 tecken" autocomplete="new-password" minlength="8">
|
||
<div class="field-hint">Minst 8 tecken — används för att logga in i dashboard</div>
|
||
</div>
|
||
|
||
<div class="flex gap-8 mt-16">
|
||
<button class="btn-secondary" onclick="goStep1()" style="flex:1">← Tillbaka</button>
|
||
<button class="btn-primary" onclick="goStep3()" style="flex:2">Starta provisioning →</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── STEP 3: Provisioning progress ── -->
|
||
<div class="step-view" id="step3">
|
||
<div class="section-header">Sätter upp bolaget…</div>
|
||
<p class="section-sub" id="step3Sub">AAMOS konfigurerar allt automatiskt — brukar ta under 15 sekunder.</p>
|
||
|
||
<div class="card fade-in">
|
||
<div class="card-header">
|
||
<div class="card-icon blue">⚙️</div>
|
||
<div>
|
||
<div class="card-title">Automatisk provisioning</div>
|
||
<div class="card-sub" id="progressLabel">Initierar…</div>
|
||
</div>
|
||
</div>
|
||
<div class="card-body">
|
||
<div class="progress-wrap">
|
||
<div class="progress-bar" id="progressBar" style="width:0%"></div>
|
||
</div>
|
||
<div class="field-hint text-center" id="progressPct">0% klart</div>
|
||
|
||
<div class="step-items mt-12" id="provSteps">
|
||
<!-- Injected by JS -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── STEP 4: Klar! Dashboard + Checklista ── -->
|
||
<div class="step-view" id="step4">
|
||
|
||
<div class="card fade-in">
|
||
<div class="success-hero">
|
||
<div class="success-icon">✅</div>
|
||
<div class="success-title" id="successTitle">Klart!</div>
|
||
<div class="success-sub" id="successSub">Bolaget är nu konfigurerat i AAMOS.<br>Välkomstmail skickat.</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Stats -->
|
||
<div class="stats-row fade-in">
|
||
<div class="stat-card">
|
||
<div class="stat-value" id="statSecs">—</div>
|
||
<div class="stat-label">Sekunder</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-value" id="statModules">—</div>
|
||
<div class="stat-label">Moduler</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-value" id="statItems">—</div>
|
||
<div class="stat-label">Åtgärdspunkter</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Onboarding checklist -->
|
||
<div class="card fade-in" id="checklistCard">
|
||
<div class="card-header">
|
||
<div class="card-icon green">📋</div>
|
||
<div>
|
||
<div class="card-title">Onboarding-checklista</div>
|
||
<div class="card-sub" id="checklistMeta">Laddar…</div>
|
||
</div>
|
||
</div>
|
||
<div class="card-body" id="checklistBody">
|
||
<!-- Injected by JS -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Dashboard link -->
|
||
<div class="card fade-in mt-16">
|
||
<div class="card-body text-center">
|
||
<div style="font-size:32px; margin-bottom:12px;">🔐</div>
|
||
<div style="font-size:17px; font-weight:600; margin-bottom:6px;">Klart att köra!</div>
|
||
<div style="font-size:14px; color: var(--ios-label-2); margin-bottom:12px;">
|
||
Ditt AAMOS-konto är aktiverat med alla moduler konfigurerade.
|
||
</div>
|
||
<div id="credentialBox" style="background:var(--ios-fill);border-radius:var(--ios-corner-med);padding:14px 16px;margin-bottom:16px;text-align:left;font-size:14px;line-height:1.7;"></div>
|
||
<a id="dashboardBtn" href="/ouroboros/app/login.html" class="btn-primary" style="display:inline-block; text-decoration:none; padding: 14px 24px;">
|
||
Klart! Logga in här →
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Restart -->
|
||
<div class="text-center mt-16">
|
||
<button class="btn-secondary" onclick="restart()">Onboarda ett till bolag</button>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /container -->
|
||
|
||
<script>
|
||
// ── State ────────────────────────────────────────────────────────────────────
|
||
let state = {
|
||
orgNr: null,
|
||
companyData: null,
|
||
plan: 'professional',
|
||
adminEmail: null,
|
||
adminName: null,
|
||
adminPassword: null,
|
||
jobId: null,
|
||
jobData: null,
|
||
};
|
||
|
||
const PROV_STEPS = [
|
||
{ label: 'Hämtar bolagsdata från Bolagsverket', icon: '🏛️' },
|
||
{ label: 'Skapar organisation i Stammregister', icon: '📋' },
|
||
{ label: 'Konfigurerar standardroller', icon: '👥' },
|
||
{ label: 'Tillämpar SNI-baserat regelset', icon: '⚖️' },
|
||
{ label: 'Aktiverar moduler', icon: '🔧' },
|
||
{ label: 'Genererar onboarding-checklista', icon: '✅' },
|
||
{ label: 'Skickar välkomstmail', icon: '📧' },
|
||
{ label: 'GECL-signerar provisioning-record', icon: '🔐' },
|
||
];
|
||
|
||
// ── DOM helpers ──────────────────────────────────────────────────────────────
|
||
const $ = id => document.getElementById(id);
|
||
const setActive = (n) => {
|
||
[1,2,3,4].forEach(i => {
|
||
$(`step${i}`).classList.toggle('active', i === n);
|
||
const dot = $(`dot${i}`);
|
||
dot.classList.toggle('active', i === n);
|
||
dot.classList.toggle('done', i < n);
|
||
if (i < 4) $(`line${i}`).classList.toggle('done', i < n);
|
||
});
|
||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||
};
|
||
|
||
// ── Org-nr input handling ────────────────────────────────────────────────────
|
||
let lookupTimeout;
|
||
|
||
$('orgNrInput').addEventListener('input', function() {
|
||
clearTimeout(lookupTimeout);
|
||
let v = this.value.replace(/[^\d-]/g, '');
|
||
// Auto-format
|
||
const digits = v.replace(/-/g, '');
|
||
if (digits.length > 6 && !v.includes('-')) {
|
||
v = digits.slice(0,6) + '-' + digits.slice(6,10);
|
||
}
|
||
this.value = v;
|
||
|
||
$('step1Next').disabled = true;
|
||
$('companyPreview').classList.remove('show');
|
||
|
||
const clean = v.replace(/[-\s]/g, '');
|
||
if (clean.length === 10) {
|
||
this.classList.remove('valid', 'error');
|
||
$('orgNrHint').textContent = 'Slår upp bolag…';
|
||
lookupTimeout = setTimeout(() => lookupOrgNr(clean), 500);
|
||
} else {
|
||
$('orgNrHint').textContent = 'Ange 10 siffror (XXXXXX-XXXX)';
|
||
}
|
||
});
|
||
|
||
async function lookupOrgNr(orgNr) {
|
||
try {
|
||
const r = await fetch(`/api/company/lookup/${orgNr}`);
|
||
const d = await r.json();
|
||
state.orgNr = orgNr;
|
||
state.companyData = d;
|
||
|
||
if (d.status === 'FOUND' || d.status === 'PARTIAL') {
|
||
$('orgNrInput').classList.add('valid');
|
||
$('orgNrHint').textContent = d.status === 'FOUND'
|
||
? '✅ Bolag verifierat via Bolagsverket'
|
||
: '⚠️ Delvis data — ange resterande uppgifter manuellt';
|
||
|
||
$('previewName').textContent = d.name || `Org.nr ${d.org_nr_formatted}`;
|
||
$('previewStatus').textContent = d.status === 'FOUND' ? '✓ VERIFIERAD' : '~ PARTIAL';
|
||
$('previewStatus').style.background = d.status === 'FOUND' ? 'rgba(52,199,89,0.15)' : 'rgba(255,149,0,0.15)';
|
||
$('previewAddress').textContent = [d.address?.street, d.address?.city].filter(Boolean).join(', ') || 'Adress saknas';
|
||
$('previewLegalForm').textContent = d.legal_form || 'Aktiebolag';
|
||
$('previewSni').textContent = d.sni_category || 'Okänd bransch';
|
||
$('companyPreview').classList.add('show');
|
||
$('step1Next').disabled = false;
|
||
} else {
|
||
$('orgNrInput').classList.add('error');
|
||
$('orgNrHint').textContent = `❌ ${d.error || 'Bolaget hittades inte'}`;
|
||
}
|
||
} catch (err) {
|
||
$('orgNrHint').textContent = '⚠️ Kunde inte nå Bolagsverket — fortsätt ändå';
|
||
$('step1Next').disabled = false;
|
||
state.orgNr = orgNr;
|
||
}
|
||
}
|
||
|
||
// ── Plan selector ────────────────────────────────────────────────────────────
|
||
function selectPlan(plan, el) {
|
||
state.plan = plan;
|
||
document.querySelectorAll('.plan-card').forEach(c => c.classList.remove('selected'));
|
||
el.classList.add('selected');
|
||
}
|
||
|
||
// ── Navigation ───────────────────────────────────────────────────────────────
|
||
function goStep1() { setActive(1); }
|
||
function goStep2() { setActive(2); }
|
||
|
||
function goStep3() {
|
||
const name = $('adminName').value.trim();
|
||
const email = $('adminEmail').value.trim();
|
||
const password = $('adminPassword').value;
|
||
if (!name || !email) {
|
||
alert('Ange namn och e-postadress.');
|
||
return;
|
||
}
|
||
if (!password || password.length < 8) {
|
||
alert('Lösenordet måste vara minst 8 tecken.');
|
||
return;
|
||
}
|
||
state.adminName = name;
|
||
state.adminEmail = email;
|
||
state.adminPassword = password;
|
||
setActive(3);
|
||
startProvisioning();
|
||
}
|
||
|
||
// ── Provisioning ─────────────────────────────────────────────────────────────
|
||
function renderProvSteps(doneCount) {
|
||
const container = $('provSteps');
|
||
container.innerHTML = PROV_STEPS.map((s, i) => {
|
||
const isDone = i < doneCount;
|
||
const isRunning = i === doneCount;
|
||
let iconClass = 'pending', iconContent = '○';
|
||
if (isDone) { iconClass = 'done'; iconContent = '✓'; }
|
||
if (isRunning) { iconClass = 'running'; iconContent = '<span class="spinner">⟳</span>'; }
|
||
return `
|
||
<div class="step-item">
|
||
<div class="step-status-icon ${iconClass}">${iconContent}</div>
|
||
<div class="step-item-text">
|
||
<div class="step-item-label">${s.label}</div>
|
||
${isRunning ? '<div class="step-item-sub">Pågår…</div>' :
|
||
isDone ? '<div class="step-item-sub" style="color:var(--ios-green)">Klart</div>' : ''}
|
||
</div>
|
||
<div style="font-size:18px">${s.icon}</div>
|
||
</div>`;
|
||
}).join('');
|
||
}
|
||
|
||
async function startProvisioning() {
|
||
renderProvSteps(0);
|
||
updateProgress(0);
|
||
|
||
try {
|
||
const resp = await fetch('/api/company/provision', {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
org_nr: state.orgNr,
|
||
plan: state.plan,
|
||
admin_email: state.adminEmail,
|
||
admin_name: state.adminName,
|
||
admin_password: state.adminPassword,
|
||
}),
|
||
});
|
||
const job = await resp.json();
|
||
state.jobId = job.job_id;
|
||
pollJob(state.jobId);
|
||
} catch (err) {
|
||
$('step3Sub').textContent = `Fel: ${err.message}`;
|
||
}
|
||
}
|
||
|
||
function updateProgress(pct) {
|
||
$('progressBar').style.width = pct + '%';
|
||
$('progressPct').textContent = pct + '% klart';
|
||
$('progressLabel').textContent = pct < 100 ? `${pct}% — pågår…` : 'Klart!';
|
||
}
|
||
|
||
let pollInterval;
|
||
function pollJob(jobId) {
|
||
let attempts = 0;
|
||
pollInterval = setInterval(async () => {
|
||
attempts++;
|
||
if (attempts > 120) { clearInterval(pollInterval); return; } // 2 min timeout
|
||
|
||
try {
|
||
const r = await fetch(`/api/company/provisioning-status/${jobId}`);
|
||
const d = await r.json();
|
||
|
||
if (d.status === 'running') {
|
||
const pct = d.progress_pct || 0;
|
||
updateProgress(pct);
|
||
renderProvSteps(d.steps_done || 0);
|
||
} else if (d.status === 'completed') {
|
||
clearInterval(pollInterval);
|
||
state.jobData = d;
|
||
updateProgress(100);
|
||
renderProvSteps(8);
|
||
setTimeout(() => goStep4(d), 800);
|
||
} else if (d.status === 'failed') {
|
||
clearInterval(pollInterval);
|
||
$('step3Sub').textContent = `Fel: ${d.error || 'Okänt fel'}`;
|
||
}
|
||
} catch (_) {}
|
||
}, 1000);
|
||
}
|
||
|
||
// ── Step 4: Result ────────────────────────────────────────────────────────────
|
||
function goStep4(data) {
|
||
setActive(4);
|
||
|
||
$('successTitle').textContent = `${data.company_name || 'Bolaget'} är redo! 🎉`;
|
||
$('successSub').innerHTML = `Konfigurerat på <strong>${data.elapsed_seconds}s</strong>. Välkomstmail skickat till ${data.welcome_email?.to || state.adminEmail}.`;
|
||
|
||
$('statSecs').textContent = data.elapsed_seconds + 's';
|
||
$('statModules').textContent = Object.keys(data.module_status || {}).length || '—';
|
||
$('statItems').textContent = data.checklist?.total_items || '—';
|
||
|
||
// Visa inloggningsuppgifter
|
||
const orgId = data.org_id || '—';
|
||
const maskPwd = state.adminPassword
|
||
? '•'.repeat(Math.min(state.adminPassword.length, 12))
|
||
: '••••••••';
|
||
$('credentialBox').innerHTML = [
|
||
'<div style="margin-bottom:8px;font-weight:700;font-size:12px;color:var(--ios-label-2);text-transform:uppercase;letter-spacing:.5px">Inloggningsuppgifter</div>',
|
||
`<div>🏢 Org-ID: <strong style="font-family:monospace;font-size:13px">${orgId}</strong></div>`,
|
||
`<div>📧 E-post: <strong>${state.adminEmail}</strong></div>`,
|
||
`<div>🔑 Lösenord: <strong>${maskPwd}</strong> (ditt valda lösenord)</div>`,
|
||
].join('');
|
||
|
||
// Sätt login-URL
|
||
$('dashboardBtn').href = data.dashboard_url || '/ouroboros/app/login.html';
|
||
|
||
if (data.checklist) {
|
||
renderChecklist(data.checklist);
|
||
}
|
||
}
|
||
|
||
function renderChecklist(cl) {
|
||
$('checklistMeta').textContent = `${cl.total_items} åtgärder · Beräknad tid ~${cl.estimated_hours}h`;
|
||
|
||
const body = $('checklistBody');
|
||
body.innerHTML = cl.categories.map(cat => `
|
||
<div class="checklist-category">
|
||
<div class="checklist-cat-header">
|
||
<span>${cat.icon}</span> ${cat.category}
|
||
<span class="priority-badge priority-${cat.priority}">${cat.priority}</span>
|
||
</div>
|
||
${cat.items.map(item => `
|
||
<div class="checklist-item ${item.status === 'auto' ? 'auto' : ''}" onclick="toggleCheck(this)">
|
||
<div class="checklist-checkbox">${item.status === 'auto' ? '<span style="color:#fff;font-size:11px;">✓</span>' : ''}</div>
|
||
<div style="flex:1">
|
||
<div class="checklist-title">${item.title}</div>
|
||
<div class="checklist-meta">
|
||
<span class="checklist-time">⏱ ${item.estimated_min} min</span>
|
||
${item.note ? `<span class="tag tag-blue">${item.note}</span>` : ''}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`).join('')}
|
||
</div>
|
||
`).join('<div class="divider"></div>');
|
||
}
|
||
|
||
function toggleCheck(el) {
|
||
if (el.classList.contains('auto')) return;
|
||
el.classList.toggle('completed');
|
||
const cb = el.querySelector('.checklist-checkbox');
|
||
cb.innerHTML = el.classList.contains('completed')
|
||
? '<span style="color:#fff;font-size:11px;">✓</span>'
|
||
: '';
|
||
}
|
||
|
||
function restart() {
|
||
clearInterval(pollInterval);
|
||
state = { orgNr: null, companyData: null, plan: 'professional', adminEmail: null, adminName: null, adminPassword: null, jobId: null, jobData: null };
|
||
$('orgNrInput').value = '';
|
||
$('orgNrInput').className = 'field-input';
|
||
$('companyPreview').classList.remove('show');
|
||
$('orgNrHint').textContent = 'Ange bolagets registrerade organisationsnummer';
|
||
$('step1Next').disabled = true;
|
||
$('adminName').value = '';
|
||
$('adminEmail').value = '';
|
||
setActive(1);
|
||
}
|
||
|
||
// Init: pre-select professional plan
|
||
document.querySelector('[data-plan="professional"]').classList.add('selected');
|
||
|
||
// Handle ?org= querystring
|
||
const params = new URLSearchParams(location.search);
|
||
if (params.get('org')) {
|
||
// Already provisioned — show success hint
|
||
const orgHint = document.createElement('div');
|
||
orgHint.style.cssText = 'background:rgba(52,199,89,0.1);border:1px solid rgba(52,199,89,0.3);border-radius:12px;padding:16px;margin:16px 0;text-align:center;font-size:14px;color:#1a7a30;font-weight:500;';
|
||
orgHint.innerHTML = `✅ Org <code style="font-family:monospace">${params.get('org')}</code> är redan provisionerat i AMOS.`;
|
||
document.querySelector('.container').prepend(orgHint);
|
||
}
|
||
</script>
|
||
|
||
</div><!-- /os-content -->
|
||
</div><!-- /os-main -->
|
||
</div><!-- /os-layout -->
|
||
</body>
|
||
</html>
|