summaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to 'web')
-rw-r--r--web/templates/settings.html257
1 files changed, 257 insertions, 0 deletions
diff --git a/web/templates/settings.html b/web/templates/settings.html
new file mode 100644
index 0000000..db84860
--- /dev/null
+++ b/web/templates/settings.html
@@ -0,0 +1,257 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Settings - Task Dashboard</title>
+ <script src="https://unpkg.com/htmx.org@1.9.10"></script>
+ <style>
+ :root {
+ --bg-primary: #1a1a2e;
+ --bg-secondary: #16213e;
+ --bg-tertiary: #0f3460;
+ --text-primary: #eee;
+ --text-secondary: #888;
+ --accent: #e94560;
+ --success: #28a745;
+ --danger: #dc3545;
+ --border: #333;
+ }
+ body {
+ font-family: system-ui, sans-serif;
+ background: var(--bg-primary);
+ color: var(--text-primary);
+ margin: 0;
+ padding: 20px;
+ min-height: 100vh;
+ }
+ .container { max-width: 900px; margin: 0 auto; }
+ h1 { color: var(--accent); margin-bottom: 8px; }
+ h2 { color: var(--text-primary); font-size: 1.1em; margin: 24px 0 16px; border-bottom: 1px solid var(--border); padding-bottom: 8px; }
+ .subtitle { color: var(--text-secondary); margin-bottom: 24px; }
+ .back-link { color: var(--accent); text-decoration: none; display: inline-block; margin-bottom: 16px; }
+ .back-link:hover { text-decoration: underline; }
+
+ .card {
+ background: var(--bg-secondary);
+ border-radius: 8px;
+ padding: 20px;
+ margin-bottom: 16px;
+ }
+ .card-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 16px;
+ }
+ .card-title {
+ font-size: 1.1em;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ }
+ .source-badge {
+ display: inline-block;
+ padding: 2px 8px;
+ border-radius: 10px;
+ font-size: 0.7em;
+ font-weight: normal;
+ }
+ .source-trello { background: #0079bf; }
+ .source-todoist { background: #e44332; }
+ .source-gcal { background: #9b59b6; }
+ .source-gtasks { background: #f39c12; }
+
+ .btn {
+ background: var(--bg-tertiary);
+ color: var(--text-primary);
+ border: 1px solid var(--border);
+ padding: 8px 16px;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 0.9em;
+ }
+ .btn:hover { background: #1a4a80; }
+ .btn:disabled { opacity: 0.5; cursor: not-allowed; }
+ .btn-danger { border-color: var(--danger); }
+ .btn-danger:hover { background: var(--danger); }
+ .btn-sm { padding: 4px 8px; font-size: 0.8em; }
+
+ .items-list { display: grid; gap: 8px; }
+ .item-row {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 10px 12px;
+ background: var(--bg-tertiary);
+ border-radius: 4px;
+ }
+ .item-row:hover { background: #1a4a80; }
+
+ .toggle-switch {
+ position: relative;
+ width: 44px;
+ height: 24px;
+ flex-shrink: 0;
+ }
+ .toggle-switch input { opacity: 0; width: 0; height: 0; }
+ .toggle-slider {
+ position: absolute;
+ cursor: pointer;
+ top: 0; left: 0; right: 0; bottom: 0;
+ background-color: #555;
+ transition: .3s;
+ border-radius: 24px;
+ }
+ .toggle-slider:before {
+ position: absolute;
+ content: "";
+ height: 18px;
+ width: 18px;
+ left: 3px;
+ bottom: 3px;
+ background-color: white;
+ transition: .3s;
+ border-radius: 50%;
+ }
+ input:checked + .toggle-slider { background-color: var(--success); }
+ input:checked + .toggle-slider:before { transform: translateX(20px); }
+
+ .item-name { flex: 1; }
+ .item-desc { color: var(--text-secondary); font-size: 0.85em; }
+ .item-id {
+ font-family: monospace;
+ font-size: 0.75em;
+ color: var(--text-secondary);
+ max-width: 200px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .empty-state {
+ color: var(--text-secondary);
+ padding: 20px;
+ text-align: center;
+ }
+
+ .add-form {
+ display: flex;
+ gap: 8px;
+ margin-top: 12px;
+ padding-top: 12px;
+ border-top: 1px solid var(--border);
+ }
+ .add-form input {
+ flex: 1;
+ background: var(--bg-tertiary);
+ border: 1px solid var(--border);
+ color: var(--text-primary);
+ padding: 8px 12px;
+ border-radius: 4px;
+ }
+ .add-form input:focus { outline: none; border-color: var(--accent); }
+
+ .htmx-indicator { display: none; }
+ .htmx-request .htmx-indicator { display: inline; }
+ </style>
+</head>
+<body>
+ <div class="container">
+ <a href="/" class="back-link">&larr; Back to Dashboard</a>
+ <h1>Settings</h1>
+ <p class="subtitle">Configure feature toggles and data sources.</p>
+
+ <!-- Feature Toggles Section -->
+ <div class="card">
+ <div class="card-header">
+ <div class="card-title">Feature Toggles</div>
+ </div>
+ <div class="items-list" id="toggles-list">
+ {{if .Toggles}}
+ {{range .Toggles}}
+ <div class="item-row" id="toggle-{{.Name}}">
+ <label class="toggle-switch">
+ <input type="checkbox"
+ {{if .Enabled}}checked{{end}}
+ hx-post="/settings/features/toggle"
+ hx-vals='{"name": "{{.Name}}", "enabled": "{{if .Enabled}}false{{else}}true{{end}}"}'
+ hx-swap="none"
+ hx-on::after-request="this.checked = !this.checked; if(event.detail.successful) this.checked = !this.checked;">
+ <span class="toggle-slider"></span>
+ </label>
+ <div class="item-name">
+ <strong>{{.Name}}</strong>
+ {{if .Description}}<div class="item-desc">{{.Description}}</div>{{end}}
+ </div>
+ <button class="btn btn-danger btn-sm"
+ hx-delete="/settings/features/{{.Name}}"
+ hx-target="#toggle-{{.Name}}"
+ hx-swap="outerHTML"
+ hx-confirm="Delete feature toggle '{{.Name}}'?">
+ Delete
+ </button>
+ </div>
+ {{end}}
+ {{else}}
+ <div class="empty-state">No feature toggles configured.</div>
+ {{end}}
+ </div>
+ <form class="add-form" hx-post="/settings/features" hx-target="#toggles-list" hx-swap="beforeend">
+ <input type="text" name="name" placeholder="Feature name (snake_case)" required pattern="[a-z_]+">
+ <input type="text" name="description" placeholder="Description (optional)">
+ <button type="submit" class="btn">Add Toggle</button>
+ </form>
+ </div>
+
+ <!-- Source Configuration Section -->
+ <h2>Data Sources</h2>
+ <form hx-post="/settings/sync" hx-swap="outerHTML" hx-target="#sources-container" hx-indicator=".sync-indicator">
+ <button type="submit" class="btn">
+ <span class="sync-indicator htmx-indicator">Syncing...</span>
+ Sync Available Sources
+ </button>
+ </form>
+
+ <div id="sources-container" style="margin-top: 16px;">
+ {{range .Sources}}
+ <div class="card">
+ <div class="card-header">
+ <div class="card-title">
+ <span class="source-badge source-{{.}}">{{.}}</span>
+ {{if eq . "trello"}}Trello Boards{{end}}
+ {{if eq . "todoist"}}Todoist Projects{{end}}
+ {{if eq . "gcal"}}Google Calendars{{end}}
+ {{if eq . "gtasks"}}Google Task Lists{{end}}
+ </div>
+ </div>
+ <div class="items-list">
+ {{$configs := index $.Configs .}}
+ {{if $configs}}
+ {{range $configs}}
+ <div class="item-row">
+ <label class="toggle-switch">
+ <input type="checkbox"
+ {{if .Enabled}}checked{{end}}
+ hx-post="/settings/toggle"
+ hx-vals='{"source": "{{.Source}}", "item_type": "{{.ItemType}}", "item_id": "{{.ItemID}}", "enabled": "{{if .Enabled}}false{{else}}true{{end}}"}'
+ hx-swap="none"
+ hx-on::after-request="this.checked = !this.checked; if(event.detail.successful) this.checked = !this.checked;">
+ <span class="toggle-slider"></span>
+ </label>
+ <span class="item-name">{{.ItemName}}</span>
+ <span class="item-id" title="{{.ItemID}}">{{.ItemID}}</span>
+ </div>
+ {{end}}
+ {{else}}
+ <div class="empty-state">
+ No items configured. Click "Sync Available Sources" to fetch.
+ </div>
+ {{end}}
+ </div>
+ </div>
+ {{end}}
+ </div>
+ </div>
+</body>
+</html>