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

Adding tooltips for status labels #135

Merged
merged 2 commits into from
Oct 25, 2024
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
12 changes: 12 additions & 0 deletions locales/en/plugin__kuadrant-console-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"Add a new Gateway": "Add a new Gateway",
"Add Limit": "Add Limit",
"All Policies": "All Policies",
"All references for the resource have been resolved.": "All references for the resource have been resolved.",
"API Designer": "API Designer",
"APIs / HTTPRoutes": "APIs / HTTPRoutes",
"Auth": "Auth",
Expand Down Expand Up @@ -96,7 +97,18 @@
"Select Issuer": "Select Issuer",
"Status": "Status",
"Targets Gateway API networking resources Gateways to provide TLS for gateway listeners by managing the lifecycle of TLS certificates using cert-manager": "Targets Gateway API networking resources Gateways to provide TLS for gateway listeners by managing the lifecycle of TLS certificates using cert-manager",
"The resource has an unknown status and is not accepted.": "The resource has an unknown status and is not accepted.",
"The resource is accepted but not all policies are enforced.": "The resource is accepted but not all policies are enforced.",
"The resource is accepted, programmed, and all policies are enforced.": "The resource is accepted, programmed, and all policies are enforced.",
"The resource is being created.": "The resource is being created.",
"The resource is invalid and not accepted.": "The resource is invalid and not accepted.",
"The resource is overridden and not enforced.": "The resource is overridden and not enforced.",
"The resource is programmed but not fully enforced.": "The resource is programmed but not fully enforced.",
"The status of the resource is unknown.": "The status of the resource is unknown.",
"The target for the resource was not found and it is not accepted.": "The target for the resource was not found and it is not accepted.",
"There are no": "There are no",
"There is a conflict and the resource is not accepted.": "There is a conflict and the resource is not accepted.",
"There is a conflict on the resource.": "There is a conflict on the resource.",
"TLS": "TLS",
"TLSPolicy": "TLSPolicy",
"to display - please create some.": "to display - please create some.",
Expand Down
312 changes: 222 additions & 90 deletions src/components/ResourceList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
EmptyStateIcon,
EmptyStateBody,
Title,
Tooltip,
} from '@patternfly/react-core';
import {
K8sResourceCommon,
Expand All @@ -35,131 +36,262 @@ import {
} from '@patternfly/react-icons';
import DropdownWithKebab from './DropdownWithKebab';

const getStatusLabel = (obj: any) => {
// Check if status has parents and handle accordingly
const parentConditions = obj.status?.parents?.flatMap((parent: any) => parent.conditions) || [];
const generateLabelWithTooltip = (labelText, color, icon, tooltipText) => {
return (
<Tooltip content={tooltipText} position="top" enableFlip>
<Label isCompact icon={icon} color={color}>
{labelText}
</Label>
</Tooltip>
);
};

if (parentConditions.length > 0) {
const acceptedCondition = parentConditions.find(
(cond: any) => cond.type === 'Accepted' && cond.status === 'True',
const getStatusLabel = (obj) => {
const { t } = useTranslation('plugin__kuadrant-console-plugin');

const tooltipTexts = {
Enforced: t('The resource is accepted, programmed, and all policies are enforced.'),
'Accepted (Not Enforced)': t('The resource is accepted but not all policies are enforced.'),
Programmed: t('The resource is programmed but not fully enforced.'),
Conflicted: t('There is a conflict on the resource.'),
'Resolved Refs': t('All references for the resource have been resolved.'),
Unknown: t('The status of the resource is unknown.'),
Creating: t('The resource is being created.'),
'Overridden (Not Enforced)': t('The resource is overridden and not enforced.'),
'Conflicted (Not Accepted)': t('There is a conflict and the resource is not accepted.'),
'TargetNotFound (Not Accepted)': t(
'The target for the resource was not found and it is not accepted.',
),
'Unknown (Not Accepted)': t('The resource has an unknown status and is not accepted.'),
'Invalid (Not Accepted)': t('The resource is invalid and not accepted.'),
};

const { kind, status } = obj;

const policiesMap = {
Gateway: [
'kuadrant.io/DNSPolicyAffected',
'kuadrant.io/TLSPolicyAffected',
'kuadrant.io/AuthPolicyAffected',
'kuadrant.io/RateLimitPolicyAffected',
],
HTTPRoute: ['kuadrant.io/AuthPolicyAffected', 'kuadrant.io/RateLimitPolicyAffected'],
};

const policiesAffected = policiesMap[kind] || [];

const hasAllPoliciesEnforced = (conditions) => {
return policiesAffected.every((policy) =>
conditions.some((cond) => cond.type === policy && cond.status === 'True'),
);
};

const hasAnyPolicyError = (conditions) => {
return policiesAffected.some((policy) =>
conditions.some((cond) => cond.type === policy && cond.status === 'False'),
);
const programmedCondition = parentConditions.find(
(cond: any) => cond.type === 'Programmed' && cond.status === 'True',
};

if (kind === 'Gateway') {
const conditions = status?.conditions || [];

const acceptedCondition = conditions.find(
(cond) => cond.type === 'Accepted' && cond.status === 'True',
);
const programmedCondition = conditions.find(
(cond) => cond.type === 'Programmed' && cond.status === 'True',
);

if (acceptedCondition && programmedCondition) {
if (hasAllPoliciesEnforced(conditions) && !hasAnyPolicyError(conditions)) {
return generateLabelWithTooltip(
'Enforced',
'green',
<CheckCircleIcon />,
tooltipTexts['Enforced'],
);
} else {
return generateLabelWithTooltip(
'Accepted (Not Enforced)',
'purple',
<UploadIcon />,
tooltipTexts['Accepted (Not Enforced)'],
);
}
} else if (programmedCondition) {
return generateLabelWithTooltip(
'Programmed',
'blue',
<CheckCircleIcon />,
tooltipTexts['Programmed'],
);
} else if (conditions.some((cond) => cond.type === 'Conflicted' && cond.status === 'True')) {
return generateLabelWithTooltip(
'Conflicted',
'red',
<ExclamationTriangleIcon />,
tooltipTexts['Conflicted'],
);
} else if (conditions.some((cond) => cond.type === 'ResolvedRefs' && cond.status === 'True')) {
return generateLabelWithTooltip(
'Resolved Refs',
'blue',
<CheckCircleIcon />,
tooltipTexts['Resolved Refs'],
);
} else {
return generateLabelWithTooltip(
'Unknown',
'orange',
<ExclamationTriangleIcon />,
tooltipTexts['Unknown'],
);
}
}

if (policiesAffected.length > 0) {
const parentConditions = status?.parents?.flatMap((parent) => parent.conditions) || [];

const acceptedCondition = parentConditions.find(
(cond) => cond.type === 'Accepted' && cond.status === 'True',
);
const conflictedCondition = parentConditions.find(
(cond: any) => cond.type === 'Conflicted' && cond.status === 'True',
(cond) => cond.type === 'Conflicted' && cond.status === 'True',
);
const resolvedRefsCondition = parentConditions.find(
(cond: any) => cond.type === 'ResolvedRefs' && cond.status === 'True',
(cond) => cond.type === 'ResolvedRefs' && cond.status === 'True',
);

let labelText: string;
let color: 'green' | 'blue' | 'red' | 'orange';
let icon: React.ReactNode;

if (acceptedCondition) {
labelText = 'Accepted';
color = 'green';
icon = <CheckCircleIcon />;
} else if (programmedCondition) {
labelText = 'Programmed';
color = 'blue';
icon = <CheckCircleIcon />;
if (hasAllPoliciesEnforced(parentConditions) && !hasAnyPolicyError(parentConditions)) {
return generateLabelWithTooltip(
'Enforced',
'green',
<CheckCircleIcon />,
tooltipTexts['Enforced'],
);
} else {
return generateLabelWithTooltip(
'Accepted (Not Enforced)',
'purple',
<UploadIcon />,
tooltipTexts['Accepted (Not Enforced)'],
);
}
} else if (conflictedCondition) {
labelText = 'Conflicted';
color = 'red';
icon = <ExclamationTriangleIcon />;
return generateLabelWithTooltip(
'Conflicted',
'red',
<ExclamationTriangleIcon />,
tooltipTexts['Conflicted'],
);
} else if (resolvedRefsCondition) {
labelText = 'Resolved Refs';
color = 'blue';
icon = <CheckCircleIcon />;
return generateLabelWithTooltip(
'Resolved Refs',
'blue',
<CheckCircleIcon />,
tooltipTexts['Resolved Refs'],
);
} else {
labelText = 'Unknown';
color = 'orange';
icon = <ExclamationTriangleIcon />;
return generateLabelWithTooltip(
'Unknown',
'orange',
<ExclamationTriangleIcon />,
tooltipTexts['Unknown'],
);
}

return (
<Label isCompact icon={icon} color={color}>
{labelText}
</Label>
);
}

// If no parent conditions, fallback to general condition checks
if (!obj.status || !obj.status.conditions || obj.status.conditions.length === 0) {
return (
<Label isCompact icon={<OutlinedHourglassIcon />} color="cyan">
Creating
</Label>
const generalConditions = status?.conditions || [];

if (generalConditions.length === 0) {
return generateLabelWithTooltip(
'Creating',
'cyan',
<OutlinedHourglassIcon />,
tooltipTexts['Creating'],
);
}

const enforcedCondition = obj.status.conditions.find(
(cond: any) => cond.type === 'Enforced' && cond.status === 'True',
const enforcedCondition = generalConditions.find(
(cond) => cond.type === 'Enforced' && cond.status === 'True',
);
const acceptedCondition = obj.status.conditions.find(
(cond: any) => cond.type === 'Accepted' && cond.status === 'True',
const acceptedCondition = generalConditions.find(
(cond) => cond.type === 'Accepted' && cond.status === 'True',
);
const acceptedConditionFalse = obj.status.conditions.find(
(cond: any) => cond.type === 'Accepted' && cond.status === 'False',
const acceptedConditionFalse = generalConditions.find(
(cond) => cond.type === 'Accepted' && cond.status === 'False',
);
const overriddenCondition = obj.status.conditions.find(
(cond: any) => cond.type === 'Overridden' && cond.status === 'False',
const overriddenCondition = generalConditions.find(
(cond) => cond.type === 'Overridden' && cond.status === 'False',
);
const conflictedCondition = obj.status.conditions.find(
(cond: any) => cond.reason === 'Conflicted' && cond.status === 'False',
const conflictedCondition = generalConditions.find(
(cond) => cond.reason === 'Conflicted' && cond.status === 'False',
);
const targetNotFoundCondition = obj.status.conditions.find(
(cond: any) => cond.reason === 'TargetNotFound' && cond.status === 'False',
const targetNotFoundCondition = generalConditions.find(
(cond) => cond.reason === 'TargetNotFound' && cond.status === 'False',
);
const unknownCondition = obj.status.conditions.find(
(cond: any) => cond.reason === 'Unknown' && cond.status === 'False',
const unknownCondition = generalConditions.find(
(cond) => cond.reason === 'Unknown' && cond.status === 'False',
);

let labelText: string;
let color: 'blue' | 'green' | 'red' | 'orange' | 'grey' | 'purple' | 'cyan';
let icon: React.ReactNode;

if (enforcedCondition) {
labelText = 'Enforced';
color = 'green';
icon = <CheckCircleIcon />;
return generateLabelWithTooltip(
'Enforced',
'green',
<CheckCircleIcon />,
tooltipTexts['Enforced'],
);
} else if (overriddenCondition) {
labelText = 'Overridden (Not Enforced)';
color = 'grey';
icon = <LayerGroupIcon />;
return generateLabelWithTooltip(
'Overridden (Not Enforced)',
'grey',
<LayerGroupIcon />,
tooltipTexts['Overridden (Not Enforced)'],
);
} else if (acceptedCondition) {
labelText = 'Accepted (Not Enforced)';
color = 'purple';
icon = <UploadIcon />;
return generateLabelWithTooltip(
'Accepted (Not Enforced)',
'purple',
<UploadIcon />,
tooltipTexts['Accepted (Not Enforced)'],
);
} else if (conflictedCondition) {
labelText = 'Conflicted (Not Accepted)';
color = 'red';
icon = <ExclamationTriangleIcon />;
return generateLabelWithTooltip(
'Conflicted (Not Accepted)',
'red',
<ExclamationTriangleIcon />,
tooltipTexts['Conflicted (Not Accepted)'],
);
} else if (targetNotFoundCondition) {
labelText = 'TargetNotFound (Not Accepted)';
color = 'red';
icon = <ExclamationTriangleIcon />;
return generateLabelWithTooltip(
'TargetNotFound (Not Accepted)',
'red',
<ExclamationTriangleIcon />,
tooltipTexts['TargetNotFound (Not Accepted)'],
);
} else if (unknownCondition) {
labelText = 'Unknown (Not Accepted)';
color = 'orange';
icon = <ExclamationTriangleIcon />;
return generateLabelWithTooltip(
'Unknown (Not Accepted)',
'orange',
<ExclamationTriangleIcon />,
tooltipTexts['Unknown (Not Accepted)'],
);
} else if (acceptedConditionFalse) {
labelText = 'Invalid (Not Accepted)';
color = 'red';
icon = <ExclamationTriangleIcon />;
return generateLabelWithTooltip(
'Invalid (Not Accepted)',
'red',
<ExclamationTriangleIcon />,
tooltipTexts['Invalid (Not Accepted)'],
);
} else {
labelText = 'Unknown';
color = 'grey';
icon = <ExclamationTriangleIcon />;
return generateLabelWithTooltip(
'Unknown',
'grey',
<ExclamationTriangleIcon />,
tooltipTexts['Unknown'],
);
}

return (
<Label isCompact icon={icon} color={color}>
{labelText}
</Label>
);
};

type ResourceListProps = {
Expand Down
Loading