Skip to content

Commit

Permalink
feat(web.office): delete user modal
Browse files Browse the repository at this point in the history
ref: MANAGER-16127

Signed-off-by: Guillaume Hyenne <[email protected]>
  • Loading branch information
ghyenne committed Jan 9, 2025
1 parent 5a85803 commit ee86648
Show file tree
Hide file tree
Showing 19 changed files with 406 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@
"microsoft_office_dashboard_consumption": "Consommation",
"microsoft_office_dashboard_licences": "Licences",
"microsoft_office_dashboard_guides": "Consulter nos guides en ligne",
"microsoft_office_dashboard_users": "Utilisateurs",
"microsoft_office_dashboard_users_delete": "Suppression d'un utilisateur",
"back_link": "Retour à la liste"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"dashboard_users_delete_title": "Suppression d'un utilisateur",
"dashboard_users_delete_confirm": "Êtes-vous sûr de vouloir supprimer l'utilisateur <strong>{{t0}}</strong> ?",
"dashboard_users_delete_info1": "L'utilisateur sera supprimé immédiatement après confirmation.",
"dashboard_users_delete_info2": "La facturation de nos produits Office 365 étant basée sur la consommation du mois précédent, vous recevrez une dernière facture pour ce compte.",
"dashboard_users_delete_cta_cancel": "Annuler",
"dashboard_users_delete_cta_confirm": "Valider",
"dashboard_users_delete_message_error": "Une erreur est survenue lors de la suppression de l'utilisateur. {{ error }}",
"dashboard_users_delete_message_success": "Utilisateur en cours de suppression."
}
11 changes: 10 additions & 1 deletion packages/manager/apps/web-office/src/api/_mock_/license.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { LicenseEnum, UserStateEnum } from '../api.type';
import { LicenseEnum, TaskStatusEnum, UserStateEnum } from '../api.type';
import { LicensePrepaidType, LicenseType } from '../license/type';
import { PendingTaskType } from '../users';

export const licensesMock: LicenseType[] = [
{
Expand Down Expand Up @@ -79,3 +80,11 @@ export const licensesPrepaidExpandedMock: LicensePrepaidType[] = [
},
},
];

export const tenantPendingTask: PendingTaskType = {
finishDate: '2025-01-09T12:00:12+01:00',
function: 'unconfigureOffice365UserNCE',
id: 581562,
status: TaskStatusEnum.DONE,
todoDate: '2025-01-09T12:00:12+01:00',
};
12 changes: 10 additions & 2 deletions packages/manager/apps/web-office/src/api/_mock_/user.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { LicenseEnum, UserStateEnum } from '../api.type';
import { UserNativeType } from '../users/type';
import { LicenseEnum, TaskStatusEnum, UserStateEnum } from '../api.type';
import { PendingTaskType, UserNativeType } from '../users/type';

