- GET /api/eterra/counties — distinct county list from GisUat
- POST /api/eterra/sync-county — background sync all UATs in a county
(TERENURI + CLADIRI + INTRAVILAN), magic mode for enriched UATs,
concurrency guard, creates notification on completion
- In-app notification service (KeyValueStore, CRUD, unread count)
- GET/PATCH /api/notifications/app — list and mark-read endpoints
- NotificationBell component in header with popover + polling
- Monitor page: county select dropdown + SyncTestButton with customBody
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New endpoint POST /api/eterra/refresh-all processes all 43 UATs
sequentially. UATs with >30% enrichment get magic mode, others
get base sync only. Each UAT uses the new delta engine (quick-count
+ VALID_FROM + rolling doc check). Progress tracked via progress store.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Always call syncLayer for TERENURI/CLADIRI (not gated by isFresh)
so that quick-count + VALID_FROM delta actually run on daily syncs
- syncLayer handles efficiency internally via quick-count match
- Add 48h freshness check for no-geom import (skip if recent)
- Admin layers: skip if synced within 24h
- Log sync summary (new features, updated features)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Manual trigger now only processes sync_terenuri and sync_cladiri steps.
import_nogeom and enrich are left for the regular weekend window.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Auto-poll every 15s when sync is running, 60s when idle
- Live status banner: running (with city/step), error list, weekend window waiting, connection error
- Highlight active city card and currently-running step with pulse animation
- Send immediate error email per failed step (not just at session end)
- Expose syncStatus/currentActivity/inWeekendWindow in API response
- Stop silently swallowing fetch/action errors — show them in the UI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Building labels (C1/C2/C3) disabled — Martin MVT tiles don't include
cadastral_ref as a property despite the PostgreSQL view exposing it.
Root cause needs investigation (Martin config or alternative tile server).
Removed temporary debug endpoints:
- /api/eterra/debug-tile-props
- /api/eterra/debug-tile-sample
Kept /api/eterra/debug-fields (useful long-term diagnostic).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Temporary diagnostic to verify what columns gis_cladiri view exposes
for Martin vector tiles. Needed to debug missing C1/C2 labels.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Shows all available eTerra fields for a parcel + buildings:
- GIS layer attributes (raw from ArcGIS)
- Immovable parcel details (intravilan, categories)
- Immovable list entry (address, areas)
- Documentation data (owners, registrations)
- Local DB state (enrichment, sync dates)
Usage: /api/eterra/debug-fields?siruta=161829&cadRef=77102
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The /wds page was showing 0 cities because the KeyValueStore was empty
until the scheduler ran for the first time. Now the GET endpoint
initializes the queue with the 9 default cities on first access.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All sync paths now include both admin layers (LIMITE_INTRAV_DYNAMIC +
LIMITE_UAT) as best-effort alongside terenuri + cladiri:
- export-bundle (hero buttons)
- sync-background (fire-and-forget)
- auto-refresh scheduler (weekday nights)
- weekend deep sync (weekend nights)
- freshness check (export tab badge)
LIMITE_UAT rarely changes so incremental sync will skip it almost
every time, but it stays fresh in the DB freshness check.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sync Incremental:
- Add fetchObjectIds (returnIdsOnly) to eterra-client — fetches only OBJECTIDs in 1 request
- Add fetchFeaturesByObjectIds — downloads only delta features by OBJECTID IN (...)
- Rewrite syncLayer: compare remote IDs vs local, download only new features
- Fallback to full sync for first sync, forceFullSync, or delta > 50%
- Reduces sync time from ~10 min to ~5-10s for typical updates
Smart Export Tab:
- Hero buttons detect DB freshness — use export-local (instant) when data is fresh
- Dynamic subtitles: "Din DB (sync acum Xh)" / "Sync incremental" / "Sync complet"
- Re-sync link when data is fresh but user wants forced refresh
- Removed duplicate "Descarca din DB" buttons from background section
Auto-Refresh Scheduler:
- Self-contained timer via instrumentation.ts (Next.js startup hook)
- Weekday 1-5 AM: incremental refresh for existing UATs in DB
- Staggered processing with random delays between UATs
- Health check before processing, respects eTerra maintenance
Weekend Deep Sync:
- Full Magic processing for 9 large municipalities (Cluj, Bistrita, TgMures, etc.)
- Runs Fri/Sat/Sun 23:00-04:00, round-robin intercalated between cities
- 4 steps per city: sync terenuri, sync cladiri, import no-geom, enrichment
- State persisted in KeyValueStore — survives restarts, continues across nights
- Email status report at end of each session via Brevo SMTP
- Admin page at /wds: add/remove cities, view progress, reset
- Hint link on export tab pointing to /wds
API endpoints:
- POST /api/eterra/auto-refresh — N8N-compatible cron endpoint (Bearer token auth)
- GET/POST /api/eterra/weekend-sync — queue management for /wds page
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Page improvements:
- County dropdown with all 41 Romanian counties (default Cluj)
- orgUnitId auto-computed (countyId * 1000 + 2)
- Sortable columns: click header to sort asc/desc with arrow indicators
- Search input: filters across all visible columns (diacritics-insensitive)
- Soft blocked message: amber toast "Documentul nu este inca disponibil"
auto-hides after 5s (no more redirect errors)
Download improvements:
- Meaningful filenames: {docType}_{appNo}.pdf (e.g. Harti_planuri_66903.pdf)
- Romanian diacritics stripped from filenames
- Returns { blocked: true } JSON instead of redirect when unavailable
Bug fix: replaced incorrect useState() side-effect with proper useEffect()
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Before downloading, now calls:
1. verifyCurrentActorAuthenticated — sets actor context in session
2. appdetail/details — loads application context
Then tries download regardless of fileVisibility result.
The session context might be what enables downloads that previously
returned 404.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When fileVisibility returns OK → download server-side (fast).
When not available → HTTP 302 redirect to eTerra direct URL.
User's browser session handles authentication automatically.
This means: if logged into eTerra in browser, ALL documents download.
If not logged in, eTerra shows its own login page.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Download route simplified:
1. fileVisibility check — if 404, returns "indisponibil" + eTerra URL
2. Single download pattern (the one that works)
When document not available server-side, response includes direct
eTerra URL as fallback. No more 7 pattern attempts = much faster.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Download route now:
- Calls fileVisibility with documentTypeId (if provided)
- Calls confirmOnView with documentPk
- Tries 3 different download URL patterns until one works
- Add &debug=1 to see diagnostic results instead of downloading
- Page now passes documentTypeId in download link
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rewrote RGI test page:
- Clean card-based UI with status icons (green=solved, amber=pending)
- Click row to expand and see issued documents
- Each document has a direct "Descarca" download button
- Filter toggle "Doar solutionate cu termen viitor"
- No more raw JSON tables
Download route now follows eTerra's 3-step flow:
1. fileVisibility — check access, get fileId
2. confirmOnView — confirm document view
3. loadDocument/downloadFile — actual file download
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
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>
- 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>
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>
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>
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>
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>
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>
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>
- 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>
- 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>
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>
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>
- 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
- 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
- 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)
- 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
- 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
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
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
- 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