+
-
+
{
onStationsChange={onStationsChange}
/>
-
-
+
)
}
+
+export default React.memo(FireWeatherPage)
diff --git a/src/features/fireWeather/slices/modelsSlice.ts b/src/features/fireWeather/slices/modelsSlice.ts
new file mode 100644
index 00000000..b9ba678c
--- /dev/null
+++ b/src/features/fireWeather/slices/modelsSlice.ts
@@ -0,0 +1,63 @@
+import { createSlice, PayloadAction } from '@reduxjs/toolkit'
+
+import { Model, getModels, ModelValue } from 'api/modelAPI'
+import { AppThunk } from 'app/store'
+import { isNoonInPST } from 'utils/date'
+
+interface State {
+ loading: boolean
+ error: string | null
+ modelsByStation: Record
+ noonModelsByStation: Record
+ models: Model[]
+}
+
+const initialState: State = {
+ loading: false,
+ error: null,
+ modelsByStation: {},
+ noonModelsByStation: {},
+ models: []
+}
+
+const modelsSlice = createSlice({
+ name: 'models',
+ initialState,
+ reducers: {
+ getModelsStart(state: State) {
+ state.loading = true
+ },
+ getModelsFailed(state: State, action: PayloadAction) {
+ state.loading = false
+ state.error = action.payload
+ },
+ getModelsSuccess(state: State, action: PayloadAction) {
+ state.loading = false
+ state.error = null
+ state.models = action.payload
+ action.payload.forEach(model => {
+ if (model.station) {
+ const code = model.station.code
+ state.modelsByStation[code] = model.values
+ state.noonModelsByStation[code] = model.values.filter(v =>
+ isNoonInPST(v.datetime)
+ )
+ }
+ })
+ }
+ }
+})
+
+export const { getModelsStart, getModelsFailed, getModelsSuccess } = modelsSlice.actions
+
+export default modelsSlice.reducer
+
+export const fetchModels = (stationCodes: number[]): AppThunk => async dispatch => {
+ try {
+ dispatch(getModelsStart())
+ const forecasts = await getModels(stationCodes)
+ dispatch(getModelsSuccess(forecasts))
+ } catch (err) {
+ dispatch(getModelsFailed(err))
+ }
+}
diff --git a/src/features/fireWeather/slices/readingsSlice.ts b/src/features/fireWeather/slices/readingsSlice.ts
new file mode 100644
index 00000000..149c1893
--- /dev/null
+++ b/src/features/fireWeather/slices/readingsSlice.ts
@@ -0,0 +1,60 @@
+import { createSlice, PayloadAction } from '@reduxjs/toolkit'
+
+import { Reading, ReadingValue, getReadings } from 'api/readingAPI'
+import { AppThunk } from 'app/store'
+
+interface State {
+ loading: boolean
+ error: string | null
+ readingsByStation: Record
+ readings: Reading[]
+}
+
+const initialState: State = {
+ loading: false,
+ error: null,
+ readingsByStation: {},
+ readings: []
+}
+
+const readingsSlice = createSlice({
+ name: 'readings',
+ initialState,
+ reducers: {
+ getReadingsStart(state: State) {
+ state.loading = true
+ },
+ getReadingsFailed(state: State, action: PayloadAction) {
+ state.loading = false
+ state.error = action.payload
+ },
+ getReadingsSuccess(state: State, action: PayloadAction) {
+ state.loading = false
+ state.error = null
+ state.readings = action.payload
+ action.payload.forEach(reading => {
+ if (reading.station) {
+ state.readingsByStation[reading.station.code] = reading.values
+ }
+ })
+ }
+ }
+})
+
+export const {
+ getReadingsStart,
+ getReadingsFailed,
+ getReadingsSuccess
+} = readingsSlice.actions
+
+export default readingsSlice.reducer
+
+export const fetchReadings = (stationCodes: number[]): AppThunk => async dispatch => {
+ try {
+ dispatch(getReadingsStart())
+ const readings = await getReadings(stationCodes)
+ dispatch(getReadingsSuccess(readings))
+ } catch (err) {
+ dispatch(getReadingsFailed(err))
+ }
+}
diff --git a/src/features/hourlies/components/HourlyReadingsDisplay.tsx b/src/features/hourlies/components/HourlyReadingsDisplay.tsx
deleted file mode 100644
index e7a8463a..00000000
--- a/src/features/hourlies/components/HourlyReadingsDisplay.tsx
+++ /dev/null
@@ -1,140 +0,0 @@
-import React from 'react'
-import { useSelector } from 'react-redux'
-import { makeStyles } from '@material-ui/core/styles'
-
-import Table from '@material-ui/core/Table'
-import TableBody from '@material-ui/core/TableBody'
-import TableCell from '@material-ui/core/TableCell'
-import TableContainer from '@material-ui/core/TableContainer'
-import TableRow from '@material-ui/core/TableRow'
-import Paper from '@material-ui/core/Paper'
-import Typography from '@material-ui/core/Typography'
-import moment from 'moment'
-
-import { selectHourlies } from 'app/rootReducer'
-import { ErrorMessage } from 'components/ErrorMessage'
-import { HOURLY_VALUES_DECIMAL, PDT_UTC_OFFSET } from 'utils/constants'
-
-const useStyles = makeStyles({
- table: {
- minWidth: 650
- },
- station: {
- paddingTop: 8,
- paddingLeft: 16
- },
- hourlies: {
- marginBottom: 20
- }
-})
-
-export const HourlyReadingsDisplay = () => {
- const classes = useStyles()
- const { error, hourlies } = useSelector(selectHourlies)
-
- if (error) {
- return (
-
- )
- }
-
- if (hourlies.length === 0) {
- return null
- }
-
- return (
-
- {hourlies.map(({ station, values }) => (
-
-
- Hourly readings for weather station: {`${station.name} (${station.code})`}
-
-
-
-
-
- Date (PDT)
- {values.map(v => (
-
- {moment(v.datetime)
- .utcOffset(PDT_UTC_OFFSET)
- .format('YYYY-MM-DD HH:mm')}
-
- ))}
-
-
- Temperature
- {values.map(v => (
-
- {v.temperature.toFixed(HOURLY_VALUES_DECIMAL)}
-
- ))}
-
-
- Relative Humidity
- {values.map(v => (
-
- {Math.round(v.relative_humidity)}
-
- ))}
-
-
- Wind Speed
- {values.map(v => (
-
- {v.wind_speed.toFixed(HOURLY_VALUES_DECIMAL)}
-
- ))}
-
-
- Wind Direction
- {values.map(v => (
-
- {Math.round(v.wind_direction)}
-
- ))}
-
-
- Precipitation
- {values.map(v => (
-
- {v.precipitation.toFixed(HOURLY_VALUES_DECIMAL)}
-
- ))}
-
-
- FFMC
- {values.map(v => (
-
- {v.ffmc?.toFixed(HOURLY_VALUES_DECIMAL)}
-
- ))}
-
-
- ISI
- {values.map(v => (
-
- {v.isi?.toFixed(HOURLY_VALUES_DECIMAL)}
-
- ))}
-
-
- FWI
- {values.map(v => (
-
- {v.fwi?.toFixed(HOURLY_VALUES_DECIMAL)}
-
- ))}
-
-
-
-
-
- ))}
-
- )
-}
diff --git a/src/features/hourlies/slices/HourliesSlice.ts b/src/features/hourlies/slices/HourliesSlice.ts
deleted file mode 100644
index 03407ac7..00000000
--- a/src/features/hourlies/slices/HourliesSlice.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { createSlice, PayloadAction } from '@reduxjs/toolkit'
-
-import { HourlyReadings, getHourlies } from 'api/hourliesAPI'
-import { AppThunk } from 'app/store'
-
-interface State {
- loading: boolean
- error: string | null
- hourlies: HourlyReadings[]
-}
-
-const initialState: State = {
- loading: false,
- error: null,
- hourlies: []
-}
-
-const hourlies = createSlice({
- name: 'hourlies',
- initialState,
- reducers: {
- getHourliesStart(state: State) {
- state.loading = true
- },
- getHourliesFailed(state: State, action: PayloadAction) {
- state.loading = false
- state.error = action.payload
- },
- getHourliesSuccess(state: State, action: PayloadAction) {
- state.loading = false
- state.hourlies = action.payload
- }
- }
-})
-
-export const {
- getHourliesStart,
- getHourliesFailed,
- getHourliesSuccess
-} = hourlies.actions
-
-export default hourlies.reducer
-
-export const fetchHistoricalReadings = (
- stationCodes: number[]
-): AppThunk => async dispatch => {
- try {
- dispatch(getHourliesStart())
- const hourlies = await getHourlies(stationCodes)
- dispatch(getHourliesSuccess(hourlies))
- } catch (err) {
- dispatch(getHourliesFailed(err))
- }
-}
diff --git a/src/features/percentileCalculator/pages/PercentileCalculatorPage.tsx b/src/features/percentileCalculator/pages/PercentileCalculatorPage.tsx
index 59e4db9e..e3bd2387 100644
--- a/src/features/percentileCalculator/pages/PercentileCalculatorPage.tsx
+++ b/src/features/percentileCalculator/pages/PercentileCalculatorPage.tsx
@@ -4,7 +4,7 @@ import { useDispatch } from 'react-redux'
import { Station } from 'api/stationAPI'
import { PageHeader, PageTitle, Container } from 'components'
import { fetchWxStations } from 'features/stations/slices/stationsSlice'
-import { WxStationDropdown } from 'features/stations/components/WxStationDropdown'
+import WxStationDropdown from 'features/stations/components/WxStationDropdown'
import { PercentileTextfield } from 'features/percentileCalculator/components/PercentileTextfield'
import {
fetchPercentiles,
diff --git a/src/features/stations/components/WxStationDropdown.tsx b/src/features/stations/components/WxStationDropdown.tsx
index bbfbef54..7ab76c7d 100644
--- a/src/features/stations/components/WxStationDropdown.tsx
+++ b/src/features/stations/components/WxStationDropdown.tsx
@@ -33,7 +33,7 @@ interface Props {
maxNumOfSelect?: number
}
-export const WxStationDropdown = (props: Props) => {
+const WxStationDropdown = (props: Props) => {
const classes = useStyles()
const { stations, error } = useSelector(selectStations)
const isError = Boolean(error)
@@ -87,3 +87,5 @@ export const WxStationDropdown = (props: Props) => {
)
}
+
+export default React.memo(WxStationDropdown)
diff --git a/src/features/stations/slices/stationsSlice.ts b/src/features/stations/slices/stationsSlice.ts
index 39c037c4..4ff1a02c 100644
--- a/src/features/stations/slices/stationsSlice.ts
+++ b/src/features/stations/slices/stationsSlice.ts
@@ -14,7 +14,7 @@ export const initialState: StationsState = {
stations: []
}
-const stations = createSlice({
+const stationsSlice = createSlice({
name: 'stations',
initialState: initialState,
reducers: {
@@ -37,9 +37,9 @@ export const {
getStationsStart,
getStationsFailed,
getStationsSuccess
-} = stations.actions
+} = stationsSlice.actions
-export default stations.reducer
+export default stationsSlice.reducer
export const fetchWxStations = (): AppThunk => async dispatch => {
try {
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index 4f76c1a1..203c18c2 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -11,8 +11,8 @@ export const WEATHER_STATION_MAP_LINK =
export const FWI_VALUES_DECIMAL = 2
-export const FORECAST_VALUES_DECIMAL = 1
+export const MODEL_VALUE_DECIMAL = 1
export const HOURLY_VALUES_DECIMAL = 1
-// The Pacific Daylight Time offest is -7 hours from UTC.
-export const PDT_UTC_OFFSET = -7
+export const PDT_UTC_OFFSET = -7 // The Pacific Daylight Time offest is -7 hours from UTC.
+export const PST_UTC_OFFSET = -8
diff --git a/src/utils/date.ts b/src/utils/date.ts
new file mode 100644
index 00000000..315780f9
--- /dev/null
+++ b/src/utils/date.ts
@@ -0,0 +1,14 @@
+import moment from 'moment'
+
+import { PST_UTC_OFFSET, PDT_UTC_OFFSET } from 'utils/constants'
+
+export const isNoonInPST = (dt: string) =>
+ moment(dt)
+ .utc()
+ .hour() ===
+ Math.abs(PST_UTC_OFFSET) + 12
+
+export const datetimeInPDT = (dt: string | number, format?: string) =>
+ moment(dt)
+ .utcOffset(PDT_UTC_OFFSET)
+ .format(format || 'YYYY-MM-DD HH:mm')