Commit Graph

54 Commits

Author SHA1 Message Date
AI Assistant aa11ca389e feat(eterra): RGI API routes + test page for issued documents
New eTerra RGI (Registrul General de Intrare) integration:

API routes (/api/eterra/rgi/):
- POST /applications — list applications with workspace/year filters
- GET /details?applicationId=X — application details
- GET /issued-docs?applicationId=X&workspaceId=Y — issued documents list
- GET /download-doc?wid=X&aid=Y&did=Z — download issued document

EterraClient: added rgiPost, rgiGet, rgiDownload methods for RGI API.

Test page (/rgi-test):
- Filters: workspace, orgUnit, year
- Toggle: "Doar solutionate cu termen viitor"
- Table with application list, expandable issued docs, download links
- Raw JSON debug sections (collapsible)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 20:59:49 +02:00
AI Assistant 3da45a4cab feat(parcel-sync): sync button on empty Harta tab + intravilan in base sync
Map tab: when UAT has no local data, shows a "Sincronizează terenuri,
clădiri și intravilan" button that triggers background base sync.

Sync background (base mode): now also syncs LIMITE_INTRAV_DYNAMIC layer
(intravilan boundaries) alongside TERENURI_ACTIVE + CLADIRI_ACTIVE.
Non-critical — if intravilan fails, the rest continues.

Also fixed remaining \u2192 unicode escapes in export/layers/epay tabs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 18:04:09 +02:00
AI Assistant 3fcf7e2a67 fix(geoportal): Google satellite, ESC/right-click exit, no UAT fill, ANCPI bbox fix
Basemaps: added Google Satellite option
ANCPI ortofoto: fixed bbox conversion (all 4 corners, not just SW/NE)
Selection: ESC key and right-click exit selection mode, tooltips updated
UAT layers: removed fill (only lines + labels), less visual clutter
Proprietari vechi: greyed out (opacity-50) so current owners stand out

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 14:19:02 +02:00
AI Assistant 6c55264fa3 feat(geoportal): OpenFreeMap vector basemaps + eTerra ORTO 2024 ortophoto
Basemap options:
- Liberty (OpenFreeMap vector) — default, sharp vector tiles
- Dark (OpenFreeMap) — dark theme, auto-styled
- Satellite (ESRI World Imagery) — raster
- ANCPI Ortofoto 2024 — proxied via /api/eterra/tiles/orto, converts
  Web Mercator z/x/y to EPSG:3844 bbox, authenticates with eTerra
  session, caches 24h. Requires ETERRA_USERNAME/PASSWORD env vars.

Replaces old raster OSM/OpenTopoMap with vector styles.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 18:43:21 +02:00
AI Assistant 14a77dd6f7 perf: cache GisFeature counts in memory (5min TTL, stale-while-revalidate)
Feature count groupBy query is expensive but data changes rarely.
First request waits for query, subsequent ones return cached instantly.
After 5min, stale cache is returned immediately while background
refresh runs. Badge "N local" is back on UAT dropdown.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 13:45:33 +02:00
AI Assistant d0c1b5d48e perf: select only needed columns from GisUat, skip geometry (~100MB) 2026-03-23 13:23:23 +02:00
AI Assistant ad4c72f527 perf(parcel-sync): make GisFeature groupBy opt-in on /api/eterra/uats
The groupBy query scanning the entire GisFeature table (~30k+ rows)
was blocking the UAT list API for 25+ seconds on every page load.
Feature counts are now opt-in via ?features=true query param.
Default response is instant (just GisUat table, no joins).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 12:44:09 +02:00
AI Assistant f6781ab851 feat(parcel-sync): store UAT geometries from LIMITE_UAT in local DB
- Add geometry (Json), areaValue (Float), lastUpdatedDtm (String) to
  GisUat model for local caching of UAT boundaries
- County refresh now fetches LIMITE_UAT with returnGeometry=true and
  stores EsriGeometry rings per UAT in EPSG:3844
