src/contexts/SpeedLimitStoreContext.tsx

 avatar
unknown
typescript
5 months ago
22 kB
4
Indexable
import {
  mutationAddNewTSOSpeedLimit,
  mutationDeleteTSOSpeedLimitById,
  mutationEditTSOSpeedLimitById
} from '@Redux/slices/portalActionSlice'
import { fetchSpeedLimitsDataByRouteId } from '@Redux/slices/portalDataSlice'
import { store } from '@Redux/store'
import { getTranslation } from '@Translations'
import { createSelectors } from '@Utils'
import { produce } from 'immer'
import { v4 as uuidv4 } from 'uuid'
import { create } from 'zustand'
import {
  Direction,
  speedLimitItemType,
  SpeedLimitType,
  StatusTypes,
  StatusUpdate
} from '../ui/components/widgets/SpeedLimitSection/types'
import {
  checkInvalidMileposts,
  findMaxTSOLimit,
  findOverlappingMileposts
} from '../ui/components/widgets/SpeedLimitSection/utils/milepostsUtil'

type TSpeedLimitState = {
  speedLimits: speedLimitItemType[] | null
  tempSpeedLimits: speedLimitItemType[] | null
  updateSpeedLimits: speedLimitItemType[] | null
  zoneSpeedLimits: speedLimitItemType[] | null
  fetchData: ({
    routeId,
    railroadId,
    startDestinationId,
    endDestinationId
  }: {
    routeId?: string
    railroadId: string
    startDestinationId: string
    endDestinationId: string
  }) => Promise<void>
  initializeTempData: () => void
  initializeNewSpeedLimit: (payload: speedLimitItemType) => void
  updateTempSpeedLimits: (
    id: string,
    field: keyof speedLimitItemType,
    value: string | number | null
  ) => void
  addNewTSOSpeedLimit: (payload: any) => void
  editTSOSpeedLimit: (payload: any) => void
  deleteTSOSpeedLimit: (payload: any) => void
  // sortSpeedLimit: () => void
  removeFirstSpeedLimit: () => void
  // editSpeedLimitItem: (item: speedLimitItemType, index: number) => void
  changeStatusSpeedLimit: (payload: { id: string; status: StatusTypes }) => void
  removeProperty: (prop: keyof speedLimitItemType) => void
  removePropertyById: (prop: keyof speedLimitItemType, id: string) => void
  checkValidLimit: (id: string, type: StatusTypes) => void
  checkValidMp: (
    id: string,
    name: keyof {
      start_mile?: number
      new_start_mile?: number | null
      end_mile?: number
      new_end_mile?: number | null
    },
    value: string
  ) => void
  checkRangeMps: (id: string, startLimit: number, endLimit: number) => void
  findOverlapMileposts: (id: string, type: StatusTypes) => void
  createUpdateList: () => void
  deleteTempTSOSpeedLimitById: (id: string) => void
  callApiForUpdate: () => void
}

const generateId = (): string => {
  return uuidv4()
}

