UNPKG

@qite/tide-booking-component

Version:

React Booking wizard & Booking product component for Tide

172 lines (146 loc) 4.92 kB
import { createAsyncThunk, createSelector, createSlice, PayloadAction, } from "@reduxjs/toolkit"; import { sum } from "lodash"; import { RootState } from "../../store"; import { BookingPriceDetail, BookingPriceDetails, BookingProductNotification, } from "@qite/tide-client/build/types"; import { setPackage } from "../booking/booking-slice"; import { selectBookingPackageBookRequest, selectLanguageCode, selectPackageDetails, } from "../booking/selectors"; import priceDetailsApi from "./price-details-api"; import { updatePackageDetails } from "./util"; export interface PriceDetailsState { priceDetails: BookingPriceDetail[]; notifications: BookingProductNotification[]; deposit?: number; isBusy: boolean; } export const fetchPriceDetails = createAsyncThunk( "priceDetails/fetchPriceDetails", async (_, { dispatch, getState, signal }) => { dispatch(setFetchingPriceDetails(true)); dispatch(resetPriceDetails()); const state = getState() as RootState; const languageCode = selectLanguageCode(state); const request = selectBookingPackageBookRequest(state); if (!request) { throw Error("Product not available."); } const priceDetails: BookingPriceDetails = await priceDetailsApi.fetchPriceDetails( state.apiSettings, request, signal, languageCode ); dispatch(setFetchingPriceDetails(false)); const packageDetails = selectPackageDetails(state); if (packageDetails) { const updatedPackageDetails = updatePackageDetails( packageDetails, priceDetails.changedLines ); dispatch(setPackage(updatedPackageDetails)); } return priceDetails; } ); const priceDetailsSlice = createSlice({ name: "priceDetails", initialState: <PriceDetailsState>{ priceDetails: [], notifications: [], isBusy: false, }, reducers: { setFetchingPriceDetails(state, action: PayloadAction<boolean>) { state.isBusy = action.payload; }, setNotifications( state, action: PayloadAction<BookingProductNotification[]> ) { state.notifications = action.payload; }, resetPriceDetails(state, _: PayloadAction<void>) { state.priceDetails = []; }, }, extraReducers: (builder) => { builder.addCase(fetchPriceDetails.fulfilled, (state, action) => { if (action.payload) { state.deposit = action.payload.deposit; state.priceDetails = action.payload.details; state.notifications = action.payload.notifications; } }); }, }); export const selectPriceDetails = (state: RootState) => state.priceDetails.priceDetails; export const selectPackagePriceDetails = createSelector( selectPriceDetails, (priceDetails) => priceDetails.filter((priceDetail) => priceDetail.isInPackage) ); export const selectSeparatePackagePriceDetails = createSelector( selectPackagePriceDetails, (pricedetails) => { const result: BookingPriceDetail[] = []; const filteredPriceDetails = pricedetails.filter((priceDetail) => priceDetail.isSeparate) filteredPriceDetails.forEach(priceDetail => { const priceDetailToMerge = result.find(x => x.productCode === priceDetail.productCode && x.accommodationCode === priceDetail.accommodationCode && x.productType === priceDetail.productType); if (priceDetailToMerge) { priceDetailToMerge.total += priceDetail.total; priceDetailToMerge.price += priceDetail.price; } else { result.push(Object.assign({}, priceDetail)); } }); return result; } ); export const selectBasePrice = createSelector( selectPackagePriceDetails, (priceDetails) => sum( priceDetails.map((priceDetail) => priceDetail.price * priceDetail.amount) ) ); export const selectSeparateExtraPriceDetails = createSelector( selectPriceDetails, (priceDetails) => priceDetails.filter( (priceDetail) => !priceDetail.isInPackage && priceDetail.isSeparate ) ); export const selectTotalPrice = createSelector( selectBasePrice, selectSeparateExtraPriceDetails, (basePrice, separatePriceDetails) => sum([ basePrice, ...separatePriceDetails.map( (priceDetail) => priceDetail.price * priceDetail.amount ), ]) ); export const selectDeposit = (state: RootState) => state.priceDetails.deposit; export const selectIsFetchingPriceDetails = (state: RootState) => state.priceDetails.isBusy; export const selectNotifications = (state: RootState) => state.priceDetails.notifications; const { setFetchingPriceDetails } = priceDetailsSlice.actions; export const { resetPriceDetails, setNotifications } = priceDetailsSlice.actions; export default priceDetailsSlice.reducer;