src\contexts\DetailedTripReviewContext.tsx
unknown
typescript
a year ago
14 kB
5
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