diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-01-21 22:53:37 -1000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-01-21 22:53:37 -1000 |
| commit | 583f90c5dedf0235fa45557359b0e6e7dd62b0f0 (patch) | |
| tree | 304e4527b6668669197fc9ffdf2ffc87566478f0 /web/templates/index.html | |
| parent | dd4689a71de8f1c0b5a2d483827411a9645ad66a (diff) | |
Implement 10 UI/UX improvements and bug fixes
- Fix outdated Todoist task URL format (showTask -> app/task)
- Fix quick-add date defaulting to tomorrow in evening (client-side JS)
- Add tap-to-expand for task descriptions with checkbox completion
- Add visual differentiation: overdue (red), future (gray), today (normal)
- Sort tasks by urgency: overdue > today-timed > today-allday > future
- Keep completed tasks visible with strikethrough until refresh
- Add random Unsplash landscape background with content overlay
- Hide future tasks behind collapsible fold with count badge
- Unified modal menu for Quick Add + Bug Report (Ctrl+K shortcut)
- Click task title to edit description in modal
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'web/templates/index.html')
| -rw-r--r-- | web/templates/index.html | 130 |
1 files changed, 115 insertions, 15 deletions
diff --git a/web/templates/index.html b/web/templates/index.html index 18aa56b..6732ffd 100644 --- a/web/templates/index.html +++ b/web/templates/index.html @@ -7,8 +7,8 @@ <link rel="icon" type="image/svg+xml" href="/static/favicon.svg"> <link rel="stylesheet" href="/static/css/output.css"> </head> -<body class="min-h-screen" hx-headers='{"X-CSRF-Token": "{{.CSRFToken}}"}'> - <div class="content-max-width py-3 sm:py-6"> +<body class="min-h-screen bg-gray-800" style="background-image: url('{{.BackgroundURL}}'); background-size: cover; background-position: center; background-attachment: fixed;" hx-headers='{"X-CSRF-Token": "{{.CSRFToken}}"}'> + <div class="content-max-width py-3 sm:py-6 bg-white/80 backdrop-blur-sm rounded-lg my-4 sm:my-6 shadow-lg"> <!-- Minimal Header --> <header class="flex mb-4 sm:mb-6 justify-between items-center no-print"> <button onclick="refreshData()" @@ -66,26 +66,66 @@ </div> </div> - <!-- Bug Report Button --> - <button onclick="document.getElementById('bug-modal').classList.remove('hidden')" - class="fixed bottom-4 right-4 bg-red-500 hover:bg-red-600 text-white p-3 rounded-full shadow-lg no-print" - title="Report a bug"> - 🐛 + <!-- Unified Action Button (FAB) --> + <button onclick="openActionModal()" + class="fixed bottom-4 right-4 bg-primary-600 hover:bg-primary-700 text-white p-4 rounded-full shadow-lg no-print" + title="Quick Actions (Ctrl+K)"> + <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path> + </svg> </button> - <!-- Bug Report Modal --> - <div id="bug-modal" class="hidden fixed inset-0 bg-black/50 flex items-center justify-center p-4 z-50"> + <!-- Unified Action Modal --> + <div id="action-modal" class="hidden fixed inset-0 bg-black/50 flex items-center justify-center p-4 z-50"> <div class="bg-white rounded-lg shadow-xl max-w-md w-full max-h-[80vh] overflow-hidden"> <div class="p-4 border-b border-gray-200 flex justify-between items-center"> - <h2 class="font-semibold text-gray-900">Report Bug</h2> - <button onclick="document.getElementById('bug-modal').classList.add('hidden')" - class="text-gray-400 hover:text-gray-600">✕</button> + <div class="flex gap-2"> + <button onclick="switchActionTab('add')" id="tab-add" + class="px-3 py-1 rounded-lg text-sm font-medium bg-primary-100 text-primary-700"> + ✓ Quick Add + </button> + <button onclick="switchActionTab('bug')" id="tab-bug" + class="px-3 py-1 rounded-lg text-sm font-medium text-gray-600 hover:bg-gray-100"> + 🐛 Bug + </button> + </div> + <button onclick="closeActionModal()" class="text-gray-400 hover:text-gray-600">✕</button> + </div> + + <!-- Quick Add Tab --> + <div id="panel-add" class="p-4"> + <form hx-post="/unified-add" + hx-swap="none" + hx-on::after-request="if(event.detail.successful) { this.reset(); closeActionModal(); htmx.trigger(document.body, 'refresh-tasks'); }"> + <input type="text" + name="title" + placeholder="Task name..." + class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm mb-3" + required + autofocus> + <div class="flex gap-2 mb-3"> + <input type="date" + name="due_date" + id="modal-add-date" + class="flex-1 border border-gray-300 rounded-lg px-3 py-2 text-sm"> + <select name="source" class="border border-gray-300 rounded-lg px-3 py-2 text-sm"> + <option value="todoist">Todoist</option> + <option value="trello">Trello</option> + </select> + </div> + <button type="submit" + class="w-full bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-lg text-sm font-medium"> + Add Task + </button> + </form> </div> - <div class="p-4"> + + <!-- Bug Report Tab --> + <div id="panel-bug" class="p-4 hidden"> <form hx-post="/bugs" hx-target="#bug-list" hx-swap="innerHTML" - hx-on::after-request="if(event.detail.successful) this.reset()"> + hx-on::after-request="if(event.detail.successful) { this.reset(); closeActionModal(); }"> <textarea name="description" placeholder="Describe the bug..." class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm mb-3 h-24" @@ -98,7 +138,7 @@ <div class="mt-4 border-t border-gray-200 pt-4"> <h3 class="text-sm font-medium text-gray-700 mb-2">Recent Reports</h3> <div id="bug-list" - class="max-h-48 overflow-y-auto" + class="max-h-32 overflow-y-auto" hx-get="/bugs" hx-trigger="load"> <p class="text-gray-400 text-sm">Loading...</p> @@ -108,6 +148,66 @@ </div> </div> + <script> + function openActionModal() { + document.getElementById('action-modal').classList.remove('hidden'); + var dateInput = document.getElementById('modal-add-date'); + if (dateInput) { + var d = new Date(); + dateInput.value = d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0'); + } + setTimeout(function() { + var input = document.querySelector('#panel-add input[name="title"]'); + if (input && !document.getElementById('panel-add').classList.contains('hidden')) input.focus(); + }, 100); + } + function closeActionModal() { + document.getElementById('action-modal').classList.add('hidden'); + } + function switchActionTab(tab) { + document.getElementById('panel-add').classList.toggle('hidden', tab !== 'add'); + document.getElementById('panel-bug').classList.toggle('hidden', tab !== 'bug'); + document.getElementById('tab-add').classList.toggle('bg-primary-100', tab === 'add'); + document.getElementById('tab-add').classList.toggle('text-primary-700', tab === 'add'); + document.getElementById('tab-add').classList.toggle('text-gray-600', tab !== 'add'); + document.getElementById('tab-bug').classList.toggle('bg-red-100', tab === 'bug'); + document.getElementById('tab-bug').classList.toggle('text-red-700', tab === 'bug'); + document.getElementById('tab-bug').classList.toggle('text-gray-600', tab !== 'bug'); + } + // Keyboard shortcut: Ctrl+K or Cmd+K + document.addEventListener('keydown', function(e) { + if ((e.ctrlKey || e.metaKey) && e.key === 'k') { + e.preventDefault(); + var modal = document.getElementById('action-modal'); + if (modal.classList.contains('hidden')) { + openActionModal(); + } else { + closeActionModal(); + } + } + if (e.key === 'Escape') { + closeActionModal(); + closeTaskModal(); + } + }); + function closeTaskModal() { + document.getElementById('task-edit-modal').classList.add('hidden'); + } + </script> + + <!-- Task Edit Modal --> + <div id="task-edit-modal" class="hidden fixed inset-0 bg-black/50 flex items-center justify-center p-4 z-50"> + <div class="bg-white rounded-lg shadow-xl max-w-md w-full max-h-[80vh] overflow-hidden"> + <div class="p-4 border-b border-gray-200 flex justify-between items-center"> + <h2 class="font-semibold text-gray-900">Edit Task</h2> + <button onclick="closeTaskModal()" class="text-gray-400 hover:text-gray-600">✕</button> + </div> + <div id="task-edit-content"> + <p class="p-4 text-gray-500 text-sm">Loading...</p> + </div> + </div> + </div> + <script src="/static/js/htmx.min.js"></script> <script src="/static/js/app.js"></script> </body> |
