diff --git a/applications/visualizer/frontend/src/components/AppLauncher.tsx b/applications/visualizer/frontend/src/components/AppLauncher.tsx index 4c1a966b..3ba20739 100644 --- a/applications/visualizer/frontend/src/components/AppLauncher.tsx +++ b/applications/visualizer/frontend/src/components/AppLauncher.tsx @@ -1,19 +1,32 @@ -import { AppBar, Box, Button, Card, CardActionArea, CardContent, Container, Grid, Toolbar, Typography } from "@mui/material"; -import footerImage from "../assets/summary-neurons.png"; +import { BarChart } from "@mui/icons-material"; +import ArrowForwardIcon from "@mui/icons-material/ArrowForward"; +import { AppBar, Box, Button, Chip, Container, Grid, IconButton, Toolbar, Typography } from "@mui/material"; +import { debounce } from "lodash"; +import { useCallback, useEffect, useState } from "react"; +import { Link, useLocation } from "react-router-dom"; import { useGlobalContext } from "../contexts/GlobalContext.tsx"; -import { parseURLParams } from "../helpers/parseURLHelper.ts"; -import { TEMPLATE_ACTIVE_DATASETS, TEMPLATE_ACTIVE_NEURONS } from "../settings/templateWorkspaceSettings.ts"; -function AppLauncher() { - const { workspaces, createWorkspace, setCurrentWorkspace, setSelectedWorkspacesIds } = useGlobalContext(); +import { CaretIcon, CheckIcon, CloseIcon } from "../icons"; +import Logo from "../icons/Logo.svg"; +import { GlobalError } from "../models/Error.ts"; +import { NeuronsService } from "../rest"; +import { TEMPLATE_ACTIVE_DATASETS } from "../settings/templateWorkspaceSettings.ts"; +import { vars } from "../theme/variables.ts"; +import CustomAutocomplete from "./CustomAutocomplete.tsx"; +function AppLauncher() { + const location = useLocation(); + const { workspaces, createWorkspace, setCurrentWorkspace, setSelectedWorkspacesIds, handleErrors } = useGlobalContext(); + const [selectedNeurons, setSelectedNeurons] = useState([]); + const [neuronNames, setNeuronsNames] = useState([]); + const [searchedNeuron, setSearchedNeuron] = useState(""); + const isActive = (path) => location.pathname === path; const handleTemplateClick = async () => { const workspaceId = `workspace-${Date.now()}`; const workspaceName = `Template Workspace ${Object.keys(workspaces).length + 1}`; - createWorkspace(workspaceId, workspaceName, new Set(TEMPLATE_ACTIVE_DATASETS), new Set(TEMPLATE_ACTIVE_NEURONS)); + createWorkspace(workspaceId, workspaceName, new Set(TEMPLATE_ACTIVE_DATASETS), new Set(selectedNeurons)); setCurrentWorkspace(workspaceId); setSelectedWorkspacesIds(new Set([workspaceId])); }; - const handleBlankClick = () => { const workspaceId = `workspace-${Date.now()}`; const workspaceName = `Workspace ${Object.keys(workspaces).length + 1}`; @@ -22,90 +35,141 @@ function AppLauncher() { setCurrentWorkspace(workspaceId); setSelectedWorkspacesIds(new Set([workspaceId])); }; + const fetchNeurons = async () => { + try { + const neuronArrays = await NeuronsService.searchCells({ name: searchedNeuron, datasetIds: TEMPLATE_ACTIVE_DATASETS }); + const uniqueNeurons = new Set(); + for (const neuron of neuronArrays.flat()) { + uniqueNeurons.add(neuron.name); + uniqueNeurons.add(neuron.nclass); + } - const handlePasteUrlClick = () => { - const exampleURL = "http://localhost:8080/mode=default&ws_name=workspace1&ids=ADAL,AIBR,RIML&ws_name=workspace3&ids=RIFL,REMV&ws_name=test&ids=ADAL"; + setNeuronsNames([...uniqueNeurons]); + } catch (error) { + handleErrors(new GlobalError(error.message)); + } + }; - const parsedParams = parseURLParams(exampleURL); - console.log(parsedParams); + const onSearchNeurons = (value) => { + setSearchedNeuron(value); + debouncedFetchNeurons(value, TEMPLATE_ACTIVE_DATASETS); }; + + const handleNeuronChange = (value) => { + setSelectedNeurons(value); + }; + + const debouncedFetchNeurons = useCallback(debounce(fetchNeurons, 300), []); + + useEffect(() => { + debouncedFetchNeurons(); + }, []); + + const getSortedNeuronNames = () => { + const uniqueNeurons = new Set(selectedNeurons.concat(neuronNames)); + return Array.from(uniqueNeurons); + }; + return ( <> - + - - + + + + + + } + label={`${TEMPLATE_ACTIVE_DATASETS.length} datasets, ${neuronNames.length} neurons`} + variant="outlined" + className="basic" + /> - - - Welcome to C. Elegans - - - Explore, query and visualize C. elegans datasets. To get started, choose from one of the options below. - - - - - - - - Start with a simple dataset - - Simple - - - - Start exploring the application without a particular dataset in mind. We’ll load a simple dataset for you to start exploring. - - - - - - - - - - - Blank canvas - - Advanced - - - Start with a blank canvas and select the datasets and neurons of your choice. - - - + + logo - - - - - - Paste URL - - Paste URL from your pre-designed view or from one that your collaborators sent to you - - - + + ( +
  • + + {option} +
  • + )} + onInputChange={onSearchNeurons} + placeholder="Start typing to select cell(s) to start with" + className="secondary" + id="tags-standard" + popupIcon={} + ChipProps={{ + deleteIcon: ( + + + + ), + }} + clearIcon={false} + value={selectedNeurons} + onChange={handleNeuronChange} + sx={{ + flex: 1, + maxWidth: "37.5rem", + "& .MuiInputBase-root": { + backgroundColor: vars.white, + padding: "0.75rem 0.875rem", + }, + }} + /> +
    - - + +
    - - footerimage -
    ); diff --git a/applications/visualizer/frontend/src/components/CustomAutocomplete.tsx b/applications/visualizer/frontend/src/components/CustomAutocomplete.tsx index 37b5dccb..4b9456ad 100644 --- a/applications/visualizer/frontend/src/components/CustomAutocomplete.tsx +++ b/applications/visualizer/frontend/src/components/CustomAutocomplete.tsx @@ -49,6 +49,7 @@ const CommonAutocomplete = ({ // @ts-ignore return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/applications/visualizer/frontend/src/models/workspace.ts b/applications/visualizer/frontend/src/models/workspace.ts index efb35c3f..6622c9d6 100644 --- a/applications/visualizer/frontend/src/models/workspace.ts +++ b/applications/visualizer/frontend/src/models/workspace.ts @@ -85,7 +85,7 @@ export class Workspace { this.layoutManager = layoutManager; this.syncOrchestrator = SynchronizerOrchestrator.create(activeSynchronizers, contexts); - this.visibilities = visibilities || Object.fromEntries([...activeNeurons].map((n) => [n, getDefaultViewerData(Visibility.Visible)])); + this.visibilities = visibilities || Object.fromEntries([...(activeNeurons || [])].map((n) => [n, getDefaultViewerData(Visibility.Visible)])); this.store = store; this.updateContext = updateContext; diff --git a/applications/visualizer/frontend/src/theme/index.tsx b/applications/visualizer/frontend/src/theme/index.tsx index 81a9ecce..1a693a81 100644 --- a/applications/visualizer/frontend/src/theme/index.tsx +++ b/applications/visualizer/frontend/src/theme/index.tsx @@ -194,6 +194,23 @@ const theme = createTheme({ borderColor: primaryPurple100, padding: "0.125rem 0.625rem", }, + + "&.basic": { + background: "transparent", + border: `1px solid ${gray200}`, + padding: "0.5rem 0.75rem", + borderRadius: "1.5rem", + color: gray600, + height: "initial", + fontSize: "0.875rem", + fontWeight: 500, + lineHeight: "1.25rem", + + "& .MuiChip-icon": { + margin: 0, + marginRight: ".38rem", + }, + }, }, }, }, @@ -637,7 +654,7 @@ const theme = createTheme({ fontSize: "0.875rem", fontWeight: 600, height: "2.25rem", - padding: "0 0.75rem", + padding: "0.5rem 0.75rem", }, outlinedSecondary: { borderColor: gray200, @@ -664,9 +681,10 @@ const theme = createTheme({ }, }, outlined: { - border: `0.0625rem solid ${gray100}`, + border: `0.0625rem solid ${gray200}`, background: white, - color: gray600, + color: gray700, + boxShadow: "0px 1px 2px 0px rgba(16, 24, 40, 0.05)", "& .MuiSvgIcon-root": { fontSize: "1.25rem", }, @@ -677,7 +695,12 @@ const theme = createTheme({ }, text: { boxShadow: "none", - color: gray500, + color: gray600, + + "&:hover": { + background: gray100, + color: gray700, + }, }, textPrimary: { "&:hover": { @@ -709,6 +732,10 @@ const theme = createTheme({ "&:focus": { boxShadow: "0rem 0.0625rem 0.125rem 0rem rgba(16, 24, 40, 0.05), 0rem 0rem 0rem 0.25rem rgba(33, 85, 186, 0.24)", }, + + "& .MuiSvgIcon-root": { + color: white, + }, }, }, },