- Uses LAST_UPDATED_DTM from eTerra for future incremental sync
- Skips geometry fetch if >50% already have geometry stored

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 23:14:52 +02:00
AI Assistant 7b10f1e533 fix(parcel-sync): use verified WORKSPACE_ID → county mapping from eTerra
LIMITE_UAT provides SIRUTA + WORKSPACE_ID for all 3186 UATs across 42
workspaces. eTerra nomenclature APIs all return 404, and immovable list
returns empty for small communes. Use verified workspace→county mapping
derived from eTerra data (cross-referenced sample UATs + DB confirmations).
Logs unknown workspaces if eTerra ever adds new ones.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:52:46 +02:00
AI Assistant 61a44525bf fix(parcel-sync): resolve county names 100% from eTerra, zero hardcoding
LIMITE_UAT gives SIRUTA + WORKSPACE_ID for all 3186 UATs. For each of
the 42 unique workspaces, fetch 1 immovable via fetchImmovableListBy
AdminUnit — the response includes workspace.name = county name.
No static mappings, no nomenclature endpoints (they 404).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:39:02 +02:00
AI Assistant ce49b9e536 fix(parcel-sync): resolve counties via LIMITE_UAT WORKSPACE_ID + known UAT seats
eTerra nomenclature endpoints (fetchCounties, fetchNomenByPk) return
404. New approach: LIMITE_UAT gives ADMIN_UNIT_ID + WORKSPACE_ID for
all 3186 UATs across 42 workspaces. Use a static mapping of county
seat SIRUTAs to identify which workspace belongs to which county.
Logs unresolved workspaces for debugging.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:36:29 +02:00
AI Assistant f9a2f6f82a fix(parcel-sync): use LIMITE_UAT + fetchNomenByPk for county data
fetchCounties() returns 404 — endpoint doesn't exist on eTerra.
New approach: query LIMITE_UAT layer for all features (no geometry)
to discover SIRUTA + WORKSPACE_ID per UAT, then resolve each unique
WORKSPACE_ID to county name via fetchNomenByPk(). Fallback: resolve
county for UATs that already have workspacePk in DB.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:29:56 +02:00
AI Assistant 899b5c4cf7 fix(parcel-sync): populate county data during login, not via PATCH
Root cause: PATCH endpoint created a new EterraClient which tried
to re-login with expired session → 401. Now county refresh runs
immediately after successful login in the session route, using the
same authenticated client (fire-and-forget). Component reloads UAT
data 5s after connection to pick up fresh county info.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:23:46 +02:00
AI Assistant 379e7e4d3f feat(parcel-sync): add diagnostic endpoint for county debugging
GET /api/eterra/uats/test-counties returns raw eTerra nomenclature
response structure — shows exact field names and data format for
fetchCounties() and fetchAdminUnitsByCounty(). Temporary diagnostic
to fix county population issue.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 22:04:37 +02:00
AI Assistant 8fa89a7675 fix(parcel-sync): restore SIRUTA in dropdown, add county debug output
- Restore SIRUTA code display in parentheses next to UAT name
- PATCH response now includes debug samples (sampleUat keys, county
  raw data) visible in browser console for diagnosing matching issues
- POST endpoint now supports resync (upsert mode, safe to call again)
- Client logs full PATCH result to browser console for debugging

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 21:34:29 +02:00
AI Assistant 431291c410 fix(parcel-sync): robust county population + local feature count in dropdown
- PATCH /api/eterra/uats: handle nested responses (unwrapArray), try
  multiple field names (extractName/extractCode), log sample UAT for
  debugging, match by code first then by name
- GET /api/eterra/uats: include localFeatures count per SIRUTA via
  GisFeature groupBy query
