diff --git a/src/features/schedule/components/ScheduleSearchPage.tsx b/src/features/schedule/components/ScheduleSearchPage.tsx index 0c3f9e14..e0c37f36 100644 --- a/src/features/schedule/components/ScheduleSearchPage.tsx +++ b/src/features/schedule/components/ScheduleSearchPage.tsx @@ -297,6 +297,16 @@ export const ScheduleSearchPage: FC = ({ params }) => { { + // Day group sections are keyed by `data-day=${ymd}` + // (see DayGroupedFlightList). Scroll the matching one + // into view; this stays a no-op if it's not rendered. + if (typeof document === "undefined") return; + const el = document.querySelector( + `[data-day="${dayYmd}"]`, + ); + if (el) el.scrollIntoView({ behavior: "smooth", block: "start" }); + }} /> {inbound && (
void; + /** Optional yyyy-MM-dd of the highlighted day within the week. */ + selectedDayYmd?: string; + /** Optional callback when a specific day tab is clicked. */ + onSelectDay?: (dayYmd: string) => void; } -interface WeekEntry { - monday: Date; - sunday: Date; +interface DayEntry { + date: Date; ymd: string; - label: string; -} - -function startOfWeekMonday(d: Date): Date { - const out = new Date(d); - const day = out.getDay(); // 0=Sun, 1=Mon, ..., 6=Sat - const diff = (day + 6) % 7; // distance back to Monday - out.setDate(out.getDate() - diff); - out.setHours(0, 0, 0, 0); - return out; -} - -function fmt(date: Date, fmt: Intl.DateTimeFormat): string { - return fmt.format(date).replace(/\.$/, ""); + dayNum: string; + weekday: string; } function ymd(d: Date): string { @@ -50,44 +39,67 @@ function ymd(d: Date): string { return `${y}-${m}-${day}`; } -export const WeekTabs: FC = ({ selectedMonday, onNavigate }) => { +function parseYmd(s: string): Date | null { + if (!s || s.length !== 10) return null; + const y = parseInt(s.slice(0, 4), 10); + const m = parseInt(s.slice(5, 7), 10) - 1; + const d = parseInt(s.slice(8, 10), 10); + if (Number.isNaN(y) || Number.isNaN(m) || Number.isNaN(d)) return null; + return new Date(y, m, d); +} + +function shiftWeek(monday: Date, deltaWeeks: number): string { + const next = new Date(monday); + next.setDate(monday.getDate() + deltaWeeks * 7); + return ymd(next); +} + +function todayYmd(): string { + return ymd(new Date()); +} + +export const WeekTabs: FC = ({ + selectedMonday, + onNavigate, + selectedDayYmd, + onSelectDay, +}) => { const { language } = useLocale(); - // Angular shows month abbreviated ("13 апр - 19 апр"). Build once - // per locale; the month part comes through in the locale's natural - // short form. - const dayMonthFmt = useMemo( - () => new Intl.DateTimeFormat(language, { day: "numeric", month: "short" }), + // 'пн', 'вт', … — short weekday in active locale. Built once per + // locale; the `.replace(/\.$/, "")` strips Russian's trailing dot. + const weekdayFmt = useMemo( + () => new Intl.DateTimeFormat(language, { weekday: "short" }), [language], ); - const weeks: WeekEntry[] = useMemo(() => { - const out: WeekEntry[] = []; - const today = new Date(); - const start = startOfWeekMonday(today); - start.setDate(start.getDate() - WEEKS_BEFORE * 7); - for (let i = 0; i < WEEKS_BEFORE + WEEKS_AFTER + 1; i++) { - const monday = new Date(start); - monday.setDate(start.getDate() + i * 7); - const sunday = new Date(monday); - sunday.setDate(monday.getDate() + 6); + const monday = useMemo(() => parseYmd(selectedMonday), [selectedMonday]); + + const days: DayEntry[] = useMemo(() => { + if (!monday) return []; + const out: DayEntry[] = []; + for (let i = 0; i < 7; i++) { + const d = new Date(monday); + d.setDate(monday.getDate() + i); out.push({ - monday, - sunday, - ymd: ymd(monday), - label: `${fmt(monday, dayMonthFmt)} - ${fmt(sunday, dayMonthFmt)}`, + date: d, + ymd: ymd(d), + dayNum: String(d.getDate()), + weekday: weekdayFmt.format(d).replace(/\.$/, "").toLowerCase(), }); } return out; - }, [dayMonthFmt]); + }, [monday, weekdayFmt]); - const initialPage = Math.max( - 0, - Math.floor(weeks.findIndex((w) => w.ymd === selectedMonday) / PAGE_SIZE), - ); - const [page, setPage] = useState(Number.isFinite(initialPage) ? initialPage : 0); - const totalPages = Math.max(1, Math.ceil(weeks.length / PAGE_SIZE)); + if (!monday || days.length === 0) { + return null; + } - const visible = weeks.slice(page * PAGE_SIZE, page * PAGE_SIZE + PAGE_SIZE); + const today = todayYmd(); + const highlight = selectedDayYmd && days.some((d) => d.ymd === selectedDayYmd) + ? selectedDayYmd + : days.some((d) => d.ymd === today) + ? today + : days[0]!.ymd; return (