8357d9d76e
- All 7 pages rewritten: Dashboard, Terminal, Snippets, Keychain, SFTP, Settings, Brief - Apply consistent Lumina glassmorphic theme (rounded-2xl, shadows, borders, tracking) - Fix SVG icons not rendering — return template.HTML instead of string - Fix Alpine x-data scoping: terminal tabs, SFTP modals, snippet tag buttons - Fix dashboard search sending literal ?q=... - Rebuild CSS with all new Tailwind classes (62KB) - Add .air.toml with hot-reload config, docs/PERBAIKANUI.md checklist - Remove unused backup partials
271 lines
18 KiB
HTML
271 lines
18 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Settings · Hostkeeper V2</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="/static/css/output.css">
|
|
<link rel="stylesheet" href="/static/css/custom.css">
|
|
<script src="/static/js/htmx.min.js"></script>
|
|
<script src="/static/js/alpine.min.js" defer></script>
|
|
<script src="/static/js/clipboard.js"></script>
|
|
<script src="/static/js/utils.js"></script>
|
|
</head>
|
|
<body class="bg-surface text-on-surface antialiased dot-grid min-h-screen">
|
|
<div class="flex min-h-screen">
|
|
{{template "nav" .}}
|
|
<div class="flex-1 flex flex-col overflow-hidden">
|
|
<header class="h-14 border-b border-outline-variant/30 flex items-center px-6 bg-white/70 backdrop-blur-md">
|
|
<h1 class="text-lg font-semibold text-on-surface">{{.Title}}</h1>
|
|
</header>
|
|
<main id="main-content" class="flex-1 overflow-auto p-6">
|
|
<div class="max-w-7xl mx-auto space-y-8 text-sm" x-data="{ toast: '', showToast: false, saveSuccess: false }"
|
|
x-init="$watch('showToast', v => v && setTimeout(() => showToast = false, 3000))">
|
|
|
|
<!-- Toast -->
|
|
<div x-show="showToast" x-cloak
|
|
class="fixed bottom-6 right-6 z-[180] bg-inverse-surface text-inverse-on-surface px-5 py-3.5 rounded-xl shadow-xl flex items-center gap-3 border border-outline/20 animate-slide-in text-xs font-semibold">
|
|
<svg class="w-4 h-4 text-secondary animate-bounce" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
|
|
<span x-text="toast"></span>
|
|
</div>
|
|
|
|
<!-- Header -->
|
|
<div class="border-b border-outline-variant/30 pb-4">
|
|
<h2 class="text-xl font-bold text-on-surface tracking-tight">Workspace Preferences</h2>
|
|
<p class="text-xs text-on-surface-variant mt-0.5">Configure authentication sync protocols and cloud billing</p>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8">
|
|
<!-- Left: profile + toggles -->
|
|
<div class="lg:col-span-8 space-y-8">
|
|
|
|
<!-- Profile Form -->
|
|
<div class="bg-white p-6 rounded-2xl border border-outline-variant/30 shadow-sm space-y-6">
|
|
<h3 class="font-bold text-on-surface text-base flex items-center gap-2">
|
|
<svg class="w-5 h-5 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/></svg>
|
|
Developer Profile
|
|
</h3>
|
|
<form method="post" action="/settings" class="grid grid-cols-1 sm:grid-cols-2 gap-5">
|
|
<div class="flex flex-col gap-1.5">
|
|
<label class="text-xs font-bold text-on-surface-variant">FULL NAME</label>
|
|
<input type="text" name="name" value="User"
|
|
class="w-full bg-surface-container-low border border-outline-variant/40 rounded-lg py-2.5 px-4 focus:ring-2 focus:ring-primary/20 focus:border-primary focus:outline-none transition-all font-semibold text-on-surface">
|
|
</div>
|
|
<div class="flex flex-col gap-1.5">
|
|
<label class="text-xs font-bold text-on-surface-variant">EMAIL ADDRESS</label>
|
|
<input type="email" name="email" value="user@hostkeeper.io"
|
|
class="w-full bg-surface-container-low border border-outline-variant/40 rounded-lg py-2.5 px-4 focus:ring-2 focus:ring-primary/20 focus:border-primary focus:outline-none transition-all font-medium text-on-surface">
|
|
</div>
|
|
<div class="flex flex-col gap-1.5 sm:col-span-2">
|
|
<label class="text-xs font-bold text-on-surface-variant">WORKPLACE DESIGNATION / ROLE</label>
|
|
<input type="text" name="role" value="DevOps Engineer"
|
|
class="w-full bg-surface-container-low border border-outline-variant/40 rounded-lg py-2.5 px-4 focus:ring-2 focus:ring-primary/20 focus:border-primary focus:outline-none transition-all font-medium text-on-surface">
|
|
</div>
|
|
<div class="sm:col-span-2 pt-2 flex justify-end">
|
|
<button type="submit"
|
|
class="flex items-center gap-2 bg-primary text-white hover:bg-primary/90 font-bold text-xs py-2.5 px-6 rounded-xl shadow-md transition-all">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4"/></svg>
|
|
Save Changes
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Feature Toggles -->
|
|
<div class="bg-white p-6 rounded-2xl border border-outline-variant/30 shadow-sm space-y-6">
|
|
<h3 class="font-bold text-on-surface text-base flex items-center gap-2">
|
|
<svg class="w-5 h-5 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"/></svg>
|
|
Security & Transmission Protocols
|
|
</h3>
|
|
<div class="space-y-4 divide-y divide-outline-variant/20">
|
|
<label class="flex items-center justify-between py-3">
|
|
<div class="space-y-0.5 pr-4">
|
|
<h4 class="font-bold text-on-surface text-xs sm:text-sm">Cryptographic Keychain Backup</h4>
|
|
<p class="text-xs text-on-surface-variant">Synchronize securely encrypted keys automatically inside iCloud/Google drive sandboxes.</p>
|
|
</div>
|
|
<button type="button" role="switch"
|
|
x-data="{ on: true }"
|
|
@click="on = !on; $el.classList.toggle('active')"
|
|
:class="on ? 'active' : ''"
|
|
class="toggle-switch" style="width:44px;height:24px">
|
|
<span class="toggle-knob"></span>
|
|
</button>
|
|
</label>
|
|
|
|
<label class="flex items-center justify-between pt-3">
|
|
<div class="space-y-0.5 pr-4">
|
|
<h4 class="font-bold text-on-surface text-xs sm:text-sm">Real-time SFTP Connection Polling</h4>
|
|
<p class="text-xs text-on-surface-variant">Enable background file watchers to automatically refresh the directory tree upon local file modifications.</p>
|
|
</div>
|
|
<button type="button" role="switch"
|
|
x-data="{ on: true }"
|
|
@click="on = !on; $el.classList.toggle('active')"
|
|
:class="on ? 'active' : ''"
|
|
class="toggle-switch" style="width:44px;height:24px">
|
|
<span class="toggle-knob"></span>
|
|
</button>
|
|
</label>
|
|
|
|
<label class="flex items-center justify-between pt-3">
|
|
<div class="space-y-0.5 pr-4">
|
|
<h4 class="font-bold text-on-surface text-xs sm:text-sm">Desktop Push Notifications</h4>
|
|
<p class="text-xs text-on-surface-variant">Trigger native system indicators upon complete transmissions of download/upload streams.</p>
|
|
</div>
|
|
<button type="button" role="switch"
|
|
x-data="{ on: false }"
|
|
@click="on = !on; $el.classList.toggle('active')"
|
|
:class="on ? 'active' : ''"
|
|
class="toggle-switch" style="width:44px;height:24px">
|
|
<span class="toggle-knob"></span>
|
|
</button>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Terminal Settings -->
|
|
<div class="bg-white p-6 rounded-2xl border border-outline-variant/30 shadow-sm space-y-6">
|
|
<h3 class="font-bold text-on-surface text-base flex items-center gap-2">
|
|
<svg class="w-5 h-5 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/></svg>
|
|
Terminal Preferences
|
|
</h3>
|
|
<form method="post" action="/settings" class="space-y-5">
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div class="flex flex-col gap-1">
|
|
<label class="text-xs font-bold text-on-surface-variant">FONT SIZE</label>
|
|
<select name="fontSize"
|
|
class="w-full bg-surface-container-low border border-outline-variant/40 rounded-lg py-2.5 px-3 focus:outline-none focus:border-primary">
|
|
<option value="12">12px</option>
|
|
<option value="13">13px</option>
|
|
<option value="14" selected>14px</option>
|
|
<option value="15">15px</option>
|
|
<option value="16">16px</option>
|
|
<option value="18">18px</option>
|
|
</select>
|
|
</div>
|
|
<div class="flex flex-col gap-1">
|
|
<label class="text-xs font-bold text-on-surface-variant">SCROLLBACK</label>
|
|
<select name="scrollback"
|
|
class="w-full bg-surface-container-low border border-outline-variant/40 rounded-lg py-2.5 px-3 focus:outline-none focus:border-primary">
|
|
<option value="1000">1,000</option>
|
|
<option value="5000" selected>5,000</option>
|
|
<option value="10000">10,000</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<label class="flex items-center justify-between py-2">
|
|
<span class="text-sm font-medium text-on-surface">Blinking Cursor</span>
|
|
<button type="button" role="switch"
|
|
x-data="{ on: true }"
|
|
@click="on = !on; $el.classList.toggle('active')"
|
|
:class="on ? 'active' : ''"
|
|
class="toggle-switch" style="width:44px;height:24px">
|
|
<span class="toggle-knob"></span>
|
|
</button>
|
|
</label>
|
|
<label class="flex items-center justify-between py-2">
|
|
<span class="text-sm font-medium text-on-surface">Terminal Bell</span>
|
|
<button type="button" role="switch"
|
|
x-data="{ on: false }"
|
|
@click="on = !on; $el.classList.toggle('active')"
|
|
:class="on ? 'active' : ''"
|
|
class="toggle-switch" style="width:44px;height:24px">
|
|
<span class="toggle-knob"></span>
|
|
</button>
|
|
</label>
|
|
<label class="flex items-center justify-between py-2">
|
|
<span class="text-sm font-medium text-on-surface">Auto-Reconnect</span>
|
|
<button type="button" role="switch"
|
|
x-data="{ on: true }"
|
|
@click="on = !on; $el.classList.toggle('active')"
|
|
:class="on ? 'active' : ''"
|
|
class="toggle-switch" style="width:44px;height:24px">
|
|
<span class="toggle-knob"></span>
|
|
</button>
|
|
</label>
|
|
<div class="pt-2 flex justify-end">
|
|
<button type="submit"
|
|
class="bg-primary text-white py-2 px-6 rounded-xl font-bold text-xs shadow-md hover:bg-primary/90 transition-all">Save Settings</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right: billing + devices + danger -->
|
|
<div class="lg:col-span-4 space-y-8">
|
|
|
|
<!-- Billing Card -->
|
|
<div class="bg-primary text-on-primary p-6 rounded-2xl shadow-lg relative overflow-hidden flex flex-col justify-between h-48">
|
|
<div class="relative z-10">
|
|
<div class="flex justify-between items-start">
|
|
<span class="px-2.5 py-1 bg-white/20 rounded-full font-bold text-[9px] uppercase tracking-wider text-white">HostKeeper Pro</span>
|
|
<svg class="w-5 h-5 text-white fill-white" viewBox="0 0 24 24"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>
|
|
</div>
|
|
<h3 class="font-black text-2xl tracking-tight mt-3">Active Workspace</h3>
|
|
</div>
|
|
<div class="relative z-10 flex justify-between items-end">
|
|
<div>
|
|
<span class="text-xs opacity-80 uppercase font-semibold">RENEWAL CYCLE</span>
|
|
<p class="text-xs font-bold text-white">July 2026</p>
|
|
</div>
|
|
<span class="text-[10px] font-bold bg-white text-primary rounded-lg px-2.5 py-1 flex items-center gap-1">
|
|
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/></svg>
|
|
PAID ACCOUNT
|
|
</span>
|
|
</div>
|
|
<div class="absolute right-0 bottom-0 opacity-10 pointer-events-none">
|
|
<svg class="w-48 h-48 -rotate-45" viewBox="0 0 24 24" fill="currentColor"><path d="M13 10V3L4 14h7v7l9-11h-7z"/></svg>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Devices -->
|
|
<div class="bg-white p-5 rounded-2xl border border-outline-variant/30 shadow-sm space-y-4">
|
|
<h3 class="font-bold text-on-surface text-sm flex items-center gap-2">
|
|
<svg class="w-4 h-4 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z"/></svg>
|
|
Synchronized Devices
|
|
</h3>
|
|
<div class="space-y-3.5">
|
|
{{range .Devices}}
|
|
<div class="flex items-center justify-between text-xs">
|
|
<div class="flex items-center gap-2.5 min-w-0">
|
|
<div class="p-1.5 bg-surface-container-low border border-outline-variant/20 rounded-lg shrink-0">
|
|
<svg class="w-4 h-4 {{if eq .Type "desktop"}}text-primary{{else}}text-outline{{end}}" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
{{if eq .Type "desktop"}}<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>{{end}}
|
|
{{if eq .Type "mobile"}}<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 18h.01M8 21h8a2 2 0 002-2V5a2 2 0 00-2-2H8a2 2 0 00-2 2v14a2 2 0 002 2z"/>{{end}}
|
|
{{if eq .Type "tablet"}}<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 18h.01M7 21h10a2 2 0 002-2V5a2 2 0 00-2-2H7a2 2 0 00-2 2v14a2 2 0 002 2z"/>{{end}}
|
|
</svg>
|
|
</div>
|
|
<span class="font-bold text-on-surface truncate pr-2">{{.Name}}</span>
|
|
</div>
|
|
<span class="px-2 py-0.5 rounded-full text-[9px] font-bold uppercase tracking-wider flex items-center gap-1 shrink-0 {{if .Current}}bg-secondary/10 text-secondary{{else}}bg-surface-container text-outline{{end}}">
|
|
<span class="w-1 h-1 rounded-full {{if .Current}}bg-secondary animate-pulse{{else}}bg-outline{{end}}"></span>
|
|
{{if .Current}}Online{{else}}Offline{{end}}
|
|
</span>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Danger Zone -->
|
|
<div class="p-4 border border-error-container/40 rounded-2xl bg-error-container/5 space-y-3 text-xs" x-data="{ confirm: false }">
|
|
<h4 class="font-bold text-error flex items-center gap-2">
|
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/></svg>
|
|
Reset Local State
|
|
</h4>
|
|
<p class="text-on-surface-variant">Resetting will clear all your locally loaded keystores, snippets, and connection cache buffers from the browser storage.</p>
|
|
<button @click="if(confirm('Are you sure you want to flush all local storage configurations?')) { localStorage.clear(); alert('Workspace storage cleared successfully.'); window.location.reload(); }"
|
|
class="w-full py-2 border border-error/30 rounded-xl font-bold text-error hover:bg-error/10 transition-colors">
|
|
Flush All App State
|
|
</button>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|