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
702 lines
28 KiB
HTML
702 lines
28 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="sv" data-theme="light">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<link rel="manifest" href="/ouroboros/manifest.json">
|
||
<meta name="mobile-web-app-capable" content="yes">
|
||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||
<meta name="apple-mobile-web-app-title" content="Ouroboros">
|
||
<meta name="theme-color" content="#007AFF">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||
<title>AAMOS Ouroboros — Dashboard</title>
|
||
<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">
|
||
<link rel="stylesheet" href="/_design-system/apple-native/ios-core.css">
|
||
<script src="/_design-system/apple-native/interactions.js" defer></script>
|
||
<meta name="apple-native-compliance" content="pass">
|
||
<style>
|
||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||
html { -webkit-text-size-adjust: 100%; }
|
||
|
||
body {
|
||
font-family: var(--font-text, -apple-system, BlinkMacSystemFont, 'SF Pro Text', system-ui, sans-serif);
|
||
background: var(--grouped-background-primary, #F2F2F7);
|
||
color: var(--label-primary, #000);
|
||
-webkit-font-smoothing: antialiased;
|
||
min-height: 100dvh;
|
||
padding-bottom: calc(49px + env(safe-area-inset-bottom, 16px) + 16px);
|
||
}
|
||
|
||
/* ── Nav Bar ── */
|
||
.nav-bar {
|
||
position: sticky;
|
||
top: 0;
|
||
z-index: 100;
|
||
padding-top: env(safe-area-inset-top, 0px);
|
||
background: rgba(248,248,248,0.94);
|
||
backdrop-filter: blur(40px) saturate(180%);
|
||
-webkit-backdrop-filter: blur(40px) saturate(180%);
|
||
border-bottom: 0.33px solid rgba(60,60,67,0.18);
|
||
}
|
||
.nav-bar-inner {
|
||
height: 44px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 8px;
|
||
}
|
||
.nav-bar-back {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
font-size: 17px;
|
||
color: var(--ios-blue, #007AFF);
|
||
text-decoration: none;
|
||
padding: 0 8px;
|
||
min-width: 44px;
|
||
min-height: 44px;
|
||
}
|
||
.nav-bar-title {
|
||
font-size: 17px;
|
||
font-weight: 600;
|
||
color: var(--label-primary, #000);
|
||
position: absolute;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
white-space: nowrap;
|
||
}
|
||
.nav-bar-action {
|
||
width: 44px;
|
||
height: 44px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: var(--ios-blue, #007AFF);
|
||
background: none;
|
||
border: none;
|
||
cursor: pointer;
|
||
font-size: 22px;
|
||
}
|
||
|
||
/* ── Large Title ── */
|
||
.large-title-section {
|
||
padding: 8px 20px 0;
|
||
}
|
||
.large-title {
|
||
font-family: var(--font-display, -apple-system);
|
||
font-size: 34px;
|
||
font-weight: 700;
|
||
letter-spacing: -0.025em;
|
||
color: var(--label-primary, #000);
|
||
margin-bottom: 4px;
|
||
}
|
||
.large-subtitle {
|
||
font-size: 14px;
|
||
color: var(--label-secondary, rgba(60,60,67,0.6));
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
/* ── Stats row ── */
|
||
.stats-row {
|
||
display: grid;
|
||
grid-template-columns: repeat(3, 1fr);
|
||
gap: 10px;
|
||
padding: 0 16px;
|
||
margin-bottom: 24px;
|
||
}
|
||
.stat-card {
|
||
background: var(--grouped-background-secondary, #fff);
|
||
border-radius: 12px;
|
||
padding: 14px 12px 12px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
.stat-icon { font-size: 20px; margin-bottom: 4px; }
|
||
.stat-value {
|
||
font-family: var(--font-display, -apple-system);
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
letter-spacing: -0.02em;
|
||
color: var(--label-primary, #000);
|
||
}
|
||
.stat-label {
|
||
font-size: 12px;
|
||
color: var(--label-secondary, rgba(60,60,67,0.6));
|
||
}
|
||
|
||
/* ── Section header ── */
|
||
.section-header {
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: space-between;
|
||
padding: 0 20px;
|
||
margin-bottom: 10px;
|
||
}
|
||
.section-title {
|
||
font-size: 22px;
|
||
font-weight: 700;
|
||
letter-spacing: -0.015em;
|
||
color: var(--label-primary, #000);
|
||
}
|
||
.section-action {
|
||
font-size: 15px;
|
||
color: var(--ios-blue, #007AFF);
|
||
text-decoration: none;
|
||
}
|
||
|
||
/* ── Module cards ── */
|
||
.modules-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 10px;
|
||
padding: 0 16px;
|
||
margin-bottom: 28px;
|
||
}
|
||
.module-card {
|
||
background: var(--grouped-background-secondary, #fff);
|
||
border-radius: 14px;
|
||
padding: 16px 14px 14px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
text-decoration: none;
|
||
color: inherit;
|
||
cursor: pointer;
|
||
transition: opacity 0.15s, transform 0.1s;
|
||
-webkit-tap-highlight-color: transparent;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.module-card:active { opacity: 0.7; transform: scale(0.97); }
|
||
.module-card-icon { font-size: 26px; margin-bottom: 6px; }
|
||
.module-card-name {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
color: var(--label-primary, #000);
|
||
}
|
||
.module-card-desc {
|
||
font-size: 12px;
|
||
color: var(--label-secondary, rgba(60,60,67,0.6));
|
||
}
|
||
.module-card-chevron {
|
||
position: absolute;
|
||
right: 12px;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
color: var(--label-quaternary, rgba(60,60,67,0.18));
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* ── Quick Actions ── */
|
||
.quick-actions {
|
||
margin: 0 16px 28px;
|
||
}
|
||
.quick-action-list {
|
||
background: var(--grouped-background-secondary, #fff);
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
}
|
||
.quick-action-row {
|
||
display: flex;
|
||
align-items: center;
|
||
min-height: 52px;
|
||
padding: 0 16px;
|
||
gap: 14px;
|
||
border-bottom: 0.33px solid var(--separator, rgba(60,60,67,0.36));
|
||
cursor: pointer;
|
||
text-decoration: none;
|
||
color: inherit;
|
||
-webkit-tap-highlight-color: transparent;
|
||
transition: background 0.1s;
|
||
}
|
||
.quick-action-row:last-child { border-bottom: none; }
|
||
.quick-action-row:active { background: transparent; }
|
||
.qa-icon {
|
||
width: 34px;
|
||
height: 34px;
|
||
border-radius: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 18px;
|
||
flex-shrink: 0;
|
||
}
|
||
.qa-text { flex: 1; }
|
||
.qa-title { font-size: 15px; font-weight: 500; color: var(--label-primary, #000); }
|
||
.qa-subtitle { font-size: 12px; color: var(--label-secondary, rgba(60,60,67,0.6)); }
|
||
.qa-chevron {
|
||
color: var(--label-quaternary, rgba(60,60,67,0.18));
|
||
font-size: 13px;
|
||
}
|
||
|
||
/* ── Plan badge ── */
|
||
.plan-badge {
|
||
display: inline-block;
|
||
font-size: 10px;
|
||
font-weight: 700;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.08em;
|
||
padding: 2px 7px;
|
||
border-radius: 4px;
|
||
background: rgba(88,86,214,0.12);
|
||
color: var(--ios-indigo, #5856D6);
|
||
margin-left: 8px;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
/* ── Logout / settings ── */
|
||
.settings-section {
|
||
margin: 0 16px 24px;
|
||
}
|
||
.settings-list {
|
||
background: var(--grouped-background-secondary, #fff);
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
}
|
||
.settings-row {
|
||
display: flex;
|
||
align-items: center;
|
||
min-height: 44px;
|
||
padding: 0 16px;
|
||
gap: 12px;
|
||
border-bottom: 0.33px solid var(--separator, rgba(60,60,67,0.36));
|
||
cursor: pointer;
|
||
-webkit-tap-highlight-color: transparent;
|
||
}
|
||
.settings-row:last-child { border-bottom: none; }
|
||
.settings-row.danger { color: var(--ios-red, #FF3B30); }
|
||
|
||
/* ── Loading / error ── */
|
||
.loading-state {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-height: 60vh;
|
||
gap: 16px;
|
||
}
|
||
.spinner {
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 50%;
|
||
border: 3px solid var(--ios-gray5, #E5E5EA);
|
||
border-top: 3px solid var(--ios-blue, #007AFF);
|
||
animation: spin 0.8s linear infinite;
|
||
}
|
||
@keyframes spin { to { transform: rotate(360deg); } }
|
||
</style>
|
||
|
||
<style>
|
||
/* iOS Perfection Fix */
|
||
.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}
|
||
.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}.large-title{color:#fff!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>
|
||
|
||
<header class="nav-bar">
|
||
<div class="nav-bar-inner" style="position:relative;">
|
||
<a href="/ouroboros/" class="nav-bar-back">
|
||
<svg viewBox="0 0 10 17" width="10" height="17" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><polyline points="9 1 1 8.5 9 16"/></svg>
|
||
Ouroboros
|
||
</a>
|
||
<span class="nav-bar-title" id="nav-org-name">Dashboard</span>
|
||
<button class="nav-bar-action" onclick="toggleSettings()" aria-label="Inställningar">⚙️</button>
|
||
</div>
|
||
</header>
|
||
|
||
<main id="main-content">
|
||
<div class="loading-state">
|
||
<div class="spinner"></div>
|
||
<span style="color:var(--label-secondary);font-size:15px;">Laddar…</span>
|
||
</div>
|
||
</main>
|
||
|
||
<script>
|
||
const MODULE_INFO = {
|
||
identity: { icon: '🔐', desc: 'Org & användare', path: '/ouroboros/identity/' },
|
||
finance: { icon: '💰', desc: 'Ekonomi & budget', path: '/ouroboros/finance/' },
|
||
people: { icon: '👥', desc: 'Anställda & HR', path: '/ouroboros/people/' },
|
||
governance: { icon: '⚖️', desc: 'Beslut & styrning', path: '/ouroboros/governance/' },
|
||
compliance: { icon: '✅', desc: 'ISO/GDPR', path: '/ouroboros/compliance/' },
|
||
analytics: { icon: '📊', desc: 'Business Intelligence', path: '/ouroboros/analytics/' },
|
||
projects: { icon: '📋', desc: 'Projekthantering', path: '/ouroboros/projects/' },
|
||
okr: { icon: '🎯', desc: 'Mål & nyckelresultat', path: '/ouroboros/okr/' },
|
||
meetings: { icon: '🗓️', desc: 'Möteshantering', path: '/ouroboros/meetings/' },
|
||
legal: { icon: '📜', desc: 'Avtal & juridik', path: '/ouroboros/legal/' },
|
||
academy: { icon: '🎓', desc: 'Lärande & utv.', path: '/ouroboros/academy/' },
|
||
operations: { icon: '⚙️', desc: 'Arbetsflöden & SLA', path: '/ouroboros/operations/' },
|
||
connect: { icon: '💬', desc: 'Kommunikation', path: '/ouroboros/connect/' },
|
||
decisions: { icon: '🧭', desc: 'Beslutsuppföljning', path: '/ouroboros/decisions/' },
|
||
commerce: { icon: '🛒', desc: 'Order & lager', path: '/ouroboros/commerce/' },
|
||
procurement: { icon: '📦', desc: 'Inköp & leverantörer', path: '/ouroboros/procurement/' },
|
||
marketing: { icon: '📣', desc: 'Kampanjer & outreach', path: '/ouroboros/marketing/' },
|
||
media: { icon: '🎬', desc: 'Media & innehåll', path: '/ouroboros/media/' },
|
||
lean: { icon: '🔄', desc: 'Ständig förbättring', path: '/ouroboros/lean/' },
|
||
quality: { icon: '⭐', desc: 'QA & standarder', path: '/ouroboros/quality/' },
|
||
systemaudit: { icon: '🔍', desc: 'Revision & spårning', path: '/ouroboros/systemaudit/' },
|
||
insurance: { icon: '🛡️', desc: 'Försäkringshantering', path: '/ouroboros/insurance/' },
|
||
travel: { icon: '✈️', desc: 'Resor & utgifter', path: '/ouroboros/travel/' },
|
||
wellbeing: { icon: '💚', desc: 'Medarbetarwellbeing', path: '/ouroboros/wellbeing/' },
|
||
// ── URBAN OS — built May 2026 ──
|
||
'urban-os': { icon: '🏗️', desc: 'Urban OS Command Center', path: '/ouroboros/urban-os.html' },
|
||
city: { icon: '🏙️', desc: 'City Intelligence Platform', path: '/ouroboros/city.html' },
|
||
waste: { icon: '🔴', desc: 'Resurssvinn & Svarta Hål', path: '/ouroboros/waste.html' },
|
||
lifecycle: { icon: '♻️', desc: 'Affärslivscykel', path: '/ouroboros/lifecycle.html' },
|
||
valuation: { icon: '💎', desc: 'Market Mirror', path: '/ouroboros/app/' },
|
||
// ── NEW MODULES — built May 2026 ──
|
||
simulation: { icon: '📊', desc: 'Enterprise Simulation', path: '/ouroboros/simulation.html' },
|
||
sims: { icon: '🎮', desc: 'Virtuell verksamhet', path: '/ouroboros/sims.html' },
|
||
banks: { icon: '🏦', desc: 'Bankintegration (20k+)', path: '/ouroboros/banks.html' },
|
||
credit: { icon: '💳', desc: 'Kreditansökan', path: '/ouroboros/credit.html' },
|
||
postal: { icon: '✉️', desc: 'Brev & paket', path: '/ouroboros/postal.html' },
|
||
certification: { icon: '🏅', desc: 'TÜV/DEKRA/DNV/RISE', path: '/ouroboros/certification.html' },
|
||
'audit-firms': { icon: '📋', desc: 'PwC/EY/Deloitte/KPMG', path: '/ouroboros/audit-partners.html' },
|
||
memory: { icon: '🧬', desc: 'Corporate Memory', path: '/ouroboros/memory.html' },
|
||
treasury: { icon: '💰', desc: 'Treasury Engine', path: '/ouroboros/banks.html' },
|
||
};
|
||
|
||
let session = null;
|
||
let dashData = null;
|
||
|
||
// ── Cookie helper ─────────────────────────────────────────────────────────
|
||
function getCookie(name) {
|
||
const match = document.cookie.match(new RegExp('(?:^|; )' + name.replace(/[.*+?^${}()|[\]\\]/g,'\\$&') + '=([^;]*)'));
|
||
return match ? decodeURIComponent(match[1]) : null;
|
||
}
|
||
|
||
async function init() {
|
||
const raw = localStorage.getItem('ouroboros_session');
|
||
const wavultToken = getCookie('wavult_token') || localStorage.getItem('amos_token');
|
||
|
||
if (!raw && wavultToken) {
|
||
// Har JWT-cookie men ingen session i localStorage — hämta via /api/auth/me
|
||
try {
|
||
const meRes = await fetch('/api/auth/me', {
|
||
headers: { 'Authorization': 'Bearer ' + wavultToken }
|
||
});
|
||
if (meRes.ok) {
|
||
const me = await meRes.json();
|
||
session = {
|
||
token: wavultToken,
|
||
org_id: me.org_id || me.organization_id,
|
||
org_name: me.org_name || me.organization_name || me.company_name || 'Din organisation',
|
||
user_name: me.name || me.display_name || me.email,
|
||
user_email: me.email,
|
||
active_modules: me.active_modules || me.modules || [],
|
||
};
|
||
localStorage.setItem('ouroboros_session', JSON.stringify(session));
|
||
} else {
|
||
window.location.href = '/ouroboros/app/login.html';
|
||
return;
|
||
}
|
||
} catch(e) {
|
||
window.location.href = '/ouroboros/app/login.html';
|
||
return;
|
||
}
|
||
} else if (!raw) {
|
||
window.location.href = '/ouroboros/app/login.html';
|
||
return;
|
||
} else {
|
||
try {
|
||
session = JSON.parse(raw);
|
||
if (!session.org_id) throw new Error('No org_id');
|
||
// Prefer wavult_token cookie if available
|
||
if (wavultToken) session.token = wavultToken;
|
||
} catch(e) {
|
||
window.location.href = '/ouroboros/app/login.html';
|
||
return;
|
||
}
|
||
}
|
||
|
||
document.getElementById('nav-org-name').textContent = session.org_name || 'Dashboard';
|
||
|
||
try {
|
||
const headers = session.token ? { 'Authorization': 'Bearer ' + session.token } : {};
|
||
const res = await fetch(`/api/v1/organizations/${session.org_id}/dashboard`, { headers });
|
||
dashData = await res.json();
|
||
} catch(e) {
|
||
dashData = null;
|
||
}
|
||
|
||
renderDashboard();
|
||
}
|
||
|
||
function renderDashboard() {
|
||
const modules = dashData?.active_modules || session.active_modules || [];
|
||
const stats = dashData?.stats || {};
|
||
const orgName = dashData?.org_name || session.org_name || 'Din organisation';
|
||
const plan = dashData?.plan || 'trial';
|
||
const userName = session.user_name || 'Admin';
|
||
|
||
const activeCount = modules.length;
|
||
const employeeCount = stats.employees ?? '—';
|
||
const userCount = stats.users ?? '—';
|
||
|
||
document.getElementById('main-content').innerHTML = `
|
||
<div class="large-title-section">
|
||
<div class="large-title">${orgName} <span class="plan-badge">${plan.toUpperCase()}</span></div>
|
||
<div class="large-subtitle">Inloggad som ${userName}</div>
|
||
</div>
|
||
|
||
<!-- Launch Readiness Banner -->
|
||
<div onclick="window.location.href='/ouroboros/launch-readiness.html'" style="background:linear-gradient(135deg,rgba(0,122,255,.06),rgba(88,86,214,.04));border:1.5px solid rgba(0,122,255,.12);border-radius:14px;padding:14px 16px;margin-bottom:14px;cursor:pointer;display:flex;align-items:center;gap:12px;">
|
||
<div style="font-size:24px">🚀</div>
|
||
<div style="flex:1;min-width:0">
|
||
<div style="font-size:14px;font-weight:700;margin-bottom:2px">Lanseringsberedskap</div>
|
||
<div style="font-size:12px;color:rgba(60,60,67,.55)">Kolla av allt som behövs inför lansering</div>
|
||
</div>
|
||
<div style="font-size:11px;font-weight:700;padding:4px 9px;border-radius:7px;background:rgba(255,59,48,.1);color:#FF3B30;white-space:nowrap" id="launch-badge">Se status →</div>
|
||
</div>
|
||
|
||
<div class="stats-row">
|
||
<div class="stat-card">
|
||
<div class="stat-icon">👥</div>
|
||
<div class="stat-value">${employeeCount}</div>
|
||
<div class="stat-label">Anställda</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-icon">🧩</div>
|
||
<div class="stat-value">${activeCount}</div>
|
||
<div class="stat-label">Aktiva moduler</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-icon">✅</div>
|
||
<div class="stat-value">100%</div>
|
||
<div class="stat-label">Driftstatus</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section-header" style="margin-bottom:10px;">
|
||
<span class="section-title">Aktiva moduler</span>
|
||
</div>
|
||
<div class="modules-grid">
|
||
${modules.map(id => {
|
||
const info = MODULE_INFO[id] || { icon: '🔲', desc: id, path: '#' };
|
||
const name = id.charAt(0).toUpperCase() + id.slice(1);
|
||
return `
|
||
<a href="${info.path}" class="module-card">
|
||
<div class="module-card-icon">${info.icon}</div>
|
||
<div class="module-card-name">${name}</div>
|
||
<div class="module-card-desc">${info.desc}</div>
|
||
<span class="module-card-chevron">›</span>
|
||
</a>
|
||
`;
|
||
}).join('')}
|
||
</div>
|
||
|
||
<div class="section-header">
|
||
<span class="section-title">Quick Actions</span>
|
||
</div>
|
||
<div class="quick-actions">
|
||
<div class="quick-action-list">
|
||
<a href="/ouroboros/people/" class="quick-action-row">
|
||
<div class="qa-icon" style="background:rgba(0,122,255,0.1)">👤</div>
|
||
<div class="qa-text">
|
||
<div class="qa-title">Lägg till anställd</div>
|
||
<div class="qa-subtitle">People-modulen</div>
|
||
</div>
|
||
<span class="qa-chevron">›</span>
|
||
</a>
|
||
<a href="/ouroboros/projects/" class="quick-action-row">
|
||
<div class="qa-icon" style="background:rgba(88,86,214,0.1)">📋</div>
|
||
<div class="qa-text">
|
||
<div class="qa-title">Skapa projekt</div>
|
||
<div class="qa-subtitle">Projects-modulen</div>
|
||
</div>
|
||
<span class="qa-chevron">›</span>
|
||
</a>
|
||
<a href="/ouroboros/legal/" class="quick-action-row">
|
||
<div class="qa-icon" style="background:rgba(255,149,0,0.1)">📜</div>
|
||
<div class="qa-text">
|
||
<div class="qa-title">Lägg till avtal</div>
|
||
<div class="qa-subtitle">Legal-modulen</div>
|
||
</div>
|
||
<span class="qa-chevron">›</span>
|
||
</a>
|
||
<a href="/ouroboros/simulation.html" class="quick-action-row">
|
||
<div class="qa-icon" style="background:rgba(48,209,88,0.1)">📊</div>
|
||
<div class="qa-text">
|
||
<div class="qa-title">Kör simulering</div>
|
||
<div class="qa-subtitle">Enterprise Simulation Engine</div>
|
||
</div>
|
||
<span class="qa-chevron">›</span>
|
||
</a>
|
||
<a href="/ouroboros/credit.html" class="quick-action-row">
|
||
<div class="qa-icon" style="background:rgba(255,149,10,0.1)">💳</div>
|
||
<div class="qa-text">
|
||
<div class="qa-title">Kreditansökan</div>
|
||
<div class="qa-subtitle">19 långivare, 12 produkter</div>
|
||
</div>
|
||
<span class="qa-chevron">›</span>
|
||
</a>
|
||
<a href="/ouroboros/insurance.html" class="quick-action-row">
|
||
<div class="qa-icon" style="background:rgba(255,45,85,0.1)">🛡️</div>
|
||
<div class="qa-text">
|
||
<div class="qa-title">Försäkringsprofil</div>
|
||
<div class="qa-subtitle">Reaktiv riskbedömning</div>
|
||
</div>
|
||
<span class="qa-chevron">›</span>
|
||
</a>
|
||
<a href="/ouroboros/sims.html" class="quick-action-row">
|
||
<div class="qa-icon" style="background:rgba(88,86,214,0.1)">🎮</div>
|
||
<div class="qa-text">
|
||
<div class="qa-title">Simuleringsläge</div>
|
||
<div class="qa-subtitle">Kör företaget virtuellt</div>
|
||
</div>
|
||
<span class="qa-chevron">›</span>
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="section-header">
|
||
<span class="section-title">Konto</span>
|
||
</div>
|
||
<div class="settings-section">
|
||
<div class="settings-list">
|
||
<div class="settings-row">
|
||
<span style="flex:1;font-size:15px;">Organisation</span>
|
||
<span style="color:var(--label-secondary);font-size:15px;">${orgName}</span>
|
||
</div>
|
||
<div class="settings-row">
|
||
<span style="flex:1;font-size:15px;">E-post</span>
|
||
<span style="color:var(--label-secondary);font-size:15px;">${session.user_email || '—'}</span>
|
||
</div>
|
||
<div class="settings-row" id="installRow" onclick="installPWA()" style="display:none">
|
||
<span style="flex:1;font-size:15px;">📲 Installera som app</span>
|
||
<span>›</span>
|
||
</div>
|
||
<div class="settings-row danger" onclick="logout()">
|
||
<span style="flex:1;font-size:15px;">Logga ut</span>
|
||
<span>›</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
function toggleSettings() {
|
||
// Simple org info in an alert
|
||
if (session) {
|
||
alert(`Organisation: ${session.org_name}\nOrg-ID: ${session.org_id}\nAnvändare: ${session.user_name}\nE-post: ${session.user_email}`);
|
||
}
|
||
}
|
||
|
||
function logout() {
|
||
if (confirm('Logga ut från ' + (session?.org_name || 'Ouroboros') + '?')) {
|
||
localStorage.removeItem('ouroboros_session');
|
||
// Clear wavult_token cookie
|
||
document.cookie = 'wavult_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; SameSite=Lax';
|
||
window.location.href = '/ouroboros/app/login.html';
|
||
}
|
||
}
|
||
|
||
init();
|
||
|
||
// PWA Install
|
||
let deferredPrompt;
|
||
window.addEventListener('beforeinstallprompt', (e) => {
|
||
e.preventDefault();
|
||
deferredPrompt = e;
|
||
const row = document.getElementById('installRow');
|
||
if (row) row.style.display = 'flex';
|
||
});
|
||
async function installPWA() {
|
||
if (!deferredPrompt) {
|
||
alert('Öppna i Chrome/Edge och tryck på ⋮ → Installera AAMOS Ouroboros');
|
||
return;
|
||
}
|
||
deferredPrompt.prompt();
|
||
const { outcome } = await deferredPrompt.userChoice;
|
||
deferredPrompt = null;
|
||
if (outcome === 'accepted') {
|
||
document.getElementById('installRow').style.display = 'none';
|
||
}
|
||
}
|
||
window.addEventListener('appinstalled', () => {
|
||
const row = document.getElementById('installRow');
|
||
if (row) row.style.display = 'none';
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|