Skip to content

Commit fac65c3

Browse files
etoyamaclaude
andcommitted
feat(#26): improve review workflow UX with guidance and batch support
- Add status-based workflow guidance to OverviewPanel (active/review/final) - Replace inline comment history with ReviewHistoryPanel in HistoryPage - Show explicit save destination in KnowledgePanel confirmation message - Update E2E tests to use review-batch API fixtures Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 643e8de commit fac65c3

5 files changed

Lines changed: 74 additions & 82 deletions

File tree

frontend/e2e/design-detail.spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { test, expect } from "@playwright/test";
22
import {
33
makeDesign,
4-
makeComment,
54
makeKnowledgeEntry,
65
makeBatchComment,
76
makeReviewBatch,
@@ -208,7 +207,7 @@ test("#10: save knowledge shows confirmation", async ({ page }) => {
208207

209208
// Now save
210209
await page.getByRole("button", { name: "Save Knowledge" }).click();
211-
await expect(page.getByText(/saved successfully/i)).toBeVisible({
210+
await expect(page.getByText(/knowledge saved to project rules/i)).toBeVisible({
212211
timeout: 5000,
213212
});
214213
});

frontend/e2e/history.spec.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { test, expect } from "@playwright/test";
2-
import { makeDesign, makeComment } from "./fixtures/mock-data";
2+
import { makeDesign, makeReviewBatch, makeBatchComment } from "./fixtures/mock-data";
33

44
// #21: Timeline display — all designs in updated_at descending order
55
test("#21: history timeline shows designs in descending order", async ({
@@ -38,23 +38,23 @@ test("#21: history timeline shows designs in descending order", async ({
3838
expect(firstCardText).toContain("New Design");
3939
});
4040

41-
// #22: History expand — click entry, review comment history shown
42-
test("#22: clicking history entry shows review comments", async ({ page }) => {
41+
// #22: History expand — click entry, review batch history shown
42+
test("#22: clicking history entry shows review batches", async ({ page }) => {
4343
const design = makeDesign({ id: "d-hist", title: "History Design" });
44-
const comments = [
45-
makeComment({
46-
id: "c-h1",
44+
const batches = [
45+
makeReviewBatch({
46+
id: "RB-h1",
4747
design_id: "d-hist",
48-
comment: "First review note",
49-
reviewer: "alice",
5048
status_after: "supported",
49+
reviewer: "alice",
50+
comments: [makeBatchComment({ comment: "First review note" })],
5151
}),
52-
makeComment({
53-
id: "c-h2",
52+
makeReviewBatch({
53+
id: "RB-h2",
5454
design_id: "d-hist",
55-
comment: "Second review note",
56-
reviewer: "bob",
5755
status_after: "rejected",
56+
reviewer: "bob",
57+
comments: [makeBatchComment({ comment: "Second review note" })],
5858
}),
5959
];
6060

@@ -66,9 +66,9 @@ test("#22: clicking history entry shows review comments", async ({ page }) => {
6666
}
6767
return route.continue();
6868
});
69-
await page.route(`**/api/designs/${design.id}/comments`, (route) =>
69+
await page.route(`**/api/designs/${design.id}/review-batches`, (route) =>
7070
route.fulfill({
71-
json: { design_id: design.id, comments, count: comments.length },
71+
json: { design_id: design.id, batches, count: batches.length },
7272
}),
7373
);
7474

@@ -78,7 +78,7 @@ test("#22: clicking history entry shows review comments", async ({ page }) => {
7878
// Click to expand
7979
await page.getByText("History Design").click();
8080

81-
// Review comments should appear
81+
// Review batch comments should appear
8282
await expect(page.getByText("First review note")).toBeVisible({
8383
timeout: 5000,
8484
});

frontend/src/pages/HistoryPage.tsx

Lines changed: 4 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,16 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
33
import { StatusBadge } from "@/components/StatusBadge";
44
import { EmptyState } from "@/components/EmptyState";
55
import { ErrorBanner } from "@/components/ErrorBanner";
6-
import { listDesigns, listComments } from "@/api/client";
7-
import type { Design, ReviewComment } from "@/types/api";
6+
import { listDesigns } from "@/api/client";
7+
import type { Design } from "@/types/api";
88
import { formatDateTime } from "@/lib/utils";
9+
import { ReviewHistoryPanel } from "@/pages/design-detail/components/ReviewHistoryPanel";
910

1011
export function HistoryPage() {
1112
const [designs, setDesigns] = useState<Design[]>([]);
1213
const [loading, setLoading] = useState(true);
1314
const [error, setError] = useState<string | null>(null);
14-
1515
const [expandedId, setExpandedId] = useState<string | null>(null);
16-
const [comments, setComments] = useState<ReviewComment[]>([]);
17-
const [commentsLoading, setCommentsLoading] = useState(false);
18-
const [commentsError, setCommentsError] = useState<string | null>(null);
1916

2017
useEffect(() => {
2118
const ctrl = new AbortController();
@@ -33,23 +30,6 @@ export function HistoryPage() {
3330
return () => ctrl.abort();
3431
}, []);
3532

36-
useEffect(() => {
37-
if (!expandedId) {
38-
setComments([]);
39-
return;
40-
}
41-
const ctrl = new AbortController();
42-
setCommentsLoading(true);
43-
setCommentsError(null);
44-
listComments(expandedId, ctrl.signal)
45-
.then((res) => setComments(res.comments))
46-
.catch((err) => {
47-
if (err.name !== "AbortError") setCommentsError(err.message);
48-
})
49-
.finally(() => setCommentsLoading(false));
50-
return () => ctrl.abort();
51-
}, [expandedId]);
52-
5333
const handleToggle = (id: string) => {
5434
setExpandedId((prev) => (prev === id ? null : id));
5535
};
@@ -81,7 +61,6 @@ export function HistoryPage() {
8161
{designs.map((design) => {
8262
const isCreated = design.created_at === design.updated_at;
8363
const isExpanded = expandedId === design.id;
84-
const showEmptyComments = isExpanded && !commentsLoading && !commentsError && comments.length === 0;
8564

8665
return (
8766
<Card
@@ -102,35 +81,7 @@ export function HistoryPage() {
10281

10382
{isExpanded && (
10483
<CardContent onClick={(e) => e.stopPropagation()}>
105-
<h3 className="mb-2 text-sm font-semibold">Review History</h3>
106-
{commentsLoading && (
107-
<p className="text-sm text-muted-foreground">Loading...</p>
108-
)}
109-
{commentsError && <ErrorBanner message={commentsError} />}
110-
{showEmptyComments && (
111-
<p className="text-sm text-muted-foreground">
112-
No review comments
113-
</p>
114-
)}
115-
{comments.length > 0 && (
116-
<div className="space-y-3">
117-
{comments.map((c) => (
118-
<div
119-
key={c.id}
120-
className="rounded border p-3 text-sm"
121-
>
122-
<div className="mb-1 flex items-center gap-2">
123-
<span className="font-medium">{c.reviewer}</span>
124-
<StatusBadge status={c.status_after} />
125-
<span className="text-muted-foreground">
126-
{formatDateTime(c.created_at)}
127-
</span>
128-
</div>
129-
<p>{c.comment}</p>
130-
</div>
131-
))}
132-
</div>
133-
)}
84+
<ReviewHistoryPanel designId={design.id} />
13485
</CardContent>
13586
)}
13687
</Card>

frontend/src/pages/design-detail/KnowledgePanel.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ export function KnowledgePanel({ designId }: { designId: string }) {
5757
{saved ? "Saved" : saving ? "Saving..." : "Save Knowledge"}
5858
</Button>
5959
{saved && (
60-
<p className="text-sm text-green-600">Knowledge saved successfully.</p>
60+
<p className="text-sm text-green-600">
61+
Knowledge saved to project rules. View all entries in the Rules tab → Domain Knowledge.
62+
</p>
6163
)}
6264
</>
6365
)}

frontend/src/pages/design-detail/OverviewPanel.tsx

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,43 @@ import { useState } from "react";
22
import { submitReview } from "@/api/client";
33
import { StatusBadge } from "@/components/StatusBadge";
44
import { ErrorBanner } from "@/components/ErrorBanner";
5+
import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert";
56
import { formatDateTime } from "@/lib/utils";
67
import { Button } from "@/components/ui/button";
7-
import type { Design } from "@/types/api";
8+
import type { Design, DesignStatus } from "@/types/api";
89
import { COMMENTABLE_SECTIONS } from "./components/sections";
910
import { SectionRenderer } from "./components/SectionRenderer";
1011
import { ReviewBatchComposer } from "./components/ReviewBatchComposer";
1112
import { useReviewDrafts } from "./components/useReviewDrafts";
1213

14+
const STATUS_GUIDE: Partial<Record<DesignStatus, { title: string; description: string }>> = {
15+
active: {
16+
title: "Ready for Review",
17+
description:
18+
"Submit for review to enable inline commenting on each section.",
19+
},
20+
pending_review: {
21+
title: "In Review",
22+
description:
23+
'Add comments to sections below, then submit with a verdict. Choose "Active" to request revisions, or a final verdict when the design is solid.',
24+
},
25+
supported: {
26+
title: "Approved",
27+
description:
28+
"Design is finalized. Proceed with your analysis, then return to the Knowledge tab to extract and save domain insights.",
29+
},
30+
rejected: {
31+
title: "Rejected",
32+
description:
33+
"Hypothesis was rejected. Go to the Knowledge tab to capture lessons learned.",
34+
},
35+
inconclusive: {
36+
title: "Inconclusive",
37+
description:
38+
"Results are inconclusive. Go to the Knowledge tab to capture observations, then consider refining the hypothesis.",
39+
},
40+
};
41+
1342
interface OverviewPanelProps {
1443
design: Design;
1544
designId: string;
@@ -53,16 +82,27 @@ export function OverviewPanel({
5382
<Field label="Created">{formatDateTime(design.created_at)}</Field>
5483
<Field label="Updated">{formatDateTime(design.updated_at)}</Field>
5584

56-
{design.status === "active" && (
57-
<div className="border-t pt-3">
58-
<Button
59-
onClick={handleSubmitReview}
60-
disabled={submittingReview}
61-
>
62-
{submittingReview ? "Submitting..." : "Submit for Review"}
63-
</Button>
64-
</div>
65-
)}
85+
{(() => {
86+
const guide = STATUS_GUIDE[design.status];
87+
if (!guide) return null;
88+
return (
89+
<Alert data-testid="workflow-guide">
90+
<AlertTitle>{guide.title}</AlertTitle>
91+
<AlertDescription>
92+
<p>{guide.description}</p>
93+
{design.status === "active" && (
94+
<Button
95+
className="mt-2"
96+
onClick={handleSubmitReview}
97+
disabled={submittingReview}
98+
>
99+
{submittingReview ? "Submitting..." : "Submit for Review"}
100+
</Button>
101+
)}
102+
</AlertDescription>
103+
</Alert>
104+
);
105+
})()}
66106

67107
{error && <ErrorBanner message={error} />}
68108

0 commit comments

Comments
 (0)