export const useSpeedLimitStore = createSelectors(
  create<TSpeedLimitState>()((set, get) => ({
    speedLimits: null,
    tempSpeedLimits: null,
    updateSpeedLimits: null,
    zoneSpeedLimits: null,
    fetchData: async ({
      routeId,
      railroadId,
      startDestinationId,
      endDestinationId
    }) => {
      const dispatch = store.dispatch
      const res: any = await dispatch(
        fetchSpeedLimitsDataByRouteId({
          routeId: routeId,
          railroadId: railroadId,
          startDestinationId: startDestinationId,
          endDestinationId: endDestinationId
        })
      )
    },
    initializeTempData: () =>
      set(state =>
        produce(state, draft => {
          draft.tempSpeedLimits = [...(state?.speedLimits || [])]
        })
      ),
    initializeNewSpeedLimit: payload => {
      set(
        produce((state: TSpeedLimitState) => {
          const newSpeedLimit: speedLimitItemType = {
            newId: generateId(),
            status: StatusTypes.New,
            direction: Direction.ASC,
            route_id: payload.route_id,
            subdivision: payload.subdivision,
            subdivision_data: payload.subdivision_data,
            speed_limit_mph: undefined,
            start_mile: undefined,
            end_mile: undefined,
            type: SpeedLimitType.TSO,
            start_limit: payload.start_limit,
            end_limit: payload.end_limit
          }
          const existNew = state?.tempSpeedLimits?.find(
            item => item.status === StatusTypes.New
          )
          !existNew && state?.tempSpeedLimits?.unshift(newSpeedLimit)
        })
      )
    },
    updateTempSpeedLimits: (id, field, value) => {
      set(
        produce(state => {
          const row = state?.tempSpeedLimits?.find(
            (row: speedLimitItemType) =>
              row.temporary_speed_limit_id === id || row?.newId === id
          )
          if (row) {
            row[field] = value
          }
        })
      )
    },
    addNewTSOSpeedLimit: async payload => {
      const dispatch = store.dispatch

      // if (
      //   !payload?.subdivision ||
      //   !payload?.routeId ||
      //   !payload?.startMilepost ||
      //   !payload?.endMilepost ||
      //   !payload?.
      // ) {
      //   console.error('Missing necessary payload fields:', payload)
      //   return
      // }

      const res: any = await dispatch(
        mutationAddNewTSOSpeedLimit({
          subdivision: payload?.subdivision,
          speedLimitType: payload?.speedLimitType,
          speedLimitMph: payload?.limit,
          routeId: payload?.routeId,
          startMilepost: payload?.startMilepost,
          endMilepost: payload?.endMilepost,
          direction: payload?.direction
        })
      )

      set(
        produce((state: TSpeedLimitState) => {
          const speedLimit = state.updateSpeedLimits?.find(
            item => item?.newId === payload?.id
          )
          console.log('🚀 ~ produce ~ speedLimit:', speedLimit)

          if (speedLimit) {
            if (!res?.error) {
              speedLimit.statusUpdate = StatusUpdate.Success
            } else {
              speedLimit.statusUpdate = StatusUpdate.Failure
              console.error('Error during mutation:', res?.error)
            }
          } else {
            console.warn(
              'SpeedLimit item not found for update:',
              payload?.newId
            )
          }
        })
      )
    },
    editTSOSpeedLimit: async payload => {
      const dispatch = store.dispatch
      const res: any = await dispatch(
        mutationEditTSOSpeedLimitById({
          temporarySpeedLimitId: payload.temporarySpeedLimitId,
          subdivision: payload?.subdivision,
          speedLimitType: payload?.speedLimitType,
          speedLimitMph: payload?.limit,
          routeId: payload?.routeId,
          startMilepost: payload?.startMilepost,
          endMilepost: payload?.endMilepost,
          direction: payload?.direction
        })
      )
      set(
        produce((state: TSpeedLimitState) => {
          const speedLimit = state.updateSpeedLimits?.find(
            item =>
              item?.temporary_speed_limit_id === payload?.temporarySpeedLimitId
          )

          if (speedLimit) {
            if (!res?.error) {
              speedLimit.statusUpdate = StatusUpdate.Success
            } else {
              speedLimit.statusUpdate = StatusUpdate.Failure
              console.error('Error during mutation:', res?.error)
            }
          } else {
            console.warn(
              'SpeedLimit item not found for update:',
              payload?.newId
            )
          }
        })
      )
    },
    deleteTSOSpeedLimit: async payload => {
      console.log('🚀 ~ payload:', payload)
      const dispatch = store.dispatch
      const res: any = await dispatch(
        mutationDeleteTSOSpeedLimitById({
          temporarySpeedLimitId: payload?.temporary_speed_limit_id
        })
      )
      set(
        produce((state: TSpeedLimitState) => {
          const speedLimit = state.updateSpeedLimits?.find(
            item =>
              item?.temporary_speed_limit_id ===
              payload?.temporary_speed_limit_id
          )
          console.log('🚀 ~ produce ~ speedLimit:', speedLimit)

          if (speedLimit) {
            if (!res?.error) {
              speedLimit.statusUpdate = StatusUpdate.Success
            } else {
              speedLimit.statusUpdate = StatusUpdate.Failure
              console.error('Error during mutation:', res?.error)
            }
          } else {
            console.warn(
              'SpeedLimit item not found for update:',
              payload?.newId
            )
          }
        })
      )
    },
    // sortSpeedLimit: () =>
    //   set(
    //     produce((state: TSpeedLimitState) => {
    //       state.speedLimits?.sort(
    //         (a, b) => a?.start_distance_miles - b?.start_distance_miles
    //       )
    //     })
    //   ),
    removeFirstSpeedLimit: () =>
      set(
        produce((state: TSpeedLimitState) => {
          state.speedLimits?.shift()
        })
      ),
    changeStatusSpeedLimit: async payload => {
      set(
        produce((state: TSpeedLimitState) => {
          const speedLimit = state.tempSpeedLimits?.find(
            item =>
              item?.temporary_speed_limit_id === payload?.id ||
              item?.newId === payload?.id
          )
          if (speedLimit) {
            speedLimit.status = payload.status
          }
        })
      )
    },
    removeProperty: prop => {
      set(
        produce((state: TSpeedLimitState) => {
          const propName: keyof speedLimitItemType = prop
          if (state.speedLimits && typeof state.speedLimits === 'object') {
            state.speedLimits.forEach(obj => {
              if (obj.hasOwnProperty(prop)) {
                delete obj[propName]
              }
            })
          }
        })
      )
    },
    removePropertyById: (prop, id) => {
      set(
        produce((state: TSpeedLimitState) => {
          const propName: keyof speedLimitItemType = prop
          if (
            state?.tempSpeedLimits &&
            typeof state?.tempSpeedLimits === 'object'
          ) {
            state?.tempSpeedLimits.forEach(obj => {
              if (obj.temporary_speed_limit_id === id) {
                if (obj.hasOwnProperty(prop)) {
                  delete obj[propName]
                }
              }
            })
          }
        })
      )
    },
    checkValidLimit: (id, type) => {
      set(
        produce((state: TSpeedLimitState) => {
          const { zoneSpeedLimits } = get()

          const speedLimit = state?.tempSpeedLimits?.find(
            item => item?.temporary_speed_limit_id === id || item?.newId === id
          )

          if (speedLimit) {
            const {
              speed_limit_mph,
              new_speed_limit_mph,
              start_mile,
              new_start_mile,
              end_mile,
              new_end_mile,
              subdivision,
              new_subdivision
            } = speedLimit

            switch (type) {
              case StatusTypes.New:
                if (start_mile && end_mile && speed_limit_mph) {
                  const TSOLimit = findMaxTSOLimit(
                    zoneSpeedLimits || [],
                    { start: start_mile, end: end_mile },
                    subdivision || ''
                  )
                  console.log(TSOLimit)

                  speedLimit.speed_limit_message =
                    speed_limit_mph > TSOLimit
                      ? `TSO limit cannot exceed Zone speed limit ${TSOLimit} mph`
                      : ''
                } else {
                  speedLimit.speed_limit_message = ''
                }
                break
              case StatusTypes.Edit:
                if (new_start_mile && new_end_mile && new_speed_limit_mph) {
                  const TSOLimit = findMaxTSOLimit(
                    zoneSpeedLimits || [],
                    { start: new_start_mile, end: new_end_mile },
                    new_subdivision || ''
                  )
                  console.log(TSOLimit)

                  speedLimit.speed_limit_message =
                    parseFloat(new_speed_limit_mph) > TSOLimit
                      ? `TSO limit cannot exceed Zone speed limit ${TSOLimit} mph`
                      : ''
                } else {
                  speedLimit.speed_limit_message = ''
                }
            }
          }
        })
      )
    },

    checkValidMp: (id, name, value) => {
      set(
        produce((state: TSpeedLimitState) => {
          const speedLimit = state.tempSpeedLimits?.find(
            item => item?.temporary_speed_limit_id === id || item?.newId === id
          )
          if (speedLimit) {
            const regex = /^\d{1,3}(\.\d{0,2})?$/
            let newValue = value

            if (value.length > 3 && value.indexOf('.') === -1) {
              newValue = value.slice(0, 3)
            }

            if (!regex.test(newValue)) {
              let roundedValue = parseFloat(newValue).toFixed(2)
              if (!isNaN(parseFloat(roundedValue))) {
                newValue = roundedValue.toString()
              } else {
                newValue = ''
              }
            }

            speedLimit[name] = newValue
          }
        })
      )
    },
    checkRangeMps: (id, startLimit, endLimit) => {
      set(
        produce((state: TSpeedLimitState) => {
          const speedLimit = state.tempSpeedLimits?.find(
            item => item?.temporary_speed_limit_id === id || item?.newId === id
          )
          if (speedLimit) {
            const { start_mile, end_mile, new_start_mile, new_end_mile } =
              speedLimit
            if (new_start_mile || new_end_mile) {
              if (new_start_mile && new_end_mile) {
                const isOutRange = checkInvalidMileposts(
                  {
                    startMile: Number(new_start_mile),
                    endMile: Number(new_end_mile)
                  },
                  startLimit?.toString() || '',
                  endLimit?.toString() || ''
                )

                if (isOutRange) {
                  speedLimit.milepost_range_message = `${getTranslation(
                    'SpeedLimit.validMilepostsMessage'
                  )} ${startLimit} ${getTranslation(
                    'SpeedLimit.to'
                  )} ${endLimit}`
                } else {
                  speedLimit.milepost_range_message = ''
                }
              }
            } else if (start_mile && end_mile) {
              const isOutRange = checkInvalidMileposts(
                {
                  startMile: Number(start_mile),
                  endMile: Number(end_mile)
                },
                startLimit?.toString() || '',
                endLimit?.toString() || ''
              )
              if (isOutRange) {
                speedLimit.milepost_range_message = `${getTranslation(
                  'SpeedLimit.validMilepostsMessage'
                )} ${startLimit} ${getTranslation('SpeedLimit.to')} ${endLimit}`
              } else {
                speedLimit.milepost_range_message = ''
              }
            }
          }
        })
      )
    },
    findOverlapMileposts: (id, type) => {
      set(
        produce((state: TSpeedLimitState) => {
          const speedLimit = state.tempSpeedLimits?.find(
            item => item?.temporary_speed_limit_id === id || item?.newId === id
          )
          if (speedLimit) {
            const {
              start_mile,
              new_start_mile,
              end_mile,
              new_end_mile,
              subdivision,
              new_subdivision
            } = speedLimit

            switch (type) {
              case StatusTypes.New:
                if (start_mile && end_mile) {
                  const overlapMpList = findOverlappingMileposts(
                    {
                      startMile: parseFloat(start_mile!),
                      endMile: parseFloat(end_mile!)
                    },
                    state.tempSpeedLimits!,
                    subdivision!,
                    id
                  )

                  speedLimit.overlap_milepost_list =
                    overlapMpList?.length !== 0 ? overlapMpList : null
                }
                break
              case StatusTypes.Edit:
                if (new_start_mile && new_end_mile) {
                  const overlapMpList = findOverlappingMileposts(
                    {
                      startMile: parseFloat(new_start_mile!),
                      endMile: parseFloat(new_end_mile!)
                    },
                    state.tempSpeedLimits!,
                    new_subdivision!,
                    id
                  )

                  speedLimit.overlap_milepost_list =
                    overlapMpList?.length !== 0 ? overlapMpList : null
                }
            }
          }
        })
      )
    },
    createUpdateList: () => {
      set(
        produce((state: TSpeedLimitState) => {
          // const indexOfEdit = state.tempSpeedLimits?.findIndex(item => {
          //   return item?.status === StatusTypes.Edit
          // })
          // console.log('🚀 ~ indexOfEdit ~ indexOfEdit:', indexOfEdit)
          // if (indexOfEdit === -1) {
          state.updateSpeedLimits =
            state?.tempSpeedLimits?.filter(item => {
              return (
                item?.status &&
                item?.status !== StatusTypes.New &&
                item?.status !== StatusTypes.Edit
              )
            }) || []
          // }
        })
      )
    },
    deleteTempTSOSpeedLimitById: id => {
      set(
        produce((state: TSpeedLimitState) => {
          const index = state?.tempSpeedLimits?.findIndex(
            (speedLimit: speedLimitItemType) => speedLimit?.newId === id
          )
          if (index !== -1 && index) {
            state?.tempSpeedLimits?.splice(index, 1)
          }
        })
      )
    },
    callApiForUpdate: async () => {
      set(
        produce((state: TSpeedLimitState) => {
          state.updateSpeedLimits =
            state.updateSpeedLimits?.map((item: speedLimitItemType) => ({
              ...item,
              statusUpdate: StatusUpdate.Loading
            })) || []
        })
      )

      const addSpeedLimit = get().addNewTSOSpeedLimit
      const editSpeedLimit = get().editTSOSpeedLimit
      const deleteSpeedLimit = get().deleteTSOSpeedLimit

      const processSpeedLimitItem = async (item: speedLimitItemType) => {
        if (!item) return

        switch (item.status) {
          case StatusTypes.NewAdded:
            await addSpeedLimit({
              id: item?.newId,
              subdivision: item?.subdivision || '',
              speedLimitType: item?.type || '',
              limit: item?.speed_limit_mph || 0,
              routeId: item?.route_id || '',
              startMilepost: parseFloat(item?.start_mile!) || 0,
              endMilepost: parseFloat(item?.end_mile!) || 0,
              direction: Direction.ASC
            })
            break

          case StatusTypes.Edited:
            const payload = {
              temporarySpeedLimitId: item.temporary_speed_limit_id,
              subdivision: item?.new_subdivision || item?.subdivision || '',
              speedLimitType: item?.type || '',
              speedLimitMph: item?.new_speed_limit_mph
                ? parseFloat(item?.new_speed_limit_mph)
                : item?.speed_limit_mph || 0,
              routeId: item?.route_id || '',
              startMilepost: item?.new_start_mile || item?.start_mile || '0',
              endMilepost: item?.new_end_mile || item?.end_mile || '0',
              direction: item?.direction || Direction.ASC
            }

            await editSpeedLimit({
              temporarySpeedLimitId: payload?.temporarySpeedLimitId!,
              subdivision: payload?.subdivision!,
              speedLimitType: payload?.speedLimitType!,
              limit: payload?.speedLimitMph!,
              routeId: payload?.routeId!,
              startMilepost: parseFloat(payload?.startMilepost!) || 0,
              endMilepost: parseFloat(payload?.endMilepost!) || 0,
              direction: payload.direction!
            })
            break

          case StatusTypes.Delete:
            await deleteSpeedLimit(item)
            break

          default:
            console.warn('Unknown status type:', item.status)
        }
      }

      const updateData = get().updateSpeedLimits
      const deleteItems =
        updateData?.filter(item => item.status === StatusTypes.Delete) || []
      const addItems =
        updateData?.filter(item => item.status === StatusTypes.NewAdded) || []
      const editItems =
        updateData?.filter(item => item.status === StatusTypes.Edited) || []

      for (const item of deleteItems) {
        await processSpeedLimitItem(item)
      }

      for (const item of editItems) {
        await processSpeedLimitItem(item)
      }

      for (const item of addItems) {
        await processSpeedLimitItem(item)
      }
    }
  }))
)
Editor is loading...
Leave a Comment