feat(parcel-sync): county-aware UAT autocomplete with workspace resolution
- New /api/eterra/uats endpoint fetches all counties + UATs from eTerra, caches server-side for 1 hour, returns enriched data with county name and workspacePk for each UAT - When eTerra is connected, auto-fetches enriched UAT list (replaces static uat.json fallback) shows 'FELEACU (57582), CLUJ' format - UAT autocomplete now searches both UAT name and county name - Selected UAT stores workspacePk in state, passes it directly to /api/eterra/search eliminates slow per-search county resolution - Search route accepts optional workspacePk, falls back to resolveWorkspace() - Dropdown shows UAT name, SIRUTA code, and county prominently - Increased autocomplete results from 8 to 12 items
This commit is contained in:
@@ -7,9 +7,10 @@ export const dynamic = "force-dynamic";
|
|||||||
|
|
||||||
type Body = {
|
type Body = {
|
||||||
siruta?: string;
|
siruta?: string;
|
||||||
search?: string; // cadastral number(s), comma or newline separated
|
search?: string; // cadastral number(s), comma or newline separated
|
||||||
username?: string;
|
username?: string;
|
||||||
password?: string;
|
password?: string;
|
||||||
|
workspacePk?: number; // county workspace PK — if provided, skips resolution
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
@@ -81,7 +82,11 @@ function formatAddress(item?: any) {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
function normalizeIntravilan(values: string[]) {
|
function normalizeIntravilan(values: string[]) {
|
||||||
const normalized = values
|
const normalized = values
|
||||||
.map((v) => String(v ?? "").trim().toLowerCase())
|
.map((v) =>
|
||||||
|
String(v ?? "")
|
||||||
|
.trim()
|
||||||
|
.toLowerCase(),
|
||||||
|
)
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
const unique = new Set(normalized);
|
const unique = new Set(normalized);
|
||||||
if (!unique.size) return "";
|
if (!unique.size) return "";
|
||||||
@@ -182,8 +187,11 @@ export async function POST(req: Request) {
|
|||||||
|
|
||||||
const client = await EterraClient.create(username, password);
|
const client = await EterraClient.create(username, password);
|
||||||
|
|
||||||
// Resolve workspace (county) for this SIRUTA
|
// Use provided workspacePk — or fall back to resolution
|
||||||
const workspaceId = await resolveWorkspace(client, siruta);
|
let workspaceId = body.workspacePk ?? null;
|
||||||
|
if (!workspaceId || !Number.isFinite(workspaceId)) {
|
||||||
|
workspaceId = await resolveWorkspace(client, siruta);
|
||||||
|
}
|
||||||
if (!workspaceId) {
|
if (!workspaceId) {
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: "Nu s-a putut determina județul pentru UAT-ul selectat." },
|
{ error: "Nu s-a putut determina județul pentru UAT-ul selectat." },
|
||||||
@@ -228,9 +236,7 @@ export async function POST(req: Request) {
|
|||||||
// Basic data from immovable list
|
// Basic data from immovable list
|
||||||
let nrCF = String(item?.paperLbNo ?? item?.paperCadNo ?? "");
|
let nrCF = String(item?.paperLbNo ?? item?.paperCadNo ?? "");
|
||||||
let nrCFVechi = "";
|
let nrCFVechi = "";
|
||||||
let nrTopo = String(
|
let nrTopo = String(item?.topNo ?? item?.paperCadNo ?? "");
|
||||||
item?.topNo ?? item?.paperCadNo ?? "",
|
|
||||||
);
|
|
||||||
let addressText = formatAddress(item);
|
let addressText = formatAddress(item);
|
||||||
let proprietari = "";
|
let proprietari = "";
|
||||||
let solicitant = "";
|
let solicitant = "";
|
||||||
@@ -298,16 +304,16 @@ export async function POST(req: Request) {
|
|||||||
// Pick most recent application
|
// Pick most recent application
|
||||||
const chosen =
|
const chosen =
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
(apps ?? []).filter((a: any) => a?.dataCerere)
|
(apps ?? [])
|
||||||
|
.filter((a: any) => a?.dataCerere)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
.sort((a: any, b: any) =>
|
.sort(
|
||||||
(b.dataCerere ?? 0) - (a.dataCerere ?? 0),
|
(a: any, b: any) =>
|
||||||
|
(b.dataCerere ?? 0) - (a.dataCerere ?? 0),
|
||||||
)[0] ?? apps?.[0];
|
)[0] ?? apps?.[0];
|
||||||
|
|
||||||
if (chosen) {
|
if (chosen) {
|
||||||
solicitant = String(
|
solicitant = String(chosen.solicitant ?? chosen.deponent ?? "");
|
||||||
chosen.solicitant ?? chosen.deponent ?? "",
|
|
||||||
);
|
|
||||||
const appId = chosen.applicationId;
|
const appId = chosen.applicationId;
|
||||||
if (appId) {
|
if (appId) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { EterraClient } from "@/modules/parcel-sync/services/eterra-client";
|
||||||
|
import { getSessionCredentials } from "@/modules/parcel-sync/services/session-store";
|
||||||
|
|
||||||
|
export const runtime = "nodejs";
|
||||||
|
export const dynamic = "force-dynamic";
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* Server-side cache */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
type EnrichedUat = {
|
||||||
|
siruta: string;
|
||||||
|
name: string;
|
||||||
|
county: string;
|
||||||
|
workspacePk: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const globalRef = globalThis as {
|
||||||
|
__eterraEnrichedUats?: { data: EnrichedUat[]; ts: number };
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Cache TTL — 1 hour (county/UAT data rarely changes) */
|
||||||
|
const CACHE_TTL_MS = 60 * 60 * 1000;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
/* GET /api/eterra/uats */
|
||||||
|
/* */
|
||||||
|
/* Returns all UATs enriched with county name + workspacePk. */
|
||||||
|
/* Cached server-side for 1 hour. */
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
export async function GET() {
|
||||||
|
try {
|
||||||
|
// Return from cache if fresh
|
||||||
|
const cached = globalRef.__eterraEnrichedUats;
|
||||||
|
if (cached && Date.now() - cached.ts < CACHE_TTL_MS) {
|
||||||
|
return NextResponse.json({ uats: cached.data, cached: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need eTerra credentials
|
||||||
|
const session = getSessionCredentials();
|
||||||
|
const username = String(
|
||||||
|
session?.username || process.env.ETERRA_USERNAME || "",
|
||||||
|
).trim();
|
||||||
|
const password = String(
|
||||||
|
session?.password || process.env.ETERRA_PASSWORD || "",
|
||||||
|
).trim();
|
||||||
|
|
||||||
|
if (!username || !password) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: "Conectează-te la eTerra mai întâi.", uats: [] },
|
||||||
|
{ status: 401 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = await EterraClient.create(username, password);
|
||||||
|
|
||||||
|
// Fetch all counties
|
||||||
|
const counties = await client.fetchCounties();
|
||||||
|
const enriched: EnrichedUat[] = [];
|
||||||
|
|
||||||
|
for (const county of counties) {
|
||||||
|
const countyPk = county?.nomenPk ?? county?.pk ?? county?.id;
|
||||||
|
const countyName = String(county?.name ?? "").trim();
|
||||||
|
if (!countyPk || !countyName) continue;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const uats = await client.fetchAdminUnitsByCounty(countyPk);
|
||||||
|
for (const uat of uats) {
|
||||||
|
const uatPk = String(uat?.nomenPk ?? uat?.pk ?? "");
|
||||||
|
const uatName = String(uat?.name ?? "").trim();
|
||||||
|
if (!uatPk || !uatName) continue;
|
||||||
|
|
||||||
|
enriched.push({
|
||||||
|
siruta: uatPk,
|
||||||
|
name: uatName,
|
||||||
|
county: countyName,
|
||||||
|
workspacePk: Number(countyPk),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Skip county if UAT fetch fails
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update cache
|
||||||
|
globalRef.__eterraEnrichedUats = { data: enriched, ts: Date.now() };
|
||||||
|
|
||||||
|
// Also populate the workspace cache used by search route
|
||||||
|
const wsGlobal = globalThis as {
|
||||||
|
__eterraWorkspaceCache?: Map<string, number>;
|
||||||
|
};
|
||||||
|
if (!wsGlobal.__eterraWorkspaceCache) {
|
||||||
|
wsGlobal.__eterraWorkspaceCache = new Map();
|
||||||
|
}
|
||||||
|
for (const u of enriched) {
|
||||||
|
wsGlobal.__eterraWorkspaceCache.set(u.siruta, u.workspacePk);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
uats: enriched,
|
||||||
|
cached: false,
|
||||||
|
total: enriched.length,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : "Eroare server";
|
||||||
|
return NextResponse.json({ error: message, uats: [] }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -51,7 +51,12 @@ import type { ParcelDetail } from "@/app/api/eterra/search/route";
|
|||||||
/* Types */
|
/* Types */
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
type UatEntry = { siruta: string; name: string; county?: string };
|
type UatEntry = {
|
||||||
|
siruta: string;
|
||||||
|
name: string;
|
||||||
|
county?: string;
|
||||||
|
workspacePk?: number;
|
||||||
|
};
|
||||||
|
|
||||||
type SessionStatus = {
|
type SessionStatus = {
|
||||||
connected: boolean;
|
connected: boolean;
|
||||||
@@ -277,7 +282,9 @@ export function ParcelSyncModule() {
|
|||||||
const [uatResults, setUatResults] = useState<UatEntry[]>([]);
|
const [uatResults, setUatResults] = useState<UatEntry[]>([]);
|
||||||
const [showUatResults, setShowUatResults] = useState(false);
|
const [showUatResults, setShowUatResults] = useState(false);
|
||||||
const [siruta, setSiruta] = useState("");
|
const [siruta, setSiruta] = useState("");
|
||||||
|
const [workspacePk, setWorkspacePk] = useState<number | null>(null);
|
||||||
const uatRef = useRef<HTMLDivElement>(null);
|
const uatRef = useRef<HTMLDivElement>(null);
|
||||||
|
const enrichedUatsFetched = useRef(false);
|
||||||
|
|
||||||
/* ── Export state ────────────────────────────────────────────── */
|
/* ── Export state ────────────────────────────────────────────── */
|
||||||
const [exportJobId, setExportJobId] = useState<string | null>(null);
|
const [exportJobId, setExportJobId] = useState<string | null>(null);
|
||||||
@@ -318,6 +325,7 @@ export function ParcelSyncModule() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// Load static UAT data as fallback
|
||||||
fetch("/uat.json")
|
fetch("/uat.json")
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then((data: UatEntry[]) => setUatData(data))
|
.then((data: UatEntry[]) => setUatData(data))
|
||||||
@@ -333,6 +341,36 @@ export function ParcelSyncModule() {
|
|||||||
};
|
};
|
||||||
}, [fetchSession]);
|
}, [fetchSession]);
|
||||||
|
|
||||||
|
/* ════════════════════════════════════════════════════════════ */
|
||||||
|
/* Fetch enriched UAT list (with county + workspace) when */
|
||||||
|
/* connected to eTerra. Falls back to static uat.json. */
|
||||||
|
/* ════════════════════════════════════════════════════════════ */
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!session.connected || enrichedUatsFetched.current) return;
|
||||||
|
enrichedUatsFetched.current = true;
|
||||||
|
|
||||||
|
fetch("/api/eterra/uats")
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then(
|
||||||
|
(data: {
|
||||||
|
uats?: {
|
||||||
|
siruta: string;
|
||||||
|
name: string;
|
||||||
|
county: string;
|
||||||
|
workspacePk: number;
|
||||||
|
}[];
|
||||||
|
}) => {
|
||||||
|
if (data.uats && data.uats.length > 0) {
|
||||||
|
setUatData(data.uats);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.catch(() => {
|
||||||
|
// Keep static uat.json data
|
||||||
|
});
|
||||||
|
}, [session.connected]);
|
||||||
|
|
||||||
/* ════════════════════════════════════════════════════════════ */
|
/* ════════════════════════════════════════════════════════════ */
|
||||||
/* UAT autocomplete filter */
|
/* UAT autocomplete filter */
|
||||||
/* ════════════════════════════════════════════════════════════ */
|
/* ════════════════════════════════════════════════════════════ */
|
||||||
@@ -346,12 +384,15 @@ export function ParcelSyncModule() {
|
|||||||
const isDigit = /^\d+$/.test(raw);
|
const isDigit = /^\d+$/.test(raw);
|
||||||
const query = normalizeText(raw);
|
const query = normalizeText(raw);
|
||||||
const results = uatData
|
const results = uatData
|
||||||
.filter((item) =>
|
.filter((item) => {
|
||||||
isDigit
|
if (isDigit) return item.siruta.startsWith(raw);
|
||||||
? item.siruta.startsWith(raw)
|
// Match UAT name or county name
|
||||||
: normalizeText(item.name).includes(query),
|
if (normalizeText(item.name).includes(query)) return true;
|
||||||
)
|
if (item.county && normalizeText(item.county).includes(query))
|
||||||
.slice(0, 8);
|
return true;
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.slice(0, 12);
|
||||||
setUatResults(results);
|
setUatResults(results);
|
||||||
}, [uatQuery, uatData]);
|
}, [uatQuery, uatData]);
|
||||||
|
|
||||||
@@ -611,6 +652,7 @@ export function ParcelSyncModule() {
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
siruta,
|
siruta,
|
||||||
search: featuresSearch.trim(),
|
search: featuresSearch.trim(),
|
||||||
|
...(workspacePk ? { workspacePk } : {}),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
const data = (await res.json()) as {
|
const data = (await res.json()) as {
|
||||||
@@ -629,7 +671,7 @@ export function ParcelSyncModule() {
|
|||||||
setSearchError("Eroare de rețea.");
|
setSearchError("Eroare de rețea.");
|
||||||
}
|
}
|
||||||
setLoadingFeatures(false);
|
setLoadingFeatures(false);
|
||||||
}, [siruta, featuresSearch]);
|
}, [siruta, featuresSearch, workspacePk]);
|
||||||
|
|
||||||
// No auto-search — user clicks button or presses Enter
|
// No auto-search — user clicks button or presses Enter
|
||||||
const handleSearchKeyDown = useCallback(
|
const handleSearchKeyDown = useCallback(
|
||||||
@@ -643,16 +685,17 @@ export function ParcelSyncModule() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Add result(s) to list for CSV export
|
// Add result(s) to list for CSV export
|
||||||
const addToList = useCallback(
|
const addToList = useCallback((item: ParcelDetail) => {
|
||||||
(item: ParcelDetail) => {
|
setSearchList((prev) => {
|
||||||
setSearchList((prev) => {
|
if (
|
||||||
if (prev.some((p) => p.nrCad === item.nrCad && p.immovablePk === item.immovablePk))
|
prev.some(
|
||||||
return prev;
|
(p) => p.nrCad === item.nrCad && p.immovablePk === item.immovablePk,
|
||||||
return [...prev, item];
|
)
|
||||||
});
|
)
|
||||||
},
|
return prev;
|
||||||
[],
|
return [...prev, item];
|
||||||
);
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const removeFromList = useCallback((nrCad: string) => {
|
const removeFromList = useCallback((nrCad: string) => {
|
||||||
setSearchList((prev) => prev.filter((p) => p.nrCad !== nrCad));
|
setSearchList((prev) => prev.filter((p) => p.nrCad !== nrCad));
|
||||||
@@ -772,18 +815,31 @@ export function ParcelSyncModule() {
|
|||||||
onMouseDown={(e) => {
|
onMouseDown={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const label = item.county
|
const label = item.county
|
||||||
? `${item.name} (${item.siruta}) — ${item.county}`
|
? `${item.name} (${item.siruta}), ${item.county}`
|
||||||
: `${item.name} (${item.siruta})`;
|
: `${item.name} (${item.siruta})`;
|
||||||
setUatQuery(label);
|
setUatQuery(label);
|
||||||
setSiruta(item.siruta);
|
setSiruta(item.siruta);
|
||||||
|
setWorkspacePk(item.workspacePk ?? null);
|
||||||
setShowUatResults(false);
|
setShowUatResults(false);
|
||||||
setSearchResults([]);
|
setSearchResults([]);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="font-medium">{item.name}</span>
|
<span>
|
||||||
<span className="text-xs text-muted-foreground font-mono ml-2">
|
<span className="font-medium">{item.name}</span>
|
||||||
|
<span className="text-muted-foreground ml-1.5">
|
||||||
|
({item.siruta})
|
||||||
|
</span>
|
||||||
|
{item.county && (
|
||||||
|
<span className="text-muted-foreground">
|
||||||
|
,{" "}
|
||||||
|
<span className="font-medium text-foreground/70">
|
||||||
|
{item.county}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-muted-foreground font-mono ml-2 shrink-0">
|
||||||
{item.siruta}
|
{item.siruta}
|
||||||
{item.county ? ` · ${item.county}` : ""}
|
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
@@ -878,7 +934,8 @@ export function ParcelSyncModule() {
|
|||||||
<Loader2 className="h-10 w-10 mx-auto mb-3 animate-spin opacity-50" />
|
<Loader2 className="h-10 w-10 mx-auto mb-3 animate-spin opacity-50" />
|
||||||
<p>Se caută în eTerra...</p>
|
<p>Se caută în eTerra...</p>
|
||||||
<p className="text-xs mt-1 opacity-60">
|
<p className="text-xs mt-1 opacity-60">
|
||||||
Prima căutare pe un UAT nou poate dura ~10-30s (se încarcă lista de județe).
|
Prima căutare pe un UAT nou poate dura ~10-30s (se încarcă
|
||||||
|
lista de județe).
|
||||||
</p>
|
</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -889,7 +946,8 @@ export function ParcelSyncModule() {
|
|||||||
{/* Action bar */}
|
{/* Action bar */}
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-sm text-muted-foreground">
|
||||||
{searchResults.length} rezultat{searchResults.length > 1 ? "e" : ""}
|
{searchResults.length} rezultat
|
||||||
|
{searchResults.length > 1 ? "e" : ""}
|
||||||
{searchList.length > 0 && (
|
{searchList.length > 0 && (
|
||||||
<span className="ml-2">
|
<span className="ml-2">
|
||||||
· <strong>{searchList.length}</strong> în listă
|
· <strong>{searchList.length}</strong> în listă
|
||||||
@@ -913,7 +971,9 @@ export function ParcelSyncModule() {
|
|||||||
size="sm"
|
size="sm"
|
||||||
variant="default"
|
variant="default"
|
||||||
onClick={downloadCSV}
|
onClick={downloadCSV}
|
||||||
disabled={searchResults.length === 0 && searchList.length === 0}
|
disabled={
|
||||||
|
searchResults.length === 0 && searchList.length === 0
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<FileDown className="mr-1 h-3.5 w-3.5" />
|
<FileDown className="mr-1 h-3.5 w-3.5" />
|
||||||
Descarcă CSV
|
Descarcă CSV
|
||||||
@@ -963,7 +1023,9 @@ export function ParcelSyncModule() {
|
|||||||
const text = [
|
const text = [
|
||||||
`Nr. Cad: ${p.nrCad}`,
|
`Nr. Cad: ${p.nrCad}`,
|
||||||
`Nr. CF: ${p.nrCF || "—"}`,
|
`Nr. CF: ${p.nrCF || "—"}`,
|
||||||
p.nrCFVechi ? `CF vechi: ${p.nrCFVechi}` : null,
|
p.nrCFVechi
|
||||||
|
? `CF vechi: ${p.nrCFVechi}`
|
||||||
|
: null,
|
||||||
p.nrTopo ? `Nr. Topo: ${p.nrTopo}` : null,
|
p.nrTopo ? `Nr. Topo: ${p.nrTopo}` : null,
|
||||||
p.suprafata != null
|
p.suprafata != null
|
||||||
? `Suprafață: ${p.suprafata.toLocaleString("ro-RO")} mp`
|
? `Suprafață: ${p.suprafata.toLocaleString("ro-RO")} mp`
|
||||||
@@ -973,8 +1035,12 @@ export function ParcelSyncModule() {
|
|||||||
? `Categorie: ${p.categorieFolosinta}`
|
? `Categorie: ${p.categorieFolosinta}`
|
||||||
: null,
|
: null,
|
||||||
p.adresa ? `Adresă: ${p.adresa}` : null,
|
p.adresa ? `Adresă: ${p.adresa}` : null,
|
||||||
p.proprietari ? `Proprietari: ${p.proprietari}` : null,
|
p.proprietari
|
||||||
p.solicitant ? `Solicitant: ${p.solicitant}` : null,
|
? `Proprietari: ${p.proprietari}`
|
||||||
|
: null,
|
||||||
|
p.solicitant
|
||||||
|
? `Solicitant: ${p.solicitant}`
|
||||||
|
: null,
|
||||||
]
|
]
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join("\n");
|
.join("\n");
|
||||||
@@ -1087,7 +1153,8 @@ export function ParcelSyncModule() {
|
|||||||
<Search className="h-10 w-10 mx-auto mb-3 opacity-30" />
|
<Search className="h-10 w-10 mx-auto mb-3 opacity-30" />
|
||||||
<p>Introdu un număr cadastral și apasă Caută.</p>
|
<p>Introdu un număr cadastral și apasă Caută.</p>
|
||||||
<p className="text-xs mt-1 opacity-60">
|
<p className="text-xs mt-1 opacity-60">
|
||||||
Poți căuta mai multe parcele simultan, separate prin virgulă.
|
Poți căuta mai multe parcele simultan, separate prin
|
||||||
|
virgulă.
|
||||||
</p>
|
</p>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
@@ -1120,10 +1187,18 @@ export function ParcelSyncModule() {
|
|||||||
<table className="w-full text-sm">
|
<table className="w-full text-sm">
|
||||||
<thead>
|
<thead>
|
||||||
<tr className="border-b bg-muted/40">
|
<tr className="border-b bg-muted/40">
|
||||||
<th className="px-3 py-2 text-left font-medium">Nr. Cad</th>
|
<th className="px-3 py-2 text-left font-medium">
|
||||||
<th className="px-3 py-2 text-left font-medium">Nr. CF</th>
|
Nr. Cad
|
||||||
<th className="px-3 py-2 text-right font-medium hidden sm:table-cell">Suprafață</th>
|
</th>
|
||||||
<th className="px-3 py-2 text-left font-medium hidden md:table-cell">Proprietari</th>
|
<th className="px-3 py-2 text-left font-medium">
|
||||||
|
Nr. CF
|
||||||
|
</th>
|
||||||
|
<th className="px-3 py-2 text-right font-medium hidden sm:table-cell">
|
||||||
|
Suprafață
|
||||||
|
</th>
|
||||||
|
<th className="px-3 py-2 text-left font-medium hidden md:table-cell">
|
||||||
|
Proprietari
|
||||||
|
</th>
|
||||||
<th className="px-3 py-2 w-8"></th>
|
<th className="px-3 py-2 w-8"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -1140,7 +1215,9 @@ export function ParcelSyncModule() {
|
|||||||
{p.nrCF || "—"}
|
{p.nrCF || "—"}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 text-right hidden sm:table-cell tabular-nums text-xs">
|
<td className="px-3 py-2 text-right hidden sm:table-cell tabular-nums text-xs">
|
||||||
{p.suprafata != null ? formatArea(p.suprafata) : "—"}
|
{p.suprafata != null
|
||||||
|
? formatArea(p.suprafata)
|
||||||
|
: "—"}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-3 py-2 hidden md:table-cell text-xs truncate max-w-[300px]">
|
<td className="px-3 py-2 hidden md:table-cell text-xs truncate max-w-[300px]">
|
||||||
{p.proprietari || "—"}
|
{p.proprietari || "—"}
|
||||||
|
|||||||
@@ -408,23 +408,52 @@ export class EterraClient {
|
|||||||
/* ---- Magic-mode methods (eTerra application APIs) ------------- */
|
/* ---- Magic-mode methods (eTerra application APIs) ------------- */
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
async fetchImmAppsByImmovable(immovableId: string | number, workspaceId: string | number): Promise<any[]> {
|
async fetchImmAppsByImmovable(
|
||||||
|
immovableId: string | number,
|
||||||
|
workspaceId: string | number,
|
||||||
|
): Promise<any[]> {
|
||||||
const url = `${BASE_URL}/api/immApps/byImm/list/${immovableId}/${workspaceId}`;
|
const url = `${BASE_URL}/api/immApps/byImm/list/${immovableId}/${workspaceId}`;
|
||||||
return this.getRawJson(url);
|
return this.getRawJson(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
async fetchParcelFolosinte(workspaceId: string | number, immovableId: string | number, applicationId: string | number, page = 1): Promise<any[]> {
|
async fetchParcelFolosinte(
|
||||||
|
workspaceId: string | number,
|
||||||
|
immovableId: string | number,
|
||||||
|
applicationId: string | number,
|
||||||
|
page = 1,
|
||||||
|
): Promise<any[]> {
|
||||||
const url = `${BASE_URL}/api/immApps/parcels/list/${workspaceId}/${immovableId}/${applicationId}/${page}`;
|
const url = `${BASE_URL}/api/immApps/parcels/list/${workspaceId}/${immovableId}/${applicationId}/${page}`;
|
||||||
return this.getRawJson(url);
|
return this.getRawJson(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
async fetchImmovableListByAdminUnit(workspaceId: string | number, adminUnitId: string | number, page = 0, size = 200, includeInscrisCF = true): Promise<any> {
|
async fetchImmovableListByAdminUnit(
|
||||||
|
workspaceId: string | number,
|
||||||
|
adminUnitId: string | number,
|
||||||
|
page = 0,
|
||||||
|
size = 200,
|
||||||
|
includeInscrisCF = true,
|
||||||
|
): Promise<any> {
|
||||||
const url = `${BASE_URL}/api/immovable/list`;
|
const url = `${BASE_URL}/api/immovable/list`;
|
||||||
const filters: Array<{ value: string | number; type: "NUMBER" | "STRING"; key: string; op: string }> = [
|
const filters: Array<{
|
||||||
{ value: Number(workspaceId), type: "NUMBER", key: "workspace.nomenPk", op: "=" },
|
value: string | number;
|
||||||
{ value: Number(adminUnitId), type: "NUMBER", key: "adminUnit.nomenPk", op: "=" },
|
type: "NUMBER" | "STRING";
|
||||||
|
key: string;
|
||||||
|
op: string;
|
||||||
|
}> = [
|
||||||
|
{
|
||||||
|
value: Number(workspaceId),
|
||||||
|
type: "NUMBER",
|
||||||
|
key: "workspace.nomenPk",
|
||||||
|
op: "=",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: Number(adminUnitId),
|
||||||
|
type: "NUMBER",
|
||||||
|
key: "adminUnit.nomenPk",
|
||||||
|
op: "=",
|
||||||
|
},
|
||||||
{ value: "C", type: "STRING", key: "immovableType", op: "<>C" },
|
{ value: "C", type: "STRING", key: "immovableType", op: "<>C" },
|
||||||
];
|
];
|
||||||
if (includeInscrisCF) {
|
if (includeInscrisCF) {
|
||||||
@@ -440,7 +469,10 @@ export class EterraClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
async fetchDocumentationData(workspaceId: string | number, immovableIds: Array<string | number>): Promise<any> {
|
async fetchDocumentationData(
|
||||||
|
workspaceId: string | number,
|
||||||
|
immovableIds: Array<string | number>,
|
||||||
|
): Promise<any> {
|
||||||
const url = `${BASE_URL}/api/documentation/data/`;
|
const url = `${BASE_URL}/api/documentation/data/`;
|
||||||
const payload = {
|
const payload = {
|
||||||
workflowCode: "EXPLORE_DATABASE",
|
workflowCode: "EXPLORE_DATABASE",
|
||||||
@@ -458,7 +490,12 @@ export class EterraClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
async fetchImmovableParcelDetails(workspaceId: string | number, immovableId: string | number, page = 1, size = 1): Promise<any[]> {
|
async fetchImmovableParcelDetails(
|
||||||
|
workspaceId: string | number,
|
||||||
|
immovableId: string | number,
|
||||||
|
page = 1,
|
||||||
|
size = 1,
|
||||||
|
): Promise<any[]> {
|
||||||
const url = `${BASE_URL}/api/immovable/details/parcels/list/${workspaceId}/${immovableId}/${page}/${size}`;
|
const url = `${BASE_URL}/api/immovable/details/parcels/list/${workspaceId}/${immovableId}/${page}/${size}`;
|
||||||
return this.getRawJson(url);
|
return this.getRawJson(url);
|
||||||
}
|
}
|
||||||
@@ -469,12 +506,38 @@ export class EterraClient {
|
|||||||
* a cadastral number in the search box.
|
* a cadastral number in the search box.
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
async searchImmovableByIdentifier(workspaceId: string | number, adminUnitId: string | number, identifierDetails: string, page = 0, size = 10): Promise<any> {
|
async searchImmovableByIdentifier(
|
||||||
|
workspaceId: string | number,
|
||||||
|
adminUnitId: string | number,
|
||||||
|
identifierDetails: string,
|
||||||
|
page = 0,
|
||||||
|
size = 10,
|
||||||
|
): Promise<any> {
|
||||||
const url = `${BASE_URL}/api/immovable/list`;
|
const url = `${BASE_URL}/api/immovable/list`;
|
||||||
const filters: Array<{ value: string | number; type: "NUMBER" | "STRING"; key: string; op: string }> = [
|
const filters: Array<{
|
||||||
{ value: Number(workspaceId), type: "NUMBER", key: "workspace.nomenPk", op: "=" },
|
value: string | number;
|
||||||
{ value: Number(adminUnitId), type: "NUMBER", key: "adminUnit.nomenPk", op: "=" },
|
type: "NUMBER" | "STRING";
|
||||||
{ value: identifierDetails, type: "STRING", key: "identifierDetails", op: "=" },
|
key: string;
|
||||||
|
op: string;
|
||||||
|
}> = [
|
||||||
|
{
|
||||||
|
value: Number(workspaceId),
|
||||||
|
type: "NUMBER",
|
||||||
|
key: "workspace.nomenPk",
|
||||||
|
op: "=",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: Number(adminUnitId),
|
||||||
|
type: "NUMBER",
|
||||||
|
key: "adminUnit.nomenPk",
|
||||||
|
op: "=",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: identifierDetails,
|
||||||
|
type: "STRING",
|
||||||
|
key: "identifierDetails",
|
||||||
|
op: "=",
|
||||||
|
},
|
||||||
{ value: -1, type: "NUMBER", key: "inscrisCF", op: "=" },
|
{ value: -1, type: "NUMBER", key: "inscrisCF", op: "=" },
|
||||||
{ value: "P", type: "STRING", key: "immovableType", op: "<>C" },
|
{ value: "P", type: "STRING", key: "immovableType", op: "<>C" },
|
||||||
];
|
];
|
||||||
@@ -502,7 +565,9 @@ export class EterraClient {
|
|||||||
* Returns array of { nomenPk, name, parentNomenPk, ... }
|
* Returns array of { nomenPk, name, parentNomenPk, ... }
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
async fetchAdminUnitsByCounty(countyNomenPk: string | number): Promise<any[]> {
|
async fetchAdminUnitsByCounty(
|
||||||
|
countyNomenPk: string | number,
|
||||||
|
): Promise<any[]> {
|
||||||
const url = `${BASE_URL}/api/adm/nomen/ADMINISTRATIVEUNIT/filterByParent/${countyNomenPk}`;
|
const url = `${BASE_URL}/api/adm/nomen/ADMINISTRATIVEUNIT/filterByParent/${countyNomenPk}`;
|
||||||
return this.getRawJson(url);
|
return this.getRawJson(url);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user