diff --git a/App/FeatureSet/Dashboard/src/Components/Kubernetes/KubernetesResourceTable.tsx b/App/FeatureSet/Dashboard/src/Components/Kubernetes/KubernetesResourceTable.tsx index b9419e4cb0..33994d6711 100644 --- a/App/FeatureSet/Dashboard/src/Components/Kubernetes/KubernetesResourceTable.tsx +++ b/App/FeatureSet/Dashboard/src/Components/Kubernetes/KubernetesResourceTable.tsx @@ -14,7 +14,10 @@ import Link from "Common/UI/Components/Link/Link"; import SortOrder from "Common/Types/BaseDatabase/SortOrder"; import Route from "Common/Types/API/Route"; import Column from "Common/UI/Components/Table/Types/Column"; -import Input from "Common/UI/Components/Input/Input"; +import Filter from "Common/UI/Components/Filters/Types/Filter"; +import FilterData from "Common/UI/Components/Filters/Types/FilterData"; +import Search from "Common/Types/BaseDatabase/Search"; +import Includes from "Common/Types/BaseDatabase/Includes"; export interface ResourceColumn { title: string; @@ -101,22 +104,91 @@ const KubernetesResourceTable: FunctionComponent = ( const [currentPage, setCurrentPage] = useState(1); const [sortBy, setSortBy] = useState(null); const [sortOrder, setSortOrder] = useState(SortOrder.Ascending); - const [filterText, setFilterText] = useState(""); + const [showFilterModal, setShowFilterModal] = useState(false); + const [filterData, setFilterData] = useState< + FilterData + >({}); + + // Build filter definitions from data + const filters: Array> = useMemo(() => { + const result: Array> = [ + { + title: "Name", + key: "name", + type: FieldType.Text, + }, + ]; + + if (showNamespace) { + const namespaces: Array = Array.from( + new Set( + props.resources + .map((r: KubernetesResource) => { + return r.namespace; + }) + .filter(Boolean), + ), + ).sort(); + result.push({ + title: "Namespace", + key: "namespace", + type: FieldType.Dropdown, + filterDropdownOptions: namespaces.map((ns: string) => { + return { label: ns, value: ns }; + }), + }); + } + + if (showStatus) { + const statuses: Array = Array.from( + new Set( + props.resources + .map((r: KubernetesResource) => { + return r.status; + }) + .filter(Boolean), + ), + ).sort(); + result.push({ + title: "Status", + key: "status", + type: FieldType.Dropdown, + filterDropdownOptions: statuses.map((s: string) => { + return { label: s, value: s }; + }), + }); + } + + return result; + }, [props.resources, showNamespace, showStatus]); // Filter and sort data client-side const processedData: Array = useMemo(() => { let data: Array = [...props.resources]; - // Filter by search text - if (filterText.trim()) { - const search: string = filterText.toLowerCase().trim(); - data = data.filter((r: KubernetesResource) => { - return ( - r.name.toLowerCase().includes(search) || - r.namespace.toLowerCase().includes(search) || - r.status.toLowerCase().includes(search) - ); - }); + // Apply filters from filterData + for (const key of Object.keys(filterData) as Array< + keyof KubernetesResource + >) { + const value: unknown = filterData[key]; + if (!value) { + continue; + } + + if (value instanceof Search) { + const searchText: string = value.toString().toLowerCase(); + data = data.filter((r: KubernetesResource) => { + const fieldValue: string = (r[key] as string) || ""; + return fieldValue.toLowerCase().includes(searchText); + }); + } else if (value instanceof Includes) { + const includeValues: Array = + value.values as Array; + data = data.filter((r: KubernetesResource) => { + const fieldValue: string = (r[key] as string) || ""; + return includeValues.includes(fieldValue); + }); + } } // Sort @@ -141,7 +213,7 @@ const KubernetesResourceTable: FunctionComponent = ( } return data; - }, [props.resources, filterText, sortBy, sortOrder]); + }, [props.resources, filterData, sortBy, sortOrder]); // Paginate const paginatedData: Array = useMemo(() => { @@ -337,18 +409,10 @@ const KubernetesResourceTable: FunctionComponent = ( }); } + const hasActiveFilters: boolean = Object.keys(filterData).length > 0; + return ( -
- { - setFilterText(value); - setCurrentPage(1); - }} - value={filterText} - /> -
id={`kubernetes-${props.title.toLowerCase().replace(/\s+/g, "-")}-table`} columns={tableColumns} @@ -372,9 +436,22 @@ const KubernetesResourceTable: FunctionComponent = ( setSortBy(newSortBy as string | null); setSortOrder(newSortOrder); }} + filters={filters} + showFilterModal={showFilterModal} + filterData={filterData} + onFilterChanged={(newFilterData: FilterData) => { + setFilterData(newFilterData); + setCurrentPage(1); + }} + onFilterModalOpen={() => { + setShowFilterModal(true); + }} + onFilterModalClose={() => { + setShowFilterModal(false); + }} noItemsMessage={ - filterText - ? `No resources match "${filterText}".` + hasActiveFilters + ? "No resources match the current filters." : props.emptyMessage || "No resources found. Resources will appear here once the kubernetes-agent is sending data." }