Commit Graph

218 Commits

Author SHA1 Message Date
AI Assistant 0c4b91707f audit: production safety fixes, cleanup, and documentation overhaul
CRITICAL fixes:
- Fix SQL injection in geoportal search (template literal in $queryRaw)
- Preserve enrichment data during GIS re-sync (upsert update explicit fields only)
- Fix ePay version race condition (advisory lock in transaction)
- Add requireAuth() to compress-pdf and unlock routes (were unauthenticated)
- Remove hardcoded Stirling PDF API key (env vars now required)

IMPORTANT fixes:
- Add admin role check on registratura debug-sequences endpoint
- Fix reserved slot race condition with advisory lock in transaction
- Use SSO identity in close-guard-dialog instead of hardcoded "Utilizator"
- Storage DELETE catches only P2025 (not found), re-throws real errors
- Add onDelete: SetNull for GisFeature → GisSyncRun relation
- Move portal-only users to PORTAL_ONLY_USERS env var
- Add security headers (X-Frame-Options, X-Content-Type-Options, Referrer-Policy)
- Add periodic cleanup for eTerra/ePay session caches and progress store
- Log warning when ePay dataDocument is missing (expiry fallback)

Cleanup:
- Delete orphaned rgi-test page (1086 lines, unregistered, inaccessible)
- Delete legacy/ folder (5 files, unreferenced from src/)
- Remove unused ensureBucketExists() from minio-client.ts

Documentation:
- Optimize CLAUDE.md: 464 → 197 lines (moved per-module details to docs/)
- Create docs/ARCHITECTURE-QUICK.md (80 lines: data flow, deps, env vars)
- Create docs/MODULE-MAP.md (140 lines: entry points, API routes, cross-deps)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 06:40:34 +02:00
AI Assistant c012adaa77 fix: export buttons direct (no dropdown), compact mobile cards
Export fix:
- Replaced DropdownMenu with direct DXF/GPKG buttons in SelectionToolbar.
  Radix dropdown portals don't work inside fixed z-[110] containers.
  Direct buttons work reliably on all platforms.

Mobile RGI cards:
- Single-row compact layout: icon + nr cerere + solicitant + termen + status
- Smaller icons (3.5), tighter spacing, shorter status labels
- No Card wrapper — lightweight border div for less visual weight

Mobile filters:
- Tighter spacing, smaller labels

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 12:03:24 +02:00
AI Assistant d82b873552 fix(portal): mobile toolbar as fixed viewport element + layout fixes
Selection toolbar:
- Moved OUTSIDE map container div to a fixed viewport position
  (bottom-4, z-[110]). iOS Safari clips absolute elements inside
  calc(100vh) containers — fixed positioning solves this.
- Only shown when UAT selected and has data.

Mobile top layout:
- UAT card takes full width (right-2 not right-[140px])
- Basemap switcher at top-[52px] left-2 on mobile (below UAT card)
- Desktop: unchanged (top-right offset from zoom controls)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 11:06:48 +02:00
AI Assistant 12ff629fbf feat: ZIP download, mobile fixes, click centering, tooltip
ZIP download:
- Both portal and RGI test page now create a single ZIP archive
  (Documente_eliberate_{appNo}.zip) instead of sequential downloads
- Uses JSZip (already in project dependencies)

Portal mobile:
- Basemap switcher drops below UAT card on mobile (top-14 sm:top-2)
- Selection toolbar at bottom-3 with z-30 (always visible)
- Click on feature centers map on that parcel (flyTo)

Tooltips:
- Green download icon: "Descarca arhiva ZIP cu documentele cererii X"
- Updated on both portal and RGI test page

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 09:17:29 +02:00
AI Assistant 8acafe958b fix: freehand drawing, click highlight, mobile toolbar visibility
Freehand drawing fix:
- Disable dragPan when in freehand mode (was only disabling dblclick
  zoom). Without this, clicks were interpreted as pan gestures.
- Re-enable dragPan when exiting freehand mode.

