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:
AI Assistant
2026-03-06 20:46:44 +02:00
parent 540b02d8d2
commit d948e5c1cf
4 changed files with 320 additions and 61 deletions
+19 -13
View File
@@ -7,9 +7,10 @@ export const dynamic = "force-dynamic";
type Body = {
siruta?: string;
search?: string; // cadastral number(s), comma or newline separated
search?: string; // cadastral number(s), comma or newline separated
username?: 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
function normalizeIntravilan(values: string[]) {
const normalized = values
.map((v) => String(v ?? "").trim().toLowerCase())
.map((v) =>
String(v ?? "")
.trim()
.toLowerCase(),
)
.filter(Boolean);
const unique = new Set(normalized);
if (!unique.size) return "";
@@ -182,8 +187,11 @@ export async function POST(req: Request) {
const client = await EterraClient.create(username, password);
// Resolve workspace (county) for this SIRUTA
const workspaceId = await resolveWorkspace(client, siruta);
// Use provided workspacePk — or fall back to resolution
let workspaceId = body.workspacePk ?? null;
if (!workspaceId || !Number.isFinite(workspaceId)) {
workspaceId = await resolveWorkspace(client, siruta);
}
if (!workspaceId) {
return NextResponse.json(
{ 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
let nrCF = String(item?.paperLbNo ?? item?.paperCadNo ?? "");
let nrCFVechi = "";
let nrTopo = String(
item?.topNo ?? item?.paperCadNo ?? "",
);
let nrTopo = String(item?.topNo ?? item?.paperCadNo ?? "");
let addressText = formatAddress(item);
let proprietari = "";
let solicitant = "";
@@ -298,16 +304,16 @@ export async function POST(req: Request) {
// Pick most recent application
const chosen =
// 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
.sort((a: any, b: any) =>
(b.dataCerere ?? 0) - (a.dataCerere ?? 0),
.sort(
(a: any, b: any) =>
(b.dataCerere ?? 0) - (a.dataCerere ?? 0),
)[0] ?? apps?.[0];
if (chosen) {
solicitant = String(
chosen.solicitant ?? chosen.deponent ?? "",
);
solicitant = String(chosen.solicitant ?? chosen.deponent ?? "");
const appId = chosen.applicationId;
if (appId) {
try {