diff --git a/src/components/dialog/observation-chart.js b/src/components/dialog/observation-chart.js
index f9d96231..bf894b56 100644
--- a/src/components/dialog/observation-chart.js
+++ b/src/components/dialog/observation-chart.js
@@ -15,6 +15,17 @@ export default function ObservationChart(url) {
return ();
}
+/**
+ * this suppresses the re-chart errors on the x/y-axis rendering.
+ *
+ * @type {{(message?: any, ...optionalParams: any[]): void, (...data: any[]): void}}
+ */
+const error = console.error;
+console.error = (...args) => {
+ if (/defaultProps/.test(args[0])) return;
+ error(...args);
+};
+
/**
* Retrieves and returns the chart data in json format
*
diff --git a/src/components/model-selection/DropDownOptions.js b/src/components/model-selection/DropDownOptions.js
new file mode 100644
index 00000000..5ea4537a
--- /dev/null
+++ b/src/components/model-selection/DropDownOptions.js
@@ -0,0 +1,45 @@
+import React, {Fragment} from "react";
+import PropTypes from 'prop-types';
+import {Option} from '@mui/joy';
+
+/**
+ * returns a list of drop down options for that data/type.
+ *
+ * @param data
+ * @param type
+ * @constructor
+ */
+export default function DropDownOptions(data) {
+ // set component prop types
+ DropDownOptions.propTypes = { data: PropTypes.any };
+
+ // do not render if there is no data
+ if (data.data != null) {
+ // if there is a warning getting the result
+ if (data.data['Warning'] !== undefined) {
+ return (
+
+ Warning: {data.data['Warning']}
+
+ );
+ }
+ // if there is an error getting the result
+ else if(data.data['Error'] !== undefined) {
+ return (
+
+ Error: {data.data['Error']}
+
+ );
+ }
+ // return all the options
+ else {
+ return (
+
+ {data.data[data.type].filter(item => item !== "").map(item => (
+
+ ))}
+
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/components/model-selection/catalogItems.js b/src/components/model-selection/catalogItems.js
new file mode 100644
index 00000000..ac181a6d
--- /dev/null
+++ b/src/components/model-selection/catalogItems.js
@@ -0,0 +1,78 @@
+import React, {Fragment, useState} from "react";
+import PropTypes from 'prop-types';
+import {AccordionGroup, Accordion, AccordionSummary, AccordionDetails, Stack} from '@mui/joy';
+
+/**
+ * returns a list of drop down options for that data/type.
+ *
+ * @param data
+ * @param type
+ * @constructor
+ */
+export default function CatalogItems(data) {
+ // set component prop types
+ CatalogItems.propTypes = { data: PropTypes.any };
+
+ // create some state for what catalog accordian is expanded/not expanded
+ const [accordianIndex, setAccordianIndex] = useState(-1);
+
+ // do not render if there is no data
+ if (data.data != null) {
+ // if there is a warning getting the result
+ if (data.data['Warning'] !== undefined) {
+ return (
+
+ Warning: {data.data['Warning']}
+
+ );
+ }
+ // if there is an error getting the result
+ else if(data.data['Error'] !== undefined) {
+ return (
+
+ Error: {data.data['Error']}
+
+ );
+ }
+ // return all the data cards
+ else {
+ return (
+
+
+ {
+ data
+ .data['catalog']
+ .filter(catalogs => catalogs !== "")
+ .map((catalog, itemIndex) =>
+ (
+
+ {
+ setAccordianIndex(expanded ? itemIndex : null);
+ }}>
+
+
+ Model date: {catalog['id']}
+
+
+
+ {catalog['members'].map((member, memberIndex) => (
+
+ {member['id'] }
+
+ ))}
+
+
+
+ )
+ )
+ }
+
+
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/components/model-selection/modelSelectionForm.js b/src/components/model-selection/modelSelectionForm.js
deleted file mode 100644
index 4da50bd2..00000000
--- a/src/components/model-selection/modelSelectionForm.js
+++ /dev/null
@@ -1,179 +0,0 @@
-import React, {Fragment, useState} from 'react';
-import {Button, Divider, Option, Select, Stack, Tab, Tabs, TabList, TabPanel} from '@mui/joy';
-import {useLayers} from "@context";
-import {DatePicker} from '@mui/x-date-pickers';
-import {AdapterDayjs} from "@mui/x-date-pickers/AdapterDayjs";
-import {LocalizationProvider} from "@mui/x-date-pickers/LocalizationProvider";
-
-import dayjs from 'dayjs';
-
-// import {LayerCard} from "@components/trays/layers/layer-card";
-
-/**
- * This component renders the layer selection form
- *
- * @returns {JSX.Element}
- * @constructor
- */
-export const ModelSelectionForm = () => {
- // get references to the filtered layer state
- const {
- //filteredModelLayers
- } = useLayers();
-
- /**
- * method to initiate a model search with the filter selections on the tropical form
- *
- * @param event
- */
- const formTropicalHandler = (event) => {
- event.preventDefault();
- const formData = new FormData(event.target);
- const formJson = Object.fromEntries(formData.entries());
- alert(JSON.stringify(formJson));
- };
-
- /**
- * method to initiate a model search with the filter selections on the synoptic form
- *
- * @param event
- */
- const formSynopticHandler = (event) => {
- alert(synopticDate);
- event.preventDefault();
- const formData = new FormData(event.target);
- const formJson = Object.fromEntries(formData.entries());
- alert(JSON.stringify(formJson));
- };
-
- // declare state variables for all tropical tab controls
- const [tropicalStorm, setTropicalStorm] = useState('');
- const [tropicalAdvisory, setTropicalAdvisory] = useState('');
- const [tropicalGrid, setTropicalGrid] = useState('');
- const [tropicalInstance, setTropicalInstance] = useState('');
-
- // declare all state variables for the synoptic tab
- const [synopticDate, setSynopticDate] = useState(new Date());
- const [synopticCycle, setSynopticCycle] = useState('');
- const [synopticGrid, setSynopticGrid] = useState('');
- const [synopticInstance, setSynopticInstance] = useState('');
-
- /**
- * build the query string to ge the data
- */
- function buildQueryString(formData, event) {
- // ['grid_type', 'instance_name', 'met_class', 'storm_name', 'cycle', 'advisory_number', 'run_date']
-
- const queryString = tropicalStorm + tropicalAdvisory + tropicalGrid + tropicalInstance + synopticCycle + synopticGrid + synopticInstance;
-
- alert(queryString);
-
- event.preventDefault();
- const formJson = Object.fromEntries(formData.entries());
- alert(JSON.stringify(formJson));
- }
-
- // render the form
- return (
-
-
-
- Tropical
- Synoptic
-
-
-
-
-
-
-
-
-
-
- );
-};
-
-/**
- * this method populates the controls on the form.
- *
- */
-// const dataLoader = () => {
-//
-// };
diff --git a/src/components/model-selection/modelSelectionTray.js b/src/components/model-selection/modelSelectionTray.js
new file mode 100644
index 00000000..25560a69
--- /dev/null
+++ b/src/components/model-selection/modelSelectionTray.js
@@ -0,0 +1,40 @@
+import React, {Fragment} from 'react';
+import {Tab, Tabs, TabList, TabPanel} from '@mui/joy';
+import {SynopticTabForm} from "@model-selection/synopticTab";
+import {TropicalTabForm} from "@model-selection/tropicalTab";
+
+/**
+ * This component renders the layer selection form
+ *
+ * @returns {JSX.Element}
+ * @constructor
+ */
+export const ModelSelectionTray = () => {
+ // render the form
+ return (
+
+
+
+ Tropical
+ Synoptic
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+/**
+ * this method populates the controls on the form.
+ *
+ */
+// const dataLoader = () => {
+//
+// };
diff --git a/src/components/model-selection/synopticTab.js b/src/components/model-selection/synopticTab.js
new file mode 100644
index 00000000..f7004064
--- /dev/null
+++ b/src/components/model-selection/synopticTab.js
@@ -0,0 +1,175 @@
+import React, { Fragment, useState, useEffect } 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";
+import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
+import { useQuery } from '@tanstack/react-query';
+import axios from 'axios';
+import DropDownOptions from "@model-selection/DropDownOptions";
+import CatalogItems from "@model-selection/catalogItems";
+
+export const SynopticTabForm = () => {
+ // declare all state variables for the synoptic tab dropdown data
+ const [synopticDate, setSynopticDate] = useState('');
+ const [synopticCycle, setSynopticCycle] = useState('');
+ const [synopticGrid, setSynopticGrid] = useState('');
+ const [synopticInstance, setSynopticInstance] = useState('');
+
+ // init the data urls
+ const rootUrl = `${process.env.REACT_APP_UI_DATA_URL}`;
+ const basePulldownUrl = 'get_pulldown_data?met_class=synoptic&use_v3_sp=true';
+ const baseDataUrl = 'get_ui_data_secure?met_class=synoptic&use_v3_sp=true';
+ const [finalDataUrl, setFinalDataUrl] = useState(rootUrl + basePulldownUrl);
+
+ // storage for received data to render pulldowns
+ const [dropDownData, setDropDownData] = useState(null);
+ const [catalogData, setCatalogData] = useState(null);
+
+ /**
+ * method to initiate a model search with the filter selections on the synoptic form
+ *
+ * @param event
+ */
+ const formSynopticSubmit = (event) => {
+ // dont do the usual form submit operations
+ event.preventDefault();
+
+ // gather all the form data
+ const formData = new FormData(event.target);
+ const formJson = Object.fromEntries(formData.entries());
+
+ // build the query string from the submitted form data
+ const queryString =
+ ((formJson['synoptic-date'] !== "") ? '&run_date=' + formJson['synoptic-date'] : '') +
+ ((formJson['synoptic-cycle'] !== "") ? '&cycle=' + formJson['synoptic-cycle'] : '') +
+ ((formJson['synoptic-grid'] !== "") ? '&grid_type=' + formJson['synoptic-grid'] : '') +
+ ((formJson['synoptic-instance'] !== "") ? '&instance=' + formJson['synoptic-instance'] : '');
+
+ // set the url to go after ui data
+ setFinalDataUrl(rootUrl + baseDataUrl + queryString);
+ };
+
+ /**
+ * Retrieves and returns the model data in json format
+ *
+ * @param url
+ * @returns { json }
+ */
+ useQuery( {
+ // specify the data key and url to use
+ queryKey: ['apsviz-synoptic-model-data', finalDataUrl],
+
+ // create the function to call for data
+ queryFn: async () => {
+ // create the authorization header
+ const requestOptions = {
+ method: 'GET',
+ headers: {Authorization: `Bearer ${process.env.REACT_APP_UI_DATA_TOKEN}`}
+ };
+
+ // make the call to get the data
+ const {data} = await axios.get(finalDataUrl, requestOptions);
+
+ // check the request type
+ if (finalDataUrl.indexOf('get_pulldown_data') !== -1) {
+ // save the dropdown data
+ setDropDownData(data);
+ }
+ else {
+ // save the catalog data
+ setCatalogData(data);
+ }
+
+ // return something
+ return true;
+ }
+ });
+
+ /**
+ * this will build the data url and will cause a DB hit
+ */
+ useEffect(() => {
+ // build the new data url
+ buildDataUrl();
+ });
+
+ /**
+ * method to build the query sting to get data
+ *
+ */
+ function buildDataUrl() {
+ // init the query string
+ let query_string = '';
+
+ // set the query string
+ if (synopticDate !== '' && synopticDate != null) { query_string += '&run_date=' + synopticDate.toISOString().split("T")[0]; }
+
+ // set the query string
+ if (synopticCycle !== '' && synopticCycle != null) { query_string += '&cycle=' + synopticCycle; }
+
+ // set the query string
+ if (synopticGrid !== '' && synopticGrid != null) { query_string += '&grid_type=' + synopticGrid; }
+
+ // set the query string
+ if (synopticInstance !== '' && synopticInstance != null) { query_string += '&instance_name=' + synopticInstance; }
+
+ // set the pulldown data url. this will trigger a data gathering
+ setFinalDataUrl(rootUrl + basePulldownUrl + query_string);
+ }
+
+ /**
+ * filter the date picker to only allow certain dates to be selected
+ *
+ * @param date
+ * @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]);
+ };
+
+ /**
+ * return the rendered component
+ */
+ return (
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/src/components/model-selection/tropicalTab.js b/src/components/model-selection/tropicalTab.js
new file mode 100644
index 00000000..d9fedc50
--- /dev/null
+++ b/src/components/model-selection/tropicalTab.js
@@ -0,0 +1,167 @@
+import React, {Fragment, useState, useEffect} from 'react';
+import {Button, Divider, Select, Stack} from '@mui/joy';
+import { useQuery } from '@tanstack/react-query';
+import axios from 'axios';
+import DropDownOptions from "@model-selection/DropDownOptions";
+import CatalogItems from "@model-selection/catalogItems";
+
+export const TropicalTabForm = () => {
+ // declare state variables for all tropical tab controls
+ const [tropicalStorm, setTropicalStorm] = useState('');
+ const [tropicalAdvisory, setTropicalAdvisory] = useState('');
+ const [tropicalGrid, setTropicalGrid] = useState('');
+ const [tropicalInstance, setTropicalInstance] = useState('');
+
+
+ // init the data urls
+ const rootUrl = `${process.env.REACT_APP_UI_DATA_URL}`;
+ const basePulldownUrl = 'get_pulldown_data?met_class=tropical&use_v3_sp=true';
+ const baseDataUrl = 'get_ui_data_secure?met_class=tropical&use_v3_sp=true';
+ const [finalDataUrl, setFinalDataUrl] = useState(rootUrl + basePulldownUrl);
+
+ // storage for received data to render pulldowns
+ const [dropDownData, setDropDownData] = useState(null);
+ const [catalogData, setCatalogData] = useState(null);
+
+ /**
+ * method to initiate a model search with the filter selections on the tropical form
+ *
+ * @param event
+ */
+ const formTropicalHandler = (event) => {
+ // dont do the usual submit operations
+ event.preventDefault();
+
+ // gather all the form data
+ const formData = new FormData(event.target);
+ const formJson = Object.fromEntries(formData.entries());
+
+ // build the query string from the submitted form data
+ const queryString =
+ ((formJson['tropical-storm-name'] !== "") ? '&storm_name=' + formJson['tropical-storm-name'] : '') +
+ ((formJson['tropical-advisory'] !== "") ? '&advisory_number=' + formJson['tropical-advisory'] : '') +
+ ((formJson['tropical-grid'] !== "") ? '&grid_type=' + formJson['tropical-grid'] : '') +
+ ((formJson['tropical-instance'] !== "") ? '&instance=' + formJson['tropical-instance'] : '');
+
+ // set the url to go after ui data
+ setFinalDataUrl(rootUrl + baseDataUrl + queryString);
+ };
+
+ /**
+ * Retrieves and returns the dropdown data in json format
+ *
+ * @param url
+ * @returns { json }
+ */
+ // return the data to the caller
+ useQuery( {
+ // specify the data key and url to use
+ queryKey: ['apsviz-tropical-dropdown-data', finalDataUrl],
+
+ // create the function to call for data
+ queryFn: async () => {
+ // create the authorization header
+ const requestOptions = {
+ method: 'GET',
+ headers: {Authorization: `Bearer ${process.env.REACT_APP_UI_DATA_TOKEN}`}
+ };
+ // make the call to get the data
+ const {data} = await axios.get(finalDataUrl, requestOptions);
+
+ // check the request type
+ if (finalDataUrl.indexOf('get_pulldown_data') !== -1) {
+ // save the dropdown data
+ setDropDownData(data);
+ }
+ else {
+ // save the catalog data
+ setCatalogData(data);
+ }
+
+ // return something
+ return true;
+ }
+ });
+
+ /**
+ * this will build the data url and will cause a DB hit
+ */
+ useEffect(() => {
+ // build the data url
+ buildDropDownDataUrl();
+ });
+
+ /**
+ * resets the form
+ */
+ function resetForm() {
+ setTropicalStorm('');
+ setTropicalAdvisory('');
+ setTropicalGrid('');
+ setTropicalInstance('');
+
+ buildDropDownDataUrl();
+ }
+
+ /**
+ * method to build the query sting to get data
+ *
+ */
+ function buildDropDownDataUrl() {
+ // init the query string
+ let query_string = '';
+
+ // set the storm query string
+ if (tropicalStorm !== '' && tropicalStorm !== null) { query_string += '&storm_name=' + tropicalStorm; }
+
+ // set the advisory query string
+ if (tropicalAdvisory !== '' && tropicalAdvisory !== null) { query_string += '&advisory_number=' + tropicalAdvisory; }
+
+ // set the grin query string
+ if (tropicalGrid !== '' && tropicalGrid !== null) {query_string += '&grid_type=' + tropicalGrid; }
+
+ // set the instance query string
+ if (tropicalInstance !== '' && tropicalInstance !== null) {query_string += '&instance_name=' + tropicalInstance; }
+
+ // set the pulldown data url. this will trigger a data gathering
+ setFinalDataUrl(rootUrl + basePulldownUrl + query_string);
+ }
+
+ /**
+ * return the rendered component
+ */
+ return (
+
+
+ );
+};
diff --git a/src/components/trays/model-selection/model-selection.js b/src/components/trays/model-selection/model-selection.js
index 44f05193..badf05fd 100644
--- a/src/components/trays/model-selection/model-selection.js
+++ b/src/components/trays/model-selection/model-selection.js
@@ -1,5 +1,5 @@
import React, { Fragment } from 'react';
-import { ModelSelectionForm } from "@model-selection/modelSelectionForm.js";
+import { ModelSelectionTray } from "@model-selection/modelSelectionTray.js";
/**
* component that handles the selection of layers for the map.
@@ -11,7 +11,7 @@ export const ModelSelection = () => {
// render the layer selection component
return (
-
+
);
};