- Dropdown: show green badge with local feature count, county with dash
- Add SKILLS.md for ParcelSync/eTerra/GIS module context

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 21:18:22 +02:00
AI Assistant 79750b2a4a fix(parcel-sync): use eTerra nomenclature API for county population
LIMITE_UAT layer lacks WORKSPACE_ID field, so the previous approach
failed silently. Now uses fetchCounties() + fetchAdminUnitsByCounty()
nomenclature API: Phase 1 fills county for UATs with existing
workspacePk, Phase 2 enumerates counties and matches by name.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 20:56:43 +02:00
AI Assistant 86c39473a5 feat(parcel-sync): show county in UAT search dropdown via eTerra data
PATCH /api/eterra/uats fetches counties from eTerra nomenclature and
LIMITE_UAT layer, then batch-updates GisUat records with county name
and workspacePk. Auto-triggers on first eTerra connection when county
data is missing. Helps distinguish same-name UATs in different counties.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 20:46:13 +02:00
AI Assistant b7a236c45a feat(parcel-sync): eTerra health check + maintenance detection
- New eterra-health.ts service: pings eTerra periodically (3min),
  detects maintenance (503, keywords), tracks consecutive failures
- New /api/eterra/health endpoint for explicit health queries
- Session route blocks login when eTerra is in maintenance (503 response)
- GET /api/eterra/session now includes eterraAvailable/eterraMaintenance
- ConnectionPill shows amber 'Mentenanță' state with AlertTriangle icon
  instead of confusing red error when eTerra is down
- Auto-connect skips when maintenance detected, retries when back online
- 30s session poll auto-detects recovery and re-enables auto-connect
2026-03-08 10:28:30 +02:00
AI Assistant 6557cd5374 feat(parcel-sync): per-UAT analytics dashboard in Database tab
- New API route /api/eterra/uat-dashboard with SQL aggregates
  (area stats, intravilan/extravilan split, land use, top owners, fun facts)
- CSS-only dashboard component: KPI cards, donut ring, bar charts
- Dashboard button on each UAT card in DB tab, expands panel below
2026-03-08 10:18:34 +02:00
AI Assistant 6558c690f5 feat(parcel-sync): owner name search (proprietar) in Search tab
- New search mode toggle: Nr. Cadastral / Proprietar
- Owner search queries:
  1. Local DB first (enrichment PROPRIETARI/PROPRIETARI_VECHI ILIKE)
  2. eTerra API fallback (tries personName/titularName/ownerName filter keys)
- DB search works offline (no eTerra connection needed) — uses enriched data
- New API route: POST /api/eterra/search-owner
- New eterra-client method: searchImmovableByOwnerName()
- Owner results show source badge (DB local / eTerra online)
- Results can be added to saved list and exported as CSV
- Relaxed search tab guard: only requires UAT selection (not eTerra connection)
- Cadastral search still requires eTerra connection (shows hint when offline)
2026-03-08 03:48:23 +02:00
AI Assistant d12f01fc02 fix(parcel-sync): add 2min timeout to no-geom scan, non-blocking UI
- Server: Promise.race with 120s timeout on no-geom-scan API route
- Client: AbortController with 120s timeout on scan fetch
- UI: show 'max 2 min' during scanning + hint that buttons work without scan
- UI: timeout state shows retry button + explains no-geom won't be available
- Prevents indefinitely stuck 'Se scanează...' on slow eTerra responses
2026-03-08 02:28:51 +02:00
AI Assistant e57ca88e7e fix: increase background job progress retention to 6h, localStorage recovery to 8h 2026-03-08 01:59:09 +02:00
AI Assistant c43082baee feat(parcel-sync): background sync + download from DB
- New POST /api/eterra/sync-background: fire-and-forget server-side processing
  Starts sync + optional enrichment in background, returns 202 immediately.
  Progress tracked via existing /api/eterra/progress polling.
  Work continues in Node.js event loop even if browser is closed.
  Progress persists 1 hour for background jobs (vs 60s for normal).

