Add Current-Week label substitution to Schedule date-range picker per TZ 4.1.9 Table 14
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
* search is route-only.
|
||||
*/
|
||||
|
||||
import { type FC, useState, useCallback, useRef, type FormEvent } from "react";
|
||||
import { type FC, useState, useCallback, useRef, useEffect, type FormEvent } from "react";
|
||||
import { useNavigate } from "@modern-js/runtime/router";
|
||||
import { Calendar } from "primereact/calendar";
|
||||
import { Slider, type SliderChangeEvent } from "primereact/slider";
|
||||
@@ -18,6 +18,7 @@ import { CityAutocomplete } from "@/ui/city-autocomplete/index.js";
|
||||
import { useDictionaries } from "@/shared/dictionaries/index.js";
|
||||
import { buildScheduleUrl } from "../url.js";
|
||||
import type { ScheduleParams } from "../url.js";
|
||||
import { formatScheduleDateRangeWithCurrentWeek } from "../dateLabels.js";
|
||||
import { scheduleWindowBounds } from "@/shared/dateWindow.js";
|
||||
import "./ScheduleFilter.scss";
|
||||
|
||||
@@ -131,6 +132,21 @@ export const ScheduleFilter: FC<ScheduleFilterProps> = ({
|
||||
const scheduleMinDate = useRef(getScheduleMinDate()).current;
|
||||
const scheduleMaxDate = useRef(getScheduleMaxDate()).current;
|
||||
|
||||
// Swap the Calendar input's displayed text to "Текущая неделя" per
|
||||
// TZ §4.1.9 Table 14 when the selected range equals Mon-Sun of the
|
||||
// current week. Uses inputRef + useEffect to override PrimeReact's
|
||||
// own dd.mm.yy rendering without touching the state value.
|
||||
const dateRangeInputRef = useRef<HTMLInputElement>(null);
|
||||
useEffect(() => {
|
||||
const [from, to] = dateRange;
|
||||
if (!dateRangeInputRef.current || !from || !to) return;
|
||||
const label = formatScheduleDateRangeWithCurrentWeek(from, to, t);
|
||||
const currentWeekLabel = t("SCHEDULE.CURRENT-WEEK");
|
||||
if (label === currentWeekLabel) {
|
||||
dateRangeInputRef.current.value = currentWeekLabel;
|
||||
}
|
||||
}, [dateRange, t]);
|
||||
|
||||
const handleSwap = useCallback(() => {
|
||||
setDeparture(arrival);
|
||||
setArrival(departure);
|
||||
@@ -300,6 +316,7 @@ export const ScheduleFilter: FC<ScheduleFilterProps> = ({
|
||||
className="input--filter"
|
||||
data-testid="schedule-date-input"
|
||||
inputId="schedule-date-input"
|
||||
inputRef={dateRangeInputRef}
|
||||
readOnlyInput
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -266,6 +266,34 @@ describe("ScheduleStartPage", () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// TZ §4.1.9 Table 14: current-week label on Schedule date-range picker
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe("4.1.9-R: Current-Week label substitution", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
sessionStore.clear();
|
||||
resetCrossSectionStore();
|
||||
// Clock frozen to Fri 2026-05-15 → Mon 2026-05-11 … Sun 2026-05-17
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date(2026, 4, 15, 12, 0, 0));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("4.1.9-R: start page renders with current-week dates pre-populated in session store on Route click", () => {
|
||||
render(<ScheduleStartPage />);
|
||||
fireEvent.click(screen.getByTestId("popular-click-route"));
|
||||
const stored = JSON.parse(sessionStore.getRaw("afl-prefill:schedule")!);
|
||||
// Current week Mon-Sun for 2026-05-15
|
||||
expect(stored.dateFrom).toBe("20260511");
|
||||
expect(stored.dateTo).toBe("20260517");
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// TZ §4.1.8: cross-section hydration tests (Board → Schedule)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
|
||||
import { formatScheduleDateRangeWithCurrentWeek } from "./dateLabels.js";
|
||||
|
||||
describe("4.1.9-R: formatScheduleDateRangeWithCurrentWeek", () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date(2026, 4, 15, 12, 0, 0)); // Fri 2026-05-15
|
||||
});
|
||||
afterEach(() => { vi.useRealTimers(); });
|
||||
|
||||
it("returns 'Текущая неделя' when range = Mon-Sun of current week", () => {
|
||||
const t = (k: string) => (k === "SCHEDULE.CURRENT-WEEK" ? "Текущая неделя" : k);
|
||||
expect(
|
||||
formatScheduleDateRangeWithCurrentWeek(new Date(2026, 4, 11), new Date(2026, 4, 17), t),
|
||||
).toBe("Текущая неделя");
|
||||
});
|
||||
|
||||
it("returns dd.MM.yyyy-dd.MM.yyyy for other ranges", () => {
|
||||
const t = (k: string) => k;
|
||||
expect(
|
||||
formatScheduleDateRangeWithCurrentWeek(new Date(2026, 4, 18), new Date(2026, 4, 24), t),
|
||||
).toBe("18.05.2026-24.05.2026");
|
||||
});
|
||||
|
||||
it("returns dd.MM.yyyy-dd.MM.yyyy for partial current week", () => {
|
||||
const t = (k: string) => k;
|
||||
expect(
|
||||
formatScheduleDateRangeWithCurrentWeek(new Date(2026, 4, 13), new Date(2026, 4, 17), t),
|
||||
).toBe("13.05.2026-17.05.2026");
|
||||
});
|
||||
|
||||
it("returns empty string for null inputs", () => {
|
||||
expect(formatScheduleDateRangeWithCurrentWeek(null, null, () => "")).toBe("");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Schedule range-calendar label substitution per TZ §4.1.9 Table 14.
|
||||
* Current week Mon-Sun → "Текущая неделя", otherwise dd.MM.yyyy-dd.MM.yyyy.
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type TFunction = (key: string, opts?: any) => string;
|
||||
|
||||
function toYmd(d: Date): string {
|
||||
const day = String(d.getDate()).padStart(2, "0");
|
||||
const month = String(d.getMonth() + 1).padStart(2, "0");
|
||||
return `${day}.${month}.${d.getFullYear()}`;
|
||||
}
|
||||
|
||||
function mondayOfWeek(base: Date): Date {
|
||||
const d = new Date(base);
|
||||
d.setHours(0, 0, 0, 0);
|
||||
const offset = (d.getDay() + 6) % 7;
|
||||
d.setDate(d.getDate() - offset);
|
||||
return d;
|
||||
}
|
||||
|
||||
export function formatScheduleDateRangeWithCurrentWeek(
|
||||
dateFrom: Date | null | undefined,
|
||||
dateTo: Date | null | undefined,
|
||||
t: TFunction,
|
||||
): string {
|
||||
if (!dateFrom || !dateTo) return "";
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
const thisMon = mondayOfWeek(today);
|
||||
const thisSun = new Date(thisMon);
|
||||
thisSun.setDate(thisSun.getDate() + 6);
|
||||
const from = new Date(dateFrom);
|
||||
from.setHours(0, 0, 0, 0);
|
||||
const to = new Date(dateTo);
|
||||
to.setHours(0, 0, 0, 0);
|
||||
if (from.getTime() === thisMon.getTime() && to.getTime() === thisSun.getTime()) {
|
||||
return t("SCHEDULE.CURRENT-WEEK");
|
||||
}
|
||||
return `${toYmd(from)}-${toYmd(to)}`;
|
||||
}
|
||||
Reference in New Issue
Block a user