diff --git a/frontend/components/EmailField.js b/frontend/components/EmailField.js new file mode 100644 index 0000000..7575be1 --- /dev/null +++ b/frontend/components/EmailField.js @@ -0,0 +1,53 @@ +import React, { useState, useEffect } from 'react' +import TextField from '@mui/material/TextField' +import PropTypes from 'prop-types' + +function EmailField({ initialValue = '', onEmailChange, onClearForm }) { + const [email, setEmail] = useState(initialValue) + const [isValidEmail, setIsValidEmail] = useState(true) + + const handleEmailChange = event => { + const newEmail = event.target.value + setEmail(newEmail) + onEmailChange(newEmail) + } + + const handleBlur = () => { + if (email.trim() !== '') { + validateEmail(email) + } + } + + const validateEmail = value => { + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ + const isValid = emailRegex.test(value) + setIsValidEmail(isValid) + } + + useEffect(() => { + setEmail(initialValue) + setIsValidEmail(true) + }, [onClearForm, initialValue]) + + return ( +
+ +
+ ) +} + +EmailField.propTypes = { + initialValue: PropTypes.string, + onEmailChange: PropTypes.func, + onClearForm: PropTypes.func +} + +export default EmailField diff --git a/frontend/components/SearchRadius.js b/frontend/components/SearchRadius.js new file mode 100644 index 0000000..ee635af --- /dev/null +++ b/frontend/components/SearchRadius.js @@ -0,0 +1,47 @@ +import React, { useState, useEffect } from 'react' +import TextField from '@mui/material/TextField' +import PropTypes from 'prop-types' + +const SearchRadius = ({ searchRadius, onChange, reset }) => { + const formatSearchRadius = value => { + if (/^\d+\.\d*$/.test(value)) { + return value + } else { + return value.replace(/[^\d.]/g, '') + } + } + + const [localSearchRadius, setLocalSearchRadius] = useState( + formatSearchRadius(searchRadius) + ) + + useEffect(() => { + setLocalSearchRadius(formatSearchRadius(searchRadius)) + }, [reset, searchRadius]) + + const handleRadiusChange = event => { + const inputValue = event.target.value + + if (/^\d+(\.\d*)?$/.test(inputValue)) { + setLocalSearchRadius(inputValue) + onChange(event) + } + } + + return ( + + ) +} + +SearchRadius.propTypes = { + searchRadius: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + onChange: PropTypes.func, + reset: PropTypes.bool +} + +export default SearchRadius diff --git a/frontend/components/SpeczData.js b/frontend/components/SpeczData.js index 9a23f24..905983d 100644 --- a/frontend/components/SpeczData.js +++ b/frontend/components/SpeczData.js @@ -1,3 +1,5 @@ +import * as React from 'react' +import { getProducts } from '../services/product' import { DataGrid } from '@mui/x-data-grid' import moment from 'moment' import { Box } from '@mui/material' @@ -30,13 +32,29 @@ const columns = [ } ] +async function fetchData(filters, query) { + try { + const response = await getProducts({ + filters, + page: 0, + page_size: 25, + sort: [{ field: 'created_at', sort: 'desc' }], + search: query + }) + + return response.results + } catch (error) { + console.error(error) + throw error + } +} + function DataTable({ rows }) { return ( - + row.id} pageSizeOptions={[5, 10]} checkboxSelection /> @@ -48,39 +66,33 @@ DataTable.propTypes = { rows: PropTypes.arrayOf(PropTypes.object).isRequired } -const rows = [ - { - id: 1, - display_name: 'Jandson 1', - uploaded_by: 'User1', - created_at: '2023-01-01' - }, - { - id: 2, - display_name: 'Jandson 2', - uploaded_by: 'User2', - created_at: '2023-02-15' - }, - { - id: 3, - display_name: 'Jandson 3', - uploaded_by: 'User3', - created_at: '2023-03-22' - }, - { - id: 4, - display_name: 'Jandson V', - uploaded_by: 'User1', - created_at: '2023-04-10' - }, - { - id: 5, - display_name: 'Jandson Try', - uploaded_by: 'User2', - created_at: '2023-05-05' - } -] +function DataTableWrapper({ filters, query }) { + const [rows, setRows] = React.useState([]) + + React.useEffect(() => { + const fetchAndSetData = async () => { + try { + const data = await fetchData(filters, query) + setRows(data) + } catch (error) { + console.error(error) + } + } + + fetchAndSetData() + }, [filters, query]) -export default function App() { return } + +DataTableWrapper.propTypes = { + filters: PropTypes.object, + query: PropTypes.string +} + +DataTableWrapper.defaultProps = { + filters: {}, + query: '' +} + +export default DataTableWrapper diff --git a/frontend/components/TsmData.js b/frontend/components/TsmData.js index eceffca..8a4acfe 100644 --- a/frontend/components/TsmData.js +++ b/frontend/components/TsmData.js @@ -1,3 +1,5 @@ +import * as React from 'react' +import { getProducts } from '../services/product' import { DataGrid } from '@mui/x-data-grid' import moment from 'moment' import { Box } from '@mui/material' @@ -30,15 +32,27 @@ const columns = [ } ] +async function fetchData(filters, query) { + try { + const response = await getProducts({ + filters, + page: 0, + page_size: 25, + sort: [{ field: 'created_at', sort: 'desc' }], + search: query + }) + + return response.results + } catch (error) { + console.error(error) + throw error + } +} + function DataTable({ rows }) { return ( - - row.id} - pageSizeOptions={[5, 10]} - /> + + ) } @@ -47,39 +61,33 @@ DataTable.propTypes = { rows: PropTypes.arrayOf(PropTypes.object).isRequired } -const rows = [ - { - id: 1, - display_name: 'Jandson 1', - uploaded_by: 'User1', - created_at: '2023-01-01' - }, - { - id: 2, - display_name: 'Jandson 2', - uploaded_by: 'User2', - created_at: '2023-02-15' - }, - { - id: 3, - display_name: 'Jandson 3', - uploaded_by: 'User3', - created_at: '2023-03-22' - }, - { - id: 4, - display_name: 'Jandson V', - uploaded_by: 'User1', - created_at: '2023-04-10' - }, - { - id: 5, - display_name: 'Jandson Try', - uploaded_by: 'User2', - created_at: '2023-05-05' - } -] +function DataTableWrapper({ filters, query }) { + const [rows, setRows] = React.useState([]) + + React.useEffect(() => { + const fetchAndSetData = async () => { + try { + const data = await fetchData(filters, query) + setRows(data) + } catch (error) { + console.error(error) + } + } + + fetchAndSetData() + }, [filters, query]) -export default function App() { return } + +DataTableWrapper.propTypes = { + filters: PropTypes.object, + query: PropTypes.string +} + +DataTableWrapper.defaultProps = { + filters: {}, + query: '' +} + +export default DataTableWrapper diff --git a/frontend/pages/specz_catalogs.js b/frontend/pages/specz_catalogs.js index b24392f..48b14d4 100644 --- a/frontend/pages/specz_catalogs.js +++ b/frontend/pages/specz_catalogs.js @@ -1,47 +1,204 @@ -import React from 'react' -import { Typography, Box } from '@mui/material' -import Breadcrumbs from '@mui/material/Breadcrumbs' -import Link from '@mui/material/Link' +import InfoIcon from '@mui/icons-material/Info' +import Box from '@mui/material/Box' +import Button from '@mui/material/Button' +import Card from '@mui/material/Card' +import CardContent from '@mui/material/CardContent' import Grid from '@mui/material/Grid' import IconButton from '@mui/material/IconButton' -import InfoIcon from '@mui/icons-material/Info' -import ArrowBackIos from '@mui/icons-material/ArrowBackIos' +import Link from '@mui/material/Link' +import Breadcrumbs from '@mui/material/Breadcrumbs' +import MenuItem from '@mui/material/MenuItem' +import Paper from '@mui/material/Paper' +import Select from '@mui/material/Select' +import Snackbar from '@mui/material/Snackbar' +import SnackbarContent from '@mui/material/SnackbarContent' +import TextField from '@mui/material/TextField' +import Typography from '@mui/material/Typography' +import React, { useState } from 'react' +import EmailField from '../components/EmailField' +import SearchField from '../components/SearchField' +import SearchRadius from '../components/SearchRadius' +import SpeczData from '../components/SpeczData' +import { useTheme } from '@mui/system' function SpeczCatalogs() { + const theme = useTheme() + + const [combinedCatalogName, setCombinedCatalogName] = useState('') + const [search, setSearch] = React.useState('') + const filters = React.useState() + const [searchRadius, setSearchRadius] = useState('1.0') + const [selectedOption, setSelectedOption] = useState('keepAll') + const [email, setEmail] = useState('') + const [snackbarOpen, setSnackbarOpen] = useState(false) + + const handleCatalogNameChange = event => { + setCombinedCatalogName(event.target.value) + } + + const handleSearchRadiusChange = event => { + const newValue = parseFloat(event.target.value) + setSearchRadius(isNaN(newValue) ? '' : newValue.toString()) + } + + const handleEmailChange = newEmail => { + setEmail(newEmail) + } + + const handleClearForm = () => { + setCombinedCatalogName('') + setSearchRadius('1.0') + setSelectedOption('keepAll') + setEmail('') + } + + const handleRun = () => { + setSnackbarOpen(true) + } + + const handleSnackbarClose = () => { + setSnackbarOpen(false) + } + + const styles = { + root: { + transition: 'box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms', + borderRadius: '4px', + padding: theme.spacing(3), + flex: '1 1 0%' + } + } + return ( - - - - Home - - - Pipelines - - Combine Spec-z Catalogs - - - - window.history.back()} - > - - - Combine Spec-z Catalogs - - - - - - - Coming soon... - - + + + + + + Home + + + Pipelines + + Combine Spec-z Catalogs + + + + Combine Spec-z Catalogs + + + + + + + + + 1. Combined catalog name: + + + + + + + + + + + 2. Select the Spec-z Catalogs to include in your sample: + + setSearch(query)} /> + + + + + + + + + + + + + 3. Select the cross-matching configuration choices: + + + + + Search Radius (arcsec):{' '} + + + + + + In case of multiple spec-z measurements for the same object: + {' '} + + + + + + 4. Enter an email address to be notified when the process is + complete (opcional): + + + + + + + + + + + + + + + + ) } diff --git a/frontend/pages/training_set_maker.js b/frontend/pages/training_set_maker.js index c261d08..eca29d7 100644 --- a/frontend/pages/training_set_maker.js +++ b/frontend/pages/training_set_maker.js @@ -1,47 +1,241 @@ -import React from 'react' -import { Typography, Box } from '@mui/material' +import InfoIcon from '@mui/icons-material/Info' +import Box from '@mui/material/Box' +import Button from '@mui/material/Button' +import Card from '@mui/material/Card' +import CardContent from '@mui/material/CardContent' +import Grid from '@mui/material/Grid' import Breadcrumbs from '@mui/material/Breadcrumbs' import Link from '@mui/material/Link' -import Grid from '@mui/material/Grid' import IconButton from '@mui/material/IconButton' -import InfoIcon from '@mui/icons-material/Info' -import ArrowBackIos from '@mui/icons-material/ArrowBackIos' +import MenuItem from '@mui/material/MenuItem' +import Paper from '@mui/material/Paper' +import Select from '@mui/material/Select' +import Snackbar from '@mui/material/Snackbar' +import SnackbarContent from '@mui/material/SnackbarContent' +import TextField from '@mui/material/TextField' +import Typography from '@mui/material/Typography' +import React, { useState } from 'react' +import EmailField from '../components/EmailField' +import SearchField from '../components/SearchField' +import SearchRadius from '../components/SearchRadius' +import TsmData from '../components/TsmData' +import { useTheme } from '@mui/system' function TrainingSetMaker() { + const theme = useTheme() + + const [combinedCatalogName, setCombinedCatalogName] = useState('') + const [search, setSearch] = React.useState('') + const filters = React.useState() + const [searchRadius, setSearchRadius] = useState('1.0') + const [selectedOption, setSelectedOption] = useState('pickOne') + const [email, setEmail] = useState('') + const [snackbarOpen, setSnackbarOpen] = useState(false) + const [selectedLsstCatalog, setSelectedLsstCatalog] = useState('DP0.2') + + const handleCatalogNameChange = event => { + setCombinedCatalogName(event.target.value) + } + + const handleSearchRadiusChange = event => { + const newValue = parseFloat(event.target.value) + setSearchRadius(isNaN(newValue) ? '' : newValue.toString()) + } + + const handleLsstCatalogChange = event => { + setSelectedLsstCatalog(event.target.value) + } + + const handleEmailChange = newEmail => { + setEmail(newEmail) + } + + const handleClearForm = () => { + setCombinedCatalogName('') + setSearchRadius('1.0') + setSelectedOption('pickOne') + setEmail('') + setSelectedLsstCatalog('DP0.2') + } + + const handleRun = () => { + setSnackbarOpen(true) + } + + const handleSnackbarClose = () => { + setSnackbarOpen(false) + } + + const styles = { + root: { + transition: 'box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms', + borderRadius: '4px', + padding: theme.spacing(3), + flex: '1 1 0%' + } + } + return ( - - - - Home - - - Pipelines - - Training Set Maker - - - - window.history.back()} - > - - - Training Set Maker - - - - - - - Coming soon... - - + + + + + + Home + + + Pipelines + + Training Set Maker + + + + Training Set Maker + + + + + + + + + + 1. Combined catalog name: + + + + + + + + + + + + 2. Select the Spec-z Catalogs to include in your sample: + + setSearch(query)} /> + + + + + + + + + + + + + + 3. Select the LSST objects catalog: + + + + + + + 4. Select the cross-matching configuration choices: + + Search Radius (arcsec):{' '} + + + + n neighbors:{' '} + + + + + In case of multiple spec-z measurements for the same object: + {' '} + + + + + + + + 5. Enter an email address to be notified when the process is + complete (opcional): + + + + + + + + + + + + + + + + ) }