- Enhanced POST /api/eterra/export-local: base/magic mode support
  mode=base: ZIP with terenuri.gpkg + cladiri.gpkg from local DB
  mode=magic: adds terenuri_magic.gpkg (enrichment merged, includes no-geom),
  terenuri_complet.csv, raport_calitate.txt, export_report.json
  All from PostgreSQL — zero eTerra API calls, instant download.

- UI: background sync section in Export tab
  'Sync fundal Baza/Magic' buttons: start background processing
  'Descarc─â din DB Baza/Magic' buttons: instant download from local DB
  Background job progress card with indigo theme (distinct from export)
  localStorage job recovery: resume polling after page refresh
  'Descarc─â din DB' button shown on completion
2026-03-08 01:53:24 +02:00
AI Assistant b287b4c34b fix: stable scan display, accurate workflow preview, cladiri count
ROOT CAUSE: The cross-reference between immovable list and GIS layer
produces wildly different matchedCount on each scan (320, 430, 629, 433)
because the eTerra immovable/list API with inscrisCF=-1 returns
inconsistent results across calls. The GIS layer count (505) is stable.

SCAN DISPLAY — now uses only stable numbers:
- Header shows 'Layer GIS: 505 terenuri + X cladiri' (stable ArcGIS count)
- Shows 'Lista imobile: 2.717 (estimat ~2.212 fara geometrie)' using
  simple subtraction totalImmovables - remoteGisCount
- Cross-ref matchedCount kept internally for import logic, but NOT shown
  as the primary number — eliminates visual instability
- hasNoGeomParcels now uses estimated count (stable)

WORKFLOW PREVIEW — now accurate:
- Step 1: 'Sync GIS — descarca 505 terenuri + X cladiri' (separate counts)
  or 'skip (date proaspete in DB)' when fresh
- Step 2 (enrichment): Fixed 'deja imbogatite' bug when DB is empty.
  Now correctly computes what WILL be in DB after sync completes:
  geoAfterSync + noGeomAfterImport - localDbEnrichedComplete
- Steps 3-4 unchanged

CLADIRI COUNT:
- Scan now also fetches CLADIRI_ACTIVE layer count (lightweight, OBJECTID only)
- New field remoteCladiriCount in NoGeomScanResult
- Displayed in header and workflow step 1
- Non-fatal: if CLADIRI fetch fails, just shows 0
2026-03-07 21:40:38 +02:00
AI Assistant 531c3b0858 fix: scan numbers always add up, match quality tracking, pipeline audit
SCAN DISPLAY:
- Use matchedCount (withGeometry) for 'cu geometrie' — ALWAYS adds up
  with noGeomCount to equal totalImmovables (ground truth arithmetic)
- Show remoteGisCount separately as 'Layer GIS: N features (se descarca toate)'
- When remoteGisCount != matchedCount, show matching detail with breakdown
  (X potrivite + cadRef/ID split) so mismatches are transparent
- Workflow preview step 1 still uses remoteGisCount (correct: all GIS
  features get downloaded regardless of matching)

MATCH QUALITY TRACKING:
- New fields: matchedByRef, matchedById in NoGeomScanResult
- Track how many immovables matched by cadastral ref vs by IMMOVABLE_ID
- Console log match quality for server-side debugging
- scannedAt timestamp for audit trail

PIPELINE AUDIT (export report):
- New 'pipeline' section in export_report.json with full trace:
  syncedGis, noGeometry (imported/cleaned/skipped), enriched, finalDb
- raport_calitate.txt now has PIPELINE section before quality analysis
  showing exactly what happened at each step
- Capture noGeomCleaned + noGeomSkipped in addition to noGeomImported
2026-03-07 21:22:29 +02:00
AI Assistant 1e6888a32a fix: show remoteGisCount (505) as cu geometrie, add no-geom cleanup step
- UI: scan card now shows remoteGisCount instead of matchedCount (withGeometry)
  as the primary 'cu geometrie' number — this is the true GIS layer feature count
