Skip to content

Commit

Permalink
feat: ANT-2726 - Link with gaia
Browse files Browse the repository at this point in the history
  • Loading branch information
marlenetienne committed Feb 6, 2025
1 parent 9347745 commit f78973a
Show file tree
Hide file tree
Showing 27 changed files with 319 additions and 172 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"i18next": "^23.14.0",
"mutative": "^1.0.6",
"postcss": "^8.4.39",
"oidc-client-ts": "^3.1.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^15.0.3",
Expand Down
56 changes: 30 additions & 26 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Route, Routes } from 'react-router-dom';
import './App.css';
import ThemeHandler from './components/common/handler/ThemeHandler';
import PegaseStar from './components/pegase/star/PegaseStar';
import { UserContext } from '@/store/contexts/UserContext';
import { UserSettingsContext } from '@/store/contexts/UserSettingsContext.tsx';
import { THEME_COLOR } from '@/shared/types';
import { menuBottomData, menuTopData } from './routes';
import { PegaseToastContainer } from './shared/notification/containers';
Expand All @@ -19,36 +19,40 @@ import { RdsNavbar } from 'rte-design-system-react';
import { navBarConfig } from '@/shared/const/navBarConfig';
import { useTranslation } from 'react-i18next';
import { translateMenuItemLabel } from '@/shared/utils/textUtils.ts';
import { PEGASE_NAVBAR_ID } from '@/shared/constants.ts';
import UserProvider from '@/store/contexts/UserProvider.tsx';

function App() {
const { t } = useTranslation();

return (
<div className="flex h-screen w-screen dark:bg-gray-900 dark:text-gray-200">
<UserContext.Provider initialState={{ theme: THEME_COLOR.LIGHT }}>
<ThemeHandler />
<PegaseToastContainer />
<RdsNavbar
id={'main-nav-bar'}
topItems={translateMenuItemLabel(menuTopData, t)}
bottomItems={translateMenuItemLabel(menuBottomData, t)}
headerLink={'/'}
config={navBarConfig}
/>
<div className="flex h-full w-full flex-col">
<PegaseStar />
<Suspense>
<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>
</Suspense>
</div>
</UserContext.Provider>
</div>
<UserProvider initialValue={{ user: null }}>
<div className="flex h-screen w-screen dark:bg-gray-900 dark:text-gray-200">
<UserSettingsContext.Provider initialState={{ theme: THEME_COLOR.LIGHT }}>
<ThemeHandler />
<PegaseToastContainer />
<RdsNavbar
id={PEGASE_NAVBAR_ID}
topItems={translateMenuItemLabel(menuTopData, t)}
bottomItems={translateMenuItemLabel(menuBottomData, t)}
headerLink={'/'}
config={navBarConfig}
/>
<div className="flex h-full w-full flex-col">
<PegaseStar />
<Suspense>
<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>
</Suspense>
</div>
</UserSettingsContext.Provider>
</div>
</UserProvider>
);
}

Expand Down
4 changes: 2 additions & 2 deletions src/components/common/handler/ThemeHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

import { UserContext } from '@/store/contexts/UserContext';
import { UserSettingsContext } from '@/store/contexts/UserSettingsContext.tsx';
import usePrevious from '@/hooks/common/usePrevious';
import { THEME_COLOR } from '@/shared/types';
import { useEffect } from 'react';

