diff --git a/scss/custom.scss b/scss/custom.scss index 700b3fb..8ad80de 100644 --- a/scss/custom.scss +++ b/scss/custom.scss @@ -4,7 +4,6 @@ @import "node_modules/bootstrap/scss/mixins"; $theme-colors: ( - // "primary": #2275ac, "primary": red, "secondary": #24B363 ); \ No newline at end of file diff --git a/src/components/app/App.tsx b/src/components/app/App.tsx index 24f26b8..eaf6719 100644 --- a/src/components/app/App.tsx +++ b/src/components/app/App.tsx @@ -17,6 +17,7 @@ import GranuleSelectionAndConfigurationView from '../sidebar/GranuleSelectionAnd import Joyride from 'react-joyride'; import { deleteProduct } from '../sidebar/actions/productSlice'; import { tutorialSteps } from '../tutorial/tutorialConstants'; +import InteractiveTutorialModal from '../tutorial/InteractiveTutorialModal'; const App = () => { const dispatch = useAppDispatch() @@ -34,10 +35,8 @@ const App = () => { run: startTutorial, steps: tutorialSteps }) - useEffect(() => { setState({...joyride, run: startTutorial }) - }, [startTutorial]); const handleJoyrideCallback = (data: { action: any; index: any; status: any; type: any; step: any; lifecycle: any; }) => { @@ -45,7 +44,7 @@ const App = () => { const stepTarget = step.target if (stepTarget === '#configure-options-breadcrumb' && action === 'update') { navigate(`/customizeProduct/configureOptions${search}`) - } else if (stepTarget === '#configure-options-breadcrumb' && action === 'prev') { + } else if (stepTarget === '#configure-options-breadcrumb' && action === 'prev' && lifecycle === 'complete') { navigate(`/customizeProduct/selectScenes${search}`) } else if (stepTarget === '#my-data-page' && action === 'prev') { navigate(`/customizeProduct/configureOptions${search}`) @@ -60,6 +59,7 @@ const App = () => { dispatch(setStartTutorial(false)) navigate(`/customizeProduct/selectScenes`) } + // TODO: Make condition to load previous page when clicking previous before trying to target component to highlight. Use conditions "stepTarget === '#alert-messages' && action === 'prev' && lifecycle === 'init'" }; useEffect(() => { @@ -118,6 +118,7 @@ const App = () => { , true) } /> , true)}/> + ); } diff --git a/src/components/edl/AuthorizationCodeHandler.tsx b/src/components/edl/AuthorizationCodeHandler.tsx index 30e12be..5ada8e8 100644 --- a/src/components/edl/AuthorizationCodeHandler.tsx +++ b/src/components/edl/AuthorizationCodeHandler.tsx @@ -30,7 +30,6 @@ export default function AuthorizationCodeHandler(): ReactElement { } else if (ex instanceof TypeError) { console.debug('Network error') } - // TODO: improve this handling resetAuth(); }); diff --git a/src/components/history/GeneratedProductHistory.tsx b/src/components/history/GeneratedProductHistory.tsx index 0ff7986..8dfbee0 100644 --- a/src/components/history/GeneratedProductHistory.tsx +++ b/src/components/history/GeneratedProductHistory.tsx @@ -8,7 +8,6 @@ import { getUserProducts } from "../../user/userData"; import { useLocation, useNavigate } from "react-router-dom"; const GeneratedProductHistory = () => { - // const generatedProducts = useAppSelector((state) => state.product.generatedProducts) const colorModeClass = useAppSelector((state) => state.navbar.colorModeClass) const { search } = useLocation(); const navigate = useNavigate() @@ -66,7 +65,6 @@ const GeneratedProductHistory = () => { {userProducts.map((generatedProductObject, index) => { const {status, utmZoneAdjust, mgrsBandAdjust, outputGranuleExtentFlag, outputSamplingGridType, rasterResolution, timestamp: dateGenerated, cycle, pass, scene, granules} = generatedProductObject const statusToUse = status[0].state - // const downloadUrl = granules && granules.length !== 0 ? {granules[0].uri.split('/').pop()} : 'N/A' const downloadUrl = granules && granules.length !== 0 ? granules[0].uri.split('/').pop() : 'N/A' const utmZoneAdjustToUse = outputSamplingGridType === 'GEO' ? 'N/A' : utmZoneAdjust const mgrsBandAdjustToUse = outputSamplingGridType === 'GEO' ? 'N/A' : mgrsBandAdjust @@ -106,12 +104,6 @@ const GeneratedProductHistory = () => { } const renderProductHistoryViews = () => { - let viewToShow - // if (userProducts.length === 0) { - // viewToShow = productHistoryAlert() - // } else { - // viewToShow = renderHistoryTable() - // } return (

Generated Products Data

diff --git a/src/components/map/WorldMap.tsx b/src/components/map/WorldMap.tsx index 70e84c9..0ae56e4 100644 --- a/src/components/map/WorldMap.tsx +++ b/src/components/map/WorldMap.tsx @@ -1,4 +1,4 @@ -import { MapContainer, Polygon, TileLayer, Tooltip, ZoomControl, useMap, FeatureGroup } from 'react-leaflet' +import { MapContainer, Polygon, TileLayer, Tooltip, ZoomControl, useMap, FeatureGroup, useMapEvent } from 'react-leaflet' import L, { LatLngExpression } from 'leaflet'; import 'leaflet/dist/leaflet.css' import { useAppDispatch, useAppSelector } from '../../redux/hooks' @@ -20,6 +20,18 @@ let DefaultIcon = L.icon({ }); L.Marker.prototype.options.icon = DefaultIcon; +function UpdateMapCenter() { + const dispatch = useAppDispatch() + const mapFocus = useAppSelector((state) => state.product.mapFocus) + + const map = useMapEvent('moveend', () => { + const center = [map.getCenter().lat, map.getCenter().lng] + const zoom = map.getZoom() + if ((mapFocus.center[0] !== center[0] && mapFocus.center[1] !== center[1]) || mapFocus.zoom !== zoom) dispatch(setMapFocus({center, zoom})) + }) + return null +} + const WorldMap = () => { const addedProducts = useAppSelector((state) => state.product.addedProducts) const mapFocus = useAppSelector((state) => state.product.mapFocus) @@ -27,8 +39,8 @@ const WorldMap = () => { const footprintStyleOptions = { color: 'limegreen' } const ChangeView = () => { - const map = useMap(); - map.setView(mapFocus.center as LatLngExpression, mapFocus.zoom); + const map = useMap() + map.setView(mapFocus.center as LatLngExpression, mapFocus.zoom) return null } @@ -80,7 +92,6 @@ const WorldMap = () => { }))).map(foundIdString => { const cyclePassSceneStringArray = foundIdString?.split('_').map(id => parseInt(id).toString()) const tileValue = parseInt(cyclePassSceneStringArray?.[2] as string) - // const sceneToUse = String(Math.floor(tileValue / 2)) const sceneToUse = String(Math.floor(tileValue)) return {cycle: cyclePassSceneStringArray?.[0], pass: cyclePassSceneStringArray?.[1], scene : sceneToUse} as SpatialSearchResult }) @@ -115,7 +126,6 @@ const WorldMap = () => { {useLocation().pathname.includes('selectScenes') ? ( @@ -124,7 +134,6 @@ const WorldMap = () => { position="topright" onCreated={(createEvent) => onCreate(createEvent)} onEdited={(editEvent) => onEdit(editEvent)} - // onDeleted={(deleteEvent) => onDelete(deleteEvent)} draw={{ rectangle: false, polyline: false, @@ -142,11 +151,12 @@ const WorldMap = () => { attribution='Esri, Maxar, Earthstar Geographics, and the GIS User Community' maxZoom = {18} /> + {addedProducts.map((productObject, index) => ( - {[
{`Cycle: ${productObject.cycle}`}
,
{`Pass: ${productObject.pass}`}
,
{`Scene: ${productObject.scene}`}
]}
+ {[
{`Cycle: ${productObject.cycle}`}
,
{`Pass: ${productObject.pass}`}
,
{`Scene: ${productObject.scene}`}
]}
))}
diff --git a/src/components/sidebar/CustomizeProductsSidebar.tsx b/src/components/sidebar/CustomizeProductsSidebar.tsx index 32f243e..ee0eff9 100644 --- a/src/components/sidebar/CustomizeProductsSidebar.tsx +++ b/src/components/sidebar/CustomizeProductsSidebar.tsx @@ -8,7 +8,6 @@ import CustomizeProductView from './CustomizeProductView'; import GranuleSelectionView from './GranuleSelectionView'; import { CustomizeProductSidebarProps } from '../../types/constantTypes'; import { ArrowsExpand } from 'react-bootstrap-icons'; -import InteractiveTutorialModal from '../tutorial/InteractiveTutorialModal'; const CustomizeProductsSidebar = (props: CustomizeProductSidebarProps) => { const { mode } = props @@ -63,7 +62,6 @@ const CustomizeProductsSidebar = (props: CustomizeProductSidebarProps) => {
handleResizeClickDown(event)}> handleResizeClickDown(event)}/>
- ); } diff --git a/src/components/sidebar/DeleteGranulesModal.tsx b/src/components/sidebar/DeleteGranulesModal.tsx index 781f215..2ed6d47 100644 --- a/src/components/sidebar/DeleteGranulesModal.tsx +++ b/src/components/sidebar/DeleteGranulesModal.tsx @@ -4,13 +4,33 @@ import { useAppSelector, useAppDispatch } from '../../redux/hooks' import { setShowDeleteProductModalFalse } from './actions/modalSlice' import { Row } from 'react-bootstrap'; import { deleteProduct, setSelectedGranules } from './actions/productSlice'; +import { useSearchParams } from 'react-router-dom'; const GenerateProductsModal = () => { const showDeleteProductModal = useAppSelector((state) => state.modal.showDeleteProductModal) const selectedGranules = useAppSelector((state) => state.product.selectedGranules) const dispatch = useAppDispatch() + const [searchParams, setSearchParams] = useSearchParams() + + const removeCPSFromUrl = (cpsCombosToRemove: string[]) => { + const cyclePassSceneParameters = searchParams.get('cyclePassScene')?.split('-') + if (cyclePassSceneParameters) { + const cyclePassSceneParametersToKeep = cyclePassSceneParameters.filter(cpsCombo => !cpsCombosToRemove.includes(cpsCombo)).join('-') + const currentUrlParameters = Object.fromEntries(searchParams.entries()) + if (cyclePassSceneParametersToKeep.length === 0) { + const {cyclePassScene, ...restOfCurrentUrlParameters} = currentUrlParameters + setSearchParams(restOfCurrentUrlParameters) + } else { + setSearchParams({...currentUrlParameters, cyclePassScene: cyclePassSceneParametersToKeep}) + } + } + } + const handleDelete = () => { dispatch(deleteProduct(selectedGranules)) + // remove url parameters of selectedGranules + removeCPSFromUrl(selectedGranules) + // addSearchParamToCurrentUrlState dispatch(setSelectedGranules([])) // unselect select-all box dispatch(setShowDeleteProductModalFalse()) diff --git a/src/components/sidebar/GranuleSelectionAndConfigurationView.tsx b/src/components/sidebar/GranuleSelectionAndConfigurationView.tsx index 737f637..e8b766b 100644 --- a/src/components/sidebar/GranuleSelectionAndConfigurationView.tsx +++ b/src/components/sidebar/GranuleSelectionAndConfigurationView.tsx @@ -1,10 +1,22 @@ import CustomizeProductsSidebar from './CustomizeProductsSidebar'; import { GranuleSelectionAndConfigurationViewProps } from '../../types/constantTypes'; import WorldMap from '../map/WorldMap' +import { setShowTutorialModalTrue, setSkipTutorialTrue } from './actions/modalSlice'; +import { useEffect } from 'react'; +import { useAppDispatch, useAppSelector } from '../../redux/hooks'; const GranuleSelectionAndConfigurationView = (props: GranuleSelectionAndConfigurationViewProps) => { + const dispatch = useAppDispatch() + const skipTutorial = useAppSelector((state) => state.modal.skipTutorial) const {mode} = props + useEffect(() => { + if (!skipTutorial) { + dispatch(setShowTutorialModalTrue()) + dispatch(setSkipTutorialTrue()) + } + }, []); + return ( <> diff --git a/src/components/sidebar/GranuleSelectionView.tsx b/src/components/sidebar/GranuleSelectionView.tsx index ca17591..c88d913 100644 --- a/src/components/sidebar/GranuleSelectionView.tsx +++ b/src/components/sidebar/GranuleSelectionView.tsx @@ -1,28 +1,14 @@ -import { Button, Col, Row } from 'react-bootstrap'; -import { ArrowReturnRight} from 'react-bootstrap-icons'; import GranuleTable from './GranulesTable'; -import { useAppSelector } from '../../redux/hooks' import GranuleTableAlerts from './GranuleTableAlerts'; -import { useLocation, useNavigate } from 'react-router-dom'; import SpatialSearchOptions from './SpatialSearchOptions'; const GranuleSelectionView = () => { - const addedProducts = useAppSelector((state) => state.product.addedProducts) - const navigate = useNavigate(); - const { search } = useLocation(); - return ( - <> +
-
- - - - - - +
); } diff --git a/src/components/sidebar/GranulesTable.tsx b/src/components/sidebar/GranulesTable.tsx index b978246..4bb8342 100644 --- a/src/components/sidebar/GranulesTable.tsx +++ b/src/components/sidebar/GranulesTable.tsx @@ -5,7 +5,7 @@ import { granuleAlertMessageConstant, granuleSelectionLabels, productCustomizati footprintSearchCollectionConceptId } from '../../constants/rasterParameterConstants'; import { Button, Col, Form, OverlayTrigger, Row, Tooltip, Spinner } from 'react-bootstrap'; import { InfoCircle, Plus, Trash } from 'react-bootstrap-icons'; -import { AdjustType, AdjustValueDecoder, GranuleForTable, GranuleTableProps, InputType, SaveType, SpatialSearchResult, TableTypes, alertMessageInput, allProductParameters, validScene } from '../../types/constantTypes'; +import { AdjustType, AdjustValueDecoder, GranuleForTable, GranuleTableProps, InputType, SaveType, SpatialSearchResult, TableTypes, alertMessageInput, allProductParameters, handleSaveResult, validScene } from '../../types/constantTypes'; import { addProduct, setSelectedGranules, setGranuleFocus, addGranuleTableAlerts, editProduct, addSpatialSearchResults, setWaitingForFootprintSearch, clearGranuleTableAlerts } from './actions/productSlice'; import { setShowDeleteProductModalTrue } from './actions/modalSlice'; import DeleteGranulesModal from './DeleteGranulesModal'; @@ -33,7 +33,7 @@ const GranuleTable = (props: GranuleTableProps) => { const {outputSamplingGridType} = generateProductParameters // search parameters - const [searchParams, setSearchParams] = useSearchParams(); + const [searchParams, setSearchParams] = useSearchParams() // set the default url state parameters useEffect(() => { @@ -44,16 +44,6 @@ const GranuleTable = (props: GranuleTableProps) => { const sceneParamArray = Array.from(new Set(cyclePassSceneParameters.split('-'))) sceneParamArray.forEach((sceneParams, index) => { const splitSceneParams = sceneParams.split('_') - if (splitSceneParams.length > 3) { - // update zone and band adjust values - const zoneAdjustValue = adjustParamDecoder('value', splitSceneParams[3]) - const bandAdjustValue = adjustParamDecoder('value', splitSceneParams[4]) - const productToEdit = addedProducts.find(granuleObj => granuleObj.cycle === splitSceneParams[0] && granuleObj.pass === splitSceneParams[1] && granuleObj.scene === splitSceneParams[2]) - if (productToEdit?.utmZoneAdjust !== zoneAdjustValue || productToEdit?.mgrsBandAdjust !== bandAdjustValue) { - const editedProduct = {...productToEdit, utmZoneAdjust: zoneAdjustValue, mgrsBandAdjust: bandAdjustValue} - dispatch(editProduct(editedProduct as allProductParameters)) - } - } handleSave('urlParameter', sceneParamArray.length, index, splitSceneParams[0], splitSceneParams[1], splitSceneParams[2]) }) } @@ -63,20 +53,25 @@ const GranuleTable = (props: GranuleTableProps) => { dispatch(clearGranuleTableAlerts()) if (spatialSearchResults.length > 0) { let scenesFoundArray: string[] = [] + let addedScenes: string[] = [] const fetchData = async () => { for(let i=0; i { - - scenesFoundArray.push(result) + if(result.savedScenes) { + addedScenes.push(...(result.savedScenes).map(productObject => productObject.granuleId)) + } + scenesFoundArray.push(result.result) }) } + // add parameters + addSearchParamToCurrentUrlState({'cyclePassScene': addedScenes.join('-')}) return scenesFoundArray } // call the function fetchData() - .then((noScenesFoundResult) => { - if(scenesFoundArray.includes('noScenesFound') && !scenesFoundArray.includes('found something')){ + .then((noScenesFoundResults) => { + if(noScenesFoundResults.includes('noScenesFound') && !noScenesFoundResults.includes('found something')){ setSaveGranulesAlert('noScenesFound') } }) @@ -89,16 +84,50 @@ const GranuleTable = (props: GranuleTableProps) => { }, [spatialSearchResults]) const addSearchParamToCurrentUrlState = (newPairsObject: object, remove?: string) => { - const currentSearchParams = Object.fromEntries(searchParams.entries()) - Object.entries(newPairsObject).forEach(pair => { + const currentSearchParams = Object.fromEntries(searchParams.entries()) + const cyclePassSceneParameters = searchParams.get('cyclePassScene') + Object.entries(newPairsObject).forEach(pair => { + if (pair[0] === 'cyclePassScene') { + if(cyclePassSceneParameters !== null) { + // check if cps already exists in cyclePassSceneParameters + const currentCpsUrlSplit = cyclePassSceneParameters.split('-') + const paramsToAddSplit = pair[1].toString().split('-') + // let combinedParamsArray = currentCpsUrlSplit + let newParamsArray: string[] = [] + paramsToAddSplit.forEach((newParam: string) => { + if(!currentCpsUrlSplit.includes(newParam)) { + newParamsArray.push(newParam) + // if cps combo not already in url param, add it + // NOTE FOR WHEN I GET BACK: making sure no duplicates of cps + } + }) + if (newParamsArray.length > 0) { + // if cps without adjust params is in currentCpsUrlSplit and newParamsArray has cps with added adjust params + const newCPSParams: string[] = [] + newParamsArray.forEach(newParam => { + currentCpsUrlSplit.forEach(oldParam => { + const splitOldParam = oldParam.split('_') + if(!newParam.includes(`${splitOldParam[0]}_${splitOldParam[1]}_${splitOldParam[2]}`) && !newCPSParams.includes(oldParam)) { + // remove old param + newCPSParams.push(oldParam) + } + }) + }) + currentSearchParams[pair[0]] = [...newCPSParams, ...newParamsArray].join('-') + } + } else { currentSearchParams[pair[0]] = pair[1].toString() - }) - - // remove unused search param - if (remove) { - delete currentSearchParams[remove] + } + } else { + currentSearchParams[pair[0]] = pair[1].toString() } - setSearchParams(currentSearchParams) + }) + + // remove unused search param + if (remove) { + delete currentSearchParams[remove] + } + setSearchParams(currentSearchParams) } // add granules @@ -159,7 +188,6 @@ const validateSceneAvailability = async (cycleToUse: number, passToUse: number, // check for characters other than integers and one - if (inputValue.includes('-')) { const inputBoundsValue = inputValue.split('-') - // const allInputsValidNumbers = inputBoundsValue.every(inputString => !isNaN(+inputString)) const min: string = inputBoundsValue[0].trim() const max: string = inputBoundsValue[1].trim() const minIsValid = checkInBounds(inputType, min) @@ -270,7 +298,7 @@ const validateSceneAvailability = async (cycleToUse: number, passToUse: number, return cpsValueToReturn } - const handleSave = async (saveType: SaveType, totalRuns: number, index: number, cycleParam?: string, passParam?: string, sceneParam?: string): Promise => { + const handleSave = async (saveType: SaveType, totalRuns: number, index: number, cycleParam?: string, passParam?: string, sceneParam?: string): Promise => { if (saveType === 'manual') dispatch(clearGranuleTableAlerts()) setWaitingForScenesToBeAdded(true) // String(+(stringParam)) is used to remove the leading zeros @@ -287,10 +315,10 @@ const validateSceneAvailability = async (cycleToUse: number, passToUse: number, if (!validCycle) setSaveGranulesAlert('invalidCycle') if (!validPass) setSaveGranulesAlert('invalidPass') if (!validScene) setSaveGranulesAlert('invalidScene') - return 'first step' + return {result: 'first step'} } else if (addedProducts.length >= granuleTableLimit) { setSaveGranulesAlert('granuleLimit') - return 'second-step' + return {result: 'second-step'} } else { const granulesToAdd: allProductParameters[] = [] let someGranulesAlreadyAdded = false @@ -336,7 +364,7 @@ const validateSceneAvailability = async (cycleToUse: number, passToUse: number, someGranulesAlreadyAdded = true } }) - if (saveType !== 'spatialSearch') { + if (saveType !== 'spatialSearch' && saveType !== 'urlParameter') { // check if any granules could not be found or they were already added if (someGranulesAlreadyAdded) { setSaveGranulesAlert('alreadyAdded') @@ -364,38 +392,44 @@ const validateSceneAvailability = async (cycleToUse: number, passToUse: number, })) })).then(async productsWithFootprints => { // don't run time range check if granule was manually entered - const productsInTimeRange: allProductParameters[] = [] - const productsNotInTimeRange:allProductParameters[] = [] - productsWithFootprints.forEach(product => { - if (product.inTimeRange){ - delete product.inTimeRange - productsInTimeRange.push(product) - } else if (!product.inTimeRange) { - delete product.inTimeRange - productsNotInTimeRange.push(product) - } - }) - if (productsInTimeRange.length > 0) { - setSaveGranulesAlert('success') - dispatch(addProduct(productsInTimeRange)) + if (saveType === 'manual' || saveType === 'urlParameter') { addSearchParamToCurrentUrlState({'cyclePassScene': cyclePassSceneSearchParams}) - } - if (productsNotInTimeRange.length > 0) { - // set alerts for not in range - setSaveGranulesAlert('notInTimeRange') + if (saveType !== 'urlParameter') { + setSaveGranulesAlert('success') + } + dispatch(addProduct(productsWithFootprints)) + } else { + const productsInTimeRange: allProductParameters[] = [] + const productsNotInTimeRange:allProductParameters[] = [] + productsWithFootprints.forEach(product => { + if (product.inTimeRange){ + delete product.inTimeRange + productsInTimeRange.push(product) + } else if (!product.inTimeRange) { + delete product.inTimeRange + productsNotInTimeRange.push(product) + } + }) + if (productsInTimeRange.length > 0) { + setSaveGranulesAlert('success') + dispatch(addProduct(productsInTimeRange)) + } + if (productsNotInTimeRange.length > 0) { + // set alerts for not in range + setSaveGranulesAlert('notInTimeRange') + } } }) - return 'found something' + return {result: 'found something', savedScenes: granulesToAdd} } else { if (index+1 === totalRuns){ - return 'noScenesFound' + return {result: 'noScenesFound'} } else { - return 'not applicable' + return {result: 'not applicable'} } } }) return validationResult - // return 'third step' } } diff --git a/src/components/sidebar/ProductCustomization.tsx b/src/components/sidebar/ProductCustomization.tsx index 363e7eb..9b7ad43 100644 --- a/src/components/sidebar/ProductCustomization.tsx +++ b/src/components/sidebar/ProductCustomization.tsx @@ -81,7 +81,6 @@ const ProductCustomization = () => { if (showUTMAdvancedOptions) { handleShowUTMAdvancedOptions() } - } else { gridType = "rasterResolutionUTM" searchParamToRemove = "rasterResolutionGEO" @@ -113,13 +112,13 @@ const ProductCustomization = () => { if (outputSamplingGridType === 'utm') { return ( setRasterResolutionUTM(parseInt(event.target.value))}> - {parameterOptionValues.rasterResolutionUTM.values.map(parameterValue => )} + {parameterOptionValues.rasterResolutionUTM.values.map((parameterValue, index) => )} ) } else if (outputSamplingGridType === 'lat/lon') { return ( setRasterResolutionGEO(parseInt(event.target.value))}> - {parameterOptionValues.rasterResolutionGEO.values.map(parameterValue => )} + {parameterOptionValues.rasterResolutionGEO.values.map((parameterValue, index) => )} ) } @@ -162,6 +161,7 @@ const ProductCustomization = () => { type={'radio'} id={`outputSamplingGridTypeGroup-radio-${index}`} onChange={() => setOutputSamplingGridType(value as string, resolutionToUse)} + key={`outputSamplingGridTypeGroup-radio-key-${index}`} /> )} ) @@ -176,6 +176,7 @@ const ProductCustomization = () => { label={'advanced options'} style={{marginTop: '10px'}} disabled={!(outputSamplingGridType === 'utm')} + key={`outputGranuleExtentFlag-switch-key`} /> ) ) @@ -206,6 +207,7 @@ const ProductCustomization = () => { type={'radio'} id={`outputGranuleExtentFlagTypeGroup-radio-${index}`} onChange={() => setOutputGranuleExtentFlag(value)} + key={`outputGranuleExtentFlagTypeGroup-radio-key-${index}`} /> ) })} diff --git a/src/components/sidebar/actions/modalSlice.ts b/src/components/sidebar/actions/modalSlice.ts index 0587281..ae3a743 100644 --- a/src/components/sidebar/actions/modalSlice.ts +++ b/src/components/sidebar/actions/modalSlice.ts @@ -1,5 +1,4 @@ import { createSlice } from '@reduxjs/toolkit' -import { allProductParameters } from '../../../types/constantTypes' // Define a type for the slice state interface AddCustomProductModalState { @@ -7,7 +6,6 @@ interface AddCustomProductModalState { showEditProductModal: boolean, showDeleteProductModal: boolean, showGenerateProductModal: boolean, - addedProducts: allProductParameters[], sampleGranuleDataArray: number[], selectedGranules: string[], showTutorialModal: boolean, @@ -20,11 +18,10 @@ const initialState: AddCustomProductModalState = { showEditProductModal: false, showDeleteProductModal: false, showGenerateProductModal: false, - showTutorialModal: true, + showTutorialModal: false, skipTutorial: true, // allProducts: this will be like a 'database' for the local state of all the products added // the key will be cycleId_passId_sceneId and the value will be a 'parameterOptionDefaults' type object - addedProducts: [], sampleGranuleDataArray: [], selectedGranules: [] } diff --git a/src/components/sidebar/actions/productSlice.ts b/src/components/sidebar/actions/productSlice.ts index 7682a9d..a98c05e 100644 --- a/src/components/sidebar/actions/productSlice.ts +++ b/src/components/sidebar/actions/productSlice.ts @@ -45,7 +45,7 @@ const initialState: GranuleState = { spatialSearchResults: [], waitingForSpatialSearch: false, waitingForFootprintSearch: false, - spatialSearchStartDate: (new Date(date.setMonth(date.getMonth() - 1))).toISOString(), + spatialSearchStartDate: (new Date(2022, 11, 16)).toISOString(), spatialSearchEndDate: (new Date()).toISOString() } diff --git a/src/components/sidebar/actions/sidebarSlice.ts b/src/components/sidebar/actions/sidebarSlice.ts index 3c70317..0a1cd5c 100644 --- a/src/components/sidebar/actions/sidebarSlice.ts +++ b/src/components/sidebar/actions/sidebarSlice.ts @@ -77,7 +77,4 @@ export const { setSidebarWidth } = sidebarSlice.actions -// Other code such as selectors can use the imported `RootState` type -// export const selectShowAddProductModal = (state: RootState) => state.addCustomProductModal.showAddProductModal - export default sidebarSlice.reducer \ No newline at end of file diff --git a/src/components/tutorial/tutorialConstants.ts b/src/components/tutorial/tutorialConstants.ts index 215d207..41c93e5 100644 --- a/src/components/tutorial/tutorialConstants.ts +++ b/src/components/tutorial/tutorialConstants.ts @@ -34,7 +34,6 @@ export const tutorialSteps = [ } }, { - // target: "#spatial-search-map", target: '#map-tutorial-target', content: "This map allows you to search for scenes by drawing a search area and will display the footprints of scenes once they have been added to your Added Scenes list.", disableBeacon: true, @@ -43,7 +42,6 @@ export const tutorialSteps = [ options: { zIndex: 1000, primaryColor: '#0d6efd', - // offset: 40, } } }, @@ -116,17 +114,6 @@ export const tutorialSteps = [ } } }, - { - target: "#configure-products-button", - content: "Click the Configure Products button to proceed to the Configure Options page where you can select options for how your selected scenes can be made into custom products.", - disableBeacon: true, - styles: { - options: { - zIndex: 1000, - primaryColor: '#0d6efd', - } - } - }, { target: "#configure-options-breadcrumb", content: "You can also click on the Configure Options tab to proceed to the Configure Options view.", diff --git a/src/components/welcome/Welcome.tsx b/src/components/welcome/Welcome.tsx index c681a0b..75eefc5 100644 --- a/src/components/welcome/Welcome.tsx +++ b/src/components/welcome/Welcome.tsx @@ -73,8 +73,8 @@ const Welcome = () => {
4.
-
Download
-
Download generated products once processing is complete.
+
Download
+
Download generated products once processing is complete.
diff --git a/src/constants/graphqlQueries.ts b/src/constants/graphqlQueries.ts index 6fe47d6..e1c1151 100644 --- a/src/constants/graphqlQueries.ts +++ b/src/constants/graphqlQueries.ts @@ -1,3 +1,5 @@ +import { userProductQueryLimit } from "./rasterParameterConstants" + export const userQuery = ` { currentUser { @@ -27,7 +29,7 @@ export const generateL2RasterProductQuery = ` export const userProductsQuery = ` { currentUser { - products { + products (limit: ${userProductQueryLimit}) { id timestamp cycle diff --git a/src/constants/rasterParameterConstants.ts b/src/constants/rasterParameterConstants.ts index a171c46..fad2e10 100644 --- a/src/constants/rasterParameterConstants.ts +++ b/src/constants/rasterParameterConstants.ts @@ -96,7 +96,6 @@ export const parameterOptionDefaults = { } export const parameterHelp: ParameterHelp = { - // outputGranuleExtentFlag: `There are two sizing options for raster granules: square (128 km x 128 km) or rectangular (256 km x 128 km). The square granule extent utilizes the data from only the specific square scene ID indicated, whereas the rectangular granule extent utilizes the specific square scene ID indicated and data from the two adjacent scene IDs along the SWOT swath. At the very edges of scenes, there is a risk that the pixels SWOT measures will not be aggregated as accurately into the raster product. The rectangular extent addresses this issue and could be most helpful with points of interest near the edges of scenes.`, outputGranuleExtentFlag: `There are two sizing options for raster granules: nonoverlapping square (128 km x 128 km) or overlapping rectangular (256 km x 128 km). The rectangular granule extent is 64 km longer in along-track on both sides of the granule and can be useful for observing areas of interest near the along-track edges of the nonoverlapping granules without the need to stitch sequential granules together.`, outputSamplingGridType: `Specifies the type of the raster sampling grid. It can be either a Universal Transverse Mercator (UTM) grid or a geodetic latitude-longitude grid.`, rasterResolution: `Resolution of the raster sampling grid in units of integer meters for UTM grids and integer arc-seconds for latitude-longitude grids.`, @@ -219,15 +218,10 @@ export const granuleAlertMessageConstant: granuleAlertMessageConstantType = { ] export const spatialSearchResultLimit = 2000 -// export const beforeCPS = '_PIXC_' -// export const afterCPSR = 'R_' -// export const afterCPSL = 'L_' -// export const spatialSearchCollectionConceptId = 'C2799438266-POCLOUD' -// export const spatialSearchCollectionConceptId = 'C2799438271-POCLOUD' - export const beforeCPS = '_x_x_x_' export const afterCPSR = 'F_' export const afterCPSL = 'F_' export const spatialSearchCollectionConceptId = 'C2799438271-POCLOUD' -// export const footprintSearchCollectionConceptId = 'C2799438266-POCLOUD' -export const footprintSearchCollectionConceptId = 'C2799438271-POCLOUD' \ No newline at end of file +export const footprintSearchCollectionConceptId = 'C2799438271-POCLOUD' + +export const userProductQueryLimit = 1000 \ No newline at end of file diff --git a/src/redux/store.ts b/src/redux/store.ts index 84eda7e..66b8e76 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -4,7 +4,6 @@ import productSlice from '../components/sidebar/actions/productSlice' import navbarSlice from '../components/navbar/navbarSlice' import appSlice from '../components/app/appSlice' import sidebarSlice from '../components/sidebar/actions/sidebarSlice' -// ... export const store = configureStore({ reducer: { diff --git a/src/types/constantTypes.ts b/src/types/constantTypes.ts index 66de6a3..b61f34a 100644 --- a/src/types/constantTypes.ts +++ b/src/types/constantTypes.ts @@ -100,7 +100,6 @@ export interface AlertMessageObject { type: string, message: string, variant: "danger" | "success" | "warning", - // timeoutId: ReturnType, tableType: TableTypes } @@ -152,4 +151,9 @@ export interface MapFocusObject { zoom: number } -export type SaveType = 'manual' | 'urlParameter' | 'spatialSearch' \ No newline at end of file +export type SaveType = 'manual' | 'urlParameter' | 'spatialSearch' + +export interface handleSaveResult { + result: string, + savedScenes?: allProductParameters[] +} \ No newline at end of file diff --git a/src/user/userData.ts b/src/user/userData.ts index f3b5558..c75aa95 100644 --- a/src/user/userData.ts +++ b/src/user/userData.ts @@ -78,21 +78,6 @@ export const generateL2RasterProduct = async ( ) => { try { // TODO: why doesn't typescript like when I don't specifiy 2 different objects for variables utm and geo??? - // const variablesToUse: ProductGenerationVariables = { - // cycle: parseInt(cycle), - // pass: parseInt(pass), - // scene: parseInt(scene), - // outputGranuleExtentFlag: Boolean(outputGranuleExtentFlag), - // outputSamplingGridType: 'GEO', - // rasterResolution, - // } - - // // if outputSamplingGridType is lat/lon (UTM) - // if (outputSamplingGridType === 'lat/lon') { - // variablesToUse.utmZoneAdjust = parseInt(utmZoneAdjust) - // variablesToUse.mgrsBandAdjust = parseInt(mgrsBandAdjust) - // variablesToUse.outputSamplingGridType = outputSamplingGridType.toUpperCase() - // } const utmVariables = { cycle: parseInt(cycle), @@ -129,14 +114,6 @@ export const getUserProducts = async () => { try { const userProductResponse = await graphQLClient.request(userProductsQuery).then(result => { const userProductsResult = (result as UserResponse).currentUser.products - // const userProductsGeneratedForm = userProductResponse.result.map(productResult => { - // const {cycle, pass, scene, rasterResolution, outputGranuleExtentFlag, outputSamplingGridType, utmZoneAdjust, timestamp, id: productId, status} = productResult - // // const generatedFormToReuturn: GeneratedProduct = - // // return generatedFormToReuturn - // }) - - // turn into GeneratedProduct - // const generatedProduct: GeneratedProduct = {} return {status: 'success', products: userProductsResult} as getUserProductsResponse }) return userProductResponse