- UI: workflow preview step 1 shows remoteGisCount for download count
- UI: mismatch note reworded as secondary detail about cross-reference matching
- Import: automatic cleanup step at start of syncNoGeometryParcels
  - Builds valid immovablePk set from fresh list (active + identification/area)
  - Deletes stale NO_GEOMETRY records not in the valid set
  - Reports cleaned count in result + progress note
- NoGeomSyncResult type: added 'cleaned' field
- Gitignore: temp-db-check.cjs
2026-03-07 21:00:43 +02:00
AI Assistant af2631920f feat(parcel-sync): quality gate filter for no-geom import + diagnostic endpoint
- Filter no-geom items before import: must have identification (cadRef/CF/paperCad/paperLb) OR area
- Multi-field area extraction: area, measuredArea, areaValue, suprafata
- Scan quality breakdown: withCadRef, withPaperCf, withPaperCad, withArea, useful, empty
- Added paperLbNo to quality analysis and samples
- UI: quality breakdown grid in scan card
- UI: filtered count in workflow preview (shows useful, not total)
- UI: enrichment estimate uses useful count
- New diagnostic endpoint /api/eterra/no-geom-debug for field inspection
2026-03-07 19:58:43 +02:00
AI Assistant 681b52e816 feat: quality analysis for no-geom parcels + raport_calitate.txt
Scan phase:
- qualityBreakdown on NoGeomScanResult: withCadRef, withPaperCad,
  withPaperCf, withArea, useful vs empty counts
- UI scan card shows quality grid before deciding to export

Export phase:
- Comprehensive enrichment quality analysis: owners, CF, address,
  area, category, building — split by with-geom vs no-geom
- raport_calitate.txt in ZIP: human-readable Romanian report with
  per-category breakdowns and percentage stats
- export_report.json includes full qualityAnalysis object
- Progress completion note shows quality summary inline
2026-03-07 19:23:57 +02:00
AI Assistant ba579d75c1 feat(parcel-sync): include no-geometry rows in Magic GPKG + HAS_GEOMETRY column
- Magic GPKG (terenuri_magic.gpkg) now contains ALL records:
  rows with geometry render as polygons, rows without have null geom
  but still carry all attribute/enrichment data (QGIS shows them fine)
- Added HAS_GEOMETRY column to Magic GPKG fields (0 or 1)
- GPKG builder now supports includeNullGeometry option: splits features
  into spatial-first (creates table), then appends null-geom rows
- Base terenuri.gpkg / cladiri.gpkg unchanged (spatial only)
- CSV still has all records as before
- GeoJsonFeature type now allows null geometry
- Reproject: null geometry guard added
- UI text updated: no longer says 'Nu apar in GPKG'
2026-03-07 18:06:28 +02:00
AI Assistant b01ea9fc37 fix(parcel-sync): scan uses remote GIS layer instead of empty local DB
- scanNoGeometryParcels now fetches TERENURI_ACTIVE features from remote
  ArcGIS (lightweight, no geometry) to cross-reference with eTerra immovable list
