Implement the 030-bulk-import-sources feature that adds a one-click bulk import button to load all bestiary sources at once, with real-time progress feedback in the side panel and a toast notification when the panel is closed, plus completion/failure reporting with auto-dismiss on success and persistent display on partial failure, while also hardening the bestiary normalizer to handle variable stat blocks (spell summons with special AC/HP/CR) and skip malformed monster entries gracefully

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lukas
2026-03-10 23:29:34 +01:00
parent c323adc343
commit 94d125d9c4
14 changed files with 850 additions and 106 deletions

View File

@@ -9,7 +9,7 @@
### User Story 1 - Bulk Load All Sources (Priority: P1)
The user wants to pre-load all 102 bestiary sources at once so that every creature's stat block is instantly available without per-source fetch prompts during gameplay. They click an import button (Import icon) in the top bar, which opens the stat block side panel. The panel shows a description of what will happen, a pre-filled base URL they can edit, and a "Load All" confirmation button. On confirmation, the app fetches all source files concurrently, normalizes them, and caches them in IndexedDB. Already-cached sources are skipped.
The user wants to pre-load all bestiary sources at once so that every creature's stat block is instantly available without per-source fetch prompts during gameplay. They click an import button (Import icon) in the top bar, which opens the stat block side panel. The panel shows a description of what will happen (including the dynamic source count from the bestiary index), a pre-filled base URL they can edit, and a "Load All" confirmation button. On confirmation, the app fetches all source files concurrently, normalizes them, and caches them in IndexedDB. Already-cached sources are skipped.
**Why this priority**: This is the core feature — enabling one-click loading of the entire bestiary is the primary user value.
@@ -18,7 +18,7 @@ The user wants to pre-load all 102 bestiary sources at once so that every creatu
**Acceptance Scenarios**:
1. **Given** no sources are cached, **When** the user clicks the import button in the top bar, **Then** the stat block side panel opens showing a descriptive explanation, an editable pre-filled base URL, and a "Load All" button.
2. **Given** the side panel is showing the bulk import prompt, **When** the user clicks "Load All", **Then** the app fires fetch requests for all 102 sources concurrently (appending `bestiary-{sourceCode}.json` to the base URL), normalizes each response, and caches results in IndexedDB.
2. **Given** the side panel is showing the bulk import prompt, **When** the user clicks "Load All", **Then** the app fires fetch requests for all sources concurrently (appending `bestiary-{sourceCode}.json` to the base URL), normalizes each response, and caches results in IndexedDB.
3. **Given** some sources are already cached, **When** the user initiates a bulk import, **Then** already-cached sources are skipped and only uncached sources are fetched.
4. **Given** the user has edited the base URL, **When** they click "Load All", **Then** the app uses their custom base URL for all fetches.
5. **Given** all fetches complete successfully, **When** the operation finishes, **Then** all creature stat blocks are immediately available for lookup without additional fetch prompts.
@@ -52,7 +52,7 @@ If the user closes the side panel while a bulk import is still in progress, a pe
1. **Given** a bulk import is in progress, **When** the user closes the side panel, **Then** a toast notification appears at the bottom-center of the screen showing the progress counter and progress bar.
2. **Given** the toast is visible, **When** all sources finish loading successfully, **Then** the toast shows "All sources loaded" and auto-dismisses after a few seconds.
3. **Given** the toast is visible, **When** some sources fail to load, **Then** the toast shows "Loaded 99/102 sources (3 failed)" (with actual counts) and remains visible until the user dismisses it.
3. **Given** the toast is visible, **When** some sources fail to load, **Then** the toast shows "Loaded N/T sources (F failed)" with actual counts (e.g., "Loaded 99/102 sources (3 failed)") and remains visible until the user dismisses it.
---
@@ -67,7 +67,7 @@ On completion, the user sees a clear success or partial-failure message. Partial
**Acceptance Scenarios**:
1. **Given** all sources load successfully, **When** the operation completes, **Then** the side panel (if open) or toast (if panel closed) shows "All sources loaded".
2. **Given** some sources fail to load, **When** the operation completes, **Then** the message shows "Loaded X/102 sources (Y failed)" with accurate counts.
2. **Given** some sources fail to load, **When** the operation completes, **Then** the message shows "Loaded X/T sources (Y failed)" with accurate counts (where T is the total number of sources in the index).
3. **Given** completion message is in the toast, **When** the result is success, **Then** the toast auto-dismisses after a few seconds.
4. **Given** completion message is in the toast, **When** the result is partial failure, **Then** the toast stays visible until manually dismissed.
@@ -76,8 +76,8 @@ On completion, the user sees a clear success or partial-failure message. Partial
### Edge Cases
- What happens when the user clicks "Load All" while a bulk import is already in progress? The button should be disabled during an active import.
- What happens when all 102 sources are already cached? The operation should complete immediately and report "All sources loaded" (0 fetches needed).
- What happens when the network is completely unavailable? All fetches fail and the result shows "Loaded 0/102 sources (102 failed)".
- What happens when all sources are already cached? The operation should complete immediately and report "All sources loaded" (0 fetches needed).
- What happens when the network is completely unavailable? All fetches fail and the result shows "Loaded 0/T sources (T failed)" where T is the total source count.
- What happens when the user navigates away or refreshes during import? Partially completed caches persist; the user can re-run to pick up remaining sources.
- What happens if the base URL is empty or invalid? The "Load All" button should be disabled when the URL field is empty.
@@ -86,7 +86,7 @@ On completion, the user sees a clear success or partial-failure message. Partial
### Functional Requirements
- **FR-001**: System MUST display an import button (Lucide Import icon) in the top bar that opens the stat block side panel with the bulk import prompt.
- **FR-002**: System MUST show a descriptive text explaining the bulk import operation, including approximate data volume (~12.5 MB) and number of sources (102).
- **FR-002**: System MUST show a descriptive text explaining the bulk import operation, including approximate data volume (~12.5 MB) and the dynamic number of sources (derived from the bestiary index at runtime).
- **FR-003**: System MUST pre-fill a base URL (`https://raw.githubusercontent.com/5etools-mirror-3/5etools-src/main/data/bestiary/`) that the user can edit.
- **FR-004**: System MUST construct individual fetch URLs by appending `bestiary-{sourceCode}.json` to the base URL for each source.
- **FR-005**: System MUST fire all fetch requests concurrently (browser handles connection pooling).
@@ -109,7 +109,7 @@ On completion, the user sees a clear success or partial-failure message. Partial
### Measurable Outcomes
- **SC-001**: Users can load all 102 bestiary sources with a single confirmation action.
- **SC-001**: Users can load all bestiary sources with a single confirmation action.
- **SC-002**: Users see real-time progress during the bulk import (counter updates as each source completes).
- **SC-003**: Users can close the side panel during import without losing progress visibility (toast appears).
- **SC-004**: Already-cached sources are skipped, reducing redundant data transfer on repeat imports.
@@ -118,7 +118,7 @@ On completion, the user sees a clear success or partial-failure message. Partial
## Assumptions
- The existing bestiary index contains all 102 source codes needed to construct fetch URLs.
- The existing bestiary index contains all source codes needed to construct fetch URLs (the count is dynamic, currently ~102104).
- The existing normalization pipeline handles all source file formats without modification.
- The existing per-source fetch-and-cache logic serves as the reference implementation for individual source loading.
- The base URL default matches the pattern already used for single-source fetches.