fix(geoportal): all layers OFF by default + full enrichment display
Layers: - ALL layers OFF by default (just basemap on load) - User activates what they need Feature panel: - Shows ALL enrichment fields: proprietari (full text, wrapping), CF vechi, nr topo, adresa, solicitant, intravilan, categorie - Building info with icon (cu acte / fara acte warning) - hasEnrichment check relaxed (any non-empty field counts) - Panel scrollable (max-h 60vh) for long data - WrapRow for multi-line text (proprietari, adresa) - Enrichment button visible when no enrichment data - Enrichment auto-updates panel on success Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { X, Loader2, Sparkles, FileDown, Download, ClipboardCopy } from "lucide-react";
|
||||
import { X, Loader2, Sparkles, FileDown, Download, ClipboardCopy, Building2, AlertTriangle } from "lucide-react";
|
||||
import { Button } from "@/shared/components/ui/button";
|
||||
import type { ClickedFeature, FeatureDetail, FeatureEnrichmentData } from "../types";
|
||||
|
||||
@@ -19,10 +19,8 @@ export function FeatureInfoPanel({ feature, onClose }: FeatureInfoPanelProps) {
|
||||
const [enrichMsg, setEnrichMsg] = useState("");
|
||||
const [cfStatus, setCfStatus] = useState<CfStatus | null>(null);
|
||||
|
||||
// Fetch feature detail
|
||||
useEffect(() => {
|
||||
if (!feature) { setDetail(null); setCfStatus(null); return; }
|
||||
|
||||
const objectId = feature.properties.object_id ?? feature.properties.objectId;
|
||||
const siruta = feature.properties.siruta;
|
||||
if (!objectId || !siruta) { setDetail(null); return; }
|
||||
@@ -37,7 +35,6 @@ export function FeatureInfoPanel({ feature, onClose }: FeatureInfoPanelProps) {
|
||||
.then((data: { feature: FeatureDetail }) => {
|
||||
if (cancelled) return;
|
||||
setDetail(data.feature);
|
||||
// Check CF status if we have a cadastral ref
|
||||
const e = data.feature.enrichment as FeatureEnrichmentData | null;
|
||||
const nrCad = e?.NR_CAD ?? data.feature.cadastralRef;
|
||||
if (nrCad) {
|
||||
@@ -61,7 +58,9 @@ export function FeatureInfoPanel({ feature, onClose }: FeatureInfoPanelProps) {
|
||||
const title = isUat
|
||||
? String(feature.properties.name ?? "UAT")
|
||||
: cadRef ? `Parcela ${cadRef}` : `#${feature.properties.object_id ?? "?"}`;
|
||||
const hasEnrichment = !!e && !!e.NR_CAD;
|
||||
|
||||
// Has ANY enrichment data (not just NR_CAD)
|
||||
const hasEnrichment = !!e && Object.values(e).some((v) => v != null && v !== "" && v !== "-" && v !== 0);
|
||||
const siruta = String(feature.properties.siruta ?? detail?.siruta ?? "");
|
||||
|
||||
const handleEnrich = async () => {
|
||||
@@ -77,21 +76,16 @@ export function FeatureInfoPanel({ feature, onClose }: FeatureInfoPanelProps) {
|
||||
});
|
||||
const d = await resp.json();
|
||||
if (resp.ok) {
|
||||
// Update detail with enrichment data directly from response
|
||||
if (d.enrichment && detail) {
|
||||
setDetail({ ...detail, enrichment: d.enrichment, enrichedAt: new Date().toISOString() });
|
||||
setEnrichMsg("");
|
||||
} else {
|
||||
// Fallback: reload from API
|
||||
// Reload from API
|
||||
const oid = feature.properties.object_id ?? feature.properties.objectId;
|
||||
if (oid) {
|
||||
const r = await fetch(`/api/geoportal/feature?objectId=${oid}&siruta=${siruta}&sourceLayer=${feature.sourceLayer}`);
|
||||
if (r.ok) {
|
||||
const data = await r.json();
|
||||
setDetail(data.feature);
|
||||
}
|
||||
}
|
||||
const r = await fetch(`/api/geoportal/feature?objectId=${oid}&siruta=${siruta}&sourceLayer=${feature.sourceLayer}`);
|
||||
if (r.ok) { const data = await r.json(); setDetail(data.feature); }
|
||||
setEnrichMsg("");
|
||||
}
|
||||
setEnrichMsg("");
|
||||
} else {
|
||||
setEnrichMsg(d.error ?? "Eroare la enrichment");
|
||||
}
|
||||
@@ -102,28 +96,17 @@ export function FeatureInfoPanel({ feature, onClose }: FeatureInfoPanelProps) {
|
||||
}
|
||||
};
|
||||
|
||||
const handleCopy = () => {
|
||||
const text = [
|
||||
cadRef && `Nr. cad: ${cadRef}`,
|
||||
e?.NR_CF && `CF: ${e.NR_CF}`,
|
||||
(e?.SUPRAFATA_2D ?? feature.properties.area_value) && `S: ${e?.SUPRAFATA_2D ?? feature.properties.area_value} mp`,
|
||||
e?.PROPRIETARI && e.PROPRIETARI !== "-" && `Prop: ${e.PROPRIETARI}`,
|
||||
siruta && `SIRUTA: ${siruta}`,
|
||||
].filter(Boolean).join("\n");
|
||||
navigator.clipboard.writeText(text);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="bg-background/95 backdrop-blur-sm border rounded-lg shadow-lg w-72 overflow-hidden">
|
||||
<div className="bg-background/95 backdrop-blur-sm border rounded-lg shadow-lg w-80 max-h-[60vh] overflow-y-auto">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-3 py-2 border-b">
|
||||
<div className="flex items-center justify-between px-3 py-2 border-b sticky top-0 bg-background/95 backdrop-blur-sm">
|
||||
<h3 className="text-sm font-semibold truncate">{title}</h3>
|
||||
<Button variant="ghost" size="sm" className="h-6 w-6 p-0 shrink-0 ml-2" onClick={onClose}>
|
||||
<X className="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="px-3 py-2 text-xs space-y-1">
|
||||
<div className="px-3 py-2 text-xs space-y-1.5">
|
||||
{loading && (
|
||||
<div className="flex items-center gap-2 text-muted-foreground py-2 justify-center">
|
||||
<Loader2 className="h-3.5 w-3.5 animate-spin" /> Se incarca...
|
||||
@@ -140,16 +123,39 @@ export function FeatureInfoPanel({ feature, onClose }: FeatureInfoPanelProps) {
|
||||
|
||||
{!loading && !isUat && (
|
||||
<>
|
||||
{/* Basic info */}
|
||||
<Row label="SIRUTA" value={siruta} />
|
||||
<Row label="Nr. cadastral" value={e?.NR_CAD ?? cadRef} />
|
||||
<Row label="Nr. CF" value={e?.NR_CF} />
|
||||
<Row label="CF vechi" value={e?.NR_CF_VECHI} />
|
||||
<Row label="Nr. topo" value={e?.NR_TOPO} />
|
||||
<Row label="Suprafata" value={formatArea(e?.SUPRAFATA_2D ?? feature.properties.area_value)} />
|
||||
|
||||
{/* Enrichment data */}
|
||||
{hasEnrichment && (
|
||||
<>
|
||||
{e?.PROPRIETARI && e.PROPRIETARI !== "-" && <Row label="Proprietari" value={e.PROPRIETARI} />}
|
||||
{e?.INTRAVILAN && e.INTRAVILAN !== "-" && <Row label="Intravilan" value={e.INTRAVILAN} />}
|
||||
{e?.CATEGORIE_FOLOSINTA && e.CATEGORIE_FOLOSINTA !== "-" && <Row label="Categorie" value={e.CATEGORIE_FOLOSINTA} />}
|
||||
<div className="border-t pt-1.5 mt-1.5" />
|
||||
<WrapRow label="Proprietari" value={e?.PROPRIETARI} />
|
||||
<WrapRow label="Proprietari vechi" value={e?.PROPRIETARI_VECHI} />
|
||||
<Row label="Intravilan" value={e?.INTRAVILAN} />
|
||||
<Row label="Categorie" value={e?.CATEGORIE_FOLOSINTA} />
|
||||
<WrapRow label="Adresa" value={e?.ADRESA} />
|
||||
<Row label="Solicitant" value={e?.SOLICITANT} />
|
||||
|
||||
{/* Building info */}
|
||||
{(e?.HAS_BUILDING === 1) && (
|
||||
<div className="flex items-center gap-1.5 pt-1">
|
||||
<Building2 className="h-3 w-3 text-blue-500" />
|
||||
<span className="text-muted-foreground">Constructie:</span>
|
||||
<span className="font-medium">
|
||||
{e?.BUILD_LEGAL === 1 ? "Cu acte" : (
|
||||
<span className="flex items-center gap-1 text-amber-600">
|
||||
<AlertTriangle className="h-3 w-3" /> Fara acte
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -159,10 +165,10 @@ export function FeatureInfoPanel({ feature, onClose }: FeatureInfoPanelProps) {
|
||||
<Button
|
||||
variant="outline" size="sm" className="h-7 text-xs gap-1 flex-1"
|
||||
onClick={handleEnrich} disabled={enriching}
|
||||
title="Obtine date detaliate (proprietari, CF, categorie) de la eTerra. Datele se salveaza permanent in baza de date."
|
||||
title="Obtine date detaliate de la eTerra (proprietari, CF, categorie). Se salveaza permanent."
|
||||
>
|
||||
{enriching ? <Loader2 className="h-3 w-3 animate-spin" /> : <Sparkles className="h-3 w-3" />}
|
||||
Enrichment
|
||||
{enriching ? "Se incarca..." : "Enrichment"}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -188,14 +194,24 @@ export function FeatureInfoPanel({ feature, onClose }: FeatureInfoPanelProps) {
|
||||
|
||||
<Button
|
||||
variant="outline" size="sm" className="h-7 text-xs gap-1"
|
||||
onClick={handleCopy} title="Copiaza informatiile in clipboard"
|
||||
onClick={() => {
|
||||
const text = [
|
||||
cadRef && `Nr. cad: ${cadRef}`,
|
||||
e?.NR_CF && `CF: ${e.NR_CF}`,
|
||||
(e?.SUPRAFATA_2D ?? feature.properties.area_value) && `S: ${e?.SUPRAFATA_2D ?? feature.properties.area_value} mp`,
|
||||
e?.PROPRIETARI && e.PROPRIETARI !== "-" && `Prop: ${e.PROPRIETARI}`,
|
||||
siruta && `SIRUTA: ${siruta}`,
|
||||
].filter(Boolean).join("\n");
|
||||
navigator.clipboard.writeText(text);
|
||||
}}
|
||||
title="Copiaza informatiile in clipboard"
|
||||
>
|
||||
<ClipboardCopy className="h-3 w-3" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{enrichMsg && (
|
||||
<p className="text-xs text-muted-foreground mt-1">{enrichMsg}</p>
|
||||
<p className="text-xs text-destructive mt-1">{enrichMsg}</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
@@ -204,12 +220,24 @@ export function FeatureInfoPanel({ feature, onClose }: FeatureInfoPanelProps) {
|
||||
);
|
||||
}
|
||||
|
||||
/** Single-line row (value truncated only if very long) */
|
||||
function Row({ label, value }: { label: string; value: unknown }) {
|
||||
if (!value || value === "-" || value === "") return null;
|
||||
if (!value || value === "-" || value === "" || value === 0) return null;
|
||||
return (
|
||||
<div className="flex justify-between gap-2">
|
||||
<span className="text-muted-foreground shrink-0">{label}</span>
|
||||
<span className="text-right font-medium truncate">{String(value)}</span>
|
||||
<span className="text-right font-medium break-words max-w-[60%]">{String(value)}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** Multi-line row (long text wraps) */
|
||||
function WrapRow({ label, value }: { label: string; value: unknown }) {
|
||||
if (!value || value === "-" || value === "") return null;
|
||||
return (
|
||||
<div>
|
||||
<span className="text-muted-foreground">{label}</span>
|
||||
<p className="font-medium text-xs leading-relaxed mt-0.5">{String(value)}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,14 +26,14 @@ const LAYER_GROUPS: LayerGroupDef[] = [
|
||||
label: "Terenuri",
|
||||
description: "Parcele cadastrale (zoom >= 13)",
|
||||
color: "#22c55e",
|
||||
defaultVisible: true,
|
||||
defaultVisible: false,
|
||||
},
|
||||
{
|
||||
id: "cladiri",
|
||||
label: "Cladiri",
|
||||
description: "Constructii (zoom >= 14)",
|
||||
color: "#3b82f6",
|
||||
defaultVisible: true,
|
||||
defaultVisible: false,
|
||||
},
|
||||
{
|
||||
id: "uats",
|
||||
|
||||
Reference in New Issue
Block a user