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

Introduce numeric search field in table filters #9376

Merged
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
65 changes: 65 additions & 0 deletions web/html/src/components/table/NumericSearchField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { useState } from "react";

import { Select } from "components/input";

type Matcher = {
label: string;
value: string;
};

const matchers: Matcher[] = [
{ label: t("less than"), value: "<" },
{ label: t("less than or equal to"), value: "<=" },
{ label: t("equal to"), value: "=" },
{ label: t("greater than or equal to"), value: ">=" },
{ label: t("greater than"), value: ">" },
{ label: t("not equal to"), value: "!=" },
];

const resultingCriteria = (matcher: string, value: string) => {
return matcher && value && value.trim() !== "" ? matcher + value : null;
};

export const NumericSearchField = ({ name, criteria, onSearch }) => {
let criteriaMatcher = "";
let criteriaValue = "";
if (criteria) {
criteriaMatcher = matchers.find((it) => criteria.startsWith(it.value))?.value ?? "=";
criteriaValue = criteria.includes(criteriaMatcher) ? criteria.split(criteriaMatcher)[1] : criteria;
}

const [matcher, setMatcher] = useState<string>(criteriaMatcher);
const [value, setValue] = useState<string>(criteriaValue);

const handleMatcherChange = (selectedMatcher: string) => {
setMatcher(selectedMatcher);
onSearch(resultingCriteria(selectedMatcher, value));
};
const handleValueChange = (newValue: string) => {
setValue(newValue);
onSearch(resultingCriteria(matcher, newValue));
};
return (
<div className="row">
<div className="col-sm-6">
<Select
name="matcher"
placeholder={t("Matcher")}
defaultValue={matcher}
options={matchers}
onChange={(_name: string | undefined, value: string) => handleMatcherChange(value)}
/>
</div>

<div className="col">
<input
className="form-control"
value={value || ""}
type="number"
onChange={(e) => handleValueChange(e.target.value)}
name={name}
/>
</div>
</div>
);
};
10 changes: 6 additions & 4 deletions web/html/src/components/table/SelectSearchField.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from "react";
import { useEffect, useState } from "react";

import { Select } from "components/input";

