feat(parcel-sync): scan shows local DB context + Magic workflow preview

- NoGeomScanResult now includes: localDbTotal, localDbWithGeom, localDbNoGeom,
  localDbEnriched, localSyncFresh (parallel DB queries, fast)
- Scan card shows 'Baza de date locala: X cu geometrie + Y fara + Z imbogatite'
- Workflow preview shows numbered steps with smart estimates:
  step 1 shows 'skip (date proaspete)' when sync is fresh
  step 2 shows '~N noi de importat' or 'deja importate' for no-geom
  step 3 shows '~N de procesat (~M min)' or 'deja imbogatite' for enrichment
- All-geometry card also shows local DB summary
- User can see exactly what will happen before pressing Magic
This commit is contained in:
AI Assistant
2026-03-07 17:50:34 +02:00
parent b01ea9fc37
commit 96859dde4f
2 changed files with 195 additions and 12 deletions
@@ -103,6 +103,16 @@ export type NoGeomScanResult = {
paperCadNo?: string;
paperCfNo?: string;
}>;
/** Total features already in local DB (geometry + no-geom) */
localDbTotal: number;
/** Geometry features already synced in local DB */
localDbWithGeom: number;
/** No-geometry features already imported in local DB */
localDbNoGeom: number;
/** How many are already enriched (magic) in local DB */
localDbEnriched: number;
/** Whether local sync is fresh (< 7 days) */
localSyncFresh: boolean;
/** Error message if workspace couldn't be resolved */
error?: string;
};
@@ -141,6 +151,11 @@ export async function scanNoGeometryParcels(
withGeometry: 0,
noGeomCount: 0,
samples: [],
localDbTotal: 0,
localDbWithGeom: 0,
localDbNoGeom: 0,
localDbEnriched: 0,
localSyncFresh: false,
error: `Nu s-a putut determina workspace-ul (județul) pentru SIRUTA ${siruta}`,
};
}
@@ -206,11 +221,48 @@ export async function scanNoGeometryParcels(
});
}
// 4. Query local DB for context (what's already synced/imported)
const [localTotal, localNoGeom, localEnriched, lastSyncRun] =
await Promise.all([
prisma.gisFeature.count({
where: { layerId: "TERENURI_ACTIVE", siruta },
}),
prisma.gisFeature.count({
where: {
layerId: "TERENURI_ACTIVE",
siruta,
geometrySource: "NO_GEOMETRY",
},
}),
prisma.gisFeature.count({
where: {
layerId: "TERENURI_ACTIVE",
siruta,
enrichedAt: { not: null },
},
}),
prisma.gisSyncRun.findFirst({
where: { siruta, layerId: "TERENURI_ACTIVE", status: "done" },
orderBy: { completedAt: "desc" },
select: { completedAt: true },
}),
]);
const localWithGeom = localTotal - localNoGeom;
const syncFresh = lastSyncRun?.completedAt
? Date.now() - lastSyncRun.completedAt.getTime() < 168 * 60 * 60 * 1000
: false;
return {
totalImmovables: allImmovables.length,
withGeometry: remoteFeatures.length,
noGeomCount: noGeomItems.length,
samples: noGeomItems.slice(0, 20),
localDbTotal: localTotal,
localDbWithGeom: localWithGeom,
localDbNoGeom: localNoGeom,
localDbEnriched: localEnriched,
localSyncFresh: syncFresh,
};
}