From 1f75bc5ceaffdac3331872e4c64eef74dcf2beec Mon Sep 17 00:00:00 2001 From: Denozordec Date: Thu, 1 Oct 2026 21:47:10 +0700 Subject: [PATCH] =?UTF-8?q?fix(scheduler):=20=D1=80=D0=B0=D0=B7=D0=BB?= =?UTF-8?q?=D0=B8=D1=87=D0=B0=D1=82=D1=8C=20=D0=BD=D0=B5=D0=B8=D0=B7=D0=B2?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D0=BD=D1=83=D1=8E=20=D0=B8=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D1=8E=20=D0=B8=20=D0=BF=D0=BE=D0=B4=D1=82=D0=B2?= =?UTF-8?q?=D0=B5=D1=80=D0=B6=D0=B4=D1=91=D0=BD=D0=BD=D1=8B=D0=B5=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=BF=D1=83=D1=81=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(main)/data-collection/page.tsx | 40 ++++++++++++++++------- docs/ux-ui-plan.md | 2 +- docs/ux-ui-progress.md | 10 +++++- docs/ux-ui-stage-11b.md | 9 +++++ shared/api/scheduler.ts | 13 +++++--- tests/e2e/data-collection-history.spec.ts | 39 ++++++++++++++++++++++ 6 files changed, 95 insertions(+), 18 deletions(-) create mode 100644 tests/e2e/data-collection-history.spec.ts diff --git a/app/(main)/data-collection/page.tsx b/app/(main)/data-collection/page.tsx index f1b5a252..ce5aad20 100644 --- a/app/(main)/data-collection/page.tsx +++ b/app/(main)/data-collection/page.tsx @@ -25,6 +25,7 @@ import { type SchedulerRunRowDto, type UptimeSettingsDto, } from "@/lib/scheduler-settings" +import { listSchedulerRuns } from "@/shared/api/scheduler" import { requestJson, ApiClientError } from "@/shared/api/http-client" import { parseSchedulerRunSnapshot, @@ -585,6 +586,9 @@ function DataCollectionContent() { const [certRenewIntervalDraft, setCertRenewIntervalDraft] = useState("21600") const [renewBeforeDaysDraft, setRenewBeforeDaysDraft] = useState("30") const [schedulerRuns, setSchedulerRuns] = useState([]) + const [runsKnown, setRunsKnown] = useState(false) + const [runsError, setRunsError] = useState(null) + const [runsReceivedAt, setRunsReceivedAt] = useState(null) const [runFilterJobKey, setRunFilterJobKey] = useState("") const [runNowJobKey, setRunNowJobKey] = useState(null) const [schedulerSaveBusy, setSchedulerSaveBusy] = useState(false) @@ -623,17 +627,14 @@ function DataCollectionContent() { setCollectorError(null) setPageBusy(true) try { - const runsQuery = - runFilterJobKey && SCHEDULER_JOB_KEYS.includes(runFilterJobKey as (typeof SCHEDULER_JOB_KEYS)[number]) - ? `?limit=80&jobKey=${encodeURIComponent(runFilterJobKey)}` - : "?limit=80" + const runsFilter = runFilterJobKey && SCHEDULER_JOB_KEYS.includes(runFilterJobKey as (typeof SCHEDULER_JOB_KEYS)[number]) ? runFilterJobKey as (typeof SCHEDULER_JOB_KEYS)[number] : undefined const [trafficRes, serversApiRes, uptimeRes, internetPathRes, certRenewRes, runsRes] = await Promise.allSettled([ apiFetch("/api/traffic/settings", { signal }), apiFetch("/api/servers-api-ping/settings", { signal }), apiFetch("/api/uptime/settings", { signal }), apiFetch("/api/internet-path/settings", { signal }), apiFetch<{ enabled: boolean; intervalSec: number; renewBeforeDays: number }>("/api/certificates/renew-settings", { signal }), - apiFetch<{ runs: SchedulerRunRowDto[] }>(`/api/scheduler/runs${runsQuery}`, { signal }), + listSchedulerRuns(backendUrl, { limit: 80, jobKey: runsFilter }, { signal }), ]) if (!current()) return @@ -699,14 +700,17 @@ function DataCollectionContent() { const runsPayload = readSettled(runsRes) if (runsPayload) { - setSchedulerRuns(runsPayload.runs ?? []) + setSchedulerRuns(runsPayload.runs) + setRunsKnown(true); setRunsError(null); setRunsReceivedAt(new Date().toISOString()) + } else if (runsRes.status === "rejected") { + setRunsError(extractApiError(runsRes.reason)) } } catch (e) { if (current()) setCollectorError(e instanceof Error ? e.message : "Не удалось загрузить данные") } finally { if (current()) setPageBusy(false) } - }, [apiFetch, isLive, runFilterJobKey]) + }, [apiFetch, backendUrl, isLive, runFilterJobKey]) const persistSchedulerDrafts = useCallback(async (overrides: SchedulerToggleOverrides = {}) => { const trafficEnabled = overrides.trafficEnabled ?? draftTrafficEnabled @@ -1041,8 +1045,8 @@ function DataCollectionContent() { }, { label: "Журнал (в списке)", - value: String(schedulerRuns.length), - sub: errorRunsInView ? `${errorRunsInView} с ошибкой` : "ошибок в показанных — нет", + value: runsKnown ? String(schedulerRuns.length) : "—", + sub: runsError ? (runsKnown ? "Показана последняя подтверждённая история" : "История не получена") : runsKnown ? (errorRunsInView ? `${errorRunsInView} с ошибкой` : "ошибок в показанных — нет") : "Ожидание истории", icon: , }, ] @@ -1260,7 +1264,7 @@ function DataCollectionContent() {