Skip to content

Commit

Permalink
Add some pv selection table optimizations and (#1530)
Browse files Browse the repository at this point in the history
enhancements.

Fixed filtering not working on claim name source
storage class.

Signed-off-by: Alexander Wels <[email protected]>
  • Loading branch information
awels authored Jan 29, 2025
1 parent 1a3712a commit 770e2de
Show file tree
Hide file tree
Showing 10 changed files with 126 additions and 62 deletions.
1 change: 1 addition & 0 deletions src/app/common/components/SimpleSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const SimpleSelect: React.FunctionComponent<ISimpleSelectProps> = ({
key={option.toString()}
value={option}
description={(option as OptionWithValue)?.props?.description}
isDisabled={(option as OptionWithValue)?.props?.isDisabled}
{...(typeof option === 'object' && (option as OptionWithValue).props)}
/>
))}
Expand Down
10 changes: 6 additions & 4 deletions src/app/common/duck/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { useState, useRef, useEffect } from 'react';
import { PaginationProps } from '@patternfly/react-core';
import { IFilterValues, FilterCategory } from '../components/FilterToolbar';
import { ISortBy, SortByDirection } from '@patternfly/react-table';
import { useEffect, useRef, useState } from 'react';
import { FilterCategory, IFilterValues } from '../components/FilterToolbar';

// TODO these could be given generic types to avoid using `any` (https://www.typescriptlang.org/docs/handbook/generics.html)

Expand All @@ -13,10 +12,13 @@ export const useFilterState = (items: any[], filterCategories: FilterCategory[])
const values = filterValues[categoryKey];
if (!values || values.length === 0) return true;
const filterCategory = filterCategories.find((category) => category.key === categoryKey);
let itemValue = item[categoryKey];
let itemValue = item['pvc'][categoryKey];
if (filterCategory.getItemValue) {
itemValue = filterCategory.getItemValue(item);
}
if (categoryKey == 'storageClass') {
itemValue = item['storageClass'];
}
return values.every((filterValue) => {
if (!itemValue) return false;
const lowerCaseItemValue = String(itemValue).toLowerCase();
Expand Down
42 changes: 42 additions & 0 deletions src/app/home/pages/PlansPage/components/Wizard/ClaimDisplay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { PackageIcon, UnknownIcon, VirtualMachineIcon } from '@patternfly/react-icons';
import React from 'react';
import { IPlanPersistentVolume } from '../../../../../plan/duck/types';
import { pvcNameToString } from '../../helpers';

const styles = require('./PVStorageClassSelect.module').default;

interface IClaimDisplayProps {
pv: IPlanPersistentVolume;
}

export const ClaimDisplay: React.FunctionComponent<IClaimDisplayProps> = ({
pv,
}: IClaimDisplayProps) => {
if (!pv) {
return null;
}
if (!pv.pvc.ownerType) {
return <span> {pvcNameToString(pv.pvc)}</span>;
}
if (pv.pvc.ownerType === 'VirtualMachine') {
return (
<>
<VirtualMachineIcon title="Virtual Machine" />
<span> {pvcNameToString(pv.pvc)}</span>
</>
);
} else if (pv.pvc.ownerType === 'Unknown') {
return (
<>
<UnknownIcon title="Unknown" />
<span> {pvcNameToString(pv.pvc)}</span>
</>
);
}
return (
<>
<PackageIcon title={pv.pvc.ownerType} />
<span> {pvcNameToString(pv.pvc)}</span>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ export const PVAccessModeSelect: React.FunctionComponent<IPVAccessModeSelectProp
storageClasses,
}: IPVAccessModeSelectProps) => {
const { values, setFieldValue } = useFormikContext<IFormValues>();
currentPV = currentPV || pv;

const currentStorageClass = values.pvStorageClassAssignment[currentPV.name];
const volumeAccessModes = currentStorageClass.volumeAccessModes;
const currentVolumeMode = currentStorageClass.volumeMode;
const possibleAccessModes = volumeAccessModes.find(
const currentStorageClass =
values.pvStorageClassAssignment[currentPV?.name] || ({} as IMigPlanStorageClass);
const volumeAccessModes = currentStorageClass?.volumeAccessModes || [];
const currentVolumeMode = currentStorageClass?.volumeMode || 'auto';
const possibleAccessModes = volumeAccessModes?.find(
(volumeAccessMode: IVolumeAccessModes) => volumeAccessMode.volumeMode === currentVolumeMode
) || { accessModes: [] as string[] };

Expand All @@ -46,7 +48,9 @@ export const PVAccessModeSelect: React.FunctionComponent<IPVAccessModeSelectProp
})),
];
accessModeOptions.splice(1, 1); // remove ReadOnly option
accessModeOptions.splice(0, 0, { value: 'auto', toString: () => 'Auto' });
if (currentPV.pvc.ownerType === 'VirtualMachine') {
accessModeOptions.splice(0, 0, { value: 'auto', toString: () => 'Auto' });
}

