Skip to content

Commit fff8eb1

Browse files
Romain Jametrjamet-ovh
authored andcommitted
feat(okms): add secret-manager okms dashboard
ref: #MANAGER-19793 Signed-off-by: Romain Jamet <[email protected]>
1 parent ca4bf78 commit fff8eb1

File tree

15 files changed

+300
-3
lines changed

15 files changed

+300
-3
lines changed

packages/manager/apps/okms/public/translations/secret-manager/Messages_fr_FR.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
"edit_metadata": "Modifier les paramètres",
3131
"okms_activation_in_progress": "Veuillez patienter, création en cours.",
3232
"okms_list": "Liste de domaines OKMS",
33+
"okms_dashboard_title": "Détail du domaine OKMS",
34+
"okms_manage_label": "Gérer mon domaine OKMS",
3335
"editor": "Éditeur JSON",
3436
"error_invalid_json": "JSON non valide",
3537
"error_path_allowed_characters": "Le path ne peut contenir que les caractères suivants: A-Z a-z 0-9 . _ : / = @ et -",

packages/manager/apps/okms/src/modules/secret-manager/components/breadcrumb/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { OkmsBreadcrumbItem } from './items/OkmsBreadcrumbItem.component';
2+
import { OkmsDashboardBreadcrumbItem } from './items/OkmsDashboardBreadcrumbItem.component';
23
import { RootBreadcrumbItem } from './items/RootBreadcrumbItem.component';
34
import { CreateSecretBreadcrumbItem } from './items/CreateSecretBreadcrumbItem.component';
45
import { SecretBreadcrumbItem } from './items/SecretBreadcrumbItem.component';
@@ -8,4 +9,5 @@ export {
89
RootBreadcrumbItem,
910
CreateSecretBreadcrumbItem,
1011
SecretBreadcrumbItem,
12+
OkmsDashboardBreadcrumbItem,
1113
};

