feat(web-ui): PRD version history panel with diff and restore#525
Conversation
Add version history modal to PRD page so users can view, compare, and restore previous PRD versions. All versions are already saved by the backend; this surfaces them in the UI. - PRDVersionHistoryModal: Dialog showing all saved versions (newest first) with timestamp and change_summary. Clicking "View" opens a read-only content preview. "Compare with current" calls prdApi.diff() and displays the unified diff. "Restore this version" creates a new version non-destructively via prdApi.createVersion(). Current version highlighted with a "Current" badge. - PRDHeader: new "History" button (Time01Icon) visible when a PRD exists - PRDView / page.tsx: versionHistoryOpen state + onViewHistory callback Closes #482
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
WalkthroughAdded a client-side PRD version history modal and integrated it into the PRD page. The modal lists versions (timestamp + summary), supports previewing a version, comparing it with the current version via diff, and restoring a version through a confirmation flow; includes tests covering loading, error, and interactive flows. Changes
Sequence DiagramsequenceDiagram
actor User
participant Header as PRDHeader
participant Modal as PRDVersionHistoryModal
participant API as prdApi
participant SWR as SWR Cache
User->>Header: Click "History"
Header->>Modal: onViewHistory -> open modal
Modal->>SWR: request versions (uses prdApi.getVersions)
SWR-->>Modal: return version list
Modal->>User: render version list
User->>Modal: Click "View" on version
Modal->>User: show preview of selected version
alt Compare Flow
User->>Modal: Click "Compare with current"
Modal->>API: prdApi.diff(currentId, workspacePath, vA, vB)
API-->>Modal: return diff text
Modal->>User: display diff
end
alt Restore Flow
User->>Modal: Click "Restore this version"
Modal->>User: show confirm prompt
User->>Modal: Confirm
Modal->>API: prdApi.createVersion(restoredContent, summary)
API-->>Modal: return new PrdResponse
Modal->>SWR: mutate PRD cache with new PrdResponse
Modal->>User: close modal, reset view
end
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Review: PRDVersionHistoryModalOverall this is clean, well-structured work. The component decomposition, non-destructive restore pattern, and test coverage are all solid. A few issues worth addressing: Bug: unused prop in
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
web-ui/src/components/prd/PRDVersionHistoryModal.tsx (1)
228-235: UnusedcurrentVersionprop inVersionPreview.The
currentVersionprop is declared inVersionPreviewProps(line 220) and accepted in the component signature but is never used in the function body. This is likely dead code that can be removed.♻️ Proposed fix
interface VersionPreviewProps { preview: PreviewState; - currentVersion: number; onBack: () => void; onCompare: () => void; onStartRestore: () => void; onCancelRestore: () => void; onConfirmRestore: () => void; } function VersionPreview({ preview, - currentVersion, onBack, onCompare, onStartRestore, onCancelRestore, onConfirmRestore, }: VersionPreviewProps) {Also update the call site at line 131:
<VersionPreview preview={preview} - currentVersion={prd.version} onBack={handleBackToList}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web-ui/src/components/prd/PRDVersionHistoryModal.tsx` around lines 228 - 235, Remove the unused currentVersion prop: update the VersionPreviewProps type to drop currentVersion, remove currentVersion from the VersionPreview component parameter list (the VersionPreview function), and update any call sites that pass currentVersion (e.g., where VersionPreview is instantiated) to stop passing that prop; ensure PropTypes/TS types and any destructuring in VersionPreview no longer reference currentVersion so there are no leftover unused variables.web-ui/src/__tests__/components/prd/PRDVersionHistoryModal.test.tsx (1)
163-194: Consider adding a test for diff fetch failure.The "Compare with current" tests verify the happy path, but there's no test for when
prdApi.diffrejects. Given that the component silently swallows this error (as noted in the component review), adding a test would document the current behavior and serve as a reminder to improve error handling.📝 Example test for diff error
it('handles diff fetch failure gracefully', async () => { mockDiff.mockRejectedValueOnce(new Error('Network error')); const user = userEvent.setup(); render(<PRDVersionHistoryModal {...defaultProps} />); const viewButtons = screen.getAllByRole('button', { name: /^view$/i }); await user.click(viewButtons[0]); // version 2 const compareBtn = screen.getByRole('button', { name: /compare with current/i }); await user.click(compareBtn); await waitFor(() => { expect(mockDiff).toHaveBeenCalled(); }); // Button should be re-enabled after failure (or show error state) // Update assertion based on desired behavior });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web-ui/src/__tests__/components/prd/PRDVersionHistoryModal.test.tsx` around lines 163 - 194, Add a new test in PRDVersionHistoryModal.test.tsx that simulates prdApi.diff failing by using mockDiff.mockRejectedValueOnce(new Error('Network error')), then render <PRDVersionHistoryModal {...defaultProps} />, open a version via the same view button flow (using userEvent and viewButtons[0]) and click the "Compare with current" button; assert that mockDiff was called with currentPrd.id, WORKSPACE and the two versions and then assert the component recovers from the failure (e.g., the compare button is re-enabled or an error message is shown) to document current behavior and prevent regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web-ui/src/components/prd/PRDVersionHistoryModal.tsx`:
- Around line 93-108: In handleConfirmRestore, the catch block for
prdApi.createVersion currently swallows errors and only resets state; import
toast from "sonner" and in the catch block call toast.error with a clear message
and the caught error (e.g., toast.error(`Failed to restore version:
${error?.message || error}`)) while still resetting preview (setPreview(...)).
Update the catch clause to capture the error parameter (catch (error) { ... })
and include the toast.error call so the user sees feedback when restore fails.
- Around line 77-91: The diff fetch currently swallows errors; add a diffError
field to PreviewState and track it via setPreview in handleCompare and
handleViewVersion: ensure handleViewVersion initializes preview.diffError =
false, set diffError = false before starting the API call, on success set
diffError = false (and set diff/result), and in the catch set diffError = true
and diffLoading = false (optionally store an error message). Update
VersionPreview to render an error message when preview?.diffError is true so
users see the failure. Use the existing functions/variables: PreviewState,
handleViewVersion, handleCompare, setPreview, VersionPreview, and prdApi.diff.
---
Nitpick comments:
In `@web-ui/src/__tests__/components/prd/PRDVersionHistoryModal.test.tsx`:
- Around line 163-194: Add a new test in PRDVersionHistoryModal.test.tsx that
simulates prdApi.diff failing by using mockDiff.mockRejectedValueOnce(new
Error('Network error')), then render <PRDVersionHistoryModal {...defaultProps}
/>, open a version via the same view button flow (using userEvent and
viewButtons[0]) and click the "Compare with current" button; assert that
mockDiff was called with currentPrd.id, WORKSPACE and the two versions and then
assert the component recovers from the failure (e.g., the compare button is
re-enabled or an error message is shown) to document current behavior and
prevent regressions.
In `@web-ui/src/components/prd/PRDVersionHistoryModal.tsx`:
- Around line 228-235: Remove the unused currentVersion prop: update the
VersionPreviewProps type to drop currentVersion, remove currentVersion from the
VersionPreview component parameter list (the VersionPreview function), and
update any call sites that pass currentVersion (e.g., where VersionPreview is
instantiated) to stop passing that prop; ensure PropTypes/TS types and any
destructuring in VersionPreview no longer reference currentVersion so there are
no leftover unused variables.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f13fbdc2-9989-4278-9061-8fa26a27c0dc
📒 Files selected for processing (6)
web-ui/src/__tests__/components/prd/PRDVersionHistoryModal.test.tsxweb-ui/src/app/prd/page.tsxweb-ui/src/components/prd/PRDHeader.tsxweb-ui/src/components/prd/PRDVersionHistoryModal.tsxweb-ui/src/components/prd/PRDView.tsxweb-ui/src/components/prd/index.ts
- Remove unused currentVersion prop from VersionPreviewProps interface and call site - Add diffError state to PreviewState; show inline error message when diff fetch fails so users can see the failure and retry - Allow Compare button to be re-clicked after a failed or successful diff (remove || !!diff from disabled condition) - Add toast.error in handleConfirmRestore so users get feedback when restore fails - Add test: shows error message and re-enables Compare button on diff failure - Clarify test comment for viewButtons index
Follow-up ReviewAll issues from my initial review have been addressed:
The component is clean and the fixes are well-integrated. Ready to merge. |
Summary
Closes #482
PRDVersionHistoryModal— Dialog listing all saved versions with timestamps and change summaries. Supports read-only preview, unified diff against current, and non-destructive restore.PRDHeader(only shown when a PRD exists)PRDView→page.tsx(versionHistoryOpen+onViewHistorycallback)How it works
The backend already stores every save as a new version via
prdApi.createVersion(). This PR surfaces that data:GET /api/v2/prd/{id}/diffand shows unified diffAcceptance criteria
Test plan
PRDVersionHistoryModal.test.tsxSummary by CodeRabbit
New Features
Tests