295 lines
8.8 KiB
Markdown
295 lines
8.8 KiB
Markdown
# 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**
|
|
|
|
```ts
|
|
// widgets/portfolio-card/index.ts
|
|
export { PortfolioCard } from './ui/PortfolioCard';
|
|
```
|
|
|
|
```ts
|
|
// widgets/portfolio-form/index.ts
|
|
export { PortfolioForm } from './ui/PortfolioForm';
|
|
```
|
|
|
|
```ts
|
|
// widgets/portfolio-summary/index.ts
|
|
export { PortfolioSummary } from './ui/PortfolioSummary';
|
|
```
|
|
|
|
```ts
|
|
// widgets/portfolio-analytics/index.ts
|
|
export { AnalyticsSummary } from './ui/AnalyticsSummary';
|
|
```
|
|
|
|
```ts
|
|
// widgets/share-positions-table/index.ts
|
|
export { SharePositionTable } from './ui/SharePositionTable';
|
|
```
|
|
|
|
```ts
|
|
// 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):
|
|
```tsx
|
|
// Same content as original — imports @/shared/api/responses which stays valid
|
|
```
|
|
|
|
**PortfolioForm.tsx** (copy from `components/portfolios/PortfolioForm.tsx`, no import changes):
|
|
```tsx
|
|
// Same content — imports @/shared/api/responses
|
|
```
|
|
|
|
**PortfolioSummary.tsx** (copy from `components/portfolios/PortfolioSummary.tsx`, update import path):
|
|
```tsx
|
|
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):
|
|
```tsx
|
|
// Same content — imports @/shared/api/responses
|
|
```
|
|
|
|
**AnalyticsSummary.tsx** (copy from `components/portfolios/AnalyticsSummary.tsx`, no import changes):
|
|
```tsx
|
|
// Same content — imports @/shared/api/responses
|
|
```
|
|
|
|
**SharePositionRow.tsx** (copy from `components/portfolios/SharePositionRow.tsx`, no import changes):
|
|
```tsx
|
|
// Same content — imports @/shared/api/responses
|
|
```
|
|
|
|
**SharePositionTable.tsx** (copy from `components/portfolios/SharePositionTable.tsx`, update import path):
|
|
```tsx
|
|
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):
|
|
```tsx
|
|
// Same content — imports @/shared/api/responses
|
|
```
|
|
|
|
**BondPositionTable.tsx** (copy from `components/portfolios/BondPositionTable.tsx`, update import path):
|
|
```tsx
|
|
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**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```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**
|
|
|
|
```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**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
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
|