Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#91 Date picker error handling #168

Merged
merged 6 commits into from
Aug 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/components/trays/model-selection/catalogItems.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@ export default function CatalogItems(data) {
</div>
);
}
else if(data.data['catalog'] === undefined || data.data['catalog'] === null) {
return (
<div>
Error: { 'No catalog data retrieved.' }
</div>
);
}

// return all the data cards
else {
// save the name of the element for tropical storms and advisory numbers
Expand Down
149 changes: 116 additions & 33 deletions src/components/trays/model-selection/synopticTab.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Fragment, useState, useEffect } from 'react';
import React, { Fragment, useState, useEffect, useMemo } from 'react';
import { Button, Divider, Select, Stack } from '@mui/joy';
import { DatePicker } from '@mui/x-date-pickers';
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
Expand All @@ -8,6 +8,7 @@ import axios from 'axios';
import DropDownOptions from "@model-selection/DropDownOptions";
import CatalogItems from "@model-selection/catalogItems";
import { getNamespacedEnvParam, getBrandingHandler } from "@utils/map-utils";
import dayjs from 'dayjs';

/**
* Form to filter/select synoptic runs
Expand All @@ -18,21 +19,39 @@ import { getNamespacedEnvParam, getBrandingHandler } from "@utils/map-utils";
export const SynopticTabForm = () => {

// declare all state variables for the synoptic tab dropdown data
const [synopticDate, setSynopticDate] = useState(null);
const [synopticDate, setSynopticDate] = useState(dayjs(new Date()));
const [synopticCycle, setSynopticCycle] = useState(null);
const [synopticGrid, setSynopticGrid] = useState(null);
const [synopticInstance, setSynopticInstance] = useState(null);

// init the data urls
const rootUrl = `${ getNamespacedEnvParam('REACT_APP_UI_DATA_URL') }`;
const basePulldownUrl = `get_pulldown_data?met_class=synoptic&use_v3_sp=true${ getBrandingHandler() }`;
const basePulldownUrl = `get_pulldown_data?met_class=synoptic${ getBrandingHandler() }`;
const baseDataUrl = `get_ui_data_secure?limit=14&met_class=synoptic&use_v3_sp=true${ getBrandingHandler() }`;
const [finalDataUrl, setFinalDataUrl] = useState(rootUrl + basePulldownUrl);

// storage for received data to render pulldowns
const [dropDownData, setDropDownData] = useState(null);
const [catalogData, setCatalogData] = useState(null);

// state for the date validation error
const [error, setError] = useState(null);

const setChangedSynopticDate = (newValue) => {
// if there was a valid date
if (!isNaN(newValue) && newValue !== null) {
//console.log('setChangedSynopticDate Good date: ' + newValue.$d + ', error: ' + error + ', newValue: ' + newValue);
// save the date
setSynopticDate(newValue.$d);
}
// else handle a bad date
else {
//console.log('setChangedSynopticDate Bad date: ' + newValue);
// clear the date
setSynopticDate(null);
}
};

/**
* method to initiate a model search with the filter selections on the synoptic form
*
Expand All @@ -42,6 +61,8 @@ export const SynopticTabForm = () => {
// avoid doing the usual form submit operations
event.preventDefault();

//console.log('formSynopticSubmit error: ' + error);

// build the query string from the submitted form data
let queryString =
((synopticDate) ? '&run_date=' + synopticDate.toISOString() : '') +
Expand Down Expand Up @@ -79,27 +100,45 @@ export const SynopticTabForm = () => {
};

// make the call to get the data
const {data} = await axios.get(finalDataUrl, requestOptions);
const ret_val = await axios
// make the call to get the data
.get(finalDataUrl, requestOptions)
// use the data returned
.then (( response ) => {
// return the data
return response.data;
})
.catch (( error ) => {
// make sure we do not render anything
return error.response.status;
});

// check the request type
if (finalDataUrl.indexOf('get_pulldown_data') !== -1) {
// save the dropdown data
setDropDownData(data);
}
else {
// save the catalog data
setCatalogData(data);
// if (finalDataUrl.indexOf('get_pulldown_data') !== -1)
// console.log('finalDataUrl: ' + finalDataUrl);

// if there was an error from the web service
if (ret_val !== 500) {
// check the request type to save it to the correct place
if (finalDataUrl.indexOf('get_pulldown_data') !== -1) {
// save the dropdown data
setDropDownData(ret_val);
}
else {
// save the catalog data
setCatalogData(ret_val);
}
}
else
// reset the form data
resetForm();

// return something
return true;
},

refetchOnWindowFocus: false
}, refetchOnWindowFocus: false
});

/**
* this will build the data url and will cause a DB hit
* this will build the data url and will cause a web service/DB hit
*/
useEffect(() => {
// build the new data url
Expand Down Expand Up @@ -159,53 +198,97 @@ export const SynopticTabForm = () => {
* @returns {boolean}
*/
const disableDate = (date) => {
// return false if the date is not found in the list of available dates
return !dropDownData['run_dates'].includes(date.toISOString().split("T")[0]);
if (dropDownData) {
// return false if the date is not found in the list of available dates
return !dropDownData['run_dates'].includes(date.toISOString().split("T")[0]);
}
};

/**
* handles date picker error text
*
* @type {string}
*/
const errorMessage = useMemo(() => {
//console.log('errorMessage error: ' + error);

switch (error) {
case 'maxDate': {
return 'Please select a date that is not in the future';
}

case 'minDate': {
return 'Please select a date after 01/01/2020';
}

case 'invalidDate': {
return 'This date is not yet valid';
}

case 'shouldDisableDate': {
return 'There is no data available on this date';
}

default: {
return '';
}
}

}, [error]);

/**
* return the rendered component
*/
return (
<Fragment>
<form name={"Synoptic"} onSubmit={formSynopticSubmit}>
<Stack spacing={1}>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<form name={ "Synoptic" } onSubmit={ formSynopticSubmit }>
<Stack spacing={ 1 }>
<LocalizationProvider dateAdapter={ AdapterDayjs }>
<DatePicker
defaultValue={ dayjs(new Date()) }
onError={ (newError) => setError(newError) }
disableFuture
name="synoptic-date"
shouldDisableDate={disableDate}
slotProps={{textField: {size: 'small'}, field: {clearable: true}}}
shouldDisableDate={ disableDate }
minDate={ dayjs('2023-06-01') }
slotProps={{
textField: { size: 'small', helperText: errorMessage},
field: { clearable: true },
actionBar: { actions: ['clear'] },
}}

onChange={(newValue) => {
setSynopticDate(newValue.$d);
setChangedSynopticDate(newValue);
}}/>

</LocalizationProvider>

<Select name="synoptic-cycle" value={synopticCycle} placeholder="Please select a cycle" onChange={(e, newValue) => {
<Select name="synoptic-cycle" value={ synopticCycle } placeholder="Please select a cycle" onChange={ (e, newValue) => {
setSynopticCycle(newValue);
}}>
<DropDownOptions data={dropDownData} type={'cycles'}/>
<DropDownOptions data={ dropDownData } type={ 'cycles' }/>
</Select>

<Select name="synoptic-grid" value={synopticGrid} placeholder="Please select a grid" onChange={(e, newValue) => {
<Select name="synoptic-grid" value={ synopticGrid } placeholder="Please select a grid" onChange={ (e, newValue) => {
setSynopticGrid(newValue);
}}>
<DropDownOptions data={dropDownData} type={'grid_types'}/>
<DropDownOptions data={ dropDownData } type={ 'grid_types' }/>
</Select>

<Select name="synoptic-instance" value={synopticInstance} placeholder="Please select an instance" onChange={(e, newValue) => {
<Select name="synoptic-instance" value={ synopticInstance } placeholder="Please select an instance" onChange={ (e, newValue) => {
setSynopticInstance(newValue);
}}>
<DropDownOptions data={dropDownData} type={'instance_names'}/>
<DropDownOptions data={ dropDownData } type={'instance_names'}/>
</Select>

<Button type="submit">Submit</Button>
<Button type="reset" onClick={resetForm}>Reset</Button>
<Button type="submit" disabled={ !!error }>Submit</Button>
<Button type="reset" onClick={ resetForm }>Reset</Button>
</Stack>

<Divider sx={{m: 2}}/>

<Stack>
{<CatalogItems data={catalogData} isTropical={false}/>}
{ <CatalogItems data={ catalogData } isTropical={ false }/> }
</Stack>
</form>
</Fragment>
Expand Down