feat(geoportal): feature panel with Enrichment + Extras CF buttons
- Parcele fara enrichment: buton "Enrichment" (sync magic de la eTerra) - Parcele cu enrichment: date complete + buton "Extras CF" (link ePay) - Buton "Copiaza" (clipboard cu NR_CAD, CF, suprafata, proprietari) - ExportFormat: removed geojson (only dxf + gpkg) - Tooltips pe fiecare buton Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { X, Loader2 } from "lucide-react";
|
import { X, Loader2, Sparkles, FileText, Download } from "lucide-react";
|
||||||
import { Button } from "@/shared/components/ui/button";
|
import { Button } from "@/shared/components/ui/button";
|
||||||
import type { ClickedFeature, FeatureDetail, FeatureEnrichmentData } from "../types";
|
import type { ClickedFeature, FeatureDetail, FeatureEnrichmentData } from "../types";
|
||||||
|
|
||||||
@@ -13,6 +13,8 @@ type FeatureInfoPanelProps = {
|
|||||||
export function FeatureInfoPanel({ feature, onClose }: FeatureInfoPanelProps) {
|
export function FeatureInfoPanel({ feature, onClose }: FeatureInfoPanelProps) {
|
||||||
const [detail, setDetail] = useState<FeatureDetail | null>(null);
|
const [detail, setDetail] = useState<FeatureDetail | null>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [enriching, setEnriching] = useState(false);
|
||||||
|
const [enrichMsg, setEnrichMsg] = useState("");
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!feature) { setDetail(null); return; }
|
if (!feature) { setDetail(null); return; }
|
||||||
@@ -23,6 +25,7 @@ export function FeatureInfoPanel({ feature, onClose }: FeatureInfoPanelProps) {
|
|||||||
|
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
setEnrichMsg("");
|
||||||
|
|
||||||
fetch(`/api/geoportal/feature?objectId=${objectId}&siruta=${siruta}&sourceLayer=${feature.sourceLayer}`)
|
fetch(`/api/geoportal/feature?objectId=${objectId}&siruta=${siruta}&sourceLayer=${feature.sourceLayer}`)
|
||||||
.then((r) => r.ok ? r.json() : Promise.reject())
|
.then((r) => r.ok ? r.json() : Promise.reject())
|
||||||
@@ -37,10 +40,36 @@ export function FeatureInfoPanel({ feature, onClose }: FeatureInfoPanelProps) {
|
|||||||
|
|
||||||
const e = detail?.enrichment as FeatureEnrichmentData | null | undefined;
|
const e = detail?.enrichment as FeatureEnrichmentData | null | undefined;
|
||||||
const isUat = feature.sourceLayer?.includes("uat");
|
const isUat = feature.sourceLayer?.includes("uat");
|
||||||
const cadRef = e?.NR_CAD ?? feature.properties.cadastral_ref ?? "";
|
const cadRef = e?.NR_CAD ?? String(feature.properties.cadastral_ref ?? "");
|
||||||
const title = isUat
|
const title = isUat
|
||||||
? String(feature.properties.name ?? "UAT")
|
? String(feature.properties.name ?? "UAT")
|
||||||
: cadRef ? `Parcela ${cadRef}` : `#${feature.properties.object_id ?? "?"}`;
|
: cadRef ? `Parcela ${cadRef}` : `#${feature.properties.object_id ?? "?"}`;
|
||||||
|
const hasEnrichment = !!e && !!e.NR_CAD;
|
||||||
|
const nrCf = e?.NR_CF ?? "";
|
||||||
|
|
||||||
|
const handleEnrich = async () => {
|
||||||
|
if (!detail?.siruta || !detail?.objectId) return;
|
||||||
|
setEnriching(true);
|
||||||
|
setEnrichMsg("");
|
||||||
|
try {
|
||||||
|
// Trigger enrichment for this specific parcel's UAT
|
||||||
|
const resp = await fetch("/api/eterra/enrich", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ siruta: detail.siruta, featureId: detail.id }),
|
||||||
|
});
|
||||||
|
if (resp.ok) {
|
||||||
|
setEnrichMsg("Enrichment pornit. Reincarca pagina dupa cateva secunde.");
|
||||||
|
} else {
|
||||||
|
const d = await resp.json().catch(() => null);
|
||||||
|
setEnrichMsg((d && typeof d === "object" && "error" in d) ? String((d as { error: string }).error) : "Eroare la enrichment");
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
setEnrichMsg("Eroare retea");
|
||||||
|
} finally {
|
||||||
|
setEnriching(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
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-72 overflow-hidden">
|
||||||
@@ -70,19 +99,78 @@ export function FeatureInfoPanel({ feature, onClose }: FeatureInfoPanelProps) {
|
|||||||
|
|
||||||
{!loading && !isUat && (
|
{!loading && !isUat && (
|
||||||
<>
|
<>
|
||||||
<Row label="UAT" value={feature.properties.siruta} />
|
|
||||||
<Row label="SIRUTA" value={feature.properties.siruta} />
|
<Row label="SIRUTA" value={feature.properties.siruta} />
|
||||||
<Row label="Nr. cadastral" value={e?.NR_CAD ?? cadRef} />
|
<Row label="Nr. cadastral" value={e?.NR_CAD ?? cadRef} />
|
||||||
<Row label="Nr. CF" value={e?.NR_CF} />
|
<Row label="Nr. CF" value={nrCf} />
|
||||||
<Row label="Suprafata" value={formatArea(e?.SUPRAFATA_2D ?? feature.properties.area_value)} />
|
<Row label="Suprafata" value={formatArea(e?.SUPRAFATA_2D ?? feature.properties.area_value)} />
|
||||||
{e?.PROPRIETARI && e.PROPRIETARI !== "-" && (
|
|
||||||
<Row label="Proprietari" value={e.PROPRIETARI} />
|
{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} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
{e?.INTRAVILAN && e.INTRAVILAN !== "-" && (
|
|
||||||
<Row label="Intravilan" value={e.INTRAVILAN} />
|
{/* Action buttons */}
|
||||||
)}
|
<div className="flex gap-1.5 pt-2 border-t mt-2">
|
||||||
{e?.CATEGORIE_FOLOSINTA && e.CATEGORIE_FOLOSINTA !== "-" && (
|
{!hasEnrichment && (
|
||||||
<Row label="Categorie" value={e.CATEGORIE_FOLOSINTA} />
|
<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 folosinta) de la eTerra"
|
||||||
|
>
|
||||||
|
{enriching ? <Loader2 className="h-3 w-3 animate-spin" /> : <Sparkles className="h-3 w-3" />}
|
||||||
|
Enrichment
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hasEnrichment && nrCf && (
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="h-7 text-xs gap-1 flex-1"
|
||||||
|
onClick={() => {
|
||||||
|
window.open(`/parcel-sync?tab=extrase&search=${encodeURIComponent(cadRef)}`, "_blank");
|
||||||
|
}}
|
||||||
|
title="Comanda sau descarca extras CF de la ANCPI ePay"
|
||||||
|
>
|
||||||
|
<FileText className="h-3 w-3" />
|
||||||
|
Extras CF
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="h-7 text-xs gap-1 flex-1"
|
||||||
|
onClick={() => {
|
||||||
|
const text = [
|
||||||
|
cadRef && `Nr. cad: ${cadRef}`,
|
||||||
|
nrCf && `CF: ${nrCf}`,
|
||||||
|
e?.SUPRAFATA_2D && `S: ${e.SUPRAFATA_2D} mp`,
|
||||||
|
e?.PROPRIETARI && e.PROPRIETARI !== "-" && `Prop: ${e.PROPRIETARI}`,
|
||||||
|
].filter(Boolean).join("\n");
|
||||||
|
navigator.clipboard.writeText(text);
|
||||||
|
}}
|
||||||
|
title="Copiaza informatiile in clipboard"
|
||||||
|
>
|
||||||
|
<Download className="h-3 w-3" />
|
||||||
|
Copiaza
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{enrichMsg && (
|
||||||
|
<p className="text-xs text-muted-foreground mt-1">{enrichMsg}</p>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -117,4 +117,4 @@ export type FeatureEnrichmentData = {
|
|||||||
/* Export */
|
/* Export */
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
export type ExportFormat = "dxf" | "gpkg" | "geojson";
|
export type ExportFormat = "dxf" | "gpkg";
|
||||||
|
|||||||
Reference in New Issue
Block a user