Skip to content

Commit

Permalink
feat(procedures): created gdpr page
Browse files Browse the repository at this point in the history
ref:MANAGER-15312

Signed-off-by: Omar ALKABOUSS MOUSSANA <[email protected]>
  • Loading branch information
Omar ALKABOUSS MOUSSANA committed Oct 8, 2024
1 parent cb1d1bd commit 35c61a0
Show file tree
Hide file tree
Showing 30 changed files with 218 additions and 70 deletions.
23 changes: 17 additions & 6 deletions packages/manager/apps/procedures/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { odsSetup } from '@ovhcloud/ods-common-core';
import { RouterProvider, createHashRouter } from 'react-router-dom';
import '@ovhcloud/ods-theme-blue-jeans';
import queryClient from './query.client';
import { Routes } from './routes/routes';
import {
accountDisable2faRoute,
exercisingYourRightsRoute,
Routes,
} from './routes/routes';
import { decodeToken, extractToken } from '@/utils/token';
import initI18n from './i18n';
import initAuthenticationInterceptor from '@/data/authentication.interceptor';
Expand All @@ -19,19 +23,26 @@ function getSubsidiary(subsidiary: string, locale: string) {
return subsidiary;
}

const activateAuthenticationInterceptorForPath = [accountDisable2faRoute];
const token = extractToken();
const user = decodeToken(token);
const { language: locale, subsidiary } = user || {};

if (!user) {
const redirectUrl = getRedirectLoginUrl(user);
window.location.assign(redirectUrl);
} else {
initI18n(locale || 'en_GB', getSubsidiary(subsidiary, locale));
initAuthenticationInterceptor(token);
initInterceptor();
}

odsSetup();
initI18n(locale || 'en_GB', getSubsidiary(subsidiary, locale));
odsSetup();
initInterceptor();

if (
activateAuthenticationInterceptorForPath.some((path) =>
window.location.href.includes(path),
)
) {
initAuthenticationInterceptor(token);
}

const router = createHashRouter(Routes);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React, { FunctionComponent } from 'react';
import ovhCloudLogo from '@/assets/logo-ovhcloud.png';

type Props = {
children: JSX.Element | JSX.Element[];
};

