diff --git a/packages/manager/apps/pci-kubernetes/package.json b/packages/manager/apps/pci-kubernetes/package.json
index 4e0a3aa53d4d..bc2cc9f04338 100644
--- a/packages/manager/apps/pci-kubernetes/package.json
+++ b/packages/manager/apps/pci-kubernetes/package.json
@@ -39,6 +39,7 @@
"i18next-http-backend": "^2.5.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
+ "react-error-boundary": "^4.1.2",
"react-hook-form": "^7.52.1",
"react-i18next": "^14.1.2",
"react-router-dom": "^6.3.0",
diff --git a/packages/manager/apps/pci-kubernetes/public/translations/service/Messages_en_GB.json b/packages/manager/apps/pci-kubernetes/public/translations/service/Messages_en_GB.json
index a1657eb349b4..27f94fd9e169 100644
--- a/packages/manager/apps/pci-kubernetes/public/translations/service/Messages_en_GB.json
+++ b/packages/manager/apps/pci-kubernetes/public/translations/service/Messages_en_GB.json
@@ -2,7 +2,6 @@
"kube_service": "Service",
"kube_service_error": "An error has occurred loading the information: {{message}}",
"kube_service_name": "Name",
- "kube_service_cluster_etcd_quota": "Utilisation de l'ETCD",
"kube_service_description_information": "Below, you will find information about your Kubernetes service, and how to access it using the kubectl tool.",
"kube_service_description_reset": "You can reset your Kubernetes cluster's configuration at any time.",
"kube_service_offer_beta": "Managed Kubernetes Service (free)",
diff --git a/packages/manager/apps/pci-kubernetes/public/translations/service/Messages_fr_FR.json b/packages/manager/apps/pci-kubernetes/public/translations/service/Messages_fr_FR.json
index ffd30e2b62b9..41e45a09aedd 100644
--- a/packages/manager/apps/pci-kubernetes/public/translations/service/Messages_fr_FR.json
+++ b/packages/manager/apps/pci-kubernetes/public/translations/service/Messages_fr_FR.json
@@ -93,6 +93,8 @@
"kube_service_cluster_network_vrack_default_gateway": "Passerelle par défaut du vRACK (DHCP)",
"kube_service_cluster_network_vrack_customer_gateway": "Passerelle : {{ vRackGatewayIp }}",
"kubernetes_add_private_network": "Configurer un réseau",
-
- "kube_service_network_edit": "Modifier les paramètres du réseau"
+ "kube_service_network_edit": "Modifier les paramètres du réseau",
+ "kube_service_cluster_etcd_quota": "Utilisation du quota ETCD",
+ "kube_service_cluster_etcd_quota_info": "Le quota Etcd représente l'espace alloué à la base de donnée ETCD de votre cluster managé. Veuillez consulter notre guide pour en savoir plus sur le quota ETCD. {{link}}",
+ "kube_service_etcd_quota_error": "Ce cluster utilise plus de 80% de l'espace ETCD attribué. Consultez cette page pour éviter tout impact sur votre service: {{link}}"
}
diff --git a/packages/manager/apps/pci-kubernetes/src/api/hooks/useKubernetes.spec.ts b/packages/manager/apps/pci-kubernetes/src/api/hooks/useKubernetes.spec.ts
index e405eb610ab3..fb053d638303 100644
--- a/packages/manager/apps/pci-kubernetes/src/api/hooks/useKubernetes.spec.ts
+++ b/packages/manager/apps/pci-kubernetes/src/api/hooks/useKubernetes.spec.ts
@@ -3,6 +3,7 @@ import { describe, it, vi } from 'vitest';
import * as ApiKubernetesModule from '@/api/data/kubernetes';
import {
useAllKube,
+ useGetClusterEtcdUsage,
useKubernetesCluster,
useKubes,
useRenameKubernetesCluster,
@@ -176,3 +177,37 @@ describe('useUpdateKubePolicy', () => {
expect(mockError).not.toHaveBeenCalled();
});
});
+
+describe('useGetClusterEtcdUsage', () => {
+ afterEach(() => {
+ vi.clearAllMocks();
+ vi.restoreAllMocks();
+ });
+ it('fetches etcd usage successfully', async () => {
+ const mockData = { usage: 500, quota: 1024 };
+ vi.spyOn(ApiKubernetesModule, 'getKubeEtcdUsage').mockResolvedValueOnce(
+ mockData,
+ );
+
+ const { result } = renderHook(
+ () => useGetClusterEtcdUsage('project-valid', 'kube1'),
+ { wrapper },
+ );
+
+ await waitFor(() => expect(result.current.isSuccess).toBe(true));
+ expect(result.current.data).toEqual(mockData);
+ });
+
+ it('handles error when fetching etcd usage', async () => {
+ vi.spyOn(ApiKubernetesModule, 'getKubeEtcdUsage').mockRejectedValueOnce(
+ new Error('Network Error'),
+ );
+
+ const { result } = renderHook(
+ () => useGetClusterEtcdUsage('project-error', 'kube1'),
+ { wrapper },
+ );
+
+ expect(result.current).toBe(null);
+ });
+});
diff --git a/packages/manager/apps/pci-kubernetes/src/api/hooks/useKubernetes.ts b/packages/manager/apps/pci-kubernetes/src/api/hooks/useKubernetes.ts
index 020f1675eb1c..a1d281a50447 100644
--- a/packages/manager/apps/pci-kubernetes/src/api/hooks/useKubernetes.ts
+++ b/packages/manager/apps/pci-kubernetes/src/api/hooks/useKubernetes.ts
@@ -1,6 +1,6 @@
import { applyFilters, Filter } from '@ovh-ux/manager-core-api';
import { PaginationState } from '@ovh-ux/manager-react-components';
-import { useMutation, useQuery } from '@tanstack/react-query';
+import { useMutation, useQuery, useSuspenseQuery } from '@tanstack/react-query';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { TKube } from '@/types';
@@ -612,17 +612,11 @@ export const useCreateKubernetesCluster = ({
export const useGetClusterEtcdUsage = (projectId, kubeId) => {
const queryKey = ['project', projectId, 'kube', kubeId, 'etcd', 'usage'];
- const query = useQuery({
+ return useSuspenseQuery({
queryKey,
queryFn: async () => {
const data = await getKubeEtcdUsage(projectId, kubeId);
return data;
},
- enabled: Boolean(projectId),
- suspense: true,
});
-
- return {
- ...query,
- };
};
diff --git a/packages/manager/apps/pci-kubernetes/src/components/service/ClusterETCD.component.spec.tsx b/packages/manager/apps/pci-kubernetes/src/components/service/ClusterETCD.component.spec.tsx
new file mode 100644
index 000000000000..1f23d802872b
--- /dev/null
+++ b/packages/manager/apps/pci-kubernetes/src/components/service/ClusterETCD.component.spec.tsx
@@ -0,0 +1,95 @@
+import { render, waitFor } from '@testing-library/react';
+import * as manager from '@ovh-ux/manager-react-components';
+import { vi } from 'vitest';
+import { UseSuspenseQueryResult } from '@tanstack/react-query';
+import ClusterEtcd from './ClusterETCD.component';
+import { wrapper } from '@/wrapperRenders';
+import * as useKubernetesModule from '@/api/hooks/useKubernetes';
+import { formatBytes, getColorByPercentage } from '@/helpers';
+
+describe('ClusterEtcd', () => {
+ it('renders progress bar and usage text correctly', async () => {
+ const mockUsage = 500;
+ const mockQuota = 1000;
+ const mockPercentage = (mockUsage / mockQuota) * 100;
+
+ vi.spyOn(useKubernetesModule, 'useGetClusterEtcdUsage').mockReturnValue(({
+ data: { usage: mockUsage, quota: mockQuota },
+ isPending: false,
+ } as unknown) as UseSuspenseQueryResult<{ usage: number; quota: number }>);
+
+ const { getByText, container } = render(, { wrapper });
+
+ await waitFor(() => {
+ const progressBar = container.querySelector('osds-progress-bar');
+ const progressBarColor = getColorByPercentage(mockPercentage);
+
+ expect(progressBar).toBeInTheDocument();
+ expect(progressBar?.getAttribute('color')).toBe(progressBarColor);
+ expect(progressBar?.getAttribute('value')).toBe(
+ mockPercentage.toString(),
+ );
+ expect(
+ getByText(`${formatBytes(mockUsage)} / ${formatBytes(mockQuota)}`),
+ ).toBeInTheDocument();
+ });
+ });
+
+ it('applies correct styles based on progress percentage', async () => {
+ const mockUsage = 700;
+ const mockQuota = 1000;
+ const mockPercentage = (mockUsage / mockQuota) * 100;
+
+ vi.spyOn(useKubernetesModule, 'useGetClusterEtcdUsage').mockReturnValue(({
+ data: { usage: mockUsage, quota: mockQuota },
+ isPending: false,
+ } as unknown) as UseSuspenseQueryResult<{ usage: number; quota: number }>);
+
+ const { container } = render(, { wrapper });
+
+ await waitFor(() => {
+ const progressBar = container.querySelector('osds-progress-bar');
+ const progressBarStyle = getColorByPercentage(mockPercentage);
+
+ expect(progressBar).toBeInTheDocument();
+ expect(progressBar).toHaveProperty('color', progressBarStyle);
+ });
+ });
+
+ it('renders correct usage and quota information', async () => {
+ const mockUsage = 300;
+ const mockQuota = 600;
+
+ vi.spyOn(useKubernetesModule, 'useGetClusterEtcdUsage').mockReturnValue(({
+ data: { usage: mockUsage, quota: mockQuota },
+ isPending: false,
+ } as unknown) as UseSuspenseQueryResult<{ usage: number; quota: number }>);
+
+ const { getByText } = render(, { wrapper });
+
+ await waitFor(() => {
+ expect(
+ getByText(`${formatBytes(mockUsage)} / ${formatBytes(mockQuota)}`),
+ ).toBeInTheDocument();
+ });
+ });
+ it('should call addError only once when percentage is over 80%', async () => {
+ const mockUsage = 550;
+ const mockQuota = 600;
+ const addWarning = vi.fn();
+ vi.spyOn(manager, 'useNotifications').mockReturnValue({
+ addWarning,
+ });
+
+ // Mock useGetClusterEtcdUsage to return usage leading to percentage > 80%
+ vi.spyOn(useKubernetesModule, 'useGetClusterEtcdUsage').mockReturnValue(({
+ data: { usage: mockUsage, quota: mockQuota },
+ isPending: false,
+ } as unknown) as UseSuspenseQueryResult<{ usage: number; quota: number }>);
+
+ // Render the component
+ render();
+ await waitFor(() => expect(addWarning).toHaveBeenCalledTimes(1));
+ // Check that addWarning is called only once
+ });
+});
diff --git a/packages/manager/apps/pci-kubernetes/src/components/service/ClusterETCD.component.test.tsx b/packages/manager/apps/pci-kubernetes/src/components/service/ClusterETCD.component.test.tsx
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/packages/manager/apps/pci-kubernetes/src/components/service/ClusterETCD.component.tsx b/packages/manager/apps/pci-kubernetes/src/components/service/ClusterETCD.component.tsx
index b90da4faa2f8..4f3ee326871b 100644
--- a/packages/manager/apps/pci-kubernetes/src/components/service/ClusterETCD.component.tsx
+++ b/packages/manager/apps/pci-kubernetes/src/components/service/ClusterETCD.component.tsx
@@ -1,22 +1,83 @@
-import { OsdsProgressBar } from '@ovhcloud/ods-components/react';
+import { useEffect, useMemo } from 'react';
+import {
+ ODS_TEXT_COLOR_INTENT,
+ ODS_TEXT_LEVEL,
+ ODS_TEXT_SIZE,
+} from '@ovhcloud/ods-components';
+import { Trans, useTranslation } from 'react-i18next';
+import { useNotifications } from '@ovh-ux/manager-react-components';
+import { OsdsProgressBar, OsdsText } from '@ovhcloud/ods-components/react';
import { useParams } from 'react-router-dom';
import { useGetClusterEtcdUsage } from '@/api/hooks/useKubernetes';
-import { formatBytes } from '@/helpers';
+import { formatBytes, getColorByPercentage, QUOTA_ERROR_URL } from '@/helpers';
-// TODO rajouter le seuil
+const getProgressBarStyle = (color: string) => `
+ progress[value] {
+ --progress: calc(var(--w) * (attr(value) / 100)); /* Largeur de la progression en fonction du pourcentage */
+ --color: ${color};
+ --background: lightgrey; /* Couleur de fond */
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ border: none;
+ background: var(--background);
+ }
+ progress[value]::-webkit-progress-bar {
+ background: var(--background);
+ }
+ progress[value]::-webkit-progress-value {
+ background: var(--color);
+ }
+ progress[value]::-moz-progress-bar {
+ background: var(--color);
+ }
+`;
function ClusterEtcd() {
const { projectId, kubeId } = useParams();
const {
data: { usage: used, quota: total },
} = useGetClusterEtcdUsage(projectId, kubeId);
- const percentage = (used / total) * 100;
+ const percentage = useMemo(() => (used / total) * 100, []);
+ const { t } = useTranslation(['service']);
+ const { addWarning } = useNotifications();
+
+ useEffect(() => {
+ if (percentage > 80) {
+ addWarning(
+ }}>
+ {t('kube_service_etcd_quota_error', {
+ link: QUOTA_ERROR_URL,
+ })}
+ ,
+ true,
+ );
+ }
+ }, [percentage]);
+
+ useEffect(() => {
+ const progressBarElement = document.querySelector('osds-progress-bar');
+ const { shadowRoot } = progressBarElement;
+ const style = document.createElement('style');
+ style.textContent = getProgressBarStyle(getColorByPercentage(percentage));
+ shadowRoot.appendChild(style);
+ }, []);
+
return (
-
-
+
+
{formatBytes(used)} / {formatBytes(total)}
-
+
);
}
diff --git a/packages/manager/apps/pci-kubernetes/src/components/service/ClusterInformation.component.spec.tsx b/packages/manager/apps/pci-kubernetes/src/components/service/ClusterInformation.component.spec.tsx
index 301a77944bf0..c5bf3257e422 100644
--- a/packages/manager/apps/pci-kubernetes/src/components/service/ClusterInformation.component.spec.tsx
+++ b/packages/manager/apps/pci-kubernetes/src/components/service/ClusterInformation.component.spec.tsx
@@ -1,13 +1,19 @@
-import { render, screen } from '@testing-library/react';
-import { describe, it, expect } from 'vitest';
+import { render, screen, waitFor, act } from '@testing-library/react';
+import * as manager from '@ovh-ux/manager-react-components';
+import { describe, it, expect, vi } from 'vitest';
import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';
import ClusterInformation from '@/components/service/ClusterInformation.component';
+import { wrapper } from '@/wrapperRenders';
import { TKube } from '@/types';
+import * as ApiKube from '@/api/data/kubernetes';
const renderClusterInformation = (kubeDetail) =>
- render();
+ render(, { wrapper });
describe('ClusterInformation', () => {
+ const mockData = { usage: 500, quota: 1024 };
+ vi.spyOn(ApiKube, 'getKubeEtcdUsage').mockResolvedValueOnce(mockData);
+
const kubeDetail = {
id: '1',
name: 'Cluster1',
@@ -35,25 +41,41 @@ describe('ClusterInformation', () => {
],
} as TKube;
- it('renders cluster information correctly', () => {
+ it.skip('calls clearNotifications on unmount', async () => {
+ const { unmount } = renderClusterInformation(kubeDetail);
+ const mockClearNotifications = vi.fn();
+
+ vi.spyOn(manager, 'useNotifications').mockReturnValue({
+ clearNotifications: mockClearNotifications,
+ });
+ expect(mockClearNotifications).not.toHaveBeenCalled();
+ act(() => unmount());
+ // not working issue
+ // https://github.com/testing-library/react-hooks-testing-library/issues/847
+ expect(mockClearNotifications).toHaveBeenCalledTimes(1);
+ });
+
+ it('renders cluster information correctly', async () => {
renderClusterInformation(kubeDetail);
- expect(
- screen.getByText(/kube_service_cluster_information/i),
- ).toBeInTheDocument();
- expect(screen.getByText('Cluster1')).toBeInTheDocument();
- expect(
- screen.getByText('kube_service_cluster_status_READY'),
- ).toBeInTheDocument();
- expect(screen.getByText('1.18')).toBeInTheDocument();
+ await waitFor(() => {
+ expect(
+ screen.getByText(/kube_service_cluster_information/i),
+ ).toBeInTheDocument();
+ expect(screen.getByText('Cluster1')).toBeInTheDocument();
+ expect(
+ screen.getByText('kube_service_cluster_status_READY'),
+ ).toBeInTheDocument();
+ expect(screen.getByText('1.18')).toBeInTheDocument();
- expect(
- screen.getByTestId('admission-plugin-chip AlwaysPullImages'),
- ).toHaveProperty('color', ODS_THEME_COLOR_INTENT.success);
- expect(
- screen.getByTestId('admission-plugin-chip NodeRestriction'),
- ).toHaveProperty('color', ODS_THEME_COLOR_INTENT.warning);
- expect(screen.getByText('Region1')).toBeInTheDocument();
+ expect(
+ screen.getByTestId('admission-plugin-chip AlwaysPullImages'),
+ ).toHaveProperty('color', ODS_THEME_COLOR_INTENT.success);
+ expect(
+ screen.getByTestId('admission-plugin-chip NodeRestriction'),
+ ).toHaveProperty('color', ODS_THEME_COLOR_INTENT.warning);
+ expect(screen.getByText('Region1')).toBeInTheDocument();
+ });
});
it('renders cluster ID with clipboard component', () => {
diff --git a/packages/manager/apps/pci-kubernetes/src/components/service/ClusterInformation.component.tsx b/packages/manager/apps/pci-kubernetes/src/components/service/ClusterInformation.component.tsx
index 7a628e641696..206491e954ac 100644
--- a/packages/manager/apps/pci-kubernetes/src/components/service/ClusterInformation.component.tsx
+++ b/packages/manager/apps/pci-kubernetes/src/components/service/ClusterInformation.component.tsx
@@ -1,4 +1,6 @@
import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';
+import { useEffect } from 'react';
+
import {
ODS_TEXT_LEVEL,
ODS_TEXT_SIZE,
@@ -10,11 +12,10 @@ import {
OsdsTile,
} from '@ovhcloud/ods-components/react';
import { useTranslation } from 'react-i18next';
-
-import ClusterEtcd from './ClusterETCD.component';
import {
Clipboard,
TileBlock as TileLine,
+ useNotifications,
} from '@ovh-ux/manager-react-components';
import { TKube } from '@/types';
import ClusterStatus from './ClusterStatus.component';
@@ -22,6 +23,7 @@ import ClusterETCD from './ClusterETCD.component';
import AdmissionPlugins from './AdmissionPlugins.component';
import { isProcessing } from './ClusterManagement.component';
+import ClusterTile from './ClusterTile.component';
export type ClusterInformationProps = {
kubeDetail: TKube;
@@ -32,6 +34,9 @@ export default function ClusterInformation({
}: Readonly) {
const { t } = useTranslation('service');
const { t: tDetail } = useTranslation('listing');
+ const { clearNotifications } = useNotifications();
+
+ useEffect(() => clearNotifications, []);
return (
-
- {' '}
-
- {kubeDetail.name}
-
-
-
-
-
-
-
-
+
-
-
- {kubeDetail.name}
-
-
-
-
-
+
+ {
+ // hacky, need to use a help icon and a tooltip inside the string label
+ }
+ ) as unknown) as string}>
+
+
);
diff --git a/packages/manager/apps/pci-kubernetes/src/components/service/ClusterTile.component.spec.tsx b/packages/manager/apps/pci-kubernetes/src/components/service/ClusterTile.component.spec.tsx
new file mode 100644
index 000000000000..ab7265f86e3a
--- /dev/null
+++ b/packages/manager/apps/pci-kubernetes/src/components/service/ClusterTile.component.spec.tsx
@@ -0,0 +1,27 @@
+import { describe, it, vi, expect } from 'vitest';
+import { fireEvent, render, screen } from '@testing-library/react';
+import { useTranslation } from 'react-i18next';
+import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';
+import ClusterTile from '@/components/service/ClusterTile.component';
+import { QUOTA_ERROR_URL } from '@/helpers';
+
+describe('ClusterTile', () => {
+ it('renders the cluster quota text', () => {
+ render();
+
+ // Check if the translated quota text is rendered
+ expect(
+ screen.getByText('kube_service_cluster_etcd_quota'),
+ ).toBeInTheDocument();
+ });
+
+ it('renders the help icon with correct color and size', () => {
+ const { container } = render();
+ const icon = container.querySelector('osds-icon'); // Assuming the icon has a role of 'img'
+ expect(icon).toHaveAttribute('color', ODS_THEME_COLOR_INTENT.primary);
+ fireEvent.click(icon);
+ expect(
+ screen.getByText('kube_service_cluster_etcd_quota_info'),
+ ).toBeVisible();
+ });
+});
diff --git a/packages/manager/apps/pci-kubernetes/src/components/service/ClusterTile.component.tsx b/packages/manager/apps/pci-kubernetes/src/components/service/ClusterTile.component.tsx
index 365332731bd9..46efca60778f 100644
--- a/packages/manager/apps/pci-kubernetes/src/components/service/ClusterTile.component.tsx
+++ b/packages/manager/apps/pci-kubernetes/src/components/service/ClusterTile.component.tsx
@@ -1,5 +1,6 @@
import { useTranslation, Trans } from 'react-i18next';
import { ODS_THEME_COLOR_INTENT } from '@ovhcloud/ods-common-theming';
+import { ErrorBoundary } from 'react-error-boundary';
import {
ODS_ICON_NAME,
ODS_ICON_SIZE,
diff --git a/packages/manager/apps/pci-kubernetes/src/helpers/index.spec.ts b/packages/manager/apps/pci-kubernetes/src/helpers/index.spec.ts
index 5337791a9979..a2e92de7be8f 100644
--- a/packages/manager/apps/pci-kubernetes/src/helpers/index.spec.ts
+++ b/packages/manager/apps/pci-kubernetes/src/helpers/index.spec.ts
@@ -1,10 +1,14 @@
-import { describe, it } from 'vitest';
+import { describe, it, vi } from 'vitest';
+import { z } from 'zod';
import {
compareFunction,
formatIP,
getFormatedKubeVersion,
isIPValid,
paginateResults,
+ validateSchema,
+ formatBytes,
+ getColorByPercentage,
} from '@/helpers/index';
describe('helper', () => {
@@ -66,3 +70,77 @@ describe('helper', () => {
expect(result).toBe(false);
});
});
+
+describe('validateSchema', () => {
+ const schema = z.object({
+ name: z.string(),
+ age: z
+ .number()
+ .int()
+ .positive(),
+ });
+
+ it('should return validated data if data is valid', () => {
+ const data = { name: 'John', age: 30 };
+ const result = validateSchema({ schema, data });
+ expect(result).toEqual(data);
+ });
+
+ it('should return null and call onInvalid if data is invalid', () => {
+ const data = { name: 'John', age: -5 };
+ const onInvalidMock = vi.fn();
+ const result = validateSchema({ schema, data, onInvalid: onInvalidMock });
+
+ expect(result).toBeNull();
+ expect(onInvalidMock).toHaveBeenCalledTimes(1);
+ expect(onInvalidMock.mock.calls[0][0]).toBeInstanceOf(z.ZodError);
+ });
+
+ it('should return null without calling onInvalid if data is invalid and onInvalid is not provided', () => {
+ const data = { name: 'John', age: -5 };
+ const result = validateSchema({ schema, data });
+ expect(result).toBeNull();
+ });
+});
+
+describe('formatBytes', () => {
+ it('should format bytes correctly for values under 1024', () => {
+ expect(formatBytes(512)).toBe('512 o');
+ });
+
+ it('should format bytes correctly in KiB', () => {
+ expect(formatBytes(2048)).toBe('2 KiB');
+ });
+
+ it('should format bytes correctly in MiB', () => {
+ expect(formatBytes(1048576)).toBe('1 MiB');
+ });
+
+ it('should format bytes correctly in GiB', () => {
+ expect(formatBytes(1073741824)).toBe('1 GiB');
+ });
+
+ it('should format bytes correctly in TiB', () => {
+ expect(formatBytes(1099511627776)).toBe('1 TiB');
+ });
+});
+
+describe('getColorByPercentage', () => {
+ it('should return primary color for percentage <= 69', () => {
+ expect(getColorByPercentage(50)).toBe('var(--ods-color-primary-500)');
+ expect(getColorByPercentage(69)).toBe('var(--ods-color-primary-500)');
+ });
+
+ it('should return warning color for percentage between 70 and 79', () => {
+ expect(getColorByPercentage(75)).toBe('var(--ods-color-warning-500)');
+ });
+
+ it('should return error color for percentage between 80 and 100', () => {
+ expect(getColorByPercentage(85)).toBe('var(--ods-color-error-500)');
+ expect(getColorByPercentage(100)).toBe('var(--ods-color-error-500)');
+ });
+
+ it('should return last color in thresholds if percentage exceeds 100', () => {
+ expect(getColorByPercentage(120)).toBe('var(--ods-color-error-500)');
+ });
+});
diff --git a/packages/manager/apps/pci-kubernetes/src/helpers/index.ts b/packages/manager/apps/pci-kubernetes/src/helpers/index.ts
index f58b68077a7b..5dc7191ba023 100644
--- a/packages/manager/apps/pci-kubernetes/src/helpers/index.ts
+++ b/packages/manager/apps/pci-kubernetes/src/helpers/index.ts
@@ -1,6 +1,9 @@
import { PaginationState } from '@ovh-ux/manager-react-components';
import { z } from 'zod';
+export const QUOTA_ERROR_URL =
+ 'https://docs.ovh.com/gb/en/kubernetes/etcd-quota-error/';
+
export const compareFunction = (key: keyof T) => (a: T, b: T) => {
const aValue = a[key] || '';
const bValue = b[key] || '';
@@ -133,14 +136,19 @@ type ColorThreshold = {
* Returns the appropriate color based on the provided percentage.
*
* @param percentage - The percentage value (0 to 100).
- * @param thresholds - The color thresholds to use for determining color.
* @returns - The color corresponding to the percentage.
*/
export function getColorByPercentage(percentage: number): string {
const colorThresholds: ColorThreshold[] = [
- { threshold: 33.333, color: 'blue' }, // Color for less than or equal to 33.333%
- { threshold: 66, color: 'yellow' }, // Color for between 33.333% and 66%
- { threshold: 100, color: 'red' }, // Color for greater than 66%
+ {
+ threshold: 69,
+ color: 'var(--ods-color-primary-500)',
+ }, // Color for less than or equal to 33.333%
+ {
+ threshold: 79,
+ color: 'var(--ods-color-warning-500)',
+ }, // Color for between 33.333% and 66%
+ { threshold: 100, color: 'var(--ods-color-error-500)' }, // Color for greater than 80%
].sort((a, b) => a.threshold - b.threshold);
// Sort thresholds to ensure they are in ascending order
colorThresholds.sort((a, b) => a.threshold - b.threshold);
@@ -154,5 +162,5 @@ export function getColorByPercentage(percentage: number): string {
}
// If percentage exceeds all thresholds, return the last color
- return thresholds[thresholds.length - 1].color;
+ return colorThresholds[colorThresholds.length - 1].color;
}
diff --git a/packages/manager/apps/pci-kubernetes/src/wrapperRenders.tsx b/packages/manager/apps/pci-kubernetes/src/wrapperRenders.tsx
index cc007cd0accd..199c6abd7cb6 100644
--- a/packages/manager/apps/pci-kubernetes/src/wrapperRenders.tsx
+++ b/packages/manager/apps/pci-kubernetes/src/wrapperRenders.tsx
@@ -4,6 +4,7 @@ import {
ShellContextType,
} from '@ovh-ux/manager-react-shell-client';
import { vi } from 'vitest';
+import { ErrorBoundary } from 'react-error-boundary';
export const shellContext = {
environment: {
@@ -16,14 +17,22 @@ export const shellContext = {
},
};
-const queryClient = new QueryClient();
+const queryClient = new QueryClient({
+ defaultOptions: {
+ queries: {
+ retry: false,
+ },
+ },
+});
export const wrapper = ({ children }) => (
-
-
- {children}
-
-
+ Error From Test}>
+
+
+ {children}
+
+
+
);