diff options
Diffstat (limited to 'web')
| -rw-r--r-- | web/templates/settings.html | 257 |
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">← 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> |
