vault1984/commercial/account/templates/dashboard.tmpl

166 lines
7.0 KiB
Cheetah

{{define "page"}}
<header class="topbar">
<div class="container">
<div class="topbar-inner">
<a href="{{.Base}}/dashboard" class="topbar-brand">vault1984 <span class="port">account</span></a>
<nav class="topbar-nav">
<a href="{{.Base}}/dashboard" class="{{if eq .ActiveNav "dashboard"}}active{{end}}">Vaults</a>
<a href="{{.Base}}/settings" class="{{if eq .ActiveNav "settings"}}active{{end}}">Settings</a>
<div class="separator"></div>
<a href="#" id="logout-link">Sign out</a>
</nav>
</div>
</div>
</header>
<main class="container" style="flex:1">
<section class="section fade-in">
<div class="section-header">
<div>
<span class="label accent">Account</span>
<h1 style="margin-top:0.5rem">Your vaults</h1>
</div>
<a href="{{.Base}}/regions" class="btn btn-primary" id="new-vault-btn">
<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
New vault
</a>
</div>
</section>
<!-- Vault list (loaded dynamically) -->
<section class="fade-in fade-in-delay-1">
<div class="card" id="vault-list" style="padding:0; overflow:hidden">
<div class="vault-row" style="justify-content:center;color:var(--text-tertiary);padding:2rem">
Loading…
</div>
</div>
</section>
<!-- Plan info -->
<section class="section fade-in fade-in-delay-2">
<div class="card" style="display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap">
<div>
<span class="label" style="margin-bottom:0.25rem;display:block">Plan</span>
<div style="font-weight:600;font-size:1.0625rem">Consumer</div>
<div class="text-secondary" style="font-size:0.8125rem;margin-top:0.125rem" id="plan-info">$12/year</div>
</div>
<a href="{{.Base}}/settings" class="btn btn-ghost">Manage billing</a>
</div>
</section>
<!-- Quick stats -->
<section class="fade-in fade-in-delay-3" style="padding-bottom:3rem">
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:0.75rem">
<div class="card" style="text-align:center;padding:1.25rem">
<div style="font-size:1.75rem;font-weight:700;letter-spacing:-0.03em" id="stat-vaults">-</div>
<div class="label" style="margin-top:0.375rem">Vaults</div>
</div>
<div class="card" style="text-align:center;padding:1.25rem">
<div style="font-size:1.75rem;font-weight:700;letter-spacing:-0.03em;color:var(--accent)" id="stat-days">-</div>
<div class="label" style="margin-top:0.375rem">Days left</div>
</div>
<div class="card" style="text-align:center;padding:1.25rem">
<div style="font-size:1.75rem;font-weight:700;letter-spacing:-0.03em" id="stat-regions">-</div>
<div class="label" style="margin-top:0.375rem">Regions</div>
</div>
</div>
</section>
</main>
{{end}}
{{define "scripts"}}
<script>
const BASE = '{{.Base}}';
const REGIONS = {
zurich:'Zürich, Switzerland', virginia:'Virginia, US', sanfrancisco:'San Francisco, US',
montreal:'Montréal, Canada', mexico:'Mexico City, Mexico', bogota:'Bogotá, Colombia',
saopaulo:'São Paulo, Brazil', santiago:'Santiago, Chile', buenosaires:'Buenos Aires, Argentina',
london:'London, UK', madrid:'Madrid, Spain',
istanbul:'Istanbul, Turkey', dubai:'Dubai, UAE', capetown:'Cape Town, South Africa',
mumbai:'Mumbai, India', singapore:'Singapore', hongkong:'Hong Kong',
seoul:'Seoul, South Korea', tokyo:'Tokyo, Japan', sydney:'Sydney, Australia',
almaty:'Almaty, Kazakhstan'
};
async function loadDashboard() {
try {
const resp = await fetch(BASE + '/api/vaults');
if (resp.status === 401) { window.location.href = BASE + '/login'; return; }
if (!resp.ok) throw new Error('Failed to load');
const data = await resp.json();
const list = document.getElementById('vault-list');
const vaults = data.vaults || [];
if (vaults.length === 0) {
list.innerHTML = `
<div style="display:flex;flex-direction:column;align-items:center;padding:3rem 2rem;gap:1rem;text-align:center">
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="var(--text-secondary)" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" style="opacity:0.5">
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>
</svg>
<h2 style="color:var(--text)">You have no vaults yet</h2>
<p style="color:var(--text-secondary);max-width:320px;font-size:0.9375rem;line-height:1.6">
Pick a region and create your first vault. Your data stays in that region — encrypted, zero-knowledge, yours.
</p>
<a href="${BASE}/regions" class="btn btn-primary btn-lg" style="margin-top:0.5rem">
Choose a region and create your vault
</a>
</div>`;
} else {
list.innerHTML = vaults.map(v => {
const expires = new Date(v.expires_at);
const days = Math.ceil((expires - new Date()) / 86400000);
const isHQ = v.region === 'zurich';
const status = days > 0 ? 'live' : 'expired';
const regionName = REGIONS[v.region] || v.region;
return `
<div class="vault-row">
<div style="display:flex;align-items:center;gap:0.75rem">
<span class="status-dot ${status}"></span>
<div>
<div class="vault-id">vault1984-${v.vault_id}</div>
<div class="vault-region">${regionName}</div>
</div>
</div>
<div class="vault-meta">
${isHQ ? '<span class="label gold">HQ</span>' : ''}
<span>${days > 0 ? 'Expires' : 'Expired'} ${expires.toLocaleDateString('en-US', {month:'short',day:'numeric',year:'numeric'})}</span>
</div>
</div>`;
}).join('');
}
// Stats
document.getElementById('stat-vaults').textContent = vaults.length;
const regions = new Set(vaults.map(v => v.region));
document.getElementById('stat-regions').textContent = regions.size;
if (vaults.length > 0) {
const earliest = vaults.map(v => new Date(v.expires_at)).sort((a,b) => a-b)[0];
const days = Math.max(0, Math.ceil((earliest - new Date()) / 86400000));
document.getElementById('stat-days').textContent = days;
} else {
document.getElementById('stat-days').textContent = '-';
}
// Hide "New vault" if at max
if (data.count >= data.max) {
document.getElementById('new-vault-btn').style.display = 'none';
}
} catch (err) {
document.getElementById('vault-list').innerHTML =
'<div class="vault-row" style="justify-content:center;color:var(--red)">Failed to load vaults</div>';
}
}
document.getElementById('logout-link')?.addEventListener('click', async (e) => {
e.preventDefault();
await fetch(BASE + '/api/auth/logout', { method: 'POST' });
window.location.href = BASE + '/login';
});
loadDashboard();
</script>
{{end}}