Auto-expand today's day group + dynamic dates in visual diff
Schedule list day accordion stays collapsed by default but auto- opens the day matching today's date when it's in the visible week window — mirrors Angular's p-accordion default-active behaviour where today's flights are visible without a click. The user can still collapse it; we never re-open after that for the same date. Visual-diff URLs were hardcoded to a past date with the wrong React URL format (Angular path-style /AAQ/16042026 instead of React's single-segment /AAQ-20260420-00002400). Switch to dynamic yyyyMMdd of today for onlineboard pages and Mon→Sun of the current week for schedule. Schedule-route diff dropped from ~91% to ~28% on desktop after these two fixes.
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
* (e.g. single-day search) — no header noise.
|
||||
*/
|
||||
|
||||
import { type FC, useCallback, useMemo, useState } from "react";
|
||||
import { type FC, useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "@/i18n/provider.js";
|
||||
import { FlightList } from "@/ui/flights/FlightList.js";
|
||||
import { FlightListSkeleton } from "@/ui/flights/FlightListSkeleton.js";
|
||||
@@ -121,15 +121,33 @@ export const DayGroupedFlightList: FC<DayGroupedFlightListProps> = ({
|
||||
const { language } = useLocale();
|
||||
const { t } = useTranslation();
|
||||
const [sortMode, setSortMode] = useState<SortMode>("none");
|
||||
// Track which days the user has expanded. Default: empty — matches
|
||||
// Angular's `p-accordion` collapsed-on-load behaviour, where the
|
||||
// user clicks a day header to reveal its flights.
|
||||
// Track which days the user has expanded. Default: today's day group
|
||||
// (if it's in scope). Angular's `p-accordion` is `[multiple]="true"`
|
||||
// and `[activeIndex]` defaults to the index of today's date when
|
||||
// present; only the user's clicks deviate after that.
|
||||
const [expandedDays, setExpandedDays] = useState<Set<string>>(() => new Set());
|
||||
const groups = useMemo(() => {
|
||||
const g = groupFlightsByDay(flights);
|
||||
return g.map((day) => ({ ...day, flights: sortFlights(day.flights, sortMode) }));
|
||||
}, [flights, sortMode]);
|
||||
|
||||
// Auto-open today's day group on first render (and when the visible
|
||||
// window shifts). The user can collapse it; we never re-open after
|
||||
// that for the same date.
|
||||
const [autoOpenedFor, setAutoOpenedFor] = useState<string | null>(null);
|
||||
useEffect(() => {
|
||||
const now = new Date();
|
||||
const todayIso = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
|
||||
const todayInScope = groups.some((g) => g.date === todayIso);
|
||||
if (!todayInScope || autoOpenedFor === todayIso) return;
|
||||
setExpandedDays((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.add(todayIso);
|
||||
return next;
|
||||
});
|
||||
setAutoOpenedFor(todayIso);
|
||||
}, [groups, autoOpenedFor]);
|
||||
|
||||
const setSort = (mode: SortMode) => {
|
||||
setSortMode((cur) => (cur === mode ? "none" : mode));
|
||||
};
|
||||
|
||||
@@ -86,6 +86,40 @@ interface RouteEntry {
|
||||
maskSelectors?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* URL conventions
|
||||
*
|
||||
* Angular and React use *different* path separators in their search URLs:
|
||||
* Angular: `/onlineboard/departure/AAQ/16042026-0000-2400` (slash between
|
||||
* station and date, hyphen between dateParts and time range)
|
||||
* React: `/onlineboard/departure/AAQ-16042026-00002400` (one
|
||||
* single-segment param: STATION-yyyyMMdd-HHHHHHHH)
|
||||
*
|
||||
* Schedule URLs share the same `MOW-KUF-yyyyMMdd-yyyyMMdd` shape on both.
|
||||
*
|
||||
* Dates default to today (yyyyMMdd) and the current Monday→Sunday week,
|
||||
* so the diff stays valid across runs and the API actually returns data.
|
||||
* Override via env: TODAY=20260420 / SCHEDULE_FROM=20260420 / SCHEDULE_TO=20260426.
|
||||
*/
|
||||
function ymd(d: Date): string {
|
||||
const y = d.getFullYear();
|
||||
const m = String(d.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(d.getDate()).padStart(2, "0");
|
||||
return `${y}${m}${day}`;
|
||||
}
|
||||
const TODAY_YMD = process.env.TODAY ?? ymd(new Date());
|
||||
const _today = new Date(
|
||||
Number(TODAY_YMD.slice(0, 4)),
|
||||
Number(TODAY_YMD.slice(4, 6)) - 1,
|
||||
Number(TODAY_YMD.slice(6, 8)),
|
||||
);
|
||||
const _monday = new Date(_today);
|
||||
_monday.setDate(_today.getDate() - ((_today.getDay() + 6) % 7));
|
||||
const _sunday = new Date(_monday);
|
||||
_sunday.setDate(_monday.getDate() + 6);
|
||||
const SCHEDULE_FROM = process.env.SCHEDULE_FROM ?? ymd(_monday);
|
||||
const SCHEDULE_TO = process.env.SCHEDULE_TO ?? ymd(_sunday);
|
||||
|
||||
const ROUTES: RouteEntry[] = [
|
||||
{
|
||||
name: "onlineboard-start",
|
||||
@@ -94,32 +128,32 @@ const ROUTES: RouteEntry[] = [
|
||||
},
|
||||
{
|
||||
name: "onlineboard-departure",
|
||||
angular: "/onlineboard/departure/AAQ/16042026-0000-2400",
|
||||
react: "/ru/onlineboard/departure/AAQ/16042026-0000-2400",
|
||||
angular: `/onlineboard/departure/AAQ/${TODAY_YMD}-0000-2400`,
|
||||
react: `/ru/onlineboard/departure/AAQ-${TODAY_YMD}-00002400`,
|
||||
waitMs: 3000,
|
||||
},
|
||||
{
|
||||
name: "onlineboard-arrival",
|
||||
angular: "/onlineboard/arrival/AAQ/16042026-0000-2400",
|
||||
react: "/ru/onlineboard/arrival/AAQ/16042026-0000-2400",
|
||||
angular: `/onlineboard/arrival/AAQ/${TODAY_YMD}-0000-2400`,
|
||||
react: `/ru/onlineboard/arrival/AAQ-${TODAY_YMD}-00002400`,
|
||||
waitMs: 3000,
|
||||
},
|
||||
{
|
||||
name: "onlineboard-route",
|
||||
angular: "/onlineboard/route/MOW-AER/16042026-0000-2400",
|
||||
react: "/ru/onlineboard/route/MOW-AER/16042026-0000-2400",
|
||||
angular: `/onlineboard/route/MOW-AER/${TODAY_YMD}-0000-2400`,
|
||||
react: `/ru/onlineboard/route/MOW-AER-${TODAY_YMD}-00002400`,
|
||||
waitMs: 3000,
|
||||
},
|
||||
{
|
||||
name: "onlineboard-flight",
|
||||
angular: "/onlineboard/flight/SU0022/16042026",
|
||||
react: "/ru/onlineboard/flight/SU0022/16042026",
|
||||
angular: `/onlineboard/flight/SU0022/${TODAY_YMD}`,
|
||||
react: `/ru/onlineboard/flight/SU0022-${TODAY_YMD}`,
|
||||
waitMs: 3000,
|
||||
},
|
||||
{
|
||||
name: "onlineboard-details",
|
||||
angular: "/onlineboard/SU0022-16042026",
|
||||
react: "/ru/onlineboard/SU0022-16042026",
|
||||
angular: `/onlineboard/SU0022-${TODAY_YMD}`,
|
||||
react: `/ru/onlineboard/SU0022-${TODAY_YMD}`,
|
||||
waitMs: 3000,
|
||||
},
|
||||
{
|
||||
@@ -129,8 +163,8 @@ const ROUTES: RouteEntry[] = [
|
||||
},
|
||||
{
|
||||
name: "schedule-route",
|
||||
angular: "/schedule/route/MOW-KUF-20260416-20260423",
|
||||
react: "/ru/schedule/route/MOW-KUF-20260416-20260423",
|
||||
angular: `/schedule/route/MOW-KUF-${SCHEDULE_FROM}-${SCHEDULE_TO}`,
|
||||
react: `/ru/schedule/route/MOW-KUF-${SCHEDULE_FROM}-${SCHEDULE_TO}`,
|
||||
waitMs: 3000,
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user