523 lines
22 KiB
HTML
523 lines
22 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="no">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="robots" content="noindex, nofollow">
|
||
<title>CostGuard — Kom i gang</title>
|
||
<style>
|
||
:root{
|
||
--bg:#0a0a0f;--bg-2:#11131a;--bg-3:#161922;
|
||
--border:rgba(255,255,255,0.08);--border-2:rgba(255,255,255,0.14);
|
||
--text:#e8eaf0;--text-dim:#8b909c;--text-faint:#5a5f6b;
|
||
--accent:#7c5cff;--accent-2:#00d4a8;
|
||
--green:#3ecf8e;--danger:#ff5c7c;--amber:#ffb547;
|
||
--radius:12px;
|
||
--sans:-apple-system,BlinkMacSystemFont,'Inter',system-ui,sans-serif;
|
||
--mono:'SF Mono',ui-monospace,'JetBrains Mono',Menlo,monospace;
|
||
}
|
||
*{margin:0;padding:0;box-sizing:border-box}
|
||
html,body{min-height:100%;background:var(--bg);color:var(--text);font-family:var(--sans);-webkit-font-smoothing:antialiased}
|
||
button{font-family:inherit;cursor:pointer;border:none;background:none;color:inherit}
|
||
input,select,textarea{font-family:inherit}
|
||
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
|
||
@keyframes spin{to{transform:rotate(360deg)}}
|
||
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
|
||
|
||
/* TOP */
|
||
.topbar{
|
||
display:flex;align-items:center;gap:14px;
|
||
padding:14px 32px;border-bottom:1px solid var(--border);
|
||
background:rgba(10,10,15,.95);backdrop-filter:blur(16px);
|
||
position:sticky;top:0;z-index:10;
|
||
}
|
||
.logo{
|
||
width:32px;height:32px;border-radius:10px;
|
||
background:linear-gradient(135deg,var(--accent),var(--accent-2));
|
||
display:flex;align-items:center;justify-content:center;
|
||
font-weight:900;font-size:13px;color:#050509;
|
||
}
|
||
.brand{font-size:15px;font-weight:800;letter-spacing:-.02em}
|
||
.brand-sub{font-size:11px;color:var(--text-faint)}
|
||
|
||
/* LAYOUT */
|
||
.page{max-width:680px;margin:0 auto;padding:48px 24px 80px;}
|
||
|
||
/* STEP INDICATOR */
|
||
.step-bar{
|
||
display:flex;align-items:center;gap:0;margin-bottom:48px;
|
||
}
|
||
.step-item{
|
||
display:flex;flex-direction:column;align-items:center;flex:1;
|
||
position:relative;
|
||
}
|
||
.step-item:not(:last-child)::after{
|
||
content:'';position:absolute;top:15px;left:50%;right:-50%;
|
||
height:1px;background:var(--border-2);
|
||
z-index:0;
|
||
}
|
||
.step-item.done::after{background:var(--accent)}
|
||
.step-num{
|
||
width:30px;height:30px;border-radius:50%;
|
||
border:1px solid var(--border-2);background:var(--bg-2);
|
||
display:flex;align-items:center;justify-content:center;
|
||
font-size:12px;font-weight:700;color:var(--text-faint);
|
||
position:relative;z-index:1;
|
||
transition:background .2s,border-color .2s,color .2s;
|
||
}
|
||
.step-item.active .step-num{
|
||
background:var(--accent);border-color:var(--accent);color:#fff;
|
||
box-shadow:0 0 0 4px rgba(124,92,255,.2);
|
||
}
|
||
.step-item.done .step-num{
|
||
background:var(--green);border-color:var(--green);color:#050509;
|
||
}
|
||
.step-label{
|
||
font-size:10px;color:var(--text-faint);margin-top:6px;
|
||
font-weight:600;text-transform:uppercase;letter-spacing:.05em;
|
||
white-space:nowrap;
|
||
}
|
||
.step-item.active .step-label,.step-item.done .step-label{color:var(--text-dim)}
|
||
|
||
/* STEP PANELS */
|
||
.step-panel{display:none;animation:fadeIn .22s ease}
|
||
.step-panel.visible{display:block}
|
||
|
||
.step-title{font-size:24px;font-weight:800;letter-spacing:-.02em;margin-bottom:6px}
|
||
.step-sub{font-size:14px;color:var(--text-dim);margin-bottom:32px;line-height:1.6}
|
||
|
||
/* FORM */
|
||
.field{display:flex;flex-direction:column;gap:6px;margin-bottom:20px}
|
||
.label{font-size:12px;font-weight:700;color:var(--text-dim);text-transform:uppercase;letter-spacing:.05em}
|
||
.label span{font-weight:400;color:var(--text-faint);text-transform:none;letter-spacing:0}
|
||
.input{
|
||
padding:11px 14px;border-radius:10px;
|
||
background:var(--bg-2);border:1px solid var(--border-2);
|
||
color:var(--text);font-size:14px;
|
||
outline:none;transition:border-color .15s,box-shadow .15s;
|
||
width:100%;
|
||
}
|
||
.input:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(124,92,255,.15)}
|
||
.input.err{border-color:var(--danger);box-shadow:0 0 0 3px rgba(255,92,124,.12)}
|
||
.field-err{font-size:11px;color:var(--danger);margin-top:-4px}
|
||
|
||
/* CHANNEL CARDS */
|
||
.ch-grid{display:flex;flex-direction:column;gap:10px;margin-bottom:28px}
|
||
.ch-card{
|
||
display:flex;align-items:flex-start;gap:14px;
|
||
padding:16px 18px;border-radius:var(--radius);
|
||
background:var(--bg-2);border:1px solid var(--border);
|
||
cursor:pointer;transition:border-color .15s,background .15s;
|
||
}
|
||
.ch-card.selected{
|
||
border-color:var(--accent);background:rgba(124,92,255,.07);
|
||
}
|
||
.ch-card .ch-ico{font-size:22px;flex-shrink:0;margin-top:1px}
|
||
.ch-card .ch-info{flex:1}
|
||
.ch-card .ch-name{font-size:14px;font-weight:700}
|
||
.ch-card .ch-desc{font-size:12px;color:var(--text-dim);margin-top:3px}
|
||
.ch-card .ch-tier{
|
||
font-size:10px;font-weight:700;padding:2px 7px;border-radius:99px;
|
||
background:var(--bg-3);color:var(--text-faint);border:1px solid var(--border);
|
||
margin-top:6px;display:inline-block;
|
||
}
|
||
.ch-check{
|
||
width:20px;height:20px;border-radius:50%;
|
||
border:2px solid var(--border-2);flex-shrink:0;
|
||
display:flex;align-items:center;justify-content:center;
|
||
font-size:11px;margin-top:2px;
|
||
transition:background .15s,border-color .15s;
|
||
}
|
||
.ch-card.selected .ch-check{
|
||
background:var(--accent);border-color:var(--accent);color:#fff;
|
||
}
|
||
|
||
/* CHANNEL DETAIL FIELDS */
|
||
.ch-detail{display:none;margin-top:12px;padding-top:12px;border-top:1px solid var(--border)}
|
||
.ch-card.selected .ch-detail{display:block}
|
||
|
||
/* BUTTONS */
|
||
.btn-row{display:flex;gap:12px;align-items:center;margin-top:32px}
|
||
.btn-primary{
|
||
padding:11px 24px;border-radius:99px;
|
||
background:linear-gradient(135deg,var(--accent),var(--accent-2));
|
||
color:#050509;font-weight:700;font-size:14px;
|
||
display:inline-flex;align-items:center;gap:8px;
|
||
box-shadow:0 8px 24px rgba(0,0,0,.4);
|
||
transition:opacity .15s;
|
||
}
|
||
.btn-primary:disabled{opacity:.4;cursor:not-allowed}
|
||
.btn-secondary{
|
||
padding:10px 20px;border-radius:99px;
|
||
border:1px solid var(--border-2);background:var(--bg-2);
|
||
color:var(--text-dim);font-size:14px;font-weight:600;
|
||
transition:background .14s,color .14s;
|
||
}
|
||
.btn-secondary:hover{background:var(--bg-3);color:var(--text)}
|
||
.spinner{
|
||
width:14px;height:14px;border-radius:50%;
|
||
border:2px solid rgba(0,0,0,.2);border-top-color:#050509;
|
||
animation:spin .6s linear infinite;
|
||
}
|
||
|
||
/* TEST RESULT */
|
||
.test-result{
|
||
margin-top:14px;padding:12px 16px;border-radius:10px;
|
||
font-size:13px;font-weight:500;
|
||
display:none;
|
||
}
|
||
.test-result.ok{background:rgba(62,207,142,.1);border:1px solid rgba(62,207,142,.25);color:var(--green)}
|
||
.test-result.err{background:rgba(255,92,124,.1);border:1px solid rgba(255,92,124,.25);color:var(--danger)}
|
||
.test-result.show{display:block}
|
||
|
||
/* SUMMARY / DONE */
|
||
.summary-card{
|
||
background:var(--bg-2);border:1px solid var(--border);
|
||
border-radius:var(--radius);padding:24px;
|
||
margin-bottom:24px;
|
||
}
|
||
.summary-row{display:flex;justify-content:space-between;gap:12px;padding:8px 0;border-bottom:1px solid var(--border);font-size:13px}
|
||
.summary-row:last-child{border-bottom:none}
|
||
.summary-key{color:var(--text-dim)}
|
||
.summary-val{font-weight:600;text-align:right;max-width:60%;word-break:break-all}
|
||
|
||
.done-check{
|
||
width:72px;height:72px;border-radius:50%;
|
||
background:rgba(62,207,142,.12);border:2px solid rgba(62,207,142,.3);
|
||
display:flex;align-items:center;justify-content:center;
|
||
font-size:34px;margin:0 auto 20px;
|
||
}
|
||
.done-title{text-align:center;font-size:22px;font-weight:800;margin-bottom:8px}
|
||
.done-sub{text-align:center;font-size:14px;color:var(--text-dim);margin-bottom:28px;line-height:1.6}
|
||
|
||
/* TOAST */
|
||
#toast{
|
||
position:fixed;bottom:24px;right:24px;
|
||
background:var(--bg-2);border:1px solid var(--border-2);
|
||
border-radius:10px;padding:12px 18px;font-size:13px;
|
||
box-shadow:0 8px 32px rgba(0,0,0,.5);
|
||
opacity:0;transform:translateY(8px);
|
||
transition:opacity .2s,transform .2s;pointer-events:none;z-index:100;
|
||
}
|
||
#toast.show{opacity:1;transform:none}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<div class="topbar">
|
||
<div class="logo">CG</div>
|
||
<div>
|
||
<div class="brand">CostGuard</div>
|
||
<div class="brand-sub">Kom i gang</div>
|
||
</div>
|
||
</div>
|
||
|
||
<main class="page">
|
||
|
||
<!-- STEP INDICATOR -->
|
||
<div class="step-bar" id="step-bar">
|
||
<div class="step-item active" data-step="0"><div class="step-num">1</div><div class="step-label">Bedrift</div></div>
|
||
<div class="step-item" data-step="1"><div class="step-num">2</div><div class="step-label">Varslingskanal</div></div>
|
||
<div class="step-item" data-step="2"><div class="step-num">3</div><div class="step-label">Test & bekreft</div></div>
|
||
<div class="step-item" data-step="3"><div class="step-num">4</div><div class="step-label">Ferdig</div></div>
|
||
</div>
|
||
|
||
<!-- STEP 1: BEDRIFT -->
|
||
<div class="step-panel visible" id="panel-0">
|
||
<div class="step-title">Hvem er du?</div>
|
||
<div class="step-sub">Grunnleggende info om bedriften din og GCP-prosjektet CostGuard skal overvåke.</div>
|
||
|
||
<div class="field">
|
||
<div class="label">Bedriftsnavn</div>
|
||
<input class="input" id="company" type="text" placeholder="Vauco AS" autocomplete="organization">
|
||
</div>
|
||
<div class="field">
|
||
<div class="label">Kontaktperson <span>(ditt navn)</span></div>
|
||
<input class="input" id="contact" type="text" placeholder="Chris Christiansen" autocomplete="name">
|
||
</div>
|
||
<div class="field">
|
||
<div class="label">E-postadresse</div>
|
||
<input class="input" id="email" type="email" placeholder="chris@bedrift.no" autocomplete="email">
|
||
</div>
|
||
<div class="field">
|
||
<div class="label">GCP-prosjekt ID</div>
|
||
<input class="input" id="gcp_project" type="text" placeholder="mitt-gcp-prosjekt-123" spellcheck="false">
|
||
</div>
|
||
<div class="field">
|
||
<div class="label">Estimert månedlig GCP-spend <span>(USD)</span></div>
|
||
<select class="input" id="gcp_spend">
|
||
<option value="">Velg range...</option>
|
||
<option value="starter">$0 – $3 000 (Starter)</option>
|
||
<option value="guard">$3 000 – $15 000 (Guard)</option>
|
||
<option value="shield">$15 000 – $50 000 (Shield)</option>
|
||
<option value="enterprise">Over $50 000 (Enterprise)</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="btn-row">
|
||
<button class="btn-primary" onclick="nextStep(0)">Neste →</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- STEP 2: VARSLINGSKANAL -->
|
||
<div class="step-panel" id="panel-1">
|
||
<div class="step-title">Hvor vil du at Jason skal nå deg?</div>
|
||
<div class="step-sub">Velg én eller flere kanaler. CostGuard leverer direkte dit du allerede er.</div>
|
||
|
||
<div class="ch-grid">
|
||
|
||
<div class="ch-card" id="ch-webhook" onclick="toggleChannel('webhook')">
|
||
<div class="ch-ico">🔗</div>
|
||
<div class="ch-info">
|
||
<div class="ch-name">Webhook <span style="font-size:11px;font-weight:400;color:var(--text-faint)">(Slack · Teams · Google Chat · Discord)</span></div>
|
||
<div class="ch-desc">Jason poster meldinger direkte i din eksisterende kanal.</div>
|
||
<div class="ch-tier">Starter+</div>
|
||
<div class="ch-detail">
|
||
<div class="field" style="margin-bottom:0">
|
||
<div class="label">Webhook URL</div>
|
||
<input class="input" id="webhook_url" type="url" placeholder="https://hooks.slack.com/services/...">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="ch-check" id="check-webhook">✓</div>
|
||
</div>
|
||
|
||
<div class="ch-card" id="ch-email" onclick="toggleChannel('email')">
|
||
<div class="ch-ico">📧</div>
|
||
<div class="ch-info">
|
||
<div class="ch-name">E-post</div>
|
||
<div class="ch-desc">Daglig digest + varsler ved spikes. Sendes via SendGrid.</div>
|
||
<div class="ch-tier">Starter+</div>
|
||
<div class="ch-detail">
|
||
<div class="field" style="margin-bottom:0">
|
||
<div class="label">Mottaker-e-post <span>(forhåndsutfylt fra forrige steg)</span></div>
|
||
<input class="input" id="email_to" type="email" placeholder="alert@bedrift.no">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="ch-check" id="check-email">✓</div>
|
||
</div>
|
||
|
||
<div class="ch-card" style="opacity:.55;cursor:not-allowed" title="Krever Guard-tier">
|
||
<div class="ch-ico">📱</div>
|
||
<div class="ch-info">
|
||
<div class="ch-name">SMS <span style="font-size:11px;font-weight:400;color:var(--text-faint)">(kun kritiske spikes)</span></div>
|
||
<div class="ch-desc">Twilio-integrasjon — kommer i Guard-tier.</div>
|
||
<div class="ch-tier" style="color:var(--amber);border-color:rgba(255,181,71,.25)">Guard+ · Coming soon</div>
|
||
</div>
|
||
<div class="ch-check"></div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<div class="btn-row">
|
||
<button class="btn-secondary" onclick="prevStep(1)">← Tilbake</button>
|
||
<button class="btn-primary" onclick="nextStep(1)">Neste →</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- STEP 3: TEST & BEKREFT -->
|
||
<div class="step-panel" id="panel-2">
|
||
<div class="step-title">Test varslingene</div>
|
||
<div class="step-sub">Send en testmelding for å bekrefte at alt er koblet opp riktig — før vi aktiverer CostGuard.</div>
|
||
|
||
<div id="test-webhook-wrap" style="display:none;margin-bottom:20px">
|
||
<div class="summary-card" style="margin-bottom:0">
|
||
<div class="summary-row"><span class="summary-key">🔗 Webhook</span><span class="summary-val" id="test-webhook-url-disp">—</span></div>
|
||
</div>
|
||
<button class="btn-secondary" style="margin-top:12px" onclick="testChannel('webhook')" id="test-wh-btn">Send testmelding til webhook</button>
|
||
<div class="test-result" id="test-webhook-result"></div>
|
||
</div>
|
||
|
||
<div id="test-email-wrap" style="display:none;margin-bottom:20px">
|
||
<div class="summary-card" style="margin-bottom:0">
|
||
<div class="summary-row"><span class="summary-key">📧 E-post</span><span class="summary-val" id="test-email-to-disp">—</span></div>
|
||
</div>
|
||
<button class="btn-secondary" style="margin-top:12px" onclick="testChannel('email')" id="test-em-btn">Send test-e-post</button>
|
||
<div class="test-result" id="test-email-result"></div>
|
||
</div>
|
||
|
||
<p style="font-size:12px;color:var(--text-faint);margin-top:8px">Du kan hoppe over testingen og aktivere direkte.</p>
|
||
|
||
<div class="btn-row">
|
||
<button class="btn-secondary" onclick="prevStep(2)">← Tilbake</button>
|
||
<button class="btn-primary" id="activate-btn" onclick="activate()">Aktiver CostGuard ✓</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- STEP 4: FERDIG -->
|
||
<div class="step-panel" id="panel-3">
|
||
<div class="done-check">✅</div>
|
||
<div class="done-title">CostGuard er aktivert!</div>
|
||
<div class="done-sub">Jason overvåker nå <span id="done-project" style="color:var(--accent);font-weight:700"></span>.<br>Du mottar første rapport innen 24 timer.</div>
|
||
|
||
<div class="summary-card">
|
||
<div class="summary-row"><span class="summary-key">Bedrift</span><span class="summary-val" id="done-company">—</span></div>
|
||
<div class="summary-row"><span class="summary-key">Kontakt</span><span class="summary-val" id="done-contact">—</span></div>
|
||
<div class="summary-row"><span class="summary-key">GCP-prosjekt</span><span class="summary-val" id="done-gcp">—</span></div>
|
||
<div class="summary-row"><span class="summary-key">Tier</span><span class="summary-val" id="done-tier">—</span></div>
|
||
<div class="summary-row"><span class="summary-key">Kanaler</span><span class="summary-val" id="done-channels">—</span></div>
|
||
</div>
|
||
|
||
<div class="btn-row" style="justify-content:center">
|
||
<a href="/static/costguard-dashboard.html" class="btn-primary" style="text-decoration:none">Gå til dashboard →</a>
|
||
</div>
|
||
</div>
|
||
|
||
</main>
|
||
|
||
<div id="toast"></div>
|
||
|
||
<script>
|
||
const TIERS = {starter:'Starter ($499/mnd)',guard:'Guard ($999/mnd)',shield:'Shield ($1.999/mnd)',enterprise:'Enterprise ($3.500+/mnd)'}
|
||
let selectedChannels = new Set()
|
||
let state = {}
|
||
|
||
function toast(msg, dur=3200){
|
||
const t=document.getElementById('toast');t.textContent=msg;t.classList.add('show')
|
||
setTimeout(()=>t.classList.remove('show'),dur)
|
||
}
|
||
function setStep(to){
|
||
document.querySelectorAll('.step-panel').forEach(p=>p.classList.remove('visible'))
|
||
document.getElementById('panel-'+to).classList.add('visible')
|
||
document.querySelectorAll('.step-item').forEach((el,i)=>{
|
||
el.classList.toggle('active', i===to)
|
||
el.classList.toggle('done', i<to)
|
||
})
|
||
window.scrollTo({top:0,behavior:'smooth'})
|
||
}
|
||
function prevStep(from){setStep(from-1)}
|
||
|
||
function nextStep(from){
|
||
if(from===0){
|
||
const company=document.getElementById('company').value.trim()
|
||
const contact=document.getElementById('contact').value.trim()
|
||
const email=document.getElementById('email').value.trim()
|
||
const gcp=document.getElementById('gcp_project').value.trim()
|
||
const spend=document.getElementById('gcp_spend').value
|
||
const errors=[]
|
||
if(!company) errors.push('company')
|
||
if(!contact) errors.push('contact')
|
||
if(!email||!/^[^@]+@[^@]+\.[^@]+$/.test(email)) errors.push('email')
|
||
if(!gcp) errors.push('gcp_project')
|
||
if(!spend) errors.push('gcp_spend')
|
||
document.querySelectorAll('#panel-0 .input').forEach(el=>el.classList.remove('err'))
|
||
errors.forEach(id=>document.getElementById(id)?.classList.add('err'))
|
||
if(errors.length){toast('⚠️ Fyll ut alle feltene');return}
|
||
state={company,contact,email,gcp_project:gcp,tier:spend}
|
||
// Pre-fill email channel
|
||
document.getElementById('email_to').value=email
|
||
setStep(1)
|
||
} else if(from===1){
|
||
if(selectedChannels.size===0){toast('⚠️ Velg minst én varslingskanal');return}
|
||
if(selectedChannels.has('webhook')&&!document.getElementById('webhook_url').value.trim()){
|
||
document.getElementById('webhook_url').classList.add('err')
|
||
toast('⚠️ Skriv inn webhook URL');return
|
||
}
|
||
if(selectedChannels.has('email')&&!document.getElementById('email_to').value.trim()){
|
||
document.getElementById('email_to').classList.add('err')
|
||
toast('⚠️ Skriv inn e-postadresse');return
|
||
}
|
||
state.webhook_url = selectedChannels.has('webhook')?document.getElementById('webhook_url').value.trim():null
|
||
state.email_to = selectedChannels.has('email')?document.getElementById('email_to').value.trim():null
|
||
// Populate test step
|
||
const wWrap=document.getElementById('test-webhook-wrap')
|
||
const eWrap=document.getElementById('test-email-wrap')
|
||
if(state.webhook_url){
|
||
wWrap.style.display='block'
|
||
document.getElementById('test-webhook-url-disp').textContent=state.webhook_url
|
||
} else { wWrap.style.display='none' }
|
||
if(state.email_to){
|
||
eWrap.style.display='block'
|
||
document.getElementById('test-email-to-disp').textContent=state.email_to
|
||
} else { eWrap.style.display='none' }
|
||
setStep(2)
|
||
}
|
||
}
|
||
|
||
function toggleChannel(key){
|
||
const card=document.getElementById('ch-'+key)
|
||
if(selectedChannels.has(key)){selectedChannels.delete(key);card.classList.remove('selected')}
|
||
else{selectedChannels.add(key);card.classList.add('selected')}
|
||
}
|
||
|
||
async function testChannel(type){
|
||
const resultEl=document.getElementById('test-'+type+'-result')
|
||
const btn=document.getElementById('test-'+(type==='webhook'?'wh':'em')+'-btn')
|
||
btn.disabled=true;btn.textContent='Sender...'
|
||
resultEl.className='test-result';resultEl.textContent=''
|
||
|
||
try{
|
||
let body, url
|
||
if(type==='webhook'){
|
||
url='/notify/webhook'
|
||
body=JSON.stringify({
|
||
url: state.webhook_url,
|
||
event: 'custom',
|
||
title: 'CostGuard — Testmelding',
|
||
body: `Hei fra CostGuard! Webhook-integrasjon for ${state.company} (${state.gcp_project}) er koblet opp og fungerer.`,
|
||
payload: {bedrift:state.company, tier:TIERS[state.tier]||state.tier, prosjekt:state.gcp_project}
|
||
})
|
||
} else {
|
||
url='/notify/email'
|
||
body=JSON.stringify({
|
||
to: state.email_to,
|
||
event: 'custom',
|
||
subject: `✅ CostGuard er koblet opp — ${state.company}`,
|
||
body_html: `<div style='font-family:sans-serif;max-width:600px;margin:0 auto;padding:32px'><h2>CostGuard er koblet opp ✅</h2><p>Hei ${state.contact},</p><p>CostGuard overvåker nå <strong>${state.gcp_project}</strong>. Tier: <strong>${TIERS[state.tier]||state.tier}</strong>.</p><p>Du mottar første rapport innen 24 timer.</p><hr><p style='font-size:12px;color:#999'>CostGuard by Vauco AS</p></div>`
|
||
})
|
||
}
|
||
const r=await fetch(url,{method:'POST',headers:{'Content-Type':'application/json'},body})
|
||
const data=await r.json()
|
||
if(r.ok&&data.status==='ok'){
|
||
resultEl.className='test-result ok show'
|
||
resultEl.textContent='✅ Testmelding sendt!'
|
||
} else {
|
||
resultEl.className='test-result err show'
|
||
resultEl.textContent='❌ Feil: '+(data.detail||data.error||r.status)
|
||
}
|
||
}catch(e){
|
||
resultEl.className='test-result err show'
|
||
resultEl.textContent='❌ Nettverksfeil: '+e.message
|
||
}finally{
|
||
btn.disabled=false
|
||
btn.textContent=type==='webhook'?'Send testmelding til webhook':'Send test-e-post'
|
||
}
|
||
}
|
||
|
||
async function activate(){
|
||
const btn=document.getElementById('activate-btn')
|
||
btn.disabled=true
|
||
btn.innerHTML='<div class="spinner"></div> Aktiverer...'
|
||
try{
|
||
const payload={
|
||
company: state.company,
|
||
contact: state.contact,
|
||
email: state.email,
|
||
gcp_project:state.gcp_project,
|
||
tier: state.tier,
|
||
channels: [...selectedChannels],
|
||
webhook_url:state.webhook_url||null,
|
||
email_to: state.email_to||null,
|
||
}
|
||
const r=await fetch('/onboard/complete',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(payload)})
|
||
const data=await r.json()
|
||
if(!r.ok) throw new Error(data.detail||data.error||r.status)
|
||
// Populate done screen
|
||
document.getElementById('done-project').textContent=state.gcp_project
|
||
document.getElementById('done-company').textContent=state.company
|
||
document.getElementById('done-contact').textContent=state.contact+' ('+state.email+')'
|
||
document.getElementById('done-gcp').textContent=state.gcp_project
|
||
document.getElementById('done-tier').textContent=TIERS[state.tier]||state.tier
|
||
document.getElementById('done-channels').textContent=[...selectedChannels].join(' + ')||'—'
|
||
setStep(3)
|
||
}catch(e){
|
||
toast('❌ Aktivering feilet: '+e.message)
|
||
btn.disabled=false
|
||
btn.textContent='Aktiver CostGuard ✓'
|
||
}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|