return (
<SimpleSelect
Expand All @@ -56,6 +60,7 @@ export const PVAccessModeSelect: React.FunctionComponent<IPVAccessModeSelectProp
onChange={(option: any) => onAccessModeChange(currentPV, option.value)}
options={accessModeOptions}
placeholderText="Select volume mode..."
isDisabled={currentPV.pvc.ownerType !== 'VirtualMachine'}
value={
accessModeOptions.find(
(option) => currentStorageClass && option.value === currentStorageClass.accessMode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,47 @@ export const PVStorageClassSelect: React.FunctionComponent<IPVStorageClassSelect
storageClasses,
}: IPVStorageClassSelectProps) => {
const { values, setFieldValue } = useFormikContext<IFormValues>();

const currentStorageClass = values.pvStorageClassAssignment[currentPV.name];
currentPV = currentPV || pv;
const currentStorageClass =
values.pvStorageClassAssignment[currentPV?.name] || ({} as IMigPlanStorageClass);

const onStorageClassChange = (currentPV: IPlanPersistentVolume, value: string) => {
const newSc = storageClasses.find((sc) => sc.name === value) || '';
const copy = JSON.parse(JSON.stringify(newSc));
copy.volumeMode = 'auto';
copy.accessMode = 'auto';
if (currentPV?.pvc.ownerType === 'VirtualMachine') {
copy.volumeMode = 'auto';
copy.accessMode = 'auto';
} else {
copy.volumeMode = currentStorageClass.volumeMode;
copy.accessMode = currentStorageClass.accessMode;
}
const updatedAssignment = {
...values.pvStorageClassAssignment,
[currentPV.name]: copy,
[currentPV?.name]: copy,
};
setFieldValue('pvStorageClassAssignment', updatedAssignment);
};

const storageClassOptions: OptionWithValue[] = [
...storageClasses.map((storageClass) => ({
value: storageClass.name,
toString: () => targetStorageClassToString(storageClass),
props: { description: storageClass.provisioner },
})),
...storageClasses
.filter(
(storageClass) =>
currentPV?.pvc.ownerType === 'VirtualMachine' ||
(storageClass.volumeAccessModes.find(
(vam) =>
vam.volumeMode === currentPV?.pvc.volumeMode || currentPV?.pvc.volumeMode === 'auto'
) &&
storageClass.volumeAccessModes.find(
(vam) =>
vam.accessModes.includes(currentPV?.pvc.accessModes[0]) ||
currentPV?.pvc.accessModes[0] === 'auto'
))
)
.map((storageClass) => ({
value: storageClass.name,
toString: () => targetStorageClassToString(storageClass),
props: { description: storageClass.provisioner },
})),
];

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,35 @@ export const PVVolumeModeSelect: React.FunctionComponent<IPVVolumeModeSelectProp
storageClasses,
}: IPVVolumeModeSelectProps) => {
const { values, setFieldValue } = useFormikContext<IFormValues>();
currentPV = currentPV || pv;

const currentStorageClass = values.pvStorageClassAssignment[currentPV.name];
const volumeAccessModes = currentStorageClass.volumeAccessModes;
const currentStorageClass =
values.pvStorageClassAssignment[currentPV?.name] || ({} as IMigPlanStorageClass);
const volumeAccessModes = currentStorageClass?.volumeAccessModes || [];

const onVolumeModeChange = (currentPV: IPlanPersistentVolume, value: string) => {
currentStorageClass.volumeMode = value;
const updatedAssignment = {
...values.pvStorageClassAssignment,
[currentPV.name]: currentStorageClass,
};
setFieldValue('pvStorageClassAssignment', updatedAssignment);
if (currentStorageClass) {
currentStorageClass.volumeMode = value;
const updatedAssignment = {
...values.pvStorageClassAssignment,
[currentPV.name]: currentStorageClass,
};
setFieldValue('pvStorageClassAssignment', updatedAssignment);
}
};

if (!volumeAccessModes) {
return null;
}
const volumeModeOptions: OptionWithValue[] = [
...volumeAccessModes.map((volumeAccessMode: IVolumeAccessModes) => ({
value: volumeAccessMode.volumeMode,
toString: () => volumeAccessMode.volumeMode,
})),
];
volumeModeOptions.splice(0, 0, { value: 'auto', toString: () => 'Auto' });
if (currentPV.pvc.ownerType === 'VirtualMachine') {
volumeModeOptions.splice(0, 0, { value: 'auto', toString: () => 'Auto' });
}

return (
<SimpleSelect
Expand All @@ -51,6 +60,7 @@ export const PVVolumeModeSelect: React.FunctionComponent<IPVVolumeModeSelectProp
onChange={(option: any) => onVolumeModeChange(currentPV, option.value)}
options={volumeModeOptions}
placeholderText="Select volume mode..."
isDisabled={currentPV.pvc.ownerType !== 'VirtualMachine'}
value={
volumeModeOptions.find(
(option) => currentStorageClass && option.value === currentStorageClass.volumeMode
Expand Down
19 changes: 1 addition & 18 deletions src/app/home/pages/PlansPage/components/Wizard/VolumesForm.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
import { StatusIcon } from '@konveyor/lib-ui';
import {
Alert,
Bullseye,
EmptyState,
Grid,
GridItem,
Spinner,
Title,
} from '@patternfly/react-core';
import { Bullseye, EmptyState, Grid, GridItem, Spinner, Title } from '@patternfly/react-core';
import { useFormikContext } from 'formik';
import { isEmpty } from 'lodash';
import React, { useEffect } from 'react';
Expand Down Expand Up @@ -131,15 +123,6 @@ const VolumesForm: React.FunctionComponent<IOtherProps> = (props) => {
</Bullseye>
);
}
if (planState.currentPlanStatus.state === 'Critical') {
return (
<Bullseye>
<EmptyState variant="large">
<Alert variant="danger" isInline title={planState.currentPlanStatus.errorMessage} />
</EmptyState>
</Bullseye>
);
}
return (
<VolumesTable
isEdit={props.isEdit}
Expand Down
22 changes: 11 additions & 11 deletions src/app/home/pages/PlansPage/components/Wizard/VolumesTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import {
targetStorageClassToString,
targetVolumeModeToString,
} from '../../helpers';
import { ClaimDisplay } from './ClaimDisplay';
import { PVAccessModeSelect } from './PVAccessModeSelect';
import { PVStorageClassSelect } from './PVStorageClassSelect';
import { PVVolumeModeSelect } from './PVVolumeModeSelect';
Expand Down Expand Up @@ -193,18 +194,12 @@ const VolumesTable: React.FunctionComponent<IVolumesTableProps> = ({
const commonFilterCategories: FilterCategory[] = [
{
key: 'name',
title: 'PV name',
type: FilterType.search,
placeholderText: 'Filter by PV name...',
},
{
key: 'claim',
title: 'Claim',
type: FilterType.search,
placeholderText: 'Filter by claim...',
},
{
key: 'project',
key: 'namespace',
title: 'Namespace',
type: FilterType.search,
placeholderText: 'Filter by namespace...',
Expand Down Expand Up @@ -412,7 +407,9 @@ const VolumesTable: React.FunctionComponent<IVolumesTableProps> = ({
const cells = isSCC
? [
pv.name,
pvcNameToString(pv.pvc),
{
title: <ClaimDisplay {...{ pv }} />,
},
pv.pvc.namespace,
// Storage class can be empty here if none exists/ none selected initially
pv.storageClass || '',
Expand Down Expand Up @@ -578,14 +575,15 @@ const VolumesTable: React.FunctionComponent<IVolumesTableProps> = ({
<Thead>
<Tr>
<Th
width={10}
select={{
onSelect: onSelectAll,
isSelected: allRowsSelected,
}}
/>
{columns
.filter((column, columnIndex) => columnIndex !== 0)
.filter(
(column, columnIndex) => !isSCC || (columnIndex !== 0 && columnIndex !== 8)
)
.map((column, columnIndex) => (
<Th key={columnIndex} width={10}>
{column.title}
Expand All @@ -608,7 +606,9 @@ const VolumesTable: React.FunctionComponent<IVolumesTableProps> = ({
}}
/>
{row.cells
.filter((column, columnIndex) => columnIndex !== 0)
.filter(
(column, columnIndex) => !isSCC || (columnIndex !== 0 && columnIndex !== 8)
)
.map((cell, cellIndex) => {
const shiftedIndex = cellIndex + 1;
console.log('cell', cell);
Expand Down
10 changes: 5 additions & 5 deletions src/app/plan/duck/reducers.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { PlanActions, PlanActionTypes } from './actions';
import dayjs from 'dayjs';
import { PlanActions, PlanActionTypes } from './actions';

import {
defaultAddEditStatus,
fetchingAddEditStatus,
IAddEditStatus,
} from '../../common/add_edit_state';
import { IMigHook } from '../../home/pages/HooksPage/types';
import {
IPlan,
ISourceClusterNamespace,
IMigPlan,
IPersistentVolumeResource,
IMigration,
IPersistentVolumeResource,
IPlan,
ISourceClusterNamespace,
} from './types';
import { IMigHook } from '../../home/pages/HooksPage/types';

export enum CurrentPlanState {
Pending = 'Pending',
Expand Down
1 change: 1 addition & 0 deletions src/app/plan/duck/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface IPlanPersistentVolume {
name: string;
volumeMode: string;
accessModes: string[];
ownerType: string;
};
storageClass?: string;
capacity: string;
Expand Down

0 comments on commit 770e2de

Please sign in to comment.