diff --git a/tests/js/scan-progress.test.js b/tests/js/scan-progress.test.js index 6f0cfb9..008cff0 100644 --- a/tests/js/scan-progress.test.js +++ b/tests/js/scan-progress.test.js @@ -76,3 +76,22 @@ test("progressLabel summarises a running scan for the minimized pill", () => { assert.equal(progressLabel(running), "Scanning 42% · 84 files · 12 prompts"); assert.equal(progressLabel(initialState()), ""); }); + +test('pair totals survive completion and cancellation closes the running state', () => { + const running=reduceScanEvent(initialState(),{type:'progress',progress:30,processed:3}); + const done=reduceScanEvent(running,{type:'complete',pairs_added:2,pair_links_added:3,enriched:1,unchanged:4,ambiguous:1,failed:2}); + assert.deepEqual([done.pairs_added,done.pair_links_added,done.enriched,done.unchanged,done.ambiguous,done.failed],[2,3,1,4,1,2]); + const cancelled=reduceScanEvent(running,{type:'cancelled',processed:3,pairs_added:1}); + assert.equal(cancelled.status,'done'); + assert.equal(cancelled.cancelled,true); + assert.equal(cancelled.statusText,'Scan cancelled.'); + assert.equal(cancelled.progress,30); +}); + +test('completion label reports pair enrichment, unresolved work and failures', () => { + const {completionLabel}=require('../../web/js/scan-progress.js'); + const state=reduceScanEvent(initialState(),{type:'complete',processed:10,found:8,added:1,linked:2,pairs_added:3,pair_links_added:4,enriched:2,unchanged:1,ambiguous:1,unresolved:2,conflict:1,failed:1}); + assert.match(completionLabel(state),/3 new pairs, 4 pair links/); + assert.match(completionLabel(state),/4 need review, 1 failed/); + assert.match(completionLabel({...state,cancelled:true}),/^Scan cancelled!/); +}); diff --git a/web/js/admin.js b/web/js/admin.js index 157abd3..5aba7aa 100644 --- a/web/js/admin.js +++ b/web/js/admin.js @@ -2353,7 +2353,7 @@ if (this.scanState.status === ScanProgress.RUNNING) { // A scan we were following ended while we were disconnected. const last = status.last_event; - const terminal = last && (last.type === "complete" || last.type === "error"); + const terminal = last && (last.type === "complete" || last.type === "cancelled" || last.type === "error"); this.applyScanEvent( terminal ? last : { type: "error", message: "the server no longer reports a running scan" } ); @@ -2418,13 +2418,8 @@ } completeScan(state) { - const { processed, found, added, linked } = state; document.getElementById("scanPill").classList.add("hidden"); - const linkedText = linked > 0 ? `, linked ${linked} images to existing prompts` : ''; - this.showNotification( - `Scan completed! Processed ${processed} files, found ${found} prompts, added ${added} new prompts to database${linkedText}.`, - "success" - ); + this.showNotification(ScanProgress.completionLabel(state), "success"); // Auto-close after a short delay so the completion message is visible setTimeout(() => { diff --git a/web/js/scan-progress.js b/web/js/scan-progress.js index 567f063..8105b9f 100644 --- a/web/js/scan-progress.js +++ b/web/js/scan-progress.js @@ -55,12 +55,18 @@ found: num(event.found, state.found), }; } - if (event.type === "complete") { + if (event.type === "complete" || event.type === "cancelled") { + const counts = Object.fromEntries( + ['pairs_added','pair_links_added','enriched','unchanged','ambiguous','unresolved','conflict','failed'] + .map(key => [key, num(event[key], state[key] || 0)]) + ); return { ...state, + ...counts, status: DONE, - progress: 100, - statusText: "Scan completed!", + cancelled: event.type === "cancelled", + progress: event.type === "cancelled" ? state.progress : 100, + statusText: event.type === "cancelled" ? "Scan cancelled." : "Scan completed!", processed: num(event.processed, state.processed), found: num(event.found, state.found), added: num(event.added, state.added), @@ -93,8 +99,14 @@ return `Scanning ${pct}% · ${state.processed} files · ${state.found} prompts`; } + function completionLabel(state) { + const review = (state.ambiguous || 0) + (state.unresolved || 0) + (state.conflict || 0); + return `${state.cancelled ? 'Scan cancelled' : 'Scan completed'}! Processed ${state.processed} files, found ${state.found} prompts; ${state.added} new image records, ${state.linked} links, ${state.enriched || 0} enriched, ${state.unchanged || 0} unchanged; ${state.pairs_added || 0} new pairs, ${state.pair_links_added || 0} pair links; ${review} need review, ${state.failed || 0} failed.`; + } + const api = { initialState, + completionLabel, reduceScanEvent, fromStatus, dismissAction,