diff --git a/src/renderer/src/components/pages/EntityDataSelector/index.jsx b/src/renderer/src/components/pages/EntityDataSelector/index.jsx index b64ebfd5b..8cee1fc5f 100644 --- a/src/renderer/src/components/pages/EntityDataSelector/index.jsx +++ b/src/renderer/src/components/pages/EntityDataSelector/index.jsx @@ -154,12 +154,13 @@ const EntityDataSelectorPage = ({ {entityType === "source-derivative-folders-and-files" ? ( - Source data is raw, unaltered data such as ... + Source data is raw, unaltered data from an experiment such as recorded neural + signals or unprocessed microscope images. - Derivative data processed or transformed data such as ... + Derivative data is data that has been processed or transformed data such as ... - Annotate your data as source or derivative in the interface below. + If you have source or derivative data, annotate it in the interface below. ) : ( diff --git a/src/renderer/src/components/pages/EntitySelector/index.jsx b/src/renderer/src/components/pages/EntitySelector/index.jsx index 0206c4400..9ebaff163 100644 --- a/src/renderer/src/components/pages/EntitySelector/index.jsx +++ b/src/renderer/src/components/pages/EntitySelector/index.jsx @@ -108,19 +108,20 @@ const EntitySelectorPage = ({ - Every {entityTypeStringSingular} that data was extracted from must be given a unique ID.{" "} - {upperCaseFirstLetter(entityTypeStringSingular)} IDs can be added via the three following - methods: + Every {entityTypeStringSingular} in your dataset must be assigned a unique{" "} + {entityTypeStringSingular} ID. {upperCaseFirstLetter(entityTypeStringSingular)} IDs can be + added via the two following methods: - 1. Manual Entry: Enter {entityTypeStringSingular} IDs manually in the interface - below. + 1. Manual Entry (Recommended if less than 10 {entityTypeStringPlural}): Enter{" "} + {entityTypeStringSingular} IDs manually by typing them out individually. 2. Spreadsheet Entry (Recommended for more than 10 {entityTypeStringPlural}):{" "} - Generate a spreadsheet template to input {entityTypeStringSingular} IDs in bulk. + Generate a spreadsheet template to input {entityTypeStringSingular} IDs into and then + import them in bulk. {/* diff --git a/src/renderer/src/components/shared/DatasetContentSelector/index.jsx b/src/renderer/src/components/shared/DatasetContentSelector/index.jsx index 628c489bb..48e663038 100644 --- a/src/renderer/src/components/shared/DatasetContentSelector/index.jsx +++ b/src/renderer/src/components/shared/DatasetContentSelector/index.jsx @@ -1,87 +1,107 @@ -import { Card, Stack, Text, Group } from "@mantine/core"; +import { Card, Stack, Text, Group, Tooltip } from "@mantine/core"; +import { IconCheck } from "@tabler/icons-react"; import FullWidthContainer from "../../containers/FullWidthContainer"; import useGlobalStore from "../../../stores/globalStore"; -import { toggleComponent } from "../../../stores/slices/datasetContentSelectorSlice"; +import { toggleEntitySelection } from "../../../stores/slices/datasetContentSelectorSlice"; + +const upperCaseFirstLetter = (string) => string.charAt(0).toUpperCase() + string.slice(1); const DatasetContentSelector = () => { - const selectedComponents = useGlobalStore((state) => state.selectedComponents); + const selectedEntities = useGlobalStore((state) => state.selectedEntities); - const options = [ + const contentOptions = [ { value: "subjects", - label: "Subjects", description: - "Individual entities, such as humans, animals, or other organisms, that participated in the study.", + "Individual entities, such as humans, animals, or other biological specimens, from which data was collected during the study.", }, { value: "samples", - label: "Samples", description: - "Biological or chemical specimens collected from subjects for further analysis or experimentation.", + "Biological specimens, such as tissue, blood, or fluid, collected from subjects for analysis or experimentation.", dependsOn: "subjects", }, { value: "sites", - label: "Sites", description: - "Specific locations in the body, environment, or experimental setup where data or samples were collected. Conditionally required if data were gathered from multiple distinct locations or experimental setups for the same subject or sample.", + "Multiple distinct anatomical or geographical locations where data was collected from subjects during the study.", dependsOn: "subjects", }, { value: "performances", - label: "Performances", description: - "Multiple distinct performances of one type of experimental protocol on the same subject or same sample (i.e. multiple visits, runs, sessions, or execution).", + "Multiple distinct performances of the same experimental protocol on the same subject or sample (e.g., multiple visits, runs, sessions, or executions).", dependsOn: "subjects", }, { value: "code", - label: "Code", description: - "Scripts, computational models, analysis pipelines, or other code/tools used in the study.", + "Scripts, computational models, analysis pipelines, or other code/tools used during the study for data processing or analysis.", }, ]; - const toggleSelection = (value) => { - toggleComponent(value); + const handleEntitySelection = (value) => { + const isSelected = selectedEntities.includes(value); + + // Deselect dependent entities if the value is deselected + if (isSelected) { + contentOptions.forEach((option) => { + if (option.dependsOn === value && selectedEntities.includes(option.value)) { + toggleEntitySelection(option.value); + } + }); + } + + // Toggle the selection of the current entity + toggleEntitySelection(value); }; return ( - {options.map((option) => { - const isDisabled = option.dependsOn && !selectedComponents.includes(option.dependsOn); - const isSelected = selectedComponents.includes(option.value); + {contentOptions.map((option) => { + const isDisabled = option.dependsOn && !selectedEntities.includes(option.dependsOn); + const isSelected = selectedEntities.includes(option.value) && !isDisabled; return ( - { - if (!isDisabled) { - toggleSelection(option.value); - } - }} + label={ + isDisabled + ? `Requires ${upperCaseFirstLetter(option.dependsOn)} to be selected` + : isSelected + ? `${upperCaseFirstLetter(option.value)} is selected` + : "" + } + disabled={!isDisabled && !isSelected} > - - - {option.label} + { + if (!isDisabled) handleEntitySelection(option.value); + }} + > + + + {upperCaseFirstLetter(option.value)} + + {isSelected && } + + + {option.description} - - - {option.description} - - + + ); })} diff --git a/src/renderer/src/components/shared/DatasetTreeViewRenderer/index.jsx b/src/renderer/src/components/shared/DatasetTreeViewRenderer/index.jsx index 76a446a45..c6e573ae7 100644 --- a/src/renderer/src/components/shared/DatasetTreeViewRenderer/index.jsx +++ b/src/renderer/src/components/shared/DatasetTreeViewRenderer/index.jsx @@ -289,10 +289,7 @@ const DatasetTreeViewRenderer = ({ folderActions, fileActions, allowStructureEdi datasetEntityObj: state.datasetEntityObj, })); - console.log("datasetEntityObj: ", datasetEntityObj); - const [inputSearchFilter, setInputSearchFilter] = useState(datasetStructureSearchFilter); - console.log("inputSearchFilter::", inputSearchFilter); const [debouncedSearchFilter] = useDebouncedValue(inputSearchFilter, 300); // 300ms debounce useEffect(() => { diff --git a/src/renderer/src/scripts/guided-mode/guided-curate-dataset.js b/src/renderer/src/scripts/guided-mode/guided-curate-dataset.js index 10b0cda49..e579ede0f 100644 --- a/src/renderer/src/scripts/guided-mode/guided-curate-dataset.js +++ b/src/renderer/src/scripts/guided-mode/guided-curate-dataset.js @@ -1168,6 +1168,25 @@ const savePageChanges = async (pageBeingLeftID) => { } if (pageBeingLeftID === "guided-prepare-dataset-structure-tab") { + const selectedEntities = useGlobalStore.getState()["selectedEntities"]; + console.log("selectedEntities", selectedEntities); + if (selectedEntities.length === 0) { + errorArray.push({ + type: "notyf", + message: "Please select at least one entity to continue", + }); + throw errorArray; + } + + /* ******************** + if (selectedEntities.length === 0) { + errorArray.push({ + type: "notyf", + message: "Please select at least one entity to continue", + }); + throw errorArray; + } + const buttonDatasetContainsSubjects = document.getElementById( "guided-button-dataset-contains-subjects" ); @@ -1296,6 +1315,8 @@ const savePageChanges = async (pageBeingLeftID) => { guidedSkipPage("guided-code-folder-tab"); guidedSkipPage("guided-add-code-metadata-tab"); } + + *********************** */ } if (pageBeingLeftID === "guided-subjects-addition-tab") { @@ -1482,17 +1503,10 @@ const savePageChanges = async (pageBeingLeftID) => { } if (pageBeingLeftID === "guided-code-folder-tab") { - const codeFolder = window.datasetStructureJSONObj["folders"]["code"]; - if (folderIsEmpty(codeFolder)) { - errorArray.push({ - type: "notyf", - message: "Please add code used to generate your dataset", - }); - throw errorArray; - } } if (pageBeingLeftID === "guided-protocol-folder-tab") { + guidedUnSkipPage("guided-code-folder-tab"); } if (pageBeingLeftID === "guided-docs-folder-tab") { diff --git a/src/renderer/src/sections/guided_mode/guided_curate_dataset.html b/src/renderer/src/sections/guided_mode/guided_curate_dataset.html index 4ab977711..05f979f99 100644 --- a/src/renderer/src/sections/guided_mode/guided_curate_dataset.html +++ b/src/renderer/src/sections/guided_mode/guided_curate_dataset.html @@ -719,9 +719,7 @@

Before getting started

>

Describe the content of your dataset

-

- Select all dataset entities relevant to your study based on the data you collected. -

+

Select the types of entities that are included in your dataset.

@@ -1111,13 +1109,13 @@

Import Your Data

- Import all of the experimental data collected during your study using the interface - below. + Use the interface below to import all the experimental data you collected during your + study.

Note: If your data is organized into subfolders (e.g., "microscopy," - "imaging"), import the subfolders directly instead of the parent folder containing - them. + "imaging"), make sure to import the subfolders directly rather than the parent folder + they’re in.

Subjects specification

data-entity-type-string-plural="samples" data-entity-type-prefix="sam-" > +
+
+
--> -
+
-

Code files

+

Code importation

- You mentioned at the beginning that you have code files in your dataset. Add those - code files into the interface below. Make sure you include a README file that - describes the code and how to use it. + Import any scripts, computational models, analysis pipelines, or other code/tools used + during the study for data processing or analysis below.

-
+
-
+
-

Protocol data

+

Protocol importation

- Protocol data refers to supplementary folders and files to accompany the experimental - protocols that must be submitted to protocols.io (more on that at a later step). + Import any files that describe the experimental procedures used in your study below.

Protocol data class="guided--section" >
-
+
-

Docs data

+

Docs importation

- Docs data refers to any data to be included in your dataset that does not belong to - any of the other five categories (primary, source, derivative, code, or protocol - data). + Import any files that provide documentation related to your study below.

Docs data

Dataset structure review

- The SDS compliant dataset structure shown below will be generated by SODA (during the - last step of this process) based on the data files you specified in the previous - steps. Review and go back to make changes if needed. Please note that all empty - folders will be deleted before generating the dataset since empty folders are not - allowed in the SDS. + The interface below displays the structure of your dataset as it will be generated by + SODA. Review the structure to ensure that all data has been imported correctly, and + then click to proceed.

diff --git a/src/renderer/src/stores/slices/datasetContentSelectorSlice.js b/src/renderer/src/stores/slices/datasetContentSelectorSlice.js index 9e28d0015..5039618bf 100644 --- a/src/renderer/src/stores/slices/datasetContentSelectorSlice.js +++ b/src/renderer/src/stores/slices/datasetContentSelectorSlice.js @@ -1,26 +1,31 @@ import useGlobalStore from "../globalStore"; -import { produce } from "immer"; export const datasetContentSelectorSlice = (set) => ({ - selectedDatasetContent: [], + selectedEntities: [], }); -export const toggleComponent = (value) => { - useGlobalStore.setState( - produce((state) => { - if (state.selectedComponents.includes(value)) { - state.selectedComponents = state.selectedComponents.filter((item) => item !== value); - } else { - state.selectedComponents.push(value); - } - }) - ); +export const getSelectedEntities = (state) => state.selectedEntities; + +export const setSelectedEntities = (selectedEntities) => { + useGlobalStore.setState({ + selectedEntities, + }); }; -export const resetSelection = () => { - useGlobalStore.setState( - produce((state) => { - state.selectedComponents = []; - }) - ); +export const toggleEntitySelection = (entity) => { + useGlobalStore.setState((state) => { + const updatedSelectedEntities = [...state.selectedEntities]; + const entityIndex = updatedSelectedEntities.indexOf(entity); + + if (entityIndex === -1) { + updatedSelectedEntities.push(entity); + } else { + updatedSelectedEntities.splice(entityIndex, 1); + } + + return { + ...state, + selectedEntities: updatedSelectedEntities, + }; + }); }; diff --git a/src/renderer/src/stores/slices/datasetEntitySelectorSlice.js b/src/renderer/src/stores/slices/datasetEntitySelectorSlice.js index 7bfd01075..3f54cf633 100644 --- a/src/renderer/src/stores/slices/datasetEntitySelectorSlice.js +++ b/src/renderer/src/stores/slices/datasetEntitySelectorSlice.js @@ -247,9 +247,6 @@ const removeFromOtherEntities = (entityEntries, targetEntityName, entityRelative // Get the entity associated with a specific file path export const getEntityForRelativePath = (datasetEntityObj, entityType, relativePath) => { - console.log("datasetEntityObj: ", datasetEntityObj); - console.log("entityType: ", entityType); - console.log("relativePath: ", relativePath); const entities = datasetEntityObj?.[entityType]; if (!entities) return null;