src/contexts/SpeedLimitStoreContext.tsx
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