refactor: decompose ReviewView into focused components (FE-16)#923
refactor: decompose ReviewView into focused components (FE-16)#923Chris0Jeky merged 2 commits intomainfrom
Conversation
Extract the monolithic ReviewView.vue (1,659 lines) into focused components and composables, each under 400 lines: Components (in frontend/taskdeck-web/src/components/review/): - ReviewHeader.vue (199 lines) - hero, board selector, action buttons - ReviewSummaryCards.vue (74 lines) - statistics cards grid - ReviewEmptyState.vue (47 lines) - empty state with guided actions - ReviewProposalCard.vue (342 lines) - individual proposal card - ReviewProposalActions.vue (229 lines) - approve/reject/apply/dismiss - ReviewProposalDetails.vue (346 lines) - collapsible details sections Composables (in frontend/taskdeck-web/src/composables/): - useReviewProposals.ts (346 lines) - state, filtering, data loading - useReviewActions.ts (159 lines) - proposal actions and diff toggle ReviewView.vue is now 148 lines (orchestration + layout only). All 45 existing ReviewView tests pass without modification. Typecheck, build, and lint all pass with no new warnings. Closes #856
Adversarial self-reviewVerified correct
Behavior change (acceptable)
No issues found
|
There was a problem hiding this comment.
Code Review
This pull request refactors the ReviewView by decomposing its logic into modular components and composables, such as ReviewHeader, ReviewProposalCard, and useReviewProposals. This restructuring enhances maintainability and separates concerns. The review feedback identifies several issues related to reactivity and null safety in the newly created components, specifically recommending the use of computed properties for prop-derived data and defensive checks for potentially null identifiers to prevent runtime errors.
| @@ -0,0 +1,346 @@ | |||
| <script setup lang="ts"> | |||
| import { ref } from 'vue' | |||
| linkDropdownOpen.value = false | ||
| } | ||
|
|
||
| const fullCorrelationId = props.proposal.correlationId.trim() |
There was a problem hiding this comment.
This assignment is not reactive. Since it is a const initialized during the component's setup phase, it will not update if the proposal prop changes (which can happen if the component instance is reused in a keyed v-for list). Additionally, calling .trim() directly on correlationId lacks null safety. Using a computed property with optional chaining resolves both issues.
const fullCorrelationId = computed(() => props.proposal.correlationId?.trim() ?? '')
| function shortCorrelationId(correlationId: string): string { | ||
| const trimmed = correlationId.trim() | ||
| return trimmed.length > 8 ? trimmed.slice(0, 8) + '...' : trimmed | ||
| } |
There was a problem hiding this comment.
This function will throw a runtime error if correlationId is null or undefined. It is safer to handle potential missing values defensively.
function shortCorrelationId(correlationId: string | null | undefined): string {
const trimmed = (correlationId || '').trim()
return trimmed.length > 8 ? trimmed.slice(0, 8) + '...' : trimmed
}
- Make fullCorrelationId a computed() in ReviewProposalDetails for reactivity when the proposal prop changes (gemini-code-assist finding) - Add null safety to shortCorrelationId in ReviewProposalCard for defensive API data handling (gemini-code-assist finding) - Narrow onBoardFilterInput type in ReviewHeader from string|number|boolean to string to match InputAssistField emit type
Adversarial Review: PR #923 -- Decompose ReviewViewVerification resultsAll frontend checks pass after the fixes below:
Issues found and fixed (pushed to branch)1. Reactivity bug in
2. Null safety in Although 3. Imprecise type in The handler accepted Analysis: No issues foundCollapsible section state isolation -- The old code stored Link dropdown state isolation -- Same pattern: old single Event wiring correctness -- All events are properly forwarded through the component hierarchy: ReviewView -> ReviewProposalCard -> ReviewProposalActions/ReviewProposalDetails. The Composable extraction -- Keyboard shortcuts -- The original Accessibility -- ARIA attributes ( Test coverage -- The existing 210 test files (including the dedicated VerdictThe decomposition is well-structured and the three issues above have been fixed and pushed. The PR is ready for merge. |
Summary
ReviewView.vuefrom 1,659 lines into 9 focused files, each under 400 linessrc/components/review/:ReviewHeader.vue(199 lines) -- hero section, board selector, action buttonsReviewSummaryCards.vue(74 lines) -- statistics cards gridReviewEmptyState.vue(47 lines) -- empty state with guided actionsReviewProposalCard.vue(342 lines) -- individual proposal card renderingReviewProposalActions.vue(229 lines) -- approve/reject/apply/dismiss action footerReviewProposalDetails.vue(346 lines) -- collapsible details (affected cards, planned changes, provenance)src/composables/:useReviewProposals.ts(346 lines) -- state management, filtering, data loading, navigationuseReviewActions.ts(159 lines) -- proposal actions (approve, reject, execute, dismiss, diff toggle)ReviewView.vueis now 148 lines (orchestration + layout only)Verification
Test plan
npm run typecheckpassesnpm run buildpassesnpm run lintpasses (no new warnings)npx vitest --run-- all 2,552 tests passCloses #856