Skip to content

Commit

Permalink
Merge branch 'develop' into fix/ANT-2764_keyword_input_component
Browse files Browse the repository at this point in the history
  • Loading branch information
marlenetienne authored Mar 7, 2025
2 parents d700250 + 47c6989 commit 69a3290
Show file tree
Hide file tree
Showing 22 changed files with 327 additions and 149 deletions.
8 changes: 4 additions & 4 deletions src/components/common/modal/ImportTrajectoryModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { addTrajectory } from '@/shared/services/trajectoryService.ts';

interface ImportTrajectoryModalProps {
options: SelectOption[] | undefined;
onClose: (value: DbTrajectory | SelectOption | null, status: RowStatus) => void;
onClose: (value: DbTrajectory | SelectOption | null, status: RowStatus) => Promise<void>;
trajectoryType: TRAJECTORY_TYPE;
studyHorizon: string;
}
Expand Down Expand Up @@ -47,17 +47,17 @@ export const ImportTrajectoryModal = ({
setProgress(+progressValue?.toFixed(0));
});
setFileStatus('success');
onClose(newTrajectory, 'success');
await onClose(newTrajectory, 'success');
} catch (error) {
// TODO handle errors considered as warning ones
setFileStatus('error');
onClose(value, 'error');
await onClose(value, 'error');
}
};