Expand All @@ -15,9 +15,11 @@ export const SelectSearchField = ({ label, criteria, options, onSearch }) => {
const allOptions = [ALL_OPTION].concat(options);

// Avoid invalid value selected when changing field.
if (!allOptions.some((it) => it.value === searchValue)) {
handleSearchValueChange(ALL_OPTION.value);
}
useEffect(() => {
if (!allOptions.some((it) => it.value === searchValue)) {
handleSearchValueChange(ALL_OPTION.value);
}
}, [searchValue, allOptions, onSearch]);

return (
<Select
Expand Down
34 changes: 32 additions & 2 deletions web/html/src/components/table/TableFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,34 @@ import { Select } from "components/input";
import { Form } from "components/input/form/Form";
import { SelectSearchField } from "components/table/SelectSearchField";

const renderSearchField = ({ filterOptions, field, criteria, onSearch, placeholder, name }) => {
import { NumericSearchField } from "./NumericSearchField";

export enum FilterOptionType {
TEXT,
SELECT,
NUMERIC,
}

type FilterOption = {
label: string;
value: string;
type?: FilterOptionType;
filterOptions?: Array<any>;
};

type SearchFieldProps = {
filterOptions: Array<FilterOption>;
field: any;
criteria: any;
onSearch: any;
placeholder: any;
name: any;
};

const renderSearchField = (props: SearchFieldProps) => {
const { filterOptions, field, criteria, onSearch, placeholder, name } = props;
const selectedOption = filterOptions.find((it) => it.value === field);
if (selectedOption?.filterOptions) {
if (selectedOption?.type === FilterOptionType.SELECT) {
return (
<SelectSearchField
label={selectedOption.label}
Expand All @@ -16,6 +41,11 @@ const renderSearchField = ({ filterOptions, field, criteria, onSearch, placehold
/>
);
}

if (selectedOption?.type === FilterOptionType.NUMERIC) {
return <NumericSearchField name={name} criteria={criteria} onSearch={onSearch} />;
}

return (
<div className="form-group">
<input
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { FilterOptionType } from "components/table/TableFilter";

// Value options for filtering by Target Type in recurring actions list
const ORG_OPTION = { value: "ORG", label: t("Organization") };
const GROUP_OPTION = { value: "GROUP", label: t("Group") };
Expand All @@ -10,9 +12,29 @@ const CUSTOM_STATE_OPTION = { value: "CUSTOM_STATE", label: t("Custom State") };
export const ACTION_TYPE_OPTIONS = [CUSTOM_STATE_OPTION, HIGHSTATE_OPTION];

// Field options for filtering in recurring actions list.
const SCHEDULE_NAME_OPTION = { value: "schedule_name", label: t("Schedule Name") };
const TARGET_NAME_OPTION = { value: "target_name", label: t("Target Name") };
export const TARGET_TYPE_OPTION = { value: "target_type", label: t("Target Type"), filterOptions: TARGET_TYPE_OPTIONS };
export const ACTION_TYPE_OPTION = { value: "action_type", label: t("Action Type"), filterOptions: ACTION_TYPE_OPTIONS };
const SCHEDULE_NAME_OPTION = {
value: "schedule_name",
label: t("Schedule Name"),
type: FilterOptionType.TEXT,
};

const TARGET_NAME_OPTION = {
value: "target_name",
label: t("Target Name"),
type: FilterOptionType.TEXT,
};

const TARGET_TYPE_OPTION = {
value: "target_type",
label: t("Target Type"),
type: FilterOptionType.SELECT,
filterOptions: TARGET_TYPE_OPTIONS,
};
export const ACTION_TYPE_OPTION = {
value: "action_type",
label: t("Action Type"),
type: FilterOptionType.SELECT,
filterOptions: ACTION_TYPE_OPTIONS,
};

export const SEARCH_FIELD_OPTIONS = [SCHEDULE_NAME_OPTION, TARGET_TYPE_OPTION, TARGET_NAME_OPTION, ACTION_TYPE_OPTION];
36 changes: 23 additions & 13 deletions web/html/src/manager/systems/list-filter.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TableFilter } from "components/table/TableFilter";
import { FilterOptionType, TableFilter } from "components/table/TableFilter";

const SYSTEM_KIND_OPTIONS = [
{ value: "mgr_server", label: t("Manager Server") },
Expand Down Expand Up @@ -38,18 +38,28 @@ const YES_NO_OPTIONS = [
];

const allListOptions = [
{ value: "server_name", label: t("System") },
{ value: "system_kind", label: t("System Kind"), filterOptions: SYSTEM_KIND_OPTIONS },
{ value: "status_type", label: t("Updates"), filterOptions: STATUS_TYPE_OPTIONS },
{ value: "total_errata_count", label: t("Patches") },
{ value: "outdated_packages", label: t("Packages") },
{ value: "extra_pkg_count", label: t("Extra Packages") },
{ value: "config_files_with_differences", label: t("Config Diffs") },
{ value: "channel_labels", label: t("Base Channel") },
{ value: "entitlement_level", label: t("System Type"), filterOptions: SYSTEM_TYPE_OPTIONS },
{ value: "requires_reboot", label: t("Requires Reboot"), filterOptions: YES_NO_OPTIONS },
{ value: "created_days", label: t("Registered Days") },
{ value: "group_count", label: t("Groups") },
{ value: "server_name", label: t("System"), type: FilterOptionType.TEXT },
{ value: "system_kind", label: t("System Kind"), type: FilterOptionType.SELECT, filterOptions: SYSTEM_KIND_OPTIONS },
{ value: "status_type", label: t("Updates"), type: FilterOptionType.SELECT, filterOptions: STATUS_TYPE_OPTIONS },
{ value: "total_errata_count", label: t("Patches"), type: FilterOptionType.NUMERIC },
{ value: "outdated_packages", label: t("Packages"), type: FilterOptionType.NUMERIC },
{ value: "extra_pkg_count", label: t("Extra Packages"), type: FilterOptionType.NUMERIC },
{ value: "config_files_with_differences", label: t("Config Diffs"), type: FilterOptionType.NUMERIC },
{ value: "channel_labels", label: t("Base Channel"), type: FilterOptionType.TEXT },
{
value: "entitlement_level",
label: t("System Type"),
type: FilterOptionType.SELECT,
filterOptions: SYSTEM_TYPE_OPTIONS,
},
{
value: "requires_reboot",
label: t("Requires Reboot"),
type: FilterOptionType.SELECT,
filterOptions: YES_NO_OPTIONS,
},
{ value: "created_days", label: t("Registered Days"), type: FilterOptionType.NUMERIC },
{ value: "group_count", label: t("Groups"), type: FilterOptionType.NUMERIC },
];

const virtualSystemsListOptions = [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Introduce numeric search field in table filters
Loading