- Cross-references by both NATIONAL_CADASTRAL_REFERENCE and IMMOVABLE_ID
- Works correctly regardless of whether user has synced to local DB
- Renamed totalInDb -> withGeometry in NoGeomScanResult, UI, and API
- Extended fetchAllLayer() to forward outFields/returnGeometry options
2026-03-07 17:32:49 +02:00
AI Assistant db6ac5d3a3 fix: dynamic workspaceId for no-geometry scan (was hardcoded 65)
- resolveWorkspacePk chain: explicit param -> GisUat DB -> ArcGIS layer query
- UI passes workspacePk from UAT selection to scan API
- Fixes: FELEACU (Cluj, workspace!=65) returning 0 immovables
- Better messaging: shows X total, Y with geometry, Z without
- Shows warning when 0 immovables found (workspace resolution failed)
2026-03-07 16:52:20 +02:00
AI Assistant 30915e8628 feat(parcel-sync): import eTerra immovables without geometry
- Add geometrySource field to GisFeature (NO_GEOMETRY marker)
- New no-geom-sync service: scan + import parcels missing from GIS layer
- Uses negative immovablePk as objectId to avoid @@unique collision
- New /api/eterra/no-geom-scan endpoint for counting
- Export-bundle: includeNoGeometry flag, imports before enrich
- CSV export: new HAS_GEOMETRY column (0/1)
- GPKG: still geometry-only (unchanged)
- UI: checkbox + scan button on Export tab
- Baza de Date tab: shows no-geometry counts per UAT
- db-summary API: includes noGeomCount per layer
2026-03-07 12:58:10 +02:00
AI Assistant d50b9ea0e2 ParcelSync: PROPRIETARI_VECHI in enrichment + global DB summary tab (all UATs without login) 2026-03-07 12:16:34 +02:00
AI Assistant 097d010b5d fix(parcel-sync): sync progress visible during GPKG/bundle export
3 bugs fixed:
- syncLayer was called without jobId -> user saw no progress duringSync
- syncLayer set status:'done' prematurely -> client stopped polling before GPKG phase
- syncLayer errors were silently ignored -> confusing 'no features in DB' error

Added isSubStep option to syncLayer: when true, keeps status as 'running'
and doesn't schedule clearProgress. Export routes now pass jobId + isSubStep
so the real sync progress (Descărcare features 50/200) is visible in the UI.
2026-03-07 11:23:36 +02:00
AI Assistant b0927ee075 feat(parcel-sync): sync-first architecture — DB as ground truth
- Rewrite export-bundle to sync-first: check freshness -> sync layers -> enrich (magic) -> build GPKG/CSV from local DB
- Rewrite export-layer-gpkg to sync-first: sync if stale -> export from DB
- Create enrich-service.ts: extracted magic enrichment logic (CF, owners, addresses) with DB storage
- Add enrichment + enrichedAt columns to GisFeature schema
- Update PostGIS views to include enrichment data
- UI: update button labels for sync-first semantics, refresh sync status after exports
- Smart caching: skip sync if data is fresh (168h / 1 week default)
2026-03-07 11:12:54 +02:00
AI Assistant 0d0b1f8c9f feat(parcel-sync): native PostGIS geometry support for QGIS
- Remove postgresqlExtensions/postgis from Prisma schema (PostGIS not yet installed)
- Add prisma/postgis-setup.sql: trigger auto-converts GeoJSON→native geometry,
  GiST spatial index, QGIS-friendly views (gis_terenuri, gis_cladiri, etc.)
- Add POST /api/eterra/setup-postgis endpoint (idempotent, runs all SQL setup)
- Add safety-net raw SQL in sync-service: backfills geom after upsert phase
- Add QGIS/PostGIS setup card in layer catalog UI with connection info
- Schema comment documents the trigger-managed 'geom' column approach
2026-03-07 10:25:30 +02:00
AI Assistant b0c4bf91d7 feat(parcel-sync): sync-to-DB + local export + layer catalog enhancements
Layer catalog now has 3 actions per layer:
- Sync: downloads from eTerra, stores in PostgreSQL (GisFeature table),
  incremental — only new OBJECTIDs fetched, removed ones deleted
- GPKG: direct download from eTerra (existing behavior)
- Local export: generates GPKG from local DB (no eTerra needed)

New features:
- /api/eterra/export-local endpoint — builds GPKG from DB, ZIP for multi-layer
- /api/eterra/sync now uses session-based auth (no credentials in request)
- Category headers show both remote + local feature counts
- Each layer shows local DB count (violet badge) + last sync timestamp
- 'Export local' button in action bar when any layer has local data
- Sync progress message with auto-dismiss

DB schema already had GisFeature + GisSyncRun tables from prior work.
2026-03-07 10:05:39 +02:00
AI Assistant f73e639e4f fix(parcel-sync): quote all CSV fields + layer feature counts + drumul de azi
- CSV export: all fields properly quoted to prevent column misalignment
  when values contain commas (e.g. nrTopo with multiple topo numbers)
