diff --git a/.vscode/launch.json b/.vscode/launch.json index fbea43bf..38148750 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -2,7 +2,7 @@ // Use IntelliSense to learn about possible attributes. // Hover to view descriptions of existing attributes. // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": , + "version": "0.2.0", "configurations": [ { "name": "Debug Backend", diff --git a/.vscode/settings.json b/.vscode/settings.json index 661bba78..b749bd20 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,12 +2,21 @@ // =========================== General ========================= // Spellcheck forcefully allowed words "cSpell.words": [ + "eqcm", "glados", "gladosbase", "matplotlib", "mouck", "pyplot", - "Vmmem" + "signin", + "tabler", + "tlds", + "Vmmem", + "ZSVUVA" + ], + // Spellcheck forcefully flagged words (please explain why) + "cSpell.flagWords": [ + "Dependent", // in our context, we are always referring to the Dependant Variable ], "shellcheck.exclude": [ "1017" // Turn off shellcheck warnings about line ending types because git is set to autoconvert diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 6ef7ce07..8494a53d 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -4,7 +4,7 @@ "version": "2.0.0", "tasks": [ { - "label": "Run Dev Install/Update Script 2", + "label": "Run Dev Install/Update Script", "type": "shell", "command": "./dev-install.sh", "problemMatcher": "$tsc", @@ -16,6 +16,12 @@ "showReuseMessage": true, "clear": false } + }, + { + "label": "Docker Compose Up (Attached)", + "type": "shell", + "command": "docker-compose up --build", + "problemMatcher": [] } ] } \ No newline at end of file diff --git a/Monorepo.wiki b/Monorepo.wiki index 9e96e0da..c90c1b41 160000 --- a/Monorepo.wiki +++ b/Monorepo.wiki @@ -1 +1 @@ -Subproject commit 9e96e0da1ce8f09d4d19819fe2c7331c888db1c0 +Subproject commit c90c1b418b50baaca4e9e23afd0e71886d4df278 diff --git a/apps/backend/.gitignore b/apps/backend/.gitignore index 7c3848e9..b9910e02 100644 --- a/apps/backend/.gitignore +++ b/apps/backend/.gitignore @@ -5,7 +5,4 @@ ExperimentFiles/ __pycache__ .venv -# For running on windows -dev-windows-set-environment-vars.sh - # All below this point is uncategorized (categorize them!) diff --git a/apps/frontend/.eslintrc.json b/apps/frontend/.eslintrc.json index b778a1b5..3747f484 100644 --- a/apps/frontend/.eslintrc.json +++ b/apps/frontend/.eslintrc.json @@ -52,6 +52,8 @@ { "avoidEscape": true } - ] + ], + // we don't really care about the Next.js image optimizations and what they require us to do + "@next/next/no-img-element": "off" } } diff --git a/apps/frontend/components/Logo.tsx b/apps/frontend/components/Logo.tsx index 3c3b8367..409442da 100644 --- a/apps/frontend/components/Logo.tsx +++ b/apps/frontend/components/Logo.tsx @@ -3,4 +3,4 @@ import { faFire } from '@fortawesome/free-solid-svg-icons'; export const Logo = () => { return ; -}; \ No newline at end of file +}; diff --git a/apps/frontend/components/NewExp.tsx b/apps/frontend/components/NewExp.tsx index 25052190..a0167cd5 100644 --- a/apps/frontend/components/NewExp.tsx +++ b/apps/frontend/components/NewExp.tsx @@ -1,21 +1,20 @@ -import { Fragment, useState, useLayoutEffect, useEffect, ReactNode } from 'react'; +import { Fragment, useState, useLayoutEffect, useEffect } from 'react'; import { Dialog, Transition } from '@headlessui/react'; import { Upload, X, File, IconProps } from 'tabler-icons-react'; import { Toggle } from './Toggle'; import Parameter from './Parameter'; -import { Code, Text, useMantineTheme, Group } from '@mantine/core'; +import { Code, Text } from '@mantine/core'; import { Dropzone } from '@mantine/dropzone'; import { useForm, formList, joiResolver } from '@mantine/form'; -import { DragDropContext, Draggable, Droppable } from 'react-beautiful-dnd'; +import { DragDropContext, Droppable } from 'react-beautiful-dnd'; import { experimentSchema } from '../utils/validators'; -import { submitExperiment, uploadExec, getDocById } from '../firebase/db'; +import { submitExperiment, uploadExec } from '../firebase/db'; import { useAuth } from '../firebase/fbAuth'; -import { firebaseApp } from "../firebase/firebaseClient"; -import { getDoc, getFirestore, doc } from "firebase/firestore"; -import Router from 'next/router'; +import { firebaseApp } from '../firebase/firebaseClient'; +import { getDoc, getFirestore, doc } from 'firebase/firestore'; export const FormStates = { @@ -24,8 +23,8 @@ export const FormStates = { Params: 1, ProcessStep: 2, Confirmation: 3, - Dispatch: 4 -} + Dispatch: 4, +}; const Steps = ({ steps }) => { return ( @@ -124,7 +123,7 @@ const InformationStep = ({ form, ...props }) => { ); }; -const ParamStep = ({form, ...props}) => { +const ParamStep = ({ form, ...props }) => { return (
@@ -137,6 +136,7 @@ const ParamStep = ({form, ...props}) => { {['integer', 'float', 'bool', 'string'].map((type) => (
); -} +}; const PostProcessStep = ({ form, ...props }) => { return ( @@ -199,44 +199,44 @@ const PostProcessStep = ({ form, ...props }) => {
- { - form.setFieldValue('scatter', !form.values.scatter) - if(!form.values.scatter){ - form.setFieldValue('scatterIndVar','') - form.setFieldValue('scatterDepVar','') + form.setFieldValue('scatter', !form.values.scatter); + if (!form.values.scatter) { + form.setFieldValue('scatterIndVar', ''); + form.setFieldValue('scatterDepVar', ''); } }}>
- {form.values.scatter ? -
- -
- -
-
- -
- -
-
-
- : ''} + {form.values.scatter ? +
+ +
+ +
+
+ +
+ +
+
+
: + ''}
); @@ -259,19 +259,18 @@ const ConfirmationStep = ({ form, ...props }) => { const dropzoneKids = (status) => { return (status.accepted) ? - - : -
+ : +
Upload your project executable. - Let's revolutionize science! + Let%apos;s revolutionize science!
-
+
; }; interface UploadIconProps extends IconProps { @@ -315,7 +314,10 @@ const DispatchStep = ({ id, form, ...props }) => { alert('Failed to upload experiment file to the backend server, is it running?'); throw new Error('Upload failed'); } - }).catch( (error) => console.log('Error uploading experiment: ', error)); + }).catch( (error) => { + console.log('Error uploading experiment: ', error); + alert(`Error uploading experiment: ${error.message}`); + }); }; return ( @@ -326,8 +328,8 @@ const DispatchStep = ({ id, form, ...props }) => { className='flex-1 flex flex-col justify-center m-4 items-center' accept={{ 'text/plain': ['.py'], - 'application/java-archive':['.jar'] - }} + 'application/java-archive': ['.jar'], + }} > <>{ (status) => dropzoneKids(status) } @@ -350,11 +352,11 @@ const NewExp = ({ formState, setFormState, copyID, setCopyId, ...rest }) => { }, schema: joiResolver(experimentSchema), }); - + useEffect(() => { if (copyID != null) { const db = getFirestore(firebaseApp); - getDoc(doc(db, "Experiments", copyID)).then(docSnap => { + getDoc(doc(db, 'Experiments', copyID)).then((docSnap) => { if (docSnap.exists()) { const expInfo = docSnap.data(); const params = JSON.parse(expInfo['params'])['params']; @@ -369,18 +371,17 @@ const NewExp = ({ formState, setFormState, copyID, setCopyId, ...rest }) => { scatter: expInfo['scatter'], scatterIndVar: expInfo['scatterIndVar'], scatterDepVar: expInfo['scatterDepVar'], - }) - setCopyId(null) - console.log("Copied!") - + }); + setCopyId(null); + console.log('Copied!'); } else { - console.log("No such document!"); + console.log('No such document!'); } - }) + }); } - }, [copyID]); + }, [copyID]); // TODO adding form or setCopyId causes render loop? + - const fields = form.values.parameters.map(({ type, ...rest }, index) => { return ; }); @@ -398,7 +399,7 @@ const NewExp = ({ formState, setFormState, copyID, setCopyId, ...rest }) => { setOpen(false); form.reset(); } - }, [formState]); + }, [formState]); // TODO adding 'form' causes an update loop return ( @@ -429,8 +430,8 @@ const NewExp = ({ formState, setFormState, copyID, setCopyId, ...rest }) => {
- { return { id: idx + 1, @@ -478,30 +479,30 @@ const NewExp = ({ formState, setFormState, copyID, setCopyId, ...rest }) => { type='button' className='rounded-md border w-1/6 border-gray-300 bg-white py-2 px-4 text-sm font-medium text-gray-700 shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2' onClick={ - status === FormStates.Info - ? () => { - localStorage.removeItem("ID") + status === FormStates.Info ? + () => { + localStorage.removeItem('ID'); setFormState(-1); - } - : () => { - setStatus(status - 1); - } + } : + () => { + setStatus(status - 1); + } } > {status === FormStates.Info ? 'Cancel' : 'Back'} diff --git a/apps/frontend/components/Parameter.tsx b/apps/frontend/components/Parameter.tsx index 60b78436..078953d6 100644 --- a/apps/frontend/components/Parameter.tsx +++ b/apps/frontend/components/Parameter.tsx @@ -53,6 +53,7 @@ const NumberParam = ({ form, type, index, ...rest }) => { {['default', 'min', 'max', 'step'].map((label, i) => { return ( { const router = useRouter(); useEffect(() => { if (!user) { - console.log("User is not signed in; redirecting them to /signin"); + console.log('User is not signed in; redirecting them to /signin'); router.push('/signin'); } }, [user, router]); - return <>{user ? children : "Not logged in"}; + return <>{user ? children : 'Not logged in'}; }; export default ProtectedRoute; diff --git a/apps/frontend/components/Toggle.tsx b/apps/frontend/components/Toggle.tsx index a73c3669..1591e88b 100644 --- a/apps/frontend/components/Toggle.tsx +++ b/apps/frontend/components/Toggle.tsx @@ -7,7 +7,7 @@ export const Toggle = ({ label, onChange }) => { const [enabled, setEnabled] = useState(false); useEffect(() => { onChange(); - }, [enabled]); + }, [enabled]); // TODO adding onChange causes render loop return ( diff --git a/apps/frontend/firebase/db.ts b/apps/frontend/firebase/db.ts index 8f062df9..60826dc6 100644 --- a/apps/frontend/firebase/db.ts +++ b/apps/frontend/firebase/db.ts @@ -1,16 +1,16 @@ -import { firebaseApp } from "./firebaseClient"; -import { getDoc, getFirestore, updateDoc } from "firebase/firestore"; -import { collection, setDoc, doc, query, where, onSnapshot } from "firebase/firestore"; -import { getDownloadURL, getStorage, ref, uploadBytes } from "firebase/storage"; +import { firebaseApp } from './firebaseClient'; +import { getDoc, getFirestore, updateDoc } from 'firebase/firestore'; +import { collection, setDoc, doc, query, where, onSnapshot } from 'firebase/firestore'; +import { getDownloadURL, getStorage, ref, uploadBytes } from 'firebase/storage'; // Initialize Cloud Firestore and get a reference to the service const db = getFirestore(firebaseApp); const storage = getStorage(firebaseApp); -const experiments = collection(db, "Experiments") +const experiments = collection(db, 'Experiments'); export const submitExperiment = async (values, userId) => { - const newExperiment = doc(experiments) - console.log("Experiment submitted. Values:", values); + const newExperiment = doc(experiments); + console.log('Experiment submitted. Values:', values); setDoc(newExperiment, { creator: userId, name: values.name, @@ -27,84 +27,83 @@ export const submitExperiment = async (values, userId) => { created: Date.now(), params: JSON.stringify({ params: values.parameters, - }) - }) - console.log("Created Experiment: " + newExperiment.id) - return newExperiment.id + }), + }); + console.log(`Created Experiment: ${newExperiment.id}`); + return newExperiment.id; }; export const uploadExec = async (id, file) => { - const fileRef = ref(storage, "experiment" + id) + const fileRef = ref(storage, `experiment${id}`); return await uploadBytes(fileRef, file).then((snapshot) => { - const experimentRef = doc(db, "Experiments", id) + const experimentRef = doc(db, 'Experiments', id); updateDoc(experimentRef, { - file: "experiment" + id + file: `experiment${id}`, }).then(() => { - console.log("Uploaded file for experiment " + id) - return true - }).catch(error => console.log("Upload doc error: ", error)) - return true - }).catch(error => { - console.log("Upload bytes error: ", error) - return false - }) + console.log(`Uploaded file for experiment ${id}`); + return true; + }).catch((error) => console.log('Upload doc error: ', error)); + return true; + }).catch((error) => { + console.log('Upload bytes error: ', error); + return false; + }); }; export const getDocById = (id) => { - getDoc(doc(db, "Experiments", id)).then(docSnap => { + getDoc(doc(db, 'Experiments', id)).then((docSnap) => { if (docSnap.exists()) { return docSnap.data(); } else { - console.log("No such document!"); + console.log('No such document!'); } - }) - -} + }); +}; export const downloadExp = (event) => { - const id = event.target.getAttribute('data-id') - console.log(`Downloading results for ${id}`) - const fileRef = ref(storage, `results/result${id}.csv`) - getDownloadURL(fileRef).then(url => { - const anchor = document.createElement('a') - anchor.href = url - anchor.download = `result${id}.csv` - document.body.appendChild(anchor) - anchor.click() - document.body.removeChild(anchor) - }).catch(error => console.log("Get download url for exp error: ", error)) -} + const id = event.target.getAttribute('data-id'); + console.log(`Downloading results for ${id}`); + const fileRef = ref(storage, `results/result${id}.csv`); + getDownloadURL(fileRef).then((url) => { + const anchor = document.createElement('a'); + anchor.href = url; + anchor.download = `result${id}.csv`; + document.body.appendChild(anchor); + anchor.click(); + document.body.removeChild(anchor); + }).catch((error) => console.log('Get download url for exp error: ', error)); +}; export const downloadExpZip = (event) => { - const id = event.target.getAttribute('data-id') - console.log(`Downloading results for ${id}`) - const fileRef = ref(storage, `results/result${id}.zip`) - getDownloadURL(fileRef).then(url => { - const anchor = document.createElement('a') - anchor.href = url - anchor.download = `result${id}.csv` - document.body.appendChild(anchor) - anchor.click() - document.body.removeChild(anchor) - }).catch(error => console.log("Upload download url for zip error: ", error)) -} + const id = event.target.getAttribute('data-id'); + console.log(`Downloading results for ${id}`); + const fileRef = ref(storage, `results/result${id}.zip`); + getDownloadURL(fileRef).then((url) => { + const anchor = document.createElement('a'); + anchor.href = url; + anchor.download = `result${id}.csv`; + document.body.appendChild(anchor); + anchor.click(); + document.body.removeChild(anchor); + }).catch((error) => console.log('Upload download url for zip error: ', error)); +}; export const subscribeToExp = (id, callback) => { - const unsubscribe = onSnapshot(doc(db, "Experiments", id), doc => { - console.log(`exp ${id} data updated: `, doc.data()) - callback(doc.data()) - }) - return unsubscribe -} + const unsubscribe = onSnapshot(doc(db, 'Experiments', id), (doc) => { + console.log(`exp ${id} data updated: `, doc.data()); + callback(doc.data()); + }); + return unsubscribe; +}; export const listenToExperiments = (uid, callback) => { - const q = query(experiments, where("creator", "==", uid)) + const q = query(experiments, where('creator', '==', uid)); const unsubscribe = onSnapshot(q, (snapshot) => { - let result: unknown[] = [] - snapshot.forEach(doc => result.push(doc.data())) - callback(result) - }) - return unsubscribe -} + const result: unknown[] = []; + snapshot.forEach((doc) => result.push(doc.data())); + callback(result); + }); + return unsubscribe; +}; diff --git a/apps/frontend/firebase/fbAuth.tsx b/apps/frontend/firebase/fbAuth.tsx index cb395fb9..aa12bbfc 100644 --- a/apps/frontend/firebase/fbAuth.tsx +++ b/apps/frontend/firebase/fbAuth.tsx @@ -5,10 +5,10 @@ import React, { useContext, createContext, useDebugValue, - FC } from 'react'; import { firebaseApp } from './firebaseClient'; -import { createUserWithEmailAndPassword, getAuth, onAuthStateChanged, signInWithEmailAndPassword, signOut, User } from 'firebase/auth' +import { createUserWithEmailAndPassword, getAuth, onAuthStateChanged, signInWithEmailAndPassword, signOut, User } from 'firebase/auth'; +import noImage from '../images/NoImage.png' export interface AuthContextType { user: User | null; @@ -19,7 +19,7 @@ export interface AuthContextType { const AuthContext = createContext({ user: null, userId: null, - authService: null + authService: null, }); export const useAuth = () => useContext(AuthContext); @@ -36,13 +36,13 @@ export const AuthProvider: React.FC = ({ children }) => { const [loading, setLoading] = useState(true); useEffect(() => onAuthStateChanged(auth, (newUser) => { - console.log("OnAuthStateChanged fired", newUser); + console.log('OnAuthStateChanged fired', newUser); setLoading(false); if (newUser) { - console.log("User is signed in"); + console.log('User is signed in'); setUser(newUser); } else { - console.log("No user signed in"); + console.log('No user signed in'); } }), [auth]); @@ -58,30 +58,30 @@ export const AuthProvider: React.FC = ({ children }) => { }, [user]), userPhotoUrl: useMemo(() => { - return user?.photoURL; + return user?.photoURL || noImage; }, [user]), signInWithEmailAndPassword: async (email: string, password: string) => { return await signInWithEmailAndPassword(auth, email, password) - .then((userCredential) => { - console.log("sign in success, UserCred is ", userCredential); + .then((userCredential) => { + console.log('sign in success, UserCred is ', userCredential); // no need to set state because onAuthStateChanged will pick it up - }).catch((error) => { - console.error('Firebase sign in error', error); - throw error; - }); + }).catch((error) => { + console.error('Firebase sign in error', error); + throw error; + }); }, signUpWithEmailAndPassword: async (email: string, password: string) => { return await createUserWithEmailAndPassword(auth, email, password) - .then((userCredential) => { - console.log("sign up success, UserCred is ", userCredential); - }).catch((error) => { - console.error('Firebase sign up error', error); - throw error; - }); + .then((userCredential) => { + console.log('sign up success, UserCred is ', userCredential); + }).catch((error) => { + console.error('Firebase sign up error', error); + throw error; + }); }, signInWithGoogle: async () => { - console.error("TODO"); + console.error('TODO'); }, signOut: async () => { return await signOut(auth); diff --git a/apps/frontend/firebase/firebaseClient.ts b/apps/frontend/firebase/firebaseClient.ts index e9605361..09c59bd4 100644 --- a/apps/frontend/firebase/firebaseClient.ts +++ b/apps/frontend/firebase/firebaseClient.ts @@ -2,13 +2,13 @@ import { initializeApp } from 'firebase/app'; const firebaseConfig = { - apiKey: "AIzaSyDj-Y8FFq2C80ZSVUVAWd3eqcmSzXMHXus", - authDomain: "gladosbase.firebaseapp.com", - databaseURL: "https://gladosbase-default-rtdb.firebaseio.com", - projectId: "gladosbase", - storageBucket: "gladosbase.appspot.com", - messagingSenderId: "431843551362", - appId: "1:431843551362:web:0bb28196e90f31a194ec9b" + apiKey: 'AIzaSyDj-Y8FFq2C80ZSVUVAWd3eqcmSzXMHXus', + authDomain: 'gladosbase.firebaseapp.com', + databaseURL: 'https://gladosbase-default-rtdb.firebaseio.com', + projectId: 'gladosbase', + storageBucket: 'gladosbase.appspot.com', + messagingSenderId: '431843551362', + appId: '1:431843551362:web:0bb28196e90f31a194ec9b', }; export const firebaseApp = initializeApp(firebaseConfig); diff --git a/apps/frontend/images/NoImage.png b/apps/frontend/images/NoImage.png new file mode 100644 index 00000000..32ea5c18 Binary files /dev/null and b/apps/frontend/images/NoImage.png differ diff --git a/apps/frontend/next.config.js b/apps/frontend/next.config.js index 7ca34f03..239ff1db 100644 --- a/apps/frontend/next.config.js +++ b/apps/frontend/next.config.js @@ -1,6 +1,22 @@ -/** @type {import('next').NextConfig} */ +/** + * @type {import('next').NextConfig} + */ +// TODO VSCode is detecting error "Parsing error: Cannot find module 'next/babel'" but the fix below just causes ESLint errors: +// https://stackoverflow.com/questions/71662525/failed-to-load-config-next-babel-to-extend-from-eslintrc-json +// https://nextjs.org/docs/api-reference/next.config.js/introduction + const nextConfig = { reactStrictMode: true, + images: { + // https://nextjs.org/docs/api-reference/next/image#remote-patterns + remotePatterns: [ + { + protocol: 'https', + hostname: 'tailwindui.com', + pathname: '/img/**' + }, + ], + } }; module.exports = nextConfig; diff --git a/apps/frontend/pages/dashboard.tsx b/apps/frontend/pages/dashboard.tsx index 30dee8ed..e02d4adb 100644 --- a/apps/frontend/pages/dashboard.tsx +++ b/apps/frontend/pages/dashboard.tsx @@ -1,9 +1,8 @@ import NewExp, { FormStates } from '../components/NewExp'; import { useAuth } from '../firebase/fbAuth'; -import { subscribeToExp, listenToExperiments, downloadExp, downloadExpZip} from '../firebase/db'; +import { subscribeToExp, listenToExperiments, downloadExp, downloadExpZip } from '../firebase/db'; import { Fragment, useState, useEffect } from 'react'; import { Disclosure, Menu, Transition } from '@headlessui/react'; -import Link from 'next/link'; import { CheckBadgeIcon, ChevronDownIcon, @@ -11,13 +10,12 @@ import { RectangleStackIcon, MagnifyingGlassIcon, BarsArrowUpIcon, - StarIcon } from '@heroicons/react/24/solid'; -import { MenuAlt1Icon, XIcon } from '@heroicons/react/outline'; import { Logo } from '../components/Logo'; import classNames from 'classnames'; import Router from 'next/router'; +import Image from 'next/image'; const navigation = [{ name: 'Admin', href: '#', current: false }]; const userNavigation = [ @@ -82,10 +80,10 @@ const Navbar = (props) => {
Open user menu -
@@ -110,9 +108,9 @@ const Navbar = (props) => { authService .signOut() .then(() => { - Router.reload() + Router.reload(); }) - .catch((err) => console.log("Sign out error", err)) + .catch((err) => console.log('Sign out error', err)) ); }} className={classNames( @@ -141,9 +139,9 @@ const Navbar = (props) => { as='a' href={item.href} className={classNames( - item.current - ? 'text-white bg-blue-800' - : 'text-blue-200 hover:text-blue-100 hover:bg-blue-600', + item.current ? + 'text-white bg-blue-800' : + 'text-blue-200 hover:text-blue-100 hover:bg-blue-600', 'block px-3 py-2 rounded-md text-base font-medium' )} aria-current={item.current ? 'page' : undefined} @@ -177,10 +175,10 @@ const Navbar = (props) => { }; -const ExpLog = ({projectinit, setFormState, setCopyId}) => { - const [project, setProject] = useState(projectinit); - useEffect(() => subscribeToExp(project.expId, setProject),[]) - return ( +const ExpLog = ({ projectinit, setFormState, setCopyId }) => { + const [project, setProject] = useState(projectinit); + useEffect(() => subscribeToExp(project.expId, setProject), []); // TODO adding project causes render loop + return (
@@ -191,27 +189,27 @@ const ExpLog = ({projectinit, setFormState, setCopyId}) => {
{project['finished'] == true ? - - : ' '} + : + ' '} {project['finished'] == true && (project['fileOutput'] != '' || project['scatter'] != '')? - - : ' '} + : + ' '} + { />
-

+

FAILS: {project['fails']} SUCCESSES: {project['passes']}

@@ -237,9 +235,9 @@ const ExpLog = ({projectinit, setFormState, setCopyId}) => { {project.location}

-
- ) -} +
+ ); +}; const SearchBar = (props) => { return ( @@ -267,15 +265,15 @@ const SearchBar = (props) => { export default function DashboardPage() { const { userId, authService } = useAuth(); - const [experiments, setExperiments] = useState([] as unknown[]); // TODO experiment type + const [experiments, setExperiments] = useState([] as unknown[]); // TODO experiment type useEffect(() => { if (!userId) { return; } - return listenToExperiments(userId, (newExperimentList)=> setExperiments(newExperimentList)) - },[userId]) - + return listenToExperiments(userId, (newExperimentList) => setExperiments(newExperimentList)); + }, [userId]); + const [copyID, setCopyId] = useState(null); const [formState, setFormState] = useState(FormStates.Closed); const [label, setLabel] = useState('New Experiment'); @@ -297,7 +295,7 @@ export default function DashboardPage() { className='fixed top-0 right-0 w-1/2 h-full bg-gray-50' aria-hidden='true' /> - +
{/* Navbar */} @@ -315,10 +313,10 @@ export default function DashboardPage() { {/* Profile */} @@ -466,12 +467,16 @@ export default function DashboardPage() {