diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-03-22 10:05:16 +0000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-03-22 10:05:16 +0000 |
| commit | 11b905fd437d651b2e39745aa82a5dd36f70331e (patch) | |
| tree | a9019cfa8fc81b274622b5b414fbb8d5f602fa78 /web | |
| parent | 77f4aa8d1f818169490a35bf2ec3eb43e60166b4 (diff) | |
style: modernize UI with dark glass theme and 40% bg opacity
Diffstat (limited to 'web')
| -rw-r--r-- | web/static/css/input.css | 17 | ||||
| -rw-r--r-- | web/templates/index.html | 24 | ||||
| -rw-r--r-- | web/templates/login.html | 11 | ||||
| -rw-r--r-- | web/templates/partials/sync-log.html | 16 | ||||
| -rw-r--r-- | web/templates/passkeys_list.html | 14 | ||||
| -rw-r--r-- | web/templates/settings.html | 372 | ||||
| -rw-r--r-- | web/templates/shopping-mode.html | 9 |
7 files changed, 160 insertions, 303 deletions
diff --git a/web/static/css/input.css b/web/static/css/input.css index 321aa4f..7dd2055 100644 --- a/web/static/css/input.css +++ b/web/static/css/input.css @@ -7,29 +7,32 @@ /* Custom base styles - Dark translucent theme */ @layer base { body { - @apply antialiased text-white bg-gray-900 min-h-screen; + @apply antialiased text-slate-200 bg-slate-950 min-h-screen relative; font-family: 'Inter', system-ui, sans-serif; - text-shadow: 0 0 8px black, 0 0 8px black; + } + + .bg-overlay { + @apply fixed inset-0 z-[-1] opacity-40 bg-cover bg-center bg-no-repeat; } /* Headings */ h1, h2, h3, h4, h5, h6 { - @apply text-white font-light tracking-wide; + @apply text-white font-medium tracking-tight; } - a { @apply text-white/90 hover:text-white; } + a { @apply text-slate-300 hover:text-white transition-colors; } } /* Custom components */ @layer components { /* Dark Glass Card */ .card { - @apply bg-black/70 backdrop-blur-sm rounded-lg p-4 transition-all duration-200 overflow-hidden; - box-shadow: 0 0 12px black; + @apply bg-slate-900/80 backdrop-blur-md rounded-xl p-5 transition-all duration-200 border border-white/5; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); } .card-hover { - @apply hover:bg-black/80; + @apply hover:bg-slate-800/90 hover:border-white/10; } /* Panel with overflow clipping */ diff --git a/web/templates/index.html b/web/templates/index.html index 1884a77..5cfa77f 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -8,25 +8,25 @@ <link rel="stylesheet" href="/static/css/output.css"> <style> :root { - --panel-bg: rgba(0, 0, 0, 0.4); - --card-bg: rgba(0, 0, 0, 0.5); - --card-bg-hover: rgba(0, 0, 0, 0.6); - --input-bg: rgba(0, 0, 0, 0.4); - --modal-bg: rgba(0, 0, 0, 0.8); - --modal-overlay: rgba(0, 0, 0, 0.7); + --panel-bg: rgba(15, 23, 42, 0.8); + --card-bg: rgba(30, 41, 59, 0.7); + --card-bg-hover: rgba(51, 65, 85, 0.8); + --input-bg: rgba(15, 23, 42, 0.9); + --modal-bg: rgba(15, 23, 42, 0.95); + --modal-overlay: rgba(2, 6, 23, 0.8); } - .text-shadow { text-shadow: 0 0 8px black, 0 0 8px black; } - .text-shadow-sm { text-shadow: 0 0 4px black; } + .text-shadow-sm { text-shadow: 0 1px 2px rgba(0,0,0,0.5); } /* CSS variable-based backgrounds */ - .bg-panel { background-color: var(--panel-bg); } - .bg-card { background-color: var(--card-bg); } - .bg-card-hover:hover { background-color: var(--card-bg-hover); } + .bg-panel { background-color: var(--panel-bg); @apply backdrop-blur-md border border-white/5; } + .bg-card { background-color: var(--card-bg); @apply backdrop-blur-sm border border-white/5; } + .bg-card-hover:hover { background-color: var(--card-bg-hover); @apply border-white/10; } .bg-input { background-color: var(--input-bg); } .bg-modal { background-color: var(--modal-bg); } .bg-modal-overlay { background-color: var(--modal-overlay); } </style> </head> -<body class="min-h-screen bg-gray-900 text-white" style="background-image: url('{{.BackgroundURL}}'); background-size: cover; background-position: center; background-attachment: fixed;" hx-headers='{"X-CSRF-Token": "{{.CSRFToken}}"}'> +<body class="min-h-screen bg-slate-950 text-slate-200" hx-headers='{"X-CSRF-Token": "{{.CSRFToken}}"}'> + <div class="bg-overlay" style="background-image: url('{{.BackgroundURL}}');"></div> <div class="content-max-width py-3 sm:py-6"> <!-- Minimal Header --> <header class="flex mb-4 sm:mb-6 justify-between items-center no-print text-shadow-sm"> diff --git a/web/templates/login.html b/web/templates/login.html index 7d40a6b..19bce00 100644 --- a/web/templates/login.html +++ b/web/templates/login.html @@ -7,13 +7,14 @@ <link rel="icon" type="image/svg+xml" href="/static/favicon.svg"> <link rel="stylesheet" href="/static/css/output.css"> <style> - .text-shadow { text-shadow: 0 0 8px black, 0 0 8px black; } + .text-shadow-sm { text-shadow: 0 1px 2px rgba(0,0,0,0.5); } </style> </head> -<body class="min-h-screen flex items-center justify-center bg-gray-900" style="background-image: url('https://picsum.photos/1920/1080?random=login'); background-size: cover; background-position: center;"> - <div class="w-full max-w-md p-8"> - <div class="bg-black/60 backdrop-blur-sm rounded-xl p-8 text-shadow" style="box-shadow: 0 0 12px black;"> - <h1 class="text-2xl font-light text-white text-center mb-8 tracking-wide">Personal Dashboard</h1> +<body class="min-h-screen flex items-center justify-center bg-slate-950 text-slate-200"> + <div class="bg-overlay" style="background-image: url('https://picsum.photos/1920/1080?random=login');"></div> + <div class="w-full max-w-md p-6 sm:p-8 relative z-10"> + <div class="card shadow-2xl"> + <h1 class="text-3xl font-light text-white text-center mb-10 tracking-tight text-shadow-sm">Personal Dashboard</h1> {{if .Error}} <div class="mb-6 p-4 bg-red-900/50 border border-red-500/30 rounded-lg text-red-300 text-sm"> diff --git a/web/templates/partials/sync-log.html b/web/templates/partials/sync-log.html index e7f8191..89b7dcf 100644 --- a/web/templates/partials/sync-log.html +++ b/web/templates/partials/sync-log.html @@ -1,14 +1,16 @@ {{define "sync-log"}} <div id="sync-log"> {{if .}} - <div style="margin-top: 12px;"> - <div style="font-size: 0.75rem; font-weight: 600; color: var(--text-secondary); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 6px;">Sync Activity</div> - {{range .}} - <div style="display: flex; gap: 8px; align-items: baseline; font-size: 0.8rem; padding: 3px 0; border-bottom: 1px solid var(--border-color, #2a2a2a);"> - <span style="color: var(--text-secondary); white-space: nowrap; font-family: monospace;">{{.CreatedAt.Format "15:04:05"}}</span> - <span style="color: var(--text-primary, #e0e0e0);">{{.Message}}</span> + <div class="mt-8"> + <div class="text-[10px] font-bold text-slate-500 uppercase tracking-widest mb-3 px-1">Sync Activity</div> + <div class="space-y-1"> + {{range .}} + <div class="flex gap-4 items-baseline text-xs py-2 px-3 bg-slate-900/40 rounded border-b border-white/5 last:border-0 transition-colors hover:bg-slate-900/60"> + <span class="text-slate-500 font-mono whitespace-nowrap">{{.CreatedAt.Format "15:04:05"}}</span> + <span class="text-slate-300 leading-relaxed">{{.Message}}</span> + </div> + {{end}} </div> - {{end}} </div> {{end}} </div> diff --git a/web/templates/passkeys_list.html b/web/templates/passkeys_list.html index 4e05461..22ed724 100644 --- a/web/templates/passkeys_list.html +++ b/web/templates/passkeys_list.html @@ -1,13 +1,13 @@ {{define "passkeys_list.html"}} -<div class="items-list"> +<div class="grid gap-3"> {{if .Passkeys}} {{range .Passkeys}} - <div class="item-row" id="passkey-{{.ID}}"> - <div class="item-name"> - <strong>{{if .Name}}{{.Name}}{{else}}Passkey{{end}}</strong> - <div class="item-desc">Added {{.CreatedAt.Format "Jan 2, 2006"}}</div> + <div class="card flex items-center gap-4" id="passkey-{{.ID}}"> + <div class="flex-1 min-w-0"> + <strong class="text-white block truncate">{{if .Name}}{{.Name}}{{else}}Passkey{{end}}</strong> + <div class="text-xs text-slate-500">Added {{.CreatedAt.Format "Jan 2, 2006"}}</div> </div> - <button class="btn btn-danger btn-sm" + <button class="text-xs text-red-400 hover:text-red-300 transition-colors" hx-delete="/passkeys/{{.ID}}" hx-target="#passkey-{{.ID}}" hx-swap="outerHTML" @@ -18,7 +18,7 @@ </div> {{end}} {{else}} - <div class="empty-state">No passkeys registered. Register one to enable passwordless login.</div> + <div class="card text-center text-slate-500 py-6 text-sm italic">No passkeys registered. Register one to enable passwordless login.</div> {{end}} </div> {{end}} diff --git a/web/templates/settings.html b/web/templates/settings.html index 4df4cb0..ca1d268 100644 --- a/web/templates/settings.html +++ b/web/templates/settings.html @@ -5,228 +5,84 @@ <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="csrf-token" content="{{.CSRFToken}}"> <title>Settings - Task Dashboard</title> - <script src="https://unpkg.com/htmx.org@1.9.10"></script> + <link rel="icon" type="image/svg+xml" href="/static/favicon.svg"> + <link rel="stylesheet" href="/static/css/output.css"> + <script src="/static/js/htmx.min.js"></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 hx-headers='{"X-CSRF-Token": "{{.CSRFToken}}"}'> - - <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> +<body class="bg-slate-950 text-slate-200 min-h-screen p-4 sm:p-8" hx-headers='{"X-CSRF-Token": "{{.CSRFToken}}"}'> + <div class="max-w-4xl mx-auto"> + <a href="/" class="inline-block mb-6 text-slate-400 hover:text-white transition-colors">← Back to Dashboard</a> + <h1 class="text-4xl font-light text-white mb-2 tracking-tight">Settings</h1> + <p class="text-slate-400 mb-10">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"> + <section class="mb-12"> + <h2 class="text-xl font-medium text-white mb-6 pb-2 border-b border-white/10">Feature Toggles</h2> + <div class="grid gap-4" 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 class="card flex items-center gap-4" id="toggle-{{.Name}}"> + <div class="flex-1"> + <strong class="text-white">{{.Name}}</strong> + {{if .Description}}<div class="text-sm text-slate-400">{{.Description}}</div>{{end}} + </div> + <div class="flex items-center gap-6"> + <label class="relative inline-flex items-center cursor-pointer"> + <input type="checkbox" value="" class="sr-only peer" + {{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;"> + <div class="w-11 h-6 bg-slate-700 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div> + </label> + <button class="text-xs text-red-400 hover:text-red-300 transition-colors" + hx-delete="/settings/features/{{.Name}}" + hx-target="#toggle-{{.Name}}" + hx-swap="outerHTML" + hx-confirm="Delete feature toggle '{{.Name}}'?"> + Delete + </button> </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> + <div class="card text-center text-slate-500 py-10">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 class="mt-4 flex flex-wrap gap-3 p-4 bg-slate-900/40 rounded-xl border border-white/5" 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_]+" + class="flex-1 bg-slate-950 border border-white/10 rounded-lg px-4 py-2 text-sm focus:ring-1 focus:ring-blue-500 outline-none"> + <input type="text" name="description" placeholder="Description (optional)" + class="flex-[2] bg-slate-950 border border-white/10 rounded-lg px-4 py-2 text-sm focus:ring-1 focus:ring-blue-500 outline-none"> + <button type="submit" class="bg-blue-600 hover:bg-blue-500 text-white px-6 py-2 rounded-lg text-sm font-medium transition-colors">Add Toggle</button> </form> - </div> + </section> <!-- Passkeys Section --> {{if .WebAuthnEnabled}} - <div class="card" id="passkeys-card"> - <div class="card-header"> - <div class="card-title">Passkeys</div> + <section class="mb-12" id="passkeys-card-section"> + <h2 class="text-xl font-medium text-white mb-6 pb-2 border-b border-white/10">Passkeys</h2> + <div id="passkeys-list" hx-get="/settings/passkeys" hx-trigger="load" hx-swap="innerHTML" class="grid gap-4"> + <div class="card text-center text-slate-500 py-10">Loading passkeys...</div> </div> - <div id="passkeys-list" hx-get="/settings/passkeys" hx-trigger="load" hx-swap="innerHTML"> - <div class="empty-state">Loading passkeys...</div> + <div class="mt-4 flex flex-wrap gap-3 p-4 bg-slate-900/40 rounded-xl border border-white/5" id="passkey-register-form"> + <input type="text" id="passkey-name" placeholder="Passkey name (e.g., MacBook Touch ID)" maxlength="100" + class="flex-1 bg-slate-950 border border-white/10 rounded-lg px-4 py-2 text-sm focus:ring-1 focus:ring-blue-500 outline-none"> + <button type="button" class="bg-slate-700 hover:bg-slate-600 text-white px-6 py-2 rounded-lg text-sm font-medium transition-colors" id="register-passkey-btn">Register New Passkey</button> </div> - <div class="add-form" id="passkey-register-form"> - <input type="text" id="passkey-name" placeholder="Passkey name (e.g., MacBook Touch ID)" maxlength="100"> - <button type="button" class="btn" id="register-passkey-btn">Register New Passkey</button> - </div> - <p id="passkey-status" style="color: var(--text-secondary); font-size: 0.85em; margin-top: 8px; display: none;"></p> - </div> - {{end}} + <p id="passkey-status" class="mt-3 text-sm text-slate-400 hidden"></p> + </section> + <script> (function() { - if (!document.getElementById('passkeys-card') || !window.PublicKeyCredential) { - var card = document.getElementById('passkeys-card'); - if (card) card.style.display = 'none'; + if (!window.PublicKeyCredential) { + var section = document.getElementById('passkeys-card-section'); + if (section) section.style.display = 'none'; return; } @@ -238,7 +94,7 @@ btn.disabled = true; statusEl.style.display = 'block'; - statusEl.style.color = 'var(--text-secondary)'; + statusEl.className = 'mt-3 text-sm text-slate-400'; statusEl.textContent = 'Starting registration...'; try { @@ -279,13 +135,12 @@ }); if (!finishResp.ok) throw new Error('Registration failed'); - statusEl.style.color = 'var(--success)'; + statusEl.className = 'mt-3 text-sm text-green-400'; statusEl.textContent = 'Passkey registered successfully!'; nameInput.value = ''; - // Reload passkeys list htmx.trigger('#passkeys-list', 'load'); } catch (e) { - statusEl.style.color = 'var(--danger)'; + statusEl.className = 'mt-3 text-sm text-red-400'; if (e.name === 'NotAllowedError') { statusEl.textContent = 'Registration was cancelled.'; } else { @@ -310,66 +165,67 @@ } })(); </script> + {{end}} - <!-- Source Configuration Section --> - <h2>Data Sources</h2> - <div style="display: flex; gap: 8px; align-items: center;"> - <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> - <button class="btn btn-danger" - hx-post="/settings/clear-cache" - hx-target="#sync-log" - hx-swap="outerHTML" - hx-confirm="Clear all cached data? Next page load will fetch fresh data from all sources."> - Clear Cache - </button> - </div> - - {{template "sync-log" .SyncLog}} - - <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> + <!-- Data Sources Section --> + <section class="mb-12"> + <div class="flex flex-wrap items-center justify-between gap-4 mb-6 pb-2 border-b border-white/10"> + <h2 class="text-xl font-medium text-white">Data Sources</h2> + <div class="flex gap-3"> + <form hx-post="/settings/sync" hx-swap="outerHTML" hx-target="#sources-container" hx-indicator=".sync-indicator"> + <button type="submit" class="text-sm bg-slate-800 hover:bg-slate-700 text-white px-4 py-2 rounded-lg transition-colors"> + <span class="sync-indicator htmx-indicator mr-2">Syncing...</span> + Sync Available Sources + </button> + </form> + <button class="text-sm border border-red-500/30 text-red-400 hover:bg-red-500/10 px-4 py-2 rounded-lg transition-colors" + hx-post="/settings/clear-cache" + hx-target="#sync-log" + hx-swap="outerHTML" + hx-confirm="Clear all cached data? Next page load will fetch fresh data from all sources."> + Clear Cache + </button> </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> + </div> + + {{template "sync-log" .SyncLog}} + + <div id="sources-container" class="grid gap-8 mt-8"> + {{range .Sources}} + <div class="space-y-4"> + <h3 class="text-sm font-semibold uppercase tracking-wider text-slate-500 flex items-center gap-2"> + {{if eq . "trello"}}Trello Boards{{else if eq . "todoist"}}Todoist Projects{{else if eq . "gcal"}}Google Calendars{{else if eq . "gtasks"}}Google Task Lists{{else}}{{.}}{{end}} + </h3> + <div class="grid gap-3 sm:grid-cols-2"> + {{$configs := index $.Configs .}} + {{if $configs}} + {{range $configs}} + <div class="card flex items-center gap-4"> + <label class="relative inline-flex items-center cursor-pointer"> + <input type="checkbox" value="" class="sr-only peer" + {{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;"> + <div class="w-11 h-6 bg-slate-700 peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div> + </label> + <div class="flex-1 min-w-0"> + <div class="text-white truncate" title="{{.ItemName}}">{{.ItemName}}</div> + <div class="text-[10px] text-slate-500 font-mono truncate" title="{{.ItemID}}">{{.ItemID}}</div> + </div> + </div> + {{end}} + {{else}} + <div class="card col-span-full text-center text-slate-500 py-6 text-sm italic"> + No items found. Click "Sync Available Sources" to fetch. + </div> {{end}} - {{else}} - <div class="empty-state"> - No items configured. Click "Sync Available Sources" to fetch. - </div> - {{end}} + </div> </div> + {{end}} </div> - {{end}} - </div> + </section> </div> </body> </html> diff --git a/web/templates/shopping-mode.html b/web/templates/shopping-mode.html index b2f129a..3a1a3bc 100644 --- a/web/templates/shopping-mode.html +++ b/web/templates/shopping-mode.html @@ -8,12 +8,6 @@ <link rel="stylesheet" href="/static/css/output.css"> <script src="/static/js/htmx.min.js"></script> <style> - body { - background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%); - min-height: 100vh; - margin: 0; - padding: 0; - } .store-switcher { scrollbar-width: none; -ms-overflow-style: none; @@ -25,6 +19,7 @@ background: rgba(34, 197, 94, 0.2); border-radius: 8px; } + .text-shadow-sm { text-shadow: 0 1px 2px rgba(0,0,0,0.5); } @media print { * { text-shadow: none !important; @@ -74,7 +69,7 @@ } </style> </head> -<body class="text-white" hx-headers='{"X-CSRF-Token": "{{.CSRFToken}}"}'> +<body class="bg-slate-950 text-slate-200" hx-headers='{"X-CSRF-Token": "{{.CSRFToken}}"}'> <!-- Header --> <header class="sticky top-0 z-50 bg-black/40 backdrop-blur-lg border-b border-white/10 no-print"> <div class="flex items-center justify-between px-4 py-3"> |
