/* eslint-disable no-console */ import { isNull } from "lodash-es"; import type { PropsWithChildren } from "react"; import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from "react"; import type { QueryObserverResult, RefetchOptions, RefetchQueryFilters, } from "react-query"; import { useUpdateEffect } from "usehooks-ts"; import { DUMMY_FLIGHT_LIST_REQUEST } from "@/data/dummy/flight-list-request"; import { usePageInfo } from "@/hooks/use-page-info"; import { useFlightListSearchQuery } from "@/services/eva-display/flight/flight-search/flight-search-services"; import type { FlightFilter, IFlightSearchRequest, } from "@/services/eva-display/flight/flight-search/flight-search-services.types"; import type { IFlightSearchResultSerializer } from "@/services/eva-display/flight/flight-search/flight-search-services-serializer"; export interface IListContext { data: IFlightSearchResultSerializer | undefined; paginatedData: IFlightSearchResultSerializer[]; filterValues: FlightFilter; isLoading: boolean; isSuccess: boolean; isFetching: boolean; departureSegmentKey: string; isFilterApplicable: boolean | null; setIsFilterApplicable: (value: boolean | null) => void; setDepartureSegmentKey: (key: string) => void; refetch: ( options?: (RefetchOptions & RefetchQueryFilters) | undefined, ) => Promise>; requestVariables: T; applyClearFilter: () => void; applyDepartureTimeFilter: ( departureTimeMin: string, departureTimeMax: string, ) => void; applyPriceRangeFilter: (priceMin: number, priceMax: number) => void; applyFlightDurationFilter: ( flightDurationMin: string, flightDurationMax: string, ) => void; applyAirlineFilter: (airlineCodes: string[]) => void; applyTransferCountFilter: (transferCount: string[]) => void; applyOrderFilter: (order: number) => void; applyLoadMoreFilter: () => void; } const initialValue: Partial> = { requestVariables: DUMMY_FLIGHT_LIST_REQUEST, }; export const FlightListContext = createContext>>(initialValue); export enum FligthListTypeEnum { ONE_WAY = "oneway", ROUND_TRIP = "roundtrip", } export type FligthListType = | FligthListTypeEnum.ONE_WAY | FligthListTypeEnum.ROUND_TRIP; interface FlightListContextProviderProps extends PropsWithChildren { urlVariables: IFlightSearchRequest | null; } const initialFilterValues: FlightFilter = { PriceMin: 0, PriceMax: 0, AirlineCodes: null, Layover: null, GoingFlightDestinations: null, ReturnFlightDestinations: null, FlightMaxTime: "60:00:00", FlightMinTime: "00:00:00", PageSize: 10, IsPaging: true, ResetPage: false, IsDirect: null, GroupId: 0, Order: 1, // "Ascending" = 1, "Descending" = 2, "PriceAscending" = 3, "PriceDescending" = 4 }; export const FlightListContextProvider = ({ children, urlVariables, }: FlightListContextProviderProps) => { const { info } = usePageInfo(); const [filterValues, setFilterValues] = useState(initialFilterValues); const [isFilterApplicable, setIsFilterApplicable] = useState( true, ); const [paginatedData, setPaginatedData] = useState< IFlightSearchResultSerializer[] >([]); const [departureSegmentKey, setDepartureSegmentKey] = useState(""); const [pageNumber, setPageNumber] = useState(1); const [requestVariables, setRequestVariables] = useState({ searchLegs: urlVariables?.searchLegs.map((x) => ({ ...x, cabinClass: x.cabinClass === 4 ? undefined : x.cabinClass, })) ?? [], searchPassengers: urlVariables?.searchPassengers ?? [], Filter: filterValues, searchId: null, FlightDisplayType: urlVariables?.FlightDisplayType ?? 0, FlightTripKind: urlVariables?.FlightTripKind ?? 0, }); const [searchData, setSearchData] = useState( null, ); const { data, isLoading, isSuccess, refetch, isFetching } = useFlightListSearchQuery({ variables: searchData!, pageNumber, }); useEffect(() => { if (isFilterApplicable && requestVariables) { setSearchData(requestVariables); } else if (isNull(isFilterApplicable) && requestVariables && searchData) { setRequestVariables(searchData); } // This warn for searchData is not used in the dependency array. // eslint-disable-next-line react-hooks/exhaustive-deps }, [isFilterApplicable, requestVariables]); /** * Clear all filters. */ const applyClearFilter = useCallback(() => { setFilterValues(initialFilterValues); setRequestVariables((prev) => ({ ...prev, searchId: data?.metadata.searchId, Filter: { ...initialFilterValues, ResetPage: true }, })); setPaginatedData([]); }, [data?.metadata.searchId]); /** * Apply departure time filter. */ const applyDepartureTimeFilter = useCallback( (departureTimeMin: string, departureTimeMax: string) => { const newFilterValues: Partial = { GoingDepartureMinTime: departureTimeMin, GoingDepartureMaxTime: departureTimeMax, ResetPage: true, }; setFilterValues((prev) => ({ ...prev, ...newFilterValues, })); setRequestVariables((prev) => ({ ...prev, searchId: data?.metadata.searchId, Filter: { ...prev.Filter, ...newFilterValues, }, })); }, [data?.metadata.searchId], ); /** * Apply price range filter. */ const applyPriceRangeFilter = useCallback( (priceMin: number, priceMax: number) => { const newFilterValues: Partial = { PriceMin: priceMin, PriceMax: priceMax, ResetPage: true, }; setFilterValues((prev) => ({ ...prev, ...newFilterValues, })); setRequestVariables((prev) => ({ ...prev, searchId: data?.metadata.searchId, Filter: { ...prev.Filter, ...newFilterValues, }, })); }, [data?.metadata.searchId], ); /** * Apply flight duration filter. */ const applyFlightDurationFilter = useCallback( (flightDurationMin: string, flightDurationMax: string) => { const newFilterValues: Partial = { FlightMinTime: flightDurationMin, FlightMaxTime: flightDurationMax, ResetPage: true, }; setFilterValues((prev) => ({ ...prev, ...newFilterValues, })); setRequestVariables((prev) => ({ ...prev, searchId: data?.metadata.searchId, Filter: { ...prev.Filter, ...newFilterValues, }, })); }, [data?.metadata.searchId], ); /** * Apply airline filter. */ const applyAirlineFilter = useCallback( (airlineCodes: string[]) => { setFilterValues((prev) => ({ ...prev, AirlineCodes: airlineCodes, ResetPage: true, })); setRequestVariables((prev) => ({ ...prev, searchId: data?.metadata.searchId, Filter: { ...prev.Filter, AirlineCodes: airlineCodes.length > 0 ? airlineCodes : null, ResetPage: true, }, })); }, [data?.metadata.searchId], ); /** * Apply transfer count filter. */ const applyTransferCountFilter = useCallback( (transferCount: string[]) => { setFilterValues((prev) => ({ ...prev, Layover: transferCount, })); setRequestVariables((prev) => ({ ...prev, searchId: data?.metadata.searchId, Filter: { ...prev.Filter, Layover: transferCount.length > 0 ? transferCount : null, ResetPage: true, }, })); }, [data?.metadata.searchId], ); /** * Apply order filter. */ const applyOrderFilter = useCallback( (order: number) => { setFilterValues((prev) => ({ ...prev, Order: order, ResetPage: true, })); setRequestVariables((prev) => ({ ...prev, searchId: data?.metadata.searchId, Filter: { ...prev.Filter, Order: order, ResetPage: true, }, })); setPaginatedData([]); refetch(); }, // eslint-disable-next-line react-hooks/exhaustive-deps [data?.metadata.searchId], ); /** * Apply load more filter. This filter don't set pagination values as empty array. */ const applyLoadMoreFilter = useCallback(() => { setRequestVariables((prev) => ({ ...prev, searchId: data?.metadata.searchId, Filter: { ...prev.Filter, ResetPage: false, }, })); if (requestVariables.searchId !== null) { setPageNumber((prev) => prev + 1); } }, [data?.metadata.searchId, requestVariables.searchId]); const mergePaginatedData = useCallback( (newResponse: IFlightSearchResultSerializer) => { const newData = [...paginatedData, newResponse]; return newData; }, [paginatedData], ); useEffect(() => { if (requestVariables.Filter.ResetPage && isFilterApplicable) { setPaginatedData([]); refetch(); } }, [requestVariables.Filter.ResetPage, isFilterApplicable, refetch]); useUpdateEffect(() => { if (data) { setPaginatedData(() => mergePaginatedData(data!)); } }, [data]); /** * This values are used to create the context. */ const contextValues = useMemo( () => ({ data, paginatedData, isLoading, isFetching, isSuccess, departureSegmentKey, filterValues, isFilterApplicable, setIsFilterApplicable, setDepartureSegmentKey, refetch, requestVariables, applyClearFilter, applyDepartureTimeFilter, applyPriceRangeFilter, applyFlightDurationFilter, applyAirlineFilter, applyTransferCountFilter, applyOrderFilter, applyLoadMoreFilter, }), [ data, paginatedData, isLoading, isFetching, isSuccess, departureSegmentKey, filterValues, refetch, requestVariables, isFilterApplicable, setIsFilterApplicable, applyClearFilter, applyDepartureTimeFilter, applyPriceRangeFilter, applyFlightDurationFilter, applyAirlineFilter, applyTransferCountFilter, applyOrderFilter, applyLoadMoreFilter, ], ); /** * This effect is used to update the request variables when the url variables change. */ useUpdateEffect(() => { setRequestVariables((prev) => ({ ...prev, searchLegs: urlVariables?.searchLegs.map((x) => ({ ...x, cabinClass: x.cabinClass === 4 ? undefined : x.cabinClass, })) ?? [], searchPassengers: urlVariables?.searchPassengers ?? [], Filter: filterValues, searchId: null, FlightDisplayType: urlVariables?.FlightDisplayType ?? 0, FlightTripKind: urlVariables?.FlightTripKind ?? 0, })); }, [ info?.queryParams.cabinClass, info?.queryParams.departureTime, info?.queryParams.destinationId, info?.queryParams.destinationType, info?.queryParams.flightTripKind, info?.queryParams.originId, info?.queryParams.originType, info?.queryParams.passenger, info?.queryParams.returnTime, ]); return ( {children} ); }; export const useFlightListContext = () => { const context = useContext(FlightListContext); return { ...context }; };