HabitatMap/AirCasting

View on GitHub
app/javascript/react/store/crowdMapSlice.ts

Summary

Maintainability
B
6 hrs
Test Coverage
import { createAsyncThunk, createSlice, PayloadAction } from "@reduxjs/toolkit";
import { AxiosResponse } from "axios";
import { oldApiClient } from "../api/apiClient";
import { API_ENDPOINTS } from "../api/apiEndpoints";
import { ApiError, StatusEnum } from "../types/api";
import { getErrorMessage } from "../utils/getErrorMessage";
import { logError } from "../utils/logController";
import { RootState } from "./";

interface RectangleData {
  value: number;
  west: number;
  east: number;
  south: number;
  north: number;
}

interface CrowdMapState {
  error: ApiError | null;
  fetchingData: boolean;
  rectangles: RectangleData[];
  status: StatusEnum;
}

const initialState: CrowdMapState = {
  fetchingData: true,
  rectangles: [],
  status: StatusEnum.Idle,
  error: null,
};

export const fetchCrowdMapData = createAsyncThunk<
  RectangleData[],
  string,
  { rejectValue: ApiError }
>("crowdMap/getCrowdMapData", async (filters: string, { rejectWithValue }) => {
  try {
    const response: AxiosResponse<RectangleData[]> = await oldApiClient.get(
      API_ENDPOINTS.fetchCrowdMap(filters)
    );
    return response.data;
  } catch (error) {
    const message = getErrorMessage(error);

    const apiError: ApiError = {
      message,
      additionalInfo: {
        action: "fetchCrowdMapData",
        endpoint: API_ENDPOINTS.fetchCrowdMap(filters),
      },
    };

    logError(error, apiError);

    return rejectWithValue(apiError);
  }
});

const crowdMapSlice = createSlice({
  name: "crowdMap",
  initialState,
  reducers: {
    clearCrowdMap: (state) => {
      state.rectangles = [];
    },
    setFetchingCrowdMapData(state, action: PayloadAction<boolean>) {
      state.fetchingData = action.payload;
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchCrowdMapData.pending, (state) => {
        state.status = StatusEnum.Pending;
        state.error = null;
      })
      .addCase(
        fetchCrowdMapData.fulfilled,
        (state, action: PayloadAction<RectangleData[]>) => {
          state.status = StatusEnum.Fulfilled;
          state.rectangles = action.payload;
          state.fetchingData = false;
          state.error = null;
        }
      )
      .addCase(
        fetchCrowdMapData.rejected,
        (state, action: PayloadAction<ApiError | undefined>) => {
          state.status = StatusEnum.Rejected;
          state.error = action.payload || { message: "Unknown error occurred" };
          state.fetchingData = false;
        }
      );
  },
});

export const { clearCrowdMap, setFetchingCrowdMapData } = crowdMapSlice.actions;

export default crowdMapSlice.reducer;

export const selectCrowdMapRectangles = (state: RootState) =>
  state.crowdMap.rectangles;
export const selectFetchingCrowdMapData = (state: RootState) =>
  state.crowdMap.fetchingData;