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..621687f4 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/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 ? ( ) => ( ), @@ -109,6 +112,7 @@ export default function FluxList() { accessor: 'status', width: 85, hAlign: 'Center', + Filter: ({ column }) => , Cell: (cellData: CellData) => cellData.cell.row.original?.isReady != null ? ( ) => ( ), diff --git a/src/components/ControlPlane/ManagedResources.tsx b/src/components/ControlPlane/ManagedResources.tsx index 3ad92b71..8531f21a 100644 --- a/src/components/ControlPlane/ManagedResources.tsx +++ b/src/components/ControlPlane/ManagedResources.tsx @@ -15,6 +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/StatusFilter.tsx'; interface CellData { 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 ? ( ) => - !!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..d9e08add 100644 --- a/src/components/ControlPlane/Providers.tsx +++ b/src/components/ControlPlane/Providers.tsx @@ -1,3 +1,4 @@ +import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { AnalyticalTable, @@ -5,23 +6,24 @@ import { AnalyticalTableScaleWidthMode, Title, } 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'; +import StatusFilter from '../Shared/StatusFilter/StatusFilter.tsx'; interface CellData { cell: { - value: T | null; // null for grouping rows + value: T | null; row: { - original?: ProvidersRow; // missing for grouping rows + original?: ProvidersRow; }; }; } @@ -29,9 +31,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 +69,12 @@ 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 +121,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 +144,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 ? ( ( diff --git a/src/components/Shared/StatusFilter/StatusFilter.tsx b/src/components/Shared/StatusFilter/StatusFilter.tsx new file mode 100644 index 00000000..4e463fa5 --- /dev/null +++ b/src/components/Shared/StatusFilter/StatusFilter.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Select } from '@ui5/webcomponents-react'; +import StatusFilterOption, { + StatusFilterOptionProps, +} from './StatusFilterOption'; + +interface StatusFilterProps { + column: { + filterValue?: string; + setFilter?: (value?: string) => void; + }; +} + +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 handleChange = ( + e: CustomEvent<{ selectedOption: { dataset?: { value?: string } } }>, + ) => { + const value = e.detail.selectedOption.dataset?.value; + column.setFilter?.(value === 'all' ? undefined : value); + }; + + return ( + + ); +}; + +export default StatusFilter; diff --git a/src/components/Shared/StatusFilter/StatusFilterOption.module.css b/src/components/Shared/StatusFilter/StatusFilterOption.module.css new file mode 100644 index 00000000..321a3a52 --- /dev/null +++ b/src/components/Shared/StatusFilter/StatusFilterOption.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/StatusFilterOption.tsx b/src/components/Shared/StatusFilter/StatusFilterOption.tsx new file mode 100644 index 00000000..ce18abc9 --- /dev/null +++ b/src/components/Shared/StatusFilter/StatusFilterOption.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { Option, Icon } from '@ui5/webcomponents-react'; +import styles from './StatusFilterOption.module.css'; + +export interface StatusFilterOptionProps { + 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;