refactor(parcel-sync): global UAT bar, connection pill, reorder tabs
- UAT autocomplete always visible above tabs (all tabs share it) - Connection status pill in top-right: breathing green dot when connected, dropdown with credentials form / disconnect button - Tab order: Cautare Parcele (1st) -> Catalog Layere -> Export (last) - Renamed 'Butonul Magic' to just 'Magic' - Removed connection/UAT cards from inside Export tab
This commit is contained in:
@@ -35,8 +35,7 @@ const validate = (body: ExportBundleRequest) => {
|
|||||||
return { username, password, siruta, jobId, mode };
|
return { username, password, siruta, jobId, mode };
|
||||||
};
|
};
|
||||||
|
|
||||||
const sleep = (ms: number) =>
|
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
new Promise((resolve) => setTimeout(resolve, ms));
|
|
||||||
|
|
||||||
const scheduleClear = (jobId?: string) => {
|
const scheduleClear = (jobId?: string) => {
|
||||||
if (!jobId) return;
|
if (!jobId) return;
|
||||||
@@ -112,11 +111,7 @@ export async function POST(req: Request) {
|
|||||||
pushProgress();
|
pushProgress();
|
||||||
};
|
};
|
||||||
|
|
||||||
const setPhaseState = (
|
const setPhaseState = (next: string, weight: number, nextTotal?: number) => {
|
||||||
next: string,
|
|
||||||
weight: number,
|
|
||||||
nextTotal?: number,
|
|
||||||
) => {
|
|
||||||
phase = next;
|
phase = next;
|
||||||
currentWeight = weight;
|
currentWeight = weight;
|
||||||
phaseTotal = nextTotal;
|
phaseTotal = nextTotal;
|
||||||
@@ -145,7 +140,7 @@ export async function POST(req: Request) {
|
|||||||
updateOverall(0);
|
updateOverall(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
const withHeartbeat = async <T,>(task: () => Promise<T>) => {
|
const withHeartbeat = async <T>(task: () => Promise<T>) => {
|
||||||
let tick = 0.1;
|
let tick = 0.1;
|
||||||
updatePhaseProgress(tick, 1);
|
updatePhaseProgress(tick, 1);
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
@@ -284,7 +279,7 @@ export async function POST(req: Request) {
|
|||||||
|
|
||||||
let lastRequest = 0;
|
let lastRequest = 0;
|
||||||
const minInterval = 250;
|
const minInterval = 250;
|
||||||
const throttled = async <T,>(fn: () => Promise<T>) => {
|
const throttled = async <T>(fn: () => Promise<T>) => {
|
||||||
let attempt = 0;
|
let attempt = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
@@ -330,7 +325,11 @@ export async function POST(req: Request) {
|
|||||||
|
|
||||||
const normalizeIntravilan = (values: string[]) => {
|
const 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 "-";
|
||||||
@@ -359,8 +358,7 @@ export async function POST(req: Request) {
|
|||||||
const address = item?.immovableAddresses?.[0]?.address ?? null;
|
const address = item?.immovableAddresses?.[0]?.address ?? null;
|
||||||
if (!address) return "-";
|
if (!address) return "-";
|
||||||
const parts: string[] = [];
|
const parts: string[] = [];
|
||||||
if (address.addressDescription)
|
if (address.addressDescription) parts.push(address.addressDescription);
|
||||||
parts.push(address.addressDescription);
|
|
||||||
if (address.street) parts.push(`Str. ${address.street}`);
|
if (address.street) parts.push(`Str. ${address.street}`);
|
||||||
if (address.buildingNo) parts.push(`Nr. ${address.buildingNo}`);
|
if (address.buildingNo) parts.push(`Nr. ${address.buildingNo}`);
|
||||||
if (address.locality?.name) parts.push(address.locality.name);
|
if (address.locality?.name) parts.push(address.locality.name);
|
||||||
@@ -368,18 +366,12 @@ export async function POST(req: Request) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Building cross-ref map */
|
/* Building cross-ref map */
|
||||||
const buildingMap = new Map<
|
const buildingMap = new Map<string, { has: boolean; legal: boolean }>();
|
||||||
string,
|
|
||||||
{ has: boolean; legal: boolean }
|
|
||||||
>();
|
|
||||||
for (const feature of cladiriFeatures) {
|
for (const feature of cladiriFeatures) {
|
||||||
const attrs = feature.attributes ?? {};
|
const attrs = feature.attributes ?? {};
|
||||||
const immovableId =
|
const immovableId = attrs.IMMOVABLE_ID ?? attrs.IMOVABLE_ID ?? null;
|
||||||
attrs.IMMOVABLE_ID ?? attrs.IMOVABLE_ID ?? null;
|
|
||||||
const workspaceId = attrs.WORKSPACE_ID ?? null;
|
const workspaceId = attrs.WORKSPACE_ID ?? null;
|
||||||
const baseRef = baseCadRef(
|
const baseRef = baseCadRef(attrs.NATIONAL_CADASTRAL_REFERENCE ?? "");
|
||||||
attrs.NATIONAL_CADASTRAL_REFERENCE ?? "",
|
|
||||||
);
|
|
||||||
const isLegal =
|
const isLegal =
|
||||||
Number(attrs.IS_LEGAL ?? 0) === 1 ||
|
Number(attrs.IS_LEGAL ?? 0) === 1 ||
|
||||||
String(attrs.IS_LEGAL ?? "").toLowerCase() === "true";
|
String(attrs.IS_LEGAL ?? "").toLowerCase() === "true";
|
||||||
@@ -408,8 +400,7 @@ export async function POST(req: Request) {
|
|||||||
|
|
||||||
const addOwner = (landbook: string, name: string) => {
|
const addOwner = (landbook: string, name: string) => {
|
||||||
if (!landbook || !name) return;
|
if (!landbook || !name) return;
|
||||||
const existing =
|
const existing = ownersByLandbook.get(landbook) ?? new Set<string>();
|
||||||
ownersByLandbook.get(landbook) ?? new Set<string>();
|
|
||||||
existing.add(name);
|
existing.add(name);
|
||||||
ownersByLandbook.set(landbook, existing);
|
ownersByLandbook.set(landbook, existing);
|
||||||
};
|
};
|
||||||
@@ -444,9 +435,7 @@ export async function POST(req: Request) {
|
|||||||
(listResponse?.content ?? []).forEach((item: any) => {
|
(listResponse?.content ?? []).forEach((item: any) => {
|
||||||
const idKey = normalizeId(item?.immovablePk);
|
const idKey = normalizeId(item?.immovablePk);
|
||||||
if (idKey) immovableListById.set(idKey, item);
|
if (idKey) immovableListById.set(idKey, item);
|
||||||
const cadKey = normalizeCadRef(
|
const cadKey = normalizeCadRef(item?.identifierDetails ?? "");
|
||||||
item?.identifierDetails ?? "",
|
|
||||||
);
|
|
||||||
if (cadKey) immovableListByCad.set(cadKey, item);
|
if (cadKey) immovableListByCad.set(cadKey, item);
|
||||||
});
|
});
|
||||||
listPage += 1;
|
listPage += 1;
|
||||||
@@ -499,10 +488,7 @@ export async function POST(req: Request) {
|
|||||||
];
|
];
|
||||||
csvRows.push(headers.join(","));
|
csvRows.push(headers.join(","));
|
||||||
|
|
||||||
const detailsByObjectId = new Map<
|
const detailsByObjectId = new Map<string, Record<string, unknown>>();
|
||||||
string,
|
|
||||||
Record<string, unknown>
|
|
||||||
>();
|
|
||||||
|
|
||||||
for (let index = 0; index < terenuriFeatures.length; index += 1) {
|
for (let index = 0; index < terenuriFeatures.length; index += 1) {
|
||||||
const feature = terenuriFeatures[index]!;
|
const feature = terenuriFeatures[index]!;
|
||||||
@@ -533,15 +519,11 @@ export async function POST(req: Request) {
|
|||||||
);
|
);
|
||||||
immAppsCache.set(appKey, apps);
|
immAppsCache.set(appKey, apps);
|
||||||
}
|
}
|
||||||
const chosen = pickApplication(
|
const chosen = pickApplication(apps, Number(applicationId ?? 0));
|
||||||
apps,
|
|
||||||
Number(applicationId ?? 0),
|
|
||||||
);
|
|
||||||
const appId =
|
const appId =
|
||||||
chosen?.applicationId ??
|
chosen?.applicationId ??
|
||||||
(applicationId ? Number(applicationId) : null);
|
(applicationId ? Number(applicationId) : null);
|
||||||
solicitant =
|
solicitant = chosen?.solicitant ?? chosen?.deponent ?? solicitant;
|
||||||
chosen?.solicitant ?? chosen?.deponent ?? solicitant;
|
|
||||||
|
|
||||||
if (appId) {
|
if (appId) {
|
||||||
const folKey = `${workspaceId}:${immovableId}:${appId}`;
|
const folKey = `${workspaceId}:${immovableId}:${appId}`;
|
||||||
@@ -563,8 +545,7 @@ export async function POST(req: Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const cadRefRaw = (attrs.NATIONAL_CADASTRAL_REFERENCE ??
|
const cadRefRaw = (attrs.NATIONAL_CADASTRAL_REFERENCE ?? "") as string;
|
||||||
"") as string;
|
|
||||||
const cadRef = normalizeCadRef(cadRefRaw);
|
const cadRef = normalizeCadRef(cadRefRaw);
|
||||||
const immKey = normalizeId(immovableId);
|
const immKey = normalizeId(immovableId);
|
||||||
const listItem =
|
const listItem =
|
||||||
@@ -573,51 +554,37 @@ export async function POST(req: Request) {
|
|||||||
const docKey = listItem?.immovablePk
|
const docKey = listItem?.immovablePk
|
||||||
? normalizeId(listItem.immovablePk)
|
? normalizeId(listItem.immovablePk)
|
||||||
: "";
|
: "";
|
||||||
const docItem = docKey
|
const docItem = docKey ? docByImmovable.get(docKey) : undefined;
|
||||||
? docByImmovable.get(docKey)
|
|
||||||
: undefined;
|
|
||||||
const landbookIE = docItem?.landbookIE ?? "";
|
const landbookIE = docItem?.landbookIE ?? "";
|
||||||
const owners =
|
const owners =
|
||||||
landbookIE && ownersByLandbook.get(String(landbookIE))
|
landbookIE && ownersByLandbook.get(String(landbookIE))
|
||||||
? Array.from(
|
? Array.from(ownersByLandbook.get(String(landbookIE)) ?? [])
|
||||||
ownersByLandbook.get(String(landbookIE)) ?? [],
|
|
||||||
)
|
|
||||||
: [];
|
: [];
|
||||||
const ownersByCad =
|
const ownersByCad =
|
||||||
cadRefRaw && ownersByLandbook.get(String(cadRefRaw))
|
cadRefRaw && ownersByLandbook.get(String(cadRefRaw))
|
||||||
? Array.from(
|
? Array.from(ownersByLandbook.get(String(cadRefRaw)) ?? [])
|
||||||
ownersByLandbook.get(String(cadRefRaw)) ?? [],
|
|
||||||
)
|
|
||||||
: [];
|
: [];
|
||||||
proprietari =
|
proprietari =
|
||||||
Array.from(new Set([...owners, ...ownersByCad])).join(
|
Array.from(new Set([...owners, ...ownersByCad])).join("; ") ||
|
||||||
"; ",
|
proprietari;
|
||||||
) || proprietari;
|
|
||||||
|
|
||||||
nrCF =
|
nrCF =
|
||||||
docItem?.landbookIE ||
|
docItem?.landbookIE ||
|
||||||
listItem?.paperLbNo ||
|
listItem?.paperLbNo ||
|
||||||
listItem?.paperCadNo ||
|
listItem?.paperCadNo ||
|
||||||
nrCF;
|
nrCF;
|
||||||
const nrCFVechiRaw =
|
const nrCFVechiRaw = listItem?.paperLbNo || listItem?.paperCadNo || "";
|
||||||
listItem?.paperLbNo || listItem?.paperCadNo || "";
|
|
||||||
nrCFVechi =
|
nrCFVechi =
|
||||||
docItem?.landbookIE && nrCFVechiRaw !== nrCF
|
docItem?.landbookIE && nrCFVechiRaw !== nrCF
|
||||||
? nrCFVechiRaw
|
? nrCFVechiRaw
|
||||||
: nrCFVechi;
|
: nrCFVechi;
|
||||||
nrTopo =
|
nrTopo =
|
||||||
listItem?.topNo ||
|
listItem?.topNo || docItem?.topNo || listItem?.paperCadNo || nrTopo;
|
||||||
docItem?.topNo ||
|
addressText = listItem ? formatAddress(listItem) : addressText;
|
||||||
listItem?.paperCadNo ||
|
|
||||||
nrTopo;
|
|
||||||
addressText = listItem
|
|
||||||
? formatAddress(listItem)
|
|
||||||
: addressText;
|
|
||||||
|
|
||||||
const parcelRef = baseCadRef(cadRefRaw);
|
const parcelRef = baseCadRef(cadRefRaw);
|
||||||
const wKey = makeWorkspaceKey(workspaceId, immovableId);
|
const wKey = makeWorkspaceKey(workspaceId, immovableId);
|
||||||
const build =
|
const build = (immKey ? buildingMap.get(immKey) : undefined) ??
|
||||||
(immKey ? buildingMap.get(immKey) : undefined) ??
|
|
||||||
(wKey ? buildingMap.get(wKey) : undefined) ??
|
(wKey ? buildingMap.get(wKey) : undefined) ??
|
||||||
(parcelRef ? buildingMap.get(parcelRef) : undefined) ?? {
|
(parcelRef ? buildingMap.get(parcelRef) : undefined) ?? {
|
||||||
has: false,
|
has: false,
|
||||||
@@ -637,10 +604,8 @@ export async function POST(req: Request) {
|
|||||||
NR_TOPO: nrTopo,
|
NR_TOPO: nrTopo,
|
||||||
ADRESA: addressText,
|
ADRESA: addressText,
|
||||||
PROPRIETARI: proprietari,
|
PROPRIETARI: proprietari,
|
||||||
SUPRAFATA_2D:
|
SUPRAFATA_2D: areaValue !== null ? Number(areaValue.toFixed(2)) : "",
|
||||||
areaValue !== null ? Number(areaValue.toFixed(2)) : "",
|
SUPRAFATA_R: areaValue !== null ? Math.round(areaValue) : "",
|
||||||
SUPRAFATA_R:
|
|
||||||
areaValue !== null ? Math.round(areaValue) : "",
|
|
||||||
SOLICITANT: solicitant,
|
SOLICITANT: solicitant,
|
||||||
INTRAVILAN: intravilan,
|
INTRAVILAN: intravilan,
|
||||||
CATEGORIE_FOLOSINTA: categorie,
|
CATEGORIE_FOLOSINTA: categorie,
|
||||||
@@ -671,8 +636,7 @@ export async function POST(req: Request) {
|
|||||||
];
|
];
|
||||||
csvRows.push(row.join(","));
|
csvRows.push(row.join(","));
|
||||||
|
|
||||||
if (index % 10 === 0)
|
if (index % 10 === 0) updatePhaseProgress(index + 1, terenuriCount);
|
||||||
updatePhaseProgress(index + 1, terenuriCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePhaseProgress(terenuriFeatures.length, terenuriCount);
|
updatePhaseProgress(terenuriFeatures.length, terenuriCount);
|
||||||
@@ -737,9 +701,7 @@ export async function POST(req: Request) {
|
|||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
const magicFeatures = terenuriGeo.features.map((feature) => {
|
const magicFeatures = terenuriGeo.features.map((feature) => {
|
||||||
const objectId = String(
|
const objectId = String(feature.properties?.OBJECTID ?? "");
|
||||||
feature.properties?.OBJECTID ?? "",
|
|
||||||
);
|
|
||||||
const extra = detailsByObjectId.get(objectId) ?? {};
|
const extra = detailsByObjectId.get(objectId) ?? {};
|
||||||
return {
|
return {
|
||||||
...feature,
|
...feature,
|
||||||
@@ -870,9 +832,7 @@ export async function POST(req: Request) {
|
|||||||
scheduleClear(jobId);
|
scheduleClear(jobId);
|
||||||
const lower = errMessage.toLowerCase();
|
const lower = errMessage.toLowerCase();
|
||||||
const statusCode =
|
const statusCode =
|
||||||
lower.includes("login failed") || lower.includes("session")
|
lower.includes("login failed") || lower.includes("session") ? 401 : 400;
|
||||||
? 401
|
|
||||||
: 400;
|
|
||||||
return new Response(JSON.stringify({ error: errMessage }), {
|
return new Response(JSON.stringify({ error: errMessage }), {
|
||||||
status: statusCode,
|
status: statusCode,
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
|
|||||||
@@ -75,11 +75,7 @@ export async function POST(req: Request) {
|
|||||||
pushProgress();
|
pushProgress();
|
||||||
};
|
};
|
||||||
|
|
||||||
const setPhaseState = (
|
const setPhaseState = (next: string, weight: number, nextTotal?: number) => {
|
||||||
next: string,
|
|
||||||
weight: number,
|
|
||||||
nextTotal?: number,
|
|
||||||
) => {
|
|
||||||
phase = next;
|
phase = next;
|
||||||
currentWeight = weight;
|
currentWeight = weight;
|
||||||
phaseTotal = nextTotal;
|
phaseTotal = nextTotal;
|
||||||
@@ -108,7 +104,7 @@ export async function POST(req: Request) {
|
|||||||
updateOverall(0);
|
updateOverall(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
const withHeartbeat = async <T,>(task: () => Promise<T>) => {
|
const withHeartbeat = async <T>(task: () => Promise<T>) => {
|
||||||
let tick = 0.1;
|
let tick = 0.1;
|
||||||
updatePhaseProgress(tick, 1);
|
updatePhaseProgress(tick, 1);
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
@@ -161,8 +157,7 @@ export async function POST(req: Request) {
|
|||||||
count = await client.countLayer(layer, validated.siruta);
|
count = await client.countLayer(layer, validated.siruta);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const msg =
|
const msg = error instanceof Error ? error.message : "Count error";
|
||||||
error instanceof Error ? error.message : "Count error";
|
|
||||||
if (!msg.toLowerCase().includes("count unavailable")) throw error;
|
if (!msg.toLowerCase().includes("count unavailable")) throw error;
|
||||||
}
|
}
|
||||||
updatePhaseProgress(2, 2);
|
updatePhaseProgress(2, 2);
|
||||||
@@ -187,9 +182,7 @@ export async function POST(req: Request) {
|
|||||||
onProgress: (value, totalCount) => {
|
onProgress: (value, totalCount) => {
|
||||||
updatePhaseProgress(
|
updatePhaseProgress(
|
||||||
value,
|
value,
|
||||||
typeof totalCount === "number"
|
typeof totalCount === "number" ? totalCount : value + pageSize,
|
||||||
? totalCount
|
|
||||||
: value + pageSize,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -200,16 +193,11 @@ export async function POST(req: Request) {
|
|||||||
onProgress: (value, totalCount) => {
|
onProgress: (value, totalCount) => {
|
||||||
updatePhaseProgress(
|
updatePhaseProgress(
|
||||||
value,
|
value,
|
||||||
typeof totalCount === "number"
|
typeof totalCount === "number" ? totalCount : value + pageSize,
|
||||||
? totalCount
|
|
||||||
: value + pageSize,
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
updatePhaseProgress(
|
updatePhaseProgress(features.length, count ?? features.length);
|
||||||
features.length,
|
|
||||||
count ?? features.length,
|
|
||||||
);
|
|
||||||
finishPhase();
|
finishPhase();
|
||||||
|
|
||||||
/* Fields */
|
/* Fields */
|
||||||
@@ -274,9 +262,7 @@ export async function POST(req: Request) {
|
|||||||
scheduleClear(jobId);
|
scheduleClear(jobId);
|
||||||
const lower = errMessage.toLowerCase();
|
const lower = errMessage.toLowerCase();
|
||||||
const statusCode =
|
const statusCode =
|
||||||
lower.includes("login failed") || lower.includes("session")
|
lower.includes("login failed") || lower.includes("session") ? 401 : 400;
|
||||||
? 401
|
|
||||||
: 400;
|
|
||||||
return new Response(JSON.stringify({ error: errMessage }), {
|
return new Response(JSON.stringify({ error: errMessage }), {
|
||||||
status: statusCode,
|
status: statusCode,
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user