Extract schedule \$.tsx body to a readable module
Modern.js routes catch-all paths via a file literally named \`\$.tsx\`. The name is framework-mandated but cryptic at a glance. Move the real component to src/features/schedule/ScheduleDetailsCatchAllRoute.tsx and make \$.tsx a 12-line re-export, so every grep/import/stack-frame shows the readable name and the \$.tsx file stays just a framework shim.
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Schedule flight-details catch-all route.
|
||||
*
|
||||
* This module backs the framework-mandated `src/routes/[lang]/schedule/$.tsx`
|
||||
* splat file. The `$.tsx` filename is how Modern.js (via react-router) marks
|
||||
* a route that captures every trailing segment, so we keep it as a thin
|
||||
* re-export and put the real code under a readable path.
|
||||
*
|
||||
* Captures variable-length paths for multi-flight chains, e.g.
|
||||
* /{lang}/schedule/{flight1-date}[/{flight2-date}]...
|
||||
* Also handles the airport-code detail variant:
|
||||
* /{lang}/schedule/{depCode}/{flight-date}/{arrCode}/...
|
||||
*/
|
||||
|
||||
import { lazy, Suspense } from "react";
|
||||
import { useParams } from "@modern-js/runtime/router";
|
||||
import { useTranslation } from "@/i18n/provider.js";
|
||||
import { parseFlightUrlParams } from "@/features/online-board/url.js";
|
||||
import { FlightListSkeleton } from "@/ui/flights/FlightListSkeleton.js";
|
||||
import { getEnv } from "@/env/index.js";
|
||||
import type { IScheduleFlightId } from "./types.js";
|
||||
|
||||
const ScheduleDetailsPage = lazy(() =>
|
||||
import("./components/ScheduleDetailsPage.js").then(
|
||||
(m) => ({ default: m.ScheduleDetailsPage }),
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Parse the catch-all segments into flight IDs.
|
||||
* Segments that are 3 chars or less are treated as airport codes and skipped.
|
||||
*/
|
||||
function parseFlightSegments(segments: string[]): IScheduleFlightId[] {
|
||||
const flights: IScheduleFlightId[] = [];
|
||||
|
||||
for (const segment of segments) {
|
||||
if (segment.length <= 3) continue;
|
||||
|
||||
const parsed = parseFlightUrlParams(segment);
|
||||
if (parsed) {
|
||||
const flight: IScheduleFlightId = {
|
||||
carrier: parsed.carrier,
|
||||
flightNumber: parsed.flightNumber,
|
||||
date: parsed.date,
|
||||
};
|
||||
if (parsed.suffix !== undefined) {
|
||||
flight.suffix = parsed.suffix;
|
||||
}
|
||||
flights.push(flight);
|
||||
}
|
||||
}
|
||||
|
||||
return flights;
|
||||
}
|
||||
|
||||
export default function ScheduleDetailsCatchAllRoute(): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
const routeParams = useParams<{ "*": string; lang: string }>();
|
||||
const locale = routeParams.lang ?? "ru";
|
||||
const canonicalOrigin = getEnv().PROD_ORIGIN;
|
||||
|
||||
// Modern.js splat route ($.tsx) provides the remaining path via "*" param.
|
||||
const rawFlights = routeParams["*"] ?? "";
|
||||
const segments = rawFlights.split("/").filter(Boolean);
|
||||
const flights = parseFlightSegments(segments);
|
||||
|
||||
if (flights.length === 0) {
|
||||
return (
|
||||
<div data-testid="invalid-params">
|
||||
<p>{t("SHARED.INVALID-PARAMS")}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Suspense fallback={<FlightListSkeleton count={flights.length} />}>
|
||||
<ScheduleDetailsPage
|
||||
flights={flights}
|
||||
locale={locale}
|
||||
canonicalOrigin={canonicalOrigin}
|
||||
/>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
@@ -1,80 +1,12 @@
|
||||
/**
|
||||
* Schedule flight details catch-all route.
|
||||
* Modern.js/react-router splat file.
|
||||
*
|
||||
* Captures variable-length paths for multi-flight chains.
|
||||
* URL: /{lang}/schedule/{flight1-date}[/{flight2-date}]...
|
||||
* Also handles: /{lang}/schedule/{depCode}/{flight-date}/{arrCode}/...
|
||||
*
|
||||
* Modern.js catch-all route: [...flights] captures all remaining segments.
|
||||
* The `$.tsx` filename is a framework convention that marks a catch-all
|
||||
* route — it captures every trailing path segment. The name is mandated
|
||||
* by the router, but cryptic at a glance; the real component lives at
|
||||
* src/features/schedule/ScheduleDetailsCatchAllRoute.tsx and this file
|
||||
* is intentionally a thin re-export so the readable name appears in
|
||||
* imports, search results and stack traces.
|
||||
*/
|
||||
|
||||
import { lazy, Suspense } from "react";
|
||||
import { useParams } from "@modern-js/runtime/router";
|
||||
import { useTranslation } from "@/i18n/provider.js";
|
||||
import { parseFlightUrlParams } from "@/features/online-board/url.js";
|
||||
import { FlightListSkeleton } from "@/ui/flights/FlightListSkeleton.js";
|
||||
import { getEnv } from "@/env/index.js";
|
||||
import type { IScheduleFlightId } from "@/features/schedule/types.js";
|
||||
|
||||
const ScheduleDetailsPage = lazy(() =>
|
||||
import("@/features/schedule/components/ScheduleDetailsPage.js").then(
|
||||
(m) => ({ default: m.ScheduleDetailsPage }),
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Parse the catch-all segments into flight IDs.
|
||||
* Segments that are 3 chars or less are treated as airport codes and skipped.
|
||||
*/
|
||||
function parseFlightSegments(segments: string[]): IScheduleFlightId[] {
|
||||
const flights: IScheduleFlightId[] = [];
|
||||
|
||||
for (const segment of segments) {
|
||||
if (segment.length <= 3) continue;
|
||||
|
||||
const parsed = parseFlightUrlParams(segment);
|
||||
if (parsed) {
|
||||
const flight: IScheduleFlightId = {
|
||||
carrier: parsed.carrier,
|
||||
flightNumber: parsed.flightNumber,
|
||||
date: parsed.date,
|
||||
};
|
||||
if (parsed.suffix !== undefined) {
|
||||
flight.suffix = parsed.suffix;
|
||||
}
|
||||
flights.push(flight);
|
||||
}
|
||||
}
|
||||
|
||||
return flights;
|
||||
}
|
||||
|
||||
export default function ScheduleDetailsRoute(): JSX.Element {
|
||||
const { t } = useTranslation();
|
||||
const routeParams = useParams<{ "*": string; lang: string }>();
|
||||
const locale = routeParams.lang ?? "ru";
|
||||
const canonicalOrigin = getEnv().PROD_ORIGIN;
|
||||
|
||||
// Modern.js $.tsx splat route provides the remaining path via "*" param.
|
||||
const rawFlights = routeParams["*"] ?? "";
|
||||
const segments = rawFlights.split("/").filter(Boolean);
|
||||
const flights = parseFlightSegments(segments);
|
||||
|
||||
if (flights.length === 0) {
|
||||
return (
|
||||
<div data-testid="invalid-params">
|
||||
<p>{t("SHARED.INVALID-PARAMS")}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Suspense fallback={<FlightListSkeleton count={flights.length} />}>
|
||||
<ScheduleDetailsPage
|
||||
flights={flights}
|
||||
locale={locale}
|
||||
canonicalOrigin={canonicalOrigin}
|
||||
/>
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
export { default } from "@/features/schedule/ScheduleDetailsCatchAllRoute.js";
|
||||
|
||||
Reference in New Issue
Block a user