Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor the concern cell renderer #738

Merged
merged 1 commit into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"CreationTimestamp is a timestamp representing the server time when this object was created.\n It is not guaranteed to be set in happens-before order across separate operations.\n Clients may not set this value. It is represented in RFC3339 form and is in UTC.": "CreationTimestamp is a timestamp representing the server time when this object was created.\n It is not guaranteed to be set in happens-before order across separate operations.\n Clients may not set this value. It is represented in RFC3339 form and is in UTC.",
"CreationTimestamp is a timestamp representing the server time when this object was created.\n It is not guaranteed to be set in happens-before order across separate operations.\n Clients may not set this value. It is represented in RFC3339 form and is in UTC.": "CreationTimestamp is a timestamp representing the server time when this object was created.\n It is not guaranteed to be set in happens-before order across separate operations.\n Clients may not set this value. It is represented in RFC3339 form and is in UTC.",
"Credentials": "Credentials",
"Critical concerns": "Critical concerns",
"Custom certification used to verify the OpenStack REST API server, when empty use system certificate.": "Custom certification used to verify the OpenStack REST API server, when empty use system certificate.",
"Custom certification used to verify the RH Virtualization REST API server, when empty use system certificate.": "Custom certification used to verify the RH Virtualization REST API server, when empty use system certificate.",
"Data centers": "Data centers",
Expand Down Expand Up @@ -128,6 +129,7 @@
"Hosts": "Hosts",
"If true, the provider's REST API TLS certificate won't be validated.": "If true, the provider's REST API TLS certificate won't be validated.",
"If true, the provider's TLS certificate won't be validated.": "If true, the provider's TLS certificate won't be validated.",
"Information concerns": "Information concerns",
"Invalid application credential ID.": "Invalid application credential ID.",
"Invalid application credential name.": "Invalid application credential name.",
"Invalid application credential secret.": "Invalid application credential secret.",
Expand Down Expand Up @@ -352,6 +354,7 @@
"To troubleshoot, view the provider status available in the provider details page\n and check the Forklift controller pod logs.": "To troubleshoot, view the provider status available in the provider details page\n and check the Forklift controller pod logs.",
"Token": "Token",
"Total": "Total",
"Total: {{length}}": "Total: {{length}}",
"True": "True",
"Type": "Type",
"Type of authentication to use when connecting to OpenStack REST API.": "Type of authentication to use when connecting to OpenStack REST API.",
Expand Down Expand Up @@ -384,6 +387,7 @@
"vSphere product name": "vSphere product name",
"vSphere REST API password credentials.": "vSphere REST API password credentials.",
"vSphere REST API user name.": "vSphere REST API user name.",
"Warning concerns": "Warning concerns",
"Warning: The provided URL does not end with \"ovirt-engine/api\". Ensure it includes the correct path, like: https://rhv.com/ovirt-engine/api.": "Warning: The provided URL does not end with \"ovirt-engine/api\". Ensure it includes the correct path, like: https://rhv.com/ovirt-engine/api.",
"Warning: The provided URL does not end with \"sdk\". Ensure it includes the correct path, like: https://vcenter.com/sdk.": "Warning: The provided URL does not end with \"sdk\". Ensure it includes the correct path, like: https://vcenter.com/sdk.",
"Welcome": "Welcome",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React from 'react';
import { TFunction } from 'react-i18next';
import { TableCell } from 'src/modules/Providers/utils';
import { useForkliftTranslation } from 'src/utils/i18n';

