has issues
This commit is contained in:
parent
2e112d7398
commit
c645b0f1d4
96 changed files with 2147083 additions and 5787 deletions
|
|
@ -64,13 +64,68 @@ export interface TravelTimeEntry {
|
|||
timeRange: [number, number] | null;
|
||||
/** Use best-case (5th percentile) travel time instead of median. Transit only. */
|
||||
useBest: boolean;
|
||||
/** Restrict transit to walk-transit-walk (0 changes). Optional; defaults to false. */
|
||||
noChange?: boolean;
|
||||
/** Drop buses from the allowed transit modes. Optional; defaults to false. */
|
||||
noBuses?: boolean;
|
||||
}
|
||||
|
||||
/** Field key matching the backend response: tt_{mode}_{slug} */
|
||||
export function travelFieldKey(entry: TravelTimeEntry): string {
|
||||
return `tt_${entry.mode}_${entry.slug}`;
|
||||
/**
|
||||
* The Java pipeline emits 6 transit variants as separate parquet directories.
|
||||
* The UI represents transit with two toggle booleans; this maps the toggle
|
||||
* state back to the directory/mode name the server expects.
|
||||
*
|
||||
* For non-transit modes the entry.mode passes through unchanged.
|
||||
*
|
||||
* Note: the transit-one-change* variants exist server-side but are not reachable
|
||||
* from the UI toggles (only no-change + no-buses are exposed). They're available
|
||||
* via direct API access for callers that want them.
|
||||
*/
|
||||
export function resolveTransitVariant(entry: TravelTimeEntry): string {
|
||||
if (entry.mode !== 'transit') return entry.mode;
|
||||
const nc = entry.noChange ?? false;
|
||||
const nb = entry.noBuses ?? false;
|
||||
if (nc && nb) return 'transit-no-change-no-bus';
|
||||
if (nc) return 'transit-no-change';
|
||||
if (nb) return 'transit-no-bus';
|
||||
return 'transit';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a server-side mode string (incl. transit variants) back into a base
|
||||
* TransportMode + UI toggle booleans. Returns null for mode strings the UI
|
||||
* cannot represent (currently: transit-one-change, transit-one-change-no-bus).
|
||||
* Callers should skip entries that parse to null rather than silently
|
||||
* normalising to a different variant.
|
||||
*/
|
||||
export function parseServerMode(
|
||||
modeStr: string
|
||||
): { mode: TransportMode; noChange: boolean; noBuses: boolean } | null {
|
||||
if (modeStr === 'car' || modeStr === 'bicycle' || modeStr === 'walking') {
|
||||
return { mode: modeStr, noChange: false, noBuses: false };
|
||||
}
|
||||
switch (modeStr) {
|
||||
case 'transit':
|
||||
return { mode: 'transit', noChange: false, noBuses: false };
|
||||
case 'transit-no-bus':
|
||||
return { mode: 'transit', noChange: false, noBuses: true };
|
||||
case 'transit-no-change':
|
||||
return { mode: 'transit', noChange: true, noBuses: false };
|
||||
case 'transit-no-change-no-bus':
|
||||
return { mode: 'transit', noChange: true, noBuses: true };
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Field key matching the backend response: tt_{server-mode}_{slug} */
|
||||
export function travelFieldKey(entry: TravelTimeEntry): string {
|
||||
return `tt_${resolveTransitVariant(entry)}_${entry.slug}`;
|
||||
}
|
||||
|
||||
/** Slider/data ceiling (minutes). Mirrors MAX_TRIP_DURATION_MINUTES in the R5 pipeline. */
|
||||
export const MAX_TRAVEL_MINUTES = 90;
|
||||
|
||||
export interface TravelTimeInitial {
|
||||
entries?: TravelTimeEntry[];
|
||||
}
|
||||
|
|
@ -81,7 +136,10 @@ export function useTravelTime(initial?: TravelTimeInitial) {
|
|||
);
|
||||
|
||||
const handleAddEntry = useCallback((mode: TransportMode) => {
|
||||
setEntries((prev) => [...prev, { mode, slug: '', label: '', timeRange: null, useBest: false }]);
|
||||
setEntries((prev) => [
|
||||
...prev,
|
||||
{ mode, slug: '', label: '', timeRange: null, useBest: false, noChange: false, noBuses: false },
|
||||
]);
|
||||
}, []);
|
||||
|
||||
const handleRemoveEntry = useCallback((index: number) => {
|
||||
|
|
@ -92,7 +150,9 @@ export function useTravelTime(initial?: TravelTimeInitial) {
|
|||
setEntries((prev) =>
|
||||
dedupeTravelTimeEntries(
|
||||
prev.map((entry, i) =>
|
||||
i === index ? { ...entry, slug, label, timeRange: slug ? [0, 120] : null } : entry
|
||||
i === index
|
||||
? { ...entry, slug, label, timeRange: slug ? [0, MAX_TRAVEL_MINUTES] : null }
|
||||
: entry
|
||||
)
|
||||
)
|
||||
);
|
||||
|
|
@ -114,6 +174,26 @@ export function useTravelTime(initial?: TravelTimeInitial) {
|
|||
);
|
||||
}, []);
|
||||
|
||||
const handleToggleNoChange = useCallback((index: number) => {
|
||||
setEntries((prev) =>
|
||||
dedupeTravelTimeEntries(
|
||||
prev.map((entry, i) =>
|
||||
i === index ? { ...entry, noChange: !(entry.noChange ?? false) } : entry
|
||||
)
|
||||
)
|
||||
);
|
||||
}, []);
|
||||
|
||||
const handleToggleNoBuses = useCallback((index: number) => {
|
||||
setEntries((prev) =>
|
||||
dedupeTravelTimeEntries(
|
||||
prev.map((entry, i) =>
|
||||
i === index ? { ...entry, noBuses: !(entry.noBuses ?? false) } : entry
|
||||
)
|
||||
)
|
||||
);
|
||||
}, []);
|
||||
|
||||
const handleSetEntries = useCallback((newEntries: TravelTimeEntry[]) => {
|
||||
setEntries(dedupeTravelTimeEntries(newEntries));
|
||||
}, []);
|
||||
|
|
@ -130,5 +210,7 @@ export function useTravelTime(initial?: TravelTimeInitial) {
|
|||
handleSetEntries,
|
||||
handleTimeRangeChange,
|
||||
handleToggleBest,
|
||||
handleToggleNoChange,
|
||||
handleToggleNoBuses,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue