From 3da45a4cabb1b7bef4c86c5e5355b335da2627e0 Mon Sep 17 00:00:00 2001 From: AI Assistant Date: Tue, 24 Mar 2026 18:04:09 +0200 Subject: [PATCH] feat(parcel-sync): sync button on empty Harta tab + intravilan in base sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- src/app/api/eterra/sync-background/route.ts | 15 ++++ .../parcel-sync/components/epay-tab.tsx | 6 +- .../components/parcel-sync-module.tsx | 8 +- .../components/tabs/export-tab.tsx | 8 +- .../components/tabs/layers-tab.tsx | 2 +- .../parcel-sync/components/tabs/map-tab.tsx | 73 ++++++++++++++++++- 6 files changed, 101 insertions(+), 11 deletions(-) diff --git a/src/app/api/eterra/sync-background/route.ts b/src/app/api/eterra/sync-background/route.ts index 8940377..f340ee1 100644 --- a/src/app/api/eterra/sync-background/route.ts +++ b/src/app/api/eterra/sync-background/route.ts @@ -221,6 +221,21 @@ async function runBackground(params: { throw new Error(r.error ?? "Sync clădiri failed"); } + // Sync intravilan limits (always, lightweight layer) + phase = "Sincronizare limite intravilan"; + push({}); + try { + await syncLayer(username, password, siruta, "LIMITE_INTRAV_DYNAMIC", { + forceFullSync: forceSync, + jobId, + isSubStep: true, + }); + } catch { + // Non-critical — don't fail the whole job + note = "Avertisment: limite intravilan nu s-au sincronizat"; + push({}); + } + if (!terenuriNeedsSync && !cladiriNeedsSync) { note = "Date proaspete — sync skip"; } diff --git a/src/modules/parcel-sync/components/epay-tab.tsx b/src/modules/parcel-sync/components/epay-tab.tsx index adf52f5..03450b2 100644 --- a/src/modules/parcel-sync/components/epay-tab.tsx +++ b/src/modules/parcel-sync/components/epay-tab.tsx @@ -60,7 +60,7 @@ type GisUatResult = { /* ------------------------------------------------------------------ */ function formatDate(iso?: string | null) { - if (!iso) return "\u2014"; + if (!iso) return "—"; return new Date(iso).toLocaleDateString("ro-RO", { day: "2-digit", month: "2-digit", @@ -71,7 +71,7 @@ function formatDate(iso?: string | null) { } function formatShortDate(iso?: string | null) { - if (!iso) return "\u2014"; + if (!iso) return "—"; return new Date(iso).toLocaleDateString("ro-RO", { day: "2-digit", month: "2-digit", @@ -715,7 +715,7 @@ export function EpayTab() { {formatShortDate(order.expiresAt)} ) : ( - {"\u2014"} + {"—"} )} diff --git a/src/modules/parcel-sync/components/parcel-sync-module.tsx b/src/modules/parcel-sync/components/parcel-sync-module.tsx index d9ca7d3..61a56e3 100644 --- a/src/modules/parcel-sync/components/parcel-sync-module.tsx +++ b/src/modules/parcel-sync/components/parcel-sync-module.tsx @@ -445,7 +445,13 @@ export function ParcelSyncModule() { - + s + c, 0)} + onSyncRefresh={() => void fetchSyncStatus()} + /> ); diff --git a/src/modules/parcel-sync/components/tabs/export-tab.tsx b/src/modules/parcel-sync/components/tabs/export-tab.tsx index 186baec..4e9fc69 100644 --- a/src/modules/parcel-sync/components/tabs/export-tab.tsx +++ b/src/modules/parcel-sync/components/tabs/export-tab.tsx @@ -1189,7 +1189,7 @@ export function ExportTab({ Sync fundal — Bază
- Terenuri + clădiri \u2192 salvează în DB + Terenuri + clădiri → salvează în DB
@@ -1215,7 +1215,7 @@ export function ExportTab({ Sync fundal — Magic
- Sync + îmbogățire \u2192 salvează în DB + Sync + îmbogățire → salvează în DB
@@ -1311,7 +1311,7 @@ export function ExportTab({
{bgPhaseTrail.map((p, i) => ( - {i > 0 && \u2192} + {i > 0 && } {phaseTrail.map((p, i) => ( - {i > 0 && \u2192} + {i > 0 && }
  1. - QGIS \u2192 Layer \u2192 Add Layer \u2192 Add PostGIS Layers + QGIS → Layer → Add Layer → Add PostGIS Layers
  2. New connection:
diff --git a/src/modules/parcel-sync/components/tabs/map-tab.tsx b/src/modules/parcel-sync/components/tabs/map-tab.tsx index 7da0b88..4d6025b 100644 --- a/src/modules/parcel-sync/components/tabs/map-tab.tsx +++ b/src/modules/parcel-sync/components/tabs/map-tab.tsx @@ -2,7 +2,8 @@ import { useState, useRef, useCallback, useEffect } from "react"; import dynamic from "next/dynamic"; -import { Map as MapIcon, Loader2, AlertTriangle } from "lucide-react"; +import { Map as MapIcon, Loader2, AlertTriangle, RefreshCw } from "lucide-react"; +import { Button } from "@/shared/components/ui/button"; import { Card, CardContent } from "@/shared/components/ui/card"; import { Badge } from "@/shared/components/ui/badge"; import { BasemapSwitcher } from "@/modules/geoportal/components/basemap-switcher"; @@ -54,6 +55,9 @@ const BASE_LAYERS = [ type MapTabProps = { siruta: string; sirutaValid: boolean; + sessionConnected: boolean; + syncLocalCount: number; // total local features for this UAT + onSyncRefresh: () => void; }; /* ------------------------------------------------------------------ */ @@ -84,7 +88,7 @@ function asMap(handle: MapViewerHandle | null): MapLike | null { /* Component */ /* ------------------------------------------------------------------ */ -export function MapTab({ siruta, sirutaValid }: MapTabProps) { +export function MapTab({ siruta, sirutaValid, sessionConnected, syncLocalCount, onSyncRefresh }: MapTabProps) { const mapHandleRef = useRef(null); const [basemap, setBasemap] = useState("liberty"); const [clickedFeature, setClickedFeature] = @@ -104,6 +108,10 @@ export function MapTab({ siruta, sirutaValid }: MapTabProps) { edge: number; } | null>(null); + /* Quick sync state */ + const [syncing, setSyncing] = useState(false); + const [syncMsg, setSyncMsg] = useState(""); + /* Layer visibility: show terenuri + cladiri, hide admin */ const [layerVisibility] = useState({ terenuri: true, @@ -419,6 +427,30 @@ export function MapTab({ siruta, sirutaValid }: MapTabProps) { setSelectionMode(mode); }, []); + /* ── Quick sync (terenuri + cladiri + intravilan) ──────────── */ + const handleQuickSync = useCallback(async () => { + if (!siruta || syncing || !sessionConnected) return; + setSyncing(true); + setSyncMsg("Se sincronizează..."); + try { + const res = await fetch("/api/eterra/sync-background", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ siruta, mode: "base" }), + }); + const data = (await res.json()) as { jobId?: string; error?: string }; + if (data.error) { + setSyncMsg(`Eroare: ${data.error}`); + } else { + setSyncMsg("Sincronizare pornită în fundal. Revino în câteva minute."); + onSyncRefresh(); + } + } catch { + setSyncMsg("Eroare rețea"); + } + setSyncing(false); + }, [siruta, syncing, sessionConnected, onSyncRefresh]); + /* ── Render ─────────────────────────────────────────────────── */ if (!sirutaValid) { @@ -432,6 +464,43 @@ export function MapTab({ siruta, sirutaValid }: MapTabProps) { ); } + /* No data for this UAT — show sync button */ + if (syncLocalCount === 0) { + return ( + + + +

+ Nu există date locale pentru acest UAT. +

+ {sessionConnected ? ( +
+ + {syncMsg && ( +

{syncMsg}

+ )} +
+ ) : ( +

+ Conectează-te la eTerra pentru a sincroniza date. +

+ )} +
+
+ ); + } + return (
{/* Boundary mismatch alert */}