From 4dd67af50392cea3ce3b97d5acd2fa0e4f32264a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=97=D0=B8=D0=BC=D0=B8=D0=BD?= Date: Fri, 15 May 2026 17:03:00 +0000 Subject: [PATCH] =?UTF-8?q?fix(timetravel):=20extend=20slider=20max=20?= =?UTF-8?q?=D0=B4=D0=BE=20now=20+=2030=20days=20(=D0=B8=D0=BB=D0=B8=20futu?= =?UTF-8?q?re=20marks)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../timetravel/TimeTravelPicker.tsx | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/ordinis-admin-ui/src/components/timetravel/TimeTravelPicker.tsx b/ordinis-admin-ui/src/components/timetravel/TimeTravelPicker.tsx index d49d80b..123bbfc 100644 --- a/ordinis-admin-ui/src/components/timetravel/TimeTravelPicker.tsx +++ b/ordinis-admin-ui/src/components/timetravel/TimeTravelPicker.tsx @@ -39,14 +39,32 @@ export function TimeTravelPicker({ return Number.isNaN(d.getTime()) ? undefined : d }, [value]) - // Range: from min(marks, value) − padding до now + padding. + // Range: from min(marks, value) − padding до max(now, future marks) + padding. + // + // Future support: если у dict'а есть records с validFrom > now (scheduled), + // или user navigated к future date (asOf=18.05 для preview new record), + // slider должен дотягиваться до этой даты. Раньше max = now + 1day pad — + // scheduled records на дату дальше 1 день были недоступны через slider + // (user не мог время-travel'ить чтобы их увидеть). + // + // Default future window = 30 days, чтобы планирование на месяц вперёд + // (типичный business horizon для NSI updates) было без ручного URL hack. const range = useMemo(() => { const now = Date.now() - let minMark = marks.length > 0 ? Math.min(...marks) : now - 365 * 86_400_000 - if (valueDate) minMark = Math.min(minMark, valueDate.getTime()) - const span = now - minMark - const pad = Math.max(span * 0.05, 86_400_000) // ≥1 день - return { min: minMark - pad, max: now + pad } + const DAY_MS = 86_400_000 + const FUTURE_WINDOW_MS = 30 * DAY_MS + + let minMark = marks.length > 0 ? Math.min(...marks) : now - 365 * DAY_MS + let maxMark = marks.length > 0 ? Math.max(...marks) : now + if (valueDate) { + minMark = Math.min(minMark, valueDate.getTime()) + maxMark = Math.max(maxMark, valueDate.getTime()) + } + // Push max forward: at least now + 30 days, и further если есть будущие marks. + const effectiveMax = Math.max(maxMark, now + FUTURE_WINDOW_MS) + const span = effectiveMax - minMark + const pad = Math.max(span * 0.05, DAY_MS) // ≥1 день + return { min: minMark - pad, max: effectiveMax + pad } }, [marks, valueDate]) return (