- Layer catalog: 'Numara toate' button fetches feature count per layer
  via /api/eterra/layers/summary (now supports session auth)
- Feature counts displayed as badges on each layer and category total
- 'Drumul de azi' section: persists today's layer counts in localStorage
  grouped by SIRUTA with timestamps
2026-03-06 23:19:58 +02:00
AI Assistant 0b049274b1 fix(search): robust address from all structured fields, multi-address support
- Always build from structured fields first (street, postalNo, building, locality)
- Fall back to addressDescription ONLY when no structured fields exist
- Support multiple addresses per immovable (joined with |)
- Deduplicate identical addresses
- Handle addressDescription as last-resort fallback
2026-03-06 22:57:11 +02:00
AI Assistant 742acb2d74 fix(search): proper address from all fields, parcel details endpoint, remove strikethrough
- Address: use street.dictionaryItem.name (Strada/Alee/etc) + street.name,
  postalNo as house number, buildingEntryNo/FloorNo/UnitNo/SectionNo
  for apartment details, locality.name, county.name
- Area+intravilan: fetch from /api/immovable/details/parcels/list (direct
  endpoint with area, intravilan, useCategory) before trying immApps
- Owners: remove strikethrough, use smaller neutral font (text-[11px]
  text-muted-foreground/80), rename label to 'Proprietari anteriori'
