Untitled

mail@pastecode.io avatar
unknown
javascript
7 months ago
5.9 kB
3
Indexable
Never
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;
  }, {});
};
Leave a Comment