8.8 KiB
Frontend FSD — Portfolio Pages Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Migrate portfolio UI components from flat components/portfolios/ to widgets/ per FSD, following the broker domain pattern.
Architecture: 6 widgets created under widgets/, each with index.ts barrel + ui/Component.tsx. Pages in pages/portfolios/ switch imports to widgets. components/portfolios/ deleted. entities/portfolio/ unchanged.
Tech Stack: React 18, TypeScript, Vite, Vitest
Task 1: Create widget directory structure and barrel files
Files:
-
Create:
apps/frontend/src/widgets/portfolio-card/index.ts -
Create:
apps/frontend/src/widgets/portfolio-form/index.ts -
Create:
apps/frontend/src/widgets/portfolio-summary/index.ts -
Create:
apps/frontend/src/widgets/portfolio-analytics/index.ts -
Create:
apps/frontend/src/widgets/share-positions-table/index.ts -
Create:
apps/frontend/src/widgets/bond-positions-table/index.ts -
Create:
apps/frontend/src/widgets/portfolio-card/ui/PortfolioCard.tsx -
Create:
apps/frontend/src/widgets/portfolio-form/ui/PortfolioForm.tsx -
Create:
apps/frontend/src/widgets/portfolio-summary/ui/PortfolioSummary.tsx -
Create:
apps/frontend/src/widgets/portfolio-summary/ui/AllocationChart.tsx -
Create:
apps/frontend/src/widgets/portfolio-analytics/ui/AnalyticsSummary.tsx -
Create:
apps/frontend/src/widgets/share-positions-table/ui/SharePositionTable.tsx -
Create:
apps/frontend/src/widgets/share-positions-table/ui/SharePositionRow.tsx -
Create:
apps/frontend/src/widgets/bond-positions-table/ui/BondPositionTable.tsx -
Create:
apps/frontend/src/widgets/bond-positions-table/ui/BondPositionRow.tsx -
Step 1: Create all widget barrel files
// widgets/portfolio-card/index.ts
export { PortfolioCard } from './ui/PortfolioCard';
// widgets/portfolio-form/index.ts
export { PortfolioForm } from './ui/PortfolioForm';
// widgets/portfolio-summary/index.ts
export { PortfolioSummary } from './ui/PortfolioSummary';
// widgets/portfolio-analytics/index.ts
export { AnalyticsSummary } from './ui/AnalyticsSummary';
// widgets/share-positions-table/index.ts
export { SharePositionTable } from './ui/SharePositionTable';
// widgets/bond-positions-table/index.ts
export { BondPositionTable } from './ui/BondPositionTable';
- Step 2: Copy and update imports for each component
For each component from components/portfolios/, copy to widgets/<name>/ui/<Component>.tsx and fix imports:
PortfolioCard.tsx (copy from components/portfolios/PortfolioCard.tsx, no import changes needed):
// Same content as original — imports @/shared/api/responses which stays valid
PortfolioForm.tsx (copy from components/portfolios/PortfolioForm.tsx, no import changes):
// Same content — imports @/shared/api/responses
PortfolioSummary.tsx (copy from components/portfolios/PortfolioSummary.tsx, update import path):
import { AllocationChart } from './AllocationChart';
import type { PortfolioDetail } from '@/shared/api/responses';
// rest identical to original
AllocationChart.tsx (copy from components/portfolios/AllocationChart.tsx, no import changes):
// Same content — imports @/shared/api/responses
AnalyticsSummary.tsx (copy from components/portfolios/AnalyticsSummary.tsx, no import changes):
// Same content — imports @/shared/api/responses
SharePositionRow.tsx (copy from components/portfolios/SharePositionRow.tsx, no import changes):
// Same content — imports @/shared/api/responses
SharePositionTable.tsx (copy from components/portfolios/SharePositionTable.tsx, update import path):
import { SharePositionRow } from './SharePositionRow';
import type { PositionWithPrice } from '@/shared/api/responses';
// rest identical to original
BondPositionRow.tsx (copy from components/portfolios/BondPositionRow.tsx, no import changes):
// Same content — imports @/shared/api/responses
BondPositionTable.tsx (copy from components/portfolios/BondPositionTable.tsx, update import path):
import { BondPositionRow } from './BondPositionRow';
import type { PositionWithPrice } from '@/shared/api/responses';
// rest identical to original
- Step 3: Verify build
Run: npm run build -w apps/frontend
Expected: PASS
- Step 4: Run tests
Run: npm test -w apps/frontend
Expected: PASS
- Step 5: Commit
git add apps/frontend/src/widgets/portfolio-card/ \
apps/frontend/src/widgets/portfolio-form/ \
apps/frontend/src/widgets/portfolio-summary/ \
apps/frontend/src/widgets/portfolio-analytics/ \
apps/frontend/src/widgets/share-positions-table/ \
apps/frontend/src/widgets/bond-positions-table/
git commit -m "feat(frontend): create portfolio widgets in FSD structure"
Task 2: Update pages to use widgets
Files:
-
Modify:
apps/frontend/src/pages/portfolios/PortfoliosListPage.tsx -
Modify:
apps/frontend/src/pages/portfolios/PortfolioDetailPage.tsx -
Step 1: Update PortfoliosListPage.tsx
import { useState } from 'react';
import { usePortfolios, usePortfolioMutations } from '../../entities/portfolio';
import { PortfolioCard } from '../../widgets/portfolio-card';
import { PortfolioForm } from '../../widgets/portfolio-form';
- Step 2: Update PortfolioDetailPage.tsx
import { useState } from 'react';
import { useParams, Link } from 'react-router-dom';
import {
usePortfolio,
usePortfolioMutations,
usePositionMutations,
} from '../../entities/portfolio';
import { PortfolioForm } from '../../widgets/portfolio-form';
import { PortfolioSummary } from '../../widgets/portfolio-summary';
import { AnalyticsSummary } from '../../widgets/portfolio-analytics';
import { SharePositionTable } from '../../widgets/share-positions-table';
import { BondPositionTable } from '../../widgets/bond-positions-table';
- Step 3: Verify build
Run: npm run build -w apps/frontend
Expected: PASS
- Step 4: Run tests
Run: npm test -w apps/frontend
Expected: PASS
- Step 5: Commit
git add apps/frontend/src/pages/portfolios/
git commit -m "refactor(frontend): switch pages/portfolios to widget imports"
Task 3: Remove flat components/portfolios/
Files:
-
Delete:
apps/frontend/src/components/portfolios/AllocationChart.tsx -
Delete:
apps/frontend/src/components/portfolios/AnalyticsSummary.tsx -
Delete:
apps/frontend/src/components/portfolios/BondPositionRow.tsx -
Delete:
apps/frontend/src/components/portfolios/BondPositionTable.tsx -
Delete:
apps/frontend/src/components/portfolios/PortfolioCard.tsx -
Delete:
apps/frontend/src/components/portfolios/PortfolioForm.tsx -
Delete:
apps/frontend/src/components/portfolios/PortfolioSummary.tsx -
Delete:
apps/frontend/src/components/portfolios/SharePositionRow.tsx -
Delete:
apps/frontend/src/components/portfolios/SharePositionTable.tsx -
Step 1: Delete all files in components/portfolios/
Run: rm apps/frontend/src/components/portfolios/*.tsx
- Step 2: Check for remaining imports from components/portfolios
Run: rg "components/portfolios" apps/frontend/src
Expected: no matches
- Step 3: Verify build
Run: npm run build -w apps/frontend
Expected: PASS
- Step 4: Run tests
Run: npm test -w apps/frontend
Expected: PASS
- Step 5: Lint check
Run: npm run lint -w apps/frontend
Expected: PASS
- Step 6: Commit
git rm apps/frontend/src/components/portfolios/*.tsx
git commit -m "refactor(frontend): remove flat components/portfolios"
Task 4: Update documentation
Files:
-
Modify:
apps/docs/docs/frontend/hooks.md -
Modify:
apps/docs/docs/frontend/routes.md -
Step 1: Update hooks.md
Remove any references to legacy components/portfolios/ paths.
Update import examples to use @/widgets/portfolio-* paths where applicable.
- Step 2: Update routes.md
Ensure portfolio route documentation reflects FSD structure.
- Step 3: Run doc build
Run: npm run build -w apps/docs
Expected: PASS
- Step 4: Commit documentation
git add apps/docs/docs/frontend/
git commit -m "docs: update frontend docs for portfolio FSD structure"
Task 5: Final verification
- Step 1: Test
Run: npm test -w apps/frontend
Expected: PASS
- Step 2: Lint
Run: npm run lint -w apps/frontend
Expected: PASS
- Step 3: Build
Run: npm run build -w apps/frontend
Expected: PASS
- Step 4: Deep import check
Run: rg "components/portfolios" apps/frontend/src
Expected: no matches
- Step 5: Docs build
Run: npm run build -w apps/docs
Expected: PASS