diff --git a/code/internal/server/server_test.go b/code/internal/server/server_test.go index a1bf8a3..8f305d4 100644 --- a/code/internal/server/server_test.go +++ b/code/internal/server/server_test.go @@ -1563,7 +1563,7 @@ func TestDashboard_HasPrijavaNav(t *testing.T) { // --- T21: UI tests --- -func TestDashboard_DetailOpenClassInJS(t *testing.T) { +func TestDashboard_EscapeClosesOverlay(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodGet, "/", nil) @@ -1571,13 +1571,12 @@ func TestDashboard_DetailOpenClassInJS(t *testing.T) { srv.Router.ServeHTTP(w, req) body := w.Body.String() - // JS should add detail-open class when detail panel opens - if !containsStr(body, "detail-open") { - t.Error("expected 'detail-open' body class in dashboard JS") + if !containsStr(body, "Escape") { + t.Error("expected Escape key handler for overlay") } } -func TestDashboard_ClickOutsideClosesDetail(t *testing.T) { +func TestDashboard_BackdropClickClosesOverlay(t *testing.T) { srv := setupTestServer(t) req := httptest.NewRequest(http.MethodGet, "/", nil) @@ -1585,9 +1584,21 @@ func TestDashboard_ClickOutsideClosesDetail(t *testing.T) { srv.Router.ServeHTTP(w, req) body := w.Body.String() - // Should have click-outside handler for detail panel - if !containsStr(body, "closest('.task-card')") { - t.Error("expected click-outside handler for detail panel") + if !containsStr(body, "e.target === this") { + t.Error("expected backdrop click handler for overlay") + } +} + +func TestTaskDetail_HasInnerWrapper(t *testing.T) { + srv := setupTestServer(t) + + req := httptest.NewRequest(http.MethodGet, "/task/T01", nil) + w := httptest.NewRecorder() + srv.Router.ServeHTTP(w, req) + + body := w.Body.String() + if !containsStr(body, "detail-inner") { + t.Error("expected detail-inner wrapper in task detail") } } diff --git a/code/web/static/style.css b/code/web/static/style.css index 1d30606..9eed076 100644 --- a/code/web/static/style.css +++ b/code/web/static/style.css @@ -86,40 +86,45 @@ body { margin-top: 4px; } -/* Task detail panel — 50% of screen */ +/* Task detail overlay (modal) */ #task-detail { + display: none; position: fixed; top: 0; - right: -50%; - width: 50%; + left: 0; + width: 100%; height: 100vh; - background: #16213e; - border-left: 2px solid #0f3460; - padding: 20px; - overflow-y: auto; - z-index: 10; - transition: right 0.3s ease; + z-index: 50; + background: rgba(0, 0, 0, 0.6); + justify-content: center; + align-items: center; } #task-detail.active { - right: 0; + display: flex; } -/* Board compresses when detail is open */ -.board { - transition: margin-right 0.3s ease; -} - -body.detail-open .board { - margin-right: 50%; +.detail-inner { + background: #16213e; + border: 1px solid #0f3460; + border-radius: 10px; + padding: 24px; + width: 700px; + max-width: 90%; + max-height: 85vh; + overflow-y: auto; + position: relative; } .detail-close { cursor: pointer; - float: right; - font-size: 1.2em; + position: absolute; + top: 12px; + right: 16px; + font-size: 1.4em; color: #888; padding: 4px 8px; + line-height: 1; } .detail-close:hover { color: #e94560; } @@ -822,8 +827,6 @@ textarea.form-input { @media (max-width: 700px) { .board { grid-template-columns: repeat(2, 1fr); } - #task-detail { width: 100%; right: -100%; } - body.detail-open .board { margin-right: 0; } .docs-layout { grid-template-columns: 1fr; } } diff --git a/code/web/templates/layout.html b/code/web/templates/layout.html index 77ee2a4..646305b 100644 --- a/code/web/templates/layout.html +++ b/code/web/templates/layout.html @@ -37,7 +37,6 @@ document.body.addEventListener('htmx:afterSwap', function(e) { if (e.detail.target.id === 'task-detail') { e.detail.target.classList.add('active'); - document.body.classList.add('detail-open'); } }); @@ -45,9 +44,25 @@ function closeDetail() { var el = document.getElementById('task-detail'); el.classList.remove('active'); el.innerHTML = ''; - document.body.classList.remove('detail-open'); } +// Close overlay on Escape +document.addEventListener('keydown', function(e) { + if (e.key === 'Escape') { + var detail = document.getElementById('task-detail'); + if (detail && detail.classList.contains('active')) { + closeDetail(); + } + } +}); + +// Close overlay on backdrop click +document.getElementById('task-detail').addEventListener('click', function(e) { + if (e.target === this) { + closeDetail(); + } +}); + function showToast(msg, type) { var toast = document.getElementById('toast'); toast.textContent = msg; @@ -85,12 +100,6 @@ document.addEventListener('DOMContentLoaded', function() { if (wrapper && !wrapper.contains(e.target)) { results.innerHTML = ''; } - - // Close detail panel on click outside - var detail = document.getElementById('task-detail'); - if (detail && detail.classList.contains('active') && !detail.contains(e.target) && !e.target.closest('.task-card')) { - closeDetail(); - } }); }); diff --git a/code/web/templates/partials/task-detail.html b/code/web/templates/partials/task-detail.html index 809d903..fbe6be9 100644 --- a/code/web/templates/partials/task-detail.html +++ b/code/web/templates/partials/task-detail.html @@ -1,28 +1,30 @@ {{define "task-detail"}} -✕ -