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 (