const ThemeHandler = () => {
const darkThemeMq = window.matchMedia('(prefers-color-scheme: dark)');
const themeColor = UserContext.useStore((store) => store.theme);
const themeColor = UserSettingsContext.useStore((store) => store.theme);
const previousTheme = usePrevious(themeColor, undefined);
useEffect(() => {
if (previousTheme) {
Expand Down
10 changes: 5 additions & 5 deletions src/components/common/handler/test/ThemeHandler.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import { render } from '@testing-library/react';
import { describe, expect, it, Mock, vi } from 'vitest';
import { THEME_COLOR } from '@/shared/types';
import ThemeHandler from '../ThemeHandler';
import { UserContext } from '@/store/contexts/UserContext';
import { UserSettingsContext } from '@/store/contexts/UserSettingsContext.tsx';
import usePrevious from '@/hooks/common/usePrevious';

// Mocking the UserContext and usePrevious hook
vi.mock('@/store/contexts/UserContext', () => ({
UserContext: {
// Mocking the UserSettingsContext and usePrevious hook
vi.mock('@/store/contexts/UserSettingsContext', () => ({
UserSettingsContext: {
useStore: vi.fn(),
},
}));
Expand All @@ -23,7 +23,7 @@ vi.mock('@/hooks/common/usePrevious', () => ({
}));

describe('ThemeHandler', () => {
const mockUseStore = UserContext.useStore as Mock;
const mockUseStore = UserSettingsContext.useStore as Mock;
const mockUsePrevious = usePrevious as Mock;

beforeEach(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,14 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

import { User } from '@/shared/types/common/User.type';
import StdAvatar from '../stdAvatar/StdAvatar';
import { AvatarSize } from '../stdAvatar/StdAvatar';
import { UserInfo } from '@/shared/types/common/User.type';
import StdAvatar, { AvatarSize } from '../stdAvatar/StdAvatar';
import { classBuilder as groupContainerClassBuilder } from './avatarGroupClassBuilder';
import { getColor, getUserFullname, getUserInitials, splitUserList } from './avatarTools';
import { useRdsId } from 'rte-design-system-react';

type StdAvatarGroup = {
users: User[];
users: UserInfo[];
avatarSize: AvatarSize;
id?: string;
};
Expand Down
12 changes: 6 additions & 6 deletions src/components/common/layout/stdAvatarGroup/avatarTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,41 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

import { User } from '@/shared/types/common/User.type';
import { UserInfo } from '@/shared/types/common/User.type';

export const AVATAR_COLORS = ['green', 'purple', 'blue', 'pink', 'gray', 'orange'] as const;
const USER_SEPARATOR = ' - ';
const MAX_USER_CHIP = 3;

export const splitUserList = (users: User[]) => {
export const splitUserList = (users: UserInfo[]) => {
if (users.length <= MAX_USER_CHIP) {
return users;
}
const [firstUser, secondUser, ...otherUsers] = users;
return [firstUser, secondUser, otherUsers];
};

export const getInitials = (user: User) => {
export const getInitials = (user: UserInfo) => {
const [firstName, lastName = ''] = user.fullname.split(' ');
return lastName.charAt(0) + firstName.charAt(0);
};

export const getUserInitials = (users: User | User[]) => {
export const getUserInitials = (users: UserInfo | UserInfo[]) => {
if (!Array.isArray(users)) {
return getInitials(users);
}
return `+${users.length}`;
};

export const getUserFullname = (users: User | User[]) => {
export const getUserFullname = (users: UserInfo | UserInfo[]) => {
if (!Array.isArray(users)) {
return users.fullname;
}
return users.map((user) => user.fullname).join(USER_SEPARATOR);
};

//assign a random color from COLORS
export const getColor = (users: User | User[]) => {
export const getColor = (users: UserInfo | UserInfo[]) => {
if (!Array.isArray(users)) {
return AVATAR_COLORS[
users.fullname.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0) % AVATAR_COLORS.length
Expand Down
37 changes: 18 additions & 19 deletions src/hooks/test/useFetchProjectList.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,22 @@ vi.mock('@/envVariables', () => ({

describe('useFetchProjectList', () => {
beforeEach(() => {
global.fetch = vi.fn(() =>
Promise.resolve({
json: () =>
Promise.resolve({
content: [
{
projectId: '1',
name: 'Project 1',
tags: ['Tag1', 'Tag2'],
creationDate: '2023-10-01',
createdBy: 'User A',
},
],
totalElements: 1,
}),
}),
) as unknown as typeof fetch;
global.fetch = vi.fn().mockResolvedValueOnce({
ok: true,
json: async () =>
Promise.resolve({
content: [
{
projectId: '1',
name: 'Project 1',
tags: ['Tag1', 'Tag2'],
creationDate: '2023-10-01',
createdBy: 'User A',
},
],
totalElements: 1,
}),
});
});

afterEach(() => {
Expand Down Expand Up @@ -60,15 +59,15 @@ describe('useFetchProjectList', () => {
renderHook(() => useFetchProjectList('test', 0, 9));

await waitFor(() => {
expect(global.fetch).toHaveBeenCalledWith('https://mockapi.com/v1/project/search?page=1&size=9&search=test');
expect(global.fetch).toHaveBeenCalledWith('https://mockapi.com/v1/project/search?page=1&size=9&search=test', {});
});
});

it('fetches projects with pagination', async () => {
renderHook(() => useFetchProjectList('', 1, 9));

await waitFor(() => {
expect(global.fetch).toHaveBeenCalledWith('https://mockapi.com/v1/project/search?page=2&size=9&search=');
expect(global.fetch).toHaveBeenCalledWith('https://mockapi.com/v1/project/search?page=2&size=9&search=', {});
});
});
});
9 changes: 6 additions & 3 deletions src/hooks/test/useStudyTableDisplay.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ vi.mock('@/envVariables', () => ({
describe('useStudyTableDisplay', () => {
beforeEach(() => {
global.fetch = vi.fn();
vi.restoreAllMocks();
});

afterEach(() => {
Expand Down Expand Up @@ -48,11 +49,11 @@ describe('useStudyTableDisplay', () => {

global.fetch = vi.fn().mockResolvedValue({
ok: true,
json: async () => mockResponse,
json: async () => Promise.resolve(mockResponse),
});

const { result } = renderHook(() =>
useStudyTableDisplay({ searchTerm: 'test', sortBy: { status: 'desc' }, reloadStudies: true }),
useStudyTableDisplay({ searchTerm: 'test', sortBy: { status: 'desc' }, reloadStudies: false }),
);
await waitFor(() => {
expect(result.current.rows).toHaveLength(2);
Expand All @@ -61,16 +62,18 @@ describe('useStudyTableDisplay', () => {
//expect(global.fetch).toHaveBeenCalledTimes(1); TODO: ANT-2719
expect(global.fetch).toHaveBeenCalledWith(
'https://mockapi.com/v1/study/search?page=1&size=9&projectId=&search=test&sortColumn=status&sortDirection=desc',
{},
);
});

await act(async () => {
renderHook(() => useStudyTableDisplay({ searchTerm: 'mouad', sortBy: { project: 'asc' }, reloadStudies: true }));
renderHook(() => useStudyTableDisplay({ searchTerm: 'mouad', sortBy: { project: 'asc' }, reloadStudies: false }));
});

//expect(global.fetch).toHaveBeenCalledTimes(1); TODO: ANT-2719
expect(global.fetch).toHaveBeenCalledWith(
'https://mockapi.com/v1/study/search?page=1&size=9&projectId=&search=mouad&sortColumn=project&sortDirection=asc',
{},
);
});

Expand Down
29 changes: 18 additions & 11 deletions src/hooks/useFetchProjectList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,31 @@ import { ProjectActionType, ProjectInfo } from '@/shared/types/pegase/Project.ty
import { fetchProjectFromSearchTerm } from '@/shared/services/projectService.ts';
import { PROJECT_ACTION } from '@/shared/enum/project.ts';
import { useProjectDispatch } from '@/store/contexts/ProjectContext.tsx';
import { PaginatedResponse } from '@/shared/types';

export const useFetchProjectList = (searchTerm: string, current: number, intervalSize: number) => {
const [projects, setProjects] = useState<ProjectInfo[]>([]);
const [count, setCount] = useState(0);
const dispatch = useProjectDispatch();

const fetchProjects = useCallback(
async (searchTerm: string, current: number, intervalSize: number) => {
fetchProjectFromSearchTerm(searchTerm, current, intervalSize)
.then((json) => {
dispatch?.({
type: PROJECT_ACTION.INIT_PROJECT_LIST,
payload: json.content,
} as ProjectActionType);
setProjects(json.content);
setCount(json.totalElements);
})
.catch((error) => console.error(error));
async (term: string, currentPage: number, size: number) => {
try {
const { content, totalElements } = (await fetchProjectFromSearchTerm(
term,
currentPage,
size,
)) as PaginatedResponse<ProjectInfo>;

dispatch?.({
type: PROJECT_ACTION.INIT_PROJECT_LIST,
payload: content,
} as ProjectActionType);
setProjects(content);
setCount(totalElements);
} catch (error) {
console.error(error);
}
},
[current, intervalSize, searchTerm],
);
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useHandlePinnedProjectList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { useCallback, useEffect } from 'react';
import { ProjectActionType } from '@/shared/types/pegase/Project.type';
import { ProjectActionType, ProjectInfo } from '@/shared/types/pegase/Project.type';
import { fetchPinnedProjects, pinProject, unpinProject } from '@/shared/services/pinnedProjectService';
import { v4 as uuidv4 } from 'uuid';
import { dismissToast, notifyToast, NotifyWithActionProps } from '@/shared/notification/notification.tsx';
Expand All @@ -20,7 +20,7 @@ export const useHandlePinnedProjectList = () => {

const getPinnedProjects = useCallback(async () => {
try {
const projects = await fetchPinnedProjects(userId);
const projects = (await fetchPinnedProjects(userId)) as ProjectInfo[];
if (projects?.length) {
dispatch?.({
type: PROJECT_ACTION.INIT_PINNED_PROJECT_LIST,
Expand Down
13 changes: 7 additions & 6 deletions src/hooks/useStudyTableDisplay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react';
import { StudyDTO } from '@/shared/types';
import { PaginatedResponse, StudyDTO } from '@/shared/types';
import { fetchSearchStudies } from '@/shared/services/studyService.ts';

const ITEMS_PER_PAGE = 9;
Expand Down Expand Up @@ -37,7 +37,7 @@ export const useStudyTableDisplay = ({
const [rows, setRows] = useState<StudyDTO[]>([]);
const [count, setCount] = useState(0);
const [currentPage, setCurrentPage] = useState(0);
const [error, setError] = useState(null);
const [errorValue, setErrorValue] = useState<Error | null>(null);

const searchTermRef = useRef(searchTerm);
const projectIdRef = useRef(projectId);
Expand All @@ -57,12 +57,13 @@ export const useStudyTableDisplay = ({

useEffect(() => {
fetchSearchStudies(searchTermRef.current, projectIdRef.current, currentPage, intervalSize, sortByRef.current)
.then(({ content, totalElements }) => {
.then((json) => {
const { content, totalElements } = json as PaginatedResponse<StudyDTO>;
setRows(content);
setCount(totalElements);
})
.catch((error) => setError(error));
}, [currentPage, searchTerm, projectId, sortBy, reloadStudies]);
.catch((error: unknown) => setErrorValue(error as Error));
}, [currentPage, searchTermRef, projectIdRef, sortByRef, reloadStudies]);

return { rows, count, intervalSize, currentPage, setPage: setCurrentPage, error };
return { rows, count, intervalSize, currentPage, setPage: setCurrentPage, error: errorValue };
};
Loading

0 comments on commit f78973a

Please sign in to comment.