mirror of
https://github.com/Heretek-AI/open-code-review.git
synced 2026-11-07 10:56:07 -10:00
feat(viewer): add back navigation to improve page traversal UX (#1217)
* feat(viewer): add back navigation * fix(viewer): refine back navigation layout and test coverage * feat(viewer): add back navigation to the compare page * style(viewer): use icon-only back navigation
This commit is contained in:
6 files changed
+112
-14
No files matched your search
@@ -208,6 +208,14 @@ func TestHandleCompare(t *testing.T) {
|
||||
contains: []string{"New (1)", "Persisting (1)", "Resolved (1)", "Not reviewed (0)",
|
||||
"newly broken", "still broken", "was broken", "none"},
|
||||
},
|
||||
{
|
||||
name: "back link returns to sessions",
|
||||
query: "before=s1&after=s2",
|
||||
status: http.StatusOK,
|
||||
contains: []string{
|
||||
`<a class="back-link" href="/r/myrepo" aria-label="Back to sessions">`,
|
||||
},
|
||||
},
|
||||
{
|
||||
// The CLI renders a suggested patch as a diff (renderComment ->
|
||||
// buildDiffLines); dropping it here would leave the page showing
|
||||
|
||||
@@ -105,18 +105,39 @@ func TestRenderTemplate_BadTemplate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRenderTemplate_Sessions(t *testing.T) {
|
||||
rr := httptest.NewRecorder()
|
||||
renderTemplate(rr, "sessions.html", sessionsData{
|
||||
EncodedRepo: "test-repo",
|
||||
RepoName: "MyProject",
|
||||
Sessions: []SessionSummary{},
|
||||
})
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Errorf("status = %d, want 200", rr.Code)
|
||||
tests := []struct {
|
||||
name string
|
||||
sessions []SessionSummary
|
||||
}{
|
||||
{name: "empty", sessions: []SessionSummary{}},
|
||||
{name: "populated", sessions: []SessionSummary{{SessionID: "session-123", GitBranch: "main"}}},
|
||||
}
|
||||
if !strings.Contains(rr.Body.String(), "MyProject") {
|
||||
t.Errorf("expected repo name in sessions template")
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
rr := httptest.NewRecorder()
|
||||
renderTemplate(rr, "sessions.html", sessionsData{
|
||||
EncodedRepo: "test-repo",
|
||||
RepoName: "MyProject",
|
||||
Sessions: tt.sessions,
|
||||
})
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Errorf("status = %d, want 200", rr.Code)
|
||||
}
|
||||
body := rr.Body.String()
|
||||
if len(tt.sessions) > 0 && !strings.Contains(body, `href="/r/test-repo/session-123"`) {
|
||||
t.Errorf("expected populated session link in rendered output")
|
||||
}
|
||||
if !strings.Contains(body, "MyProject") {
|
||||
t.Errorf("expected repo name in sessions template")
|
||||
}
|
||||
if !strings.Contains(body, `<a class="back-link" href="/" aria-label="Back to repositories">`) {
|
||||
t.Errorf("expected back link to repositories in sessions template")
|
||||
}
|
||||
if !strings.Contains(body, `<a href="/" class="nav-brand">`) {
|
||||
t.Errorf("expected breadcrumb navigation to remain in sessions template")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,6 +175,13 @@ func TestRenderTemplate_SessionPage(t *testing.T) {
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Errorf("status = %d, want 200", rr.Code)
|
||||
}
|
||||
body := rr.Body.String()
|
||||
if !strings.Contains(body, `<a class="back-link" href="/r/repo" aria-label="Back to sessions">`) {
|
||||
t.Errorf("expected back link to repository sessions in session template")
|
||||
}
|
||||
if !strings.Contains(body, `<a href="/r/repo">MyRepo</a>`) {
|
||||
t.Errorf("expected breadcrumb navigation to remain in session template")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderTemplate_SecondarySectionsCollapsedByDefault(t *testing.T) {
|
||||
|
||||
@@ -344,6 +344,58 @@ h3 {
|
||||
font-size: 0.82em;
|
||||
}
|
||||
|
||||
/* ── Page heading ── */
|
||||
/* Keep the back link reachable beside long repository and session titles. */
|
||||
.page-heading {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.75rem;
|
||||
min-width: 0;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
.page-heading > h2 {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
margin-bottom: 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.back-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 0 0 auto;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
padding: 0;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
background: var(--surface-alt);
|
||||
color: var(--link);
|
||||
text-decoration: none;
|
||||
transition: color var(--transition), background var(--transition), border-color var(--transition);
|
||||
}
|
||||
|
||||
.back-link::before {
|
||||
content: "";
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background: currentColor;
|
||||
-webkit-mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cline%20x1%3D%2219%22%20y1%3D%2212%22%20x2%3D%225%22%20y2%3D%2212%22%2F%3E%3Cpolyline%20points%3D%2212%2019%205%2012%2012%205%22%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
|
||||
mask: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22none%22%20stroke%3D%22black%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%3E%3Cline%20x1%3D%2219%22%20y1%3D%2212%22%20x2%3D%225%22%20y2%3D%2212%22%2F%3E%3Cpolyline%20points%3D%2212%2019%205%2012%2012%205%22%2F%3E%3C%2Fsvg%3E") center / contain no-repeat;
|
||||
}
|
||||
.back-link:hover {
|
||||
color: var(--accent-hover);
|
||||
background: var(--accent-soft);
|
||||
border-color: var(--link);
|
||||
}
|
||||
.back-link:focus-visible {
|
||||
outline: 2px solid var(--link);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
.session-header .page-heading {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
/* ── Token Usage Summary ── */
|
||||
.token-summary {
|
||||
background: var(--surface);
|
||||
@@ -1232,6 +1284,7 @@ p {
|
||||
main { padding: 0 1rem; margin: 1.25rem auto; }
|
||||
nav.breadcrumb { padding: 0 1rem; }
|
||||
.session-header { padding: 1.25rem; }
|
||||
.page-heading { gap: 0.5rem; }
|
||||
.token-stats { grid-template-columns: repeat(2, 1fr); }
|
||||
.meta { gap: 0.5rem 1rem; font-size: 0.8rem; }
|
||||
.table th, .table td { padding: 0.6rem 0.75rem; font-size: 0.82rem; }
|
||||
|
||||
@@ -11,7 +11,10 @@
|
||||
<nav class="breadcrumb"><a href="/" class="nav-brand"><span class="brand-icon"></span>Open Code Review Viewer</a><span class="sep">/</span><a href="/r/{{.EncodedRepo}}">{{.RepoName}}</a><span class="sep">/</span><span class="current">compare</span></nav>
|
||||
<main>
|
||||
<div class="session-header">
|
||||
<h2>Compare sessions</h2>
|
||||
<div class="page-heading">
|
||||
<a class="back-link" href="/r/{{.EncodedRepo}}" aria-label="Back to sessions"></a>
|
||||
<h2>Compare sessions</h2>
|
||||
</div>
|
||||
<div class="meta">
|
||||
<span><strong>Before:</strong> <a href="/r/{{.EncodedRepo}}/{{.Before.SessionID}}"><code>{{truncate 12 .Before.SessionID}}</code></a></span>
|
||||
<span><strong>After:</strong> <a href="/r/{{.EncodedRepo}}/{{.After.SessionID}}"><code>{{truncate 12 .After.SessionID}}</code></a></span>
|
||||
|
||||
@@ -11,7 +11,10 @@
|
||||
<nav class="breadcrumb"><a href="/" class="nav-brand"><span class="brand-icon"></span>Open Code Review Viewer</a><span class="sep">/</span><a href="/r/{{.EncodedRepo}}">{{.RepoName}}</a><span class="sep">/</span><span class="current">{{.Session.Summary.SessionID | truncate 12}}</span></nav>
|
||||
<main>
|
||||
<div class="session-header">
|
||||
<h2>Session: {{.Session.Summary.SessionID}}</h2>
|
||||
<div class="page-heading">
|
||||
<a class="back-link" href="/r/{{.EncodedRepo}}" aria-label="Back to sessions"></a>
|
||||
<h2>Session: {{.Session.Summary.SessionID}}</h2>
|
||||
</div>
|
||||
<div class="meta">
|
||||
<span><strong>CWD:</strong> {{.Session.Summary.CWD}}</span>
|
||||
<span><strong>Branch:</strong> {{.Session.Summary.GitBranch}}</span>
|
||||
|
||||
@@ -10,7 +10,10 @@
|
||||
<body>
|
||||
<nav class="breadcrumb"><a href="/" class="nav-brand"><span class="brand-icon"></span>Open Code Review Viewer</a><span class="sep">/</span><span class="current">{{.RepoName}}</span></nav>
|
||||
<main>
|
||||
<h2>Sessions: {{.RepoName}}</h2>
|
||||
<div class="page-heading">
|
||||
<a class="back-link" href="/" aria-label="Back to repositories"></a>
|
||||
<h2>Sessions: {{.RepoName}}</h2>
|
||||
</div>
|
||||
{{if .Sessions}}
|
||||
<table class="table">
|
||||
<thead><tr><th>Session ID</th><th>Branch</th><th>Mode</th><th>Model</th><th>Files</th><th>Status</th><th>Comments</th><th>Duration</th><th>Started At</th><th>Compare</th></tr></thead>
|
||||
|
||||
Reference in new issue
Block a user