feat(good): wire GOOD import/export UI into the Diagnose view
Completes the GOOD interop point end-to-end (the conversion engine landed earlier in goodInterop.ts). No new IPC needed - reuses the existing artifacts:load / artifacts:saveMany / good:export bridge. - Scan controller gains exportGoodFromStore (loadArtifacts -> storedArtifactsToGood -> exportGood) and importGoodArtifacts (saveMany + snapshot refresh), plus a canGoodInterop flag. - DiagnosticsView adds a GOOD Interop card: export the scan store as GOOD, or import a GOOD file. The file is read in the renderer via a file input + goodDatabaseToStoredArtifacts, so no file-dialog IPC is required. Verified in the browser preview after a clean restart: the Diagnose view renders all cards (Status, Last scan, GOOD Interop, Automation log, Crops/OCR), the Scan<->Diagnose switch works with no console errors (the earlier hook-order warnings were stale-HMR artifacts from deleting files mid-session). 120 tests + build green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,8 @@ import { useScanViewStateSync } from "./useScanViewStateSync";
|
||||
import { emptyAutoScanStats, resolveScanTargetCount, type AutoScanStats, type ScanSummary } from "../../../lib/scannerSession";
|
||||
import type { ScanViewProps, ScanViewControllerResult } from "../types";
|
||||
import type { CaptureResult, ClickResult, ReviewSampleRecord } from "../../../types/global";
|
||||
import type { StoredArtifactRecord } from "../../../types/storage";
|
||||
import { storedArtifactsToGood } from "../../../lib/goodInterop";
|
||||
import { createRendererRepositories } from "../../../infrastructure/repositories/rendererBridgeRepositories";
|
||||
|
||||
export function useScanViewController({
|
||||
@@ -37,6 +39,7 @@ export function useScanViewController({
|
||||
const snapshotRepo = repositories?.snapshot;
|
||||
const automationRepo = repositories?.automation;
|
||||
const captureRepo = repositories?.capture;
|
||||
const exportRepo = repositories?.export;
|
||||
|
||||
const [detailsOpen, setDetailsOpen] = useState(false);
|
||||
const [diagnosticsOpen, setDiagnosticsOpen] = useState(false);
|
||||
@@ -152,6 +155,25 @@ export function useScanViewController({
|
||||
setReviewQueueOpen,
|
||||
});
|
||||
|
||||
const canGoodInterop = bridgeReady && Boolean(artifactRepo?.loadAll) && Boolean(artifactRepo?.saveMany);
|
||||
|
||||
const exportGoodFromStore = useCallback(async () => {
|
||||
if (!artifactRepo?.loadAll || !exportRepo?.exportGood) return { ok: false, count: 0 };
|
||||
const loaded = await artifactRepo.loadAll();
|
||||
const records = loaded.artifacts ?? [];
|
||||
const good = storedArtifactsToGood(records);
|
||||
const result = await exportRepo.exportGood(good);
|
||||
return { ok: Boolean(result.ok), path: result.path, count: good.artifacts.length };
|
||||
}, [artifactRepo, exportRepo]);
|
||||
|
||||
const importGoodArtifacts = useCallback(async (records: StoredArtifactRecord[]) => {
|
||||
if (!artifactRepo?.saveMany || records.length === 0) return { ok: false, added: 0, updated: 0 };
|
||||
const result = await artifactRepo.saveMany(records);
|
||||
if (typeof result.total === "number") setStoredTotal(result.total);
|
||||
await onStoredArtifactsChanged?.();
|
||||
return { ok: Boolean(result.ok), added: result.added ?? 0, updated: result.updated ?? 0 };
|
||||
}, [artifactRepo, onStoredArtifactsChanged]);
|
||||
|
||||
useScanViewStateSync({
|
||||
artifactRepo,
|
||||
latestCapture,
|
||||
@@ -229,5 +251,8 @@ export function useScanViewController({
|
||||
openReviewQueue: openReviewQueueModal,
|
||||
runAutoReviewScan,
|
||||
runVisibleGridScan,
|
||||
canGoodInterop,
|
||||
exportGoodFromStore,
|
||||
importGoodArtifacts,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user