import { Concern } from '@kubev2v/types';
import {
Expand All @@ -11,11 +13,12 @@ import { Button, Flex, FlexItem, Label, Popover, Stack, StackItem } from '@patte

import { VMCellProps } from './VMCellProps';

type ConcernCategories = {
category: 'Critical' | 'Information' | 'Warning';
label: string;
};

/**
* Renders a table cell containing concerns grouped by category.
*
* @param {VMCellProps} props - The properties of the VMConcernsCellRenderer component.
* @returns {ReactElement} The rendered table cell.
*/
export const VMConcernsCellRenderer: React.FC<VMCellProps> = ({ data }) => {
const groupedConcerns = groupConcernsByCategory(data?.vm?.concerns);

Expand All @@ -32,59 +35,123 @@ export const VMConcernsCellRenderer: React.FC<VMCellProps> = ({ data }) => {
);
};

const groupConcernsByCategory = (concerns: Concern[]): Record<string, ConcernCategories[]> => {
return (
concerns?.reduce((acc, concern) => {
acc[concern.category] = (acc[concern.category] || []).concat(concern);
return acc;
}, {}) || {}
);
};

/**
* Renders a popover for a specific concern category.
*
* @param {Object} props - The properties of the ConcernPopover component.
* @param {string} props.category - The category of the concern.
* @param {Concern[]} props.concerns - The list of concerns for the category.
* @returns {ReactElement} The rendered popover.
*/
const ConcernPopover: React.FC<{
category: string;
concerns: ConcernCategories[];
concerns: Concern[];
}> = ({ category, concerns }) => {
const { t } = useForkliftTranslation();

if (concerns.length < 1) return <></>;

return (
<Popover
aria-label={`${category} popover`}
headerContent={<div>{category} Concerns</div>}
headerContent={<div>{getCategoryTitle(category, t)}</div>}
bodyContent={<ConcernList concerns={concerns} />}
footerContent={`Total: ${concerns.length}`}
footerContent={t('Total: {{length}}', { length: concerns.length })}
>
<Button variant="link" className="forklift-page-provider-vm_concern-button">
<ConcernLabel category={category} count={concerns.length} />
<Label color={getCategoryColor(category)} icon={getCategoryIcon(category)}>
{concerns.length}
</Label>
</Button>
</Popover>
);
};

const ConcernList: React.FC<{ concerns: ConcernCategories[] }> = ({ concerns }) => (
/**
* Renders a list of concerns.
*
* @param {Object} props - The properties of the ConcernList component.
* @param {Concern[]} props.concerns - The list of concerns to render.
* @returns {ReactElement} The rendered list of concerns.
*/
const ConcernList: React.FC<{ concerns: Concern[] }> = ({ concerns }) => (
<Stack>
{concerns.map((c) => (
<StackItem key={c.category}>
{statusIcons[c.category]} {c.label}
{getCategoryIcon(c.category)} {c.label}
</StackItem>
))}
</Stack>
);

const ConcernLabel: React.FC<{ category: string; count: number }> = ({ category, count }) => (
<Label variant="outline" color={categoryColors[category]} icon={statusIcons[category]}>
{count}
</Label>
);
/**
* Groups concerns by their category.
*
* @param {Concern[]} concerns - The list of concerns to group.
* @returns {Record<string, Concern[]>} The grouped concerns by category.
*/
const groupConcernsByCategory = (concerns: Concern[] = []): Record<string, Concern[]> => {
return concerns.reduce(
(acc, concern) => {
if (!acc[concern.category]) {
acc[concern.category] = [];
}
acc[concern.category].push(concern);
return acc;
},
{
Critical: [],
Information: [],
Warning: [],
},
);
};

/**
* Retrieves the title for a given concern category.
*
* @param {string} category - The category of the concern.
* @param {TFunction} t - The translation function.
* @returns {string} The title for the given category.
*/
const getCategoryTitle = (category: string, t: TFunction): string => {
const titles = {
Critical: t('Critical concerns'),
Information: t('Information concerns'),
Warning: t('Warning concerns'),
};

const statusIcons = {
Critical: <RedExclamationCircleIcon />,
Information: <BlueInfoCircleIcon />,
Warning: <YellowExclamationTriangleIcon />,
return titles[category] || '';
};

const categoryColors = {
Critical: 'red',
Information: 'blue',
Warning: 'orange',
/**
* Retrieves the icon for a given concern category.
*
* @param {string} category - The category of the concern.
* @returns {ReactElement} The icon for the given category.
*/
const getCategoryIcon = (category: string) => {
const icons = {
Critical: <RedExclamationCircleIcon />,
Information: <BlueInfoCircleIcon />,
Warning: <YellowExclamationTriangleIcon />,
};

return icons[category] || <></>;
};

/**
* Retrieves the color for a given concern category.
*
* @param {string} category - The category of the concern.
* @returns {string} The color for the given category.
*/
const getCategoryColor = (category: string) => {
const colors = {
Critical: 'red',
Information: 'blue',
Warning: 'orange',
};

return colors[category] || 'grey';
};