Add spec, plan, and tasks for 030-bulk-import-sources feature
Defines the "Bulk Import All Sources" feature for the on-demand bestiary system: one-click loading of all ~104 bestiary sources with concurrent fetching, progress feedback, toast notifications, and completion reporting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
171
specs/030-bulk-import-sources/tasks.md
Normal file
171
specs/030-bulk-import-sources/tasks.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# Tasks: Bulk Import All Sources
|
||||
|
||||
**Input**: Design documents from `/specs/030-bulk-import-sources/`
|
||||
**Prerequisites**: plan.md (required), spec.md (required), research.md, data-model.md
|
||||
|
||||
**Organization**: Tasks are grouped by user story to enable independent implementation and testing of each story.
|
||||
|
||||
## Format: `[ID] [P?] [Story] Description`
|
||||
|
||||
- **[P]**: Can run in parallel (different files, no dependencies)
|
||||
- **[Story]**: Which user story this task belongs to (e.g., US1, US2, US3)
|
||||
- Include exact file paths in descriptions
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Foundational (Blocking Prerequisites)
|
||||
|
||||
**Purpose**: Adapter helpers and core hook that all user stories depend on
|
||||
|
||||
- [ ] T001 Add `getAllSourceCodes()` helper to `apps/web/src/adapters/bestiary-index-adapter.ts` that returns all source codes from the bestiary index's `sources` object as a `string[]`
|
||||
- [ ] T002 Add `getBulkFetchUrl(baseUrl: string, sourceCode: string)` helper to `apps/web/src/adapters/bestiary-index-adapter.ts` that constructs `{baseUrl}bestiary-{sourceCode.toLowerCase()}.json` (ensure trailing slash normalization on baseUrl)
|
||||
- [ ] T003 Create `apps/web/src/hooks/use-bulk-import.ts` — the `useBulkImport` hook managing `BulkImportState` (status, total, completed, failed) with a `startImport(baseUrl: string, fetchAndCacheSource, isSourceCached, refreshCache)` method that: filters out already-cached sources via `isSourceCached`, fires all remaining fetches concurrently via `Promise.allSettled()`, increments completed/failed counters as each settles, calls `refreshCache()` once when all settle, and transitions status to `complete` or `partial-failure`
|
||||
|
||||
**Checkpoint**: Foundation ready — user story implementation can begin
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: User Story 1 — Bulk Load All Sources (Priority: P1)
|
||||
|
||||
**Goal**: User clicks an Import button in the top bar, sees a bulk import prompt in the side panel with editable base URL, and can load all sources with one click.
|
||||
|
||||
**Independent Test**: Click Import button → side panel opens with prompt → click "Load All" → all uncached sources are fetched, normalized, and cached in IndexedDB.
|
||||
|
||||
### Implementation for User Story 1
|
||||
|
||||
- [ ] T004 [US1] Create `apps/web/src/components/bulk-import-prompt.tsx` — component showing descriptive text ("Load stat block data for all 102 sources at once. This will download approximately 12.5 MB..."), an editable Input pre-filled with `https://raw.githubusercontent.com/5etools-mirror-3/5etools-src/main/data/bestiary/`, and a "Load All" Button (disabled when URL is empty or import is active). On click, calls a provided `onStartImport(baseUrl)` callback.
|
||||
- [ ] T005 [US1] Add Import button (Lucide `Import` icon) to `apps/web/src/components/action-bar.tsx` in the top bar area. Clicking it calls a new `onBulkImport` callback prop.
|
||||
- [ ] T006 [US1] Add bulk import mode to `apps/web/src/components/stat-block-panel.tsx` — when a new `bulkImportMode` prop is true, render `BulkImportPrompt` instead of the normal stat block or source fetch prompt content. Pass through `onStartImport` and bulk import state props.
|
||||
- [ ] T007 [US1] Wire bulk import in `apps/web/src/App.tsx` — add `bulkImportMode` state, pass `onBulkImport` to ActionBar (sets mode + opens panel), pass `bulkImportMode` and `useBulkImport` state to StatBlockPanel, call `useBulkImport.startImport()` with `fetchAndCacheSource`, `isSourceCached`, and `refreshCache` from `useBestiary`.
|
||||
|
||||
**Checkpoint**: User Story 1 fully functional — Import button opens panel, "Load All" fetches all sources, cached sources skipped
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 2 — Progress Feedback in Side Panel (Priority: P1)
|
||||
|
||||
**Goal**: During bulk import, the side panel shows a text counter ("Loading sources... 34/102") and a progress bar that updates in real time.
|
||||
|
||||
**Independent Test**: Start bulk import → observe counter and progress bar updating as each source completes.
|
||||
|
||||
### Implementation for User Story 2
|
||||
|
||||
- [ ] T008 [US2] Add progress display to `apps/web/src/components/bulk-import-prompt.tsx` — when import status is `loading`, replace the "Load All" button area with a text counter ("Loading sources... {completed}/{total}") and a Tailwind-styled progress bar (`<div>` with percentage width based on `(completed + failed) / total`). The component receives `BulkImportState` as a prop.
|
||||
|
||||
**Checkpoint**: User Stories 1 AND 2 work together — full import flow with live progress
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: User Story 3 — Toast Notification on Panel Close (Priority: P2)
|
||||
|
||||
**Goal**: If the user closes the side panel during an active import, a toast notification appears at bottom-center showing progress counter and bar.
|
||||
|
||||
**Independent Test**: Start bulk import → close side panel → toast appears with progress → toast updates as sources complete.
|
||||
|
||||
### Implementation for User Story 3
|
||||
|
||||
- [ ] T009 [P] [US3] Create `apps/web/src/components/toast.tsx` — lightweight toast component using `ReactDOM.createPortal` to `document.body`. Renders at bottom-center with fixed positioning. Shows: message text, optional progress bar, optional dismiss button (X). Accepts `onDismiss` callback. Styled with Tailwind (dark background, rounded, shadow, z-50).
|
||||
- [ ] T010 [US3] Wire toast visibility in `apps/web/src/App.tsx` — show the toast when bulk import status is `loading` AND the stat block panel is closed (or `bulkImportMode` is false). Derive toast message from `BulkImportState`: "Loading sources... {completed}/{total}" with progress value `(completed + failed) / total`. Hide toast when panel reopens in bulk import mode.
|
||||
|
||||
**Checkpoint**: User Story 3 functional — closing panel during import shows toast with progress
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: User Story 4 — Completion and Failure Reporting (Priority: P2)
|
||||
|
||||
**Goal**: On completion, show "All sources loaded" (auto-dismiss) or "Loaded X/Y sources (Z failed)" (persistent until dismissed).
|
||||
|
||||
**Independent Test**: Complete a successful import → see success message auto-dismiss. Simulate failures → see partial-failure message persist until dismissed.
|
||||
|
||||
### Implementation for User Story 4
|
||||
|
||||
- [ ] T011 [US4] Add completion states to `apps/web/src/components/bulk-import-prompt.tsx` — when status is `complete`, show "All sources loaded" success message. When status is `partial-failure`, show "Loaded {completed}/{total + completed} sources ({failed} failed)" message. Include a "Done" button to reset bulk import mode.
|
||||
- [ ] T012 [US4] Add completion behavior to toast in `apps/web/src/App.tsx` — when status is `complete`, show "All sources loaded" toast with `autoDismissMs` (e.g., 3000ms) and auto-hide via `setTimeout`. When status is `partial-failure`, show count message with dismiss button, no auto-dismiss. On dismiss, reset bulk import state to `idle`.
|
||||
- [ ] T013 [US4] Add auto-dismiss support to `apps/web/src/components/toast.tsx` — accept optional `autoDismissMs` prop. When set, start a `setTimeout` on mount that calls `onDismiss` after the delay. Clear timeout on unmount.
|
||||
|
||||
**Checkpoint**: All user stories complete — full flow with progress, toast, and completion reporting
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Polish & Cross-Cutting Concerns
|
||||
|
||||
**Purpose**: Edge cases and cleanup
|
||||
|
||||
- [ ] T014 Disable Import button in `apps/web/src/components/action-bar.tsx` while bulk import status is `loading` to prevent double-trigger
|
||||
- [ ] T015 Handle all-cached edge case in `apps/web/src/hooks/use-bulk-import.ts` — if all sources are already cached (0 to fetch), immediately transition to `complete` status without firing any fetches
|
||||
- [ ] T016 Run `pnpm check` (knip + format + lint + typecheck + test) and fix any issues
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & Execution Order
|
||||
|
||||
### Phase Dependencies
|
||||
|
||||
- **Foundational (Phase 1)**: No dependencies — can start immediately
|
||||
- **US1 (Phase 2)**: Depends on Phase 1 completion
|
||||
- **US2 (Phase 3)**: Depends on Phase 2 (extends `bulk-import-prompt.tsx` from US1)
|
||||
- **US3 (Phase 4)**: Depends on Phase 1 (uses `BulkImportState`); toast component (T009) can be built in parallel with US1/US2
|
||||
- **US4 (Phase 5)**: Depends on Phase 2 and Phase 4 (extends both panel and toast)
|
||||
- **Polish (Phase 6)**: Depends on all story phases complete
|
||||
|
||||
### User Story Dependencies
|
||||
|
||||
- **US1 (P1)**: Depends only on Foundational
|
||||
- **US2 (P1)**: Depends on US1 (extends same component)
|
||||
- **US3 (P2)**: Toast component (T009) is independent; wiring (T010) depends on US1
|
||||
- **US4 (P2)**: Depends on US1 (panel completion) and US3 (toast completion behavior)
|
||||
|
||||
### Parallel Opportunities
|
||||
|
||||
- T001 and T002 can run in parallel (different functions, same file)
|
||||
- T009 (toast component) can run in parallel with T004–T008 (different files)
|
||||
- T011 and T013 can run in parallel (different files)
|
||||
|
||||
---
|
||||
|
||||
## Parallel Example: Phase 1
|
||||
|
||||
```bash
|
||||
# These foundational tasks modify the same file, so run T001+T002 together then T003:
|
||||
Task: T001 + T002 "Add getAllSourceCodes and getBulkFetchUrl helpers"
|
||||
Task: T003 "Create useBulkImport hook" (independent file)
|
||||
```
|
||||
|
||||
## Parallel Example: US1 + Toast
|
||||
|
||||
```bash
|
||||
# Toast component can be built while US1 is in progress:
|
||||
Task: T004 "Create bulk-import-prompt.tsx" (US1)
|
||||
Task: T009 "Create toast.tsx" (US3) — runs in parallel, different file
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Strategy
|
||||
|
||||
### MVP First (User Stories 1 + 2)
|
||||
|
||||
1. Complete Phase 1: Foundational helpers + hook
|
||||
2. Complete Phase 2: US1 — Import button, prompt, fetch logic
|
||||
3. Complete Phase 3: US2 — Progress counter + bar in panel
|
||||
4. **STOP and VALIDATE**: Full import flow works with progress feedback
|
||||
5. This delivers the core user value with 10 tasks (T001–T008)
|
||||
|
||||
### Incremental Delivery
|
||||
|
||||
1. Foundational → helpers and hook ready
|
||||
2. US1 + US2 → Full import with progress (MVP!)
|
||||
3. US3 → Toast notification on panel close
|
||||
4. US4 → Completion/failure reporting
|
||||
5. Polish → Edge cases and merge gate
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- [P] tasks = different files, no dependencies
|
||||
- [Story] label maps task to specific user story
|
||||
- US1 and US2 share `bulk-import-prompt.tsx` — US2 extends the component from US1
|
||||
- US3's toast component (T009) is fully independent and can be built any time
|
||||
- US4 adds completion behavior to both panel (from US1) and toast (from US3)
|
||||
- No test tasks included — not explicitly requested in spec
|
||||
Reference in New Issue
Block a user