WIP Advanced filter.

This commit is contained in:
Ahmed Bouhuolia
2020-03-23 23:00:37 +02:00
parent 52f94c30f5
commit 2ab714127a
7 changed files with 180 additions and 17 deletions

View File

@@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React, { useMemo, useState } from 'react';
import Icon from 'components/Icon';
import {
Button,
@@ -19,23 +19,32 @@ import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
import DialogConnect from 'connectors/Dialog.connector';
import AccountsConnect from 'connectors/Accounts.connector';
import {compose} from 'utils';
import FilterDropdown from 'components/FilterDropdown';
import ResourceConnect from 'connectors/Resource.connector';
function AccountsActionsBar({
openDialog,
views,
bulkActions,
getResourceFields,
onFilterChange,
}) {
const {path} = useRouteMatch();
const onClickNewAccount = () => { openDialog('account-form', {}); };
const accountsFields = getResourceFields('accounts');
const viewsMenuItems = views.map((view) => {
return (<MenuItem href={`${path}/${view.id}/custom_view`} text={view.name} />);
});
const hasBulkActionsSelected = useMemo(() => {
return Object.keys(bulkActions).length > 0;
}, [bulkActions]);
const filterDropdown = FilterDropdown({
fields: accountsFields,
onFilterChange,
});
return (
<DashboardActionsBar>
<NavbarGroup>
@@ -60,6 +69,18 @@ function AccountsActionsBar({
text="New Account"
onClick={onClickNewAccount} />
<Popover
content={filterDropdown}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_LEFT}>
<Button
className={classNames(Classes.MINIMAL, 'button--filter')}
text="Filter"
icon={ <Icon icon="filter" /> } />
</Popover>
{hasBulkActionsSelected && (
<Button
className={Classes.MINIMAL}
@@ -95,5 +116,6 @@ const mapStateToProps = (state) => {
export default compose(
DialogConnect,
AccountsConnect,
ResourceConnect,
connect(mapStateToProps),
)(AccountsActionsBar);

View File

@@ -26,6 +26,7 @@ import ViewConnect from 'connectors/View.connector';
import LoadingIndicator from 'components/LoadingIndicator';
function AccountsDataTable({
filterConditions,
accounts,
onDeleteAccount,
onInactiveAccount,
@@ -43,9 +44,17 @@ function AccountsDataTable({
// Fetch accounts list according to the given custom view id.
const fetchHook = useAsync(async () => {
await Promise.all([
fetchAccounts({ custom_view_id: customViewId }),
fetchAccounts({
custom_view_id: customViewId,
stringified_filter_roles: JSON.stringify(filterConditions) || '',
}),
]);
});
useEffect(() => {
fetchHook.execute();
}, [filterConditions]);
// Refetch accounts list after custom view id change.
useEffect(() => {
const viewMeta = getViewItem(customViewId);

View File

@@ -0,0 +1,127 @@
import React, {useEffect, useMemo} from 'react';
import {
FormGroup,
InputGroup,
Classes,
HTMLSelect,
Button,
Intent,
} from "@blueprintjs/core"
import { useFormik } from 'formik';
import { isEqual } from 'lodash';
import { usePrevious } from 'react-use';
import Icon from 'components/Icon';
export default function FilterDropdown({
fields,
onFilterChange,
}) {
const conditionalsItems = [
{ value: 'and', label: 'AND' },
{ value: 'or', label: 'OR' },
];
const resourceFields = [
...fields.map((field) => ({ value: field.key, label: field.label_name, })),
];
const compatatorsItems = [
{value: '', label: 'Select a compatator'},
{value: 'equals', label: 'Equals'},
{value: 'not_equal', label: 'Not Equal'},
{value: 'contain', label: 'Contain'},
{value: 'not_contain', label: 'Not Contain'},
];
const defaultFilterCondition = {
condition: 'and',
field_key: fields.length > 0 ? fields[0].key : '',
compatator: 'equals',
value: '',
};
const formik = useFormik({
initialValues: {
conditions: [ defaultFilterCondition ],
},
});
const onClickNewFilter = () => {
formik.setFieldValue('conditions', [
...formik.values.conditions, defaultFilterCondition,
]);
};
const filteredFilterConditions = useMemo(() => {
return formik.values.conditions.filter(condition => !!condition.value);
}, [formik.values.conditions]);
const prevConditions = usePrevious(filteredFilterConditions);
const onClickRemoveCondition = (index) => () => {
if (formik.values.conditions.length === 1) { return; }
const conditions = [ ...formik.values.conditions ];
conditions.splice(index, 1);
formik.setFieldValue('conditions', [ ...conditions ]);
}
useEffect(() => {
if (!isEqual(filteredFilterConditions, prevConditions)) {
onFilterChange(filteredFilterConditions);
}
}, [filteredFilterConditions])
return (
<div class="filter-dropdown">
<div class="filter-dropdown__body">
{formik.values.conditions.map((condition, index) => (
<div class="filter-dropdown__condition">
<FormGroup
className={'form-group--condition'}>
<HTMLSelect
options={conditionalsItems}
className={Classes.FILL}
disabled={index > 1}
{...formik.getFieldProps(`conditions[${index}].condition`)} />
</FormGroup>
<FormGroup
className={'form-group--field'}>
<HTMLSelect
options={resourceFields}
value={1}
className={Classes.FILL}
{...formik.getFieldProps(`conditions[${index}].field_key`)} />
</FormGroup>
<FormGroup
className={'form-group--compatator'}>
<HTMLSelect
options={compatatorsItems}
className={Classes.FILL}
{...formik.getFieldProps(`conditions[${index}].compatator`)} />
</FormGroup>
<FormGroup
className={'form-group--value'}>
<InputGroup
placeholder="Value"
{...formik.getFieldProps(`conditions[${index}].value`)} />
</FormGroup>
<Button
icon={<Icon icon="times" />}
iconSize={14}
minimal={true}
onClick={onClickRemoveCondition(index)} />
</div>
))}
</div>
<div class="filter-dropdown__footer">
<Button
minimal={true}
intent={Intent.PRIMARY}
onClick={onClickNewFilter}>
+ New Conditional
</Button>
</div>
</div>
)
}

View File

@@ -16,6 +16,7 @@ import DashboardActionsBar from 'components/Accounts/AccountsActionsBar';
import AccountsConnect from 'connectors/Accounts.connector';
import DashboardConnect from 'connectors/Dashboard.connector';
import CustomViewConnect from 'connectors/CustomView.connector';
import ResourceConnect from 'connectors/Resource.connector';
import { compose } from 'utils';
function AccountsChart({
@@ -23,7 +24,9 @@ function AccountsChart({
fetchAccounts,
deleteAccount,
inactiveAccount,
fetchResourceViews
fetchResourceViews,
fetchResourceFields,
getResourceFields,
}) {
const [state, setState] = useState({
deleteAlertActive: false,
@@ -32,9 +35,12 @@ function AccountsChart({
targetAccount: {},
});
const [filterConditions, setFilterConditions] = useState([]);
const fetchHook = useAsync(async () => {
await Promise.all([
fetchResourceViews('accounts'),
fetchResourceFields('accounts'),
]);
});
@@ -106,9 +112,11 @@ function AccountsChart({
const handleDeleteBulkAccounts = (accounts) => {
};
const handleFilterChange = (conditions) => { setFilterConditions(conditions); };
return (
<DashboardInsider loading={fetchHook.pending} name={'accounts-chart'}>
<DashboardActionsBar />
<DashboardActionsBar onFilterChange={handleFilterChange} />
<DashboardPageContent>
<Switch>
<Route
@@ -118,8 +126,9 @@ function AccountsChart({
'/dashboard/accounts'
]}>
<AccountsViewsTabs onDeleteBulkAccounts={handleDeleteBulkAccounts} />
<AccountsDataTable
filterConditions={filterConditions}
onDeleteAccount={handleDeleteAccount}
onInactiveAccount={handleInactiveAccount}
onRestoreAccount={handleRestoreAccount}
@@ -176,5 +185,6 @@ function AccountsChart({
export default compose(
AccountsConnect,
CustomViewConnect,
ResourceConnect,
DashboardConnect,
)(AccountsChart);

View File

@@ -4,7 +4,6 @@ import t from 'store/types';
export const fetchResourceColumns = ({ resourceSlug }) => {
return (dispatch) => new Promise((resolve, reject) => {
ApiService.get(`resources/${resourceSlug}/columns`).then((response) => {
console.log(t.RESOURCE_COLUMNS_SET);
dispatch({
type: t.RESOURCE_COLUMNS_SET,
columns: response.data.resource_columns,