src\contexts\DetailedTripReviewContext.tsx
unknown
typescript
2 years ago
14 kB
11
Indexable
import React, { createContext } from 'react'
import { convertUnit, createSelectors } from '@Utils'
import { StoreApi, createStore } from 'zustand'
import { useLocation, useParams } from 'react-router-dom'
import { useDispatch, useSelector } from 'react-redux'
import {
fetchStationsByRouteId,
fetchTrip,
fetchTripDetails
} from '@Redux/slices/portalDataSlice'
import {
getFeaturizersInterpolateReverifyPath,
getFeaturizersTripInformationOnPath
} from '@GraphQL/Api'
import Papa from 'papaparse'
import _ from 'lodash'
type TDetailedTripReviewStore = {
tripId: string | undefined
stcId: string | undefined
trainId: string | undefined
crewId: string | undefined
startDateUTC: string | undefined
endDateUTC: string | undefined
startLocation: string | undefined
endLocation: string | undefined
routeId: string | undefined
tripDurationSecs: number | undefined
dataAvailability: string[] | null | undefined
consistWeight: number | undefined
consistLength: number | undefined
extraLocomotiveInfo: any | undefined
numberOfLocos: number | undefined
numberOfCars: number | undefined
tripInformation:
| {
lat: number
lng: number
speed: number
speedLimit: number
}[]
| null
showMapPopup: boolean
selectedMovement: {
index: number
info: any
} | null
selectedStations:
| {
lat: number
lng: number
abbreviation: string
location_name: number
}[]
| null
mapModes: { key: string; label: string }[]
mapMode: { key: string; label: string }
mapZoom: number
mapCenter: { lat: number; lng: number }
loadingCsvGPSData: boolean | undefined
loadingCsvInterpolateData: boolean | undefined
csvGPSData: Papa.ParseResult<any> | null
csvInterpolateData: Papa.ParseResult<any> | null
pickedXAxisProperty: string | null
pickedYAxisProperties: string[]
pickedVisualizeFile: string | null
pickedVisualizeDataCells: string[]
pickedVisualizeLatitude: string | null
pickedVisualizeLongitude: string | null
pickedVisualizeLatLngs: { lat: string; lng: string }[]
showOutputVisualizeModal: boolean
triggerOutputVisualize: boolean
downloadFileSize: number
loadedFileSize: number
actions: {}
}
export const DetailedTripReviewStoreContext = createContext<
StoreApi<TDetailedTripReviewStore> | undefined
>(undefined)
export const createDetailedTripReviewStore = () => {
return createStore<TDetailedTripReviewStore>((set, get) => ({
loadingCsvGPSData: undefined,
loadingCsvInterpolateData: undefined,
tripId: undefined,
stcId: undefined,
trainId: undefined,
crewId: undefined,
startDateUTC: undefined,
endDateUTC: undefined,
startLocation: undefined,
endLocation: undefined,
routeId: undefined,
tripDurationSecs: undefined,
dataAvailability: undefined,
tripInformation: null,
showMapPopup: false,
selectedMovement: null,
selectedStations: null,
consistWeight: undefined,
consistLength: undefined,
extraLocomotiveInfo: undefined,
numberOfLocos: undefined,
numberOfCars: undefined,
mapModes: [
{
key: 'speed-profile',
label: 'Speed profile'
},
{
key: 'speed-relative-to-speed-limit',
label: 'Speed relative to speed limit'
}
],
mapMode: {
key: 'speed-profile',
label: 'Speed profile'
},
csvGPSData: null,
csvInterpolateData: null,
pickedXAxisProperty: null,
pickedYAxisProperties: [],
pickedVisualizeFile: null,
pickedVisualizeDataCells: [],
pickedVisualizeLatitude: null,
pickedVisualizeLongitude: null,
pickedVisualizeLatLngs: [],
showOutputVisualizeModal: false,
triggerOutputVisualize: false,
mapZoom: 0,
mapCenter: { lat: 0, lng: 0 },
downloadFileSize: 0,
loadedFileSize: 0,
actions: {}
}))
}
export const DetailedTripReviewStoreProvider = ({
children
}: {
children: React.ReactNode
}) => {
const storeRef = React.useRef<StoreApi<TDetailedTripReviewStore>>()
if (!storeRef.current) {
storeRef.current = createDetailedTripReviewStore()
}
const storeSelectors = createSelectors(storeRef.current)
const startLocation = storeSelectors.use.startLocation()
const endLocation = storeSelectors.use.endLocation()
const routeId = storeSelectors.use.routeId()
const stcId = storeSelectors.use.stcId()
const showOutputVisualizeModal = storeSelectors.use.showOutputVisualizeModal()
const csvInterpolateData = storeSelectors.use.csvInterpolateData()
const triggerOutputVisualize = storeSelectors.use.triggerOutputVisualize()
const location: any = useLocation()
let { tripId } = useParams<{ tripId: string }>()
const state = location?.state
const dispatch = useDispatch()
const { localizationUnits } = useSelector((state: any) => state.portalData)
const {
data: { railroadId }
} = useSelector((state: any) => state.userInfo)
const {
trip: { data: tripData }
} = useSelector((state: any) => state.portalData)
const {
routesByRailroad: { data: routesData }
} = useSelector((state: any) => state.portalData!)
React.useEffect(() => {
// State from trips pages
storeSelectors.setState({
trainId: state?.trainId,
stcId: state?.stcId,
crewId: state?.crewId,
startDateUTC: state?.startDateUTC,
endDateUTC: state?.endDateUTC,
startLocation: state?.startLocation,
endLocation: state?.endLocation,
tripDurationSecs: state?.tripDurationSecs
})
if (state?.dataAvailability) {
storeSelectors.setState({
dataAvailability: state?.dataAvailability
})
}
if (tripId) {
storeSelectors.setState({ tripId: tripId })
}
if (
!state?.trainId ||
!state?.stcId ||
!state?.crewId ||
!state?.startDateUTC ||
!state?.endDateUTC ||
!state?.startLocation ||
!state?.endLocation ||
!state?.tripDurationSecs ||
state?.dataAvailability === undefined ||
!routeId
) {
dispatch(fetchTrip({ tripId: tripId }))
}
}, [])
React.useEffect(() => {
// State from trip details query
if (tripData && tripData?.trip_id === tripId) {
storeSelectors.setState({
trainId: tripData?.train_id,
stcId: tripData?.simple_train_consist_id,
crewId: tripData?.crew_id,
startDateUTC: tripData?.start_date_utc,
endDateUTC: tripData?.end_date_utc,
startLocation: tripData?.route_id?.split('_')[0],
endLocation: tripData?.route_id?.split('_')[1],
routeId: tripData?.route_id,
tripDurationSecs: tripData?.trip_duration_secs,
dataAvailability: tripData.data_availability,
consistWeight: tripData?.total_weight_kg,
consistLength: tripData?.total_length_m,
numberOfLocos: tripData?.locomotive_count,
numberOfCars: tripData?.car_count,
extraLocomotiveInfo: tripData.extra_locomotive_info
})
}
}, [tripData])
React.useEffect(() => {
dispatch(fetchTripDetails({ tripId }))
}, [tripId])
React.useEffect(() => {
if (routeId) return
if (!routesData) return
const targetRouteData = routesData.find((route: any) => {
return (
(route.route_id.startsWith(startLocation) &&
route.route_id.endsWith(endLocation)) ||
(route.route_id.startsWith(endLocation) &&
route.route_id.endsWith(startLocation))
)
})
if (!targetRouteData) return
storeSelectors.setState({ routeId: targetRouteData.route_id })
}, [routesData, startLocation, endLocation, railroadId])
React.useEffect(() => {
if (!routeId) return
dispatch(
fetchStationsByRouteId({
railroadId: railroadId,
routeId: routeId
})
)
}, [routeId])
React.useEffect(() => {
if (!stcId) {
storeSelectors.setState({
tripInformation: null
})
return
}
;(async () => {
try {
storeSelectors.setState({
loadingCsvGPSData: true
})
const csvUrl = await getFeaturizersTripInformationOnPath({
expirySecs: 60,
simpleTrainConsistId: stcId
}).then(res => res.presigned_url)
const csvText = await fetch(csvUrl)
.then(response => response.text())
.then(text => {
return text
})
React.startTransition(() => {
const csvData = Papa.parse<any>(csvText, {
header: true
})
storeSelectors.setState({
csvGPSData: {
...csvData,
data: _.values(
_.chain(csvData.data)
.omitBy(item =>
Object.values(item).some(
value => _.isNaN(value) || value === ''
)
)
.value()
)
}
})
const _tripInformation = csvData.data.map((row: any) => {
// Temporary logic - TODO: should be based on tier
if (row?.evr_gps_filled_latitude_track_deg) {
return {
lat: parseFloat(row?.evr_gps_filled_latitude_track_deg),
lng: parseFloat(row?.evr_gps_filled_longitude_track_deg),
speed: convertUnit({
measurementType: 'speed_unit',
value: Math.max(parseFloat(row?.evr_gps_speed_gps_mph), 0),
fromUnit: 'mph',
toUnit: localizationUnits.data?.speed_unit || 'mph',
decimalPlaces: 0
}).value,
speedLimit: convertUnit({
measurementType: 'speed_unit',
value: Math.max(parseFloat(row?.speed_limit_mph), 0),
fromUnit: 'mph',
toUnit: localizationUnits.data?.speed_unit || 'mph',
decimalPlaces: 0
}).value
}
}
return {
lat: parseFloat(row?.app_gps_filled_latitude_track_deg),
lng: parseFloat(row?.app_gps_filled_longitude_track_deg),
speed: convertUnit({
measurementType: 'speed_unit',
value: Math.max(parseFloat(row?.app_gps_speed_gps_mph), 0),
fromUnit: 'mph',
toUnit: localizationUnits.data?.speed_unit || 'mph',
decimalPlaces: 0
}).value,
speedLimit: convertUnit({
measurementType: 'speed_unit',
value: Math.max(parseFloat(row?.speed_limit_mph), 0),
fromUnit: 'mph',
toUnit: localizationUnits.data?.speed_unit || 'mph',
decimalPlaces: 0
}).value
}
})
const _tripInformationValidated = _.chain(_tripInformation)
.omitBy(
item =>
_.isNaN(item.lat) || _.isNaN(item.lng) || _.isNaN(item.speed)
)
.value()
const _uniqueTripInformationValidated = _.uniqBy(
_.values(_tripInformationValidated),
item => `${item.lat},${item.lng}`
)
storeSelectors.setState({
tripInformation: _uniqueTripInformationValidated
})
})
} catch (error) {
throw new Error(`Oops, can't fetch GPS: ${error}`)
} finally {
storeSelectors.setState({
loadingCsvGPSData: false
})
}
})()
}, [stcId])
React.useEffect(() => {
if (csvInterpolateData) return
if (!stcId) return
if (!triggerOutputVisualize) return
;(async () => {
try {
storeSelectors.setState({
loadingCsvInterpolateData: true
})
const csvUrl = await getFeaturizersInterpolateReverifyPath({
expirySecs: 60,
simpleTrainConsistId: stcId
}).then(res => res.presigned_url)
const csvText = await fetch(csvUrl)
.then(response => {
const responseClone = response.clone()
let loadedMB = 0
const reader = response?.body?.getReader()
const contentLength = response?.headers.get('Content-Length')
storeRef.current?.setState({
downloadFileSize: parseInt(contentLength || '0') / 1024 / 1024
})
reader?.read().then(function processData(result): any {
if (result.done) {
return
}
loadedMB += result.value.length
storeRef.current?.setState({
loadedFileSize: loadedMB / 1024 / 1024
})
return reader.read().then(processData)
})
return responseClone.text()
})
.then(text => {
return text
})
React.startTransition(() => {
const csvData = Papa.parse<any>(csvText, {
header: true
})
storeSelectors.setState({
csvInterpolateData: csvData
})
})
} catch (error) {
throw new Error(
`Oops, can't fetch CSV Interpolate Reverify Data: ${error}`
)
} finally {
storeSelectors.setState({
loadingCsvInterpolateData: false
})
}
})()
}, [stcId, triggerOutputVisualize])
return (
<DetailedTripReviewStoreContext.Provider value={storeRef.current}>
{children}
</DetailedTripReviewStoreContext.Provider>
)
}
export const useDetailedTripReviewStore = () => {
const context = React.useContext(DetailedTripReviewStoreContext)
if (!context) {
throw new Error(
'DetailedTripReviewStore must be used within a DetailedTripReviewStoreProvider'
)
}
return createSelectors(context)
}
Editor is loading...
Leave a Comment