Files
HostKeeper/app/backend/views/settings.html
T

223 lines
14 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>
<script src="/static/js/toggles.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-3xl mx-auto space-y-8 pb-16">
<section>
<h2 class="text-lg font-semibold text-on-surface mb-4">Profile</h2>
<div class="bg-white rounded-xl border border-outline-variant/30 p-6 space-y-4">
<div class="flex items-center gap-4 mb-4">
<div class="w-14 h-14 rounded-full bg-tertiary/20 flex items-center justify-center text-tertiary text-xl font-semibold">U</div>
<div>
<p class="text-base font-medium text-on-surface">User</p>
<p class="text-sm text-on-surface-variant">Local administrator</p>
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-on-surface mb-1">Display Name</label>
<input type="text" value="User" readonly
class="w-full px-3 py-2 rounded-lg border border-outline-variant/50 bg-surface-container-low/30 text-sm text-on-surface">
</div>
<div>
<label class="block text-sm font-medium text-on-surface mb-1">Username</label>
<input type="text" value="admin" readonly
class="w-full px-3 py-2 rounded-lg border border-outline-variant/50 bg-surface-container-low/30 text-sm text-on-surface">
</div>
</div>
</div>
</section>
<section x-data="{ tab: 'appearance' }">
<div class="flex gap-1 mb-4 bg-surface-container-low/50 rounded-lg p-1 w-fit">
<button @click="tab = 'appearance'" :class="tab === 'appearance' ? 'bg-white shadow-sm text-on-surface' : 'text-on-surface-variant hover:text-on-surface'"
class="px-4 py-1.5 rounded-md text-sm font-medium transition-colors">Appearance</button>
<button @click="tab = 'terminal'" :class="tab === 'terminal' ? 'bg-white shadow-sm text-on-surface' : 'text-on-surface-variant hover:text-on-surface'"
class="px-4 py-1.5 rounded-md text-sm font-medium transition-colors">Terminal</button>
<button @click="tab = 'connection'" :class="tab === 'connection' ? 'bg-white shadow-sm text-on-surface' : 'text-on-surface-variant hover:text-on-surface'"
class="px-4 py-1.5 rounded-md text-sm font-medium transition-colors">Connection</button>
</div>
<form method="post" action="/settings" class="bg-white rounded-xl border border-outline-variant/30 p-6 space-y-5">
<div x-show="tab === 'appearance'" x-cloak class="space-y-5">
<div>
<label class="block text-sm font-medium text-on-surface mb-1">Theme</label>
<select name="theme"
class="w-full max-w-xs px-3 py-2 rounded-lg border border-outline-variant/50 bg-white text-sm text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/30">
<option value="light" {{if eq .Config.Theme "light"}}selected{{end}}>Light</option>
<option value="dark" {{if eq .Config.Theme "dark"}}selected{{end}}>Dark (coming soon)</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-on-surface mb-1">Font Size</label>
<select name="fontSize"
class="w-full max-w-xs px-3 py-2 rounded-lg border border-outline-variant/50 bg-white text-sm text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/30">
<option value="12" {{if eq .Config.FontSize 12}}selected{{end}}>12px</option>
<option value="13" {{if eq .Config.FontSize 13}}selected{{end}}>13px</option>
<option value="14" {{if eq .Config.FontSize 14}}selected{{end}}>14px</option>
<option value="15" {{if eq .Config.FontSize 15}}selected{{end}}>15px</option>
<option value="16" {{if eq .Config.FontSize 16}}selected{{end}}>16px</option>
<option value="18" {{if eq .Config.FontSize 18}}selected{{end}}>18px</option>
</select>
</div>
</div>
<div x-show="tab === 'terminal'" x-cloak class="space-y-5">
<div>
<label class="block text-sm font-medium text-on-surface mb-1">Scrollback Lines</label>
<select name="scrollback"
class="w-full max-w-xs px-3 py-2 rounded-lg border border-outline-variant/50 bg-white text-sm text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/30">
<option value="1000" {{if eq .Config.Scrollback 1000}}selected{{end}}>1,000</option>
<option value="5000" {{if eq .Config.Scrollback 5000}}selected{{end}}>5,000</option>
<option value="10000" {{if eq .Config.Scrollback 10000}}selected{{end}}>10,000</option>
</select>
</div>
<label class="flex items-center justify-between py-2">
<span class="text-sm text-on-surface">Blinking Cursor</span>
<button type="button" role="switch"
x-data="{ on: {{if .Config.BlinkCursor}}true{{else}}false{{end}} }"
@click="on = !on; $el.classList.toggle('active'); $el.nextElementSibling.value = on ? 'on' : 'off'"
:class="on ? 'active' : ''"
class="toggle-switch" aria-checked="{{if .Config.BlinkCursor}}true{{else}}false{{end}}">
<span class="toggle-knob"></span>
</button>
<input type="hidden" name="blinkCursor" value="{{if .Config.BlinkCursor}}on{{end}}">
</label>
<label class="flex items-center justify-between py-2">
<span class="text-sm text-on-surface">Terminal Bell</span>
<button type="button" role="switch"
x-data="{ on: {{if .Config.BellEnabled}}true{{else}}false{{end}} }"
@click="on = !on; $el.classList.toggle('active'); $el.nextElementSibling.value = on ? 'on' : 'off'"
:class="on ? 'active' : ''"
class="toggle-switch">
<span class="toggle-knob"></span>
</button>
<input type="hidden" name="bellEnabled" value="{{if .Config.BellEnabled}}on{{end}}">
</label>
</div>
<div x-show="tab === 'connection'" x-cloak class="space-y-5">
<div>
<label class="block text-sm font-medium text-on-surface mb-1">Auto-Lock (minutes)</label>
<select name="autoLock"
class="w-full max-w-xs px-3 py-2 rounded-lg border border-outline-variant/50 bg-white text-sm text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/30">
<option value="1" {{if eq .Config.AutoLock 1}}selected{{end}}>1 min</option>
<option value="5" {{if eq .Config.AutoLock 5}}selected{{end}}>5 min</option>
<option value="15" {{if eq .Config.AutoLock 15}}selected{{end}}>15 min</option>
<option value="30" {{if eq .Config.AutoLock 30}}selected{{end}}>30 min</option>
<option value="0" {{if eq .Config.AutoLock 0}}selected{{end}}>Never</option>
</select>
</div>
<label class="flex items-center justify-between py-2">
<span class="text-sm text-on-surface">TCP Keepalive</span>
<button type="button" role="switch"
x-data="{ on: {{if .Config.Keepalive}}true{{else}}false{{end}} }"
@click="on = !on; $el.classList.toggle('active'); $el.nextElementSibling.value = on ? 'on' : 'off'"
:class="on ? 'active' : ''"
class="toggle-switch">
<span class="toggle-knob"></span>
</button>
<input type="hidden" name="keepalive" value="{{if .Config.Keepalive}}on{{end}}">
</label>
<label class="flex items-center justify-between py-2">
<span class="text-sm text-on-surface">Copy on Select</span>
<button type="button" role="switch"
x-data="{ on: {{if .Config.CopyOnSelect}}true{{else}}false{{end}} }"
@click="on = !on; $el.classList.toggle('active'); $el.nextElementSibling.value = on ? 'on' : 'off'"
:class="on ? 'active' : ''"
class="toggle-switch">
<span class="toggle-knob"></span>
</button>
<input type="hidden" name="copyOnSelect" value="{{if .Config.CopyOnSelect}}on{{end}}">
</label>
<label class="flex items-center justify-between py-2">
<span class="text-sm text-on-surface">Auto-Reconnect</span>
<button type="button" role="switch"
x-data="{ on: {{if .Config.AutoReconnect}}true{{else}}false{{end}} }"
@click="on = !on; $el.classList.toggle('active'); $el.nextElementSibling.value = on ? 'on' : 'off'"
:class="on ? 'active' : ''"
class="toggle-switch">
<span class="toggle-knob"></span>
</button>
<input type="hidden" name="autoReconnect" value="{{if .Config.AutoReconnect}}on{{end}}">
</label>
</div>
<div class="flex justify-end pt-2">
<button type="submit"
class="px-4 py-2 rounded-lg text-sm font-medium bg-primary text-white hover:bg-primary/90 transition-colors">Save Settings</button>
</div>
</form>
</section>
<section>
<h2 class="text-lg font-semibold text-on-surface mb-4">Connected Devices</h2>
<div class="bg-white rounded-xl border border-outline-variant/30 divide-y divide-outline-variant/20">
{{range .Devices}}
<div class="flex items-center gap-4 px-6 py-4">
<div class="w-8 h-8 rounded-lg bg-tertiary/10 text-tertiary flex items-center justify-center shrink-0">
<svg class="w-4 h-4" 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>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2">
<p class="text-sm font-medium text-on-surface">{{.Name}}</p>
{{if .Current}}<span class="text-xs font-medium px-1.5 py-0.5 rounded-full bg-secondary/10 text-secondary">Current</span>{{end}}
</div>
<p class="text-xs text-on-surface-variant">{{.OS}} · {{.LastSeen}}</p>
</div>
<button class="text-xs font-medium text-red-500 hover:text-red-600 transition-colors">Remove</button>
</div>
{{end}}
</div>
</section>
<section x-data="{ confirm: false }">
<h2 class="text-lg font-semibold text-red-600 mb-4">Danger Zone</h2>
<div class="bg-white rounded-xl border border-red-200 p-6">
<p class="text-sm text-on-surface-variant mb-4">This will permanently delete all your data, including hosts, keys, snippets, and settings. This action cannot be undone.</p>
<div x-show="!confirm">
<button @click="confirm = true"
class="px-4 py-2 rounded-lg text-sm font-medium bg-red-500 text-white hover:bg-red-600 transition-colors">Reset All Data</button>
</div>
<div x-show="confirm" x-cloak class="flex items-center gap-3">
<p class="text-sm font-medium text-red-600">Are you sure?</p>
<button @click="localStorage.clear(); confirm = false; alert('Data cleared')"
class="px-4 py-2 rounded-lg text-sm font-medium bg-red-500 text-white hover:bg-red-600 transition-colors">Yes, Delete Everything</button>
<button @click="confirm = false"
class="px-4 py-2 rounded-lg text-sm font-medium text-on-surface-variant hover:bg-surface-container-low transition-colors">Cancel</button>
</div>
</div>
</section>
</div>
</main>
</div>
</div>
</body>
</html>