Skip to content

Commit

Permalink
feat(web.office): add licences datagrid
Browse files Browse the repository at this point in the history
ref: MANAGER-16124

Signed-off-by: Guillaume Hyenne <[email protected]>
  • Loading branch information
ghyenne committed Dec 10, 2024
1 parent bf15336 commit 023fd12
Show file tree
Hide file tree
Showing 32 changed files with 662 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ export default function WebSidebar() {
id: 'web-office',
label: t('sidebar_license_office'),
icon: getIcon('ms-Icon ms-Icon--OfficeLogo'),
routeMatcher: new RegExp(`^/web-office`),
routeMatcher: new RegExp('^/web-office'),
async loader() {
const services = await loadServices('/license/office');
return [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"title": "Dashboard page",
"error_service": "No services info",
"general_informations": "Informations générales",
"tab2": "Tab 2",
"microsoft_office_dashboard_consumption": "Consommation",
"microsoft_office_dashboard_licences": "Licences",
"back_link": "Retour à la liste"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"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_status": "Statut",
"dashboard_users_download_text": "Les programmes d'installation pour PC et MAC sont disponibles sur le site de microsoft <officeLink/>.",
"dashboard_users_download_info": "Pour smartphones et tablettes, rendez-vous directement dans votre App Store.",
"dashboard_users_download_id": "Les identifiants de connexion et d'installation vous sont transmis par email lorsque vous ajoutez une nouvelle licence.",
"dashboard_users_status_creating": "En création",
"dashboard_users_status_updating": "Mise à jour",
"dashboard_users_status_deleting": "En suppression",
"dashboard_users_status_ok": "Créé",
"dashboard_users_status_unconfigured": "Non configuré",
"dashboard_users_action_user_change_password": "Changer le mot de passe",
"dashboard_users_action_user_edit": "Editer le compte",
"dashboard_users_action_user_delete": "Supprimer le compte"
}
5 changes: 3 additions & 2 deletions packages/manager/apps/web-office/src/api/_mock_/license.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { UserStateEnum } from '../api.type';
import { LicenseType } from '../license/type';

export const licensesMock: LicenseType[] = [
Expand All @@ -11,7 +12,7 @@ export const licensesMock: LicenseType[] = [
phone: '0033123456789',
serviceName: 'user123.o365.ovh.com',
serviceType: 'payAsYouGo',
status: 'active',
status: UserStateEnum.OK,
zipCode: '75001',
iam: {
id: '12345abc-6789-def0-gh12-3456789ijklm',
Expand All @@ -28,7 +29,7 @@ export const licensesMock: LicenseType[] = [
phone: '0033123456789',
serviceName: 'topuser125.o365.ovh.com',
serviceType: 'payAsYouGo',
status: 'active',
status: UserStateEnum.OK,
zipCode: '59000',
iam: {
id: '12345abc-6789-def0-gh12-3456789ijklm',
Expand Down
14 changes: 14 additions & 0 deletions packages/manager/apps/web-office/src/api/api.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export enum UserStateEnum {
CREATING = 'creating',
DELETING = 'deleting',
OK = 'ok',
UNCONFIGURED = 'unconfigured',
SUSPENDED = 'suspended',
SUSPENDING = 'suspending',
UNSUSPENDING = 'unsuspending',
}

export enum LicenseEnum {
OFFICE_BUSINESS = 'officeBusiness',
OFFICE_PRO_PLUS = 'officeProPlus',
}
21 changes: 20 additions & 1 deletion packages/manager/apps/web-office/src/api/license/api.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { v6 } from '@ovh-ux/manager-core-api';
import { GetOfficeLicenseServiceParams } from './type';
import { getApiPath } from '../utils/apiPath';
import { getApiPath, getApiPathWithoutServiceName } from '../utils/apiPath';

// GET

Expand All @@ -13,6 +13,25 @@ export const getOfficeLicenseDetails = async (serviceName: string) => {
return data;
};

export const getOfficePrepaidLicenseDetails = async (serviceName: string) => {
const { data } = await v6.get(`${getApiPath(serviceName)}`);
return data;
};

export const getOfficeLicenses = async (serviceName: string) => {
const { data } = await v6.get<string[]>(
getApiPathWithoutServiceName(serviceName),
);
return data;
};

export const getOfficePrepaidLicenses = async (serviceName: string) => {
const { data } = await v6.get<string[]>(
getApiPathWithoutServiceName(serviceName),
);
return data;
};

// POST

// PUT
Expand Down
8 changes: 6 additions & 2 deletions packages/manager/apps/web-office/src/api/license/key.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { getApiPath } from '../utils/apiPath';

export const getOfficeLicenseDetailsQueryKey = (serviceName: string) => [
`get/license/office/${serviceName}`,
`get/${getApiPath(serviceName)}${serviceName}`,
];

export const getOfficeLicenseQueryKey = () => [`get/license/office`];
export const getOfficeLicenseQueryKey = (serviceName: string) => [
`get/${getApiPath(serviceName)}`,
];
23 changes: 21 additions & 2 deletions packages/manager/apps/web-office/src/api/license/type.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { LicenseEnum, UserStateEnum } from '../api.type';

export type LicenseType = {
address: string;
city: string;
Expand All @@ -8,13 +10,30 @@ export type LicenseType = {
phone: string;
serviceName: string;
serviceType: string;
status: string;
status: UserStateEnum;
zipCode: string;
iam: {
id: string;
urn: string;
};
[key: string]: string | { id: string; urn: string };
[key: string]: string | number | boolean | { id: string; urn: string };
};

export type LicensePrepaidType = {
activationEmail: string;
firstName: string;
isVirtual: boolean;
lastName: string;
licences: LicenseEnum;
serviceName: string;
status: UserStateEnum;
taskPendingId: number;
tenantServiceName: string;
usageLocation: string;
iam: {
id: string;
urn: string;
};
};

export type GetOfficeLicenseServiceParams = {
Expand Down
21 changes: 21 additions & 0 deletions packages/manager/apps/web-office/src/api/users/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { fetchIcebergV6 } from '@ovh-ux/manager-core-api';
import { getApiPath } from '../utils/apiPath';
import { UserNativeType } from './type';

// GET

export const getOfficeUsers = async (
serviceName: string,
): Promise<UserNativeType[]> => {
const { data } = await fetchIcebergV6<UserNativeType>({
route: `${getApiPath(serviceName)}user`,
disableCache: true,
});
return data;
};

// POST

// PUT

// DELETE
3 changes: 3 additions & 0 deletions packages/manager/apps/web-office/src/api/users/key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const getOfficeUsersQueryKey = (serviceName: string) => [
`get/license/office/${serviceName}/user`,
];
14 changes: 14 additions & 0 deletions packages/manager/apps/web-office/src/api/users/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { LicenseEnum, UserStateEnum } from '../api.type';

export type UserNativeType = {
activationEmail: string;
deleteAtExpiration: boolean;
firstName: string;
isVirtual: boolean;
lastName: string;
licences: LicenseEnum;
status: UserStateEnum;
taskPendingId: number;
usageLocation: string;
[key: string]: string | number | boolean | { id: string; urn: string };
};
17 changes: 15 additions & 2 deletions packages/manager/apps/web-office/src/api/utils/apiPath.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,15 @@
export const getApiPath = (serviceName: string) =>
`/license/office/${serviceName}/`;
import { useOfficeServiceType } from '@/hooks';

export const getApiPath = (serviceName: string) => {
const serviceType = useOfficeServiceType(serviceName);
return `/license/${
serviceType === 'payAsYouGo' ? 'office' : 'officePrepaid'
}/${serviceName}/`;
};

export const getApiPathWithoutServiceName = (serviceName: string) => {
const serviceType = useOfficeServiceType(serviceName);
return `/license/${
serviceType === 'payAsYouGo' ? 'office' : 'officePrepaid'
}/`;
};
36 changes: 36 additions & 0 deletions packages/manager/apps/web-office/src/components/BadgeStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React, { useMemo } from 'react';
import { OdsBadge } from '@ovhcloud/ods-components/react';
import { ODS_BADGE_COLOR } from '@ovhcloud/ods-components';
import { UserStateEnum } from '@/api/api.type';

export type BadgeStatusProps = {
itemStatus: string;
t: any;
translationPrefixKey: string;
'data-testid'?: string;
};

const getStatusColor = (status: string) => {
switch (status) {
case UserStateEnum.OK:
return ODS_BADGE_COLOR.success;
case UserStateEnum.DELETING:
return ODS_BADGE_COLOR.critical;
default:
return ODS_BADGE_COLOR.information;
}
};

export const BadgeStatus: React.FC<BadgeStatusProps> = (props) => {
const statusColor = useMemo(() => getStatusColor(props.itemStatus), [
props.itemStatus,
]);

return (
<OdsBadge
data-testid={props['data-testid']}
color={statusColor}
label={props.t(`${props.translationPrefixKey}${props.itemStatus}`)}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -18,40 +18,34 @@ export const Breadcrumb: React.FC<BreadcrumbProps> = () => {
const location = useLocation();

const rootUrl = serviceName
? '#/:serviceName'.replace(':serviceName', '')
? '#/license/:serviceName'.replace(':serviceName', '')
: '#/';

const breadcrumbItems = useMemo(() => {
const pathParts = location.pathname.split('/').filter(Boolean);
const breadcrumbParts = pathParts.slice(1);
const breadcrumbParts = pathParts.slice(2);
return [
{
label: 'Microsoft 365',
href: rootUrl,
},
...(breadcrumbParts.length === 1
? [
{
label: serviceName,
href: `${rootUrl}`,
},
]
: breadcrumbParts.map((_, index) => {
const label =
index === breadcrumbParts.length - 1
? serviceName
: t(
`microsoft_office__dashboard_${breadcrumbParts
.slice(0, index + 1)
.join('_')}`,
);
{
label: pathParts[1],
href: `#/${pathParts.slice(0, 2).join('/')}`,
},
...breadcrumbParts.map((_, index) => {
const label = t(
`microsoft_office_dashboard_${breadcrumbParts
.slice(0, index + 1)
.join('_')}`,
);

const url = `#/${pathParts.slice(0, index + 2).join('/')}`;
return {
label,
href: url,
};
})),
const url = `#/${pathParts.slice(0, index + 2).join('/')}`;
return {
label,
href: url,
};
}),
].filter(Boolean);
}, [location, serviceName, rootUrl, t]);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React, { useState, useEffect } from 'react';
import { NavLink, useLocation, useNavigate } from 'react-router-dom';
import { OdsTabs, OdsTab } from '@ovhcloud/ods-components/react';

export type TabItemProps = {
name: string;
title: string;
pathMatchers?: RegExp[];
to: string;
};

export type TabsProps = {
tabs: TabItemProps[];
};

const TabsPanel: React.FC<TabsProps> = ({ tabs }) => {
const [activePanel, setActivePanel] = useState('');
const location = useLocation();
const navigate = useNavigate();

useEffect(() => {
if (!location.pathname) {
setActivePanel(tabs[0].name);
navigate(tabs[0].to);
} else {
const activeTab = tabs
.filter(
(tab) =>
tab.to === location.pathname ||
tab.pathMatchers?.some((pathMatcher) =>
pathMatcher.test(location.pathname),
),
)
.pop();
if (activeTab) {
setActivePanel(activeTab.name);
}
}
}, [location.pathname]);

return (
<OdsTabs>
{tabs.map((tab: TabItemProps) => (
<NavLink
key={`osds-tab-bar-item-${tab.name}`}
to={tab.to}
className="no-underline"
>
<OdsTab
id={tab.name}
role="tab"
isSelected={activePanel === tab.name}
>
{tab.title}
</OdsTab>
</NavLink>
))}
</OdsTabs>
);
};

export default TabsPanel;
Loading

0 comments on commit 023fd12

Please sign in to comment.