2026-03-06 22:50:57 +02:00
AI Assistant 4aa8e6c324 fix(search): use legalArea/measuredArea + nodeStatus=-1 for radiated owners
- Area: use measuredArea/legalArea from immovable list and documentation
  (actual fields from eTerra API, not area/areaValue which don't exist)
- Owners: detect radiated via nodeStatus === -1 on ancestor I (inscription)
  nodes. Walk up parentId tree from P (person)  I  A  C.
  nodeStatus: -1=radiated, 0=active, 2=pending
- Remove debug logging (data structure now understood)
2026-03-06 22:16:14 +02:00
AI Assistant 79c45adc37 fix(search): address [object Object], suprafata from folosinte, owner tree separation
- Address: handle street/locality/county as objects (extract .name)
  Fixes 'Str. [object Object], Feleacu'  'Str. X, Feleacu'
- Suprafata: fallback to total area from folosinte endpoint when
  immovable list and documentation APIs return null
- Owners: use tree traversal (nodeId/parentNodeId) to detect radiated
  inscriptions. Walk up parent chain to check radiationDate/cancelled/
  isActive/closed/status on ancestor inscription nodes.
- Enhanced logging: first/last 3 partTwoRegs entries + node types
  for debugging owner structure in Dozzle
2026-03-06 22:06:28 +02:00
AI Assistant 6eae4fa1c5 feat(search): separate active/old owners, improve address format, debug area fields
- Proprietari split into proprietariActuali + proprietariVechi (radiati)
  based on cancelled/isActive/radiat/status/radiationDate fields
- UI shows owners separated: actuali bold, vechi strikethrough
- CSV export has separate PROPRIETARI_ACTUALI / PROPRIETARI_VECHI columns
- Address: use addressDescription directly when present (>3 chars)
- Add county to address fallback
- Try area/areaValue/areaMP/suprafata fields for surface
- Debug logging: log immovable item keys + partTwoRegs sample on first search
2026-03-06 21:53:18 +02:00
AI Assistant 6b8feb9075 fix: workspace resolution via ArcGIS listLayer + seed UATs from uat.json
- resolveWorkspace: use listLayer() instead of listLayerByWhere() with
  hardcoded field names. Auto-discovers admin field (ADMIN_UNIT_ID/SIRUTA)
  from ArcGIS layer metadata via buildWhere().
- resolveWorkspace: persist WORKSPACE_ID to DB on first resolution for
  fast subsequent lookups.
- UATs POST: seed from uat.json (correct SIRUTA codes) instead of eTerra
  nomenclature API (nomenPk != SIRUTA, county nomenPk != WORKSPACE_ID).
- Remove eTerra nomenclature dependency from UATs endpoint.
- Fix activeJobs Set iteration error on container restart.
- Remove unused enrichedUatsFetched ref.
2026-03-06 21:24:51 +02:00
AI Assistant 1b72d641cd fix(parcel-sync): robust workspace resolution with direct nomen lookup
- Add fetchNomenByPk() to EterraClient  single nomen entry lookup
- resolveWorkspace() now tries fast path first: direct nomen lookup for
  SIRUTA  walk parentNomenPk chain to find COUNTY (1-3 calls vs 42+)
- Falls back to full county scan only if direct lookup fails
- Search route: DB lookup as middle fallback between workspacePk and resolve
- Debug logging to trace workspace resolution on production
- Fix: try all possible UAT identifier fields (nomenPk, siruta, code, pk)
2026-03-06 21:09:22 +02:00
AI Assistant ec5a866673 feat(parcel-sync): store UATs in PostgreSQL, eliminate repeated eTerra calls
- GisUat table now includes workspacePk column (created via raw SQL)
- GET /api/eterra/uats serves from PostgreSQL  instant, no eTerra login needed
- POST /api/eterra/uats triggers sync check: compares county count with DB,
  only does full eTerra fetch if data differs or DB is empty
- Frontend loads UATs from DB on mount (fast), falls back to uat.json if empty
- On eTerra connect, fires POST to sync-check; if data changed, reloads from DB
- Workspace cache populated from DB on GET for search route performance
2026-03-06 20:56:12 +02:00
AI Assistant d948e5c1cf feat(parcel-sync): county-aware UAT autocomplete with workspace resolution
- New /api/eterra/uats endpoint fetches all counties + UATs from eTerra,
  caches server-side for 1 hour, returns enriched data with county name
  and workspacePk for each UAT
- When eTerra is connected, auto-fetches enriched UAT list (replaces
  static uat.json fallback)  shows 'FELEACU (57582), CLUJ' format
- UAT autocomplete now searches both UAT name and county name
- Selected UAT stores workspacePk in state, passes it directly to
  /api/eterra/search  eliminates slow per-search county resolution
- Search route accepts optional workspacePk, falls back to resolveWorkspace()
- Dropdown shows UAT name, SIRUTA code, and county prominently
- Increased autocomplete results from 8 to 12 items
2026-03-06 20:46:44 +02:00
AI Assistant 540b02d8d2 feat(parcel-sync): search by cadastral number with full details
Search tab now uses eTerra application API (same as the web UI):

- POST /api/eterra/search queries /api/immovable/list with exact
  identifierDetails filter + /api/documentation/data for full details
- Returns: nr cad, nr CF, CF vechi, nr topo, suprafata, intravilan,
  categorii folosinta, adresa, proprietari, solicitant
- Automatic workspace (county) resolution from SIRUTA with cache
- Support for multiple cadastral numbers (comma separated)

UI changes:
- Detail cards instead of flat ArcGIS feature table
- Copy details to clipboard button per parcel
- Add parcels to list + CSV export
- Search list with summary table + CSV download
- No more layer filter or pagination (not needed for app API)

New EterraClient methods:
- searchImmovableByIdentifier (exact cadaster lookup)
- fetchCounties / fetchAdminUnitsByCounty (workspace resolution)
2026-03-06 19:58:33 +02:00
AI Assistant c98ce81cb7 feat(parcel-sync): live eTerra search by cadastral number
- Add /api/eterra/search  queries eTerra ArcGIS REST API directly
  by NATIONAL_CADASTRAL_REFERENCE, NATIONAL_CADNR, or INSPIRE_ID
  across TERENURI_ACTIVE + CLADIRI_ACTIVE layers
- Search tab now queries eTerra live (not local DB) with 600ms debounce
- Requires session connected + UAT selected to search
- Updated placeholder and empty state messages in Romanian
2026-03-06 19:18:18 +02:00