Skip to content

Commit

Permalink
PMM-12476 Cluster view search (#687)
Browse files Browse the repository at this point in the history
* PMM-12476 Add clusters search and filtering

* PMM-12476 Use correct type

* PMM-12476 Cleanup

* PMM-12476 Remove logging and dead code

* PMM-12476 Allow actions dialog to be visible
  • Loading branch information
matejkubinec authored Nov 27, 2023
1 parent 34b70c3 commit 43622fa
Show file tree
Hide file tree
Showing 19 changed files with 443 additions and 67 deletions.
5 changes: 5 additions & 0 deletions public/app/percona/inventory/Inventory.messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const Messages = {
monitoring: 'Monitoring',
address: 'Address',
port: 'Port',
cluster: 'Cluster',
},
actions: {
dashboard: 'Dashboard',
Expand All @@ -30,6 +31,10 @@ export const Messages = {
organizeByClusters: 'Organize by Clusters',
technicalPreview: '(Technical Preview) ',
},
clusters: {
empty: 'No clusters available',
noMatch: 'No clusters found',
},
agents: {
goBackToServices: 'Go back to services',
goBackToNodes: 'Go back to nodes',
Expand Down
10 changes: 8 additions & 2 deletions public/app/percona/inventory/Tabs/Services/ClusterItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import { ClusterItemProps } from './Clusters.type';
import { removeClusterFilters, shouldClusterBeExpanded } from './Clusters.utils';
import ServicesTable from './ServicesTable';

const ClusterItem: FC<ClusterItemProps> = ({ cluster, onDelete, onSelectionChange }) => {
const [isOpen, setIsOpen] = useState(shouldClusterBeExpanded(cluster.name));
const ClusterItem: FC<ClusterItemProps> = ({ cluster, onDelete, onSelectionChange, openByDefault }) => {
const [isOpen, setIsOpen] = useState(shouldClusterBeExpanded(cluster.name) || openByDefault);
const icon: IconName = cluster.type ? (DATABASE_ICONS[cluster.type] as IconName) : 'database';

const handleSelectionChange = useCallback(
Expand All @@ -27,6 +27,12 @@ const ClusterItem: FC<ClusterItemProps> = ({ cluster, onDelete, onSelectionChang
}
}, [isOpen, cluster.name]);

useEffect(() => {
if (openByDefault !== undefined) {
setIsOpen(openByDefault || shouldClusterBeExpanded(cluster.name));
}
}, [openByDefault, cluster.name]);

return (
<Collapse
collapsible
Expand Down
52 changes: 52 additions & 0 deletions public/app/percona/inventory/Tabs/Services/Clusters.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { ExtendedColumn, FilterFieldTypes } from 'app/percona/shared/components/Elements/Table';

import { Messages } from '../../Inventory.messages';
import { FlattenService } from '../../Inventory.types';

import { MONITORING_OPTIONS, STATUS_OPTIONS } from './Services.constants';

export const CLUSTERS_COLUMNS: Array<ExtendedColumn<FlattenService>> = [
{
Header: Messages.services.columns.serviceId,
id: 'serviceId',
accessor: 'serviceId',
type: FilterFieldTypes.TEXT,
},
{
Header: Messages.services.columns.cluster,
accessor: 'cluster',
type: FilterFieldTypes.TEXT,
},
{
Header: Messages.services.columns.status,
accessor: 'status',
type: FilterFieldTypes.DROPDOWN,
options: STATUS_OPTIONS,
},
{
Header: Messages.services.columns.serviceName,
accessor: 'serviceName',
type: FilterFieldTypes.TEXT,
},
{
Header: Messages.services.columns.nodeName,
accessor: 'nodeName',
type: FilterFieldTypes.TEXT,
},
{
Header: Messages.services.columns.monitoring,
accessor: 'agentsStatus',
type: FilterFieldTypes.RADIO_BUTTON,
options: MONITORING_OPTIONS,
},
{
Header: Messages.services.columns.address,
accessor: 'address',
type: FilterFieldTypes.TEXT,
},
{
Header: Messages.services.columns.port,
accessor: 'port',
type: FilterFieldTypes.TEXT,
},
];
41 changes: 32 additions & 9 deletions public/app/percona/inventory/Tabs/Services/Clusters.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import React, { FC, useCallback, useMemo, useState } from 'react';
import { Row } from 'react-table';

import { SearchFilter } from 'app/percona/shared/components/SearchFilter';

import { Messages } from '../../Inventory.messages';
import { FlattenService } from '../../Inventory.types';

import ClusterItem from './ClusterItem';
import { CLUSTERS_COLUMNS } from './Clusters.constants';
import { ClustersProps, ServicesCluster } from './Clusters.type';
import { getClustersFromServices } from './Clusters.utils';

const Clusters: FC<ClustersProps> = ({ services, onDelete, onSelectionChange }) => {
const clusters = useMemo(() => getClustersFromServices(services), [services]);
const [filtered, setFiltered] = useState(services);
const clusters = useMemo(() => getClustersFromServices(filtered), [filtered]);
const [selection, setSelection] = useState({});
const filterEnabled = filtered !== services;

const handleSelectionChange = useCallback(
(cluster: ServicesCluster, selectedServices: Array<Row<FlattenService>>) => {
Expand All @@ -25,16 +31,33 @@ const Clusters: FC<ClustersProps> = ({ services, onDelete, onSelectionChange })
[onSelectionChange]
);

const handleFiltering = useCallback((rows: FlattenService[]) => {
setFiltered(rows);
}, []);

return (
<div>
{clusters.map((cluster) => (
<ClusterItem
key={cluster.name}
cluster={cluster}
onDelete={onDelete}
onSelectionChange={handleSelectionChange}
/>
))}
<SearchFilter
tableKey="clusters-global"
rawData={services}
columns={CLUSTERS_COLUMNS}
onFilteredDataChange={handleFiltering}
/>
{clusters.length ? (
clusters.map((cluster) => (
<ClusterItem
key={cluster.name}
cluster={cluster}
onDelete={onDelete}
openByDefault={filterEnabled}
onSelectionChange={handleSelectionChange}
/>
))
) : filterEnabled ? (
<div>{Messages.clusters.noMatch}</div>
) : (
<div>{Messages.clusters.empty}</div>
)}
</div>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface ClustersProps {

export interface ClusterItemProps {
cluster: ServicesCluster;
openByDefault?: boolean;
onDelete: (service: FlattenService) => void;
onSelectionChange: (cluster: ServicesCluster, services: Array<Row<FlattenService>>) => void;
}
Expand Down
40 changes: 40 additions & 0 deletions public/app/percona/inventory/Tabs/Services/Services.constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ALL_LABEL, ALL_VALUE } from 'app/percona/shared/components/Elements/Table/Filter/Filter.constants';
import { ServiceStatus } from 'app/percona/shared/services/services/Services.types';

import { MonitoringStatus } from '../../Inventory.types';

export const ALL_OPTION = { value: ALL_VALUE, label: ALL_LABEL };

export const STATUS_OPTIONS = [
{
label: 'Up',
value: ServiceStatus.UP,
},
{
label: 'Down',
value: ServiceStatus.DOWN,
},
{
label: 'Unknown',
value: ServiceStatus.UNKNOWN,
},
{
label: 'N/A',
value: ServiceStatus.NA,
},
];

export const STATUS_OPTIONS_WITH_ALL = [ALL_OPTION, ...STATUS_OPTIONS];

export const MONITORING_OPTIONS = [
{
label: MonitoringStatus.OK,
value: MonitoringStatus.OK,
},
{
label: MonitoringStatus.FAILED,
value: MonitoringStatus.FAILED,
},
];

export const MONITORING_OPTIONS_WITH_ALL = [ALL_OPTION, ...MONITORING_OPTIONS];
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,22 @@ import {
buildEmptyValues,
buildParamsFromKey,
buildSearchOptions,
getFilteredData,
getQueryParams,
isInOptions,
isOtherThanTextType,
isValueInTextColumn,
} from './Filter.utils';
import { RadioButtonField } from './components/fields/RadioButtonField';
import { SearchTextField } from './components/fields/SearchTextField';
import { SelectColumnField } from './components/fields/SelectColumnField';
import { SelectDropdownField } from './components/fields/SelectDropdownField';

export const Filter = ({ columns, rawData, setFilteredData, hasBackendFiltering = false, tableKey }: FilterProps) => {
export const Filter = <T,>({
columns,
rawData,
setFilteredData,
hasBackendFiltering = false,
tableKey,
}: FilterProps<T>) => {
const [openCollapse, setOpenCollapse] = useState(false);
const [openSearchFields, setOpenSearchFields] = useState(false);
const styles = useStyles2(getStyles);
Expand Down Expand Up @@ -89,12 +94,7 @@ export const Filter = ({ columns, rawData, setFilteredData, hasBackendFiltering
useEffect(() => {
const queryParamsObj = getQueryParams(columns, queryParamsByKey);
if (Object.keys(queryParamsByKey).length > 0 && !hasBackendFiltering) {
const dataArray = rawData.filter(
(filterValue) =>
isValueInTextColumn(columns, filterValue, queryParamsObj) &&
isInOptions(columns, filterValue, queryParamsObj, FilterFieldTypes.DROPDOWN) &&
isInOptions(columns, filterValue, queryParamsObj, FilterFieldTypes.RADIO_BUTTON)
);
const dataArray = getFilteredData(rawData, columns, queryParamsObj);
setFilteredData(dataArray);
} else {
setFilteredData(rawData);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

import { ExtendedColumn } from '../Table.types';

export interface FilterProps {
export interface FilterProps<T> {
columns: Array<ExtendedColumn<any>>;
rawData: Object[];
setFilteredData: (data: Object[]) => void;
hasBackendFiltering: boolean;
rawData: T[];
setFilteredData: (data: T[]) => void;
hasBackendFiltering?: boolean;
tableKey?: string;
onFilterStateChange?: (isActive: boolean) => void;
}

// prevent additional usage of "any"
export type FilterFormValues = Record<string, any>;
Loading

0 comments on commit 43622fa

Please sign in to comment.