Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import AddCircleOutlineIcon from '@mui/icons-material/AddCircleOutline';
import { ButtonGroup } from '@mui/material';
import LoadingButton from 'components/Buttons/LoadingButton';
import ConfirmationDialog from 'components/Dialogs/ConfirmationDialog';
import NavToolbarStyles from 'components/Navbar/NavToolbar.styles';
import { hasUnsavedChanges, unsetUnloadHandler } from 'helpers/BeforeUnload.helpers';
import { useCreateProject } from 'hooks';
import { generateNewProjectData } from 'pages/Project/store/ProjectStore.helpers';
import { useRef } from 'react';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';

const CreateProjectButton: React.FC = () => {
const { mutate, isLoading: createProjectIsLoading } = useCreateProject();
const navigate = useNavigate();
const buttonGroupRef = useRef<HTMLDivElement>(null);
const [confirmationDialogIsOpen, setConfirmationDialogIsOpen] = useState(false);

const handleCreateProject = () => {
mutate(generateNewProjectData('Untitled', ''), {
Expand All @@ -20,24 +21,48 @@ const CreateProjectButton: React.FC = () => {
});
};

const handleButtonClick = () => {
const unsavedChangesExist = hasUnsavedChanges();
if (unsavedChangesExist) {
setConfirmationDialogIsOpen(true);
return;
} else {
handleCreateProject();
}
};

const handleConfirmationDialogClose = (ok: boolean | undefined) => {
if (ok) {
unsetUnloadHandler('project');
unsetUnloadHandler('study');
unsetUnloadHandler('annotation');
handleCreateProject();
}
setConfirmationDialogIsOpen(false);
};

return (
<ButtonGroup
ref={buttonGroupRef}
sx={{ marginRight: '8px' }}
variant="contained"
disableElevation
color="secondary"
>
<>
<ConfirmationDialog
dialogTitle="You have unsaved changes"
dialogMessage="Are you sure you want to continue? You'll lose your unsaved changes"
confirmText="Continue"
rejectText="Cancel"
onCloseDialog={handleConfirmationDialogClose}
isOpen={confirmationDialogIsOpen}
/>
<LoadingButton
variant="contained"
color="secondary"
disableElevation
loaderColor="primary"
onClick={handleCreateProject}
onClick={handleButtonClick}
isLoading={createProjectIsLoading}
sx={[NavToolbarStyles.menuItem, NavToolbarStyles.createProjectButton]}
startIcon={<AddCircleOutlineIcon />}
text="NEW PROJECT"
/>
</ButtonGroup>
</>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const setAnalysesInAnnotationAsIncluded = async (annotationId: string) =>
annotationId
)) as AxiosResponse<NeurostoreAnnotation>;

let notes = (annotation.data.notes || []) as NoteCollectionReturn[];
const notes = (annotation.data.notes || []) as NoteCollectionReturn[];

await API.NeurostoreServices.AnnotationsService.annotationsIdPut(annotationId, {
notes: notes.map((x) => ({
Expand All @@ -18,7 +18,10 @@ export const setAnalysesInAnnotationAsIncluded = async (annotationId: string) =>
...x.note,
// included can be null meaning it has not been instantiated. We only want to set it to true
// if it has not been instantiated as that will overwrite the value is the user previously set it to false
included: (x.note as any)?.included === false ? false : true,
// null ?? true === true
// undefined ?? true === true
// false ?? true === false
included: (x.note as any)?.included ?? true,
},
})),
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AxiosResponse } from 'axios';
import { AnnotationReturnOneOf1 } from 'neurostore-typescript-sdk';
import { AnnotationReturnOneOf } from 'neurostore-typescript-sdk';
import { useQuery } from 'react-query';
import API from 'api/api.config';

Expand All @@ -8,7 +8,7 @@ const useGetAnnotationById = (annotationId: string | undefined | null) => {
['annotations', annotationId],
() => API.NeurostoreServices.AnnotationsService.annotationsIdGet(annotationId || ''),
{
select: (res: AxiosResponse<AnnotationReturnOneOf1>) => res.data,
select: (res: AxiosResponse<AnnotationReturnOneOf>) => res.data,
enabled: !!annotationId,
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,27 @@ import {
useStudyAnalysisName,
} from 'pages/Study/store/StudyStore';
import { IStoreAnalysis } from 'pages/Study/store/StudyStore.helpers';
import { useEffect } from 'react';
import { useUpdateAnnotationNoteName } from 'stores/AnnotationStore.actions';
import EditStudyAnalysisDeleteButton from './EditStudyAnalysisDeleteButton';
import { useUpdateAnnotationNoteDetails } from 'stores/AnnotationStore.actions';

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

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

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

const handleUpdateAnalysisDetails = (field: keyof IStoreAnalysis, analysisId: string, value: string) => {
if (!analysisId) return;
addOrUpdateAnalysis({
id: analysisId,
[field]: value,
});
if (field === 'name') {
updateAnnotationNoteName({
analysis: analysisId,
analysis_name: value,
});
}
};

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ const useSaveStudy = () => {

const handleUpdateDB = async () => {
try {
if (studyHasBeenEdited && annotationIsEdited) {
const newAnalysesWereCreated = storeStudy.analyses.some((analysis) => analysis.isNew);

if (studyHasBeenEdited && (annotationIsEdited || newAnalysesWereCreated)) {
await handleUpdateBothInDB();
} else if (studyHasBeenEdited) {
await handleUpdateStudyInDB();
Expand Down
14 changes: 5 additions & 9 deletions compose/neurosynth-frontend/src/pages/Study/store/StudyStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -651,21 +651,17 @@ export const useStudyAnalysisPointStatistic = (analysisId?: string) =>
export const useNumStudyAnalyses = () => useStudyStore((state) => state.study.analyses.length);
export const useStudyAnalyses = () => useStudyStore((state) => state.study.analyses);
export const useDebouncedStudyAnalyses = () => {
const studyAnalyses = useStudyAnalyses();
const [debouncedAnalyses, setDebouncedAnalyses] = useState(useStudyStore.getState().study.analyses);
useEffect(() => {
let debounce: NodeJS.Timeout;
const unsub = useStudyStore.subscribe((state) => {
if (debounce) clearTimeout(debounce);
debounce = setTimeout(() => {
setDebouncedAnalyses(state.study.analyses);
}, 400);
});
const debounce: NodeJS.Timeout = setTimeout(() => {
setDebouncedAnalyses(studyAnalyses);
}, 500);

return () => {
unsub();
clearTimeout(debounce);
};
}, []);
}, [studyAnalyses]);
return debouncedAnalyses;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ export const useUpdateAnnotationInDB = () => useAnnotationStore((state) => state
export const useCreateAnnotationNote = () => useAnnotationStore((state) => state.createAnnotationNote);

export const useDeleteAnnotationNote = () => useAnnotationStore((state) => state.deleteAnnotationNote);
export const useUpdateAnnotationNoteName = () => useAnnotationStore((state) => state.updateAnnotationNoteName);

export const useUpdateAnnotationNoteDetails = () => useAnnotationStore((state) => state.updateAnnotationNoteDetails);
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ export const useGetAnnotationIsLoading = () =>
useAnnotationStore((state) => state.storeMetadata.getAnnotationIsLoading);
export const useUpdateAnnotationIsLoading = () =>
useAnnotationStore((state) => state.storeMetadata.updateAnnotationIsLoading);
export const useAnnotationIsEdited = () =>
useAnnotationStore((state) => state.storeMetadata.annotationIsEdited);

export const useAnnotationIsError = () =>
useAnnotationStore((state) => state.storeMetadata.isError);
export const useAnnotationIsEdited = () => useAnnotationStore((state) => state.storeMetadata.annotationIsEdited);
export const useAnnotationIsError = () => useAnnotationStore((state) => state.storeMetadata.isError);
export const useAnnotationId = () => useAnnotationStore((state) => state.annotation.id);
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,15 @@ export const noteKeyArrToDefaultNoteKeyObj = (noteKeys: NoteKeyType[]): Annotati
return x;
};

export const storeNotesToDBNotes = (
notes: IStoreNoteCollectionReturn[]
): NoteCollectionRequest[] => {
export const storeNotesToDBNotes = (notes: IStoreNoteCollectionReturn[]): NoteCollectionRequest[] => {
return notes.map((annotationNote) => ({
analysis: annotationNote.analysis,
study: annotationNote.study,
note: annotationNote.note,
}));
};

export const updateNoteNameHelper = (
export const updateNoteDetailsHelper = (
notes: IStoreNoteCollectionReturn[],
update: Partial<IStoreNoteCollectionReturn>
): IStoreNoteCollectionReturn[] => {
Expand Down
24 changes: 12 additions & 12 deletions compose/neurosynth-frontend/src/stores/AnnotationStore.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { AnnotationReturnOneOf1, NoteCollectionReturn } from 'neurostore-typescript-sdk';
import API from 'api/api.config';
import { NoteKeyType } from 'components/HotTables/HotTables.types';
import { noteKeyArrToObj } from 'components/HotTables/HotTables.utils';
import { setUnloadHandler } from 'helpers/BeforeUnload.helpers';
import { AnnotationReturnOneOf, NoteCollectionReturn } from 'neurostore-typescript-sdk';
import {
noteKeyArrToDefaultNoteKeyObj,
noteKeyObjToArr,
storeNotesToDBNotes,
updateNoteNameHelper,
updateNoteDetailsHelper,
} from 'stores/AnnotationStore.helpers';
import { NoteKeyType } from 'components/HotTables/HotTables.types';
import API from 'api/api.config';
import { create } from 'zustand';
import {
AnnotationStoreActions,
AnnotationStoreMetadata,
IStoreAnnotation,
IStoreNoteCollectionReturn,
} from 'stores/AnnotationStore.types';
import { setUnloadHandler } from 'helpers/BeforeUnload.helpers';
import { noteKeyArrToObj } from 'components/HotTables/HotTables.utils';
import { create } from 'zustand';

const normalizeNoteKeyOrder = (noteKeys: NoteKeyType[]) =>
noteKeys.map((noteKey, index) => ({ ...noteKey, order: index }));
Expand Down Expand Up @@ -64,7 +64,7 @@ export const useAnnotationStore = create<

try {
const annotationRes = (await API.NeurostoreServices.AnnotationsService.annotationsIdGet(annotationId))
.data as AnnotationReturnOneOf1;
.data as AnnotationReturnOneOf;

const noteKeysArr = noteKeyObjToArr(annotationRes.note_keys);
const notes: IStoreNoteCollectionReturn[] = (annotationRes.notes as Array<NoteCollectionReturn>)?.map(
Expand Down Expand Up @@ -157,7 +157,7 @@ export const useAnnotationStore = create<
...state,
annotation: {
...state.annotation,
note_keys: normalizeNoteKeyOrder([{ ...noteKey }, ...(state.annotation.note_keys ?? [])]),
note_keys: normalizeNoteKeyOrder([{ ...noteKey, order: 0 }, ...(state.annotation.note_keys ?? [])]),
notes: (state.annotation.notes ?? []).map((note) => ({
...note,
note: {
Expand Down Expand Up @@ -200,12 +200,12 @@ export const useAnnotationStore = create<
};
});
},
updateAnnotationNoteName: (note) => {
updateAnnotationNoteDetails: (note) => {
set((state) => ({
...state,
annotation: {
...state.annotation,
notes: updateNoteNameHelper(state.annotation.notes || [], note),
notes: updateNoteDetailsHelper(state.annotation.notes || [], note),
},
}));
},
Expand Down Expand Up @@ -270,7 +270,7 @@ export const useAnnotationStore = create<
note_keys: noteKeyArrToObj(state.annotation.note_keys ?? []),
notes: storeNotesToDBNotes(state.annotation.notes),
})
).data as AnnotationReturnOneOf1;
).data as AnnotationReturnOneOf;

const noteKeysArr = noteKeyObjToArr(annotationRes.note_keys);
const notes: IStoreNoteCollectionReturn[] = (annotationRes.notes as Array<NoteCollectionReturn>)?.map(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NoteKeyType } from 'components/HotTables/HotTables.types';
import { AnnotationReturnOneOf1, NoteCollectionReturn } from 'neurostore-typescript-sdk';
import { AnnotationReturnOneOf, NoteCollectionReturn } from 'neurostore-typescript-sdk';

export type AnnotationStoreMetadata = {
annotationIsEdited: boolean;
Expand All @@ -12,7 +12,7 @@ export interface IStoreNoteCollectionReturn extends NoteCollectionReturn {
isNew?: boolean;
}

export interface IStoreAnnotation extends Omit<AnnotationReturnOneOf1, 'notes' | 'note_keys'> {
export interface IStoreAnnotation extends Omit<AnnotationReturnOneOf, 'notes' | 'note_keys'> {
notes: IStoreNoteCollectionReturn[] | undefined;
note_keys: NoteKeyType[] | undefined;
}
Expand All @@ -22,12 +22,12 @@ export type AnnotationStoreActions = {
setAnnotationIsEdited: (isEdited: boolean) => void;
clearAnnotationStore: () => void;
updateNotes: (updatedNotes: Array<NoteCollectionReturn>) => void;
createAnnotationColumn: (noteKey: NoteKeyType) => void;
createAnnotationColumn: (noteKey: Omit<NoteKeyType, 'order'>) => void;
updateAnnotationInDB: () => Promise<void>;
createAnnotationNote: (analysisId: string, studyId: string, analysisName: string) => void;
deleteAnnotationNote: (analysisId: string) => void;
updateAnnotationNoteName: (analysis: Partial<IStoreNoteCollectionReturn>) => void;
removeAnnotationColumn: (noteKey: string) => void;
updateAnnotationNoteDetails: (note: Partial<IStoreNoteCollectionReturn>) => void;
};

export type AnnotationNoteType = {
Expand Down
Loading