export const PageLayout: FunctionComponent<Props> = ({ children }) => (
<div className="sm:container mx-auto px-6">
<div className="md:py-12 p-6">
<div className="inline-block pb-6 md:pb-12">
<img src={ovhCloudLogo} alt="ovh-cloud-logo" className="app-logo" />
</div>
<div className="flex justify-center app-content lg:w-8/12 mx-auto min-h-[500px] sm:shadow sm:border-none border-t-[1px] border-gray-300 px-6">
<div className="md:p-8 w-full">{children}</div>
</div>
</div>
</div>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React, { FunctionComponent, useCallback, useContext } from 'react';
import { useSessionModal } from '../useSessionModal';
import { getRedirectLoginUrl } from '@/utils/url-builder';
import { ExpiredSessionModal } from './ExpiredSessionModal';
import { WarningSessionModal } from './WarningSessionModal';
import userContext from '../context';

export const SessionModals: FunctionComponent = () => {
const { user } = useContext(userContext);
const {
setShowExpiredModal,
setShowWarningModal,
showExpiredModal,
showWarningModal,
} = useSessionModal(user, 0.75);

const handleCloseExpiredModal = useCallback(() => {
setShowExpiredModal(false);
const redirectUrl = getRedirectLoginUrl(user);
window.location.assign(redirectUrl);
}, [user]);

const handleCloseWarningModal = () => setShowWarningModal(false);

return (
<>
{showExpiredModal && (
<ExpiredSessionModal onClose={handleCloseExpiredModal} />
)}
{showWarningModal && (
<WarningSessionModal onClose={handleCloseWarningModal} />
)}
</>
);
};
29 changes: 3 additions & 26 deletions packages/manager/apps/procedures/src/context/User/provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,35 +11,12 @@ type Props = {
};

export const UserProvider = ({ children = null, user }: Props): JSX.Element => {
const {
setShowExpiredModal,
setShowWarningModal,
showExpiredModal,
showWarningModal,
} = useSessionModal(user, 0.75);

const handleCloseExpiredModal = useCallback(() => {
setShowExpiredModal(false);
const redirectUrl = getRedirectLoginUrl(user);
window.location.assign(redirectUrl);
}, [user]);

const handleCloseWarningModal = () => setShowWarningModal(false);

const memoizedUser = useMemo(() => ({ user }), []);

return (
<>
<userContext.Provider value={memoizedUser}>
{user && children}
</userContext.Provider>
{showExpiredModal && (
<ExpiredSessionModal onClose={handleCloseExpiredModal} />
)}
{showWarningModal && (
<WarningSessionModal onClose={handleCloseWarningModal} />
)}
</>
<userContext.Provider value={memoizedUser}>
{user && children}
</userContext.Provider>
);
};

Expand Down
1 change: 1 addition & 0 deletions packages/manager/apps/procedures/src/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export default function initI18n(locale = 'fr_FR', sub: string) {
'account-disable-2fa/error',
'account-disable-2fa-sub',
'account-disable-2fa-documents',
'rgdp',
], // namespaces to load by default
backend: {
loadPath: (lngs: string[], namespaces: string[], ...d: []) => {
Expand Down
29 changes: 25 additions & 4 deletions packages/manager/apps/procedures/src/pages/404.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,32 @@
import React, { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { rootRoute } from '@/routes/routes';
import { useLocation, useNavigate } from 'react-router-dom';
import { useEffect, useMemo } from 'react';
import {
accountDisable2faRoute,
exercisingYourRightsRoute,
} from '@/routes/routes';

export default function NotFound(): null {
const navigate = useNavigate();
const { pathname, search } = useLocation();

const redirectMap: Record<string, string> = useMemo(
() => ({
[accountDisable2faRoute]: `${accountDisable2faRoute}${search || ''}`,
[exercisingYourRightsRoute]: `${exercisingYourRightsRoute}${search ||
''}`,
}),
[search],
);

useEffect(() => {
navigate(rootRoute, { replace: true });
const matchedRoute = Object.keys(redirectMap).find((route) =>
pathname.startsWith(route),
);
if (matchedRoute) {
navigate(redirectMap[matchedRoute], { replace: true });
} else {
window.location.assign('https://www.ovhcloud.com');
}
}, []);

return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import Home from './Home.page';
import Home from './DisableMFA.page';

const mockedUsedNavigate = vi.fn();
const mockedUsedLocation = vi.fn();
Expand All @@ -20,6 +20,10 @@ vi.mock('react-router-dom', () => ({
Outlet: () => <p>TestOutlet</p>,
}));

vi.mock('@/context/User/modals/SessionModals', () => ({
SessionModals: () => <div>SessionModals</div>,
}));

vi.mock('@/components/Loading/Loading', () => ({
default: () => <p>TestLoading</p>,
}));
Expand All @@ -28,7 +32,7 @@ vi.mock('@/data/hooks/useStatus', () => ({
useFetch2faStatus: () => fetch2faStatusFakeResponse,
}));

describe('Home.page', () => {
describe('DisableMFA.page', () => {
it('should navigate to see page when status is open', async () => {
fetch2faStatusFakeResponse.isFetched = true;
fetch2faStatusFakeResponse.isSuccess = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,23 @@ import {
createRoutePath,
seeRoutePath,
errorRoutePath,
} from '@/routes/home.constants';
import { rootRoute } from '@/routes/routes';
import Loading from '@/components/Loading/Loading';
} from '@/routes/mfa.constants';
import { accountDisable2faRoute } from '@/routes/routes';
import { SkeletonLoading } from '@/components/Loading/SkeletonLoading';
import { SessionModals } from '@/context/User/modals/SessionModals';
import { PageLayout } from '@/components/PageLayout/PageLayout.component';

const redirectStrategies: Record<Status2fa['status'] | 'error', string> = {
open: `${rootRoute}/${seeRoutePath}`,
creationAuthorized: `${rootRoute}/${createRoutePath}`,
error: `${rootRoute}/${errorRoutePath}`,
open: `${accountDisable2faRoute}/${seeRoutePath}`,
creationAuthorized: `${accountDisable2faRoute}/${createRoutePath}`,
error: `${accountDisable2faRoute}/${errorRoutePath}`,
};

const checkIfCreationIsAllowed = (error: AxiosError<any>) =>
error?.response?.status === 404 &&
error?.response?.data?.class === 'Client::ErrNotFound::ErrNotFound';

export default function Home() {
export default function DisableMFA() {
const navigate = useNavigate();
const location = useLocation();

Expand All @@ -50,17 +51,9 @@ export default function Home() {
}, [isFetched]);

return (
<div className="sm:container mx-auto px-6">
<div className="md:py-12 p-6">
<div className="inline-block pb-6 md:pb-12">
<img src={ovhCloudLogo} alt="ovh-cloud-logo" className="app-logo" />
</div>
<div className="flex justify-center app-content lg:w-8/12 mx-auto min-h-[500px] sm:shadow sm:shadow-[0_0_6px_0_rgba(40,89,192,0.2)] sm:border-none border-t-[1px] border-gray-300 px-6">
<div className="md:p-8 w-full">
{isLoading ? <SkeletonLoading /> : <Outlet />}
</div>
</div>
</div>
</div>
<>
<PageLayout>{isLoading ? <SkeletonLoading /> : <Outlet />}</PageLayout>
<SessionModals />
</>
);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { describe, expect, it, vi } from 'vitest';
import { render } from '@testing-library/react';
import Create from '@/pages/create/Create.page';
import Create from '@/pages/disableMFA/create/Create.page';
import { LegalPolicyLinkByLanguage } from '@/constants';

const user = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const LegalPolicyLinkByLanguage = {
DEFAULT:
'https://www.ovhcloud.com/en-ie/terms-and-conditions/privacy-policy/',
en_CA: 'https://www.ovhcloud.com/en-ca/terms-and-conditions/privacy-policy/',
de_DE: 'https://www.ovhcloud.com/de/terms-and-conditions/privacy-policy/',
es_ES: 'https://www.ovhcloud.com/es-es/terms-and-conditions/privacy-policy/',
fr_FR: 'https://www.ovhcloud.com/fr/terms-and-conditions/privacy-policy/',
fr_CA: 'https://www.ovhcloud.com/fr-ca/terms-and-conditions/privacy-policy/',
en_GB: 'https://www.ovhcloud.com/en-gb/terms-and-conditions/privacy-policy/',
it_IT: 'https://www.ovhcloud.com/it/terms-and-conditions/privacy-policy/',
pl_PL: 'https://www.ovhcloud.com/pl/terms-and-conditions/privacy-policy/',
pt_PT: 'https://www.ovhcloud.com/pt/terms-and-conditions/privacy-policy/',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { render, screen } from '@testing-library/react';
import { describe, it, vi } from 'vitest';
import { useTranslation } from 'react-i18next';
import { MemoryRouter } from 'react-router-dom';
import React from 'react';
import RGDP from './RGDP.page';

vi.mock('react-i18next', () => ({
useTranslation: () => ({
t: vi.fn((key) => key),
}),
}));

vi.mock('@ovhcloud/ods-components/react', () => ({
OsdsText: ({ children, color, level, size, className }: any) => (
<div data-testid="osds-text" color={color} className={className}>
{children}
</div>
),
}));

describe('RGDP Component', () => {
it('renders the component correctly', () => {
render(
<MemoryRouter>
<RGDP />
</MemoryRouter>,
);

const titleElement = screen.getByText('rgdp-title');
expect(titleElement).toBeInTheDocument();
});
});
28 changes: 28 additions & 0 deletions packages/manager/apps/procedures/src/pages/rgdp/RGDP.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {
ODS_THEME_COLOR_INTENT,
ODS_THEME_TYPOGRAPHY_LEVEL,
} from '@ovhcloud/ods-common-theming';
import { OsdsText } from '@ovhcloud/ods-components/react';
import { ODS_TEXT_SIZE } from '@ovhcloud/ods-components';
import { useTranslation } from 'react-i18next';
import { Outlet } from 'react-router-dom';
import React from 'react';
import { PageLayout } from '@/components/PageLayout/PageLayout.component';

export default function RGDP() {
const { t } = useTranslation('rgdp');

return (
<PageLayout>
<OsdsText
color={ODS_THEME_COLOR_INTENT.info}
level={ODS_THEME_TYPOGRAPHY_LEVEL.heading}
className="block mb-6 text-center"
size={ODS_TEXT_SIZE._500}
>
{t('rgdp-title')}
</OsdsText>
<Outlet />
</PageLayout>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"rgdp-title": "Comment exercer vos droits ou contacter le DPO"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Loading

0 comments on commit 35c61a0

Please sign in to comment.