From fa56cff72d60fe91e2b909d133e565d7e4c32333 Mon Sep 17 00:00:00 2001 From: Hubert Date: Tue, 10 Jun 2025 11:27:49 +0200 Subject: [PATCH 1/5] WiP --- public/locales/en.json | 2 +- src/components/ControlPlane/FluxList.tsx | 2 + .../ControlPlane/ManagedResources.tsx | 3 +- src/components/ControlPlane/Providers.tsx | 105 +++++++++++++++--- .../ControlPlane/ProvidersConfig.tsx | 1 + src/components/Projects/ProjectsList.tsx | 1 + 6 files changed, 97 insertions(+), 17 deletions(-) diff --git a/public/locales/en.json b/public/locales/en.json index 773d7800..d6e49e35 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -294,7 +294,7 @@ }, "yaml": { "copiedToClipboard": "YAML copied to clipboard!", - "YAML": "YAML" + "YAML": "File" }, "createMCP": { "dialogTitle": "Create Managed Control Plane", diff --git a/src/components/ControlPlane/FluxList.tsx b/src/components/ControlPlane/FluxList.tsx index 772fa47f..bbd7bf13 100644 --- a/src/components/ControlPlane/FluxList.tsx +++ b/src/components/ControlPlane/FluxList.tsx @@ -85,6 +85,7 @@ export default function FluxList() { hAlign: 'Center', width: 85, accessor: 'yaml', + disableFilters: true, Cell: (cellData: CellData) => ( ), @@ -127,6 +128,7 @@ export default function FluxList() { hAlign: 'Center', width: 85, accessor: 'yaml', + disableFilters: true, Cell: (cellData: CellData) => ( ), diff --git a/src/components/ControlPlane/ManagedResources.tsx b/src/components/ControlPlane/ManagedResources.tsx index 3ad92b71..2ef3ba92 100644 --- a/src/components/ControlPlane/ManagedResources.tsx +++ b/src/components/ControlPlane/ManagedResources.tsx @@ -92,8 +92,9 @@ export function ManagedResources() { hAlign: 'Center', width: 85, accessor: 'yaml', + disableFilters: true, Cell: (cellData: CellData) => - !!cellData.cell.row.original?.item ? ( + cellData.cell.row.original?.item ? ( ) : undefined, }, diff --git a/src/components/ControlPlane/Providers.tsx b/src/components/ControlPlane/Providers.tsx index 5242c9f0..756c78bb 100644 --- a/src/components/ControlPlane/Providers.tsx +++ b/src/components/ControlPlane/Providers.tsx @@ -1,27 +1,31 @@ +import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { AnalyticalTable, AnalyticalTableColumnDefinition, AnalyticalTableScaleWidthMode, Title, + Select, + Option, + Icon, } from '@ui5/webcomponents-react'; + import useResource from '../../lib/api/useApiResource'; import IllustratedError from '../Shared/IllustratedError'; -import '@ui5/webcomponents-icons/dist/sys-enter-2'; -import '@ui5/webcomponents-icons/dist/sys-cancel-2'; import { ProvidersListRequest } from '../../lib/api/types/crossplane/listProviders'; import { resourcesInterval } from '../../lib/shared/constants'; import { timeAgo } from '../../utils/i18n/timeAgo'; import { ResourceStatusCell } from '../Shared/ResourceStatusCell'; - import { YamlViewButton } from '../Yaml/YamlViewButton.tsx'; -import { useMemo } from 'react'; + +import '@ui5/webcomponents-icons/dist/sys-enter-2'; +import '@ui5/webcomponents-icons/dist/sys-cancel-2'; interface CellData { cell: { - value: T | null; // null for grouping rows + value: T | null; row: { - original?: ProvidersRow; // missing for grouping rows + original?: ProvidersRow; }; }; } @@ -29,9 +33,9 @@ interface CellData { type ProvidersRow = { name: string; version: string; - healthy: boolean; + healthy: string; healthyTransitionTime: string; - installed: boolean; + installed: string; installedTransitionTime: string; created: string; item: unknown; @@ -67,10 +71,46 @@ export function Providers() { accessor: 'installed', hAlign: 'Center', width: 85, + Filter: ({ column }) => ( + + ), + filter: 'equals', Cell: (cellData: CellData) => cellData.cell.row.original?.installed != null ? ( ( + + ), + filter: 'equals', Cell: (cellData: CellData) => cellData.cell.row.original?.installed != null ? ( ) : null, }, - { Header: t('yaml.YAML'), hAlign: 'Center', width: 85, accessor: 'yaml', + disableFilters: true, Cell: (cellData: CellData) => ( ), }, ], - [], + [t], ); const rows: ProvidersRow[] = @@ -115,9 +191,9 @@ export function Providers() { return { name: item.metadata.name, created: timeAgo.format(new Date(item.metadata.creationTimestamp)), - installed: installed?.status === 'True', + installed: installed?.status === 'True' ? 'true' : 'false', installedTransitionTime: installed?.lastTransitionTime ?? '', - healthy: healthy?.status === 'True', + healthy: healthy?.status === 'True' ? 'true' : 'false', healthyTransitionTime: healthy?.lastTransitionTime ?? '', version: item.spec.package.match(/\d+(\.\d+)+/g)?.toString() ?? '', item: item, @@ -138,7 +214,6 @@ export function Providers() { scaleWidthMode={AnalyticalTableScaleWidthMode.Smart} loading={isLoading} filterable - // Prevent the table from resetting when the data changes retainColumnWidth reactTableOptions={{ autoResetHiddenColumns: false, diff --git a/src/components/ControlPlane/ProvidersConfig.tsx b/src/components/ControlPlane/ProvidersConfig.tsx index 04056e58..a888220b 100644 --- a/src/components/ControlPlane/ProvidersConfig.tsx +++ b/src/components/ControlPlane/ProvidersConfig.tsx @@ -76,6 +76,7 @@ export function ProvidersConfig() { hAlign: 'Center', width: 85, accessor: 'yaml', + disableFilters: true, Cell: (cellData: CellData) => cellData.cell.row.original?.resource ? ( ( From 62978c5cff59c9b068ae97093e6118a9aad0645e Mon Sep 17 00:00:00 2001 From: Hubert Date: Tue, 17 Jun 2025 09:50:14 +0200 Subject: [PATCH 2/5] Adding Status filter --- src/components/ControlPlane/FluxList.tsx | 3 + .../ControlPlane/ManagedResources.tsx | 3 + src/components/ControlPlane/Providers.tsx | 76 +------------ src/components/Shared/StatusFilter.tsx | 103 ++++++++++++++++++ 4 files changed, 112 insertions(+), 73 deletions(-) create mode 100644 src/components/Shared/StatusFilter.tsx diff --git a/src/components/ControlPlane/FluxList.tsx b/src/components/ControlPlane/FluxList.tsx index bbd7bf13..8f36a2a5 100644 --- a/src/components/ControlPlane/FluxList.tsx +++ b/src/components/ControlPlane/FluxList.tsx @@ -16,6 +16,7 @@ import { timeAgo } from '../../utils/i18n/timeAgo.ts'; import { ResourceStatusCell } from '../Shared/ResourceStatusCell.tsx'; import { YamlViewButton } from '../Yaml/YamlViewButton.tsx'; import { useMemo } from 'react'; +import StatusFilter from '../Shared/StatusFilter.tsx'; export default function FluxList() { const { @@ -68,6 +69,7 @@ export default function FluxList() { accessor: 'status', width: 85, hAlign: 'Center', + Filter: ({ column }) => , Cell: (cellData: CellData) => cellData.cell.row.original?.isReady != null ? ( , Cell: (cellData: CellData) => cellData.cell.row.original?.isReady != null ? ( { cell: { @@ -66,6 +67,7 @@ export function ManagedResources() { accessor: 'synced', hAlign: 'Center', width: 85, + Filter: ({ column }) => , Cell: (cellData: CellData) => cellData.cell.row.original?.synced != null ? ( , Cell: (cellData: CellData) => cellData.cell.row.original?.ready != null ? ( { cell: { @@ -71,41 +69,7 @@ export function Providers() { accessor: 'installed', hAlign: 'Center', width: 85, - Filter: ({ column }) => ( - - ), + Filter: ({ column }) => , filter: 'equals', Cell: (cellData: CellData) => cellData.cell.row.original?.installed != null ? ( @@ -122,41 +86,7 @@ export function Providers() { accessor: 'healthy', hAlign: 'Center', width: 85, - Filter: ({ column }) => ( - - ), + Filter: ({ column }) => , filter: 'equals', Cell: (cellData: CellData) => cellData.cell.row.original?.installed != null ? ( diff --git a/src/components/Shared/StatusFilter.tsx b/src/components/Shared/StatusFilter.tsx new file mode 100644 index 00000000..76f74270 --- /dev/null +++ b/src/components/Shared/StatusFilter.tsx @@ -0,0 +1,103 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Select, Option, Icon } from '@ui5/webcomponents-react'; + +export interface StatusFilterProps { + column: { + filterValue?: string; + setFilter?: (value?: string) => void; + }; +} + +interface SelectChangeEventDetail { + selectedOption: HTMLElement & { + dataset?: { + value?: string; + }; + }; +} + +type SelectChangeEvent = CustomEvent; + +interface OptionProps { + value: string; + iconName: string; + color: string; + labelKey: string; + isSelected: boolean; +} + +const StatusFilter: React.FC = ({ column }) => { + const { t } = useTranslation(); + + const handleChange = (e: SelectChangeEvent) => { + const selectedOption = e.detail.selectedOption; + if (!selectedOption || !column.setFilter) return; + + const val = selectedOption.dataset?.value; + column.setFilter(val === 'all' ? undefined : val); + }; + + const renderOption = ({ + value, + iconName, + color, + labelKey, + isSelected, + }: OptionProps) => ( + + ); + + return ( + + ); +}; + +export default StatusFilter; From 22f62109553d2bf9d002ec5602870a141827838a Mon Sep 17 00:00:00 2001 From: Hubert Date: Tue, 17 Jun 2025 11:14:29 +0200 Subject: [PATCH 3/5] refactor --- src/components/ControlPlane/FluxList.tsx | 2 +- .../ControlPlane/ManagedResources.tsx | 2 +- src/components/ControlPlane/Providers.tsx | 2 +- src/components/Shared/StatusFilter.tsx | 103 ------------------ .../StatusFilter/StatusFilter.module.css | 19 ++++ .../Shared/StatusFilter/StatusFilter.tsx | 64 +++++++++++ 6 files changed, 86 insertions(+), 106 deletions(-) delete mode 100644 src/components/Shared/StatusFilter.tsx create mode 100644 src/components/Shared/StatusFilter/StatusFilter.module.css create mode 100644 src/components/Shared/StatusFilter/StatusFilter.tsx diff --git a/src/components/ControlPlane/FluxList.tsx b/src/components/ControlPlane/FluxList.tsx index 8f36a2a5..621687f4 100644 --- a/src/components/ControlPlane/FluxList.tsx +++ b/src/components/ControlPlane/FluxList.tsx @@ -16,7 +16,7 @@ import { timeAgo } from '../../utils/i18n/timeAgo.ts'; import { ResourceStatusCell } from '../Shared/ResourceStatusCell.tsx'; import { YamlViewButton } from '../Yaml/YamlViewButton.tsx'; import { useMemo } from 'react'; -import StatusFilter from '../Shared/StatusFilter.tsx'; +import StatusFilter from '../Shared/StatusFilter/StatusFilter.tsx'; export default function FluxList() { const { diff --git a/src/components/ControlPlane/ManagedResources.tsx b/src/components/ControlPlane/ManagedResources.tsx index 26ed79eb..8531f21a 100644 --- a/src/components/ControlPlane/ManagedResources.tsx +++ b/src/components/ControlPlane/ManagedResources.tsx @@ -15,7 +15,7 @@ import { resourcesInterval } from '../../lib/shared/constants'; import { ResourceStatusCell } from '../Shared/ResourceStatusCell'; import { YamlViewButton } from '../Yaml/YamlViewButton.tsx'; import { useMemo } from 'react'; -import StatusFilter from '../Shared/StatusFilter.tsx'; +import StatusFilter from '../Shared/StatusFilter/StatusFilter.tsx'; interface CellData { cell: { diff --git a/src/components/ControlPlane/Providers.tsx b/src/components/ControlPlane/Providers.tsx index f0908f68..d9e08add 100644 --- a/src/components/ControlPlane/Providers.tsx +++ b/src/components/ControlPlane/Providers.tsx @@ -17,7 +17,7 @@ import { YamlViewButton } from '../Yaml/YamlViewButton.tsx'; import '@ui5/webcomponents-icons/dist/sys-enter-2'; import '@ui5/webcomponents-icons/dist/sys-cancel-2'; -import StatusFilter from '../Shared/StatusFilter.tsx'; +import StatusFilter from '../Shared/StatusFilter/StatusFilter.tsx'; interface CellData { cell: { diff --git a/src/components/Shared/StatusFilter.tsx b/src/components/Shared/StatusFilter.tsx deleted file mode 100644 index 76f74270..00000000 --- a/src/components/Shared/StatusFilter.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import { Select, Option, Icon } from '@ui5/webcomponents-react'; - -export interface StatusFilterProps { - column: { - filterValue?: string; - setFilter?: (value?: string) => void; - }; -} - -interface SelectChangeEventDetail { - selectedOption: HTMLElement & { - dataset?: { - value?: string; - }; - }; -} - -type SelectChangeEvent = CustomEvent; - -interface OptionProps { - value: string; - iconName: string; - color: string; - labelKey: string; - isSelected: boolean; -} - -const StatusFilter: React.FC = ({ column }) => { - const { t } = useTranslation(); - - const handleChange = (e: SelectChangeEvent) => { - const selectedOption = e.detail.selectedOption; - if (!selectedOption || !column.setFilter) return; - - const val = selectedOption.dataset?.value; - column.setFilter(val === 'all' ? undefined : val); - }; - - const renderOption = ({ - value, - iconName, - color, - labelKey, - isSelected, - }: OptionProps) => ( - - ); - - return ( - - ); -}; - -export default StatusFilter; diff --git a/src/components/Shared/StatusFilter/StatusFilter.module.css b/src/components/Shared/StatusFilter/StatusFilter.module.css new file mode 100644 index 00000000..321a3a52 --- /dev/null +++ b/src/components/Shared/StatusFilter/StatusFilter.module.css @@ -0,0 +1,19 @@ +.option { + padding: 6px 10px; +} + +.container { + display: flex; + align-items: center; + gap: 8px; +} + +.icon { + width: 16px; + height: 16px; + flex-shrink: 0; +} + +.label { + font-size: 14px; +} \ No newline at end of file diff --git a/src/components/Shared/StatusFilter/StatusFilter.tsx b/src/components/Shared/StatusFilter/StatusFilter.tsx new file mode 100644 index 00000000..1bf622be --- /dev/null +++ b/src/components/Shared/StatusFilter/StatusFilter.tsx @@ -0,0 +1,64 @@ +import { Icon, Option, Select } from '@ui5/webcomponents-react'; +import { useTranslation } from 'react-i18next'; +import styles from './StatusFilter.module.css'; + +interface StatusFilterProps { + column: { + filterValue?: string; + setFilter?: (value?: string | undefined) => void; + }; +} + +interface OptionConfig { + value: string; + iconName: string; + color: string; + labelKey: string; +} + +const StatusFilter: React.FC = ({ column }) => { + const { t } = useTranslation(); + + const options: OptionConfig[] = [ + { value: 'all', iconName: 'filter', color: 'gray', labelKey: 'All' }, + { + value: 'true', + iconName: 'sys-enter-2', + color: 'green', + labelKey: 'Enabled', + }, + { + value: 'false', + iconName: 'sys-cancel-2', + color: 'red', + labelKey: 'Disabled', + }, + ]; + + const handleChange = ( + e: CustomEvent<{ selectedOption: { dataset?: { value?: string } } }>, + ) => { + const value = e.detail.selectedOption.dataset?.value; + column.setFilter?.(value === 'all' ? undefined : value); + }; + + const renderOption = ({ value, iconName, color, labelKey }: OptionConfig) => ( + + ); + + return ; +}; + +export default StatusFilter; From 0c1610f8120b12e834ca82223434d84877397f5c Mon Sep 17 00:00:00 2001 From: Hubert Date: Tue, 17 Jun 2025 12:00:01 +0200 Subject: [PATCH 4/5] Refactoring 2 --- .../Shared/StatusFilter/RenderOption.tsx | 35 ++++++++++++++++++ .../Shared/StatusFilter/StatusFilter.tsx | 37 +++++++++---------- 2 files changed, 53 insertions(+), 19 deletions(-) create mode 100644 src/components/Shared/StatusFilter/RenderOption.tsx diff --git a/src/components/Shared/StatusFilter/RenderOption.tsx b/src/components/Shared/StatusFilter/RenderOption.tsx new file mode 100644 index 00000000..de653083 --- /dev/null +++ b/src/components/Shared/StatusFilter/RenderOption.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { Option, Icon } from '@ui5/webcomponents-react'; +import styles from './StatusFilter.module.css'; + +interface RenderOptionProps { + value: string; + iconName: string; + color: string; + labelKey: string; + t: (key: string) => string; + isSelected: boolean; +} + +const RenderOption: React.FC = ({ + value, + iconName, + color, + labelKey, + t, + isSelected, +}) => ( + +); + +export default RenderOption; diff --git a/src/components/Shared/StatusFilter/StatusFilter.tsx b/src/components/Shared/StatusFilter/StatusFilter.tsx index 1bf622be..4346aaae 100644 --- a/src/components/Shared/StatusFilter/StatusFilter.tsx +++ b/src/components/Shared/StatusFilter/StatusFilter.tsx @@ -1,11 +1,12 @@ -import { Icon, Option, Select } from '@ui5/webcomponents-react'; +import React from 'react'; import { useTranslation } from 'react-i18next'; -import styles from './StatusFilter.module.css'; +import { Select } from '@ui5/webcomponents-react'; +import RenderOption from './RenderOption'; interface StatusFilterProps { column: { filterValue?: string; - setFilter?: (value?: string | undefined) => void; + setFilter?: (value?: string) => void; }; } @@ -42,23 +43,21 @@ const StatusFilter: React.FC = ({ column }) => { column.setFilter?.(value === 'all' ? undefined : value); }; - const renderOption = ({ value, iconName, color, labelKey }: OptionConfig) => ( - + return ( + ); - - return ; }; export default StatusFilter; From 62eb95dd2dd2a09e997a79c07dd0f67ab7590468 Mon Sep 17 00:00:00 2001 From: Hubert Date: Tue, 17 Jun 2025 12:11:34 +0200 Subject: [PATCH 5/5] refactor 3 --- .../Shared/StatusFilter/StatusFilter.tsx | 46 +++++++++---------- ...dule.css => StatusFilterOption.module.css} | 0 ...enderOption.tsx => StatusFilterOption.tsx} | 6 +-- 3 files changed, 25 insertions(+), 27 deletions(-) rename src/components/Shared/StatusFilter/{StatusFilter.module.css => StatusFilterOption.module.css} (100%) rename src/components/Shared/StatusFilter/{RenderOption.tsx => StatusFilterOption.tsx} (79%) diff --git a/src/components/Shared/StatusFilter/StatusFilter.tsx b/src/components/Shared/StatusFilter/StatusFilter.tsx index 4346aaae..4e463fa5 100644 --- a/src/components/Shared/StatusFilter/StatusFilter.tsx +++ b/src/components/Shared/StatusFilter/StatusFilter.tsx @@ -1,7 +1,9 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { Select } from '@ui5/webcomponents-react'; -import RenderOption from './RenderOption'; +import StatusFilterOption, { + StatusFilterOptionProps, +} from './StatusFilterOption'; interface StatusFilterProps { column: { @@ -10,32 +12,28 @@ interface StatusFilterProps { }; } -interface OptionConfig { - value: string; - iconName: string; - color: string; - labelKey: string; -} +const options: Pick< + StatusFilterOptionProps, + 'value' | 'iconName' | 'color' | 'labelKey' +>[] = [ + { value: 'all', iconName: 'filter', color: 'gray', labelKey: 'All' }, + { + value: 'true', + iconName: 'sys-enter-2', + color: 'green', + labelKey: 'Enabled', + }, + { + value: 'false', + iconName: 'sys-cancel-2', + color: 'red', + labelKey: 'Disabled', + }, +]; const StatusFilter: React.FC = ({ column }) => { const { t } = useTranslation(); - const options: OptionConfig[] = [ - { value: 'all', iconName: 'filter', color: 'gray', labelKey: 'All' }, - { - value: 'true', - iconName: 'sys-enter-2', - color: 'green', - labelKey: 'Enabled', - }, - { - value: 'false', - iconName: 'sys-cancel-2', - color: 'red', - labelKey: 'Disabled', - }, - ]; - const handleChange = ( e: CustomEvent<{ selectedOption: { dataset?: { value?: string } } }>, ) => { @@ -46,7 +44,7 @@ const StatusFilter: React.FC = ({ column }) => { return (