export const usersMock: UserNativeType[] = [
{
Expand All @@ -25,3 +25,11 @@ export const usersMock: UserNativeType[] = [
usageLocation: 'fr',
},
];

export const pendingTask: PendingTaskType = {
finishDate: null,
function: 'deleteOffice365UserPaygNCE',
id: 581553,
status: TaskStatusEnum.TODO,
todoDate: '2025-01-09T11:20:52+01:00',
};
8 changes: 8 additions & 0 deletions packages/manager/apps/web-office/src/api/api.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,11 @@ export enum LicenseEnum {
OFFICE_BUSINESS = 'officeBusiness',
OFFICE_PRO_PLUS = 'officeProPlus',
}

export enum TaskStatusEnum {
CANCELLED = 'cancelled',
DOING = 'doing',
DONE = 'done',
ERROR = 'error',
TODO = 'todo',
}
12 changes: 12 additions & 0 deletions packages/manager/apps/web-office/src/api/license/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ export const getOfficePrepaidLicenses = async (serviceName: string) => {

// POST

export const postOfficePrepaidLicenseUnconfigure = async (
serviceName: string,
activationEmail: string,
) => {
const { data } = await v6.post(
`${getApiPathWithoutServiceName(
serviceName,
)}${activationEmail}/unconfigure`,
);
return data;
};

// PUT

// DELETE
12 changes: 11 additions & 1 deletion packages/manager/apps/web-office/src/api/users/api.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fetchIcebergV6 } from '@ovh-ux/manager-core-api';
import { fetchIcebergV6, v6 } from '@ovh-ux/manager-core-api';
import { getApiPath } from '../utils/apiPath';
import { UserNativeType } from './type';

Expand All @@ -19,3 +19,13 @@ export const getOfficeUsers = async (
// PUT

// DELETE

export const deleteOfficeUser = async (
serviceName: string,
activationEmail: string,
) => {
const { data } = await v6.delete(
`${getApiPath(serviceName)}user/${activationEmail}`,
);
return data;
};
10 changes: 9 additions & 1 deletion packages/manager/apps/web-office/src/api/users/type.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { LicenseEnum, UserStateEnum } from '../api.type';
import { LicenseEnum, TaskStatusEnum, UserStateEnum } from '../api.type';

export type UserNativeType = {
activationEmail: string;
Expand All @@ -17,3 +17,11 @@ export type UserNativeType = {
| LicenseEnum[]
| { id: string; urn: string };
};

export type PendingTaskType = {
finishDate?: string;
function: string;
id: number;
status: TaskStatusEnum;
todoDate: string;
};
100 changes: 100 additions & 0 deletions packages/manager/apps/web-office/src/components/Modals/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React from 'react';
import { OdsButton, OdsModal, OdsText } from '@ovhcloud/ods-components/react';
import {
ODS_BUTTON_VARIANT,
ODS_MODAL_COLOR,
ODS_BUTTON_COLOR,
ODS_TEXT_PRESET,
} from '@ovhcloud/ods-components';
import Loading from '@/components/Loading/Loading';

export interface ButtonType {
testid?: string;
action: () => void;
label: string;
isDisabled?: boolean;
isLoading?: boolean;
variant?: ODS_BUTTON_VARIANT;
}

export interface ModalProps {
title?: string;
color?: ODS_MODAL_COLOR;
isDismissible?: boolean;
isLoading?: boolean;
isOpen?: boolean;
onClose?: () => void;
children?: React.ReactElement;
primaryButton?: ButtonType;
secondaryButton?: ButtonType;
}

const mapModalColorToButtonColor = (modalColor: ODS_MODAL_COLOR) => {
if (modalColor === ODS_MODAL_COLOR.critical) {
return ODS_BUTTON_COLOR.critical;
}
return ODS_BUTTON_COLOR.primary;
};

const Modal: React.FC<ModalProps> = ({
color = ODS_MODAL_COLOR.information,
isDismissible,
onClose,
isLoading,
primaryButton,
secondaryButton,
children,
isOpen,
title,
}) => {
const buttonColor = mapModalColorToButtonColor(color);

return (
<OdsModal
data-testid="modal"
color={color}
isDismissible={isDismissible}
className="text-left"
onOdsClose={onClose}
isOpen={isOpen}
>
<OdsText className="mb-4" preset={ODS_TEXT_PRESET.heading4}>
{title}
</OdsText>
{!isLoading && <div className="flex flex-col text-left">{children}</div>}
{isLoading && <Loading />}
{secondaryButton && (
<OdsButton
{...(secondaryButton.testid
? { 'data-testid': secondaryButton.testid }
: {})}
slot="actions"
color={buttonColor}
onClick={!secondaryButton.isDisabled ? secondaryButton.action : null}
isDisabled={secondaryButton.isDisabled}
isLoading={secondaryButton.isLoading}
variant={secondaryButton.variant ?? ODS_BUTTON_VARIANT.outline}
label={secondaryButton.label}
className="mt-4"
/>
)}
{primaryButton && (
<OdsButton
{...(primaryButton.testid
? { 'data-testid': primaryButton.testid }
: {})}
slot="actions"
color={buttonColor}
onClick={!primaryButton.isDisabled ? primaryButton.action : null}
isDisabled={primaryButton.isDisabled}
isLoading={primaryButton.isLoading || isLoading}
variant={primaryButton.variant ?? ODS_BUTTON_VARIANT.default}
label={primaryButton.label}
className="mt-4"
/>
)}
</OdsModal>
);
};

export default Modal;
6 changes: 1 addition & 5 deletions packages/manager/apps/web-office/src/hooks/useGenerateUrl.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
import { useHref } from 'react-router-dom';
import { useOfficeLicenseDetail } from '@/hooks';

export const UseGenerateUrl = (
export const useGenerateUrl = (
baseURL: string,
type: 'path' | 'href' = 'path',
params?: Record<string, string | number>,
) => {
const { data: serviceName } = useOfficeLicenseDetail();

const URL = baseURL.replace(
':serviceName',
(params?.serviceName as string) || '',
);

const queryParams = {
...params,
...(serviceName && { serviceNameDetail: serviceName }),
};

const queryString = Object.entries(queryParams)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { useTranslation } from 'react-i18next';
import { Outlet, useParams, useResolvedPath } from 'react-router-dom';
import {
BaseLayout,
Notifications,
useNotifications,
GuideButton,
GuideItem,
} from '@ovh-ux/manager-react-components';
Expand All @@ -26,6 +28,7 @@ export type DashboardLayoutProps = {
export default function DashboardPage() {
const { serviceName } = useParams();
const { t } = useTranslation('dashboard');
const { notifications } = useNotifications();
const basePath = useResolvedPath('').pathname;
const context = useContext(ShellContext);
const { ovhSubsidiary } = context.environment.getUser();
Expand Down Expand Up @@ -69,6 +72,10 @@ export default function DashboardPage() {
breadcrumb={<Breadcrumb />}
header={header}
tabs={<TabsPanel tabs={tabsList} />}
message={
// temporary fix margin even if empty
notifications.length ? <Notifications /> : null
}
>
<Outlet />
</BaseLayout>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import { ActionMenu } from '@ovh-ux/manager-react-components';
import { ODS_BUTTON_COLOR, ODS_BUTTON_VARIANT } from '@ovhcloud/ods-components';
import { useNavigate } from 'react-router-dom';
import { IAM_ACTIONS } from '@/utils/iamAction.constants';
import { UserStateEnum } from '@/api/api.type';
import { UserNativeType } from '@/api/users/type';
import { LicenseType } from '@/api/license/type';
import { useGenerateUrl } from '@/hooks';

interface ActionButtonUsersProps {
usersItem: UserNativeType;
Expand All @@ -16,16 +18,21 @@ const ActionButtonUsers: React.FC<ActionButtonUsersProps> = ({
licenceDetail,
}) => {
const { t } = useTranslation('dashboard/users');
const navigate = useNavigate();

const hrefDeleteUsers = useGenerateUrl('./users/delete', 'path', {
activationEmail: usersItem.activationEmail,
...(!licenceDetail.serviceType && {
licencePrepaidName: licenceDetail.serviceName,
}),
});

const handlePasswordChangeClick = () => {
// @todo: for next user story
console.log('handlePasswordChangeClick');
};

const handleDeleteUserClick = () => {
// @todo: for next user story
console.log('handleDeleteUserClick');
};
const handleDeleteUserClick = () => navigate(hrefDeleteUsers);

const handleEditUserClick = () => {
// @todo: for next user story
Expand Down
Loading

0 comments on commit ee86648

Please sign in to comment.