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