Skip to content

Commit

Permalink
Convert Providers screen to Console page
Browse files Browse the repository at this point in the history
Code changes:
1. use StandardPage list component
2. entity list is produced by merging results from k8s API and
   exsting Forklift inventory REST API.
3. actions in the table (kebab actions) are provided via extension
   point 'console.action/provider'
4. use existing actions for Add/Edit/Delete/Select Network

Functional changes:
1. display all providers in one table
2. use only 'Ready' condition to describe the state of the provider
3. disable rich content in Network/Storage columns until the design for
   the details page is known

Signed-off-by: Radoslaw Szwajkowski <[email protected]>
  • Loading branch information
rszwajko committed Nov 16, 2022
1 parent ec17aa7 commit 7f441b3
Show file tree
Hide file tree
Showing 20 changed files with 885 additions and 43 deletions.
24 changes: 17 additions & 7 deletions console-extensions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import type { EncodedExtension } from '@openshift/dynamic-plugin-sdk';
import type { HrefNavItem, NavSection, Separator } from '@openshift-console/dynamic-plugin-sdk';
import type {
HrefNavItem,
NavSection,
ResourceNSNavItem,
Separator,
} from '@openshift-console/dynamic-plugin-sdk';

import { extensions as hostExtensions } from './src/modules/Hosts/dynamic-plugin';
import { extensions as mappingExtensions } from './src/modules/Mappings/dynamic-plugin';
Expand Down Expand Up @@ -39,17 +44,22 @@ const extensions: EncodedExtension[] = [
} as EncodedExtension<Separator>,

