feat(geoportal-v2): swap refresh path to /parcel/enrich (deep-enrich)

gis-api session shipped PR3 (gis-api 09f1ab8 + gis-sync-orchestrator
0371d81): new POST /api/v1/parcel/enrich does the full eTerra
round-trip (searchImmovableByIdentifier → fetchDocumentationData
→ fetchImmovableParcelDetails) and merges NR_CF / ADRESA / PROPRIETARI
+ 20-plus fields into gis_core.GisFeature.enrichment with a 30-day
cache. Verified on 266888 + 328607 → 27 keys with full PII.

Wired in three places:

1. src/lib/gis-api-client.ts — gisApi.parcel.enrich({siruta,
   cadastralRef, force?}) thin wrapper.

2. src/app/api/gis/parcel/enrich/route.ts — architots-side proxy,
   matches the parcel/tech pattern (auth check → forward → bubble up
   GisApiError status codes).

3. src/modules/geoportal/v2/feature-info-panel.tsx — refreshFromAncpi
   now POSTs to /api/gis/parcel/enrich instead of /api/gis/parcel/tech.
   After the orchestrator returns, the panel re-fetches the canonical
   record via parcela.get (when uuid known) or parcela.find (when
   not), so it sees exactly what gis_core stores rather than the
   orchestrator response shape.

The existing auto-trigger (fires when detail has no NR_CF/ADRESA/
PROPRIETARI) now actually fills those fields. Subsequent clicks on the
same parcel hit gis-api's 30-day cache (5ms vs 1-2s live fetch).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude VM
2026-05-19 22:24:02 +03:00
parent 87f9d72e4f
commit 02a466ccaa
3 changed files with 83 additions and 18 deletions
+23 -18
View File
@@ -417,7 +417,12 @@ export function FeatureInfoPanel({ feature, onClose, basic = false }: Props) {
setRefreshing(true);
setError(null);
try {
const res = await fetch("/api/gis/parcel/tech", {
// PR3 deep-enrich path: gis-api orchestrates the eTerra round-trip
// and persists NR_CF / ADRESA / PROPRIETARI + tech fields in gis_core
// (30-day cache; force=true bypasses). After this returns the
// central record is canonical — we re-fetch it via parcela.get or
// parcela.find so the panel sees what's actually in gis_core.
const enrichResp = await fetch("/api/gis/parcel/enrich", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
@@ -426,16 +431,17 @@ export function FeatureInfoPanel({ feature, onClose, basic = false }: Props) {
force: true,
}),
});
if (!res.ok) {
const body = await res.json().catch(() => ({}));
setError(body.error || "refresh_failed");
if (!enrichResp.ok) {
const body = await enrichResp.json().catch(() => ({}));
setError(body.error || `enrich_failed_${enrichResp.status}`);
return;
}
const techData = await res.json().catch(() => null);
// After orchestrator updates the central DB, re-fetch via the
// server-side find/get path so we land on the canonical shape
// (and pick up rich enrichment that the tech response itself
// doesn't carry).
const enriched = (await enrichResp.json().catch(() => null)) as
| { siruta?: string; cadastralRef?: string; enrichment?: Record<string, unknown>; enrichedAt?: string }
| null;
// Re-fetch canonical record so the panel matches what other clients
// would see (and so we get isActive / layerId / etc.).
const id = detail?.id ?? feature.id;
let updated: Response | null = null;
if (id) {
@@ -447,19 +453,18 @@ export function FeatureInfoPanel({ feature, onClose, basic = false }: Props) {
`&layerId=${encodeURIComponent(feature.layerId)}`,
);
}
if (updated.ok) {
if (updated && updated.ok) {
setDetail(await updated.json());
} else if (techData) {
// Final fallback project the orchestrator response if we
// can't re-fetch the canonical record.
const inner =
(techData?.data as Record<string, unknown> | undefined) ?? techData;
} else if (enriched?.enrichment) {
// Fallback: project the enrich response directly when the
// canonical re-fetch can't run.
setDetail({
siruta: feature.siruta,
cadastralRef: feature.cadastralRef,
siruta: enriched.siruta ?? feature.siruta,
cadastralRef: enriched.cadastralRef ?? feature.cadastralRef,
areaValue: feature.areaValue,
layerId: feature.layerId,
enrichment: inner as Record<string, unknown>,
enrichment: enriched.enrichment,
enrichedAt: enriched.enrichedAt,
});
}
} catch {