Fix flights map calendar lower bound

This commit is contained in:
2026-05-06 14:10:31 +03:00
parent eda44d4218
commit 385a6e55ee
5 changed files with 77 additions and 5 deletions
@@ -40,6 +40,25 @@ describe("getMinDate / getMaxDate", () => {
const max = getMaxDate(); const max = getMaxDate();
expect(max.getTime()).toBe(expected.getTime()); expect(max.getTime()).toBe(expected.getTime());
}); });
it("derives date-only bounds from the provided yyyyMMdd anchor", () => {
const min = getMinDate("20260506");
const max = getMaxDate("20260506");
expect(min.toISOString()).toBe(new Date(2026, 4, 5).toISOString());
expect(max.toISOString()).toBe(new Date(2026, 10, 6).toISOString());
expect(min.getHours()).toBe(0);
expect(max.getHours()).toBe(0);
});
it("falls back to the runtime clock when the provided anchor is invalid", () => {
const min = getMinDate("20269999");
const expected = addDays(new Date(), -1);
expect(min.getFullYear()).toBe(expected.getFullYear());
expect(min.getMonth()).toBe(expected.getMonth());
expect(min.getDate()).toBe(expected.getDate());
});
}); });
describe("buildDisabledDates", () => { describe("buildDisabledDates", () => {
+34 -3
View File
@@ -12,19 +12,33 @@
import { mapWindowBounds } from "@/shared/dateWindow.js"; import { mapWindowBounds } from "@/shared/dateWindow.js";
/** Today with time set to 00:00:00 local. */ /** Today with time set to 00:00:00 local. */
export function today(): Date { export function today(baseYyyymmdd?: string): Date {
if (baseYyyymmdd) {
const parsed = parseYyyymmdd(baseYyyymmdd);
if (parsed) return parsed;
}
const d = new Date(); const d = new Date();
d.setHours(0, 0, 0, 0); d.setHours(0, 0, 0, 0);
return d; return d;
} }
/** minDate = today - 1 day (Angular parity). */ /** minDate = today - 1 day (Angular parity). */
export function getMinDate(): Date { export function getMinDate(baseYyyymmdd?: string): Date {
if (baseYyyymmdd) {
const d = today(baseYyyymmdd);
d.setDate(d.getDate() - 1);
return d;
}
return mapWindowBounds()[0]; return mapWindowBounds()[0];
} }
/** maxDate = today + 6 months (Angular parity). */ /** maxDate = today + 6 months (Angular parity). */
export function getMaxDate(): Date { export function getMaxDate(baseYyyymmdd?: string): Date {
if (baseYyyymmdd) {
const d = today(baseYyyymmdd);
d.setMonth(d.getMonth() + 6);
return d;
}
return mapWindowBounds()[1]; return mapWindowBounds()[1];
} }
@@ -92,3 +106,20 @@ function toYyyymmdd(d: Date): string {
const day = d.getDate().toString().padStart(2, "0"); const day = d.getDate().toString().padStart(2, "0");
return `${y}${m}${day}`; return `${y}${m}${day}`;
} }
function parseYyyymmdd(value: string): Date | null {
if (!/^\d{8}$/.test(value)) return null;
const y = Number(value.slice(0, 4));
const m = Number(value.slice(4, 6));
const d = Number(value.slice(6, 8));
const parsed = new Date(y, m - 1, d);
parsed.setHours(0, 0, 0, 0);
if (
parsed.getFullYear() !== y ||
parsed.getMonth() !== m - 1 ||
parsed.getDate() !== d
) {
return null;
}
return parsed;
}
@@ -92,6 +92,25 @@ describe("FlightsMapFilter — Calendar wiring", () => {
expect(max.getTime()).toBe(expectedMax.getTime()); expect(max.getTime()).toBe(expectedMax.getTime());
}); });
it("uses the provided SSR today anchor for date-only calendar bounds", () => {
const onChange = vi.fn();
render(
<FlightsMapFilter
value={filter()}
today="20260506"
onChange={onChange}
/>,
);
const min = lastCalendarProps!["minDate"] as Date;
const max = lastCalendarProps!["maxDate"] as Date;
expect(min.toISOString()).toBe(new Date(2026, 4, 5).toISOString());
expect(max.toISOString()).toBe(new Date(2026, 10, 6).toISOString());
expect(min.getHours()).toBe(0);
expect(min.getMinutes()).toBe(0);
});
it("disables every date when availableDays is empty", () => { it("disables every date when availableDays is empty", () => {
const onChange = vi.fn(); const onChange = vi.fn();
render( render(
@@ -25,6 +25,7 @@ import type { IFlightsMapFilterState } from "../types.js";
export interface FlightsMapFilterProps { export interface FlightsMapFilterProps {
value: IFlightsMapFilterState; value: IFlightsMapFilterState;
availableDays?: string[]; availableDays?: string[];
today?: string;
onChange: (state: IFlightsMapFilterState) => void; onChange: (state: IFlightsMapFilterState) => void;
} }
@@ -50,6 +51,7 @@ function dateToYyyymmdd(value: Date): string {
export const FlightsMapFilter: FC<FlightsMapFilterProps> = ({ export const FlightsMapFilter: FC<FlightsMapFilterProps> = ({
value, value,
availableDays, availableDays,
today: todayYmd,
onChange, onChange,
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -76,8 +78,8 @@ export const FlightsMapFilter: FC<FlightsMapFilterProps> = ({
); );
}, [dictionaries, onChange, value]); }, [dictionaries, onChange, value]);
const minDate = useMemo(() => getMinDate(), []); const minDate = useMemo(() => getMinDate(todayYmd), [todayYmd]);
const maxDate = useMemo(() => getMaxDate(), []); const maxDate = useMemo(() => getMaxDate(todayYmd), [todayYmd]);
const disabledDates = useMemo( const disabledDates = useMemo(
() => buildDisabledDates(minDate, maxDate, availableDays ?? []), () => buildDisabledDates(minDate, maxDate, availableDays ?? []),
@@ -393,6 +393,7 @@ export const FlightsMapStartPage: FC<FlightsMapStartPageProps> = ({
<FlightsMapFilter <FlightsMapFilter
value={filterState} value={filterState}
availableDays={availableDays} availableDays={availableDays}
today={todayYmd}
onChange={handleFilterChange} onChange={handleFilterChange}
/> />
} }