feat(monitor): separate delta test buttons for Cluj-Napoca and Feleacu
- Cluj-Napoca (54975): base mode, parcele+cladiri only (no magic) - Feleacu (57582): magic + no-geom (full enrichment test) - Both with elapsed timer and phase-change logging Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -235,43 +235,29 @@ export default function MonitorPage() {
|
|||||||
loading={actionLoading === "warm-cache"}
|
loading={actionLoading === "warm-cache"}
|
||||||
onClick={triggerWarmCache}
|
onClick={triggerWarmCache}
|
||||||
/>
|
/>
|
||||||
<ActionButton
|
<SyncTestButton
|
||||||
label="Test Delta Sync — Cluj-Napoca"
|
label="Test Delta — Cluj-Napoca (baza)"
|
||||||
description="Testeaza smart delta sync (quick-count + VALID_FROM + rolling doc)"
|
description="Doar sync parcele+cladiri existente, fara magic (54975)"
|
||||||
loading={actionLoading === "delta-test"}
|
siruta="54975"
|
||||||
onClick={async () => {
|
mode="base"
|
||||||
setActionLoading("delta-test");
|
includeNoGeometry={false}
|
||||||
addLog("info", "Pornire delta sync test pe Cluj-Napoca (54975)...");
|
actionKey="delta-cluj-base"
|
||||||
try {
|
actionLoading={actionLoading}
|
||||||
const res = await fetch("/api/eterra/sync-background", {
|
setActionLoading={setActionLoading}
|
||||||
method: "POST",
|
addLog={addLog}
|
||||||
headers: { "Content-Type": "application/json" },
|
pollRef={pollRef}
|
||||||
body: JSON.stringify({ siruta: "54975", mode: "magic", includeNoGeometry: false }),
|
/>
|
||||||
});
|
<SyncTestButton
|
||||||
const d = await res.json() as { jobId?: string; error?: string };
|
label="Test Delta — Feleacu (magic complet)"
|
||||||
if (!res.ok) { addLog("error", d.error ?? "Eroare start"); setActionLoading(""); return; }
|
description="Magic + no-geom pe Feleacu care are deja enrichment (57582)"
|
||||||
addLog("ok", `Job pornit: ${d.jobId}`);
|
siruta="57582"
|
||||||
// Poll progress
|
mode="magic"
|
||||||
const jid = d.jobId;
|
includeNoGeometry={true}
|
||||||
if (pollRef.current) clearInterval(pollRef.current);
|
actionKey="delta-feleacu-magic"
|
||||||
pollRef.current = setInterval(async () => {
|
actionLoading={actionLoading}
|
||||||
try {
|
setActionLoading={setActionLoading}
|
||||||
const pr = await fetch(`/api/eterra/progress?jobId=${jid}`);
|
addLog={addLog}
|
||||||
const pg = await pr.json() as { status?: string; phase?: string; downloaded?: number; total?: number; note?: string; message?: string };
|
pollRef={pollRef}
|
||||||
const pct = pg.total ? Math.round(((pg.downloaded ?? 0) / pg.total) * 100) : 0;
|
|
||||||
addLog("wait", `${pg.phase ?? "..."} (${pct}%)${pg.note ? " — " + pg.note : ""}`);
|
|
||||||
if (pg.status === "done" || pg.status === "error") {
|
|
||||||
addLog(pg.status === "done" ? "ok" : "error", pg.message ?? pg.phase ?? "Finalizat");
|
|
||||||
if (pollRef.current) { clearInterval(pollRef.current); pollRef.current = null; }
|
|
||||||
setActionLoading("");
|
|
||||||
}
|
|
||||||
} catch { /* continue */ }
|
|
||||||
}, 5000);
|
|
||||||
setTimeout(() => {
|
|
||||||
if (pollRef.current) { clearInterval(pollRef.current); pollRef.current = null; addLog("error", "Timeout 30min"); setActionLoading(""); }
|
|
||||||
}, 30 * 60_000);
|
|
||||||
} catch { addLog("error", "Eroare retea"); setActionLoading(""); }
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{logs.length > 0 && (
|
{logs.length > 0 && (
|
||||||
@@ -366,3 +352,82 @@ function ActionButton({ label, description, loading, onClick }: {
|
|||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SyncTestButton({ label, description, siruta, mode, includeNoGeometry, actionKey, actionLoading, setActionLoading, addLog, pollRef }: {
|
||||||
|
label: string; description: string; siruta: string; mode: "base" | "magic";
|
||||||
|
includeNoGeometry: boolean; actionKey: string; actionLoading: string;
|
||||||
|
setActionLoading: (v: string) => void;
|
||||||
|
addLog: (type: "info" | "ok" | "error" | "wait", msg: string) => void;
|
||||||
|
pollRef: React.MutableRefObject<ReturnType<typeof setInterval> | null>;
|
||||||
|
}) {
|
||||||
|
const startTimeRef = useRef<number>(0);
|
||||||
|
const formatElapsed = () => {
|
||||||
|
if (!startTimeRef.current) return "";
|
||||||
|
const s = Math.round((Date.now() - startTimeRef.current) / 1000);
|
||||||
|
return s < 60 ? `${s}s` : `${Math.floor(s / 60)}m${String(s % 60).padStart(2, "0")}s`;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={async () => {
|
||||||
|
setActionLoading(actionKey);
|
||||||
|
startTimeRef.current = Date.now();
|
||||||
|
addLog("info", `[${label}] Pornire sync (${mode}, noGeom=${includeNoGeometry})...`);
|
||||||
|
try {
|
||||||
|
const res = await fetch("/api/eterra/sync-background", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({ siruta, mode, includeNoGeometry }),
|
||||||
|
});
|
||||||
|
const d = await res.json() as { jobId?: string; error?: string };
|
||||||
|
if (!res.ok) {
|
||||||
|
addLog("error", `[${label}] ${d.error ?? "Eroare start"}`);
|
||||||
|
setActionLoading(""); return;
|
||||||
|
}
|
||||||
|
addLog("ok", `[${label}] Job: ${d.jobId?.slice(0, 8)}`);
|
||||||
|
const jid = d.jobId;
|
||||||
|
let lastPhase = "";
|
||||||
|
if (pollRef.current) clearInterval(pollRef.current);
|
||||||
|
pollRef.current = setInterval(async () => {
|
||||||
|
try {
|
||||||
|
const pr = await fetch(`/api/eterra/progress?jobId=${jid}`);
|
||||||
|
const pg = await pr.json() as { status?: string; phase?: string; downloaded?: number; total?: number; note?: string; message?: string };
|
||||||
|
const pct = pg.total ? Math.round(((pg.downloaded ?? 0) / pg.total) * 100) : 0;
|
||||||
|
const elapsed = formatElapsed();
|
||||||
|
const phaseChanged = pg.phase !== lastPhase;
|
||||||
|
if (phaseChanged) lastPhase = pg.phase ?? "";
|
||||||
|
// Only log phase changes and completion to keep log clean
|
||||||
|
if (phaseChanged || pg.status === "done" || pg.status === "error") {
|
||||||
|
const noteStr = pg.note ? ` — ${pg.note}` : "";
|
||||||
|
addLog(
|
||||||
|
pg.status === "done" ? "ok" : pg.status === "error" ? "error" : "wait",
|
||||||
|
`[${label}] ${elapsed} | ${pg.phase ?? "..."} (${pct}%)${noteStr}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (pg.status === "done" || pg.status === "error") {
|
||||||
|
const totalTime = formatElapsed();
|
||||||
|
addLog(pg.status === "done" ? "ok" : "error",
|
||||||
|
`[${label}] TOTAL: ${totalTime}${pg.message ? " — " + pg.message : ""}`,
|
||||||
|
);
|
||||||
|
if (pollRef.current) { clearInterval(pollRef.current); pollRef.current = null; }
|
||||||
|
setActionLoading("");
|
||||||
|
}
|
||||||
|
} catch { /* continue */ }
|
||||||
|
}, 3000);
|
||||||
|
setTimeout(() => {
|
||||||
|
if (pollRef.current) {
|
||||||
|
clearInterval(pollRef.current); pollRef.current = null;
|
||||||
|
addLog("error", `[${label}] Timeout 60min (${formatElapsed()})`);
|
||||||
|
setActionLoading("");
|
||||||
|
}
|
||||||
|
}, 60 * 60_000);
|
||||||
|
} catch { addLog("error", `[${label}] Eroare retea`); setActionLoading(""); }
|
||||||
|
}}
|
||||||
|
disabled={!!actionLoading}
|
||||||
|
className="flex flex-col items-start px-4 py-3 rounded-lg border border-border hover:border-primary/50 hover:bg-primary/5 transition-colors disabled:opacity-50 text-left"
|
||||||
|
>
|
||||||
|
<span className="font-medium text-sm">{actionLoading === actionKey ? "Se ruleaza..." : label}</span>
|
||||||
|
<span className="text-xs text-muted-foreground">{description}</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user