clavitor/clavitor.com/account/templates/settings.tmpl

180 lines
5.4 KiB
Cheetah

{{define "page"}}
<header class="topbar">
<div class="container">
<div class="topbar-inner">
<a href="{{.Base}}/dashboard" class="topbar-brand">clavitor <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="{{.Base}}/login" id="logout-link">Sign out</a>
</nav>
</div>
</div>
</header>
<main class="container" style="flex:1">
<section class="section fade-in">
<span class="label">Account</span>
<h1 style="margin-top:0.5rem">Settings</h1>
</section>
<section class="card fade-in fade-in-delay-1">
<!-- Email -->
<div class="settings-section">
<div class="settings-row">
<div>
<div class="settings-label">Email</div>
<div class="settings-desc">johan@example.com</div>
</div>
</div>
</div>
<!-- Authentication -->
<div class="settings-section">
<div class="settings-row">
<div>
<div class="settings-label">Authentication</div>
<div class="settings-desc">
Passkey registered &middot;
<span class="text-accent" style="font-size:0.8125rem">Active</span>
</div>
</div>
<button class="btn btn-ghost" onclick="registerPasskey()">Add passkey</button>
</div>
</div>
<!-- Billing -->
<div class="settings-section">
<div class="settings-row">
<div>
<div class="settings-label">Billing</div>
<div class="settings-desc">
$12/year &middot; Next charge Mar 20, 2027
</div>
</div>
<button class="btn btn-ghost" onclick="manageBilling()">Manage in Stripe</button>
</div>
</div>
<!-- Invoices -->
<div class="settings-section">
<div class="settings-row">
<div>
<div class="settings-label">Invoices</div>
<div class="settings-desc">View and download past invoices</div>
</div>
<button class="btn btn-ghost" onclick="viewInvoices()">View invoices</button>
</div>
</div>
</section>
<!-- Danger zone -->
<section class="section fade-in fade-in-delay-2" style="padding-bottom:3rem">
<div class="danger-zone">
<h3>Danger zone</h3>
<p>
Deleting your account cancels your subscription, deletes all vaults,
and removes all data. This cannot be undone.
</p>
<button class="btn btn-danger" onclick="deleteAccount()">
Delete account
</button>
</div>
</section>
</main>
{{end}}
{{define "scripts"}}
<script>
function registerPasskey() {
// TODO: WebAuthn registration flow for account site
showToast('Passkey registration not yet implemented', 'error');
}
async function manageBilling() {
try {
const resp = await fetch('{{.Base}}/api/billing/portal', { method: 'POST' });
if (!resp.ok) throw new Error('Could not open billing portal');
const { url } = await resp.json();
window.location.href = url;
} catch (err) {
showToast(err.message, 'error');
}
}
function viewInvoices() {
manageBilling(); // Stripe portal handles invoices too
}
function deleteAccount() {
// Two-step confirmation
const zone = document.querySelector('.danger-zone');
if (zone.dataset.confirming) {
performDelete();
return;
}
zone.dataset.confirming = 'true';
const btn = zone.querySelector('.btn-danger');
btn.textContent = 'Confirm — delete everything';
btn.style.background = 'rgba(239,68,68,0.25)';
// Add email confirmation input
const field = document.createElement('div');
field.className = 'field';
field.style.marginBottom = '1rem';
field.innerHTML = `
<label style="color:var(--red)">Type your email to confirm</label>
<input type="email" id="delete-confirm-email" class="input" placeholder="you@example.com" style="border-color:rgba(239,68,68,0.3)">
`;
zone.insertBefore(field, btn);
document.getElementById('delete-confirm-email').focus();
}
async function performDelete() {
const email = document.getElementById('delete-confirm-email')?.value;
if (!email) {
showToast('Please enter your email to confirm', 'error');
return;
}
const btn = document.querySelector('.danger-zone .btn-danger');
btn.disabled = true;
btn.textContent = 'Deleting…';
try {
const resp = await fetch('{{.Base}}/api/account/delete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
});
if (!resp.ok) throw new Error('Could not delete account');
window.location.href = '/login?deleted=1';
} catch (err) {
showToast(err.message, 'error');
btn.disabled = false;
btn.textContent = 'Confirm — delete everything';
}
}
document.getElementById('logout-link')?.addEventListener('click', (e) => {
e.preventDefault();
window.location.href = '{{.Base}}/login';
});
function showToast(msg, type) {
let t = document.querySelector('.toast');
if (!t) {
t = document.createElement('div');
t.className = 'toast';
document.body.appendChild(t);
}
t.textContent = msg;
t.className = 'toast ' + type;
requestAnimationFrame(() => t.classList.add('show'));
setTimeout(() => t.classList.remove('show'), type === 'error' ? 6000 : 3000);
}
</script>
{{end}}