import { mapKeys, mapValues } from 'lodash'; import { camelCasePackageResponse, createReducer, getConfig, getCustomsInformation, getLabelDestinations, getLabelOrigins, getPurchasedLabels, getSelectedHazmat, getSelectedRateOptions, getSelectedRates, groupRatesByCarrier, } from 'utils'; import { ORDER_STATUS_UPDATE_FAILED, ORDER_STATUS_UPDATED, RATES_FETCHED, RATES_RESET, STATE_RESET, } from './action-types'; import { LabelPurchaseState } from '../types'; import { LabelPurchaseActions, LabelPurchaseSuccessAction, LabelStatusResolvedAction, OrderStatusUpdatedAction, OrderStatusUpdatedFailedAction, PackageUpdateAction, PackageUpdateFailedAction, RatesFetchedAction, StageLabelsNewShipmentIdsAction, } from './types.d'; import { PACKAGES_UPDATE, PACKAGES_UPDATE_ERROR, } from './packages/action-types'; import { LABEL_PURCHASE_SUCCESS, LABEL_STAGE_NEW_SHIPMENT_IDS, LABEL_STATUS_RESOLVED, } from './label/action-types'; import { SelectedRates } from 'types'; import { LABEL_PURCHASE_STATUS } from '../constants'; const { packagesSettings: { packages }, } = getConfig(); const defaultState = (): LabelPurchaseState => ( { packages: { ...camelCasePackageResponse( packages ), errors: {}, }, rates: {}, labels: getPurchasedLabels(), selectedRates: getSelectedRates(), selectedHazmatConfig: getSelectedHazmat(), selectedDestinations: getLabelDestinations(), selectedOrigins: getLabelOrigins(), purchaseAPIErrors: {}, customsInformation: getCustomsInformation(), selectedRateOptions: getSelectedRateOptions(), } ) as const; export const labelPurchaseReducer = createReducer( defaultState() ) .on( PACKAGES_UPDATE, ( state, { payload }: PackageUpdateAction ) => ( { ...state, packages: { ...state.packages, ...payload, }, } ) ) .on( PACKAGES_UPDATE_ERROR, ( state, { payload }: PackageUpdateFailedAction ) => { return { ...state, packages: { ...state.packages, errors: payload, }, }; } ) .on( RATES_FETCHED, ( state, { payload }: RatesFetchedAction ) => ( { ...state, rates: { ...state.rates, ...groupRatesByCarrier( payload ), }, } ) ) .on( RATES_RESET, ( state ) => ( { ...state, rates: undefined, } ) ) .on( STATE_RESET, () => ( { ...defaultState() } ) ) .on( LABEL_STATUS_RESOLVED, ( state, { payload }: LabelStatusResolvedAction ) => ( { ...state, labels: payload?.labelId ? mapValues( state.labels, ( shipmentLabels ) => { return shipmentLabels.map( ( label ) => { if ( label.labelId === payload.labelId ) { return payload; } return label; } ); } ) : state.labels, // If the label is refunded or status is unknown or purchase_error, we need to remove the selected rate from the selectedRates object selectedRates: Object.entries( state.selectedRates ?? {} ).reduce( ( acc, [ key, rate ] ) => { // Find the shipment index (0, 1, 2, etc.) using label.labelId const shipmentIndex = ( payload?.refund ?? [ LABEL_PURCHASE_STATUS.PURCHASE_ERROR, LABEL_PURCHASE_STATUS.UNKNOWN, ].includes( payload?.status ?? '' ) ) && typeof state.labels === 'object' && state.labels ? Object.keys( state.labels ).find( ( shipmentId ) => { return state.labels![ shipmentId ].some( ( label ) => label.labelId === payload?.labelId ); } ) : null; if ( shipmentIndex && key === `shipment_${ shipmentIndex }` ) { return acc; } // Safe to cast as keyof SelectedRates because we know the key is in the object acc[ key as keyof SelectedRates ] = rate; return acc; }, {} as SelectedRates ), } ) ) .on( LABEL_PURCHASE_SUCCESS, ( state, { payload: { label, selectedRates, selectedHazmat, selectedDestinations, selectedOrigins, }, }: LabelPurchaseSuccessAction ) => { // Note: Package dimensions sync to global config is now handled // in the action (see label/actions.ts) to keep reducer pure return { ...state, labels: { ...state.labels, ...label, }, selectedRates: { ...state.selectedRates, ...selectedRates, }, selectedHazmatConfig: { ...state.selectedHazmatConfig, ...selectedHazmat, }, selectedDestinations: { ...state.selectedDestinations, ...selectedDestinations, }, selectedOrigins: { ...state.selectedOrigins, ...selectedOrigins, }, }; } ) .on( LABEL_STAGE_NEW_SHIPMENT_IDS, ( state, { payload: shipmentIdsToUpdate }: StageLabelsNewShipmentIdsAction ) => ( { ...state, labels: mapKeys( mapValues( state.labels, ( label, key ) => key === shipmentIdsToUpdate[ key ] ? { ...label, id: shipmentIdsToUpdate[ key ], } : label ), ( _, key ) => shipmentIdsToUpdate[ key ] ?? key ), } ) ) .on( ORDER_STATUS_UPDATED, ( state, { payload }: OrderStatusUpdatedAction ) => { return { ...state, order: { ...state.order, status: payload.status, }, }; } ) .on( ORDER_STATUS_UPDATE_FAILED, ( state, { payload }: OrderStatusUpdatedFailedAction ) => { return { ...state, order: { ...state.order, status: '', error: payload.error, }, }; } ) .bind< LabelPurchaseActions >();