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>
- 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>
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>
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>
Rectangle mode (Dreptunghi):
- Mousedown starts drawing, mousemove shows amber overlay, mouseup selects
- All terenuri features in the drawn bbox are added to selection
- Map panning disabled during draw, re-enabled after
- Minimum 5px size to prevent accidental micro-selections
Freehand mode (Desen):
- Each click adds a point, polygon drawn with GeoJSON source
- Double-click closes polygon, selects features whose centroid is inside
- Ray-casting point-in-polygon algorithm for spatial filtering
- Double-click zoom disabled during freehand mode
Draw state clears when switching selection modes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 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>
- 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>
Martin v0.15.0 serves tiles at /{source}/{z}/{x}/{y} without .pbf
extension. Requests with .pbf returned 404.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Basemap switching now recreates the map (basemap in useEffect deps)
instead of buggy remove/re-add that swallowed errors
- OpenTopoMap maxzoom set to 17 (was requesting z19 → 400 errors)
- Basemap maxzoom applied to both source and map maxZoom
Note: Martin vector tiles return 404 — PostGIS views or Martin container
need to be checked on the server.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
MapLibre sets position:relative on its container element, which overrides
our absolute inset-0 and causes height to collapse to 0. Using w-full h-full
instead works because the parent (absolute inset-0) has a definite computed
height from positioning.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
h-full (height:100%) doesn't propagate through absolutely positioned
parents. The MapLibre container had width=1065 but height=0. Using
absolute inset-0 on the wrapper fills the parent directly via positioning
instead of percentage height.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
height: 100% doesn't work when the parent gets its height from flex-1
(flexbox-computed, not explicit height). Fixed by adding position:relative
to main in fullscreen mode and using absolute inset-0 on the geoportal
container.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add /geoportal to FULLSCREEN_ROUTES (overflow-hidden, no padding)
- Copy maplibre-gl.css to public/ and load from same origin (avoids CDN/CSP)
- Simplify layout: fill parent via h-full w-full (no negative margin hack)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Static CSS import doesn't work with next/dynamic + standalone output.
Now injects a <link> tag to unpkg CDN at module load time (bulletproof).
- Geoportal is now fullscreen: map fills entire viewport below the header,
no duplicate title/description, negative margins bleed to edges.
- Removed page-level CSS imports (no longer needed).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Major geoportal enhancements:
- Basemap switcher (OSM/Satellite/Terrain) with ESRI + OpenTopoMap tiles
- Search bar with debounced lookup (UATs by name, parcels by cadastral ref, owners by name)
- Feature info panel showing enrichment data from ParcelSync (cadastru, proprietari, suprafata, folosinta)
- Parcel selection mode with amber highlight + export (GeoJSON/DXF/GPKG via ogr2ogr)
- Next.js /tiles rewrite proxying to Martin (fixes dev + avoids mixed content)
- Fixed MapLibre web worker relative URL resolution (window.location.origin)
API routes: /api/geoportal/search, /api/geoportal/feature, /api/geoportal/export
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
React's useDeferredValue lets the input update immediately while
deferring the expensive filter (3186 items) to a lower priority.
Removes the setTimeout debounce in favor of React's built-in
concurrent rendering scheduler. Input stays responsive.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Checkbox on each row (ordered selection → numbered files in ZIP)
- "Descarcă selecție (N)" button appears when items selected
- Tooltip shows position in ZIP: "#1 in ZIP", "#2 in ZIP"
- Select-all checkbox in header
- Tooltips on Descarcă tot + Descarcă selecție buttons
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The nested JSON in ePay HTML breaks [^}]* regex. New approach:
find all CF.stringValues independently, find all solutii independently,
then zip them by position (they appear in same order in HTML).
This correctly maps CF number → document for batch orders.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Re-download: all 7 orders re-downloaded using documentsByCadastral
for correct CF→document matching. No more hardcoded order→parcel map.
Tooltips on all CF extract UI elements:
- No extract: "Comandă extras CF (1 credit)"
- Valid: "Valid până la DD.MM.YYYY" + "Descarcă extras CF"
- Expired: "Expirat pe DD.MM.YYYY" + "Comandă extras CF nou (1 credit)"
- Processing: "Comanda în curs de procesare"
Animations: Loader2 spinner while ordering, transition to green check.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Critical fix: batch order documents are now matched by CF number
from parsed metadateCereri (documentsByCadastral), not by index.
Prevents PDF content mismatch when ePay returns docs in different order.
UAT search: name matches shown first, county-only matches after.
Typing "cluj" now shows CLUJ-NAPOCA before county "Cluj" matches.
Cleaned MinIO + DB of incorrectly mapped old test data.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Major rewrite:
- Queue now processes batches: addToCart×N → saveMetadata×N → ONE
submitOrder → poll → download ALL documents → store in MinIO
- Removed unique constraint on orderId (shared across batch items)
- Added step=download to test endpoint: downloads PDFs from 5
existing orders (9685480-9685484) and stores in MinIO
- step=order now uses enqueueBatch for 2 test parcels (61904, 309952)
as ONE ePay order instead of separate orders
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
EditCartSubmit returns 200 (not redirect) — Angular does client-side
redirect to CheckoutConfirmationSubmit.action. Added this step to
actually confirm the order before looking for the orderId.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
submitOrder now captures the previous orderId BEFORE submitting, then
searches for a NEW orderId that isn't in the knownOrderIds set. Queue
passes knownOrderIds between sequential items to prevent duplicate
orderId assignment (unique constraint violation).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Angular uses doPostAsFormMultipart — the save endpoint requires
multipart/form-data, not application/x-www-form-urlencoded.
Install form-data package and restore multipart upload.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
form-data package not installed — crashes at runtime. Use
URLSearchParams instead (the Angular source uses doPostAsForm
which is form-urlencoded, so this should work too).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Zero discovery calls needed! ePay internal county IDs are identical
to eTerra WORKSPACE_IDs (CLUJ=127, ALBA=10, etc.) and ePay UAT IDs
are SIRUTA codes (Cluj-Napoca=54975, Florești=57706). Queue now
uses workspacePk + siruta directly from GisUat DB.
Flow: AddToCart → saveMetadata → EditCartSubmit → Poll+Download.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>