{
type: 'console.navigation/href',
type: 'console.navigation/resource-ns',
properties: {
id: 'providers',
insertAfter: 'importSeparator',
perspective: 'admin',
section: 'virtualization',
name: '%plugin__forklift-console-plugin~Providers for VM Import%',
href: '/mtv/providers',
model: {
group: 'forklift.konveyor.io',
kind: 'Provider',
version: 'v1beta1',
},
dataAttributes: {
'data-quickstart-id': 'qs-nav-providers',
'data-test-id': 'providers-nav-item',
},
},
} as EncodedExtension<HrefNavItem>,

} as EncodedExtension<ResourceNSNavItem>,
{
type: 'console.navigation/href',
properties: {
Expand Down
32 changes: 31 additions & 1 deletion locales/en/plugin__forklift-console-plugin.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,53 @@
{
"{{type}} provider {{name}} will no longer be selectable as a migration source.": "{{type}} provider {{name}} will no longer be selectable as a migration source.",
"{{type}} provider {{name}} will no longer be selectable as a migration target.": "{{type}} provider {{name}} will no longer be selectable as a migration target.",
"Actions": "Actions",
"Add Provider": "Add Provider",
"Cancel": "Cancel",
"Cannot remove provider": "Cannot remove provider",
"Clear all filters": "Clear all filters",
"Clusters": "Clusters",
"Delete": "Delete",
"Delete Provider": "Delete Provider",
"Edit Provider": "Edit Provider",
"Endpoint": "Endpoint",
"False": "False",
"Filter by endpoint": "Filter by endpoint",
"Filter by name": "Filter by name",
"Filter by namespace": "Filter by namespace",
"Hosts": "Hosts",
"KubeVirt": "KubeVirt",
"Loading": "Loading",
"Manage Columns": "Manage Columns",
"Mappings for VM Import": "Mappings for VM Import",
"Name": "Name",
"Namespace": "Namespace",
"Networks": "Networks",
"No information": "No information",
"No results found": "No results found",
"No results match the filter criteria. Clear all filters and try again.": "No results match the filter criteria. Clear all filters and try again.",
"oVirt": "oVirt",
"Permanently delete provider?": "Permanently delete provider?",
"Plans for VM Import": "Plans for VM Import",
"Providers": "Providers",
"Providers for VM Import": "Providers for VM Import",
"Ready": "Ready",
"Reorder": "Reorder",
"Restore default colums": "Restore default colums",
"Save": "Save",
"Select a default migration network for the provider. This network will be used for migrating data to all namespaces to which it is attached.": "Select a default migration network for the provider. This network will be used for migrating data to all namespaces to which it is attached.",
"Select Filter": "Select Filter",
"Select migration network": "Select migration network",
"Selected columns will be displayed in the table.": "Selected columns will be displayed in the table.",
"Storage": "Storage",
"Table column management": "Table column management",
"The host provider cannot be edited": "The host provider cannot be edited",
"This provider cannot be edited because it has running migrations": "This provider cannot be edited because it has running migrations",
"True": "True",
"Type": "Type",
"Unable to retrieve data": "Unable to retrieve data",
"Virtualization": "Virtualization"
"Unknown": "Unknown",
"Virtualization": "Virtualization",
"VMs": "VMs",
"VMware": "VMware"
}
6 changes: 2 additions & 4 deletions pkg/web/src/app/Providers/HostsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from '@patternfly/react-core';
import spacing from '@patternfly/react-styles/css/utilities/Spacing/spacing';
import { Breadcrumb, BreadcrumbItem } from '@patternfly/react-core';
import { Link, useRouteMatch } from 'react-router-dom';
import { useRouteMatch } from 'react-router-dom';
import { VMwareProviderHostsTable } from './components/VMwareProviderHostsTable';
import PlusCircleIcon from '@patternfly/react-icons/dist/esm/icons/plus-circle-icon';
import { useHostsQuery, useInventoryProvidersQuery } from '@app/queries';
Expand Down Expand Up @@ -45,9 +45,7 @@ export const HostsPage: React.FunctionComponent = () => {
<LevelItem>
<Breadcrumb>
<BreadcrumbItem>Providers</BreadcrumbItem>
<BreadcrumbItem>
<Link to={`${PATH_PREFIX}/providers/vsphere`}>{PROVIDER_TYPE_NAMES.vsphere}</Link>
</BreadcrumbItem>
<BreadcrumbItem>{PROVIDER_TYPE_NAMES.vsphere}</BreadcrumbItem>
<BreadcrumbItem>{match?.params.providerName}</BreadcrumbItem>
<BreadcrumbItem isActive>Hosts</BreadcrumbItem>
</Breadcrumb>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import * as React from 'react';
import { Dropdown, KebabToggle, DropdownItem, DropdownPosition } from '@patternfly/react-core';
import { useDeleteProviderMutation } from '@app/queries';
import { ICorrelatedProvider, InventoryProvider } from '@app/queries/types';
import {
ICorrelatedProvider,
INameNamespaceRef,
InventoryProvider,
IPlan,
} from '@app/queries/types';
import { PATH_PREFIX, ProviderType, PROVIDER_TYPE_NAMES } from '@app/common/constants';
import { ConfirmModal } from '@app/common/components/ConfirmModal';
import { EditProviderContext } from '@app/Providers/ProvidersPage';
Expand All @@ -11,6 +16,22 @@ import { isSameResource } from '@app/queries/helpers';
import { useHistory } from 'react-router-dom';
import { useClusterProvidersQuery } from '@app/queries';

export const hasRunningMigration = ({
plans = [],
providerMetadata,
}: {
plans: IPlan[];
providerMetadata: INameNamespaceRef;
}): boolean =>
!!plans
.filter((plan) => hasCondition(plan.status?.conditions || [], 'Executing'))
.find((runningPlan) => {
const { source, destination } = runningPlan.spec.provider;
return (
isSameResource(providerMetadata, source) || isSameResource(providerMetadata, destination)
);
});

interface IProviderActionsDropdownProps {
provider: ICorrelatedProvider<InventoryProvider>;
providerType: ProviderType;
Expand All @@ -37,15 +58,15 @@ export const ProviderActionsDropdown: React.FunctionComponent<IProviderActionsDr
}
});

const hasRunningMigration = !!plans
.filter((plan) => hasCondition(plan.status?.conditions || [], 'Executing'))
.find((runningPlan) => {
const { source, destination } = runningPlan.spec.provider;
return (
isSameResource(provider.metadata, source) || isSameResource(provider.metadata, destination)
);
});
const isEditDeleteDisabled = !provider.spec.url || hasRunningMigration;
const isEditDeleteDisabled =
!provider.spec.url || hasRunningMigration({ plans, providerMetadata: provider.metadata });

const disabledEditTooltip = !provider.spec.url
? 'The host provider cannot be edited'
: 'This provider cannot be edited because it has running migrations';
const disabledDeleteTooltip = !provider.spec.url
? 'The host provider cannot be removed'
: 'This provider cannot be removed because it has running migrations';

return (
<>
Expand All @@ -58,13 +79,7 @@ export const ProviderActionsDropdown: React.FunctionComponent<IProviderActionsDr
<ConditionalTooltip
key="edit"
isTooltipEnabled={isEditDeleteDisabled}
content={
!provider.spec.url
? 'The host provider cannot be edited'
: hasRunningMigration
? 'This provider cannot be edited because it has running migrations'
: ''
}
content={isEditDeleteDisabled ? disabledEditTooltip : ''}
>
<DropdownItem
aria-label="Edit"
Expand All @@ -80,13 +95,7 @@ export const ProviderActionsDropdown: React.FunctionComponent<IProviderActionsDr
<ConditionalTooltip
key="remove"
isTooltipEnabled={isEditDeleteDisabled}
content={
!provider.spec.url
? 'The host provider cannot be removed'
: hasRunningMigration
? 'This provider cannot be removed because it has running migrations'
: ''
}
content={isEditDeleteDisabled ? disabledDeleteTooltip : ''}
>
<DropdownItem
aria-label="Remove"
Expand Down
39 changes: 39 additions & 0 deletions src/components/ActionServiceDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useState } from 'react';
import { useTranslation } from 'src/internal/i18n';

import { ActionService } from '@openshift-console/dynamic-plugin-sdk';
import { Dropdown, DropdownItem, DropdownToggle, KebabToggle } from '@patternfly/react-core';

export const createActions = (variant: 'kebab' | 'dropdown') =>
function GenericActions({ actions }: ActionService) {
const { t } = useTranslation();
const [isActionMenuOpen, setIsActionMenuOpen] = useState(false);
const isPlain = variant === 'kebab';
const toggle =
variant === 'kebab' ? (
<KebabToggle onToggle={setIsActionMenuOpen} />
) : (
<DropdownToggle onToggle={setIsActionMenuOpen}>{t('Actions')}</DropdownToggle>
);
return (
<>
<Dropdown
position="right"
onSelect={() => setIsActionMenuOpen(!isActionMenuOpen)}
toggle={toggle}
isOpen={isActionMenuOpen}
isPlain={isPlain}
dropdownItems={actions.map(({ id, label, cta, disabled, disabledTooltip }) => (
<DropdownItem
key={id}
onClick={typeof cta === 'function' ? cta : () => undefined}
isAriaDisabled={disabled}
tooltip={disabledTooltip}
>
{label}
</DropdownItem>
))}
/>
</>
);
};
3 changes: 3 additions & 0 deletions src/components/Filter/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ export const toFieldFilter = ({
toLabel: toFieldLabel,
filter: filterDef,
}: Field): FieldFilter => ({ fieldId, toFieldLabel, filterDef });

export const fromI18nEnum = (i18nEnum: { [k: string]: (t: (k: string) => string) => string }) =>
Object.entries(i18nEnum).map(([type, toLabel]) => ({ id: type, toLabel }));
3 changes: 3 additions & 0 deletions src/components/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@ export interface Field {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
comparator?: (a: any, b: any, locale: string) => number;
}

export const K8sConditionStatusValues = ['True', 'False', 'Unknown'] as const;
export type K8sConditionStatus = typeof K8sConditionStatusValues[number];
15 changes: 15 additions & 0 deletions src/internal/i18n/enums.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { K8sConditionStatus } from '_/components/types';

import { ProviderType } from '@app/common/constants';

export const PROVIDERS: Record<ProviderType, (t: (k: string) => string) => string> = {
vsphere: (t) => t('VMware'),
ovirt: (t) => t('oVirt'),
openshift: (t) => t('KubeVirt'),
};

export const CONDITIONS: Record<K8sConditionStatus, (t: (k: string) => string) => string> = {
True: (t) => t('True'),
False: (t) => t('False'),
Unknown: (t) => t('Unknown'),
};
1 change: 1 addition & 0 deletions src/internal/i18n/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './enums';
export * from './i18n';
5 changes: 5 additions & 0 deletions src/internal/k8s/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,8 @@ export type ProviderResource = {
conditions?: Condition[];
};
} & K8sResourceCommon;

export type ResourceConsolePageProps = {
kind: string;
namespace: string;
};
Loading

0 comments on commit 7f441b3

Please sign in to comment.