diff --git a/src/App.css b/src/App.css index 0b76f94..6edf708 100644 --- a/src/App.css +++ b/src/App.css @@ -1,3 +1,5 @@ +@layer rte-design-system-react +@tailwind base; @tailwind components; @tailwind utilities; diff --git a/src/envVariables.ts b/src/envVariables.ts index e5c4805..883d455 100644 --- a/src/envVariables.ts +++ b/src/envVariables.ts @@ -10,7 +10,7 @@ type EnvVariableType = { // Environment Variable Template to Be Replaced at Runtime export const envVariables: EnvVariableType = { - VITE_BACK_END_BASE_URL: 'http://pegase-integration.rte-france.com:8080', + VITE_BACK_END_BASE_URL: '${URL_BACKEND}', }; export const getEnvVariables = (key: keyof EnvVariableType) => envVariables[key].startsWith('$') ? (import.meta.env[key] as string) : envVariables[key]; diff --git a/src/hooks/useFocusTrapping.ts b/src/hooks/useFocusTrapping.ts new file mode 100644 index 0000000..4f9df88 --- /dev/null +++ b/src/hooks/useFocusTrapping.ts @@ -0,0 +1,55 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +import { useEffect } from 'react'; + +const FOCUSABLE_ELEMENTS = [ + 'button', + 'a[href]', + 'input', + 'select', + 'textarea', + 'details', + '[tabindex]:not([tabindex="-1"])', +]; +const FOCUSABLE_ELEMENTS_QUERY = FOCUSABLE_ELEMENTS.map((elmt) => elmt + ':not([disabled]):not([aria-hidden])').join( + ',', +); + +const getFocusableElements = (containerElement: HTMLElement) => { + const elmts = containerElement.querySelectorAll(FOCUSABLE_ELEMENTS_QUERY) as unknown as HTMLElement[]; + return [elmts[0], elmts[elmts.length - 1]]; +}; + +const useFocusTrapping = (ref: React.RefObject, show: boolean) => { + useEffect(() => { + if (!show || !ref.current) { + return; + } + const containerElement = ref.current; + + const handleTabKeyPress = (event: KeyboardEvent) => { + const [firstElement, lastElement] = getFocusableElements(containerElement); + if (event.key === 'Tab') { + if (event.shiftKey && document.activeElement === firstElement) { + event.preventDefault(); + lastElement.focus(); + } else if (!event.shiftKey && document.activeElement === lastElement) { + event.preventDefault(); + firstElement.focus(); + } + } + }; + + containerElement.addEventListener('keydown', handleTabKeyPress); + + return () => { + containerElement.removeEventListener('keydown', handleTabKeyPress); + }; + }, [ref, show]); +}; + +export default useFocusTrapping; diff --git a/src/pages/pegase/home/components/StudyTableDisplay.tsx b/src/pages/pegase/home/components/StudyTableDisplay.tsx index a63a24e..8f9ee84 100644 --- a/src/pages/pegase/home/components/StudyTableDisplay.tsx +++ b/src/pages/pegase/home/components/StudyTableDisplay.tsx @@ -5,16 +5,17 @@ */ import { useState } from 'react'; +import StdSimpleTable from '@/components/common/data/stdSimpleTable/StdSimpleTable'; import { StudyDTO } from '@/shared/types/index'; +import getStudyTableHeaders from './StudyTableHeaders'; +import { addSortColumn, useNewStudyModal } from './StudyTableUtils'; import StudiesPagination from './StudiesPagination'; import { RowSelectionState } from '@tanstack/react-table'; -import { useStudyTableDisplay } from './useStudyTableDisplay'; -import { RdsButton } from 'rte-design-system-react'; - -import getStudyTableHeaders from './StudyTableHeaders'; -import { addSortColumn } from './StudyTableUtils'; import { StudyStatus } from '@/shared/types/common/StudyStatus.type'; -import StdSimpleTable from '@/components/common/data/stdSimpleTable/StdSimpleTable'; +import { useStudyTableDisplay } from './useStudyTableDisplay'; +import { useTranslation } from 'react-i18next'; +import StudyCreationModal from '../../studies/StudyCreationModal'; +import {RdsButton} from "rte-design-system-react"; interface StudyTableDisplayProps { searchStudy: string | undefined; @@ -26,18 +27,21 @@ const StudyTableDisplay = ({ searchStudy, projectId }: StudyTableDisplayProps) = const [rowSelection, setRowSelection] = useState({}); const [sortedColumn, setSortedColumn] = useState('status'); const [isHeaderHovered, setIsHeaderHovered] = useState(false); + const { isModalOpen, toggleModal } = useNewStudyModal(); + const { t } = useTranslation(); + const [selectedStudy, setSelectedStudy] = useState(null); const handleSort = (column: string) => { const newSortOrder = sortByState[column] === 'asc' ? 'desc' : 'asc'; setSortByState({ [column]: newSortOrder }); setSortedColumn(column); }; + const handleHeaderHover = (hovered: boolean) => { setIsHeaderHovered(hovered); }; const headers = getStudyTableHeaders(); - console.log('Original Headers:', headers); const sortedHeaders = addSortColumn( headers, @@ -48,8 +52,6 @@ const StudyTableDisplay = ({ searchStudy, projectId }: StudyTableDisplayProps) = isHeaderHovered, ); - console.log('Sorted Headers:', sortedHeaders); - const { rows, count, intervalSize, current, setPage } = useStudyTableDisplay({ searchStudy, projectId, @@ -63,6 +65,12 @@ const StudyTableDisplay = ({ searchStudy, projectId }: StudyTableDisplayProps) = const isDuplicateActive = selectedStatus === StudyStatus.GENERATED; const isDeleteActive = selectedStatus === StudyStatus.ERROR || selectedStatus === StudyStatus.IN_PROGRESS; + const handleDuplicate = () => { + const selectedStudy = rows[Number.parseInt(selectedRowId || '-1')]; + setSelectedStudy(selectedStudy); + toggleModal(); + }; + return (
@@ -88,12 +96,7 @@ const StudyTableDisplay = ({ searchStudy, projectId }: StudyTableDisplayProps) =
{selectedRowId !== undefined ? ( <> - console.log('duplicate')} - variant="outlined" - disabled={!isDuplicateActive} - /> + console.log('Delete')} @@ -103,8 +106,9 @@ const StudyTableDisplay = ({ searchStudy, projectId }: StudyTableDisplayProps) = /> ) : ( - console.log('NewStudy')} /> + )} + {isModalOpen && }
diff --git a/src/pages/pegase/home/components/StudyTableUtils.tsx b/src/pages/pegase/home/components/StudyTableUtils.tsx index f28e4af..1438ae2 100644 --- a/src/pages/pegase/home/components/StudyTableUtils.tsx +++ b/src/pages/pegase/home/components/StudyTableUtils.tsx @@ -6,6 +6,7 @@ import { StdIconId } from '@/shared/utils/common/mappings/iconMaps'; import StdIcon from '@common/base/stdIcon/StdIcon'; +import { useState } from 'react'; export function addSortColumn( headers: any[], @@ -60,3 +61,16 @@ export function addSortColumn( }; }); } + +export function useNewStudyModal() { + const [isModalOpen, setModalOpen] = useState(false); + + const toggleModal = () => { + setModalOpen((prev) => !prev); + }; + + return { + isModalOpen, + toggleModal, + }; +} diff --git a/src/pages/pegase/home/components/studyService.ts b/src/pages/pegase/home/components/studyService.ts new file mode 100644 index 0000000..2a937d1 --- /dev/null +++ b/src/pages/pegase/home/components/studyService.ts @@ -0,0 +1,51 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +import { notifyToast } from '@/shared/notification/notification'; + +interface StudyData { + name: string; + createdBy: string; + keywords: string[]; + project: string; + horizon: string; + trajectoryIds: number[]; +} + +export const saveStudy = async (studyData: StudyData, toggleModal: () => void) => { + try { + const response = await fetch('http://localhost:8093/v1/study', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(studyData), + }); + if (!response.ok) { + const errorText = await response.text(); + const errorData = JSON.parse(errorText); + throw new Error(`${errorData.message || errorText}`); + } + notifyToast({ + type: 'success', + message: 'Study created successfully', + }); + toggleModal(); + } catch (error: any) { + notifyToast({ + type: 'error', + message: `${error.message}`, + }); + } +}; +export const fetchSuggestedKeywords = async (query: string): Promise => { + const response = await fetch(`http://localhost:8093/v1/study/keywords/search?partialName=${query}`); + if (!response.ok) { + throw new Error('Failed to fetch suggested keywords'); + } + const data = await response.json(); + return data; +}; diff --git a/src/pages/pegase/home/components/useStudyTableDisplay.ts b/src/pages/pegase/home/components/useStudyTableDisplay.ts index 7ffc6b5..4bccf53 100644 --- a/src/pages/pegase/home/components/useStudyTableDisplay.ts +++ b/src/pages/pegase/home/components/useStudyTableDisplay.ts @@ -8,7 +8,7 @@ import { useEffect, useState } from 'react'; import { StudyDTO } from '@/shared/types/index'; import { getEnvVariables } from '@/envVariables'; -const ITEMS_PER_PAGE = 5; +const ITEMS_PER_PAGE = 10; const BASE_URL = getEnvVariables('VITE_BACK_END_BASE_URL'); const PAGINATION_CURRENT = 0; const PAGINATION_COUNT = 0; diff --git a/src/pages/pegase/studies/HorizonInput.tsx b/src/pages/pegase/studies/HorizonInput.tsx new file mode 100644 index 0000000..90db5f4 --- /dev/null +++ b/src/pages/pegase/studies/HorizonInput.tsx @@ -0,0 +1,106 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +import React, { useState } from 'react'; +import { RdsButton, RdsIconId, RdsInputText } from 'rte-design-system-react'; + +interface YearDropdownProps { + value: string; + onChange: (value: string) => void; +} + +const generateYears = (startYear: number, endYear: number): number[] => { + const years = []; + for (let year = startYear; year <= endYear; year++) { + years.push(year); + } + return years; +}; + +const HorizonInput: React.FC = ({ value, onChange }) => { + const [isOpen, setIsOpen] = useState(false); // State to control dropdown visibility + const [errorMessage, setErrorMessage] = useState(''); // State for error message + + const currentYear = new Date().getFullYear(); + const years = generateYears(currentYear, 2050); + + const toggleDropdown = () => { + setIsOpen(!isOpen); // Toggle dropdown visibility + }; + + const handleBlur = () => { + // If the entered value is not in the list, show an error and clear it + if (!years.includes(parseInt(value))) { + setErrorMessage('Veuillez choisir une date valide.'); + onChange(''); + } else { + setErrorMessage(''); // Clear error if the value is valid + } + }; + + return ( +
+ {/* Container for input and button */} +
+ {/* Input field */} + { + onChange(t || ''); + setErrorMessage(''); // Clear error message on input change + }} + onBlur={handleBlur} // Validate input on blur + placeHolder="Select a horizon" + variant="outlined" + /> + + {/* Toggle Button */} + +
+ + {/* Error Message */} + {errorMessage &&
{errorMessage}
} + + {/* Dropdown list */} + {isOpen && ( +
e.preventDefault()} // Prevent dropdown from closing when clicking inside + > + {years.map((year, index) => ( +
{ + onChange(year.toString()); + setIsOpen(false); // Close the dropdown on selection + setErrorMessage(''); // Clear error on valid selection + }} + > + {year} +
+ ))} +
+ )} +
+ ); +}; + +export default HorizonInput; diff --git a/src/pages/pegase/studies/KeywordsInput.tsx b/src/pages/pegase/studies/KeywordsInput.tsx new file mode 100644 index 0000000..76becba --- /dev/null +++ b/src/pages/pegase/studies/KeywordsInput.tsx @@ -0,0 +1,136 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +import { useState } from 'react'; +import { RdsButton, RdsIcon, RdsIconId, RdsInputText } from 'rte-design-system-react'; +import { fetchSuggestedKeywords } from '@/pages/pegase/home/components/studyService'; + +const MAX_KEYWORDS = 6; + +interface KeywordsInputProps { + keywords: string[]; + setKeywords: React.Dispatch>; +} + +const KeywordsInput: React.FC = ({ keywords, setKeywords }) => { + const [keywordInput, setKeywordInput] = useState(''); + const [errorMessage, setErrorMessage] = useState(''); + const [suggestedKeywords, setSuggestedKeywords] = useState([]); + + const handleKeywordChange = async (value: string) => { + setKeywordInput(value); + setErrorMessage(''); // Clear error message when input changes + try { + const tags = await fetchSuggestedKeywords(value); + setSuggestedKeywords(tags); + } catch (error) { + setErrorMessage('Failed to fetch suggested keywords'); + } + }; + + const handleAddKeyword = (suggestedKeyword = keywordInput) => { + if (suggestedKeyword.trim()) { + if (keywords.includes(suggestedKeyword.trim())) { + setErrorMessage('Keyword already exists'); + } else if (suggestedKeyword.trim().length < 3 || suggestedKeyword.trim().length > 10) { + setErrorMessage('Keyword must be between 3 and 10 characters'); + } else if (keywords.length >= MAX_KEYWORDS) { + setErrorMessage('Cannot add more than 6 keywords'); + } else { + setKeywords((prevKeywords) => [...prevKeywords, suggestedKeyword.trim()]); + setKeywordInput(''); + setErrorMessage(''); + } + } + }; + + const clearAllKeywords = () => { + setKeywords([]); + }; + + const handleRemoveKeyword = (index: number) => { + setKeywords((prevKeywords) => prevKeywords.filter((_, i) => i !== index)); + }; + + return ( +
+
+
+ + {keywordInput && keywordInput.length >= 3 && ( + handleAddKeyword()} + icon={RdsIconId.Add} + color="secondary" + size="extraSmall" + variant="transparent" + /> + )} +
+ + {/* Suggested Keywords Dropdown */} + {keywordInput && ( +
e.preventDefault()} // Prevent closing when interacting with dropdown + > + {suggestedKeywords.map((suggestedKeyword, index) => ( +
handleAddKeyword(suggestedKeyword)} + > + {suggestedKeyword} +
+ ))} +
+ )} +
+ + {/* Error Message */} + {errorMessage &&
{errorMessage}
} + + {/* Keywords Display and Clear All Button */} +
+ {keywords.map((keyword, index) => ( +
+ {keyword} + handleRemoveKeyword(index)} + size="extraSmall" + variant="text" + color="secondary" + /> +
+ ))} +
+ + {/* Clear All Keywords Button */} + {keywords.length > 0 && ( +
+ + Clear all +
+ )} +
+ ); +}; + +export default KeywordsInput; diff --git a/src/pages/pegase/studies/ProjectInput.tsx b/src/pages/pegase/studies/ProjectInput.tsx new file mode 100644 index 0000000..2af0017 --- /dev/null +++ b/src/pages/pegase/studies/ProjectInput.tsx @@ -0,0 +1,92 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +// src/components/ProjectInput.tsx +import React, { useState, useEffect } from 'react'; +import { RdsInputText } from 'rte-design-system-react'; + +interface ProjectManagerProps { + value: string; + onChange: (value: string) => void; +} + +const fetchProjects = async (query: string): Promise => { + const response = await fetch(`http://localhost:8093/v1/project/autocomplete?partialName=${query}`); + if (!response.ok) { + throw new Error('Failed to fetch projects'); + } + const data = await response.json(); + return data.map((project: { name: string }) => project.name); // Extract and return only the 'name' property +}; + +const ProjectInput: React.FC = ({ value, onChange }) => { + const [projects, setProjects] = useState([]); + const [errorMessage, setErrorMessage] = useState(''); + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + + useEffect(() => { + const loadProjects = async () => { + try { + const projectList = await fetchProjects(value); + setProjects(projectList); + } catch (error) { + setErrorMessage('Failed to fetch projects'); + } + }; + + if (value) { + loadProjects(); + } else { + setProjects([]); + } + }, [value]); + + const handleProjectSelect = (project: string) => { + onChange(project); + setIsDropdownOpen(false); + }; + + return ( +
+ { + onChange(t || ''); + setIsDropdownOpen(true); + }} + placeHolder="Select or add a project" + variant="outlined" + /> + {isDropdownOpen && projects.length > 0 && ( +
e.preventDefault()} // Prevent dropdown from closing when clicking inside + > + {projects.map((project, index) => ( +
handleProjectSelect(project)} + > + {project} +
+ ))} +
+ )} + {errorMessage &&
{errorMessage}
} +
+ ); +}; + +export default ProjectInput; diff --git a/src/pages/pegase/studies/StudyCreationModal.tsx b/src/pages/pegase/studies/StudyCreationModal.tsx new file mode 100644 index 0000000..7a320e0 --- /dev/null +++ b/src/pages/pegase/studies/StudyCreationModal.tsx @@ -0,0 +1,121 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +import React, { useState, useEffect } from 'react'; +import { RdsButton, RdsIconId, RdsInputText, RdsModal } from 'rte-design-system-react'; +import { useTranslation } from 'react-i18next'; +import KeywordsInput from '@/pages/pegase/studies/KeywordsInput'; +import HorizonInput from '@/pages/pegase/studies/HorizonInput'; +import ProjectInput from '@/pages/pegase/studies/ProjectInput'; +import { saveStudy } from '@/pages/pegase/home/components/studyService'; +import { StudyDTO } from '@/shared/types/index'; + +interface StudyCreationModalProps { + isOpen: boolean; + onClose: () => void; + study?: StudyDTO | null; +} + +const StudyCreationModal: React.FC = ({ onClose, study }) => { + const { t } = useTranslation(); + const [studyName, setStudyName] = useState(''); + const [horizon, setHorizon] = useState(''); + const [projectName, setProjectName] = useState(study?.project || ''); + const [keywords, setKeywords] = useState(study?.keywords || []); + const [trajectoryIds] = useState(study?.trajectoryIds || []); + const [isFormValid, setIsFormValid] = useState(false); + const [errorMessage, setErrorMessage] = useState(''); + + const saveStudyHandler = async () => { + const studyData = { + name: studyName, + createdBy: 'currentUser', // Replace with the actual user identifier + keywords: keywords, + project: projectName, + horizon: horizon, + trajectoryIds: trajectoryIds, + }; + + await saveStudy(studyData, onClose); + // Clear form fields + setStudyName(''); + setProjectName(''); + setHorizon(''); + setKeywords([]); + }; + + const validateForm = () => { + if (studyName && projectName && horizon && keywords.length > 0 && !errorMessage) { + setIsFormValid(true); + } else { + setIsFormValid(false); + } + }; + + useEffect(() => { + validateForm(); + }, [studyName, projectName, horizon, keywords, errorMessage]); + + const validateHorizon = (year: string) => { + const currentYear = new Date().getFullYear(); + const selectedYear = parseInt(year); + if (selectedYear < currentYear) { + setErrorMessage('Horizon must be a year greater than the current year'); + } else if (selectedYear > 2100) { + setErrorMessage('Horizon must be a year less than or equal to 2100'); + } else { + setErrorMessage(''); + } + }; + + const handleHorizonChange = (value: string) => { + const numericRegex = /^[0-9]*$/; + if (!numericRegex.test(value)) { + setErrorMessage('Please enter a valid year'); + setHorizon(''); + } else { + setHorizon(value); + validateHorizon(value); + } + }; + + return ( + + {study ? t('home.@duplicate_study') : t('home.@new_study')} + +
+
+ setStudyName(t || '')} + variant="outlined" + placeHolder="Name your study..." + /> +
+
+ +
+
+ + +
+ + + + +
+ ); +}; + +export default StudyCreationModal; diff --git a/src/shared/i18n/en.json b/src/shared/i18n/en.json index 86da2b6..aeb6473 100644 --- a/src/shared/i18n/en.json +++ b/src/shared/i18n/en.json @@ -37,7 +37,9 @@ "@keywords": "Keywords", "@project": "Project", "@my_studies": "My studies", - "@my_projects": "My projects" + "@my_projects": "My projects", + "@new_study": "Create a study", + "@duplicate_study": "Duplicate a study" }, "project": { "@setting": "Setting", diff --git a/src/shared/types/pegase/study.ts b/src/shared/types/pegase/study.ts index 00b498c..27e5462 100644 --- a/src/shared/types/pegase/study.ts +++ b/src/shared/types/pegase/study.ts @@ -13,4 +13,5 @@ export type StudyDTO = { project: string; status: string; horizon: string; + trajectoryIds: number[]; }; diff --git a/src/shared/utils/slotsUtils.ts b/src/shared/utils/slotsUtils.ts new file mode 100644 index 0000000..e439d48 --- /dev/null +++ b/src/shared/utils/slotsUtils.ts @@ -0,0 +1,15 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +import React, { Children, FunctionComponent, ReactElement } from 'react'; + +export const findSlotOfType = ( + children: ReactElement | ReactElement[] | undefined, + slotType: FunctionComponent, +): ReactElement | null => + Children.toArray(children).find( + (child) => React.isValidElement(child) && child.type === slotType, + ) as ReactElement | null; diff --git a/tsconfig.tsbuildinfo b/tsconfig.tsbuildinfo index cacc68a..eca007b 100644 --- a/tsconfig.tsbuildinfo +++ b/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/App.tsx","./src/envVariables.ts","./src/i18n.ts","./src/main.tsx","./src/routes.tsx","./src/testSetup.ts","./src/vite-env.d.ts","./src/components/common/base/stdIcon/Icon.tsx","./src/components/common/base/stdIcon/StdIcon.tsx","./src/components/common/base/stdIcon/iconClassBuilder.ts","./src/components/common/data/stdSimpleTable/StdSimpleTable.tsx","./src/components/common/data/stdSimpleTable/tests/stdSimpleTable.test.tsx","./src/components/common/data/stdTable/TableContext.tsx","./src/components/common/data/stdTable/TableCore.tsx","./src/components/common/data/stdTable/tableCoreRowClassBuilder.ts","./src/components/common/data/stdTable/useTableContext.ts","./src/components/common/data/stdTable/cells/ExpandableCell.tsx","./src/components/common/data/stdTable/cells/tests/expandableCell.test.tsx","./src/components/common/data/stdTable/features/readOnly.ts","./src/components/common/data/stdTable/lineRender/StdCollapseIcon.tsx","./src/components/common/data/stdTable/tests/TableCore.test.tsx","./src/components/common/data/stdTable/tests/tableCoreRowClassBuilder.test.ts","./src/components/common/data/stdTable/tests/testTableUtils.ts","./src/components/common/data/stdTable/types/readOnly.type.d.ts","./src/components/common/data/stdTable/types/sizeClassNames.d.ts","./src/components/common/handler/ThemeHandler.tsx","./src/components/common/handler/test/ThemeHandler.test.tsx","./src/components/common/layout/stdAvatar/StdAvatar.tsx","./src/components/common/layout/stdAvatar/avatarClassBuilder.ts","./src/components/common/layout/stdAvatar/tests/StdAvatar.test.tsx","./src/components/common/layout/stdAvatar/tests/avatarClassBuilder.test.ts","./src/components/common/layout/stdAvatarGroup/StdAvatarGroup.tsx","./src/components/common/layout/stdAvatarGroup/avatarGroupClassBuilder.ts","./src/components/common/layout/stdAvatarGroup/avatarTools.ts","./src/components/common/layout/stdAvatarGroup/tests/StdAvatarGroup.test.tsx","./src/components/common/layout/stdAvatarGroup/tests/avatarTools.test.ts","./src/components/common/layout/stdNavbar/StdNavbar.tsx","./src/components/common/layout/stdNavbar/StdNavbarController.tsx","./src/components/common/layout/stdNavbar/StdNavbarHeader.tsx","./src/components/common/layout/stdNavbar/StdNavbarMenu.tsx","./src/components/common/layout/stdNavbar/StdNavbarMenuItem.tsx","./src/components/common/layout/stdNavbar/navbarClassBuilder.ts","./src/components/common/layout/stdNavbar/tests/StdNavbar.test.tsx","./src/components/common/layout/stdNavbar/tests/StdNavbarController.test.tsx","./src/components/common/layout/stdNavbar/tests/StdNavbarHeader.test.tsx","./src/components/common/layout/stdNavbar/tests/StdNavbarMenuItem.test.tsx","./src/components/common/layout/stdNavbar/tests/navbarClassBuilder.test.ts","./src/components/common/layout/stdTextTooltip/StdTextTooltip.tsx","./src/components/common/layout/stdTextTooltip/tests/stdTooltipText.test.tsx","./src/components/common/layout/stdTextWithTooltip/StdTextWithTooltip.tsx","./src/components/pegase/header/Header.tsx","./src/components/pegase/navbar/Navbar.tsx","./src/components/pegase/pegaseCard/cardClassBuilder.ts","./src/components/pegase/pegaseCard/pegaseCard.tsx","./src/components/pegase/pegaseCard/useDropdownOptions.ts","./src/components/pegase/pegaseCard/pegaseCardTitle/cardTitleClassBuilder.tsx","./src/components/pegase/pegaseCard/pegaseCardTitle/pegaseCardTitle.tsx","./src/components/pegase/pegaseCard/pegaseCardTitle/tests/cardTitleClassBuilder.test.ts","./src/components/pegase/pegaseCard/pegaseCardTitle/tests/pegaseCardTitle.test.tsx","./src/components/pegase/pegaseCard/tests/cardTripleActionClassBuilder.test.ts","./src/components/pegase/pegaseCard/tests/pegaseCardTripleAction.test.tsx","./src/components/pegase/star/PegaseStar.tsx","./src/contexts/UserContext.tsx","./src/contexts/createFastContext.tsx","./src/contexts/modalContext.ts","./src/hooks/useDateFormatter.ts","./src/hooks/useInputFormState.ts","./src/hooks/useModal.ts","./src/hooks/useProjectNavigation.ts","./src/hooks/useStdId.ts","./src/hooks/common/useActiveKeyboard.ts","./src/hooks/common/useCallOnResize.ts","./src/hooks/common/useDebounce.ts","./src/hooks/common/useInputFormState.ts","./src/hooks/common/usePrevious.ts","./src/hooks/common/useStdId.ts","./src/hooks/common/useTimeout.ts","./src/hooks/common/test/useCallOnResize.test.ts","./src/hooks/common/test/useDebounce.test.ts","./src/hooks/common/test/useInputFormState.test.ts","./src/hooks/common/test/usePrevious.test.ts","./src/hooks/common/test/useStdId.test.ts","./src/hooks/common/test/useTimeout.test.ts","./src/mocks/mockTools.ts","./src/mocks/data/components/dropdownItems.mock.ts","./src/mocks/data/components/navbarHeader.ts","./src/mocks/data/features/menuItemData.mock.tsx","./src/mocks/data/list/keywords.ts","./src/mocks/data/list/names.ts","./src/mocks/data/list/projectName.ts","./src/mocks/data/list/studyName.ts","./src/mocks/data/list/user.mocks.ts","./src/mocks/data/list/user.ts","./src/pages/pegase/antares/Antares.tsx","./src/pages/pegase/home/HomePage.tsx","./src/pages/pegase/home/components/HomePageContent.tsx","./src/pages/pegase/home/components/SearchBar.tsx","./src/pages/pegase/home/components/StudiesPagination.tsx","./src/pages/pegase/home/components/StudyTableDisplay.tsx","./src/pages/pegase/home/components/StudyTableHeaders.tsx","./src/pages/pegase/home/components/StudyTableUtils.tsx","./src/pages/pegase/home/components/useStudyTableDisplay.ts","./src/pages/pegase/home/components/tests/useStudyTableDisplay.test.ts","./src/pages/pegase/home/pinnedProjects/PinnedProject.tsx","./src/pages/pegase/home/pinnedProjects/PinnedProjectCard.tsx","./src/pages/pegase/home/pinnedProjects/ProjectCreator.tsx","./src/pages/pegase/home/pinnedProjects/tests/handleUnpin.test.ts","./src/pages/pegase/logout/Logout.tsx","./src/pages/pegase/projects/ProjectContent.tsx","./src/pages/pegase/projects/Projects.tsx","./src/pages/pegase/projects/ProjectsPagination.tsx","./src/pages/pegase/projects/projectService.test.tsx","./src/pages/pegase/projects/projectService.ts","./src/pages/pegase/projects/projectDetails/ProjectDetails.tsx","./src/pages/pegase/projects/projectDetails/ProjectDetailsContent.tsx","./src/pages/pegase/projects/projectDetails/ProjectDetailsHeader.tsx","./src/pages/pegase/projects/projectDetails/fetchProjectDetails.test.tsx","./src/pages/pegase/reports/LogsPage.tsx","./src/pages/pegase/settings/Settings.tsx","./src/shared/constants.ts","./src/shared/notification/containers.tsx","./src/shared/notification/notification.tsx","./src/shared/types/index.ts","./src/shared/types/common/DisplayStatus.type.ts","./src/shared/types/common/MenuNavItem.type.ts","./src/shared/types/common/StdBase.type.ts","./src/shared/types/common/StudyStatus.type.ts","./src/shared/types/common/Tailwind.type.ts","./src/shared/types/common/TailwindColorClass.type.ts","./src/shared/types/common/User.type.ts","./src/shared/types/common/UserSettings.type.ts","./src/shared/types/common/tests/testUtils.tsx","./src/shared/types/pegase/Project.type.ts","./src/shared/types/pegase/study.ts","./src/shared/utils/dateFormatter.ts","./src/shared/utils/tabIndexUtils.ts","./src/shared/utils/common/defaultUtils.ts","./src/shared/utils/common/displayUtils.ts","./src/shared/utils/common/slotsUtils.ts","./src/shared/utils/common/classes/classMerger.ts","./src/shared/utils/common/classes/test/classMerger.test.ts","./src/shared/utils/common/dom/getDimensions.ts","./src/shared/utils/common/dom/test/getDimensions.test.tsx","./src/shared/utils/common/mappings/iconMaps.ts","./tailwind.config.ts","./vite-env.d.ts"],"version":"5.7.2"} \ No newline at end of file +{"root":["./src/App.tsx","./src/envVariables.ts","./src/i18n.ts","./src/main.tsx","./src/routes.tsx","./src/testSetup.ts","./src/vite-env.d.ts","./src/components/common/base/stdIcon/Icon.tsx","./src/components/common/base/stdIcon/StdIcon.tsx","./src/components/common/base/stdIcon/iconClassBuilder.ts","./src/components/common/data/stdSimpleTable/StdSimpleTable.tsx","./src/components/common/data/stdSimpleTable/tests/stdSimpleTable.test.tsx","./src/components/common/data/stdTable/TableContext.tsx","./src/components/common/data/stdTable/TableCore.tsx","./src/components/common/data/stdTable/tableCoreRowClassBuilder.ts","./src/components/common/data/stdTable/useTableContext.ts","./src/components/common/data/stdTable/cells/ExpandableCell.tsx","./src/components/common/data/stdTable/cells/tests/expandableCell.test.tsx","./src/components/common/data/stdTable/features/readOnly.ts","./src/components/common/data/stdTable/lineRender/StdCollapseIcon.tsx","./src/components/common/data/stdTable/tests/TableCore.test.tsx","./src/components/common/data/stdTable/tests/tableCoreRowClassBuilder.test.ts","./src/components/common/data/stdTable/tests/testTableUtils.ts","./src/components/common/data/stdTable/types/readOnly.type.d.ts","./src/components/common/data/stdTable/types/sizeClassNames.d.ts","./src/components/common/handler/ThemeHandler.tsx","./src/components/common/handler/test/ThemeHandler.test.tsx","./src/components/common/layout/stdAvatar/StdAvatar.tsx","./src/components/common/layout/stdAvatar/avatarClassBuilder.ts","./src/components/common/layout/stdAvatar/tests/StdAvatar.test.tsx","./src/components/common/layout/stdAvatar/tests/avatarClassBuilder.test.ts","./src/components/common/layout/stdAvatarGroup/StdAvatarGroup.tsx","./src/components/common/layout/stdAvatarGroup/avatarGroupClassBuilder.ts","./src/components/common/layout/stdAvatarGroup/avatarTools.ts","./src/components/common/layout/stdAvatarGroup/tests/StdAvatarGroup.test.tsx","./src/components/common/layout/stdAvatarGroup/tests/avatarTools.test.ts","./src/components/common/layout/stdNavbar/StdNavbar.tsx","./src/components/common/layout/stdNavbar/StdNavbarController.tsx","./src/components/common/layout/stdNavbar/StdNavbarHeader.tsx","./src/components/common/layout/stdNavbar/StdNavbarMenu.tsx","./src/components/common/layout/stdNavbar/StdNavbarMenuItem.tsx","./src/components/common/layout/stdNavbar/navbarClassBuilder.ts","./src/components/common/layout/stdNavbar/tests/StdNavbar.test.tsx","./src/components/common/layout/stdNavbar/tests/StdNavbarController.test.tsx","./src/components/common/layout/stdNavbar/tests/StdNavbarHeader.test.tsx","./src/components/common/layout/stdNavbar/tests/StdNavbarMenuItem.test.tsx","./src/components/common/layout/stdNavbar/tests/navbarClassBuilder.test.ts","./src/components/common/layout/stdTextTooltip/StdTextTooltip.tsx","./src/components/common/layout/stdTextTooltip/tests/stdTooltipText.test.tsx","./src/components/common/layout/stdTextWithTooltip/StdTextWithTooltip.tsx","./src/components/pegase/header/Header.tsx","./src/components/pegase/navbar/Navbar.tsx","./src/components/pegase/pegaseCard/cardClassBuilder.ts","./src/components/pegase/pegaseCard/pegaseCard.tsx","./src/components/pegase/pegaseCard/useDropdownOptions.ts","./src/components/pegase/pegaseCard/pegaseCardTitle/cardTitleClassBuilder.tsx","./src/components/pegase/pegaseCard/pegaseCardTitle/pegaseCardTitle.tsx","./src/components/pegase/pegaseCard/pegaseCardTitle/tests/cardTitleClassBuilder.test.ts","./src/components/pegase/pegaseCard/pegaseCardTitle/tests/pegaseCardTitle.test.tsx","./src/components/pegase/pegaseCard/tests/cardTripleActionClassBuilder.test.ts","./src/components/pegase/pegaseCard/tests/pegaseCardTripleAction.test.tsx","./src/components/pegase/star/PegaseStar.tsx","./src/contexts/UserContext.tsx","./src/contexts/createFastContext.tsx","./src/contexts/modalContext.ts","./src/hooks/useDateFormatter.ts","./src/hooks/useFocusTrapping.ts","./src/hooks/useInputFormState.ts","./src/hooks/useModal.ts","./src/hooks/useProjectNavigation.ts","./src/hooks/useStdId.ts","./src/hooks/common/useActiveKeyboard.ts","./src/hooks/common/useCallOnResize.ts","./src/hooks/common/useDebounce.ts","./src/hooks/common/useInputFormState.ts","./src/hooks/common/usePrevious.ts","./src/hooks/common/useStdId.ts","./src/hooks/common/useTimeout.ts","./src/hooks/common/test/useCallOnResize.test.ts","./src/hooks/common/test/useDebounce.test.ts","./src/hooks/common/test/useInputFormState.test.ts","./src/hooks/common/test/usePrevious.test.ts","./src/hooks/common/test/useStdId.test.ts","./src/hooks/common/test/useTimeout.test.ts","./src/mocks/mockTools.ts","./src/mocks/data/components/dropdownItems.mock.ts","./src/mocks/data/components/navbarHeader.ts","./src/mocks/data/features/menuItemData.mock.tsx","./src/mocks/data/list/keywords.ts","./src/mocks/data/list/names.ts","./src/mocks/data/list/projectName.ts","./src/mocks/data/list/studyName.ts","./src/mocks/data/list/user.mocks.ts","./src/mocks/data/list/user.ts","./src/pages/pegase/antares/Antares.tsx","./src/pages/pegase/home/HomePage.tsx","./src/pages/pegase/home/components/HomePageContent.tsx","./src/pages/pegase/home/components/SearchBar.tsx","./src/pages/pegase/home/components/StudiesPagination.tsx","./src/pages/pegase/home/components/StudyTableDisplay.tsx","./src/pages/pegase/home/components/StudyTableHeaders.tsx","./src/pages/pegase/home/components/StudyTableUtils.tsx","./src/pages/pegase/home/components/studyService.ts","./src/pages/pegase/home/components/useStudyTableDisplay.ts","./src/pages/pegase/home/components/tests/useStudyTableDisplay.test.ts","./src/pages/pegase/home/pinnedProjects/PinnedProject.tsx","./src/pages/pegase/home/pinnedProjects/PinnedProjectCard.tsx","./src/pages/pegase/home/pinnedProjects/ProjectCreator.tsx","./src/pages/pegase/home/pinnedProjects/tests/handleUnpin.test.ts","./src/pages/pegase/logout/Logout.tsx","./src/pages/pegase/projects/ProjectContent.tsx","./src/pages/pegase/projects/Projects.tsx","./src/pages/pegase/projects/ProjectsPagination.tsx","./src/pages/pegase/projects/projectService.test.tsx","./src/pages/pegase/projects/projectService.ts","./src/pages/pegase/projects/projectDetails/ProjectDetails.tsx","./src/pages/pegase/projects/projectDetails/ProjectDetailsContent.tsx","./src/pages/pegase/projects/projectDetails/ProjectDetailsHeader.tsx","./src/pages/pegase/projects/projectDetails/fetchProjectDetails.test.tsx","./src/pages/pegase/reports/LogsPage.tsx","./src/pages/pegase/settings/Settings.tsx","./src/pages/pegase/studies/HorizonInput.tsx","./src/pages/pegase/studies/KeywordsInput.tsx","./src/pages/pegase/studies/ProjectInput.tsx","./src/pages/pegase/studies/StudyCreationModal.tsx","./src/shared/constants.ts","./src/shared/notification/containers.tsx","./src/shared/notification/notification.tsx","./src/shared/types/index.ts","./src/shared/types/common/DisplayStatus.type.ts","./src/shared/types/common/MenuNavItem.type.ts","./src/shared/types/common/StdBase.type.ts","./src/shared/types/common/StudyStatus.type.ts","./src/shared/types/common/Tailwind.type.ts","./src/shared/types/common/TailwindColorClass.type.ts","./src/shared/types/common/User.type.ts","./src/shared/types/common/UserSettings.type.ts","./src/shared/types/common/tests/testUtils.tsx","./src/shared/types/pegase/Project.type.ts","./src/shared/types/pegase/study.ts","./src/shared/utils/dateFormatter.ts","./src/shared/utils/slotsUtils.ts","./src/shared/utils/tabIndexUtils.ts","./src/shared/utils/common/defaultUtils.ts","./src/shared/utils/common/displayUtils.ts","./src/shared/utils/common/slotsUtils.ts","./src/shared/utils/common/classes/classMerger.ts","./src/shared/utils/common/classes/test/classMerger.test.ts","./src/shared/utils/common/dom/getDimensions.ts","./src/shared/utils/common/dom/test/getDimensions.test.tsx","./src/shared/utils/common/mappings/iconMaps.ts","./tailwind.config.ts","./vite-env.d.ts"],"version":"5.7.2"} \ No newline at end of file