OSVauco/static/opax.html

618 lines
24 KiB
HTML

<!DOCTYPE html>
<html lang="no">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OPAX — Vauco Operator Hub</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;500;600&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet">
<style>
:root {
--bg: #0a0908;
--surface: #111010;
--surface2: #161514;
--border: rgba(255,255,255,0.06);
--border2: rgba(255,255,255,0.10);
--text: #dedad5;
--muted: #6a6864;
--faint: #3a3835;
--primary: #4f98a3;
--primary2: #3a7a85;
--green: #4a9e6a;
--amber: #c4873a;
--red: #9e4a4a;
--mono: 'JetBrains Mono', monospace;
--sans: 'Inter', sans-serif;
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
font-family: var(--mono);
background: var(--bg);
color: var(--text);
min-height: 100vh;
display: grid;
grid-template-columns: 220px 1fr;
grid-template-rows: auto 1fr;
}
/* ── TOPBAR ── */
.topbar {
grid-column: 1 / -1;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
height: 48px;
border-bottom: 1px solid var(--border);
background: var(--bg);
position: sticky;
top: 0;
z-index: 100;
}
.topbar-brand {
display: flex;
align-items: center;
gap: 10px;
}
.topbar-logo { color: var(--primary); }
.topbar-name {
font-size: 13px;
font-weight: 600;
letter-spacing: 0.12em;
color: var(--text);
}
.topbar-sep { color: var(--faint); font-size: 12px; margin: 0 4px; }
.topbar-sub {
font-size: 11px;
color: var(--muted);
letter-spacing: 0.05em;
}
.topbar-right {
display: flex;
align-items: center;
gap: 16px;
}
.status-dot {
display: flex;
align-items: center;
gap: 6px;
font-size: 11px;
color: var(--muted);
}
.dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--green);
box-shadow: 0 0 6px var(--green);
}
.dot.amber { background: var(--amber); box-shadow: 0 0 6px var(--amber); }
.topbar-id { font-size: 11px; color: var(--faint); }
/* ── SIDEBAR ── */
.sidebar {
grid-row: 2;
border-right: 1px solid var(--border);
padding: 20px 0;
display: flex;
flex-direction: column;
gap: 4px;
position: sticky;
top: 48px;
height: calc(100vh - 48px);
overflow-y: auto;
}
.sidebar-label {
font-size: 9px;
letter-spacing: 0.15em;
color: var(--faint);
text-transform: uppercase;
padding: 8px 12px 4px;
}
.nav-item {
display: flex;
align-items: center;
gap: 10px;
padding: 7px 12px;
border-radius: 5px;
font-size: 12px;
color: var(--muted);
cursor: pointer;
transition: all 0.15s;
text-decoration: none;
border: none;
background: none;
width: 100%;
text-align: left;
}
.nav-item:hover { color: var(--text); background: var(--surface2); }
.nav-item.active { color: var(--primary); background: rgba(79,152,163,0.08); }
.nav-item svg { flex-shrink: 0; }
.nav-badge {
margin-left: auto;
font-size: 9px;
padding: 1px 5px;
border-radius: 3px;
background: var(--surface2);
color: var(--muted);
border: 1px solid var(--border);
}
.nav-badge.live { background: rgba(74,158,106,0.12); color: var(--green); border-color: rgba(74,158,106,0.2); }
.nav-badge.soon { background: rgba(196,135,58,0.1); color: var(--amber); border-color: rgba(196,135,58,0.2); }
.sidebar-footer {
margin-top: auto;
padding: 12px;
border-top: 1px solid var(--border);
}
.agent-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 6px;
padding: 10px 12px;
}
.agent-name { font-size: 11px; color: var(--primary); margin-bottom: 2px; }
.agent-role { font-size: 10px; color: var(--muted); }
/* ── MAIN ── */
.main {
grid-row: 2;
padding: 28px 32px;
overflow-y: auto;
max-width: 960px;
}
.page-header { margin-bottom: 28px; }
.page-title {
font-size: 14px;
font-weight: 600;
color: var(--text);
letter-spacing: 0.05em;
margin-bottom: 4px;
}
.page-desc {
font-size: 11px;
color: var(--muted);
font-family: var(--sans);
}
.section-label {
font-size: 9px;
letter-spacing: 0.15em;
color: var(--faint);
text-transform: uppercase;
margin-bottom: 12px;
}
.module-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 12px;
margin-bottom: 32px;
}
.module-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 8px;
padding: 18px;
transition: border-color 0.2s, background 0.2s;
cursor: default;
position: relative;
overflow: hidden;
}
.module-card.live { cursor: pointer; }
.module-card.live:hover { border-color: var(--border2); background: var(--surface2); }
.module-card.placeholder { opacity: 0.45; }
.module-card::before {
content: '';
position: absolute;
top: 0; left: 0;
width: 2px;
height: 100%;
background: var(--faint);
}
.module-card.live::before { background: var(--green); }
.module-card.placeholder::before { background: var(--faint); }
.module-top {
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-bottom: 12px;
}
.module-icon {
width: 32px;
height: 32px;
border-radius: 6px;
background: var(--surface2);
border: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: center;
color: var(--primary);
}
.module-card.placeholder .module-icon { color: var(--muted); }
.module-status {
font-size: 9px;
padding: 2px 7px;
border-radius: 3px;
letter-spacing: 0.08em;
}
.module-status.live { background: rgba(74,158,106,0.12); color: var(--green); border: 1px solid rgba(74,158,106,0.2); }
.module-status.planned { background: rgba(58,56,53,0.4); color: var(--muted); border: 1px solid var(--border); }
.module-status.building { background: rgba(196,135,58,0.10); color: var(--amber); border: 1px solid rgba(196,135,58,0.2); }
.module-name { font-size: 13px; font-weight: 600; color: var(--text); margin-bottom: 4px; }
.module-desc { font-size: 11px; color: var(--muted); font-family: var(--sans); line-height: 1.5; margin-bottom: 14px; }
.module-meta {
display: flex;
gap: 12px;
font-size: 10px;
color: var(--faint);
}
.module-meta span { display: flex; align-items: center; gap: 4px; }
.module-link {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 11px;
color: var(--primary);
text-decoration: none;
margin-top: 12px;
padding: 5px 10px;
border: 1px solid rgba(79,152,163,0.25);
border-radius: 4px;
transition: all 0.15s;
}
.module-link:hover { background: rgba(79,152,163,0.08); border-color: rgba(79,152,163,0.4); }
/* ── AGENT STATUS ── */
.agent-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 10px;
margin-bottom: 32px;
}
.agent-row {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 6px;
padding: 12px 14px;
display: flex;
align-items: center;
gap: 12px;
}
.agent-dot {
width: 7px;
height: 7px;
border-radius: 50%;
flex-shrink: 0;
}
.agent-dot.online { background: var(--green); box-shadow: 0 0 5px var(--green); }
.agent-dot.idle { background: var(--amber); }
.agent-dot.offline { background: var(--faint); }
.agent-info { flex: 1; min-width: 0; }
.agent-info .name { font-size: 11px; color: var(--text); margin-bottom: 2px; }
.agent-info .desc { font-size: 10px; color: var(--muted); font-family: var(--sans); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* ── PROVISION ── */
.provision-box {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 8px;
padding: 20px;
margin-bottom: 32px;
position: relative;
}
.provision-box::after {
content: '// FASE 2 — IKKE AKTIVERT';
position: absolute;
top: 12px; right: 14px;
font-size: 9px;
color: var(--faint);
letter-spacing: 0.08em;
}
.provision-title { font-size: 12px; font-weight: 600; color: var(--muted); margin-bottom: 14px; }
.provision-flow {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.flow-step {
background: var(--surface2);
border: 1px solid var(--border);
border-radius: 5px;
padding: 8px 14px;
font-size: 11px;
color: var(--muted);
white-space: nowrap;
}
.flow-arrow { color: var(--faint); font-size: 12px; }
/* ── INFRA ── */
.infra-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 10px;
margin-bottom: 32px;
}
.infra-item {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 6px;
padding: 12px;
}
.infra-label { font-size: 9px; color: var(--faint); letter-spacing: 0.1em; text-transform: uppercase; margin-bottom: 6px; }
.infra-value { font-size: 12px; color: var(--text); }
.infra-value.ok { color: var(--green); }
.infra-value.warn { color: var(--amber); }
/* ── FOOTER ── */
.page-footer {
font-size: 10px;
color: var(--faint);
padding-top: 20px;
border-top: 1px solid var(--border);
display: flex;
justify-content: space-between;
}
</style>
</head>
<body>
<header class="topbar">
<div class="topbar-brand">
<svg class="topbar-logo" width="18" height="18" viewBox="0 0 20 20" fill="none" aria-label="OPAX">
<polygon points="10,1 18,5.5 18,14.5 10,19 2,14.5 2,5.5" stroke="currentColor" stroke-width="1.4" fill="none"/>
<polygon points="10,5 15,7.8 15,13.2 10,16 5,13.2 5,7.8" stroke="currentColor" stroke-width="1" fill="currentColor" opacity="0.12"/>
<circle cx="10" cy="10" r="2" fill="currentColor"/>
</svg>
<span class="topbar-name">OPAX</span>
<span class="topbar-sep">/</span>
<span class="topbar-sub">vauco operator hub</span>
</div>
<div class="topbar-right">
<div class="status-dot">
<span class="dot"></span>
<span>cloud run live</span>
</div>
<div class="status-dot">
<span class="dot amber"></span>
<span>cg3 pending</span>
</div>
<div class="topbar-id">propane-will-491900-m5 · europe-west1</div>
</div>
</header>
<nav class="sidebar" aria-label="Navigasjon">
<div class="sidebar-label">Oversikt</div>
<a class="nav-item active" href="#produkter">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/></svg>
Produkter
<span class="nav-badge live">1 live</span>
</a>
<a class="nav-item" href="#agenter">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="8" r="4"/><path d="M4 20c0-4 3.6-7 8-7s8 3 8 7"/></svg>
Agenter
<span class="nav-badge live">Jason</span>
</a>
<a class="nav-item" href="#infrastruktur">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="2" width="20" height="8" rx="1"/><rect x="2" y="14" width="20" height="8" rx="1"/><path d="M6 6h.01M6 18h.01"/></svg>
Infrastruktur
</a>
<a class="nav-item" href="#provisjonering">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07A19.5 19.5 0 0 1 4.14 12 19.79 19.79 0 0 1 1 3.18 2 2 0 0 1 3 1h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L7.09 8.91a16 16 0 0 0 5.99 5.99l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z"/></svg>
Provisjonering
<span class="nav-badge soon">fase 2</span>
</a>
<div class="sidebar-label" style="margin-top:8px">Ressurser</div>
<a class="nav-item" href="https://console.cloud.google.com/run?project=propane-will-491900-m5" target="_blank" rel="noopener">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
GCP Console
</a>
<a class="nav-item" href="https://github.com/vauco-saas/OSVauco" target="_blank" rel="noopener">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"/></svg>
GitHub
</a>
<a class="nav-item" href="/static/billing-dashboard.html">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg>
CostGuard Dashboard
</a>
<div class="sidebar-footer">
<div class="agent-card">
<div class="agent-name">jason.vauger@vauco.no</div>
<div class="agent-role">billing.viewer · OPAX agent</div>
</div>
</div>
</nav>
<main class="main">
<div class="page-header">
<div class="page-title">operator hub</div>
<div class="page-desc">Vauco OS — intern plattform kun for Chris. Operate, Plan, Audit, Execute.</div>
</div>
<section id="produkter">
<div class="section-label">Produkter</div>
<div class="module-grid">
<div class="module-card live" onclick="window.location.href='/static/billing-dashboard.html'">
<div class="module-top">
<div class="module-icon">
<svg width="16" height="16" viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M10 2L17 6V14L10 18L3 14V6L10 2Z"/><circle cx="10" cy="10" r="2" fill="currentColor"/></svg>
</div>
<span class="module-status live">LIVE</span>
</div>
<div class="module-name">CostGuard</div>
<div class="module-desc">Sanntids GCP & AWS kostnadskontroll. Anomali-deteksjon, budsjett-alerts, historikk og CSV/PDF eksport.</div>
<div class="module-meta">
<span>
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
0 kunder
</span>
<span>costguard.oss.vauco.no</span>
</div>
<a class="module-link" href="/static/billing-dashboard.html">
Åpne dashboard
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
</a>
</div>
<div class="module-card placeholder">
<div class="module-top">
<div class="module-icon">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
</div>
<span class="module-status planned">PLANLAGT</span>
</div>
<div class="module-name">Threadstone</div>
<div class="module-desc">Kundeskreddersydd AI-salgsassistent. Threadstone-landing er live, app under utvikling.</div>
<div class="module-meta"><span>threadstone.vauco.no</span></div>
</div>
<div class="module-card placeholder">
<div class="module-top">
<div class="module-icon">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
</div>
<span class="module-status planned">FASE 3</span>
</div>
<div class="module-name">Module Builder</div>
<div class="module-desc">Bygg og deploy nye salgsmoduler visuelt direkte fra OPAX. Ingen terminal nødvendig.</div>
<div class="module-meta"><span>opax.vauco.no/build</span></div>
</div>
</div>
</section>
<section id="agenter">
<div class="section-label">Agenter</div>
<div class="agent-grid">
<div class="agent-row">
<span class="agent-dot online"></span>
<div class="agent-info">
<div class="name">Jason</div>
<div class="desc">billing.viewer · GCP kostnadsrapport</div>
</div>
</div>
<div class="agent-row">
<span class="agent-dot idle"></span>
<div class="agent-info">
<div class="name">OPAX Core</div>
<div class="desc">POST /run · orkestrering</div>
</div>
</div>
<div class="agent-row">
<span class="agent-dot offline"></span>
<div class="agent-info">
<div class="name">Provisioner</div>
<div class="desc">mail · magic link · fase 2</div>
</div>
</div>
<div class="agent-row">
<span class="agent-dot offline"></span>
<div class="agent-info">
<div class="name">Voice (OPAX)</div>
<div class="desc">Web Speech API · fase 2</div>
</div>
</div>
</div>
</section>
<section id="infrastruktur">
<div class="section-label">Infrastruktur</div>
<div class="infra-grid">
<div class="infra-item"><div class="infra-label">Cloud Run</div><div class="infra-value ok">osvauco-agent</div></div>
<div class="infra-item"><div class="infra-label">Region</div><div class="infra-value">europe-west1</div></div>
<div class="infra-item"><div class="infra-label">Project</div><div class="infra-value">propane-will-491900-m5</div></div>
<div class="infra-item"><div class="infra-label">opax.vauco.no</div><div class="infra-value ok">CNAME live</div></div>
<div class="infra-item"><div class="infra-label">costguard.oss.vauco.no</div><div class="infra-value warn">CG3 pending</div></div>
<div class="infra-item"><div class="infra-label">OAuth</div><div class="infra-value warn">redirect_uri fix</div></div>
<div class="infra-item"><div class="infra-label">Cloud Build</div><div class="infra-value ok">CI/CD aktiv</div></div>
<div class="infra-item"><div class="infra-label">Secret Manager</div><div class="infra-value ok">aktiv</div></div>
</div>
</section>
<section id="provisjonering">
<div class="section-label">Provisjonering</div>
<div class="provision-box">
<div class="provision-title">Kundeprovisjoneringsflyten — Voice → Modul → Mail</div>
<div class="provision-flow">
<div class="flow-step">🎤 Chris taler (AirPods)</div>
<span class="flow-arrow"></span>
<div class="flow-step">Web Speech API</div>
<span class="flow-arrow"></span>
<div class="flow-step">Jason analyserer</div>
<span class="flow-arrow"></span>
<div class="flow-step">Modul genereres</div>
<span class="flow-arrow"></span>
<div class="flow-step">Chris godkjenner</div>
<span class="flow-arrow"></span>
<div class="flow-step">Magic link → kunde</div>
</div>
<div style="margin-top:14px; font-size:10px; color:var(--faint); font-family:var(--sans)">
Avhenger av: Fase 2 (SendGrid/Resend) · POST /opax/generate · kunde-subdomain provisjonering via Terraform
</div>
</div>
</section>
<footer class="page-footer">
<span>OPAX · Vauco OS · kun for Chris</span>
<span>propane-will-491900-m5 · europe-west1 · 2026</span>
</footer>
</main>
</body>
</html>