Skip to content

Commit

Permalink
Austenem/CAT-835 add id search to dialog (#3517)
Browse files Browse the repository at this point in the history
* add empty alert to entities table

* show empty datasets alert in table

* update dialog language

* fix key issue

* basic table addition

* working minus dialog from selection

* fix dataset selection table issue

* fix workspace detail page dialog

* fix viz button

* fix deletion issue

* modify data description

* update doc type

* add changelog

* update useeffect

* remove memo
  • Loading branch information
austenem authored Aug 26, 2024
1 parent b28fecc commit 93f8af2
Show file tree
Hide file tree
Showing 19 changed files with 214 additions and 72 deletions.
1 change: 1 addition & 0 deletions CHANGELOG-add-id-search-to-dialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Add the HuBMAP ID search bar component to the "Launch New Workspace" dialog on the "My Workspaces" landing page.
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,14 @@ function WorkspaceButton() {
const {
control,
errors,
removeDatasets,
setDialogIsOpen: setOpenCreateWorkspace,
dialogIsOpen: createWorkspaceIsOpen,
...rest
} = useCreateWorkspaceForm({ defaultName: currentDataset?.hubmap_id });
} = useCreateWorkspaceForm({
defaultName: currentDataset?.hubmap_id,
initialSelectedDatasets: currentDataset ? [currentDataset.uuid] : [],
});

const openEditWorkspaceDialog = useOpenDialog('ADD_DATASETS_FROM_SEARCH');

Expand Down Expand Up @@ -183,13 +187,7 @@ function WorkspaceButton() {
Add to Workspace
</MenuItem>
</Menu>
<NewWorkspaceDialog
datasetUUIDs={new Set([currentDataset.uuid])}
dialogIsOpen={createWorkspaceIsOpen}
control={control}
errors={errors}
{...rest}
/>
<NewWorkspaceDialog dialogIsOpen={createWorkspaceIsOpen} control={control} errors={errors} {...rest} />
<AddDatasetsFromSearchDialog />
</SelectableTableProvider>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,10 @@ function CreateWorkspaceButton({
hubmap_id,
mapped_data_access_level,
}: Pick<Entity, 'uuid' | 'hubmap_id' | 'mapped_data_access_level'>) {
const { setDialogIsOpen, ...rest } = useCreateWorkspaceForm({ defaultName: hubmap_id });
const { setDialogIsOpen, removeDatasets, ...rest } = useCreateWorkspaceForm({
defaultName: hubmap_id,
initialSelectedDatasets: [uuid],
});

const disabled = mapped_data_access_level === 'Protected';

Expand All @@ -108,7 +111,7 @@ function CreateWorkspaceButton({
tooltip={disabled ? 'Protected datasets are not available in Workspaces.' : 'Launch a new workspace.'}
disabled={disabled}
/>
<NewWorkspaceDialog datasetUUIDs={new Set([uuid])} {...rest} />
<NewWorkspaceDialog {...rest} />
</>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ function VisualizationNotebookButton({ uuid, hubmap_id, mapped_data_access_level
const trackEntityPageEvent = useTrackEntityPageEvent();
const { toastError } = useSnackbarActions();

const { setDialogIsOpen, ...rest } = useCreateWorkspaceForm({
const { setDialogIsOpen, removeDatasets, ...rest } = useCreateWorkspaceForm({
defaultName: hubmap_id,
defaultTemplate: 'visualization',
initialSelectedDatasets: [uuid],
});

const downloadNotebook = useCallback(() => {
Expand Down Expand Up @@ -57,7 +58,7 @@ function VisualizationNotebookButton({ uuid, hubmap_id, mapped_data_access_level

return (
<>
<NewWorkspaceDialog datasetUUIDs={new Set([uuid])} {...rest} />
<NewWorkspaceDialog {...rest} />
<IconDropdownMenu tooltip={tooltip} icon={WorkspacesIcon}>
{options.map((props) => (
<IconDropdownMenuItem key={props.children} {...props} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

import { Workspace } from '../types';
import { useUpdateWorkspaceDatasets } from '../hooks';
import { useUpdateWorkspaceDatasets, useWorkspaceDetail } from '../hooks';
import { datasetsField } from '../workspaceFormFields';
import { useDatasetsAutocomplete } from '../AddDatasetsTable';
import { useTooManyDatasetsErrors, useTooManyDatasetsWarnings } from '../formHooks';
Expand Down Expand Up @@ -51,6 +51,8 @@ function useAddDatasetsDialog({ workspace }: { workspace: Workspace }) {
name: 'datasets',
});

const { workspaceDatasets: initialDatasets } = useWorkspaceDetail({ workspaceId });

const {
inputValue,
setInputValue,
Expand All @@ -62,7 +64,7 @@ function useAddDatasetsDialog({ workspace }: { workspace: Workspace }) {
searchHits,
resetAutocompleteState,
} = useDatasetsAutocomplete({
workspaceId,
workspaceDatasets: initialDatasets,
selectedDatasets: field.value,
updateDatasetsFormState: field.onChange,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

import { useEditWorkspaceStore } from 'js/stores/useWorkspaceModalStore';
import { useUpdateWorkspaceDatasets } from '../hooks';
import { useUpdateWorkspaceDatasets, useWorkspaceDetail } from '../hooks';
import {
datasetsField as datasetsFieldSchema,
workspaceIdField as workspaceIdFieldSchema,
Expand Down Expand Up @@ -102,6 +102,8 @@ function useAddDatasetsFromSearchDialog() {
name: 'workspaceId',
});

const { workspaceDatasets: initialDatasets } = useWorkspaceDetail({ workspaceId: workspaceIdField.value });

const {
inputValue,
setInputValue,
Expand All @@ -113,7 +115,7 @@ function useAddDatasetsFromSearchDialog() {
searchHits,
resetAutocompleteState,
} = useDatasetsAutocomplete({
workspaceId: workspaceIdField.value,
workspaceDatasets: initialDatasets,
selectedDatasets: datasetsField.value,
updateDatasetsFormState: datasetsField.onChange,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Autocomplete, { AutocompleteRenderInputParams } from '@mui/material/Autoc
import InputAdornment from '@mui/material/InputAdornment';
import Typography from '@mui/material/Typography';

import { Alert } from 'js/shared-styles/alerts/Alert';
import { UseDatasetsAutocompleteReturnType, SearchAheadHit } from './hooks';
import WorkspaceDatasetsTable from '../WorkspaceDatasetsTable';

Expand Down Expand Up @@ -75,6 +76,7 @@ function AddDatasetsTable({
datasetsUUIDs={allDatasets}
disabledIDs={new Set(workspaceDatasets)}
removeDatasets={removeDatasets}
emptyAlert={<Alert severity="info">No datasets available.</Alert>}
/>
</Stack>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { useSearchHits } from 'js/hooks/useSearchData';
import { Dataset } from 'js/components/types';
import { useSnackbarActions } from 'js/shared-styles/snackbars/store';

import { useWorkspaceDetail } from '../hooks';

interface BuildIDPrefixQueryType {
value: string;
valuePrefix?: string;
Expand Down Expand Up @@ -81,23 +79,26 @@ function useSearchAhead({ value, valuePrefix = '', uuidsToExclude = [] }: BuildI
}

function useDatasetsAutocomplete({
workspaceId,
workspaceDatasets = [],
selectedDatasets = [],
updateDatasetsFormState,
}: {
workspaceId: number;
workspaceDatasets?: string[];
selectedDatasets: string[];
updateDatasetsFormState: (datasetUUIDS: string[]) => void;
}) {
const [inputValue, setInputValue] = useState('');
const [, setRefresh] = useState(false);
const [autocompleteValue, setAutocompleteValue] = useState<SearchAheadHit | null>(null);
const { toastSuccess } = useSnackbarActions();

const removeDatasets = useCallback(
(uuids: string[]) => {
const selectedDatasetsSet = new Set(selectedDatasets);
uuids.forEach((uuid) => selectedDatasetsSet.delete(uuid));
updateDatasetsFormState([...selectedDatasetsSet]);
const updatedDatasets = selectedDatasets.filter((uuid) => !uuids.includes(uuid));
updateDatasetsFormState(updatedDatasets);

// Trigger a re-render to update the table results
setRefresh((prev) => !prev);
},
[selectedDatasets, updateDatasetsFormState],
);
Expand All @@ -120,7 +121,6 @@ function useDatasetsAutocomplete({
[selectedDatasets, updateDatasetsFormState, resetAutocompleteState, toastSuccess],
);

const { workspaceDatasets } = useWorkspaceDetail({ workspaceId });
const allDatasets = [...workspaceDatasets, ...selectedDatasets];
const { searchHits } = useSearchAhead({ value: inputValue, valuePrefix: 'HBM', uuidsToExclude: allDatasets });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,23 @@ import Box from '@mui/material/Box';
import Stack from '@mui/material/Stack';
import Button from '@mui/material/Button';
import LoadingButton from '@mui/lab/LoadingButton';
import Typography from '@mui/material/Typography';

import Step, { StepDescription } from 'js/shared-styles/surfaces/Step';
import { Alert } from 'js/shared-styles/alerts/Alert';
import WorkspaceField from 'js/components/workspaces/WorkspaceField';
import { useLaunchWorkspaceStore } from 'js/stores/useWorkspaceModalStore';
import { useSelectItems } from 'js/hooks/useSelectItems';
import InternalLink from 'js/shared-styles/Links/InternalLink';

import { useWorkspaceTemplates } from './hooks';
import { CreateWorkspaceFormTypes } from './useCreateWorkspaceForm';
import { CreateTemplateNotebooksTypes } from '../types';
import WorkspaceDatasetsTable from '../WorkspaceDatasetsTable';
import TemplateSelectStep from '../TemplateSelectStep';
import WorkspaceJobTypeField from '../WorkspaceJobTypeField';
import AddDatasetsTable from '../AddDatasetsTable';
import { SearchAheadHit } from '../AddDatasetsTable/hooks';

const text = {
overview: {
Expand All @@ -31,10 +36,27 @@ const text = {
},
datasets: {
title: 'Edit Datasets Selection',
description: [
'To remove a dataset, select the dataset and press the delete button. If all datasets are removed, an empty workspace will be launched.',
'To add more datasets to a workspace, you must navigate to the dataset search page, select datasets of interests and follow steps to launch a workspace from the search page. As a reminder, once you navigate away from this page, all selected datasets will be lost so take note of any HuBMAP IDs of interest, or copy IDs to your clipboard by selecting datasets in the table below and pressing the copy button. You can also save datasets to the “My Lists” feature.',
],
description: {
searchBar: [
<span key="datasets-step">
{' '}
Add datasets by HuBMAP ID below or navigate to the{' '}
<InternalLink href="/search?entity_type[0]=Dataset">dataset search page</InternalLink>, select datasets and
follow steps to launch a workspace.
</span>,
],
all: [
'To remove a dataset, select the dataset and press the delete button. If all datasets are removed, an empty workspace will be launched.',
'Once you navigate away from this page, all progress will be lost. You can copy IDs to your clipboard by selecting datasets in the table below and pressing the copy button. You can also save datasets to “My Lists”.',
<Button variant="outlined" key="datasets-button">
<InternalLink href="/search?entity_type[0]=Dataset">
<Typography color="primary" variant="button">
Select Additional Datasets
</Typography>
</InternalLink>
</Button>,
],
},
},
configure: {
title: 'Configure Workspace',
Expand All @@ -53,27 +75,41 @@ type ReactHookFormProps = Pick<UseFormReturn<CreateWorkspaceFormTypes>, 'handleS
};

interface NewWorkspaceDialogProps {
datasetUUIDs?: Set<string>;
errorMessages?: string[];
dialogIsOpen: boolean;
handleClose: () => void;
removeDatasets?: (datasetUUIDs: string[]) => void;
removeDatasets?: (uuids: string[]) => void;
onSubmit: ({ workspaceName, templateKeys, uuids }: CreateTemplateNotebooksTypes) => void;
isSubmitting?: boolean;
showDatasetsSearchBar?: boolean;
inputValue: string;
setInputValue: React.Dispatch<React.SetStateAction<string>>;
autocompleteValue: SearchAheadHit | null;
addDataset: (e: React.SyntheticEvent<Element, Event>, newValue: SearchAheadHit | null) => void;
workspaceDatasets: string[];
allDatasets: string[];
searchHits: SearchAheadHit[];
}

function NewWorkspaceDialog({
datasetUUIDs = new Set(),
errorMessages = [],
dialogIsOpen,
handleClose,
handleSubmit,
control,
errors,
onSubmit,
removeDatasets,
children,
isSubmitting,
showDatasetsSearchBar,
inputValue,
setInputValue,
autocompleteValue,
addDataset,
removeDatasets,
workspaceDatasets,
allDatasets,
searchHits,
}: PropsWithChildren<NewWorkspaceDialogProps & ReactHookFormProps>) {
const { selectedItems: selectedRecommendedTags, toggleItem: toggleTag } = useSelectItems([]);
const [selectedTags, setSelectedTags] = useState<string[]>([]);
Expand All @@ -83,15 +119,20 @@ function NewWorkspaceDialog({
const { templates } = useWorkspaceTemplates([...selectedTags, ...selectedRecommendedTags]);

const submit = useCallback(
({ 'workspace-name': workspaceName, templates: templateKeys, workspaceJobTypeId }: CreateWorkspaceFormTypes) => {
({
'workspace-name': workspaceName,
templates: templateKeys,
workspaceJobTypeId,
datasets,
}: CreateWorkspaceFormTypes) => {
onSubmit({
workspaceName,
templateKeys,
uuids: [...datasetUUIDs],
uuids: datasets,
workspaceJobTypeId,
});
},
[datasetUUIDs, onSubmit],
[onSubmit],
);

return (
Expand All @@ -114,9 +155,29 @@ function NewWorkspaceDialog({
<Step title={text.datasets.title} index={0}>
<Stack spacing={1}>
{children}
<StepDescription blocks={text.datasets.description} />
{datasetUUIDs.size > 0 && (
<WorkspaceDatasetsTable datasetsUUIDs={[...datasetUUIDs]} removeDatasets={removeDatasets} />
<StepDescription
blocks={[
...(showDatasetsSearchBar ? [...text.datasets.description.searchBar] : []),
...text.datasets.description.all,
]}
/>
{showDatasetsSearchBar && removeDatasets ? (
<AddDatasetsTable
inputValue={inputValue}
setInputValue={setInputValue}
autocompleteValue={autocompleteValue}
addDataset={addDataset}
removeDatasets={removeDatasets}
workspaceDatasets={workspaceDatasets}
allDatasets={allDatasets}
searchHits={searchHits}
/>
) : (
<WorkspaceDatasetsTable
datasetsUUIDs={allDatasets}
removeDatasets={removeDatasets}
emptyAlert={<Alert severity="info">No datasets available.</Alert>}
/>
)}
</Stack>
</Step>
Expand Down
Loading

0 comments on commit 93f8af2

Please sign in to comment.