return (
<RdsModal size="small">
<RdsModal.Title onClose={() => onClose(null, 'empty')} icon="Upload">
<RdsModal.Title onClose={() => void onClose(null, 'empty')} icon="Upload">
{t('studyDetails.@import_from_file_system', {
trajectoryType: trajectoryType === TRAJECTORY_TYPE.AREA ? 'areas' : 'links',
})}
Expand Down
16 changes: 12 additions & 4 deletions src/components/common/modal/StudyCreationModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,15 @@ interface StudyCreationModalProps {
onClose: () => void;
study?: StudyDTO | null;
setReloadStudies: React.Dispatch<React.SetStateAction<boolean>>;
projectInfoName?: string;
}

const StudyCreationModal: React.FC<StudyCreationModalProps> = ({ onClose, study, setReloadStudies }) => {
const StudyCreationModal: React.FC<StudyCreationModalProps> = ({
onClose,
study,
setReloadStudies,
projectInfoName,
}) => {
const { t } = useTranslation();
const [studyName, setStudyName] = useState<string>('');
const [horizon, setHorizon] = useState<string>(
Expand Down Expand Up @@ -110,9 +116,11 @@ const StudyCreationModal: React.FC<StudyCreationModalProps> = ({ onClose, study,
minNbCharacters={3}
/>
</div>
<div className="flex w-32 flex-col items-start justify-start">
<ProjectInput value={projectName} onChange={setProjectName} required />
</div>
{study && (
<div className="flex w-32 flex-col items-start justify-start">
<ProjectInput value={projectName} onChange={setProjectName} />
</div>
)}
</div>
</RdsModal.Content>
<RdsModal.Footer>
Expand Down
2 changes: 1 addition & 1 deletion src/components/forms/ProgressBar.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getBgColor } from '@/shared/utils/formFormatter.ts';
import { getBgColor } from '@/shared/utils/trajectoryUtils';

export type FileInputStatus = 'loading' | 'success' | 'error' | 'empty';

Expand Down
4 changes: 3 additions & 1 deletion src/components/input/SelectAndSearchableInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import { useRef, useState } from 'react';
import { RdsButton, RdsIconId, RdsInputText } from 'rte-design-system-react';
import { useTranslation } from 'react-i18next';

interface ProjectManagerProps {
options: SelectOption[] | undefined;
Expand Down Expand Up @@ -34,6 +35,7 @@ const SelectAndSearchableInput = ({
const [placeHolder] = useState<string>(defaultPlaceHolder);
const [valueInput, setValueInput] = useState<string>('');
const dropdownList = useRef<HTMLDivElement | null>(null);
const { t } = useTranslation();

const handleInputChange = async (value: string) => {
try {
Expand Down Expand Up @@ -110,7 +112,7 @@ const SelectAndSearchableInput = ({
setIsDropdownOpen(false);
}
}}
placeHolder={placeHolder}
placeHolder={!options?.length ? t('components.selectAnsSearchInput.@emptyList') : placeHolder}
variant="outlined"
value={valueInput}
disabled={isInputDisabled}
Expand Down
179 changes: 117 additions & 62 deletions src/components/tab/AreaLinkTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,58 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

import { useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import StdSimpleTable from '@common/data/stdSimpleTable/StdSimpleTable.tsx';
import { ReadOnlyObject } from '@common/data/stdTable/types/readOnly.type';
import getAreaLinkTableHeaders from '@/pages/pegase/studies/studyDetails/AreaLinkTableHeaders.tsx';
import { useNewStudyModal } from '@/hooks/useNewStudyModal.ts';
import { useTranslation } from 'react-i18next';
import { fetchTrajectoriesFromDB, fetchTrajectoriesFromFS } from '@/shared/services/trajectoryService.ts';
import {
fetchTrajectoriesFromDB,
fetchTrajectoriesFromFS,
linkTrajectoryToStudy,
} from '@/shared/services/trajectoryService.ts';
import { TRAJECTORY_SELECTION_STATUS, TRAJECTORY_TYPE } from '@/shared/enum/trajectory.ts';
import { AreaAndLinkRowData, DbTrajectory, RowStatus, StudyActionType } from '@/shared/types';
import { AreaAndLinkRowData, DbTrajectory, RowStatus, StudyActionType, StudyDTO } from '@/shared/types';
import { useFetchTrajectoriesFromDB } from '@/hooks/useFetchTrajectoriesFromDB.ts';
import {
convertToFSSelectionOptionType,
convertToSelectionOptionType,
getStatus,
} from '@/shared/utils/formFormatter.ts';
import { getStatus, getTrajectoryDB } from '@/shared/utils/trajectoryUtils';
import { convertToFSSelectionOptionType, convertToSelectionOptionType } from '@/shared/utils/formFormatter';
import { ImportTrajectoryModal } from '@common/modal/ImportTrajectoryModal.tsx';
import { useStudyDispatch } from '@/store/contexts/StudyContext';
import { useStudy, useStudyDispatch } from '@/store/contexts/StudyContext';
import { STUDY_ACTION } from '@/shared/enum/study.ts';

interface AreaLinkTabProps {
studyHorizon: string;
study: StudyDTO;
}

const AreaLinkTab = ({ studyHorizon }: AreaLinkTabProps) => {
const AreaLinkTab = ({ study }: AreaLinkTabProps) => {
const studyState = useStudy();
const trajectoryNameArea: string | null = studyState[`${TRAJECTORY_TYPE.AREA}`]?.trajectoryName ?? null;
const trajectoryNameLink: string | null = studyState[`${TRAJECTORY_TYPE.LINK}`]?.trajectoryName ?? null;
const [data, setData] = useState<AreaAndLinkRowData[]>([
{ hypothesis: 'Areas', trajectory: null, status: TRAJECTORY_SELECTION_STATUS.MISSING },
{ hypothesis: 'Links', trajectory: null, status: TRAJECTORY_SELECTION_STATUS.MISSING },
{
hypothesis: 'Areas',
trajectory: trajectoryNameArea,
status: trajectoryNameArea ? TRAJECTORY_SELECTION_STATUS.OK : TRAJECTORY_SELECTION_STATUS.MISSING,
},
{
hypothesis: 'Links',
trajectory: trajectoryNameLink,
status: trajectoryNameLink ? TRAJECTORY_SELECTION_STATUS.OK : TRAJECTORY_SELECTION_STATUS.MISSING,
},
]);
const [readOnly, setReadOnly] = useState<ReadOnlyObject>({ '0': false, '1': !data[0].trajectory });
const [readOnly, setReadOnly] = useState<ReadOnlyObject>({
'0': false,
'1': !trajectoryNameArea,
});
const [optionsDB, setOptionsDB] = useState<SelectOption[][]>();
const [optionsFS, setOptionsFS] = useState<SelectOption[]>();
const [rowIndexSelected, setRowIndexSelected] = useState<number>(0);
const { isModalOpen, toggleModal } = useNewStudyModal();
const dispatch = useStudyDispatch();
const { t } = useTranslation();
const { trajectories: trajectoriesArea } = useFetchTrajectoriesFromDB(TRAJECTORY_TYPE.AREA, studyHorizon);
const { trajectories: trajectoriesLink } = useFetchTrajectoriesFromDB(TRAJECTORY_TYPE.LINK, studyHorizon);
const { trajectories: trajectoriesArea } = useFetchTrajectoriesFromDB(TRAJECTORY_TYPE.AREA, study.horizon);
const { trajectories: trajectoriesLink } = useFetchTrajectoriesFromDB(TRAJECTORY_TYPE.LINK, study.horizon);

useEffect(() => {
if (trajectoriesArea && trajectoriesLink) {
Expand All @@ -58,49 +73,68 @@ const AreaLinkTab = ({ studyHorizon }: AreaLinkTabProps) => {
}
};

const handleTrajectoryUpdate = (index: number, trajectory: SelectOption | DbTrajectory | null, status: RowStatus) => {
const updatedData = [...data];
updatedData[index].trajectory = trajectory
? ((trajectory as SelectOption)?.label ?? (trajectory as DbTrajectory)?.trajectoryName)
: null;
updatedData[index].status = getStatus(status);
if (status === 'success') {
dispatch?.({
type: index === 0 ? STUDY_ACTION.ADD_TRAJECTORY_AREA : STUDY_ACTION.ADD_TRAJECTORY_LINK,
payload: trajectory,
} as StudyActionType);
const handleTrajectoryUpdate = useCallback(
async (index: number, trajectory: SelectOption | DbTrajectory | null, status: RowStatus) => {
const updatedData = [...data];
const payload: DbTrajectory | null | undefined =
trajectory && 'label' in trajectory
? getTrajectoryDB(index === 0 ? trajectoriesArea : trajectoriesLink, trajectory.id as number)
: trajectory;

setOptionsDB((prev) => {
if (prev && prev[index]?.length >= 0) {
prev[index] = [
...prev[index],
{
id: (trajectory as DbTrajectory).id,
label: (trajectory as DbTrajectory).trajectoryName,
},
];
return prev;
try {
if (status === 'success' && payload) {
await linkTrajectoryToStudy(payload.type, payload.id, study.id);
// Update context
dispatch?.({
type: index === 0 ? STUDY_ACTION.ADD_TRAJECTORY_AREA : STUDY_ACTION.ADD_TRAJECTORY_LINK,
payload,
} as StudyActionType);
// Update data state
updatedData[index].trajectory = trajectory
? ((trajectory as SelectOption)?.label ?? (trajectory as DbTrajectory)?.trajectoryName)
: null;
updatedData[index].status = getStatus(status);
setReadOnly({ '0': false, '1': false });
}
});
}

// Handle deletion case for areas
if (index === 0 && status === 'empty') {
updatedData[1].trajectory = null;
updatedData[1].status = TRAJECTORY_SELECTION_STATUS.MISSING;
dispatch?.({
type: STUDY_ACTION.CLEAR_AREA_LINK_TRAJECTORY,
} as StudyActionType);
setReadOnly({ '0': false, '1': true });
} else if (index === 1) {
dispatch?.({
type: STUDY_ACTION.CLEAR_LINK_TRAJECTORY,
} as StudyActionType);
} else {
setReadOnly({ '0': false, '1': false });
}
setData(updatedData);
};
// Handle deletion case for areas
if (status === 'empty') {
// TODO: ANT-2892 (delete link between study and trajectory in data base)
if (index === 0) {
updatedData.forEach((rowData) => {
rowData.trajectory = null;
rowData.status = TRAJECTORY_SELECTION_STATUS.MISSING;
});
dispatch?.({
type: STUDY_ACTION.CLEAR_AREA_LINK_TRAJECTORY,
} as StudyActionType);
setReadOnly({ '0': false, '1': true });
} else if (index === 1) {
updatedData[1].trajectory = null;
updatedData[1].status = TRAJECTORY_SELECTION_STATUS.MISSING;
dispatch?.({
type: STUDY_ACTION.CLEAR_LINK_TRAJECTORY,
} as StudyActionType);
setReadOnly({ '0': false, '1': false });
}
}

if (status === 'error') {
updatedData[index].trajectory =
(trajectory as SelectOption).label || (trajectory as DbTrajectory).trajectoryName || null;
updatedData[index].status = TRAJECTORY_SELECTION_STATUS.ERROR;
}
} catch {
// Trajectory status is set to error one
updatedData[index].trajectory =
(trajectory as SelectOption).label || (trajectory as DbTrajectory).trajectoryName || null;
updatedData[index].status = TRAJECTORY_SELECTION_STATUS.ERROR;
} finally {
setData(updatedData);
}
},
[trajectoriesArea, trajectoriesLink],
);

const handleTrajectorySearch = async (
index: number,
Expand All @@ -109,7 +143,7 @@ const AreaLinkTab = ({ studyHorizon }: AreaLinkTabProps) => {
try {
const results = await fetchTrajectoriesFromDB(
index === 0 ? TRAJECTORY_TYPE.AREA : TRAJECTORY_TYPE.LINK,
studyHorizon,
study.horizon,
value,
);
return convertToSelectionOptionType(results);
Expand All @@ -118,10 +152,31 @@ const AreaLinkTab = ({ studyHorizon }: AreaLinkTabProps) => {
}
};

const closeModal = (value: DbTrajectory | SelectOption | null, status: RowStatus) => {
value && handleTrajectoryUpdate(rowIndexSelected, value, status);
toggleModal();
};
const closeModal = useCallback(
async (value: DbTrajectory | SelectOption | null, status: RowStatus) => {
try {
await handleTrajectoryUpdate(rowIndexSelected, value, status);
// Update trajectory list options (synchronized with update of trajectory list in BDD after trajectory import) for dropdown
if (status !== 'error' && value) {
setOptionsDB((prev) => {
if (prev && prev[rowIndexSelected]?.length >= 0) {
prev[rowIndexSelected] = [
...prev[rowIndexSelected],
{
id: (value as DbTrajectory).id,
label: (value as DbTrajectory).trajectoryName,
},
];
return prev;
}
});
}
} finally {
toggleModal();
}
},
[rowIndexSelected],
);

const columns = useMemo(
() =>
Expand All @@ -145,7 +200,7 @@ const AreaLinkTab = ({ studyHorizon }: AreaLinkTabProps) => {
options={optionsFS}
onClose={closeModal}
trajectoryType={rowIndexSelected === 0 ? TRAJECTORY_TYPE.AREA : TRAJECTORY_TYPE.LINK}
studyHorizon={studyHorizon}
studyHorizon={study.horizon}
/>
)}
</div>
Expand Down
23 changes: 14 additions & 9 deletions src/pages/pegase/home/components/MainContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,20 @@ const MainContent = () => {
<div className="flex h-full w-full flex-col">
<PegaseStar />
<Suspense>
<StudyProvider initialValue={{ isStudyGenerated: false, areaTrajectory: null, linkTrajectory: null }}>
<Routes>
<Route path="/study/:studyName" element={<StudyDetails />} />
<Route path="/project/:projectName" element={<ProjectDetails />} />
{Object.entries([...menuBottomData, ...menuTopData]).map(([key, route]) => (
<Route key={key} path={route.path} Component={route.component} />
))}
</Routes>
</StudyProvider>
<Routes>
<Route
path="/study/:studyName"
element={
<StudyProvider initialValue={{ isStudyGenerated: false }}>
<StudyDetails />
</StudyProvider>
}
/>
<Route path="/project/:projectName" element={<ProjectDetails />} />
{Object.entries([...menuBottomData, ...menuTopData]).map(([key, route]) => (
<Route key={key} path={route.path} Component={route.component} />
))}
</Routes>
</Suspense>
</div>
</UserSettingsContext.Provider>
Expand Down
4 changes: 3 additions & 1 deletion src/pages/pegase/home/components/StudyTableDisplay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ import StudyCreationModal from '@common/modal/StudyCreationModal';
interface StudyTableDisplayProps {
searchStudy: string | undefined;
projectId?: string;
projectInfoName?: string;
}

const StudyTableDisplay = ({ searchStudy, projectId }: StudyTableDisplayProps) => {
const StudyTableDisplay = ({ searchStudy, projectId, projectInfoName }: StudyTableDisplayProps) => {
const { t } = useTranslation();
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
const [isHeaderHovered, setIsHeaderHovered] = useState<boolean>(false);
Expand Down Expand Up @@ -141,6 +142,7 @@ const StudyTableDisplay = ({ searchStudy, projectId }: StudyTableDisplayProps) =
onClose={handleModalClose}
study={selectedStudy}
setReloadStudies={setReloadStudies}
projectInfoName={projectInfoName}
/>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ const ProjectDetails = () => {
status={activeChip ? 'secondary' : 'primary'}
/>
</div>
<StudyTableDisplay searchStudy={searchTerm} projectId={projectInfo.id} />
<StudyTableDisplay searchStudy={searchTerm} projectId={projectInfo.id} projectInfoName={projectInfo.name} />
</div>
);
};
Expand Down
Loading

0 comments on commit 69a3290

Please sign in to comment.