Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check results filtering #799

Merged
merged 2 commits into from
Sep 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions assets/js/components/Button/Button.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const getSizeClasses = (size) => {

const getButtonClasses = (type) => {
switch (type) {
case 'transparent':
return 'bg-transparent hover:opacity-75 focus:outline-none w-full transition ease-in duration-200 font-semibold';
case 'secondary':
return 'bg-persimmon hover:opacity-75 focus:outline-none text-gray-800 w-full transition ease-in duration-200 text-center font-semibold rounded shadow';
default:
Expand Down
81 changes: 81 additions & 0 deletions assets/js/components/ClusterDetails/ChecksResultFilters.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React, { useState, useEffect } from 'react';
import Filter from '@components/Table/Filter';

export const RESULT_FILTER_FIELD = 'result';

export const useFilteredChecks = (cluster) => {
const [filtersPredicates, setFiltersPredicates] = useState([]);
const [filteredChecks, setFilteredChecks] = useState([]);

const filterChecks = (checks, predicates) => {
if (predicates.length === 0) return checks;

return checks.filter((check) =>
predicates.some((predicate) => predicate(check))
);
};

const checksForHost = (hostID) =>
filteredChecks
.filter((result) => result.host_id === hostID)
.map((result) => result.check_id);

useEffect(() => {
if (cluster?.checks_results.length > 0) {
const selectedCheckResults = cluster?.checks_results.filter((result) =>
cluster?.selected_checks.includes(result?.check_id)
);

setFilteredChecks(filterChecks(selectedCheckResults, filtersPredicates));
}
}, [cluster?.checks_results, cluster?.selected_checks, filtersPredicates]);

return {
setFiltersPredicates,
filteredChecksyByHost: checksForHost,
};
};

const ChecksResultFilters = ({ onChange }) => {
const [filtersForField, setFiltersForField] = useState({});

// This structure is the foundation for a multi field filters
// we can reuse later this structure in other parts of the application

useEffect(() => {
if (Object.keys(filtersForField).length >= 0) {
const filtersToApply = Object.keys(filtersForField).reduce(
(acc, curr) => {
return [...acc, ...filtersForField[curr].predicates];
},
[]
);

onChange(filtersToApply);
}
}, [filtersForField]);

return (
<div className="flex">
<Filter
key={RESULT_FILTER_FIELD}
title={'checks result'}
options={['passing', 'warning', 'critical', 'unknown']}
value={filtersForField[RESULT_FILTER_FIELD]?.values || []}
onChange={(list) => {
setFiltersForField((existingFilters) => ({
...existingFilters,
[RESULT_FILTER_FIELD]: {
predicates: list.map(
(value) => (checks) => checks[RESULT_FILTER_FIELD] === value
),
values: list,
},
}));
}}
/>
</div>
);
};

export default ChecksResultFilters;
39 changes: 27 additions & 12 deletions assets/js/components/ClusterDetails/ChecksResults.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import {
} from './ClusterDetails';
import { ExecutionIcon } from './ExecutionIcon';
import { getClusterName } from '@components/ClusterLink';
import ChecksResultFilters, {
useFilteredChecks,
} from '@components/ClusterDetails/ChecksResultFilters';

import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
Expand Down Expand Up @@ -86,6 +89,9 @@ export const ChecksResults = () => {

const hostname = getHostname(useSelector((state) => state.hostsList.hosts));

const { filteredChecksyByHost, setFiltersPredicates } =
useFilteredChecks(cluster);

useEffect(() => {
cluster?.provider && dispatchUpdateCatalog();
}, [cluster?.provider]);
Expand Down Expand Up @@ -136,6 +142,7 @@ export const ChecksResults = () => {
);
} else {
const lastExecution = sortHosts(cluster?.hosts_executions.slice());

pageContent = lastExecution.map(
({ _cluster_id, host_id, reachable, msg }, idx) => (
<div key={idx} className="flex flex-col">
Expand Down Expand Up @@ -179,7 +186,7 @@ export const ChecksResults = () => {
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{sortChecks(cluster?.selected_checks.slice()).map(
{sortChecks(filteredChecksyByHost(host_id)).map(
(checkId) => (
<tr
key={checkId}
Expand Down Expand Up @@ -234,24 +241,32 @@ export const ChecksResults = () => {
{findCheckDataByID(selectedCheck)?.remediation}
</ReactMarkdown>
</Modal>
<div className="flex mb-4">
<h1 className="text-3xl w-3/5">
<span className="font-medium">Checks Results for cluster</span>{' '}
<span className={`font-bold ${truncatedClusterNameClasses}`}>
{getClusterName(cluster)}
</span>
</h1>
<div className="flex w-2/5 justify-end text-white">
<div className="flex mb-8">
<div className="flex w-2/5">
<Button
className="w-1/3 bg-waterhole-blue text-white"
className="w-2/3 text-jungle-green-500 text-left py-0 px-0"
size="small"
type="transparent"
onClick={() => navigate(`/clusters/${cluster.id}`)}
>
<EOS_ARROW_BACK className="inline-block fill-white" /> Back to
Cluster
<EOS_ARROW_BACK className="inline-block fill-jungle-green-500" />{' '}
Back to Cluster Details
</Button>
</div>
</div>
<div className="flex mb-4 justify-between">
<h1 className="text-3xl w-3/5">
<span className="font-medium">Checks Results for cluster</span>{' '}
<span className={`font-bold ${truncatedClusterNameClasses}`}>
{getClusterName(cluster)}
</span>
</h1>
<ChecksResultFilters
onChange={(filtersPredicates) =>
setFiltersPredicates(filtersPredicates)
}
/>
</div>
{pageContent}
</div>
);
Expand Down