Skip to content

Commit 2b68700

Browse files
authored
Merge pull request #1506 from jpuzz0/MTV-2016-migration-type-column
[MTV-2016] Add 'Migration type' column to plan list table
2 parents de01073 + 628c442 commit 2b68700

File tree

21 files changed

+305
-140
lines changed

21 files changed

+305
-140
lines changed

packages/common/src/components/Filter/GroupedEnumFilter.tsx

+48-8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
SelectOption,
1111
ToolbarFilter,
1212
} from '@patternfly/react-core';
13+
import { FilterIcon } from '@patternfly/react-icons';
1314

1415
import { FilterTypeProps } from './types';
1516

@@ -40,6 +41,8 @@ export const GroupedEnumFilter = ({
4041
supportedGroups = [],
4142
placeholderLabel,
4243
showFilter = true,
44+
showFilterIcon,
45+
hasMultipleResources,
4346
}: FilterTypeProps) => {
4447
const [isOpen, setIsOpen] = useState(false);
4548

@@ -52,23 +55,54 @@ export const GroupedEnumFilter = ({
5255
supportedEnumValues.map(({ label, ...rest }) => [label, { label, ...rest }]),
5356
);
5457

55-
const deleteGroup = (groupId: string): void =>
58+
const deleteGroup = (groupId: string): void => {
59+
if (hasMultipleResources) {
60+
return onSelectedEnumIdsChange([], groupId);
61+
}
62+
5663
onSelectedEnumIdsChange(
57-
selectedEnumIds
58-
.filter((id) => id2enum[id])
59-
.filter((enumId) => id2enum[enumId].groupId !== groupId),
64+
selectedEnumIds.filter((id) => id2enum[id] && id2enum[id].groupId !== groupId),
6065
);
66+
};
67+
68+
const deleteFilter = (id: string): void => {
69+
if (hasMultipleResources) {
70+
onSelectedEnumIdsChange(
71+
selectedEnumIds.filter(
72+
(selectedId) =>
73+
id2enum[selectedId]?.resourceFieldId === id2enum[id]?.resourceFieldId &&
74+
selectedId !== id,
75+
),
76+
id2enum[id].resourceFieldId,
77+
);
78+
}
6179

62-
const deleteFilter = (id: string): void =>
6380
onSelectedEnumIdsChange(
64-
selectedEnumIds.filter((id) => id2enum[id]).filter((enumId) => enumId !== id),
81+
selectedEnumIds.filter((id) => id2enum[id] && id !== id),
82+
id2enum[id].resourceFieldId,
6583
);
84+
};
6685

6786
const hasFilter = (id: string): boolean =>
6887
!!id2enum[id] && !!selectedEnumIds.find((enumId) => enumId === id);
6988

7089
const addFilter = (id: string): void => {
71-
onSelectedEnumIdsChange([...selectedEnumIds.filter((id) => id2enum[id]), id]);
90+
if (hasMultipleResources) {
91+
onSelectedEnumIdsChange(
92+
[
93+
...selectedEnumIds.filter(
94+
(selectedId) => id2enum[selectedId]?.resourceFieldId === id2enum[id]?.resourceFieldId,
95+
),
96+
id,
97+
],
98+
id2enum[id].resourceFieldId,
99+
);
100+
}
101+
102+
onSelectedEnumIdsChange(
103+
[...selectedEnumIds.filter((id) => id2enum[id]), id],
104+
id2enum[id].resourceFieldId,
105+
);
72106
};
73107

74108
const onSelect = (
@@ -85,7 +119,13 @@ export const GroupedEnumFilter = ({
85119
};
86120

87121
const toggle = (toggleRef: Ref<MenuToggleElement>) => (
88-
<MenuToggle ref={toggleRef} onClick={onToggleClick} isExpanded={isOpen} isFullWidth>
122+
<MenuToggle
123+
ref={toggleRef}
124+
onClick={onToggleClick}
125+
isExpanded={isOpen}
126+
isFullWidth
127+
{...(showFilterIcon && { icon: <FilterIcon /> })}
128+
>
89129
{placeholderLabel}
90130
{selectedEnumIds.length > 0 && <Badge isRead>{selectedEnumIds.length}</Badge>}
91131
</MenuToggle>

packages/common/src/components/Filter/types.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export interface FilterTypeProps {
1212
* Filter apply handler. Implementation of filter values is filter specific.
1313
* @param values list of selected filter values
1414
*/
15-
onFilterUpdate(values: string[]);
15+
onFilterUpdate(values: string[], resourceFieldId?: string);
1616
/**
1717
* A text located inside the filter field or next to it.
1818
*/
@@ -43,6 +43,10 @@ export interface FilterTypeProps {
4343
resolvedLanguage: string;
4444
/** Text that explains how to use the filter. */
4545
helperText?: string | React.ReactNode;
46+
/** Toggles visibility of FilterIcon within the Select input field. */
47+
showFilterIcon?: boolean;
48+
/** Used for grouped enum filters that deal with groups pointing to different resources. */
49+
hasMultipleResources?: boolean;
4650
}
4751

4852
export interface InlineFilter {
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useMemo, useState } from 'react';
22

33
import { FilterDef } from '../../utils';
44
import { FilterTypeProps } from '../Filter';
@@ -17,7 +17,7 @@ interface FilterFromDefProps {
1717
}
1818

1919
export const FilterFromDef = ({
20-
resourceFieldId: id,
20+
resourceFieldId,
2121
label,
2222
filterDef: def,
2323
selectedFilters,
@@ -26,26 +26,49 @@ export const FilterFromDef = ({
2626
showFilter = true,
2727
resolvedLanguage,
2828
}: FilterFromDefProps) => {
29-
return (
30-
FilterType && (
31-
<FilterType
32-
key={id}
33-
filterId={id}
34-
onFilterUpdate={(values) =>
35-
onFilterUpdate({
36-
...selectedFilters,
37-
[id]: values,
38-
})
29+
const [filterId, setFilterId] = useState(resourceFieldId);
30+
31+
const selectedFilterValues = useMemo(() => {
32+
const groupSelectedIds = def.groups?.map((group) => group.groupId);
33+
34+
if (!resourceFieldId && groupSelectedIds.length > 0) {
35+
return Object.entries(selectedFilters).reduce((acc, [selectedId, selectedValues]) => {
36+
if (groupSelectedIds.includes(selectedId)) {
37+
acc = acc.length > 0 ? acc.concat(selectedValues) : selectedValues;
3938
}
40-
placeholderLabel={def.placeholderLabel}
41-
selectedFilters={selectedFilters[id] ?? []}
42-
title={def?.fieldLabel ?? label}
43-
showFilter={showFilter}
44-
supportedValues={def.values}
45-
supportedGroups={def.groups}
46-
resolvedLanguage={resolvedLanguage}
47-
helperText={def.helperText}
48-
/>
49-
)
50-
);
39+
40+
return acc;
41+
}, []);
42+
}
43+
44+
return selectedFilters[filterId] ?? [];
45+
}, [def.groups, filterId, resourceFieldId, selectedFilters]);
46+
47+
const setSelectedFilters = (values: string[], selectedResourceId?: string) => {
48+
if (selectedResourceId) {
49+
setFilterId(selectedResourceId);
50+
}
51+
52+
onFilterUpdate({
53+
...selectedFilters,
54+
[selectedResourceId || resourceFieldId]: values,
55+
});
56+
};
57+
58+
return !def.isHidden && FilterType ? (
59+
<FilterType
60+
filterId={filterId}
61+
onFilterUpdate={setSelectedFilters}
62+
placeholderLabel={def.placeholderLabel}
63+
selectedFilters={selectedFilterValues}
64+
title={def?.fieldLabel ?? label}
65+
showFilter={showFilter}
66+
supportedValues={def.values}
67+
supportedGroups={def.groups}
68+
resolvedLanguage={resolvedLanguage}
69+
helperText={def.helperText}
70+
showFilterIcon={def.showFilterIcon}
71+
hasMultipleResources={!resourceFieldId && def.groups.length > 0}
72+
/>
73+
) : null;
5174
};

packages/common/src/utils/types.ts

+14-3
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,20 @@ export interface EnumValue {
1010
id: string;
1111
groupId?: string;
1212
label: string;
13+
resourceFieldId?: string;
14+
}
15+
16+
export enum FilterDefType {
17+
FreeText = 'freetext',
18+
Enum = 'enum',
19+
GroupedEnum = 'groupedEnum',
20+
DateRange = 'dateRange',
21+
Slider = 'slider',
1322
}
1423

1524
export interface FilterDef {
1625
type: string;
17-
placeholderLabel: string;
26+
placeholderLabel?: string;
1827
values?: EnumValue[];
1928
fieldLabel?: string;
2029
primary?: boolean;
@@ -25,14 +34,16 @@ export interface FilterDef {
2534
defaultValues?: string[];
2635
helperText?: string | React.ReactNode;
2736
dynamicFilter?: (items: unknown[]) => Partial<FilterDef>;
37+
isHidden?: boolean;
38+
showFilterIcon?: boolean;
2839
}
2940

3041
type OpenApiJsonPath = string | ((resourceData: unknown) => unknown);
3142

3243
export interface ResourceField {
33-
resourceFieldId: string;
44+
resourceFieldId: string | null;
3445
jsonPath?: OpenApiJsonPath;
35-
label: string;
46+
label: string | null;
3647
// visibility status, can change in time
3748
isVisible?: boolean;
3849
isIdentity?: boolean;

packages/forklift-console-plugin/locales/en/plugin__forklift-console-plugin.json

+2
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@
123123
"Click to unselect.": "Click to unselect.",
124124
"Cluster": "Cluster",
125125
"Clusters": "Clusters",
126+
"Cold": "Cold",
126127
"Completed {{completed}} of {{total}} {{name}} tasks": "Completed {{completed}} of {{total}} {{name}} tasks",
127128
"Completed at": "Completed at",
128129
"Concern": "Concern",
@@ -230,6 +231,7 @@
230231
"False": "False",
231232
"Features": "Features",
232233
"Fetch certificate from URL": "Fetch certificate from URL",
234+
"Filter": "Filter",
233235
"Filter by cluster": "Filter by cluster",
234236
"Filter by endpoint": "Filter by endpoint",
235237
"Filter by features": "Filter by features",

packages/forklift-console-plugin/src/modules/NetworkMaps/actions/NetworkMapActionsDropdown.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ const NetworkMapActionsKebabDropdown_: FC<NetworkMapActionsDropdownProps> = ({ d
6464
export const NetworkMapActionsDropdown: FC<NetworkMapActionsDropdownProps> = (props) => (
6565
<ModalHOC>
6666
<Flex flex={{ default: 'flex_3' }} flexWrap={{ default: 'nowrap' }}>
67-
<FlexItem grow={{ default: 'grow' }}></FlexItem>
67+
<FlexItem grow={{ default: 'grow' }} />
6868
<FlexItem align={{ default: 'alignRight' }}>
6969
<NetworkMapActionsKebabDropdown_ {...props} />
7070
</FlexItem>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { MigrationType } from '../types';
2+
3+
export const migrationTypes: { id: MigrationType; label: MigrationType }[] = [
4+
{ id: MigrationType.Warm, label: MigrationType.Warm },
5+
{ id: MigrationType.Cold, label: MigrationType.Cold },
6+
];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { MigrationType, PlanData } from '../types';
2+
3+
export const getMigrationType = (data: PlanData): MigrationType => {
4+
const plan = data?.obj;
5+
6+
if (plan?.spec?.warm) {
7+
return MigrationType.Warm;
8+
}
9+
10+
return MigrationType.Cold;
11+
};

packages/forklift-console-plugin/src/modules/Plans/utils/helpers/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// @index(['./*', /style/g], f => `export * from '${f.path}';`)
22
export * from './anyValidationErrorExists';
33
export * from './getMigrationPhase';
4+
export * from './getMigrationType';
45
export * from './getMigrationVmsCounts';
56
export * from './getPlanPhase';
67
export * from './getPlanProgressVariant';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export enum MigrationType {
2+
Warm = 'Warm',
3+
Cold = 'Cold',
4+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// @index(['./*', /style/g], f => `export * from '${f.path}';`)
22
export * from './MigrationPhase';
3+
export * from './MigrationType';
34
export * from './PlanData';
45
export * from './PlanPhase';
56
// @endindex

packages/forklift-console-plugin/src/modules/Plans/views/details/tabs/VirtualMachines/Plan/ActionsCell.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import PlanVMActionsDropdown from './PlanVMActionsDropdown';
1010
const ActionsCell: FC<PlanVMsCellProps> = ({ data: vm }) => {
1111
return (
1212
<Flex flex={{ default: 'flex_3' }} flexWrap={{ default: 'nowrap' }}>
13-
<FlexItem grow={{ default: 'grow' }}></FlexItem>
13+
<FlexItem grow={{ default: 'grow' }} />
1414

1515
<FlexItem align={{ default: 'alignRight' }}>
1616
<ModalHOC>

packages/forklift-console-plugin/src/modules/Plans/views/list/PlanRow.tsx

+16-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { FC } from 'react';
22
import { ConsoleTimestamp } from 'src/components';
33
import { TableCell } from 'src/modules/Providers/utils';
44

@@ -10,12 +10,14 @@ import { PlanData } from '../../utils';
1010
import {
1111
ActionsCell,
1212
CellProps,
13+
MigrationTypeCell,
1314
NamespaceCell,
1415
PlanCell,
1516
PlanStatusCell,
1617
ProviderLinkCell,
1718
VMsCell,
1819
} from './components';
20+
import { PlanTableResourceId } from './constants';
1921

2022
export const PlanRow: React.FC<RowProps<PlanData>> = ({ resourceFields, resourceData }) => {
2123
return (
@@ -38,19 +40,22 @@ const renderTd = ({ resourceData, resourceFieldId, resourceFields }: RenderTdPro
3840
);
3941
};
4042

41-
const cellRenderers: Record<string, React.FC<CellProps>> = {
42-
['name']: PlanCell,
43-
['namespace']: NamespaceCell,
44-
['migration-started']: (props: CellProps) => {
43+
const cellRenderers: Partial<Record<PlanTableResourceId, FC<CellProps>>> = {
44+
[PlanTableResourceId.Name]: PlanCell,
45+
[PlanTableResourceId.Namespace]: NamespaceCell,
46+
[PlanTableResourceId.MigrationStarted]: (props: CellProps) => {
4547
const value = getResourceFieldValue(props.data, props.fieldId, props.fields);
4648
return <ConsoleTimestamp timestamp={value} />;
4749
},
48-
['destination']: ProviderLinkCell,
49-
['source']: ProviderLinkCell,
50-
['phase']: PlanStatusCell,
51-
['vms']: VMsCell,
52-
['description']: ({ data }: CellProps) => <TableCell>{data?.obj?.spec?.description}</TableCell>,
53-
['actions']: ActionsCell,
50+
[PlanTableResourceId.Destination]: ProviderLinkCell,
51+
[PlanTableResourceId.Source]: ProviderLinkCell,
52+
[PlanTableResourceId.Phase]: PlanStatusCell,
53+
[PlanTableResourceId.MigrationType]: MigrationTypeCell,
54+
[PlanTableResourceId.Vms]: VMsCell,
55+
[PlanTableResourceId.Description]: ({ data }: CellProps) => (
56+
<TableCell>{data?.obj?.spec?.description}</TableCell>
57+
),
58+
[PlanTableResourceId.Actions]: ActionsCell,
5459
};
5560

5661
interface RenderTdProps {

0 commit comments

Comments
 (0)