diff --git a/components/experiment.tsx b/components/experiment.tsx index 4409243e..3c754ff5 100644 --- a/components/experiment.tsx +++ b/components/experiment.tsx @@ -1,4 +1,4 @@ -import { Button, Card, CardContent, Grid, Snackbar, Typography } from '@material-ui/core' +import { Box, Button, Card, CardContent, CircularProgress, Grid, Snackbar, Typography } from '@material-ui/core' import Layout from './layout' import OptimizerModel from './optimizer-model'; import OptimizerConfigurator from './optimizer-configurator'; @@ -9,9 +9,10 @@ import { useStyles } from '../styles/experiment.style'; import { useExperiment, saveExperiment, runExperiment } from '../context/experiment-context'; import React, { useState, useEffect } from 'react'; import { ValueVariableType, CategoricalVariableType, OptimizerConfig, DataPointType } from '../types/common'; +import LoadingExperiment from './loading-experiment'; export default function Experiment() { - const classes = useStyles(); + const classes = useStyles() const { state: { experiment }, dispatch, loading } = useExperiment() @@ -45,7 +46,6 @@ export default function Experiment() { } catch (error) { console.error('fetch error', error) } - } function handleCloseSnackbar() { @@ -54,9 +54,11 @@ export default function Experiment() { const valueVariables = experiment.valueVariables const categoricalVariables = experiment.categoricalVariables + if (loading) { - return <>Loading experiment... + return } + return ( diff --git a/components/home.tsx b/components/home.tsx new file mode 100644 index 00000000..dfc710ef --- /dev/null +++ b/components/home.tsx @@ -0,0 +1,123 @@ +import { Box, Card, CardContent, List, ListItem, ListItemText, Typography } from "@material-ui/core"; +import { useCallback, useState } from "react"; +import { useDropzone } from 'react-dropzone'; +import Layout from "../components/layout"; +import useStyles from "../styles/home.style"; +import { NextRouter, useRouter } from 'next/router' +import SystemUpdateAltIcon from '@material-ui/icons/SystemUpdateAlt'; +import ChevronRightIcon from '@material-ui/icons/ChevronRight'; +import { paths } from "../paths"; +import { ExperimentType } from "../types/common"; +import { useGlobal } from "../context/global-context"; +import { saveExperiment } from '../context/experiment-context'; + +export default function Home() { + const classes = useStyles() + const router: NextRouter = useRouter() + const [uploadMessage, setUploadMessage] = useState("Drag file here") + const { state } = useGlobal() + + const onDrop = useCallback(acceptedFiles => { + const reader = new FileReader() + reader.onabort = () => setUploadMessage('Upload aborted') + reader.onerror = () => setUploadMessage('Upload failed') + reader.onprogress = () => setUploadMessage('Loading file...') + reader.onload = () => load(reader) + reader.readAsText(acceptedFiles[0]) + }, []) + + const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop }) + + const load = (reader: FileReader) => { + const binaryResult: string | ArrayBuffer = reader.result + try { + const experiment: ExperimentType = JSON.parse(binaryResult as string) + if (experiment.id === undefined) { + setUploadMessage('Id not found') + } else { + saveAndRedirect(experiment) + } + } catch (e) { + console.error('File parsing failed', e) + setUploadMessage('Upload failed') + } + } + + const saveAndRedirect = async (experiment: ExperimentType) => { + const id: string = experiment.id + if (state.useLocalStorage) { + try { + localStorage.setItem(id, JSON.stringify({ experiment })) + router.push(`${paths.experiment}/${id}`) + } catch (e) { + console.error('Unable to use local storage') + setUploadMessage('Upload failed') + } + } else { + await saveExperiment(experiment) + router.push(`${paths.experiment}/${id}`) + } + } + + return ( + + + + + + + Get started + + + + + + + + + + + + + + + Upload experiment file + + + + +
+ + {uploadMessage} + +
+
+
+ + + + Saved experiments + + + + + + + + + + + + + + + + + + + +
+
+
+ ); +} \ No newline at end of file diff --git a/components/layout.tsx b/components/layout.tsx index 93f36770..74c9f89f 100644 --- a/components/layout.tsx +++ b/components/layout.tsx @@ -1,4 +1,4 @@ -import { AppBar, Button, Switch, Toolbar, Typography } from '@material-ui/core' +import { AppBar, Box, Button, Switch, Toolbar, Typography } from '@material-ui/core' import Link from 'next/link' import useStyles from '../styles/layout.style' import Image from 'next/image' @@ -33,10 +33,10 @@ export default function Layout ( {children} ) { -
+ {state.debug &&
{JSON.stringify(state, null, 2)}
} {children} -
+ ) } \ No newline at end of file diff --git a/components/loading-experiment.tsx b/components/loading-experiment.tsx new file mode 100644 index 00000000..b048c37a --- /dev/null +++ b/components/loading-experiment.tsx @@ -0,0 +1,19 @@ +import { Box, Card, CardContent, CircularProgress, Typography } from "@material-ui/core"; +import useStyles from "../styles/loading-experiment.style"; +import Layout from "./layout"; + +export default function LoadingExperiment() { + const classes = useStyles() + return + + + + + + Loading experiment... + + + + + +} \ No newline at end of file diff --git a/hooks/useLocalStorageReducer.ts b/hooks/useLocalStorageReducer.ts index 96c9fc2b..2213f8df 100644 --- a/hooks/useLocalStorageReducer.ts +++ b/hooks/useLocalStorageReducer.ts @@ -1,7 +1,4 @@ -import { useCallback, useReducer } from 'react' -import { ExperimentAction } from '../reducers/experiment-reducers' -import { Action } from '../reducers/reducers' -import { State } from '../store' +import { useReducer } from 'react' const init = (localStorageKey: string) => (initialState: S) => { try { diff --git a/package.json b/package.json index 8e81f334..f65cdae7 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "next": "^10.2.0", "react": "17.0.1", "react-dom": "17.0.1", + "react-dropzone": "^11.3.2", "react-hook-form": "^6.15.1", "swr": "^0.4.2", "uuid": "^8.3.2" diff --git a/pages/_app.tsx b/pages/_app.tsx index e353c7ad..4bb9b4a6 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -25,7 +25,7 @@ function App({ Component, pageProps }: AppProps) { return ( - My page + BrownieBee diff --git a/pages/experiment/[experimentid].tsx b/pages/experiment/[experimentid].tsx index a1f2a0fb..b43e9658 100644 --- a/pages/experiment/[experimentid].tsx +++ b/pages/experiment/[experimentid].tsx @@ -3,6 +3,7 @@ import { ExperimentProvider } from "../../context/experiment-context"; import Experiment from "../../components/experiment"; import DebugExperiment from "../../components/debugexperiment"; import { useGlobal } from "../../context/global-context"; +import LoadingExperiment from "../../components/loading-experiment"; export default function ExperimentContainer() { const router = useRouter(); @@ -10,7 +11,7 @@ export default function ExperimentContainer() { const { state } = useGlobal() if (!experimentid) { - return
Loading experiment
; + return } return ( <> diff --git a/pages/index.tsx b/pages/index.tsx index 8acf588f..5f213000 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,16 +1,5 @@ -import Head from "next/head"; -import Layout from "../components/layout"; +import Home from "../components/home"; -export default function Home() { - return ( -
- - Brownie Bee - - - -

Welcome!

-
-
- ); +export default function Index() { + return } \ No newline at end of file diff --git a/paths.ts b/paths.ts new file mode 100644 index 00000000..2757bc68 --- /dev/null +++ b/paths.ts @@ -0,0 +1,3 @@ +export const paths = { + experiment: 'experiment' +} \ No newline at end of file diff --git a/styles/experiment.style.tsx b/styles/experiment.style.tsx index e3320c4b..f5e06b32 100644 --- a/styles/experiment.style.tsx +++ b/styles/experiment.style.tsx @@ -3,8 +3,8 @@ import { makeStyles } from "@material-ui/core"; export const useStyles = makeStyles(theme => ({ experimentContainer: { marginTop: theme.spacing(4), - minWidth: 1200, - maxWidth: 1800, + minWidth: theme.sizes.mainWidthMin, + maxWidth: theme.sizes.mainWidthMax, background: theme.palette.custom.background.main, color: 'white', }, @@ -32,7 +32,7 @@ export const useStyles = makeStyles(theme => ({ '100%': { backgroundColor: theme.palette.primary.main, } - } + }, })); export default useStyles \ No newline at end of file diff --git a/styles/home.style.tsx b/styles/home.style.tsx new file mode 100644 index 00000000..790bdc77 --- /dev/null +++ b/styles/home.style.tsx @@ -0,0 +1,35 @@ +import { makeStyles } from "@material-ui/core"; + +export const useStyles = makeStyles(theme => ({ + mainContainer: { + maxWidth: 500, + background: theme.palette.custom.background.main, + color: 'white', + }, + mainContent: { + padding: 0, + }, + box: { + backgroundColor: 'rgba(255,255,255,0.15)', + }, + uploadBox: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: 200, + height: 180, + borderRadius: 4, + padding: theme.spacing(4), + }, + uploadBoxInner: { + marginTop: 108, + color: 'white', + }, + uploadIcon: { + position: 'absolute', + fontSize: '13rem', + opacity: .35 + } +})); + +export default useStyles \ No newline at end of file diff --git a/styles/layout.style.tsx b/styles/layout.style.tsx index 642c4188..95d86447 100644 --- a/styles/layout.style.tsx +++ b/styles/layout.style.tsx @@ -7,9 +7,6 @@ export const useStyles = makeStyles(theme => ({ link: { color: "white", }, - mainContent: { - marginTop: 56, - }, logo: { marginRight: theme.spacing(1), } diff --git a/styles/loading-experiment.style.tsx b/styles/loading-experiment.style.tsx new file mode 100644 index 00000000..c49148ec --- /dev/null +++ b/styles/loading-experiment.style.tsx @@ -0,0 +1,18 @@ +import { makeStyles } from "@material-ui/core"; + +export const useStyles = makeStyles(theme => ({ + loadingContainer: { + marginTop: theme.spacing(4), + minWidth: theme.sizes.mainWidthMin, + maxWidth: theme.sizes.mainWidthMax, + background: theme.palette.custom.background.main, + color: 'white', + textAlign: 'center', + height: '50vh', + }, + progress: { + color: 'white', + } +})); + +export default useStyles \ No newline at end of file diff --git a/theme/theme.ts b/theme/theme.ts index 798e9234..73f9c313 100644 --- a/theme/theme.ts +++ b/theme/theme.ts @@ -13,8 +13,6 @@ declare module '@material-ui/core/styles/createPalette' { } } -export const defaultTheme: Theme = createMuiTheme() - const overrides: Overrides = { MuiTableCell: { sizeSmall: { @@ -23,13 +21,32 @@ const overrides: Overrides = { }, MuiSelect: { select: { - paddingBottom: "4px", + paddingBottom: 4, + } + } +} +declare module '@material-ui/core/styles/createMuiTheme' { + interface Theme { + sizes: { + mainWidthMin: number + mainWidthMax: number + } + } + + interface ThemeOptions { + sizes?: { + mainWidthMin?: number + mainWidthMax?: number } } } export const theme: Theme = createMuiTheme({ overrides, + sizes: { + mainWidthMin: 1200, + mainWidthMax: 1800, + }, palette: { primary: { main: 'rgba(0,121,145,1)', diff --git a/yarn.lock b/yarn.lock index 1272bd61..add63a97 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1143,6 +1143,11 @@ atob@^2.1.2: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +attr-accept@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.2.tgz#646613809660110749e92f2c10833b70968d929b" + integrity sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg== + available-typed-arrays@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" @@ -2485,6 +2490,13 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +file-selector@^0.2.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.2.4.tgz#7b98286f9dbb9925f420130ea5ed0a69238d4d80" + integrity sha512-ZDsQNbrv6qRi1YTDOEWzf5J2KjZ9KMI1Q2SGeTkCJmNNW25Jg4TW4UMcmoqcg4WrAyKRcpBXdbWRxkfrOzVRbA== + dependencies: + tslib "^2.0.3" + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -5010,6 +5022,15 @@ react-dom@17.0.1: object-assign "^4.1.1" scheduler "^0.20.1" +react-dropzone@^11.3.2: + version "11.3.2" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-11.3.2.tgz#2efb6af800a4779a9daa1e7ba1f8d51d0ab862d7" + integrity sha512-Z0l/YHcrNK1r85o6RT77Z5XgTARmlZZGfEKBl3tqTXL9fZNQDuIdRx/J0QjvR60X+yYu26dnHeaG2pWU+1HHvw== + dependencies: + attr-accept "^2.2.1" + file-selector "^0.2.2" + prop-types "^15.7.2" + react-error-boundary@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.1.tgz#932c5ca5cbab8ec4fe37fd7b415aa5c3a47597e7" @@ -5980,6 +6001,11 @@ ts-pnp@^1.1.6: resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== +tslib@^2.0.3: + version "2.2.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" + integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w== + tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"