Skip to content

Commit

Permalink
feat: fixed bug with cloning and annotations, added swap button
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoalee committed Oct 28, 2023
1 parent 5b3210c commit aac0355
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ const EditAnalysis: React.FC<{ analysisId?: string; onDeleteAnalysis: () => void
confirmText="delete analysis"
rejectText="cancel"
/>
<Button onClick={() => setDialogIsOpen(true)} variant="outlined" color="error">
<Button
variant="contained"
onClick={() => setDialogIsOpen(true)}
disableElevation
color="error"
>
Delete Analysis
</Button>
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,29 @@ import {
useStudyAnalysisName,
} from 'pages/Studies/StudyStore';
import { IStoreAnalysis } from 'pages/Studies/StudyStore.helpers';
import { useEffect } from 'react';
import { useUpdateAnnotationNoteName } from 'stores/AnnotationStore.actions';

const EditAnalysisDetails: React.FC<{ analysisId?: string }> = (props) => {
const addOrUpdateAnalysis = useAddOrUpdateAnalysis();
const name = useStudyAnalysisName(props.analysisId);
const description = useStudyAnalysisDescription(props.analysisId);
const updateAnnotationNoteName = useUpdateAnnotationNoteName();

useEffect(() => {
if (!props.analysisId) return;
let debounce: NodeJS.Timeout;
debounce = setTimeout(() => {
updateAnnotationNoteName({
analysis: props.analysisId,
analysis_name: name,
});
}, 500);

return () => {
clearTimeout(debounce);
};
}, [name, props.analysisId, updateAnnotationNoteName]);

const handleUpdateAnalysisDetails = (
field: keyof IStoreAnalysis,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
import { Box } from '@mui/material';
import { Box, Typography } from '@mui/material';
import FloatingStatusButtons from 'components/EditStudyComponents/FloatingStatusButtons/FloatingStatusButtons';
import NeurosynthBreadcrumbs from 'components/NeurosynthBreadcrumbs/NeurosynthBreadcrumbs';
import { useProjectId, useProjectName } from 'pages/Projects/ProjectPage/ProjectStore';
import { useStudyId, useStudyName } from 'pages/Studies/StudyStore';
import {
useStudyId,
useStudyLastUpdated,
useStudyName,
useStudyUsername,
} from 'pages/Studies/StudyStore';
import { useMemo } from 'react';

const EditStudyPageHeader: React.FC = (props) => {
const studyId = useStudyId();
const projectId = useProjectId();
const studyName = useStudyName();
const projectName = useProjectName();
const studyOwnerUsername = useStudyUsername();
const lastUpdatedAt = useStudyLastUpdated();

const nicelyFormattedDate = useMemo(() => {
const date = new Date(lastUpdatedAt || '');
return date.toDateString() + ' ' + date.toLocaleTimeString();
}, [lastUpdatedAt]);

return (
<>
Expand Down Expand Up @@ -38,6 +51,16 @@ const EditStudyPageHeader: React.FC = (props) => {
},
]}
/>
<Box sx={{ margin: '5px 0 10px 0', display: 'flex' }}>
<Box>
<Typography variant="body2" sx={{ color: 'muted.main' }}>
Study owner: {studyOwnerUsername ? studyOwnerUsername : 'neurosynth'}
</Typography>
<Typography variant="caption" sx={{ color: 'muted.main' }}>
Last updated: {nicelyFormattedDate}
</Typography>
</Box>
</Box>
</Box>
</>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Box, Typography } from '@mui/material';
import { Box, Button } from '@mui/material';
import LoadingButton from 'components/Buttons/LoadingButton/LoadingButton';
import {
AnalysisReturn,
Expand All @@ -15,6 +15,7 @@ import {
} from 'pages/Projects/ProjectPage/ProjectStore';

import { useAuth0 } from '@auth0/auth0-react';
import SwapHorizIcon from '@mui/icons-material/SwapHoriz';
import {
useCreateStudy,
useGetStudysetById,
Expand All @@ -27,14 +28,13 @@ import {
useStudy,
useStudyAnalyses,
useStudyHasBeenEdited,
useStudyLastUpdated,
useStudyUser,
useStudyUsername,
useUpdateStudyInDB,
useUpdateStudyIsLoading,
} from 'pages/Studies/StudyStore';
import { storeAnalysesToStudyAnalyses } from 'pages/Studies/StudyStore.helpers';
import React, { useMemo, useState } from 'react';
import React, { useState } from 'react';
import { useQueryClient } from 'react-query';
import { useHistory } from 'react-router-dom';
import { useUpdateAnnotationInDB, useUpdateAnnotationNotes } from 'stores/AnnotationStore.actions';
Expand All @@ -47,6 +47,7 @@ import { storeNotesToDBNotes } from 'stores/AnnotationStore.helpers';
import API from 'utils/api';
import { arrayToMetadata } from '../EditStudyMetadata/EditStudyMetadata';
import { hasDuplicateStudyAnalysisNames, hasEmptyStudyPoints } from './EditStudySaveButton.helpers';
import EditStudySwapVersionButton from '../EditStudySwapVersionButton/EditStudySwapVersionButton';

const EditStudySaveButton: React.FC = React.memo((props) => {
const { user } = useAuth0();
Expand All @@ -61,13 +62,11 @@ const EditStudySaveButton: React.FC = React.memo((props) => {
const projectId = useProjectId();
// study stuff
const storeStudy = useStudy();
const studyOwnerUser = useStudyUser();
const updateStudyIsLoading = useUpdateStudyIsLoading();
const studyHasBeenEdited = useStudyHasBeenEdited();
const analyses = useStudyAnalyses();
const studyOwnerUsername = useStudyUsername();
const updateStudyInDB = useUpdateStudyInDB();
const studyOwnerUser = useStudyUser();
const lastUpdatedAt = useStudyLastUpdated();
// annotation stuff
const updateAnnotationIsLoading = useUpdateAnnotationIsLoading();
const annotationHasBeenEdited = useAnnotationIsEdited();
Expand Down Expand Up @@ -126,13 +125,10 @@ const EditStudySaveButton: React.FC = React.memo((props) => {
const handleUpdateDB = () => {
try {
if (studyHasBeenEdited && annotationIsEdited) {
console.log('UPDATING STUDY AND ANNOTATION');
handleUpdateBothInDB();
} else if (studyHasBeenEdited) {
console.log('UPDATING STUDY');
handleUpdateStudyInDB();
} else if (annotationIsEdited) {
console.log('UPDATING ANNOTATION');
handleUpdateAnnotationInDB();
}
} catch (e) {
Expand Down Expand Up @@ -197,7 +193,7 @@ const EditStudySaveButton: React.FC = React.memo((props) => {
if (!clonedStudyId) throw new Error('study not cloned correctly');

// 2. update the clone with our latest updates
await updateStudy({
const x = await updateStudy({
studyId: clonedStudyId,
study: {
...getNewScrubbedStudyFromStore(),
Expand All @@ -208,7 +204,6 @@ const EditStudySaveButton: React.FC = React.memo((props) => {
const updatedClone = (
await API.NeurostoreServices.StudiesService.studiesIdGet(clonedStudyId, true)
).data;
console.log(updatedClone);

// 3. update the studyset containing the study with our new clone
const updatedStudies = [...(studyset.studies as string[])];
Expand All @@ -223,8 +218,9 @@ const EditStudySaveButton: React.FC = React.memo((props) => {
// 4. update the project as this keeps track of completion status of studies
replaceStudyWithNewClonedStudy(storeStudy.id, clonedStudyId);

// 5. as this is a completely new study, the annotations are cleared. We need to update the annotations with our latest changes, and associate
// newly created analyses with their corresponding analysis changes
// 5. as this is a completely new study, that we've just created, the annotations are cleared.
// We need to update the annotations with our latest changes, and associate newly created analyses with their corresponding analysis changes.
// - we do this based on the analysis names since the IDs are assigned by neurostore
const updatedNotes = [...(notes || [])];
((updatedClone.analyses || []) as AnalysisReturn[]).forEach((analysis) => {
const foundNoteIndex = updatedNotes.findIndex(
Expand Down Expand Up @@ -289,29 +285,21 @@ const EditStudySaveButton: React.FC = React.memo((props) => {
}
};

const nicelyFormattedDate = useMemo(() => {
const date = new Date(lastUpdatedAt || '');
return date.toDateString() + ' ' + date.toLocaleTimeString();
}, [lastUpdatedAt]);
const handleSwapStudy = () => {
console.log('handle swap study');
};

return (
<Box sx={EditStudyPageStyles.loadingButtonContainer}>
<Box>
<Typography variant="body2" sx={{ color: 'muted.main' }}>
Owner: {studyOwnerUsername ? studyOwnerUsername : 'neurosynth'}
</Typography>
<Typography variant="caption" sx={{ color: 'muted.main' }}>
Last Updated: {nicelyFormattedDate}
</Typography>
</Box>
<EditStudySwapVersionButton />
<LoadingButton
text="save"
isLoading={updateStudyIsLoading || updateAnnotationIsLoading || isCloning}
variant="contained"
loaderColor="secondary"
disabled={!studyHasBeenEdited && !annotationHasBeenEdited}
disableElevation
sx={{ width: '300px', height: '36px' }}
sx={{ width: '280px', height: '36px' }}
onClick={handleSave}
/>
</Box>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Button, ButtonGroup, ListItem, ListItemButton, Menu } from '@mui/material';
import SwapHorizIcon from '@mui/icons-material/SwapHoriz';
import { useState } from 'react';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';

const EditStudySwapVersionButton: React.FC = (props) => {
const [anchorEl, setAnchorEl] = useState<null | HTMLButtonElement>(null);
const open = Boolean(anchorEl);

const handleButtonPress = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};

const handleCloseNavMenu = () => {
setAnchorEl(null);
};

const handleSwapStudy = () => {};

return (
<>
<Button
sx={{ width: '280px', height: '36px' }}
variant="contained"
disableElevation
color="secondary"
onClick={handleButtonPress}
startIcon={<SwapHorizIcon />}
>
Switch study version
</Button>
<Menu
open={open}
onClose={handleCloseNavMenu}
anchorEl={anchorEl}
anchorOrigin={{ vertical: 'top', horizontal: 'left' }}
transformOrigin={{ vertical: 'bottom', horizontal: 'left' }}
>
<ListItem sx={{ padding: '0 1rem' }}>
<ButtonGroup variant="text">
<Button>
Version 238iERGtug | Owner: Neurosynth | Last Updated: Oct 28 2023
</Button>
<Button endIcon={<OpenInNewIcon />}>View </Button>
</ButtonGroup>
</ListItem>
<ListItem sx={{ padding: '0 1rem' }}>
<ButtonGroup variant="text">
<Button>
Version 238iERGtug | Owner: Neurosynth | Last Updated: Oct 28 2023
</Button>
<Button endIcon={<OpenInNewIcon />}>View </Button>
</ButtonGroup>
</ListItem>
<ListItem sx={{ padding: '0 1rem' }}>
<ButtonGroup variant="text">
<Button>
Version 238iERGtug | Owner: Neurosynth | Last Updated: Oct 28 2023
</Button>
<Button endIcon={<OpenInNewIcon />}>View </Button>
</ButtonGroup>
</ListItem>
</Menu>
</>
);
};

export default EditStudySwapVersionButton;
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ export const useCreateAnnotationNote = () =>

export const useDeleteAnnotationNote = () =>
useAnnotationStore((state) => state.deleteAnnotationNote);
export const useUpdateAnnotationNoteName = () =>
useAnnotationStore((state) => state.updateAnnotationNoteName);
15 changes: 15 additions & 0 deletions compose/neurosynth-frontend/src/stores/AnnotationStore.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,18 @@ export const storeNotesToDBNotes = (
note: annotationNote.note,
}));
};

export const updateNoteNameHelper = (
notes: IStoreNoteCollectionReturn[],
update: Partial<IStoreNoteCollectionReturn>
): IStoreNoteCollectionReturn[] => {
const updatedNotes = [...notes];
const foundNoteIndex = updatedNotes.findIndex((note) => note.analysis === update.analysis);
if (foundNoteIndex < 0) return updatedNotes;

updatedNotes[foundNoteIndex] = {
...updatedNotes[foundNoteIndex],
analysis_name: update.analysis_name,
};
return updatedNotes;
};
10 changes: 10 additions & 0 deletions compose/neurosynth-frontend/src/stores/AnnotationStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
noteKeyArrToDefaultNoteKeyObj,
noteKeyObjToArr,
storeNotesToDBNotes,
updateNoteNameHelper,
} from 'stores/AnnotationStore.helpers';
import API from 'utils/api';
import { create } from 'zustand';
Expand Down Expand Up @@ -148,6 +149,15 @@ export const useAnnotationStore = create<
},
}));
},
updateAnnotationNoteName: (note) => {
set((state) => ({
...state,
annotation: {
...state.annotation,
notes: updateNoteNameHelper(state.annotation.notes || [], note),
},
}));
},
createAnnotationNote: (analysisId, studyId, analysisName) => {
set((state) => {
if (!state.annotation.notes || !state.annotation.note_keys) return state;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type AnnotationStoreActions = {
updateAnnotationInDB: () => Promise<void>;
createAnnotationNote: (analysisId: string, studyId: string, analysisName: string) => void;
deleteAnnotationNote: (analysisId: string) => void;
updateAnnotationNoteName: (analysis: Partial<IStoreNoteCollectionReturn>) => void;
};

export type AnnotationNoteType = {
Expand Down

0 comments on commit aac0355

Please sign in to comment.