packages/manager/apps/okms/src/modules/secret-manager/components/breadcrumb/items/BreadcrumbItem.constants.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ export const BREADCRUMB_ITEM_TEST_IDS = {
33
CREATE_SECRET: 'create-secret-breadcrumb-item',
44
OKMS: 'okms-breadcrumb-item',
55
SECRET: 'secret-breadcrumb-item',
6+
OKMS_DASHBOARD: 'okms-dashboard-breadcrumb-item',
67
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React from 'react';
2+
import { useNavigate, useParams } from 'react-router-dom';
3+
import { OdsBreadcrumbItem } from '@ovhcloud/ods-components/react';
4+
import {
5+
LocationPathParams,
6+
SECRET_MANAGER_ROUTES_URIS,
7+
SECRET_MANAGER_ROUTES_URLS,
8+
} from '@secret-manager/routes/routes.constants';
9+
import { isLocationParamsDefined } from '@secret-manager/utils/locationParams';
10+
import { useTranslation } from 'react-i18next';
11+
import { BREADCRUMB_ITEM_TEST_IDS } from './BreadcrumbItem.constants';
12+
13+
const Item = ({ okmsId }: { okmsId: string }) => {
14+
const { t } = useTranslation('secret-manager');
15+
const navigate = useNavigate();
16+
17+
return (
18+
<OdsBreadcrumbItem
19+
data-testid={BREADCRUMB_ITEM_TEST_IDS.OKMS_DASHBOARD}
20+
key={SECRET_MANAGER_ROUTES_URIS.dashboard}
21+
label={t('okms_dashboard_title')}
22+
onClick={() => navigate(SECRET_MANAGER_ROUTES_URLS.okmsDashboard(okmsId))}
23+
href={null}
24+
/>
25+
);
26+
};
27+
28+
export const OkmsDashboardBreadcrumbItem = () => {
29+
const { okmsId } = useParams<LocationPathParams>();
30+
31+
return isLocationParamsDefined([okmsId]) ? <Item okmsId={okmsId} /> : null;
32+
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const OKMS_DASHBOARD_TEST_IDS = {
2+
okmsDashboardTiles: 'okms-dashboard-tiles',
3+
errorBanner: 'okms-dashboard-error-banner',
4+
};
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import React from 'react';
2+
import { useTranslation } from 'react-i18next';
3+
import { Outlet, useNavigate, useParams } from 'react-router-dom';
4+
import {
5+
BaseLayout,
6+
ErrorBanner,
7+
Notifications,
8+
useNotifications,
9+
} from '@ovh-ux/manager-react-components';
10+
import { OdsBreadcrumb } from '@ovhcloud/ods-components/react';
11+
import {
12+
OkmsBreadcrumbItem,
13+
RootBreadcrumbItem,
14+
} from '@secret-manager/components/breadcrumb';
15+
import { OkmsDashboardBreadcrumbItem } from '@secret-manager/components/breadcrumb/items/OkmsDashboardBreadcrumbItem.component';
16+
import { SecretManagerChangelogButton } from '@secret-manager/components/secretManagerChangelogButton/SecretManagerChangelogButton.component';
17+
import {
18+
LocationPathParams,
19+
SECRET_MANAGER_ROUTES_URLS,
20+
} from '@secret-manager/routes/routes.constants';
21+
import { queryClient } from '@ovh-ux/manager-react-core-application';
22+
import { PageSpinner } from '@/common/components/pageSpinner/PageSpinner.component';
23+
import { OkmsDomainDashboardTiles } from '@/common/components/okmsDashboard/OkmsDomainDashboardTiles.component';
24+
import { useOkmsById } from '@/data/hooks/useOkms';
25+
import { okmsQueryKeys } from '@/data/api/okms';
26+
import { OkmsDashboardOutletContext } from './OkmsDashboard.type';
27+
import { OKMS_DASHBOARD_TEST_IDS } from './OkmsDashboard.constants';
28+
29+
export default function OkmsDashboardPage() {
30+
const { t } = useTranslation('secret-manager');
31+
const navigate = useNavigate();
32+
const { notifications } = useNotifications();
33+
const { okmsId } = useParams<LocationPathParams>();
34+
35+
const { data: okms, isPending, error } = useOkmsById(okmsId);
36+
37+
if (isPending) {
38+
return <PageSpinner />;
39+
}
40+
41+
if (error) {
42+
return (
43+
<ErrorBanner
44+
error={error.response}
45+
onRedirectHome={() => navigate(SECRET_MANAGER_ROUTES_URLS.root)}
46+
onReloadPage={() =>
47+
queryClient.refetchQueries({
48+
queryKey: okmsQueryKeys.detail(okmsId),
49+
})
50+
}
51+
data-testid={OKMS_DASHBOARD_TEST_IDS.errorBanner}
52+
/>
53+
);
54+
}
55+
56+
const contextValue: OkmsDashboardOutletContext = {
57+
okms: okms.data,
58+
};
59+
60+
return (
61+
<BaseLayout
62+
header={{
63+
title: t('okms_dashboard_title'),
64+
changelogButton: <SecretManagerChangelogButton />,
65+
}}
66+
backLinkLabel={t('back_to_secret_list')}
67+
onClickReturn={() => navigate('..')}
68+
breadcrumb={
69+
<OdsBreadcrumb>
70+
<RootBreadcrumbItem />
71+
<OkmsBreadcrumbItem />
72+
<OkmsDashboardBreadcrumbItem />
73+
</OdsBreadcrumb>
74+
}
75+
message={notifications.length > 0 && <Notifications />}
76+
>
77+
<OkmsDomainDashboardTiles okms={okms.data} />
78+
<Outlet context={contextValue} />
79+
</BaseLayout>
80+
);
81+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { SECRET_MANAGER_ROUTES_URLS } from '@secret-manager/routes/routes.constants';
2+
import { assertTextVisibility } from '@ovh-ux/manager-core-test-utils';
3+
import { screen, waitFor } from '@testing-library/react';
4+
import { vi } from 'vitest';
5+
import React from 'react';
6+
import { assertBreadcrumbItems } from '@secret-manager/utils/tests/breadcrumb';
7+
import { okmsMock } from '@/mocks/kms/okms.mock';
8+
import { renderTestApp } from '@/utils/tests/renderTestApp';
9+
import { labels } from '@/utils/tests/init.i18n';
10+
import { PAGE_SPINNER_TEST_ID } from '@/common/components/pageSpinner/PageSpinner.constants';
11+
import { OKMS_DASHBOARD_TEST_IDS } from './OkmsDashboard.constants';
12+
13+
const mockPageUrl = SECRET_MANAGER_ROUTES_URLS.okmsDashboard(okmsMock[0].id);
14+
15+
vi.mock(
16+
'@/common/components/okmsDashboard/OkmsDomainDashboardTiles.component',
17+
async (original) => ({
18+
...(await original()),
19+
OkmsDomainDashboardTiles: vi.fn(() => (
20+
<div data-testid={OKMS_DASHBOARD_TEST_IDS.okmsDashboardTiles} />
21+
)),
22+
}),
23+
);
24+
25+
describe('OkmsDashboard page test suite', () => {
26+
it('should display a PageSpinner while the data is fetched', async () => {
27+
// GIVEN
28+
29+
// WHEN
30+
await renderTestApp(mockPageUrl);
31+
32+
// THEN
33+
await waitFor(
34+
() => expect(screen.getByTestId(PAGE_SPINNER_TEST_ID)).toBeVisible(),
35+
{ timeout: 5000 },
36+
);
37+
});
38+
39+
it('should display an error when there is an API error', async () => {
40+
// GIVEN
41+
42+
// WHEN
43+
await renderTestApp(mockPageUrl, { isOkmsKO: true });
44+
45+
// THEN
46+
await waitFor(
47+
() =>
48+
expect(
49+
screen.getByTestId(OKMS_DASHBOARD_TEST_IDS.errorBanner),
50+
).toBeVisible(),
51+
{
52+
timeout: 5000,
53+
},
54+
);
55+
});
56+
57+
it('should display the page', async () => {
58+
// GIVEN
59+
60+
// WHEN
61+
await renderTestApp(mockPageUrl);
62+
63+
// THEN
64+
await assertTextVisibility(labels.secretManager.okms_dashboard_title);
65+
66+
await assertBreadcrumbItems([
67+
'RootBreadcrumbItem',
68+
'OkmsBreadcrumbItem',
69+
'OkmsDashboardBreadcrumbItem',
70+
]);
71+
72+
expect(
73+
screen.getByTestId(OKMS_DASHBOARD_TEST_IDS.okmsDashboardTiles),
74+
).toBeInTheDocument();
75+
});
76+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { OKMS } from '@/types/okms.type';
2+
3+
export type OkmsDashboardOutletContext = {
4+
okms: OKMS;
5+
};
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from 'react';
2+
import { useParams } from 'react-router-dom';
3+
import { LocationPathParams } from '@secret-manager/routes/routes.constants';
4+
import OkmsTerminateModal from '@/common/components/okmsTerminateModal/OkmsTerminateModal.component';
5+
6+
const OkmsTerminateModalPage = () => {
7+
const { okmsId } = useParams<LocationPathParams>();
8+
9+
return <OkmsTerminateModal okmsId={okmsId} />;
10+
};
11+
12+
export default OkmsTerminateModalPage;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from 'react';
2+
import { useOutletContext } from 'react-router-dom';
3+
import OkmsUpdateNameModal from '@/common/components/okmsUpdateNameModal/OkmsUpdateNameModal.component';
4+
import { OkmsDashboardOutletContext } from '../OkmsDashboard.type';
5+
6+
const OkmsUpdateNameModalPage = () => {
7+
const { okms } = useOutletContext<OkmsDashboardOutletContext>();
8+
9+
return <OkmsUpdateNameModal okms={okms} />;
10+
};
11+
12+
export default OkmsUpdateNameModalPage;

0 commit comments

Comments
 (0)