diff --git a/packages/manager-react-components/src/components/content/headers/headers.component.tsx b/packages/manager-react-components/src/components/content/headers/headers.component.tsx
index 7ef38e8368f1..3e9305f01580 100644
--- a/packages/manager-react-components/src/components/content/headers/headers.component.tsx
+++ b/packages/manager-react-components/src/components/content/headers/headers.component.tsx
@@ -3,7 +3,7 @@ import { OdsText } from '@ovhcloud/ods-components/react';
import { Title, Subtitle } from '../../typography';
export interface HeadersProps {
- title?: string;
+ title?: React.ReactElement | string;
subtitle?: string;
description?: string;
headerButton?: React.ReactElement;
diff --git a/packages/manager/apps/web-office/public/translations/dashboard/Messages_fr_FR.json b/packages/manager/apps/web-office/public/translations/dashboard/Messages_fr_FR.json
index 090a4cf35d72..657b9e1b86e6 100644
--- a/packages/manager/apps/web-office/public/translations/dashboard/Messages_fr_FR.json
+++ b/packages/manager/apps/web-office/public/translations/dashboard/Messages_fr_FR.json
@@ -2,6 +2,13 @@
"title": "Dashboard page",
"error_service": "No services info",
"microsoft_office_dashboard_consumption": "Consommation",
- "microsoft_office_dashboard_licences": "Licences",
+ "microsoft_office_dashboard_licenses_group": "Groupes de licences",
+ "microsoft_office_dashboard_licenses": "Licences",
+ "microsoft_office_dashboard_edit-name": "Éditer",
+ "microsoft_office_modal_update_headline": "Changer le nom",
+ "microsoft_office_modal_update_description": "Veuilliez saisir le nouveau nom que vous souhaitez donner à votre service.",
+ "microsoft_office_modal_update_input_label": "Nouveau nom",
+ "microsoft_office_modal_update_confirm": "Confirmer",
+ "microsoft_office_modal_update_cancel": "Annuler",
"back_link": "Retour à la liste"
}
diff --git a/packages/manager/apps/web-office/public/translations/dashboard/users/Messages_fr_FR.json b/packages/manager/apps/web-office/public/translations/dashboard/users/Messages_fr_FR.json
index 795956650d38..71db7055d169 100644
--- a/packages/manager/apps/web-office/public/translations/dashboard/users/Messages_fr_FR.json
+++ b/packages/manager/apps/web-office/public/translations/dashboard/users/Messages_fr_FR.json
@@ -2,7 +2,7 @@
"dashboard_users_table_firstName": "Prénom",
"dashboard_users_table_lastName": " Nom",
"dashboard_users_table_activationEmail": "Email d'activation",
- "dashboard_users_table_licences": "Licences",
+ "dashboard_users_table_licenses": "Licences",
"dashboard_users_table_status": "Statut",
"dashboard_users_download_text": "Les programmes d'installation pour PC et MAC sont disponibles sur le site de microsoft .",
"dashboard_users_download_info": "Pour smartphones et tablettes, rendez-vous directement dans votre App Store.",
diff --git a/packages/manager/apps/web-office/src/api/license/api.ts b/packages/manager/apps/web-office/src/api/license/api.ts
index 6c3be779716b..494797972020 100644
--- a/packages/manager/apps/web-office/src/api/license/api.ts
+++ b/packages/manager/apps/web-office/src/api/license/api.ts
@@ -1,12 +1,12 @@
import { v6 } from '@ovh-ux/manager-core-api';
-import { GetOfficeLicenseServiceParams } from './type';
import { getApiPath, getApiPathWithoutServiceName } from '../utils/apiPath';
+import { LicenseType } from './type';
// GET
-export const getlicenseOfficeServiceQueryKey = (
- params: GetOfficeLicenseServiceParams,
-) => [`get/license/office/${params.serviceName}`];
+export const getlicenseOfficeServiceQueryKey = (serviceName: string) => [
+ `get/license/office/${serviceName}`,
+];
export const getOfficeLicenseDetails = async (serviceName: string) => {
const { data } = await v6.get(`${getApiPath(serviceName)}`);
@@ -35,5 +35,12 @@ export const getOfficePrepaidLicenses = async (serviceName: string) => {
// POST
// PUT
+export const updateOfficeLicenseDetails = async (
+ serviceName: string,
+ licenseData: Partial,
+): Promise => {
+ const { data } = await v6.put(`${getApiPath(serviceName)}`, licenseData);
+ return data;
+};
// DELETE
diff --git a/packages/manager/apps/web-office/src/api/parentTenant/api.ts b/packages/manager/apps/web-office/src/api/parentTenant/api.ts
new file mode 100644
index 000000000000..a710ac5fb483
--- /dev/null
+++ b/packages/manager/apps/web-office/src/api/parentTenant/api.ts
@@ -0,0 +1,28 @@
+import { v6 } from '@ovh-ux/manager-core-api';
+import { getApiPath } from '../utils/apiPath';
+import { ParentTenantType } from './type';
+
+// GET
+export const getParentTenant = async (
+ serviceName: string,
+): Promise => {
+ const { data } = await v6.get(
+ `${getApiPath(serviceName)}parentTenant`,
+ );
+ return data;
+};
+// POST
+
+// PUT
+export const updateParentTenant = async (
+ serviceName: string,
+ parentTenantData: Partial,
+): Promise => {
+ const { data } = await v6.put(
+ `${getApiPath(serviceName)}parentTenant`,
+ parentTenantData,
+ );
+ return data;
+};
+
+// DELETE
diff --git a/packages/manager/apps/web-office/src/api/parentTenant/key.ts b/packages/manager/apps/web-office/src/api/parentTenant/key.ts
new file mode 100644
index 000000000000..13ff4c442574
--- /dev/null
+++ b/packages/manager/apps/web-office/src/api/parentTenant/key.ts
@@ -0,0 +1,3 @@
+export const getOfficeParentTenantQueryKey = (serviceName: string) => [
+ `get/license/officePrepaid/${serviceName}/parentTenant`,
+];
diff --git a/packages/manager/apps/web-office/src/api/parentTenant/type.ts b/packages/manager/apps/web-office/src/api/parentTenant/type.ts
new file mode 100644
index 000000000000..1b6b20c635d8
--- /dev/null
+++ b/packages/manager/apps/web-office/src/api/parentTenant/type.ts
@@ -0,0 +1,15 @@
+import { UserStateEnum } from '../api.type';
+
+export type ParentTenantType = {
+ address: string;
+ city: string;
+ creationDate: string;
+ displayName: string;
+ firstName: string;
+ lastName: string;
+ phone: string;
+ serviceName: string;
+ serviceType: string;
+ status: UserStateEnum;
+ zipCode: string;
+};
diff --git a/packages/manager/apps/web-office/src/components/Breadcrumb/Breadcrumb.tsx b/packages/manager/apps/web-office/src/components/Breadcrumb/Breadcrumb.tsx
index 57d6eff8aa9b..8c434f387bb7 100644
--- a/packages/manager/apps/web-office/src/components/Breadcrumb/Breadcrumb.tsx
+++ b/packages/manager/apps/web-office/src/components/Breadcrumb/Breadcrumb.tsx
@@ -33,7 +33,7 @@ export const Breadcrumb: React.FC = () => {
label: pathParts[1],
href: `#/${pathParts.slice(0, 2).join('/')}`,
},
- ...breadcrumbParts.map((_, index) => {
+ ...breadcrumbParts.map((_: any, index: number) => {
const label = t(
`microsoft_office_dashboard_${breadcrumbParts
.slice(0, index + 1)
diff --git a/packages/manager/apps/web-office/src/hooks/index.ts b/packages/manager/apps/web-office/src/hooks/index.ts
index e14379ea2f19..3230a0166921 100644
--- a/packages/manager/apps/web-office/src/hooks/index.ts
+++ b/packages/manager/apps/web-office/src/hooks/index.ts
@@ -3,3 +3,4 @@ export * from './useOfficeLicenseDetails';
export * from './useGenerateUrl';
export * from './useOfficeServiceType';
export * from './useOfficeUsers';
+export * from './officeParentTenant/getOfficeParentTenant';
diff --git a/packages/manager/apps/web-office/src/hooks/officeParentTenant/getOfficeParentTenant.ts b/packages/manager/apps/web-office/src/hooks/officeParentTenant/getOfficeParentTenant.ts
new file mode 100644
index 000000000000..0bc2e339ea52
--- /dev/null
+++ b/packages/manager/apps/web-office/src/hooks/officeParentTenant/getOfficeParentTenant.ts
@@ -0,0 +1,26 @@
+import { useQuery } from '@tanstack/react-query';
+import { useParams } from 'react-router-dom';
+import { useOfficeServiceType } from '../useOfficeServiceType';
+import { getParentTenant } from '@/api/parentTenant/api';
+import {
+ getlicenseOfficeServiceQueryKey,
+ getOfficeLicenseDetails,
+} from '@/api/license/api';
+import { getOfficeParentTenantQueryKey } from '@/api/parentTenant/key';
+
+export const getOfficeParentTenant = () => {
+ const { serviceName } = useParams();
+ const serviceType = useOfficeServiceType(serviceName);
+ const isPrepaid = serviceType === 'prepaid';
+
+ return useQuery({
+ queryKey: [
+ isPrepaid
+ ? getOfficeParentTenantQueryKey(serviceName)
+ : getlicenseOfficeServiceQueryKey(serviceName),
+ ],
+ queryFn: isPrepaid
+ ? () => getParentTenant(serviceName)
+ : () => getOfficeLicenseDetails(serviceName),
+ });
+};
diff --git a/packages/manager/apps/web-office/src/hooks/useGenerateUrl.ts b/packages/manager/apps/web-office/src/hooks/useGenerateUrl.ts
index eb36dba84aab..7855d6f1e380 100644
--- a/packages/manager/apps/web-office/src/hooks/useGenerateUrl.ts
+++ b/packages/manager/apps/web-office/src/hooks/useGenerateUrl.ts
@@ -1,32 +1,17 @@
import { useHref } from 'react-router-dom';
-import { useOfficeLicenseDetail } from '@/hooks';
export const UseGenerateUrl = (
baseURL: string,
type: 'path' | 'href' = 'path',
params?: Record,
) => {
- const { data: serviceName } = useOfficeLicenseDetail();
-
const URL = baseURL.replace(
':serviceName',
(params?.serviceName as string) || '',
);
- const queryParams = {
- ...params,
- ...(serviceName && { serviceNameDetail: serviceName }),
- };
-
- const queryString = Object.entries(queryParams)
- .filter(([key]) => key !== 'serviceName')
- .map(([key, value]) => `${key}=${value}`)
- .join('&');
-
- const fullURL = queryString ? `${URL}?${queryString}` : URL;
-
if (type === 'href') {
- return useHref(fullURL);
+ return useHref(URL);
}
- return fullURL;
+ return URL;
};
diff --git a/packages/manager/apps/web-office/src/hooks/useOfficeLicenseDetails.ts b/packages/manager/apps/web-office/src/hooks/useOfficeLicenseDetails.ts
index 4b98d3ae2f7a..594aa96536b4 100644
--- a/packages/manager/apps/web-office/src/hooks/useOfficeLicenseDetails.ts
+++ b/packages/manager/apps/web-office/src/hooks/useOfficeLicenseDetails.ts
@@ -12,9 +12,9 @@ export const useOfficeLicenseDetail = (
const { serviceName: selectedServiceName } = useParams();
return useQuery({
- queryKey: getlicenseOfficeServiceQueryKey({
- serviceName: serviceName || selectedServiceName,
- }),
+ queryKey: getlicenseOfficeServiceQueryKey(
+ serviceName || selectedServiceName,
+ ),
queryFn: () =>
getOfficeLicenseDetails(serviceName || selectedServiceName || ''),
enabled: Boolean(serviceName || selectedServiceName),
diff --git a/packages/manager/apps/web-office/src/pages/dashboard/components/UpdateDisplayNameModal.component.tsx b/packages/manager/apps/web-office/src/pages/dashboard/components/UpdateDisplayNameModal.component.tsx
new file mode 100644
index 000000000000..ae5c8cf7f13c
--- /dev/null
+++ b/packages/manager/apps/web-office/src/pages/dashboard/components/UpdateDisplayNameModal.component.tsx
@@ -0,0 +1,65 @@
+import React from 'react';
+import { useTranslation } from 'react-i18next';
+import { UpdateNameModal } from '@ovh-ux/manager-react-components';
+import { useNavigate, useParams } from 'react-router-dom';
+import { useMutation } from '@tanstack/react-query';
+import {
+ getOfficeParentTenant,
+ UseGenerateUrl,
+ useOfficeServiceType,
+} from '@/hooks';
+import queryClient from '@/queryClient';
+import { ParentTenantType } from '@/api/parentTenant/type';
+import { updateOfficeLicenseDetails } from '@/api/license/api';
+import { updateParentTenant } from '@/api/parentTenant/api';
+import { LicenseType } from '@/api/license/type';
+import { getOfficeParentTenantQueryKey } from '@/api/parentTenant/key';
+import { getOfficeLicenseDetailsQueryKey } from '@/api/license/key';
+
+export default function UpdateDisplayNameModal() {
+ const navigate = useNavigate();
+ const { t } = useTranslation('dashboard');
+ const { serviceName } = useParams();
+ const serviceType = useOfficeServiceType(serviceName);
+ const isPrepaid = serviceType === 'prepaid';
+ const { data: getData, refetch } = getOfficeParentTenant();
+
+ const goBackUrl = UseGenerateUrl('..', 'path');
+ const closeModal = () => {
+ navigate(goBackUrl, { replace: true });
+ };
+ const { mutate: editName, isPending } = useMutation({
+ mutationFn: isPrepaid
+ ? (parentTenantData: Partial) =>
+ updateParentTenant(serviceName, parentTenantData)
+ : (licenseData: Partial) =>
+ updateOfficeLicenseDetails(serviceName, licenseData),
+ onSuccess: () => {
+ queryClient.invalidateQueries({
+ queryKey: isPrepaid
+ ? getOfficeParentTenantQueryKey(serviceName)
+ : getOfficeLicenseDetailsQueryKey(serviceName),
+ });
+ closeModal();
+ refetch();
+ },
+ });
+ const handleSaveClick = (displayName: string) => {
+ editName({ displayName });
+ };
+
+ return (
+
+ );
+}
diff --git a/packages/manager/apps/web-office/src/pages/dashboard/index.tsx b/packages/manager/apps/web-office/src/pages/dashboard/index.tsx
index 5e826b783bf0..cb2bcdefaa44 100644
--- a/packages/manager/apps/web-office/src/pages/dashboard/index.tsx
+++ b/packages/manager/apps/web-office/src/pages/dashboard/index.tsx
@@ -1,11 +1,23 @@
-import React from 'react';
+import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
-import { Outlet, useParams, useResolvedPath } from 'react-router-dom';
-
-import { BaseLayout } from '@ovh-ux/manager-react-components';
+import {
+ Outlet,
+ useNavigate,
+ useParams,
+ useResolvedPath,
+} from 'react-router-dom';
+import { BaseLayout, UpdateNameModal } from '@ovh-ux/manager-react-components';
+import { OdsButton, OdsText } from '@ovhcloud/ods-components/react';
+import {
+ ODS_BUTTON_VARIANT,
+ ODS_ICON_NAME,
+ ODS_TEXT_PRESET,
+} from '@ovhcloud/ods-components';
import { Breadcrumb } from '@/components/Breadcrumb/Breadcrumb';
import { urls } from '@/routes/routes.constants';
import TabsPanel from '@/components/layout-helpers/Dashboard/TabsPanel';
+import { getOfficeParentTenant, UseGenerateUrl } from '@/hooks';
+import { ParentTenantType } from '@/api/parentTenant/type';
export type DashboardTabItemProps = {
name: string;
@@ -21,7 +33,18 @@ export type DashboardLayoutProps = {
export default function DashboardPage() {
const { serviceName } = useParams();
const { t } = useTranslation('dashboard');
+ const navigate = useNavigate();
+
const basePath = useResolvedPath('').pathname;
+ const { data } = getOfficeParentTenant() as {
+ data?: ParentTenantType;
+ };
+
+ const hrefEditName = UseGenerateUrl('./edit-name', 'path');
+
+ const handleEditNamelick = () => {
+ navigate(hrefEditName);
+ };
function computePathMatchers(routes: string[]) {
return routes.map(
@@ -31,8 +54,8 @@ export default function DashboardPage() {
const tabsList: DashboardTabItemProps[] = [
{
- name: 'licence',
- title: t('microsoft_office_dashboard_licences'),
+ name: 'license',
+ title: t('microsoft_office_dashboard_licenses'),
to: basePath,
pathMatchers: computePathMatchers([urls.license]),
},
@@ -45,7 +68,26 @@ export default function DashboardPage() {
];
const header = {
- title: serviceName,
+ title: (
+ <>
+
+ {t('microsoft_office_dashboard_licenses_group')}
+
+
+
{data?.displayName}
+
+
+
+ {data?.serviceName}
+
+ >
+ ),
};
return (
diff --git a/packages/manager/apps/web-office/src/pages/dashboard/users/ActionButtonUsers.component.tsx b/packages/manager/apps/web-office/src/pages/dashboard/users/ActionButtonUsers.component.tsx
index 5099863e0df5..dc2a2279a009 100644
--- a/packages/manager/apps/web-office/src/pages/dashboard/users/ActionButtonUsers.component.tsx
+++ b/packages/manager/apps/web-office/src/pages/dashboard/users/ActionButtonUsers.component.tsx
@@ -9,11 +9,11 @@ import { LicenseType } from '@/api/license/type';
interface ActionButtonUsersProps {
usersItem: UserNativeType;
- licenceDetail: LicenseType;
+ licenseDetail: LicenseType;
}
const ActionButtonUsers: React.FC = ({
usersItem,
- licenceDetail,
+ licenseDetail,
}) => {
const { t } = useTranslation('dashboard/users');
@@ -39,7 +39,7 @@ const ActionButtonUsers: React.FC = ({
id: 1,
onclick: handlePasswordChangeClick,
label: t('dashboard_users_action_user_change_password'),
- urn: licenceDetail.iam.urn,
+ urn: licenseDetail.iam.urn,
iamActions: [IAM_ACTIONS.user.edit],
},
]
@@ -48,7 +48,7 @@ const ActionButtonUsers: React.FC = ({
id: 2,
onclick: handleEditUserClick,
label: t('dashboard_users_action_user_edit'),
- urn: licenceDetail.iam.urn,
+ urn: licenseDetail.iam.urn,
iamActions: [IAM_ACTIONS.user.edit],
},
...(!usersItem.isVirtual
@@ -57,7 +57,7 @@ const ActionButtonUsers: React.FC = ({
id: 3,
onclick: handleDeleteUserClick,
label: t('dashboard_users_action_user_delete'),
- urn: licenceDetail.iam.urn,
+ urn: licenseDetail.iam.urn,
iamActions: [IAM_ACTIONS.user.delete],
color: ODS_BUTTON_COLOR.critical,
},
diff --git a/packages/manager/apps/web-office/src/pages/dashboard/users/index.tsx b/packages/manager/apps/web-office/src/pages/dashboard/users/index.tsx
index f2b03c3c5926..0c8a1c4566b8 100644
--- a/packages/manager/apps/web-office/src/pages/dashboard/users/index.tsx
+++ b/packages/manager/apps/web-office/src/pages/dashboard/users/index.tsx
@@ -3,6 +3,7 @@ import { Trans, useTranslation } from 'react-i18next';
import { Datagrid, DatagridColumn } from '@ovh-ux/manager-react-components';
import { ODS_ICON_NAME, ODS_TEXT_PRESET } from '@ovhcloud/ods-components';
import { OdsLink, OdsText } from '@ovhcloud/ods-components/react';
+import { Outlet } from 'react-router-dom';
import { UserNativeType } from '@/api/users/type';
import Loading from '@/components/Loading/Loading';
import { useOfficeLicenseDetail, useOfficeUsers } from '@/hooks';
@@ -15,7 +16,7 @@ export default function License() {
const { data: dataUsers, isLoading: isLoadingUsers } = useOfficeUsers();
const {
- data: dataLicenceDetail,
+ data: dataLicenseDetail,
isLoading: isLoadingLicenceDetail,
} = useOfficeLicenseDetail();
@@ -48,11 +49,11 @@ export default function License() {
label: 'dashboard_users_table_activationEmail',
},
{
- id: 'licences',
+ id: 'licenses',
cell: (item) => (
{item.licences}
),
- label: 'dashboard_users_table_licences',
+ label: 'dashboard_users_table_licenses',
},
{
id: 'status',
@@ -81,7 +82,7 @@ export default function License() {
cell: (item) => (
),
label: '',
@@ -121,6 +122,7 @@ export default function License() {
className="mt-4"
/>
)}
+
);
}
diff --git a/packages/manager/apps/web-office/src/pages/licences/licences.page.tsx b/packages/manager/apps/web-office/src/pages/licenses/licenses.page.tsx
similarity index 100%
rename from packages/manager/apps/web-office/src/pages/licences/licences.page.tsx
rename to packages/manager/apps/web-office/src/pages/licenses/licenses.page.tsx
diff --git a/packages/manager/apps/web-office/src/pages/licences/licences.spec.tsx b/packages/manager/apps/web-office/src/pages/licenses/licenses.spec.tsx
similarity index 94%
rename from packages/manager/apps/web-office/src/pages/licences/licences.spec.tsx
rename to packages/manager/apps/web-office/src/pages/licenses/licenses.spec.tsx
index 3f0c7b172fd1..eb26febca891 100644
--- a/packages/manager/apps/web-office/src/pages/licences/licences.spec.tsx
+++ b/packages/manager/apps/web-office/src/pages/licenses/licenses.spec.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import { render, screen } from '@/utils/test.provider';
-import Licenses from './licences.page';
+import Licenses from './licenses.page';
describe('Licenses Page', () => {
it('should render page with content', async () => {
diff --git a/packages/manager/apps/web-office/src/queryClient.ts b/packages/manager/apps/web-office/src/queryClient.ts
new file mode 100644
index 000000000000..94100b5328ad
--- /dev/null
+++ b/packages/manager/apps/web-office/src/queryClient.ts
@@ -0,0 +1,11 @@
+import { QueryClient } from '@tanstack/react-query';
+
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ staleTime: 300_000,
+ },
+ },
+});
+
+export default queryClient;
diff --git a/packages/manager/apps/web-office/src/routes/routes.tsx b/packages/manager/apps/web-office/src/routes/routes.tsx
index 844c79775969..4adaa49033e7 100644
--- a/packages/manager/apps/web-office/src/routes/routes.tsx
+++ b/packages/manager/apps/web-office/src/routes/routes.tsx
@@ -23,7 +23,7 @@ export const Routes: any = [
children: [
{
path: urls.listing,
- ...lazyRouteConfig(() => import('@/pages/licences/licences.page')),
+ ...lazyRouteConfig(() => import('@/pages/licenses/licenses.page')),
handle: {
tracking: {
pageName: 'licenses',
@@ -36,7 +36,6 @@ export const Routes: any = [
...lazyRouteConfig(() => import('@/pages/dashboard')),
children: [
{
- id: 'licence',
path: '',
...lazyRouteConfig(() => import('@/pages/dashboard/users')),
handle: {
@@ -45,9 +44,24 @@ export const Routes: any = [
pageType: PageType.dashboard,
},
},
+ children: [
+ {
+ path: 'edit-name',
+ ...lazyRouteConfig(() =>
+ import(
+ '@/pages/dashboard/components/UpdateDisplayNameModal.component'
+ ),
+ ),
+ handle: {
+ tracking: {
+ pageName: 'edit-name',
+ pageType: PageType.dashboard,
+ },
+ },
+ },
+ ],
},
{
- id: 'consumption',
path: 'consumption',
...lazyRouteConfig(() => import('@/pages/dashboard/consumption')),
handle: {