Click highlight:
- Clicking a parcel in "off" mode now highlights it with the selection
  layer (amber fill + orange outline). Clicking empty space clears it.
- Provides visual feedback for which parcel was clicked.

Mobile toolbar:
- Moved selection toolbar higher (bottom-12 on mobile) with z-20
  to ensure it's above MapLibre attribution bar.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 07:11:16 +02:00
AI Assistant 45d4d1bf40 fix: hide enrichment in portal, CF disabled button, no UAT flash, fix overlaps
SelectionToolbar: new hideEnrichment prop hides the Enrichment button.
Portal uses it to show only Export + Clear in selection toolbar.

Portal feature panel: added disabled "Solicita extras CF" button with
tooltip "Sectiune platita — contacteaza administratorul".

Initial map zoom: starts at zoom 15 (close-up) instead of 7 (Romania
overview). Prevents the UAT boundaries flash before fitBounds runs.
Applied to both ParcelSync Harta tab and Portal.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 06:58:01 +02:00
AI Assistant 6f46a85ff3 fix(rgi+portal): default sort desc by termen, fix overlaps, tooltip
RGI (both pages):
- Default sort: Termen descrescator (cel mai in viitor sus)

Portal map:
- Basemap switcher moved left (right-12) to avoid zoom controls overlap
- Selection toolbar moved up (bottom-8) to avoid attribution overlap
- Download button has title tooltip on mobile cards

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 06:52:22 +02:00
AI Assistant 2cd35c790d fix(portal): mobile responsive — card view for RGI, visible map controls
Mobile (< 640px):
- RGI: card-based layout instead of table (shows nr cerere, status,
  solicitant, termen, rezolutie, UAT in compact card)
- Header: compact "Portal" title, smaller tab buttons
- Map: selection toolbar centered at bottom (always visible)
- UAT info card: smaller text, truncated, doesn't overlap basemap switcher
- Feature panel: narrower (w-56 vs w-64)
- Filter buttons: smaller text

Desktop (>= 640px):
- Same table view as before (hidden on mobile)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 06:25:12 +02:00
AI Assistant 7a36f0b613 fix(portal): build error + simple feature panel without enrichment/CF
Fixed:
- Added setLayoutProperty to MapLike type (was missing, broke build)
- Replaced FeatureInfoPanel with simple inline panel showing only:
  SIRUTA, Nr. cadastral, Suprafata (no enrichment, no CF extract,
  no "Actualizeaza" button)
- Fixed unknown type errors in JSX property access
- Hidden basemap boundaries + UAT layers for cleaner map

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 01:23:37 +02:00
AI Assistant 1919155d41 perf: hide basemap boundaries, remove UAT layers, optimize Martin minzoom
Basemap cleanup (after map load):
- Hide OpenFreeMap boundary/admin layers (redundant with our UATs)
- Keep city/town place labels, remove village-level
- Hide our Martin UAT layers (not needed in ParcelSync — filtered by siruta)

Martin tile optimization:
- gis_terenuri_status minzoom: 10 → 13 (parcels not visible below 13)
- gis_cladiri_status minzoom: 12 → 14 (buildings not visible below 14)
- Prevents unnecessary tile fetches at low zoom levels

Applied to both ParcelSync Harta tab and Portal map.
Requires docker restart martin.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 01:15:00 +02:00
AI Assistant 1a5487f0f7 fix: zoom no longer resets after manual pan/zoom (fitBounds once per siruta)
The fitBounds effect was re-triggered every time mapReady toggled
(which happened frequently due to the source-checking polling interval).
Now uses boundsFittedForSirutaRef to ensure fitBounds runs only ONCE
per siruta selection — changing UAT still zooms correctly, but manual
zoom/pan is preserved afterwards.

Fixed in both ParcelSync Harta tab and Portal map.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 01:08:33 +02:00
AI Assistant f1f4dc097e fix(portal): full-screen overlay + redirect portal-only users
Portal layout: removed conflicting (portal)/layout.tsx that had
duplicate html/body tags. Portal page now uses fixed overlay
(z-[100]) that covers the entire screen including sidebar.

