From f137703f1b4b1340bc50a62a9933ada3783442e9 Mon Sep 17 00:00:00 2001 From: djuka Date: Fri, 20 Feb 2026 13:28:29 +0000 Subject: [PATCH] =?UTF-8?q?T21:=20UI=20pobolj=C5=A1anja=20=E2=80=94=20deta?= =?UTF-8?q?lj=20panel=2050%,=20konzola=20fullscreen,=20docs=20full=20heigh?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- TASKS/reports/T21-report.md | 56 +++++++++++++++++++ code/internal/server/server_test.go | 83 +++++++++++++++++++++++++++++ code/web/static/style.css | 35 +++++++++--- code/web/templates/console.html | 8 +-- code/web/templates/layout.html | 8 +++ 5 files changed, 179 insertions(+), 11 deletions(-) create mode 100644 TASKS/reports/T21-report.md diff --git a/TASKS/reports/T21-report.md b/TASKS/reports/T21-report.md new file mode 100644 index 0000000..069cb41 --- /dev/null +++ b/TASKS/reports/T21-report.md @@ -0,0 +1,56 @@ +# T21 Izveštaj: UI poboljšanja (konzola, task detalj, layout) + +**Agent:** coder +**Model:** Opus +**Datum:** 2026-02-20 + +--- + +## Šta je urađeno + +Tri UI poboljšanja: task detalj panel 50% ekrana, konzola fullscreen, dokumenti full height. + +### Izmenjeni fajlovi + +| Fajl | Izmena | +|------|--------| +| `web/static/style.css` | #task-detail 50% width, body.detail-open board compression, console fullscreen, docs full height, sidebar/main scroll | +| `web/templates/layout.html` | detail-open body class toggle, click-outside-to-close za detalj panel | +| `web/templates/console.html` | Toolbar premešten iznad panela | +| `internal/server/server_test.go` | 5 novih testova | + +### 1. Task detalj panel — 50% ekrana + +- Panel se otvara na 50% širine ekrana (umesto fiksnih 420px) +- Board se kompresuje na 50% kad je panel otvoren (`body.detail-open .board { margin-right: 50% }`) +- Klik van panela zatvara panel (osim klika na task karticu) +- Na mobilnom (<700px) panel je 100% kao overlay +- Tranzicija: slide sa desne strane (0.3s ease) + +### 2. Konzola — fullscreen layout + +- Toolbar (+ Sesija 2) premešten iznad panela za bolji UX +- `min-height: 80vh` na kontejneru +- Input red: `flex-shrink: 0` za uvek vidljiv input +- Toolbar: `flex-shrink: 0` za stabilnu poziciju + +### 3. Dokumenti — full height + +- `docs-container`: `height: calc(100vh - 60px)` umesto `min-height: 80vh` +- `docs-layout`: `flex: 1; min-height: 0` za ispravno popunjavanje prostora +- `docs-sidebar` i `docs-main`: `overflow-y: auto` za nezavisni scroll + +### Novi testovi — 5 PASS + +``` +TestDashboard_DetailOpenClassInJS PASS +TestDashboard_ClickOutsideClosesDetail PASS +TestConsolePage_ToolbarAbovePanels PASS +TestConsolePage_HasSessionToggle PASS +TestDocsPage_HasFullHeightLayout PASS +``` + +### Ukupno projekat: 142 testova, svi prolaze + +- `go vet ./...` — čist +- `go build ./...` — prolazi diff --git a/code/internal/server/server_test.go b/code/internal/server/server_test.go index ce0708a..4308ed5 100644 --- a/code/internal/server/server_test.go +++ b/code/internal/server/server_test.go @@ -1314,6 +1314,89 @@ func TestRewriteLinksSimple_NestedDir(t *testing.T) { } } +func TestDashboard_DetailOpenClassInJS(t *testing.T) { + srv := setupTestServer(t) + + req := httptest.NewRequest(http.MethodGet, "/", nil) + w := httptest.NewRecorder() + 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") + } +} + +func TestDashboard_ClickOutsideClosesDetail(t *testing.T) { + srv := setupTestServer(t) + + req := httptest.NewRequest(http.MethodGet, "/", nil) + w := httptest.NewRecorder() + 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") + } +} + +func TestConsolePage_ToolbarAbovePanels(t *testing.T) { + srv := setupTestServer(t) + + req := httptest.NewRequest(http.MethodGet, "/console", nil) + w := httptest.NewRecorder() + srv.Router.ServeHTTP(w, req) + + body := w.Body.String() + // Toolbar should appear before console-panels in the HTML + toolbarIdx := strings.Index(body, "console-toolbar") + panelsIdx := strings.Index(body, "console-panels") + + if toolbarIdx == -1 { + t.Fatal("expected console-toolbar in console page") + } + if panelsIdx == -1 { + t.Fatal("expected console-panels in console page") + } + if toolbarIdx > panelsIdx { + t.Error("expected toolbar before panels in console HTML") + } +} + +func TestConsolePage_HasSessionToggle(t *testing.T) { + srv := setupTestServer(t) + + req := httptest.NewRequest(http.MethodGet, "/console", nil) + w := httptest.NewRecorder() + srv.Router.ServeHTTP(w, req) + + body := w.Body.String() + if !containsStr(body, "togglePanel2") { + t.Error("expected togglePanel2 button in console page") + } + if !containsStr(body, `+ Sesija 2`) { + t.Error("expected '+ Sesija 2' toggle button") + } +} + +func TestDocsPage_HasFullHeightLayout(t *testing.T) { + srv := setupTestServer(t) + + req := httptest.NewRequest(http.MethodGet, "/docs", nil) + w := httptest.NewRecorder() + srv.Router.ServeHTTP(w, req) + + body := w.Body.String() + if !containsStr(body, "docs-container") { + t.Error("expected docs-container class") + } + if !containsStr(body, "docs-layout") { + t.Error("expected docs-layout class for grid layout") + } +} + func containsStr(s, substr string) bool { return len(s) >= len(substr) && findStr(s, substr) } diff --git a/code/web/static/style.css b/code/web/static/style.css index db9cf15..db0a81c 100644 --- a/code/web/static/style.css +++ b/code/web/static/style.css @@ -86,12 +86,12 @@ body { margin-top: 4px; } -/* Task detail panel */ +/* Task detail panel — 50% of screen */ #task-detail { position: fixed; top: 0; - right: -420px; - width: 420px; + right: -50%; + width: 50%; height: 100vh; background: #16213e; border-left: 2px solid #0f3460; @@ -105,6 +105,15 @@ body { right: 0; } +/* Board compresses when detail is open */ +.board { + transition: margin-right 0.3s ease; +} + +body.detail-open .board { + margin-right: 50%; +} + .detail-close { cursor: pointer; float: right; @@ -386,17 +395,20 @@ body { border-color: #e94560; } -/* Docs */ +/* Docs — full height */ .docs-container { padding: 16px 24px; - min-height: 80vh; + height: calc(100vh - 60px); + display: flex; + flex-direction: column; } .docs-layout { display: grid; grid-template-columns: 25% 1fr; gap: 16px; - min-height: 80vh; + flex: 1; + min-height: 0; } .docs-sidebar h2 { @@ -450,8 +462,13 @@ body { margin: 0 4px; } +.docs-sidebar { + overflow-y: auto; +} + .docs-main { min-width: 0; + overflow-y: auto; } .docs-content { @@ -536,12 +553,13 @@ body { margin: 16px 0; } -/* Console */ +/* Console — fullscreen */ .console-container { padding: 16px; height: calc(100vh - 60px); display: flex; flex-direction: column; + min-height: 80vh; } .console-panels { @@ -624,6 +642,7 @@ body { gap: 4px; padding: 8px; border-top: 1px solid #0f3460; + flex-shrink: 0; } .console-input { @@ -650,6 +669,7 @@ body { padding: 8px 0; display: flex; gap: 8px; + flex-shrink: 0; } /* Responsive */ @@ -660,6 +680,7 @@ body { @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/console.html b/code/web/templates/console.html index 48b8a23..598d238 100644 --- a/code/web/templates/console.html +++ b/code/web/templates/console.html @@ -21,6 +21,10 @@
+
+ +
+
@@ -48,10 +52,6 @@
- -
- -