ScheduleFilter: add return-flight date range + time slider when round-trip (Angular parity)
This commit is contained in:
@@ -54,6 +54,10 @@ export interface ScheduleFilterProps {
|
||||
initialDateTo?: string;
|
||||
initialTimeFrom?: string;
|
||||
initialTimeTo?: string;
|
||||
initialReturnDateFrom?: string;
|
||||
initialReturnDateTo?: string;
|
||||
initialReturnTimeFrom?: string;
|
||||
initialReturnTimeTo?: string;
|
||||
initialDirectOnly?: boolean;
|
||||
initialReturnFlights?: boolean;
|
||||
}
|
||||
@@ -90,6 +94,10 @@ export const ScheduleFilter: FC<ScheduleFilterProps> = ({
|
||||
initialDateTo,
|
||||
initialTimeFrom,
|
||||
initialTimeTo,
|
||||
initialReturnDateFrom,
|
||||
initialReturnDateTo,
|
||||
initialReturnTimeFrom,
|
||||
initialReturnTimeTo,
|
||||
initialDirectOnly = false,
|
||||
initialReturnFlights = false,
|
||||
}) => {
|
||||
@@ -111,6 +119,15 @@ export const ScheduleFilter: FC<ScheduleFilterProps> = ({
|
||||
]);
|
||||
const [directOnly, setDirectOnly] = useState(initialDirectOnly);
|
||||
const [returnFlights, setReturnFlights] = useState(initialReturnFlights);
|
||||
const initRetFrom = yyyymmddToDate(initialReturnDateFrom);
|
||||
const initRetTo = yyyymmddToDate(initialReturnDateTo);
|
||||
const [returnDateRange, setReturnDateRange] = useState<(Date | null)[]>(
|
||||
initRetFrom && initRetTo ? [initRetFrom, initRetTo] : [null, null],
|
||||
);
|
||||
const [returnTimeRange, setReturnTimeRange] = useState<[number, number]>([
|
||||
hhmmToMinutes(initialReturnTimeFrom, 0),
|
||||
hhmmToMinutes(initialReturnTimeTo, 1440),
|
||||
]);
|
||||
// Mirrors Angular ScheduleFilterValidationService: a single inline
|
||||
// error shown below the arrival input when the user submits with
|
||||
// departure === arrival. Cleared whenever either city changes.
|
||||
@@ -166,11 +183,53 @@ export const ScheduleFilter: FC<ScheduleFilterProps> = ({
|
||||
: {}),
|
||||
...(directOnly ? { connections: 0 } : {}),
|
||||
};
|
||||
const params: ScheduleParams = { type: "route", outbound };
|
||||
|
||||
let params: ScheduleParams;
|
||||
if (returnFlights) {
|
||||
// Default return range to week-after-outbound if the user
|
||||
// turned the checkbox on but hasn't picked dates.
|
||||
const retFromDate = returnDateRange[0] ?? (() => {
|
||||
const d = new Date(to);
|
||||
d.setDate(d.getDate() + 1);
|
||||
return d;
|
||||
})();
|
||||
const retToDate = returnDateRange[1] ?? (() => {
|
||||
const d = new Date(to);
|
||||
d.setDate(d.getDate() + 7);
|
||||
return d;
|
||||
})();
|
||||
const inbound = {
|
||||
departure: arr,
|
||||
arrival: dep,
|
||||
dateFrom: dateToYyyymmdd(retFromDate),
|
||||
dateTo: dateToYyyymmdd(retToDate),
|
||||
...(returnTimeRange[0] !== 0 || returnTimeRange[1] !== 1440
|
||||
? {
|
||||
timeFrom: minutesToHhmm(returnTimeRange[0]),
|
||||
timeTo: minutesToHhmm(returnTimeRange[1]),
|
||||
}
|
||||
: {}),
|
||||
...(directOnly ? { connections: 0 } : {}),
|
||||
};
|
||||
params = { type: "roundtrip", outbound, inbound };
|
||||
} else {
|
||||
params = { type: "route", outbound };
|
||||
}
|
||||
const url = buildScheduleUrl(params);
|
||||
void navigate(`/${locale}/${url}`);
|
||||
},
|
||||
[departure, arrival, dateRange, timeRange, directOnly, navigate, locale],
|
||||
[
|
||||
departure,
|
||||
arrival,
|
||||
dateRange,
|
||||
timeRange,
|
||||
directOnly,
|
||||
returnFlights,
|
||||
returnDateRange,
|
||||
returnTimeRange,
|
||||
navigate,
|
||||
locale,
|
||||
],
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -291,6 +350,57 @@ export const ScheduleFilter: FC<ScheduleFilterProps> = ({
|
||||
/>
|
||||
<span>{t("SHARED.RETURN_FLIGHT_VIEW")}</span>
|
||||
</label>
|
||||
|
||||
{returnFlights && (
|
||||
<>
|
||||
<div className="calendar">
|
||||
<label className="label--filter">
|
||||
{t("SHARED.RETURN_FLIGHT_DATE")}
|
||||
</label>
|
||||
<Calendar
|
||||
value={returnDateRange}
|
||||
onChange={(e) =>
|
||||
setReturnDateRange(
|
||||
(e.value as (Date | null)[]) ?? [null, null],
|
||||
)
|
||||
}
|
||||
selectionMode="range"
|
||||
minDate={scheduleMinDate}
|
||||
maxDate={scheduleMaxDate}
|
||||
dateFormat="dd.mm.yy"
|
||||
placeholder={`${t("SHARED.DATE_FORMAT")} - ${t("SHARED.DATE_FORMAT")}`}
|
||||
showIcon
|
||||
className="input--filter"
|
||||
data-testid="schedule-return-date-input"
|
||||
inputId="schedule-return-date-input"
|
||||
readOnlyInput
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="wrapper--time-selector compact-view" data-testid="return-time-selector">
|
||||
<div className="time-selector__label-value">
|
||||
<div className="time-selector__label">
|
||||
{t("SHARED.RETURN_FLIGHT_TIME")}
|
||||
</div>
|
||||
<div className="time-selector__value">
|
||||
{minutesToTime(returnTimeRange[0])} — {minutesToTime(returnTimeRange[1])}
|
||||
</div>
|
||||
</div>
|
||||
<div className="time-selector">
|
||||
<Slider
|
||||
value={returnTimeRange}
|
||||
onChange={(e: SliderChangeEvent) =>
|
||||
setReturnTimeRange(e.value as [number, number])
|
||||
}
|
||||
range
|
||||
min={0}
|
||||
max={1440}
|
||||
step={60}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="filter-button">
|
||||
|
||||
@@ -287,6 +287,10 @@ export const ScheduleSearchPage: FC<ScheduleSearchPageProps> = ({ params }) => {
|
||||
initialDateTo={outbound.dateTo}
|
||||
{...(outbound.timeFrom ? { initialTimeFrom: outbound.timeFrom } : {})}
|
||||
{...(outbound.timeTo ? { initialTimeTo: outbound.timeTo } : {})}
|
||||
{...(inbound?.dateFrom ? { initialReturnDateFrom: inbound.dateFrom } : {})}
|
||||
{...(inbound?.dateTo ? { initialReturnDateTo: inbound.dateTo } : {})}
|
||||
{...(inbound?.timeFrom ? { initialReturnTimeFrom: inbound.timeFrom } : {})}
|
||||
{...(inbound?.timeTo ? { initialReturnTimeTo: inbound.timeTo } : {})}
|
||||
initialDirectOnly={outbound.connections === 0}
|
||||
initialReturnFlights={Boolean(inbound)}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user