Middleware: portal-only users (dan.tiurbe) are automatically
redirected from any non-portal route to /portal. They can still
access /api/ and /auth/ routes normally.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:49:44 +02:00
AI Assistant e420cd4609 feat: standalone portal page for Dan Tiurbe at /portal
Dedicated external portal combining RGI documents + cadastral map,
without ArchiTools sidebar/navigation.

Layout: (portal) route group with minimal layout (no AppShell).

Tab "Documente RGI":
- Full RGI functionality (county selector, sortable/filterable table,
  expandable docs with download, batch download)

Tab "Harta":
- UAT autocomplete selector
- Sync button when UAT has no data
- Map with basic parcel/building styling (no enrichment overlay)
- Only 3 basemaps: Harta (liberty), Noapte (dark), Google satellite
- Selection tools (click/rect/freehand) + DXF/GPKG export
- Feature info panel on click
- No CF extract ordering (hidden)

URL: https://tools.beletage.ro/portal

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:29:29 +02:00
AI Assistant 9df6c9f542 fix(rgi): default columns, date sort, clean filenames, green icon downloads all
Default columns: Nr. cerere, Solicitant, Termen, Status, Rezolutie, UAT
(matching user's preferred view). Obiect, Identificatori, Deponent,
Data depunere now off by default.

Date sort: dueDate and appDate columns now sort by raw timestamp
(not by DD.MM.YYYY string which sorted incorrectly).

Filenames: removed long documentPk from filename. Now uses
DocType_AppNo.pdf (e.g. Receptie_tehnica_66903.pdf). Duplicate
types get suffix: Receptie_tehnica_66903_2.pdf.

Green icon: click downloads ALL documents from that application
sequentially. Shows spinner while downloading. Tooltip shows
"Nr. 66903 — click descarca toate" + details.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 00:03:39 +02:00
AI Assistant aebe1d521c feat(rgi): download all docs button + tooltips on status icon
Download all:
- "Descarca toate" button in expanded docs panel
- Downloads each document sequentially with correct filename
  (e.g. Receptie_tehnica_66903_10183217654.pdf)
- Progress indicator: "2/5: Harti & planuri..."
- Skips blocked docs, shows summary "3 descarcate, 2 indisponibile"
- 300ms delay between downloads to avoid browser blocking

Status icon tooltip:
- Hover on green/clock icon shows: Nr cerere, Obiect, Status,
  Rezolutie, Termen, Identificatori

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:41:51 +02:00
AI Assistant 2f114d47de feat(rgi): sortable/filterable table, county selector, smart filenames, soft blocked msg
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>
2026-03-24 23:37:00 +02:00
AI Assistant c1006f395c fix(rgi): remove wrong dueDate lock — always show download button
The dueDate-based lock was incorrect: some documents with future
dueDate ARE downloadable. The availability depends on eTerra internal
rules, not predictably on dueDate.

Now all documents show a download button. If server-side download
fails (fileVisibility 404), it redirects to eTerra direct URL
which works in the user's browser session.

Filters changed to: Solutionate / Confirmate / Toate

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:23:48 +02:00
AI Assistant a191a684b2 feat(rgi): filter by downloadable/pending + locked document indicator
eTerra blocks document downloads until dueDate passes (new rule).
Now the page shows:

Filter modes:
- "Descarcabile acum" (default) — solved + dueDate passed
- "In asteptare" — solved + dueDate future (documents locked)
- "Toate" — no filter

UI indicators:
- Green download icon: ready to download
- Amber clock icon: solved but locked until dueDate
- Documents panel shows "Disponibile de la DD.MM.YYYY" badge when locked
- Download button replaced with date badge for locked documents

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:12:48 +02:00
AI Assistant 3614c2fc4a fix(rgi): set application context before download attempt
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>
2026-03-24 22:50:27 +02:00
AI Assistant 4beac959c8 fix(rgi): redirect to eTerra when server-side download unavailable
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>
2026-03-24 22:48:01 +02:00
AI Assistant b0a5918bd7 fix(rgi): fast download with fileVisibility gate + clear error message
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>
2026-03-24 22:39:53 +02:00
AI Assistant 5966a11f7e fix(rgi): download via user's eTerra browser session (not server-side)
Server credentials can list RGI applications and docs but can't download
files (confirmOnView returns false — only the current actor/deponent
has download permission).

Download now opens the eTerra URL directly in the user's browser,
which uses their existing eTerra session cookie. Flow:
1. Hidden iframe calls confirmOnView
2. After 500ms, opens downloadFile URL in new tab

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 22:32:09 +02:00
AI Assistant 0e5c01839d fix(rgi): exhaustive download debug — tries 7 URL patterns + GET/POST confirmOnView
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 22:25:14 +02:00
AI Assistant d780c3c973 fix(rgi): diagnostic download route — tries multiple URL patterns
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>
2026-03-24 22:13:34 +02:00
AI Assistant 7a28d3ad33 fix(rgi): proper table layout with td per column for alignment
Replaced single colSpan td with flex layout → proper td per column.
Headers and data cells now align correctly. Expanded docs row uses
colSpan only for the detail panel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 22:03:41 +02:00
AI Assistant e5e2fabb1d fix(rgi): correct download flow — confirmOnView + downloadFile by documentPk
The download uses documentPk (not documentTypeId) as the file identifier:
1. confirmOnView/{wid}/{appId}/{documentPk} — confirm view
2. loadDocument/downloadFile/{wid}/{documentPk} — actual download

Removed fileVisibility step (not needed, was causing 404).
Updated page download links to pass documentPk.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 21:46:37 +02:00
AI Assistant 227c363e13 fix(rgi): correct field mapping + configurable columns + download fix
Mapped all eTerra RGI fields correctly:
- App: appNo, applicationPk, appDate, dueDate, deponent, requester,
  applicationObject, identifiers, statusName, resolutionName, hasSolution
- Docs: docType, documentPk (fileId), documentTypeId (docId),
  fileExtension, digitallySigned, startDate

Features:
- Configurable columns: click "Coloane" to toggle 17 available columns
- Table layout with proper column rendering
- Click row to expand issued documents
- Documents show type name, date, extension, digital signature badge
- Download button with correct fileId/docId mapping
- Filter: hasSolution===1 && dueDate > now (not string matching)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 21:33:10 +02:00
AI Assistant 64f10a63ff fix(rgi): user-friendly page + 3-step download flow
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>
2026-03-24 21:18:32 +02:00
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 3f5eed25f4 fix(geoportal): DROP enrichment views before recreate (column change)
PostgreSQL CREATE OR REPLACE VIEW fails when column structure changes.
Now drops views first, then recreates them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:21:57 +02:00
AI Assistant 0dc5e58b55 fix(geoportal): use subquery instead of JOIN for gis_cladiri_status view
LEFT JOIN caused duplicate rows and column conflicts. Replaced with a
correlated subquery (LIMIT 1) to safely look up BUILD_LEGAL from the
parent parcel's enrichment.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:16:06 +02:00
AI Assistant ba71ca3ef5 feat(parcel-sync): fix click, color styling, UAT boundary cross-check
Click fix:
- Keep l-terenuri-fill visible but transparent (opacity 0) so it still
  catches click events for FeatureInfoPanel. Enrichment overlay renders
  underneath.

Color changes:
- No enrichment: amber/yellow fill (was light green)
- With enrichment: green fill
- Buildings: red fill = no legal docs, blue = legal, gray = unknown
- Parcel outline: red = building no legal, blue = building legal

Boundary cross-check (/api/geoportal/boundary-check?siruta=X):
- Finds "foreign" parcels: registered in other UATs but geometrically
  within this UAT boundary (orange dashed)
- Finds "edge" parcels: registered here but centroid outside boundary
  (purple dashed)
- Alert banner shows count, legend updated with mismatch indicators

Martin config: added gis_cladiri_status source with build_legal property.
Enrichment views: gis_cladiri_status now JOINs parent parcel's BUILD_LEGAL.

Requires: docker restart martin + POST /api/geoportal/setup-enrichment-views

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:05:12 +02:00
AI Assistant d48a2bbf5d refactor(parcel-sync): split 4800-line module into 9 files + Harta tab + enrichment views
Split parcel-sync-module.tsx (4800 lines) into modular files:
- Orchestrator (452 lines): shared state (session, UAT, sync) + tab routing
- Types + helpers, ConnectionPill, 6 tab components (search, layers, export, database, cf, map)

New ParcelSync Harta tab:
- UAT-scoped map: zoom to extent, filter parcels/buildings by siruta
- Data-driven styling via gis_terenuri_status enrichment overlay
  (green=no enrichment, dark green=enriched, blue outline=building, red=no legal docs)
- Reuses Geoportal components (MapViewer, SelectionToolbar, FeatureInfoPanel, BasemapSwitcher)
- Export DXF/GPKG for selection, legend

New PostGIS views (gis_terenuri_status, gis_cladiri_status):
- has_enrichment, has_building, build_legal columns from enrichment JSON
- Auto-created via /api/geoportal/setup-enrichment-views
- Does not modify existing Geoportal views

New API: /api/geoportal/uat-bounds (WGS84 bbox from PostGIS geometry)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 15:02:01 +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 1cc73a3033 fix(geoportal): enrichment now calls proven /api/eterra/search internally
Instead of reimplementing eTerra search logic (which missed most fields),
now calls the existing /api/eterra/search endpoint that already works
perfectly in ParcelSync. Same data, same format: proprietari, CF, CFvechi,
topo, intravilan, categorie, adresa, solicitant.

Per-parcel, 2-5 seconds, persists in DB.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 14:05:07 +02:00
AI Assistant 48fe47d2c0 fix(geoportal): per-parcel enrichment via searchImmovableByIdentifier
Replaces background UAT-wide enrichment with instant per-parcel search.
Uses eTerra searchImmovableByIdentifier (cadastral number lookup) which
returns in 1-3 seconds instead of minutes.

Extracts: NR_CF, proprietari (with shares), intravilan, categorie,
adresa, has_building, build_legal. Persists in DB immediately.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 13:35:03 +02:00
AI Assistant 91034c41ee fix(geoportal): background enrichment using proven enrichFeatures()
Previous single-parcel enrichment wrote empty data (couldn't match in eTerra).
Now uses the original enrichFeatures() which properly fetches owners, CF, etc.

Changes:
- Enrichment runs in BACKGROUND (returns immediately with message)
- Clears bad enrichment data before re-running
- Tracks running enrichments to avoid duplicates
- GET /api/geoportal/enrich?siruta=... checks if enrichment is running
- Panel: hasRealEnrichment checks for CF/PROPRIETARI/CATEGORIE (not just NR_CAD)
- Enrichment button stays visible until real data exists
- Message: "Enrichment pornit in background. Datele vor aparea in 1-3 minute."

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 13:23:44 +02:00
AI Assistant 903dc67ac4 fix: drop views before recreating slim versions (cannot drop columns from view)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 12:43:56 +02:00
AI Assistant 60919122d9 feat(geoportal): one-click optimize-tiles + unified setup banner
New endpoint POST /api/geoportal/optimize-tiles:
- Slims gis_features view (drops attributes, enrichment, timestamps)
- Cascades to gis_terenuri, gis_cladiri, gis_administrativ, gis_documentatii
- Makes vector tiles dramatically smaller

Setup banner now checks 3 optimizations:
1. UAT zoom views (gis_uats_z0/z5/z8/z12)
2. Pre-computed geometry (geom_z0/z5/z8 columns)
3. Slim tile views (no JSON columns)

One "Aplica toate" button runs all pending steps sequentially.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 12:26:08 +02:00
AI Assistant 8ead985c7e perf(geoportal): single-parcel enrichment instead of full UAT
Previous enrichment tried to enrich ALL parcels in a UAT (minutes).
Now enriches just the clicked parcel (seconds):
1. Finds the GisFeature by ID or objectId+siruta
2. Fetches immovable data from eTerra for that specific parcel
3. Persists enrichment in DB
4. Skips if enriched < 7 days ago

Auto-uses env credentials (ETERRA_USERNAME/PASSWORD) — no manual login needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 11:56:04 +02:00
AI Assistant 566d7c4bb1 fix(geoportal): better enrichment error messages + login retry
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 11:25:55 +02:00
AI Assistant 3ffb617970 fix(geoportal): font 404s + slim tile views for performance
- Labels: add text-font ["Noto Sans Regular"] (OpenFreeMap compatible)
- Optimize views: gis_features/terenuri/cladiri/administrativ now exclude
  attributes, enrichment, timestamps (huge JSON columns that made tiles slow)
- Views only include: id, layer_id, siruta, object_id, cadastral_ref,
  area_value, is_active, geom

Run optimize-views again after deploy to apply slimmer views.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 11:23:05 +02:00
AI Assistant 8362e3fd84 fix(geoportal): drop views before adding columns (fixes geometry type conflict)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 11:01:11 +02:00
AI Assistant 53c241c20f perf(geoportal): materialize simplified UAT geometries (fixes 90% CPU on PostgreSQL)
ST_SimplifyPreserveTopology in views runs on every Martin tile request,
causing constant CPU load. Fix: pre-compute simplified geometries into
dedicated columns (geom_z0, geom_z5, geom_z8) on the GisUat table.

POST /api/geoportal/optimize-views:
1. Adds geom_z0/z5/z8 columns to GisUat
2. Backfills with pre-computed simplifications (one-time cost)
3. Creates GiST spatial indexes on each
4. Replaces views to use pre-computed columns (zero CPU reads)
5. Updates trigger to auto-compute on INSERT/UPDATE

Setup banner: now checks optimization status and shows "Optimizeaza"
button if needed. One-click, then docker restart martin.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 10:51:17 +02:00
AI Assistant c4122cea01 feat(geoportal): enrichment API + CF download + bulk enrichment
New API endpoints:
- POST /api/geoportal/enrich — enriches all parcels for a SIRUTA,
  skips already-enriched, persists in GisFeature.enrichment column
- GET /api/geoportal/cf-status?nrCad=... — checks if CF extract exists,
  returns download URL if available

Feature panel:
- No enrichment: "Enrichment" button (triggers eTerra sync for UAT)
- Has enrichment + CF available: "Descarca CF" button (direct download)
- Has enrichment + no CF: "Comanda CF" button (link to ePay tab)
- Copy button always visible
- After enrichment completes, panel auto-reloads data

Selection toolbar:
- Bulk "Enrichment" button for selected parcels (per unique SIRUTA)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 10:48:08 +02:00
AI Assistant 836d60b72f feat(geoportal): one-time setup banner for PostGIS views
- GET /api/geoportal/setup-views checks if zoom views exist
- POST creates them (idempotent)
- SetupBanner component: auto-checks on mount, shows amber banner if
  views missing, button to create them, success message with docker
  restart reminder, auto-hides when everything is ready

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 08:05:22 +02:00
AI Assistant 78625d6415 fix(geoportal): simplified info panel, preserve basemap zoom, DXF export, intravilan outline
- Feature panel: simplified (NR_CAD/NR_CF/SIRUTA/Suprafata/Proprietari),
  aligned top-right under basemap switcher, click empty space to close
- Basemap switch: preserves zoom+center via viewStateRef + moveend listener
- DXF export: use -s_srs + -t_srs (not -a_srs + -t_srs which ogr2ogr rejects)
- Intravilan: double line (black outer + orange inner), z13+, no fill
- Parcel labels: cadastral_ref shown at z16+
- UAT z12: original geometry (no simplification)
- Removed MapLibre popup (only side panel)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 07:43:11 +02:00
AI Assistant 0af3e16a2b feat(geoportal): add /api/geoportal/setup-views endpoint for creating UAT zoom views
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 21:08:14 +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