264 lines
12 KiB
Cheetah
264 lines
12 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">Vaults</a>
|
|
<a href="{{.Base}}/settings">Settings</a>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main class="container" style="flex:1">
|
|
<section class="section fade-in">
|
|
<span class="label accent">New vault</span>
|
|
<h1 style="margin-top:0.5rem">Choose your region</h1>
|
|
<p class="text-secondary" style="margin-top:0.5rem;max-width:480px">
|
|
Your vault lives in one region. All data stays there. Pick the closest location for the best performance.
|
|
</p>
|
|
</section>
|
|
|
|
<section class="fade-in fade-in-delay-1" style="padding-bottom:1.5rem">
|
|
<div class="label gold" style="margin-bottom:0.75rem">Headquarters</div>
|
|
<div class="region-grid">
|
|
<div class="card card-interactive card-gold region-card" data-region="zurich" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇨🇭</span>
|
|
<span class="region-name">Zürich</span>
|
|
<span class="region-location">Switzerland</span>
|
|
<span class="label gold">HQ · Swiss jurisdiction</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="fade-in fade-in-delay-2" style="padding-bottom:1.5rem">
|
|
<div class="label" style="margin-bottom:0.75rem">Americas</div>
|
|
<div class="region-grid">
|
|
<div class="card card-interactive region-card" data-region="virginia" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇺🇸</span>
|
|
<span class="region-name">Virginia</span>
|
|
<span class="region-location">US East</span>
|
|
</div>
|
|
<div class="card card-interactive region-card" data-region="sanfrancisco" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇺🇸</span>
|
|
<span class="region-name">San Francisco</span>
|
|
<span class="region-location">US West</span>
|
|
</div>
|
|
<div class="card card-interactive region-card" data-region="montreal" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇨🇦</span>
|
|
<span class="region-name">Montréal</span>
|
|
<span class="region-location">Canada</span>
|
|
</div>
|
|
<div class="card card-interactive region-card" data-region="mexico" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇲🇽</span>
|
|
<span class="region-name">Mexico City</span>
|
|
<span class="region-location">Mexico</span>
|
|
</div>
|
|
<div class="card card-interactive region-card" data-region="bogota" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇨🇴</span>
|
|
<span class="region-name">Bogotá</span>
|
|
<span class="region-location">Colombia</span>
|
|
</div>
|
|
<div class="card card-interactive region-card" data-region="saopaulo" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇧🇷</span>
|
|
<span class="region-name">São Paulo</span>
|
|
<span class="region-location">Brazil</span>
|
|
</div>
|
|
<div class="card card-interactive region-card" data-region="santiago" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇨🇱</span>
|
|
<span class="region-name">Santiago</span>
|
|
<span class="region-location">Chile</span>
|
|
</div>
|
|
<div class="card card-interactive region-card" data-region="buenosaires" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇦🇷</span>
|
|
<span class="region-name">Buenos Aires</span>
|
|
<span class="region-location">Argentina</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="fade-in fade-in-delay-3" style="padding-bottom:1.5rem">
|
|
<div class="label" style="margin-bottom:0.75rem">Europe</div>
|
|
<div class="region-grid">
|
|
<div class="card card-interactive region-card" data-region="london" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇬🇧</span>
|
|
<span class="region-name">London</span>
|
|
<span class="region-location">United Kingdom</span>
|
|
</div>
|
|
<div class="card card-interactive region-card" data-region="madrid" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇪🇸</span>
|
|
<span class="region-name">Madrid</span>
|
|
<span class="region-location">Spain</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="fade-in fade-in-delay-3" style="padding-bottom:1.5rem">
|
|
<div class="label" style="margin-bottom:0.75rem">Middle East & Africa</div>
|
|
<div class="region-grid">
|
|
<div class="card card-interactive region-card" data-region="istanbul" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇹🇷</span>
|
|
<span class="region-name">Istanbul</span>
|
|
<span class="region-location">Turkey</span>
|
|
</div>
|
|
<div class="card card-interactive region-card" data-region="dubai" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇦🇪</span>
|
|
<span class="region-name">Dubai</span>
|
|
<span class="region-location">UAE</span>
|
|
</div>
|
|
<div class="card card-interactive region-card" data-region="capetown" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇿🇦</span>
|
|
<span class="region-name">Cape Town</span>
|
|
<span class="region-location">South Africa</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="fade-in fade-in-delay-3" style="padding-bottom:3rem">
|
|
<div class="label" style="margin-bottom:0.75rem">Asia Pacific</div>
|
|
<div class="region-grid">
|
|
<div class="card card-interactive region-card" data-region="mumbai" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇮🇳</span>
|
|
<span class="region-name">Mumbai</span>
|
|
<span class="region-location">India</span>
|
|
</div>
|
|
<div class="card card-interactive region-card" data-region="singapore" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇸🇬</span>
|
|
<span class="region-name">Singapore</span>
|
|
<span class="region-location">Singapore</span>
|
|
</div>
|
|
<div class="card card-interactive region-card" data-region="hongkong" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇭🇰</span>
|
|
<span class="region-name">Hong Kong</span>
|
|
<span class="region-location">China</span>
|
|
</div>
|
|
<div class="card card-interactive region-card" data-region="seoul" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇰🇷</span>
|
|
<span class="region-name">Seoul</span>
|
|
<span class="region-location">South Korea</span>
|
|
</div>
|
|
<div class="card card-interactive region-card" data-region="tokyo" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇯🇵</span>
|
|
<span class="region-name">Tokyo</span>
|
|
<span class="region-location">Japan</span>
|
|
</div>
|
|
<div class="card card-interactive region-card" data-region="sydney" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇦🇺</span>
|
|
<span class="region-name">Sydney</span>
|
|
<span class="region-location">Australia</span>
|
|
</div>
|
|
<div class="card card-interactive region-card" data-region="almaty" onclick="selectRegion(this)">
|
|
<span class="region-flag">🇰🇿</span>
|
|
<span class="region-name">Almaty</span>
|
|
<span class="region-location">Kazakhstan</span>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Sticky bottom bar -->
|
|
<div id="region-confirm" style="position:fixed;bottom:0;left:0;right:0;transform:translateY(100%);transition:transform 0.35s cubic-bezier(0.16,1,0.3,1);z-index:50">
|
|
<div style="background:rgba(6,10,16,0.9);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);border-top:1px solid var(--glass-border);padding:1rem 0">
|
|
<div class="container" style="display:flex;align-items:center;justify-content:space-between;gap:1rem;max-width:960px">
|
|
<div>
|
|
<span id="selected-name" style="font-weight:600"></span>
|
|
<span id="selected-location" class="text-secondary" style="font-size:0.875rem;margin-left:0.5rem"></span>
|
|
</div>
|
|
<button class="btn btn-primary" onclick="confirmRegion()">
|
|
Create vault here
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
{{end}}
|
|
|
|
{{define "scripts"}}
|
|
<script>
|
|
let selectedRegion = null;
|
|
|
|
function selectRegion(el) {
|
|
document.querySelectorAll('.region-card.selected').forEach(c => c.classList.remove('selected'));
|
|
el.classList.add('selected');
|
|
selectedRegion = el.dataset.region;
|
|
|
|
const name = el.querySelector('.region-name').textContent;
|
|
const loc = el.querySelector('.region-location').textContent;
|
|
document.getElementById('selected-name').textContent = name;
|
|
document.getElementById('selected-location').textContent = loc;
|
|
document.getElementById('region-confirm').style.transform = 'translateY(0)';
|
|
}
|
|
|
|
async function confirmRegion() {
|
|
if (!selectedRegion) return;
|
|
const btn = document.querySelector('#region-confirm .btn');
|
|
btn.disabled = true;
|
|
btn.textContent = 'Creating vault…';
|
|
|
|
try {
|
|
const resp = await fetch('{{.Base}}/api/vault/create', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ region: selectedRegion })
|
|
});
|
|
if (resp.status === 401) { window.location.href = '{{.Base}}/login'; return; }
|
|
if (resp.status === 409) throw new Error('Vault limit reached');
|
|
if (!resp.ok) throw new Error('Could not create vault');
|
|
window.location.href = '{{.Base}}/dashboard';
|
|
} catch (err) {
|
|
showToast(err.message || 'Could not create vault', 'error');
|
|
btn.disabled = false;
|
|
btn.textContent = 'Create vault here';
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// Auto-detect closest region
|
|
if (navigator.geolocation) {
|
|
navigator.geolocation.getCurrentPosition(pos => {
|
|
const lat = pos.coords.latitude;
|
|
const lon = pos.coords.longitude;
|
|
const regions = {
|
|
zurich: [47.37, 8.54], virginia: [38.95, -77.45], sanfrancisco: [37.77, -122.42],
|
|
montreal: [45.50, -73.57], mexico: [19.43, -99.13], bogota: [4.71, -74.07],
|
|
saopaulo: [-23.55, -46.63], santiago: [-33.45, -70.65], buenosaires: [-34.60, -58.38],
|
|
london: [51.51, -0.13], madrid: [40.42, -3.70],
|
|
istanbul: [41.01, 28.98], dubai: [25.20, 55.27], capetown: [-33.92, 18.42],
|
|
mumbai: [19.08, 72.88], singapore: [1.35, 103.82], hongkong: [22.32, 114.17],
|
|
seoul: [37.57, 126.98], tokyo: [35.68, 139.69], sydney: [-33.87, 151.21],
|
|
almaty: [43.24, 76.95]
|
|
};
|
|
let closest = null, minDist = Infinity;
|
|
for (const [id, [rlat, rlon]] of Object.entries(regions)) {
|
|
const d = Math.hypot(lat - rlat, lon - rlon);
|
|
if (d < minDist) { minDist = d; closest = id; }
|
|
}
|
|
if (closest) {
|
|
const el = document.querySelector(`[data-region="${closest}"]`);
|
|
if (el) {
|
|
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
el.style.boxShadow = '0 0 0 2px var(--accent-dim)';
|
|
const hint = document.createElement('span');
|
|
hint.className = 'label accent';
|
|
hint.textContent = 'Nearest to you';
|
|
hint.style.marginTop = '0.25rem';
|
|
el.appendChild(hint);
|
|
}
|
|
}
|
|
}, () => {}, { timeout: 5000 });
|
|
}
|
|
</script>
|
|
{{end}}
|