From c9086f4410b67c77ff3ba6c92046de1fc07fb6a9 Mon Sep 17 00:00:00 2001 From: Paul Dickerson Date: Wed, 13 Nov 2024 15:32:10 +0100 Subject: [PATCH] feat(vcd): add reset password setup ref: MANAGER-15191 Signed-off-by: Paul Dickerson --- .../hpc-vmware-managed-vcd/mocks/index.ts | 1 + .../vcd-datacentre.handler.ts | 71 ++++++++++++++ .../vcd-organization.handler.ts | 75 +++------------ .../dashboard/Messages_fr_FR.json | 6 +- .../apps/hpc-vmware-managed-vcd/src/App.tsx | 5 +- .../layout/VcdDashboardLayout.component.tsx | 9 +- .../components/message/Message.component.tsx | 84 ++++++++++++++++ .../message/MessageList.component.tsx | 15 +++ .../modal/UpdateDetailModalHandler.tsx | 14 +-- ...OrganizationServiceManagementTile.spec.tsx | 5 + .../src/context/Message.context.tsx | 95 +++++++++++++++++++ .../hpc-vmware-managed-vcd-reset-password.ts | 10 ++ .../src/data/hooks/useResetPassword.ts | 16 ++++ .../edit/EditVdcDescription.page.tsx | 14 +-- .../OrganizationGeneralInformation.spec.tsx | 43 +++------ .../edit/EditPassword.page.tsx | 65 +++++++++++++ .../src/routes/routes.constant.ts | 2 + .../src/test-utils/TestApp.tsx | 5 +- .../src/test-utils/index.ts | 1 + .../src/test-utils/render-test.tsx | 4 + .../src/test-utils/uiTestHelpers.ts | 14 ++- .../src/utils/queryKeys.ts | 4 + .../manager/apps/veeam-backup/package.json | 2 +- 23 files changed, 443 insertions(+), 117 deletions(-) create mode 100644 packages/manager/apps/hpc-vmware-managed-vcd/mocks/vcd-organization/vcd-datacentre.handler.ts create mode 100644 packages/manager/apps/hpc-vmware-managed-vcd/src/components/message/Message.component.tsx create mode 100644 packages/manager/apps/hpc-vmware-managed-vcd/src/components/message/MessageList.component.tsx create mode 100644 packages/manager/apps/hpc-vmware-managed-vcd/src/context/Message.context.tsx create mode 100644 packages/manager/apps/hpc-vmware-managed-vcd/src/data/api/hpc-vmware-managed-vcd-reset-password.ts create mode 100644 packages/manager/apps/hpc-vmware-managed-vcd/src/data/hooks/useResetPassword.ts create mode 100644 packages/manager/apps/hpc-vmware-managed-vcd/src/pages/dashboard/organization/general-information/edit/EditPassword.page.tsx diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/mocks/index.ts b/packages/manager/apps/hpc-vmware-managed-vcd/mocks/index.ts index 3d40b59a8819..495b7f1ffc5c 100644 --- a/packages/manager/apps/hpc-vmware-managed-vcd/mocks/index.ts +++ b/packages/manager/apps/hpc-vmware-managed-vcd/mocks/index.ts @@ -1,4 +1,5 @@ export * from './iam/iam.handler'; export * from './vcd-organization/vcd-organization.handler'; +export * from './vcd-organization/vcd-datacentre.handler'; export * from './vcd-organization/vcd-datacentre-order.handler'; export * from './veeam-backup/veeam-backup.handler'; diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/mocks/vcd-organization/vcd-datacentre.handler.ts b/packages/manager/apps/hpc-vmware-managed-vcd/mocks/vcd-organization/vcd-datacentre.handler.ts new file mode 100644 index 000000000000..27e4e84f4f3d --- /dev/null +++ b/packages/manager/apps/hpc-vmware-managed-vcd/mocks/vcd-organization/vcd-datacentre.handler.ts @@ -0,0 +1,71 @@ +import { PathParams } from 'msw'; +import { Handler } from '../../../../../../playwright-helpers'; +import { datacentreList } from './vcd-datacentre.mock'; +import { computeList } from './vdc-compute.mock'; +import { storageList } from './vdc-storage.mock'; + +export type GetDatacentresMocksParams = { + isDatacentresKo?: boolean; + isDatacentreUpdateKo?: boolean; + nbDatacentres?: number; + isComputeKO?: boolean; + nbCompute?: number; + isStorageKO?: boolean; + nbStorage?: number; +}; + +const findDatacentreById = (params: PathParams) => + datacentreList.find(({ id }) => id === params.id); + +export const getDatacentresMocks = ({ + isDatacentresKo, + isDatacentreUpdateKo, + nbDatacentres = Number.POSITIVE_INFINITY, + isComputeKO, + nbCompute = Number.POSITIVE_INFINITY, + isStorageKO, + nbStorage = Number.POSITIVE_INFINITY, +}: GetDatacentresMocksParams): Handler[] => [ + { + url: '/vmwareCloudDirector/organization/:id/virtualDataCenter/:id/storage', + response: isStorageKO + ? { message: 'Storage error' } + : storageList.slice(0, nbStorage), + api: 'v2', + status: isStorageKO ? 500 : 200, + }, + { + url: '/vmwareCloudDirector/organization/:id/virtualDataCenter/:id/compute', + response: isComputeKO + ? { message: 'Compute error' } + : computeList.slice(0, nbCompute), + api: 'v2', + status: isComputeKO ? 500 : 200, + }, + { + url: '/vmwareCloudDirector/organization/:id/virtualDataCenter', + response: isDatacentresKo + ? { message: 'Datacentres error' } + : datacentreList.slice(0, nbDatacentres), + api: 'v2', + status: isDatacentresKo ? 500 : 200, + }, + { + url: '/vmwareCloudDirector/organization/:id/virtualDataCenter/:id', + response: (_: unknown, params: PathParams) => + isDatacentresKo + ? { message: 'Datacentre error' } + : findDatacentreById(params), + api: 'v2', + status: isDatacentresKo ? 500 : 200, + }, + { + url: '/vmwareCloudDirector/organization/:id/virtualDataCenter/:id', + response: isDatacentreUpdateKo + ? { message: 'Datacentre update error' } + : {}, + method: 'put', + api: 'v2', + status: isDatacentreUpdateKo ? 500 : 200, + }, +]; diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/mocks/vcd-organization/vcd-organization.handler.ts b/packages/manager/apps/hpc-vmware-managed-vcd/mocks/vcd-organization/vcd-organization.handler.ts index 68da1e813be3..3009416f69ee 100644 --- a/packages/manager/apps/hpc-vmware-managed-vcd/mocks/vcd-organization/vcd-organization.handler.ts +++ b/packages/manager/apps/hpc-vmware-managed-vcd/mocks/vcd-organization/vcd-organization.handler.ts @@ -1,89 +1,27 @@ import { PathParams } from 'msw'; import { Handler } from '../../../../../../playwright-helpers'; import { organizationList } from './vcd-organization.mock'; -import { datacentreList } from './vcd-datacentre.mock'; -import { computeList } from './vdc-compute.mock'; -import { storageList } from './vdc-storage.mock'; export type GetOrganizationMocksParams = { isOrganizationKo?: boolean; isOrganizationUpdateKo?: boolean; + isOrganizationResetPasswordKo?: boolean; nbOrganization?: number; allOrgsBackedUp?: boolean; - isDatacentresKo?: boolean; - isDatacentreUpdateKo?: boolean; - nbDatacentres?: number; - isComputeKO?: boolean; - nbCompute?: number; - isStorageKO?: boolean; - nbStorage?: number; }; const findOrganizationById = (params: PathParams) => organizationList.find(({ id }) => id === params.id); -const findDatacentreById = (params: PathParams) => - datacentreList.find(({ id }) => id === params.id); - export const getOrganizationMocks = ({ isOrganizationKo, isOrganizationUpdateKo, + isOrganizationResetPasswordKo, nbOrganization = Number.POSITIVE_INFINITY, allOrgsBackedUp, - isDatacentresKo, - isDatacentreUpdateKo, - nbDatacentres = Number.POSITIVE_INFINITY, - isComputeKO, - nbCompute = Number.POSITIVE_INFINITY, - isStorageKO, - nbStorage = Number.POSITIVE_INFINITY, }: GetOrganizationMocksParams): Handler[] => { const nb = allOrgsBackedUp ? 1 : nbOrganization; return [ - { - url: - '/vmwareCloudDirector/organization/:id/virtualDataCenter/:id/storage', - response: isStorageKO - ? { message: 'Storage error' } - : storageList.slice(0, nbStorage), - api: 'v2', - status: isStorageKO ? 500 : 200, - }, - { - url: - '/vmwareCloudDirector/organization/:id/virtualDataCenter/:id/compute', - response: isComputeKO - ? { message: 'Compute error' } - : computeList.slice(0, nbCompute), - api: 'v2', - status: isComputeKO ? 500 : 200, - }, - { - url: '/vmwareCloudDirector/organization/:id/virtualDataCenter', - response: isDatacentresKo - ? { message: 'Datacentres error' } - : datacentreList.slice(0, nbDatacentres), - api: 'v2', - status: isDatacentresKo ? 500 : 200, - }, - { - url: '/vmwareCloudDirector/organization/:id/virtualDataCenter/:id', - response: (_: unknown, params: PathParams) => - isDatacentresKo - ? { message: 'Datacentre error' } - : findDatacentreById(params), - api: 'v2', - status: isDatacentresKo ? 500 : 200, - }, - { - url: '/vmwareCloudDirector/organization/:id/virtualDataCenter/:id', - response: isDatacentreUpdateKo - ? { message: 'Datacentre update error' } - : {}, - method: 'put', - api: 'v2', - status: isDatacentreUpdateKo ? 500 : 200, - }, { url: '/vmwareCloudDirector/organization/:id', response: isOrganizationUpdateKo @@ -93,6 +31,15 @@ export const getOrganizationMocks = ({ api: 'v2', status: isOrganizationUpdateKo ? 500 : 200, }, + { + url: '/vmwareCloudDirector/organization/:id/password', + response: isOrganizationResetPasswordKo + ? { message: 'Organization reset password error' } + : {}, + method: 'post', + api: 'v2', + status: isOrganizationResetPasswordKo ? 500 : 200, + }, { url: '/vmwareCloudDirector/organization/:id', response: (_: unknown, params: PathParams) => diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/public/translations/dashboard/Messages_fr_FR.json b/packages/manager/apps/hpc-vmware-managed-vcd/public/translations/dashboard/Messages_fr_FR.json index c1654ead13d2..9ab63c50698d 100644 --- a/packages/manager/apps/hpc-vmware-managed-vcd/public/translations/dashboard/Messages_fr_FR.json +++ b/packages/manager/apps/hpc-vmware-managed-vcd/public/translations/dashboard/Messages_fr_FR.json @@ -24,7 +24,10 @@ "managed_vcd_dashboard_service_cancellation": "Résilier le service", "managed_vcd_dashboard_password": "Mot de passe", "managed_vcd_dashboard_password_renew": "Renouveler le mot de passe admin", - "managed_vcd_dashboard_password_tooltip": "Si vous souhaitez changer votre mot de passe administrateur, merci de contacter le support", + "managed_vcd_dashboard_password_modal_title": "Changer le mot de passe", + "managed_vcd_dashboard_password_modal_subtitle": "Êtes-vous certain de changer de mot de passe ?", + "managed_vcd_dashboard_password_renew_success": "Vous allez recevoir un email pour visualiser votre mot de passe", + "managed_vcd_dashboard_password_renew_error": "Une erreur est survenue. Veuillez réessayer plus tard ou contacter le support pour changer votre mot de passe administrateur", "managed_vcd_dashboard_back_link": "Retour à la liste", "managed_vcd_dashboard_coming_soon": "Bientôt disponible", "managed_vcd_dashboard_data_protection": "Protection de Données", @@ -38,6 +41,7 @@ "managed_vcd_dashboard_edit_modal_error": "Une erreur est survenue: {{error}}.", "managed_vcd_dashboard_edit_modal_cta_cancel": "Annuler", "managed_vcd_dashboard_edit_modal_cta_edit": "Modifier", + "managed_vcd_dashboard_edit_modal_cta_validate": "Valider", "managed_vcd_dashboard_edit_name_modal_title": "Modifier le nom", "managed_vcd_dashboard_edit_name_modal_label": "Nom", "managed_vcd_dashboard_edit_name_modal_success": "Le nom a été modifié avec succès.", diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/src/App.tsx b/packages/manager/apps/hpc-vmware-managed-vcd/src/App.tsx index 2c333cefe6d7..ec72c8de080e 100644 --- a/packages/manager/apps/hpc-vmware-managed-vcd/src/App.tsx +++ b/packages/manager/apps/hpc-vmware-managed-vcd/src/App.tsx @@ -4,6 +4,7 @@ import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { odsSetup } from '@ovhcloud/ods-common-core'; import { RouterProvider, createHashRouter } from 'react-router-dom'; import { Routes } from './routes/routes'; +import { MessageContextProvider } from './context/Message.context'; odsSetup(); @@ -20,7 +21,9 @@ function App() { return ( - + + + ); diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/src/components/dashboard/layout/VcdDashboardLayout.component.tsx b/packages/manager/apps/hpc-vmware-managed-vcd/src/components/dashboard/layout/VcdDashboardLayout.component.tsx index 6aabda0848d0..d694a86df261 100644 --- a/packages/manager/apps/hpc-vmware-managed-vcd/src/components/dashboard/layout/VcdDashboardLayout.component.tsx +++ b/packages/manager/apps/hpc-vmware-managed-vcd/src/components/dashboard/layout/VcdDashboardLayout.component.tsx @@ -1,9 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { - HeadersProps, - BaseLayout, - Notifications, -} from '@ovh-ux/manager-react-components'; +import { HeadersProps, BaseLayout } from '@ovh-ux/manager-react-components'; import { OsdsTabBar, OsdsTabBarItem, @@ -12,6 +8,7 @@ import { import { NavLink, Outlet, useLocation, useNavigate } from 'react-router-dom'; import Breadcrumb from '@/components/breadcrumb/Breadcrumb.component'; import { BreadcrumbItem } from '@/hooks/breadcrumb/useBreadcrumb'; +import { MessageList } from '@/components/message/MessageList.component'; export type DashboardTabItemProps = { name: string; @@ -74,7 +71,7 @@ export default function VcdDashboardLayout({ } breadcrumb={} - message={} + message={} backLinkLabel={backLinkLabel} onClickReturn={onClickReturn} /> diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/src/components/message/Message.component.tsx b/packages/manager/apps/hpc-vmware-managed-vcd/src/components/message/Message.component.tsx new file mode 100644 index 000000000000..9430a74e2399 --- /dev/null +++ b/packages/manager/apps/hpc-vmware-managed-vcd/src/components/message/Message.component.tsx @@ -0,0 +1,84 @@ +import React, { useEffect } from 'react'; +import { useLocation } from 'react-router-dom'; +import { OsdsMessage, OsdsText } from '@ovhcloud/ods-components/react'; +import { ODS_THEME_TYPOGRAPHY_SIZE } from '@ovhcloud/ods-common-theming'; +import { NotificationType } from '@ovh-ux/manager-react-components'; +import { + ODS_MESSAGE_TYPE, + ODS_TEXT_COLOR_INTENT, +} from '@ovhcloud/ods-components'; +import { MessageType, useMessageContext } from '@/context/Message.context'; + +type MessageProps = { + message: MessageType; +}; + +const messageColors = { + [NotificationType.Success]: ODS_MESSAGE_TYPE.success, + [NotificationType.Error]: ODS_MESSAGE_TYPE.error, + [NotificationType.Warning]: ODS_MESSAGE_TYPE.warning, + [NotificationType.Info]: ODS_MESSAGE_TYPE.info, +}; +const textColors = { + [NotificationType.Success]: ODS_TEXT_COLOR_INTENT.success, + [NotificationType.Error]: ODS_TEXT_COLOR_INTENT.error, + [NotificationType.Warning]: ODS_TEXT_COLOR_INTENT.warning, + [NotificationType.Info]: ODS_TEXT_COLOR_INTENT.info, +}; + +const getMessageColor = (type: NotificationType) => + messageColors[type] || ODS_MESSAGE_TYPE.info; + +const getTextColor = (type: NotificationType) => + textColors[type] || ODS_TEXT_COLOR_INTENT.info; + +export const Message: React.FC = ({ message }) => { + const { pathname } = useLocation(); + const { clearMessage } = useMessageContext(); + const { + content, + uid, + type, + persistent, + includedSubRoutes, + excludedSubRoutes, + duration, + } = message; + + useEffect(() => { + if ( + !includedSubRoutes.every((route) => pathname.includes(route)) || + excludedSubRoutes.some((route) => pathname.includes(route)) + ) { + clearMessage(uid); + } + }, [uid, includedSubRoutes, excludedSubRoutes, pathname]); + + useEffect(() => { + if (!duration) return; + const durationTimeout = setTimeout(() => clearMessage(uid), duration); + + // eslint-disable-next-line consistent-return + return () => clearTimeout(durationTimeout); + }, [duration]); + + return ( + clearMessage(uid), + })} + > + + {content} + + + ); +}; diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/src/components/message/MessageList.component.tsx b/packages/manager/apps/hpc-vmware-managed-vcd/src/components/message/MessageList.component.tsx new file mode 100644 index 000000000000..369fe886f023 --- /dev/null +++ b/packages/manager/apps/hpc-vmware-managed-vcd/src/components/message/MessageList.component.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { useMessageContext } from '@/context/Message.context'; +import { Message } from './Message.component'; + +export const MessageList: React.FC = () => { + const { messages } = useMessageContext(); + + return ( + <> + {messages.map((message) => ( + + ))} + + ); +}; diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/src/components/modal/UpdateDetailModalHandler.tsx b/packages/manager/apps/hpc-vmware-managed-vcd/src/components/modal/UpdateDetailModalHandler.tsx index e5092bce2b54..de8a6f62f73e 100644 --- a/packages/manager/apps/hpc-vmware-managed-vcd/src/components/modal/UpdateDetailModalHandler.tsx +++ b/packages/manager/apps/hpc-vmware-managed-vcd/src/components/modal/UpdateDetailModalHandler.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { useNotifications } from '@ovh-ux/manager-react-components'; import useManagedVcdOrganization from '@/data/hooks/useManagedVcdOrganization'; import { useUpdateVcdOrganizationDetails } from '@/data/hooks/useUpdateVcdOrganization'; import { IVcdOrganizationState } from '@/types/vcd-organization.interface'; @@ -10,6 +9,8 @@ import { validateOrganizationName, } from '@/utils/formValidation'; import { EditDetailModal } from './EditDetailModal'; +import { useMessageContext } from '@/context/Message.context'; +import { subRoutes } from '@/routes/routes.constant'; type OrganizationDetailName = 'name' | 'description'; type TValidationFunctions = { @@ -27,16 +28,17 @@ export const UpdateDetailModalHandler = ({ const { t } = useTranslation('dashboard'); const navigate = useNavigate(); const closeModal = () => navigate('..'); - const { addSuccess } = useNotifications(); + const { addSuccess } = useMessageContext(); const { id } = useParams(); const { data: vcdOrganization } = useManagedVcdOrganization({ id }); const { updateDetails, error, isError } = useUpdateVcdOrganizationDetails({ id, onSuccess: () => { - addSuccess( - t(`managed_vcd_dashboard_edit_${detailName}_modal_success`), - true, - ); + addSuccess({ + content: t(`managed_vcd_dashboard_edit_${detailName}_modal_success`), + includedSubRoutes: [id], + excludedSubRoutes: [subRoutes.datacentres], + }); closeModal(); }, }); diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/src/components/tiles/organization-service-tile/OrganizationServiceManagementTile.spec.tsx b/packages/manager/apps/hpc-vmware-managed-vcd/src/components/tiles/organization-service-tile/OrganizationServiceManagementTile.spec.tsx index b9fbbdd16567..33468b19f3c7 100644 --- a/packages/manager/apps/hpc-vmware-managed-vcd/src/components/tiles/organization-service-tile/OrganizationServiceManagementTile.spec.tsx +++ b/packages/manager/apps/hpc-vmware-managed-vcd/src/components/tiles/organization-service-tile/OrganizationServiceManagementTile.spec.tsx @@ -12,6 +12,11 @@ import { } from '@ovh-ux/manager-react-shell-client'; import OrganizationServiceManagementTile from './OrganizationServiceManagementTile.component'; +vi.mock('react-router-dom', () => ({ + useNavigate: () => ({ navigate: vi.fn() }), + useParams: () => ({ id: 'id' }), +})); + const shellContext = { environment: { getUser: vi.fn(), diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/src/context/Message.context.tsx b/packages/manager/apps/hpc-vmware-managed-vcd/src/context/Message.context.tsx new file mode 100644 index 000000000000..f1aecf0cefda --- /dev/null +++ b/packages/manager/apps/hpc-vmware-managed-vcd/src/context/Message.context.tsx @@ -0,0 +1,95 @@ +import { NotificationType } from '@ovh-ux/manager-react-components/src/components/notifications/useNotifications'; +import React, { + createContext, + FC, + PropsWithChildren, + ReactNode, + useContext, + useMemo, + useState, +} from 'react'; + +export type MessageType = { + uid: number; + content: ReactNode; + type: NotificationType; + persistent?: boolean; + includedSubRoutes?: string[]; + excludedSubRoutes?: string[]; + duration?: number; +}; + +type AddMessageProps = Omit; +type AddSpecificMessageProps = Omit; + +export type MessageContextType = { + messages: MessageType[]; + addSuccess: (props: AddSpecificMessageProps) => void; + addError: (props: AddSpecificMessageProps) => void; + addWarning: (props: AddSpecificMessageProps) => void; + addInfo: (props: AddSpecificMessageProps) => void; + clearMessage: (uid: number) => void; + clearMessages: () => void; +}; + +const MessageContext = createContext({ + messages: [], + addSuccess: () => null, + addError: () => null, + addWarning: () => null, + addInfo: () => null, + clearMessage: () => null, + clearMessages: () => null, +}); + +export const MessageContextProvider: FC = ({ children }) => { + const [messages, setMessages] = useState([]); + + const addMessage = (props: AddMessageProps) => { + setMessages((prevList) => [ + ...prevList, + { + uid: Date.now(), + includedSubRoutes: props.includedSubRoutes || [], + excludedSubRoutes: props.excludedSubRoutes || [], + ...props, + }, + ]); + }; + const deleteMessage = (id: number) => { + setMessages((prevList) => prevList.filter((msg) => msg.uid !== id)); + }; + + const messageContextValue: MessageContextType = useMemo( + () => ({ + messages, + addSuccess: (props: AddSpecificMessageProps) => + addMessage({ type: NotificationType.Success, ...props }), + addError: (props: AddSpecificMessageProps) => + addMessage({ type: NotificationType.Error, ...props }), + addInfo: (props: AddSpecificMessageProps) => + addMessage({ type: NotificationType.Info, ...props }), + addWarning: (props: AddSpecificMessageProps) => + addMessage({ type: NotificationType.Warning, ...props }), + clearMessage: (id) => deleteMessage(id), + clearMessages: () => setMessages([]), + }), + [messages], + ); + + return ( + + {children} + + ); +}; + +export const useMessageContext = (): MessageContextType => { + const context = useContext(MessageContext); + if (context === undefined) { + throw new Error( + 'useMessageContext must be used within a MessageContextProvider', + ); + } + return context; +}; diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/src/data/api/hpc-vmware-managed-vcd-reset-password.ts b/packages/manager/apps/hpc-vmware-managed-vcd/src/data/api/hpc-vmware-managed-vcd-reset-password.ts new file mode 100644 index 000000000000..724ad903b770 --- /dev/null +++ b/packages/manager/apps/hpc-vmware-managed-vcd/src/data/api/hpc-vmware-managed-vcd-reset-password.ts @@ -0,0 +1,10 @@ +import { apiClient, ApiResponse } from '@ovh-ux/manager-core-api'; +import { VCD_ORGANIZATION_ROUTE } from './hpc-vmware-managed-vcd.constants'; + +const getResetPasswordRoute = (id: string) => + `${VCD_ORGANIZATION_ROUTE}/${id}/password`; + +export const resetOrganizationPassword = ( + id: string, +): Promise> => + apiClient.v2.post(getResetPasswordRoute(id)); diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/src/data/hooks/useResetPassword.ts b/packages/manager/apps/hpc-vmware-managed-vcd/src/data/hooks/useResetPassword.ts new file mode 100644 index 000000000000..db118380b031 --- /dev/null +++ b/packages/manager/apps/hpc-vmware-managed-vcd/src/data/hooks/useResetPassword.ts @@ -0,0 +1,16 @@ +import { useMutation, UseMutationOptions } from '@tanstack/react-query'; +import { ApiError, ApiResponse } from '@ovh-ux/manager-core-api'; +import { resetOrganizationPassword } from '../api/hpc-vmware-managed-vcd-reset-password'; +import { getVcdOrganizationResetPasswordQueryKey } from '@/utils/queryKeys'; + +export const useResetPassword = ( + { id }: { id: string }, + options?: Partial< + UseMutationOptions, ApiError, void, unknown> + >, +) => + useMutation({ + mutationKey: getVcdOrganizationResetPasswordQueryKey(id), + mutationFn: () => resetOrganizationPassword(id), + ...options, + }); diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/src/pages/dashboard/datacentre/general-informations/edit/EditVdcDescription.page.tsx b/packages/manager/apps/hpc-vmware-managed-vcd/src/pages/dashboard/datacentre/general-informations/edit/EditVdcDescription.page.tsx index f1a618d9c607..7bc9b462e3a9 100644 --- a/packages/manager/apps/hpc-vmware-managed-vcd/src/pages/dashboard/datacentre/general-informations/edit/EditVdcDescription.page.tsx +++ b/packages/manager/apps/hpc-vmware-managed-vcd/src/pages/dashboard/datacentre/general-informations/edit/EditVdcDescription.page.tsx @@ -1,28 +1,30 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate, useParams } from 'react-router-dom'; -import { useNotifications } from '@ovh-ux/manager-react-components'; import { useManagedVcdDatacentre } from '@/data/hooks/useManagedVcdDatacentres'; import { useUpdateVdcDetails } from '@/data/hooks/useUpdateVcdDatacentre'; import { validateDescription } from '@/utils/formValidation'; import { IVcdDatacentreState } from '@/types/vcd-datacenter.interface'; import { EditDetailModal } from '@/components/modal/EditDetailModal'; +import { useMessageContext } from '@/context/Message.context'; +import { subRoutes } from '@/routes/routes.constant'; export default function EditVdcDescription() { const { t } = useTranslation('dashboard'); const navigate = useNavigate(); const closeModal = () => navigate('..'); - const { addSuccess } = useNotifications(); + const { addSuccess } = useMessageContext(); const { id, vdcId } = useParams(); const { data: vcdDatacentre } = useManagedVcdDatacentre(id, vdcId); const { updateDetails, error, isError } = useUpdateVdcDetails({ id, vdcId, onSuccess: () => { - addSuccess( - t('managed_vcd_dashboard_edit_description_modal_success'), - true, - ); + addSuccess({ + content: t('managed_vcd_dashboard_edit_description_modal_success'), + includedSubRoutes: [id], + excludedSubRoutes: [subRoutes.datacentres], + }); closeModal(); }, }); diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/src/pages/dashboard/organization/general-information/OrganizationGeneralInformation.spec.tsx b/packages/manager/apps/hpc-vmware-managed-vcd/src/pages/dashboard/organization/general-information/OrganizationGeneralInformation.spec.tsx index 15c81490486e..c5314a0d14e8 100644 --- a/packages/manager/apps/hpc-vmware-managed-vcd/src/pages/dashboard/organization/general-information/OrganizationGeneralInformation.spec.tsx +++ b/packages/manager/apps/hpc-vmware-managed-vcd/src/pages/dashboard/organization/general-information/OrganizationGeneralInformation.spec.tsx @@ -7,6 +7,7 @@ import { mockSubmitNewValue, checkModalError, DEFAULT_TIMEOUT, + checkTextVisibility, } from '../../../../test-utils'; import { organizationList } from '../../../../../mocks/vcd-organization/vcd-organization.mock'; @@ -19,21 +20,15 @@ describe('Organization General Information Page', () => { initialRoute: `/${organizationList[1].id}`, }); - await waitFor( - () => - expect( - screen.getByText( - labels.dashboard.managed_vcd_dashboard_data_protection, - ), - ).toBeVisible(), - { timeout: DEFAULT_TIMEOUT }, + await checkTextVisibility( + labels.dashboard.managed_vcd_dashboard_data_protection, ); let editButton; await waitFor( () => { editButton = screen.getAllByTestId('editIcon').at(0); - return expect(editButton).not.toHaveAttribute('disabled'); + return expect(editButton).toBeEnabled(); }, { timeout: DEFAULT_TIMEOUT }, ); @@ -44,12 +39,9 @@ describe('Organization General Information Page', () => { await mockSubmitNewValue({ submitButtonLabel }); await checkModalVisibility({ container, isVisible: false }); - - expect( - screen.queryByText( - labels.dashboard.managed_vcd_dashboard_edit_name_modal_success, - ), - ).toBeVisible(); + await checkTextVisibility( + labels.dashboard.managed_vcd_dashboard_edit_name_modal_success, + ); }); it('trying to update name displays an error if update organization service is KO', async () => { @@ -71,21 +63,15 @@ describe('Organization General Information Page', () => { initialRoute: `/${organizationList[1].id}`, }); - await waitFor( - () => - expect( - screen.getByText( - labels.dashboard.managed_vcd_dashboard_data_protection, - ), - ).toBeVisible(), - { timeout: DEFAULT_TIMEOUT }, + await checkTextVisibility( + labels.dashboard.managed_vcd_dashboard_data_protection, ); let editButton; await waitFor( () => { editButton = screen.getAllByTestId('editIcon').at(1); - return expect(editButton).not.toHaveAttribute('disabled'); + return expect(editButton).toBeEnabled(); }, { timeout: DEFAULT_TIMEOUT }, ); @@ -96,12 +82,9 @@ describe('Organization General Information Page', () => { await mockSubmitNewValue({ submitButtonLabel }); await checkModalVisibility({ container, isVisible: false }); - - expect( - screen.queryByText( - labels.dashboard.managed_vcd_dashboard_edit_description_modal_success, - ), - ).toBeVisible(); + await checkTextVisibility( + labels.dashboard.managed_vcd_dashboard_edit_description_modal_success, + ); }); it('trying to update description displays an error if update organization service is KO', async () => { diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/src/pages/dashboard/organization/general-information/edit/EditPassword.page.tsx b/packages/manager/apps/hpc-vmware-managed-vcd/src/pages/dashboard/organization/general-information/edit/EditPassword.page.tsx new file mode 100644 index 000000000000..5426f12aa3b4 --- /dev/null +++ b/packages/manager/apps/hpc-vmware-managed-vcd/src/pages/dashboard/organization/general-information/edit/EditPassword.page.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import { OsdsModal, OsdsButton } from '@ovhcloud/ods-components/react'; +import { Description } from '@ovh-ux/manager-react-components'; +import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming'; +import { ODS_BUTTON_VARIANT } from '@ovhcloud/ods-components'; +import { useResetPassword } from '@/data/hooks/useResetPassword'; +import { useMessageContext } from '@/context/Message.context'; +import { subRoutes } from '@/routes/routes.constant'; + +export default function EditPassword() { + const { t } = useTranslation('dashboard'); + const navigate = useNavigate(); + const closeModal = () => navigate('..'); + const { id } = useParams(); + const { addSuccess, addError } = useMessageContext(); + const { mutate: resetPassword } = useResetPassword( + { id }, + { + onSettled: () => closeModal(), + onSuccess: () => + addSuccess({ + content: t('managed_vcd_dashboard_password_renew_success'), + includedSubRoutes: [id], + excludedSubRoutes: [subRoutes.datacentres], + }), + onError: () => + addError({ + content: t('managed_vcd_dashboard_password_renew_error'), + includedSubRoutes: [id], + excludedSubRoutes: [subRoutes.datacentres], + }), + }, + ); + + return ( + + + {t('managed_vcd_dashboard_password_modal_subtitle')} + + + {t('managed_vcd_dashboard_edit_modal_cta_cancel')} + + resetPassword()} + > + {t('managed_vcd_dashboard_edit_modal_cta_validate')} + + + ); +} diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/src/routes/routes.constant.ts b/packages/manager/apps/hpc-vmware-managed-vcd/src/routes/routes.constant.ts index 1eb746e97450..6a72bceb5a1e 100644 --- a/packages/manager/apps/hpc-vmware-managed-vcd/src/routes/routes.constant.ts +++ b/packages/manager/apps/hpc-vmware-managed-vcd/src/routes/routes.constant.ts @@ -8,6 +8,7 @@ export const subRoutes = { datacentres: 'datacentres', datacentreStorage: 'storage', datacentreCompute: 'compute', + resetPassword: 'reset-password', }; export const urls = { @@ -15,6 +16,7 @@ export const urls = { listing: '/', onboarding: `/${subRoutes.onboarding}`, dashboard: `/${subRoutes.dashboard}`, + resetPassword: `/${subRoutes.dashboard}/${subRoutes.resetPassword}`, editName: `/${subRoutes.dashboard}/${subRoutes.editName}`, editDescription: `/${subRoutes.dashboard}/${subRoutes.editDescription}`, datacentres: `/${subRoutes.dashboard}/${subRoutes.datacentres}`, diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/src/test-utils/TestApp.tsx b/packages/manager/apps/hpc-vmware-managed-vcd/src/test-utils/TestApp.tsx index f2fe8695cbc1..99fa0eaedc42 100644 --- a/packages/manager/apps/hpc-vmware-managed-vcd/src/test-utils/TestApp.tsx +++ b/packages/manager/apps/hpc-vmware-managed-vcd/src/test-utils/TestApp.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; import { createMemoryRouter, RouterProvider } from 'react-router-dom'; import { Routes } from '../routes/routes'; +import { MessageContextProvider } from '@/context/Message.context'; export function TestApp({ initialRoute = '/' }) { const router = createMemoryRouter(Routes, { @@ -15,7 +16,9 @@ export function TestApp({ initialRoute = '/' }) { return ( - + + + ); } diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/src/test-utils/index.ts b/packages/manager/apps/hpc-vmware-managed-vcd/src/test-utils/index.ts index 1304c9bc2038..27f202fe11ad 100644 --- a/packages/manager/apps/hpc-vmware-managed-vcd/src/test-utils/index.ts +++ b/packages/manager/apps/hpc-vmware-managed-vcd/src/test-utils/index.ts @@ -4,6 +4,7 @@ export { DEFAULT_TIMEOUT, DEFAULT_LISTING_ERROR, checkTextVisibility, + waitForEnabledElement, mockEditInputValue, mockSubmitNewValue, checkModalVisibility, diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/src/test-utils/render-test.tsx b/packages/manager/apps/hpc-vmware-managed-vcd/src/test-utils/render-test.tsx index 4ab566b5cee6..1c85cc7ac150 100644 --- a/packages/manager/apps/hpc-vmware-managed-vcd/src/test-utils/render-test.tsx +++ b/packages/manager/apps/hpc-vmware-managed-vcd/src/test-utils/render-test.tsx @@ -19,6 +19,8 @@ import { getVeeamBackupMocks, getOrganizationMocks, GetOrganizationMocksParams, + getDatacentresMocks, + GetDatacentresMocksParams, getDatacentreOrderMocks, GetDatacentreOrderMocksParams, GetVeeamBackupMocksParams, @@ -37,6 +39,7 @@ export const renderTest = async ({ }: { initialRoute?: string; } & GetOrganizationMocksParams & + GetDatacentresMocksParams & GetDatacentreOrderMocksParams & GetVeeamBackupMocksParams & GetServicesMocksParams = {}) => { @@ -45,6 +48,7 @@ export const renderTest = async ({ ...getAuthenticationMocks({ isAuthMocked: true }), ...getVeeamBackupMocks(mockParams), ...getOrganizationMocks(mockParams), + ...getDatacentresMocks(mockParams), ...getDatacentreOrderMocks(mockParams), ...getIamMocks(), ...getServicesMocks(mockParams), diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/src/test-utils/uiTestHelpers.ts b/packages/manager/apps/hpc-vmware-managed-vcd/src/test-utils/uiTestHelpers.ts index 9870fe869fa0..72b8d1c0a686 100644 --- a/packages/manager/apps/hpc-vmware-managed-vcd/src/test-utils/uiTestHelpers.ts +++ b/packages/manager/apps/hpc-vmware-managed-vcd/src/test-utils/uiTestHelpers.ts @@ -12,10 +12,11 @@ import { expect } from 'vitest'; export const DEFAULT_TIMEOUT = 30_000; export const DEFAULT_LISTING_ERROR = 'An error occured while fetching data'; +// General helpers /** * @description Standard check: wait and expect some text to be visible on the screen * @param text expected to be visible - * @param timeout time to wait for + * @param timeout time to wait for (default to 30sec) * @returns */ export const checkTextVisibility = ( @@ -26,6 +27,17 @@ export const checkTextVisibility = ( timeout, }); +export const waitForEnabledElement = async ( + elementLabel: string, +): Promise => { + let button; + await waitFor(() => { + button = screen.getByText(elementLabel); + return expect(button).toBeEnabled(); + }); + return button; +}; + // Form helpers export const mockEditInputValue = async (value: string) => { const input = screen.getByLabelText('edit-input'); diff --git a/packages/manager/apps/hpc-vmware-managed-vcd/src/utils/queryKeys.ts b/packages/manager/apps/hpc-vmware-managed-vcd/src/utils/queryKeys.ts index 5ed87dc0edf6..4386c620355c 100644 --- a/packages/manager/apps/hpc-vmware-managed-vcd/src/utils/queryKeys.ts +++ b/packages/manager/apps/hpc-vmware-managed-vcd/src/utils/queryKeys.ts @@ -12,6 +12,10 @@ export const getVcdOrganizationBackupQueryKey = (id: string) => [ ...getVcdOrganizationQueryKey(id), 'backup', ]; +export const getVcdOrganizationResetPasswordQueryKey = (id: string) => [ + ...getVcdOrganizationQueryKey(id), + 'resetPassword', +]; export const getVcdCatalogQueryKey = (serviceName: string) => [ 'order', 'cartServiceOption', diff --git a/packages/manager/apps/veeam-backup/package.json b/packages/manager/apps/veeam-backup/package.json index 47f9c5138108..398805554833 100644 --- a/packages/manager/apps/veeam-backup/package.json +++ b/packages/manager/apps/veeam-backup/package.json @@ -29,7 +29,7 @@ "@ovh-ux/manager-react-core-application": "^0.11.1", "@ovh-ux/manager-react-shell-client": "^0.8.1", "@ovh-ux/manager-tailwind-config": "^0.2.1", - "@ovh-ux/manager-module-order": "^0.6.3", + "@ovh-ux/manager-module-order": "^0.7.1", "@ovh-ux/request-tagger": "^0.4.0", "@ovhcloud/ods-common-core": "17.2.2", "@ovhcloud/ods-common-theming": "17.2.2",