- 19 Jul, 2026 2 commits
-
-
Administrator authored
Extract src/composables/useCrudList.js: owns list state + dialog state machine + applySaved/applyDeleted + 5 toggle methods. Config: { list, create, update, deleteFn, idKey, mapRow?, loadErrorKey? }. Decompose 4 master-data views into table + edit-dialog + delete-dialog SFCs (11 new files), mirroring the StoryView pattern proven in Candidate B. View shrinks to pure wiring of useCrudList + useFlashMessage + child SFCs. Structural fixes (now composition-enforced, not patch-enforced): - D5: applySaved merges backend response into local list, so audit fields (created_by, creation_date, last_updated_by, last_update_date) are visible immediately after save, no refresh required. - D7: useCrudList owns deleteError; only *DeleteDialog.vue reads it. Cross-dialog delete-error bug fixed by composition. Net change: SubjectView 763 → 85 TermView 619 → 95 StudentView 1312 → 115 StoryView 173 → 85 Total 2867 → 380 (-2487 lines) Tests: useCrudList.spec.js covers fetch (success/error/auto-clear/ mapRow/non-array fallback), applySaved (create/update/audit-preservation/ no-id/null-undefined), applyDeleted, 5 toggle methods, clearDeleteError, idKey+mapRow together. 23 new tests, 214 total passing. Other quality wins: ~30 console.log calls in the 3 large views removed; view-owned reference data (R2) keeps useCrudList agnostic of students/ subjects/terms. Co-Authored-By:Claude <noreply@anthropic.com>
-
Administrator authored
StoryView was the last "master-data" god-view whose script section still owned state, fetch, CRUD methods, table config, and three dialog templates in one file. Mirrors the Candidate 2 TaskView decomposition pattern. - src/components/story/StoryTable.vue — v-data-table + three Map<id, name> computeds for O(1) display lookup (replaces O(n^2) find); emits @edit / @delete - src/components/story/StoryEditDialog.vue — create+edit merged via nullable `story` prop; owns save flow + inline error alert - src/components/story/StoryDeleteDialog.vue — owns deleteStory call, isDeleting, inline error; preserves the existing 48-line UX - src/composables/useFlashMessage.js — { message, show, clear } with built-in 3s auto-dismiss - src/__tests__/composables/useFlashMessage.spec.js — 13 tests (fake timers) Key design choices (settled via /grilling before implementation): 1. Dialog seam shape is explicit props+events — :open, :story, @close, @saved, @deleted. Not v-model. Keeps multiple emits semantically distinct. 2. Lookup data (students/subjects/terms) lives in StoryView as raw refs and is passed down to children. No useMasterData composable — that scope belongs to Candidate A (useCrudList). 3. StoryEditDialog handles create+edit (TaskEditDialog pattern), with @saved carrying the backend response so the parent can do { ...existing, ...updated } merge and pick up audit fields (created_at, last_updated_at, etc.). 4. StoryDeleteDialog owns the delete flow internally — parent just wires @deleted(id) to a list splice. The existing "close-edit-after-delete" contract is preserved via the parent's onStoryDeleted handler. 5. Row-level delete is now in StoryTable (mdi-delete icon next to edit). Replaces the awkward "open edit -> click delete inside edit dialog -> confirm" three-click path with a two-click one. 6. console.log calls in StoryView removed (match TaskEditDialog's console.error-only pattern; CLAUDE.md says services log heavily, not components). StoryView is now a thin orchestrator: refs, two fetch functions, open/close handlers, two merge handlers. fetchStories still depends on fetchRelatedData completing first (sequential await, NOT Promise.all — see comment in onMounted). Tests 178 -> 191. Co-Authored-By:Claude <noreply@anthropic.com>
-