refactor(transcription): extract block CRUD into createTranscriptionBlocks hook
Pulls the transcription-block state (load, save, delete, reviewToggle,
markAllReviewed, createFromDraw, toggleTrainingLabel, deleteAnnotation
+ derived blockNumbers / hasBlocks / lastEditedAt / annotationReloadKey)
out of documents/[id]/+page.svelte into a reusable factory in
lib/document/transcription/useTranscriptionBlocks.svelte.ts.
The page now reads transcription.blocks / .blockNumbers / .hasBlocks /
.lastEditedAt / .annotationReloadKey reactively and delegates writes
to transcription.{load, save, delete, reviewToggle, markAllReviewed,
createFromDraw, toggleTrainingLabel, deleteAnnotation,
findByAnnotationId, bumpAnnotationReloadKey}. The confirm-then-delete
dialog stays in the page; the hook only handles the data ops.
24 unit tests cover initial state, load (success / non-OK / network /
empty-id), derived state (blockNumbers in sortOrder, lastEditedAt
recent-pick, lastEditedAt-null fallback), delete (success bumps key /
non-OK throws), reviewToggle (success updates / non-OK no-op), markAll
(success / non-OK), createFromDraw (success / non-OK / network all
return correct shape), toggleTrainingLabel (200 / 500), deleteAnnotation
(linked-block path / orphan-annotation path / orphan-fail throw),
findByAnnotationId match + miss, bumpAnnotationReloadKey.
Also bumps the polling-loop test waits in useOcrJob.svelte.test.ts to
150-200ms (from 60-80ms) so the suite is reliable when run in parallel.
Refs #496.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -261,7 +261,7 @@ describe('createOcrJob — polling loop (short interval, real timers)', () => {
|
||||
pollIntervalMs: 20
|
||||
});
|
||||
await job.triggerOcr('KURRENT', false);
|
||||
await wait(60);
|
||||
await wait(150);
|
||||
|
||||
expect(job.progressMessage).not.toBe('');
|
||||
job.destroy();
|
||||
@@ -287,7 +287,7 @@ describe('createOcrJob — polling loop (short interval, real timers)', () => {
|
||||
pollIntervalMs: 20
|
||||
});
|
||||
await job.triggerOcr('KURRENT', false);
|
||||
await wait(60);
|
||||
await wait(150);
|
||||
|
||||
expect(job.skippedPages).toBeGreaterThanOrEqual(0);
|
||||
job.destroy();
|
||||
@@ -317,7 +317,7 @@ describe('createOcrJob — polling loop (short interval, real timers)', () => {
|
||||
resetDelayMs: 10
|
||||
});
|
||||
await job.triggerOcr('KURRENT', false);
|
||||
await wait(80);
|
||||
await wait(200);
|
||||
|
||||
expect(onJobFinished).toHaveBeenCalledWith('DONE');
|
||||
job.destroy();
|
||||
@@ -347,7 +347,7 @@ describe('createOcrJob — polling loop (short interval, real timers)', () => {
|
||||
resetDelayMs: 10
|
||||
});
|
||||
await job.triggerOcr('KURRENT', false);
|
||||
await wait(80);
|
||||
await wait(200);
|
||||
|
||||
expect(onJobFinished).toHaveBeenCalledWith('FAILED');
|
||||
expect(job.errorMessage).toBeTruthy();
|
||||
@@ -372,7 +372,7 @@ describe('createOcrJob — polling loop (short interval, real timers)', () => {
|
||||
pollIntervalMs: 20
|
||||
});
|
||||
await job.triggerOcr('KURRENT', false);
|
||||
await wait(60);
|
||||
await wait(150);
|
||||
|
||||
expect(job.running).toBe(true);
|
||||
job.destroy();
|
||||
@@ -399,7 +399,7 @@ describe('createOcrJob — polling loop (short interval, real timers)', () => {
|
||||
pollIntervalMs: 20
|
||||
});
|
||||
await job.triggerOcr('KURRENT', false);
|
||||
await wait(60);
|
||||
await wait(150);
|
||||
|
||||
expect(job.running).toBe(true);
|
||||
job.destroy();
|
||||
@@ -437,7 +437,7 @@ describe('createOcrJob.destroy', () => {
|
||||
job.destroy();
|
||||
|
||||
const callsAtDestroy = fetchImpl.mock.calls.length;
|
||||
await wait(80);
|
||||
await wait(200);
|
||||
// No additional fetch calls after destroy
|
||||
expect(fetchImpl.mock.calls.length).toBe(callsAtDestroy);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user