fix(parcel-sync): add 2min timeout to no-geom scan, non-blocking UI

- Server: Promise.race with 120s timeout on no-geom-scan API route
- Client: AbortController with 120s timeout on scan fetch
- UI: show 'max 2 min' during scanning + hint that buttons work without scan
- UI: timeout state shows retry button + explains no-geom won't be available
- Prevents indefinitely stuck 'Se scanează...' on slow eTerra responses
This commit is contained in:
AI Assistant
2026-03-08 02:28:51 +02:00
parent e57ca88e7e
commit d12f01fc02
2 changed files with 63 additions and 4 deletions
@@ -742,6 +742,9 @@ export function ParcelSyncModule() {
scannedAt: "",
};
try {
// 2min timeout — scan is informational, should not block the page
const scanAbort = new AbortController();
const scanTimer = setTimeout(() => scanAbort.abort(), 120_000);
const res = await fetch("/api/eterra/no-geom-scan", {
method: "POST",
headers: { "Content-Type": "application/json" },
@@ -749,7 +752,9 @@ export function ParcelSyncModule() {
siruta: s,
workspacePk: workspacePk ?? undefined,
}),
signal: scanAbort.signal,
});
clearTimeout(scanTimer);
const data = (await res.json()) as Record<string, unknown>;
if (data.error) {
console.warn("[no-geom-scan]", data.error);
@@ -783,8 +788,19 @@ export function ParcelSyncModule() {
scannedAt: String(data.scannedAt ?? new Date().toISOString()),
});
}
} catch {
setNoGeomScan(emptyResult);
} catch (err) {
// Distinguish timeout from other errors for the user
const isTimeout =
err instanceof DOMException && err.name === "AbortError";
if (isTimeout) {
console.warn(
"[no-geom-scan] Timeout after 2 min — server eTerra lent",
);
}
setNoGeomScan({
...emptyResult,
scannedAt: isTimeout ? "timeout" : "",
});
}
setNoGeomScanning(false);
},
@@ -2642,8 +2658,37 @@ export function ParcelSyncModule() {
<CardContent className="py-3 px-4">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<Loader2 className="h-4 w-4 animate-spin text-amber-500" />
Se scanează lista de imobile din eTerra
Se scanează lista de imobile din eTerra (max 2 min)
</div>
<p className="text-[11px] text-muted-foreground mt-1 ml-6">
Poți folosi butoanele de mai jos fără aștepți scanarea.
</p>
</CardContent>
</Card>
);
// Scan timed out
if (scanDone && noGeomScan.scannedAt === "timeout")
return (
<Card className="border-amber-200/50 dark:border-amber-800/50">
<CardContent className="py-3 px-4">
<div className="flex items-center gap-2 text-sm text-amber-600 dark:text-amber-400">
<Clock className="h-4 w-4 shrink-0" />
Scanarea a depășit 2 minute serverul eTerra e lent.
</div>
<p className="text-[11px] text-muted-foreground mt-1 ml-6">
Poți lansa sincronizarea fundal fără rezultate de scanare.
Include no-geom nu va fi disponibil.
</p>
<Button
variant="ghost"
size="sm"
className="h-6 text-xs mt-1 ml-6"
onClick={() => void handleNoGeomScan()}
>
<RefreshCw className="h-3 w-3 mr-1" />
Reîncearcă scanarea
</Button>
</CardContent>
</Card>
);