Untitled
unknown
javascript
2 years ago
5.9 kB
9
Indexable
import {
createSlice,
createAsyncThunk,
createEntityAdapter,
PayloadAction,
} from '@reduxjs/toolkit';
import {
getMerchantBasket,
addToMerchantBasket,
updateMerchantBasketLineQty,
applyMerchantBasketCoupon,
removeMerchantBasketVoucher,
getBasketsApi,
} from 'api/index';
import { Basket, BasketLine, Product } from 'api/models';
import * as Analytics from 'utils/analytics';
import { RootState } from '../store';
type ThunkApiType = { state: RootState };
export const fetchMerchantBasket = createAsyncThunk<Basket, number | string>(
'basket/fetch',
async (merchantId) => {
const result = await getMerchantBasket({ merchantId });
return result.data as Basket;
},
);
interface AddToBasketParams {
productId: number | string;
merchantId: number | string;
quantity: number;
}
export const addToBasket = createAsyncThunk<Basket, AddToBasketParams, ThunkApiType>(
'basket/add',
async ({ productId, merchantId, quantity }) => {
const payload = { merchantId, productId, quantity };
Analytics.logEvent('BASKET_ADD_TO', payload);
const result = await addToMerchantBasket(payload);
return result.data as Basket;
},
);
export const getBaskets = createAsyncThunk('baskets/getBaskets', async (_, { dispatch }) => {
const results = await getBasketsApi();
return results.data as Basket[];
});
interface UpdateQtyParams {
lineId: number | string;
productId: number | string;
merchantId: number | string;
quantity: number;
}
export const updateLineQty = createAsyncThunk<Basket, UpdateQtyParams, ThunkApiType>(
'basket/update',
async ({ lineId, productId, merchantId, quantity }) => {
Analytics.logEvent('BASKET_UPDATE_LINE_QTY', {
merchantId,
productId,
quantity,
});
const result = await updateMerchantBasketLineQty({
merchantId,
lineId,
quantity,
});
return result.data as Basket;
},
);
interface RemoveBasketLineParams {
lineId: number | string;
productId: number | string;
merchantId: number | string;
}
export const removeBasketLine = createAsyncThunk<Basket, RemoveBasketLineParams, ThunkApiType>(
'basket/removeLine',
async ({ lineId, productId, merchantId }) => {
Analytics.logEvent('BASKET_UPDATE_LINE_QTY', {
merchantId,
productId,
});
const result = await updateMerchantBasketLineQty({
merchantId,
lineId,
quantity: 0,
});
return result.data as Basket;
},
);
export const applyVoucher = createAsyncThunk<
Basket,
{ code: string; merchantId: number },
ThunkApiType
>('basket/applyVoucher', async ({ code, merchantId }) => {
const results = await applyMerchantBasketCoupon({ merchantId, code });
return results.data as Basket;
});
export const removeVoucher = createAsyncThunk<
Basket,
{ voucherId: string | number; merchantId: number },
ThunkApiType
>('basket/removeVoucher', async ({ voucherId, merchantId }) => {
const results = await removeMerchantBasketVoucher({ merchantId, voucherId });
return results.data as Basket;
});
/* Utility */
const updateBasket = (state: any, { payload }: PayloadAction<Basket>) => {
const basketIndex = state.baskets.findIndex((basket: Basket) => basket.id === payload.id);
if (basketIndex !== -1) {
const updatedBaskets = [...state.baskets];
updatedBaskets[basketIndex] = payload;
state.baskets = updatedBaskets;
basketAdapter.setAll(state, payload.allLines);
}
};
export const basketAdapter = createEntityAdapter<Basket>();
type InitialStateType = {
fetchStatus: 'idle' | 'pending' | 'fulfilled';
baskets: Basket[];
stagedProduct?: Product;
};
const initialState = basketAdapter.getInitialState<InitialStateType>({
fetchStatus: 'idle',
baskets: [],
stagedProduct: undefined,
});
export const basketSlice = createSlice({
name: 'basket',
initialState,
reducers: {
resetBasket(state) {
state.fetchStatus = 'idle';
state.baskets = [];
basketAdapter.removeAll(state);
},
setStagedProduct(state, { payload }) {
state.stagedProduct = payload;
},
resetStagedProduct(state) {
state.stagedProduct = undefined;
},
},
extraReducers: (builder) => {
builder.addCase(getBaskets.pending, (state) => {
state.fetchStatus = 'pending';
});
builder.addCase(getBaskets.fulfilled, (state, { payload }) => {
state.fetchStatus = 'fulfilled';
state.baskets = payload;
basketAdapter.setAll(state, payload);
});
builder.addCase(getBaskets.rejected, (state) => {
state.fetchStatus = 'fulfilled';
});
builder.addCase(addToBasket.fulfilled, updateBasket);
builder.addCase(updateLineQty.fulfilled, updateBasket);
builder.addCase(removeBasketLine.fulfilled, updateBasket);
builder.addCase(applyVoucher.fulfilled, updateBasket);
builder.addCase(removeVoucher.fulfilled, updateBasket);
},
});
export const { resetBasket, setStagedProduct, resetStagedProduct } = basketSlice.actions;
export default basketSlice.reducer;
// Selectors
export const {
selectById: selectBasketById,
selectIds: selectBasketIds,
selectEntities: selectBaskets,
selectAll: selectAllBaskets,
selectTotal: selectTotalBaskets,
} = basketAdapter.getSelectors((state: RootState) => state.basket);
export const selectAllLinesForBasket = (state: RootState, basketId: number): BasketLine[] => {
const basket = selectBasketById(state, basketId);
return basket?.allLines ?? [];
};
// export const selectBasketMerchantId = (state: RootState) => state.basket?.partner.id;
export const selectBasketsFetchStatus = (state: RootState) => state.basket.fetchStatus;
export const selectBasketStagedProduct = (state: RootState) => state.basket.stagedProduct;
export const getProductsQty = (basketLines: BasketLine[]) => {
return basketLines.reduce((res: any, line) => {
res[line.product.id] = line.quantity;
return res;
}, {});
};
Editor is loading...
Leave a Comment