Schedule heading + column header row

- ScheduleSearchPage: H1 now reads `Расписание по маршруту: …`
  using SCHEDULE.SCHEDULE-BY-ROUTE — the existing onlineboard
  variant was leaking through. Matches Angular's schedule-search
  title-bar verbatim.
- DayGroupedFlightList: render a non-sortable column header bar
  above the list with `Рейс / Авиакомпания, борт / Вылет / Время
  в пути / Прилет`. Mirrors Angular's schedule-list-flight-header
  column row. Sort arrows still TBD.
- New i18n keys: SCHEDULE.COL-FLIGHT, COL-AIRLINE, COL-DEPARTURE,
  COL-DURATION, COL-ARRIVAL (RU + EN both filled).

Schedule-route mismatch now 11.99% (was 12.47% pre-heading fix).
This commit is contained in:
2026-04-19 21:44:19 +03:00
parent 64c86dcdd6
commit 71d0eef3e2
5 changed files with 56 additions and 8 deletions
@@ -3,6 +3,19 @@
flex-direction: column;
gap: 18px;
&__column-headers {
display: grid;
grid-template-columns: 80px 1fr 100px 120px 1fr;
gap: 12px;
padding: 8px 16px;
color: #6b7280;
font-size: 11px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.04em;
border-bottom: 1px solid #e8edf3;
}
&__group {
border: 1px solid #e8edf3;
border-radius: 6px;
@@ -11,6 +11,7 @@
*/
import { type FC, useMemo } from "react";
import { useTranslation } from "@/i18n/provider.js";
import { FlightList } from "@/ui/flights/FlightList.js";
import { FlightListSkeleton } from "@/ui/flights/FlightListSkeleton.js";
import { useLocale } from "@/i18n/useLocale.js";
@@ -72,8 +73,25 @@ export const DayGroupedFlightList: FC<DayGroupedFlightListProps> = ({
initialCurrentFlightId,
}) => {
const { language } = useLocale();
const { t } = useTranslation();
const groups = useMemo(() => groupFlightsByDay(flights), [flights]);
/** Angular's schedule renders a sortable column header row above the
* list. We render the same labels as a non-sortable bar — sortable
* headers are tracked separately. Skipped while loading or empty. */
const headerRow = (
<div
className="day-grouped-flight-list__column-headers"
data-testid="schedule-column-headers"
>
<span>{t("SCHEDULE.COL-FLIGHT") || "РЕЙС"}</span>
<span>{t("SCHEDULE.COL-AIRLINE") || "АВИАКОМПАНИЯ, БОРТ"}</span>
<span>{t("SCHEDULE.COL-DEPARTURE") || "ВЫЛЕТ"}</span>
<span>{t("SCHEDULE.COL-DURATION") || "ВРЕМЯ В ПУТИ"}</span>
<span>{t("SCHEDULE.COL-ARRIVAL") || "ПРИЛЕТ"}</span>
</div>
);
if (loading) return <FlightListSkeleton count={5} />;
if (groups.length === 0) {
@@ -83,13 +101,16 @@ export const DayGroupedFlightList: FC<DayGroupedFlightListProps> = ({
// Single-day result: skip grouping noise and render the flat list.
if (groups.length === 1) {
return (
<FlightList
flights={flights}
loading={false}
direction="schedule"
{...(onFlightClick ? { onFlightClick } : {})}
{...(initialCurrentFlightId ? { initialCurrentFlightId } : {})}
/>
<div className="day-grouped-flight-list">
{headerRow}
<FlightList
flights={flights}
loading={false}
direction="schedule"
{...(onFlightClick ? { onFlightClick } : {})}
{...(initialCurrentFlightId ? { initialCurrentFlightId } : {})}
/>
</div>
);
}
@@ -103,6 +124,7 @@ export const DayGroupedFlightList: FC<DayGroupedFlightListProps> = ({
return (
<div className="day-grouped-flight-list" data-testid="day-grouped-flight-list">
{headerRow}
{groups.map((g) => {
const weekday = weekdayFmt.format(g.parsed);
const dayMonth = dayMonthFmt.format(g.parsed);
@@ -97,7 +97,10 @@ export const ScheduleSearchPage: FC<ScheduleSearchPageProps> = ({ params }) => {
};
const depName = describeStation(outbound.departure);
const arrName = describeStation(outbound.arrival);
const routeHeading = `${t("BOARD.ROUTE-TEXT")}${depName} - ${arrName}`;
// Angular's schedule pages render the heading as
// `Расписание по маршруту: Москва - Самара` (SCHEDULE.SCHEDULE-BY-ROUTE);
// the onlineboard variant uses the shorter `Маршрут:` (BOARD.ROUTE-TEXT).
const routeHeading = `${t("SCHEDULE.SCHEDULE-BY-ROUTE")}: ${depName} - ${arrName}`;
// Fetch outbound flights
const outboundRequest = toSearchRequest(outbound);
+5
View File
@@ -177,6 +177,11 @@
"TO-HOME": "HOME"
},
"SCHEDULE": {
"COL-FLIGHT": "Flight",
"COL-AIRLINE": "Airline, aircraft",
"COL-DEPARTURE": "Departure",
"COL-DURATION": "Duration",
"COL-ARRIVAL": "Arrival",
"DOWNLOAD-SCHEDULE": "Download schedule",
"DOWNLOAD-SCHEDULE-FOR-DAY": "For the day",
"DOWNLOAD-SCHEDULE-FOR-THE-CURRENT-MONTH": "For the current month",
+5
View File
@@ -177,6 +177,11 @@
"TO-HOME": "НА ГЛАВНУЮ"
},
"SCHEDULE": {
"COL-FLIGHT": "Рейс",
"COL-AIRLINE": "Авиакомпания, борт",
"COL-DEPARTURE": "Вылет",
"COL-DURATION": "Время в пути",
"COL-ARRIVAL": "Прилет",
"DOWNLOAD-SCHEDULE": "Выгрузить расписание",
"DOWNLOAD-SCHEDULE-FOR-DAY": "На день",
"DOWNLOAD-SCHEDULE-FOR-THE-CURRENT-MONTH": "На текущий месяц",