Revert "Convert schedule WeekTabs to day-of-week strip"

This reverts commit c097ab21fe.
This commit is contained in:
2026-04-20 01:38:12 +03:00
parent c097ab21fe
commit 4095344b7b
3 changed files with 71 additions and 109 deletions
@@ -297,16 +297,6 @@ export const ScheduleSearchPage: FC<ScheduleSearchPageProps> = ({ params }) => {
<WeekTabs
selectedMonday={selectedMonday}
onNavigate={handleWeekChange}
onSelectDay={(dayYmd) => {
// 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 && (
<div
+3 -18
View File
@@ -36,19 +36,15 @@
&__tab {
flex: 1;
min-width: 0;
padding: 6px 4px;
padding: 8px 12px;
background: transparent;
border: none;
font-size: 13px;
color: #1f6fb8;
cursor: pointer;
border-radius: 4px;
white-space: nowrap;
transition: background 120ms;
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
line-height: 1.1;
&:hover { background: #e8f0fa; }
@@ -57,18 +53,7 @@
color: #022040;
font-weight: 600;
box-shadow: inset 0 -2px 0 #2563eb;
cursor: default;
}
}
&__day-num {
font-size: 15px;
font-weight: 600;
}
&__day-name {
font-size: 11px;
text-transform: lowercase;
color: inherit;
opacity: 0.85;
}
}
+68 -81
View File
@@ -1,35 +1,46 @@
/**
* Day-of-week tabs for the schedule pages.
* Weekly date-range tabs for the schedule pages.
*
* Mirrors Angular's schedule day strip (`20 пн 21 вт 22 ср …`) — seven
* tabs spanning MonSun of the active week. Prev/next navigates by
* whole weeks; clicking a day highlights it and (optionally) scrolls
* the corresponding day group into view via `onSelectDay`.
* Mirrors Angular's `week-tabs` (Monday-anchored 7-day windows shown
* as `13 апр - 19 апр`). Distinct from the onlineboard's `DayTabs`
* which is daily.
*
* Public API: still keyed off `selectedMonday` (yyyy-MM-dd Monday) and
* `onNavigate(mondayYmd)` so the parent's URL plumbing keeps working.
* Emits the Monday yyyy-MM-dd of the chosen week on click.
*/
import { type FC, useMemo } from "react";
import { type FC, useMemo, useState } from "react";
import { useLocale } from "@/i18n/useLocale.js";
import "./WeekTabs.scss";
const PAGE_SIZE = 7;
const WEEKS_BEFORE = 1;
const WEEKS_AFTER = 30;
export interface WeekTabsProps {
/** Monday yyyy-MM-dd of the currently active week. */
selectedMonday: string;
/** Fired with the Monday yyyy-MM-dd of the chosen week (prev/next). */
/** Fired with the Monday yyyy-MM-dd of the chosen week. */
onNavigate: (mondayYmd: string) => 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 DayEntry {
date: Date;
interface WeekEntry {
monday: Date;
sunday: Date;
ymd: string;
dayNum: string;
weekday: 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(/\.$/, "");
}
function ymd(d: Date): string {
@@ -39,67 +50,44 @@ function ymd(d: Date): string {
return `${y}-${m}-${day}`;
}
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<WeekTabsProps> = ({
selectedMonday,
onNavigate,
selectedDayYmd,
onSelectDay,
}) => {
export const WeekTabs: FC<WeekTabsProps> = ({ selectedMonday, onNavigate }) => {
const { language } = useLocale();
// 'пн', 'вт', … — 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" }),
// 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" }),
[language],
);
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);
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);
out.push({
date: d,
ymd: ymd(d),
dayNum: String(d.getDate()),
weekday: weekdayFmt.format(d).replace(/\.$/, "").toLowerCase(),
monday,
sunday,
ymd: ymd(monday),
label: `${fmt(monday, dayMonthFmt)} - ${fmt(sunday, dayMonthFmt)}`,
});
}
return out;
}, [monday, weekdayFmt]);
}, [dayMonthFmt]);
if (!monday || days.length === 0) {
return null;
}
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));
const today = todayYmd();
const highlight = selectedDayYmd && days.some((d) => d.ymd === selectedDayYmd)
? selectedDayYmd
: days.some((d) => d.ymd === today)
? today
: days[0]!.ymd;
const visible = weeks.slice(page * PAGE_SIZE, page * PAGE_SIZE + PAGE_SIZE);
return (
<nav
@@ -110,27 +98,25 @@ export const WeekTabs: FC<WeekTabsProps> = ({
<button
type="button"
className="week-tabs__nav week-tabs__nav--prev"
onClick={() => onNavigate(shiftWeek(monday, -1))}
aria-label="Previous week"
disabled={page === 0}
onClick={() => setPage((p) => Math.max(0, p - 1))}
aria-label="Previous week range"
>
</button>
<div className="week-tabs__list">
{days.map((d) => {
const isActive = d.ymd === highlight;
{visible.map((w) => {
const isActive = w.ymd === selectedMonday;
return (
<button
key={d.ymd}
key={w.ymd}
type="button"
className={`week-tabs__tab${isActive ? " week-tabs__tab--active" : ""}`}
data-active={isActive ? "true" : "false"}
data-testid={`week-tab-${d.ymd}`}
onClick={() => {
if (onSelectDay) onSelectDay(d.ymd);
}}
data-testid={`week-tab-${w.ymd}`}
onClick={() => onNavigate(w.ymd)}
>
<span className="week-tabs__day-num">{d.dayNum}</span>
<span className="week-tabs__day-name">{d.weekday}</span>
{w.label}
</button>
);
})}
@@ -138,8 +124,9 @@ export const WeekTabs: FC<WeekTabsProps> = ({
<button
type="button"
className="week-tabs__nav week-tabs__nav--next"
onClick={() => onNavigate(shiftWeek(monday, 1))}
aria-label="Next week"
disabled={page >= totalPages - 1}
onClick={() => setPage((p) => Math.min(totalPages - 1, p + 1))}
aria-label="Next week range"
>
</button>