diff --git a/src/components/presentational/HelpBox.jsx b/src/components/presentational/HelpBox.jsx new file mode 100644 index 0000000000000000000000000000000000000000..51bb4274c5b43099ef90b9b761e4bb292da29b7c --- /dev/null +++ b/src/components/presentational/HelpBox.jsx @@ -0,0 +1,74 @@ +import React, { useState } from 'react'; + +function RasterVsVectorBox() { + return ( + <div className="contentBox"> + <h3>Raster vs Vector</h3> + <p> This is some sort of information In Raster vs Vector</p> + </div> + ); +} + +function SortingBox() { + return ( + <div className="contentBox"> + <h3>Sorting</h3> + <p> This is some sort of information In Sorting</p> + </div> + ); +} + +function FootprintCardBox() { + return ( + <div className="contentBox"> + <h3>Footprint Card</h3> + <p> This is some sort of information in FootprintCard</p> + </div> + ); +} + +export default function HelpBox({ isOpen, onClose }) { + if(!isOpen) return null; + + const [showSubPopup, setShowSubPopup] = useState(null); + + const handleRasterVsVectorClick = () => { + setShowSubPopup('rasterVsVector'); + }; + + const handleSortingClick = () => { + setShowSubPopup('sorting'); + }; + + const handleFootprintCardClick = () => { + setShowSubPopup('footprintCard'); + }; + + return ( + <div id="helpBoxBackground"> + <div className="helpBoxContent"> + <h2>Help Menu</h2> + <button className="helpButton" onClick={handleRasterVsVectorClick}>Raster vs Vector</button> + <button className="helpButton" onClick={handleSortingClick}>Sorting</button> + <button className="helpButton" onClick={handleFootprintCardClick}>Footprint Card</button> + <button onClick={onClose} className="closeButton">Close</button> + </div> + + {showSubPopup === 'rasterVsVector' && + <div className="subPopup"> + <RasterVsVectorBox/> + </div> + } + {showSubPopup === 'sorting' && + <div className="subPopup"> + <SortingBox/> + </div> + } + {showSubPopup === 'footprintCard' && + <div className="subPopup"> + <FootprintCardBox/> + </div> + } + </div> + ); +} diff --git a/src/components/presentational/SearchAndFilterInput.jsx b/src/components/presentational/SearchAndFilterInput.jsx index 34b7cb25c2597a373fa3ff8e14f19a6ca29b918d..40d6f13ce12a7b05a2a201343837f264afe7dba5 100644 --- a/src/components/presentational/SearchAndFilterInput.jsx +++ b/src/components/presentational/SearchAndFilterInput.jsx @@ -19,6 +19,8 @@ import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward"; import { Collapse, Divider } from "@mui/material"; import ListItemText from "@mui/material/ListItemText"; + //help box +import HelpBox from "./HelpBox.jsx"; /** * Controls css styling for this component using js to css @@ -71,11 +73,11 @@ export default function SearchAndFilterInput(props) { // Allows showing/hiding of fields const [expandFilter, setExpandFilter] = React.useState(false); const [expandDate, setExpandDate] = React.useState(false); - + // Sort By const [sortVal, setSortVal] = React.useState(""); // Sort By What? const [sortAscCheckVal, setSortAscCheckVal] = React.useState(true); // Sort Ascending or Descending - + // Filter by X checkboxes const [areaCheckVal, setAreaCheckVal] = React.useState(false); // Area const [dateCheckVal, setDateCheckVal] = React.useState(false); // Date @@ -85,6 +87,9 @@ export default function SearchAndFilterInput(props) { const [dateFromVal, setDateFromVal] = React.useState(null); // From Date const [dateToVal, setDateToVal] = React.useState(null); // To Date + // help box + const [showHelpBox, setShowHelpBox] = React.useState(false); + //const for callback const {UpdateQueryableTitles} = props; const handleExpandFilterClick = () => { @@ -93,10 +98,10 @@ export default function SearchAndFilterInput(props) { const buildQueryString = () => { let myFilterString = "?"; - + // Date if (dateCheckVal) { - + let d = new Date(); const lowestYear = 1970; const highestYear = d.getFullYear(); @@ -151,11 +156,11 @@ export default function SearchAndFilterInput(props) { let pyGeoAPIFlag = false; // New state for queryable titles - const [queryableTitles, setQueryableTitles] = useState([]); - + const [queryableTitles, setQueryableTitles] = useState([]); + // all collections const collection = props.target.collections; - + // retrieves all PyGEO collections const isInPyAPI = collection.filter(data => data.hasOwnProperty('itemType')); @@ -165,9 +170,9 @@ export default function SearchAndFilterInput(props) { // retrieves all pyGEO titles const collectionTitles = isInPyAPI.map(data => data.title); - - - // checks if correct title selected + + + // checks if correct title selected if (collectionTitles.includes(props.selectedTitle)) { //set pyGeoAPI flag @@ -188,31 +193,31 @@ export default function SearchAndFilterInput(props) { // Extract the "properties" property from the JSON response let Queryables = data.properties; - + // loop over titles for (const property in Queryables) { if (Queryables.hasOwnProperty(property) && Queryables[property].hasOwnProperty("title")) { - + queryableTitlesArray.push(data.properties[property].title); - + } } // Set the state with the queryable titles setQueryableTitles(queryableTitlesArray); - - + + }, []) .catch(error => { console.error("Error fetching data:", error); }); } - - - + + + const [selectedOptions, setSelectedOptions] = useState([]); - + const handleOptionChange = event => { const selectedValues = event.target.value; setSelectedOptions(selectedValues); @@ -220,14 +225,14 @@ export default function SearchAndFilterInput(props) { // Create an array of objects with selected option and value const selectedOptionsWithValues = selectedValues.map((option) => ({ option, - value: queryableTitles.find((title) => title.title === option)?.value, + value: queryableTitles.find((title) => title.title === option)?.value, })); // Pass the selected options and values to FootprintResults - UpdateQueryableTitles(selectedOptionsWithValues); + UpdateQueryableTitles(selectedOptionsWithValues); }; - + // Sorting const handleSortChange = (event) => { setSortVal(event.target.value); @@ -283,11 +288,11 @@ export default function SearchAndFilterInput(props) { // If target is changed, reset filter values; useEffect(() => { - + // Sort By setSortVal(""); setSortAscCheckVal(true); - + // Filter by X checkboxes setAreaCheckVal(false); setDateCheckVal(false); @@ -299,7 +304,14 @@ export default function SearchAndFilterInput(props) { }, [props.targetName]); - + //help box + const handleOpenHelpBox = () => { + setShowHelpBox(true); + }; + + const handleCloseHelpBox = () => { + setShowHelpBox(false); + } /* Control IDs for reference: sortBySelect @@ -352,12 +364,12 @@ export default function SearchAndFilterInput(props) { /> </span> </div> - + {pyGeoAPIFlag && ( <div className="panelSection panelBar"> <span> - <FormControl sx={{ minWidth: 150 }}> - <InputLabel id="selectQueryLabel" size="small"> + <FormControl sx={{ minWidth: 150 , minHeight: 40}}> + <InputLabel id="selectQueryLabel" size="small" style={{paddingTop: '0.2rem'}}> Select Query </InputLabel> <Select @@ -367,6 +379,7 @@ export default function SearchAndFilterInput(props) { value={selectedOptions} onChange={handleOptionChange} renderValue={(selected) => selected.join(', ')} + style={{height: 43}} > {queryableTitles.map((title) => ( <MenuItem key={title} value={title}> @@ -442,6 +455,8 @@ export default function SearchAndFilterInput(props) { </Collapse> </div> </Collapse> + <button onClick={handleOpenHelpBox} style={{marginTop: "20px"}}>Help</button> + <HelpBox isOpen={showHelpBox} onClose={handleCloseHelpBox} /> </div> ); } diff --git a/src/js/FetchData.js b/src/js/FetchData.js index 2697f849baf2f9ebf1d1808019a5e74862ff6750..8708b2f4b6e619a68808da4ea69811b51472aad5 100644 --- a/src/js/FetchData.js +++ b/src/js/FetchData.js @@ -1,7 +1,7 @@ /** - * - * @returns + * + * @returns */ export default async function Initialize(){ @@ -10,10 +10,10 @@ export default async function Initialize(){ "https://astrowebmaps.wr.usgs.gov/webmapatlas/Layers/maps.json"; // STAC API, has footprint data for select planetary bodies - const stacApiCollections = + const stacApiCollections = "https://stac.astrogeology.usgs.gov/api/collections"; - const vectorApiCollections = + const vectorApiCollections = "https://astrogeology.usgs.gov/pygeoapi/collections"; // Async tracking @@ -66,7 +66,7 @@ export default async function Initialize(){ // Combine data from Astro Web Maps and STAC API into one new object function organizeData(astroWebMaps, stacApiCollections, vectorApiCollections) { - + // Initialize Objects let mapList = { "systems" : [] }; let stacList = []; @@ -112,17 +112,22 @@ export default async function Initialize(){ if (hasFootprints) { for (const collection of stacApiCollections.collections){ if (target.name == collection.summaries["ssys:targets"][0].toUpperCase()) { + // Add a specification to the title in order to show what kind of data the user is requesting + collection.title = collection.title.concat(" (Raster)") myCollections.push(collection); } } + for (const pycollection of vectorApiCollections.collections){ // view the collection as GEOJSON let target_name = pycollection.id.split('/')[0]; if (target.name == target_name.toUpperCase()) { pycollection.links[9].href = "https://astrogeology.usgs.gov/pygeoapi" + pycollection.links[9].href; - myCollections.push(pycollection); - } - } + // Add a specification to the title in order to show what kind of data the user is requesting + pycollection.title = pycollection.title.concat(" (Vector)"); + myCollections.push(pycollection); + } + } } // Add a body data entry @@ -211,7 +216,7 @@ export default async function Initialize(){ return mapList; } - // Fetch and organize data from + // Fetch and organize data from async function getStacAndAstroWebMapsData() { // Start fetching from AWM and STAC API concurrently ensureFetched(astroWebMaps); diff --git a/src/styles.css b/src/styles.css index 891787fc86e1da3cd722f071c1c0daf8ebeb69d6..776d8554c698bd9aa069245e1aa4b7a8a2f98244 100644 --- a/src/styles.css +++ b/src/styles.css @@ -340,7 +340,7 @@ Controls the CSS for projection buttons when there is no available projection .pyGeoResultContainer { width: 230px; - + } .resultSub { @@ -490,3 +490,75 @@ summary { opacity: 1; pointer-events: all; } + +/* +Help Box Inside Search and Filter +*/ +#helpBoxBackground { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.7); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +} + +.helpBoxContent, .subPopup { + width: 300px; + padding: 20px; + background-color: #fff; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + text-align: center; + display: flex; + flex-direction: column; + align-items: stretch; +} +.helpButton { + margin: 10px 0; + padding: 15px; + flex: 1; + border: none; + border-radius: 5px; + cursor: pointer; + background-color: #351370; + color: white; + font-weight: bold; + transition: background-color 0.3s; + + &:hover { + background-color: #113851; + } +} +.closeButton { + margin: 10px 0; + padding: 15px; + flex: 1; + border: none; + border-radius: 5px; + cursor: pointer; + background-color: #e10e19; + color: white; + font-weight: bold; + transition: background-color 0.8s ease; + + &:hover { + background-color: #980000; + } +} + +.contentBox h3 { + font-size: 24px; + margin-bottom: 20px; + color: #333; +} + +.contentBox p { + font-size: 16px; + line-height: 1.5; + color: #555; +}