diff --git a/client/src/components/Accounts/AccountsActionsBar.js b/client/src/components/Accounts/AccountsActionsBar.js index bf32148f3..f8322b585 100644 --- a/client/src/components/Accounts/AccountsActionsBar.js +++ b/client/src/components/Accounts/AccountsActionsBar.js @@ -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 (); }); - const hasBulkActionsSelected = useMemo(() => { return Object.keys(bulkActions).length > 0; }, [bulkActions]); + const filterDropdown = FilterDropdown({ + fields: accountsFields, + onFilterChange, + }); return ( @@ -60,6 +69,18 @@ function AccountsActionsBar({ text="New Account" onClick={onClickNewAccount} /> + + + } /> + + + {hasBulkActionsSelected && ( { export default compose( DialogConnect, AccountsConnect, + ResourceConnect, connect(mapStateToProps), )(AccountsActionsBar); \ No newline at end of file diff --git a/client/src/components/Accounts/AccountsDataTable.js b/client/src/components/Accounts/AccountsDataTable.js index 1fcd53253..d547ffa07 100644 --- a/client/src/components/Accounts/AccountsDataTable.js +++ b/client/src/components/Accounts/AccountsDataTable.js @@ -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); diff --git a/client/src/components/FilterDropdown.js b/client/src/components/FilterDropdown.js new file mode 100644 index 000000000..22aa69a3e --- /dev/null +++ b/client/src/components/FilterDropdown.js @@ -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 ( + + + {formik.values.conditions.map((condition, index) => ( + + + 1} + {...formik.getFieldProps(`conditions[${index}].condition`)} /> + + + + + + + + + + + + + + + } + iconSize={14} + minimal={true} + onClick={onClickRemoveCondition(index)} /> + + ))} + + + + + ) +} \ No newline at end of file diff --git a/client/src/containers/Dashboard/Accounts/AccountsChart.js b/client/src/containers/Dashboard/Accounts/AccountsChart.js index 4306c8fe6..6a489acea 100644 --- a/client/src/containers/Dashboard/Accounts/AccountsChart.js +++ b/client/src/containers/Dashboard/Accounts/AccountsChart.js @@ -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 ( - + - + { 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, diff --git a/server/src/database/seeds/seed_accounts_fields.js b/server/src/database/seeds/seed_accounts_fields.js index 4d6c0f547..2a5c3cbb9 100644 --- a/server/src/database/seeds/seed_accounts_fields.js +++ b/server/src/database/seeds/seed_accounts_fields.js @@ -5,8 +5,8 @@ exports.seed = function(knex) { .then(() => { // Inserts seed entries return knex('resource_fields').insert([ - {id: 1, label_name: 'Name', key: 'name', data_type: '', active: 1, predefined: 1}, - {id: 2, label_name: 'Code', key: 'code', data_type: '', active: 1, predefined: 1}, + {id: 1, label_name: 'Name', key: 'name', data_type: '', active: 1, predefined: 1} , + {id: 2, label_name: 'Code', key: 'code', data_type: '', active: 1, predefined: 1 }, {id: 3, label_name: 'Account Type', key: 'account_type_id', data_type: '', active: 1, predefined: 1}, {id: 4, label_name: 'Description', key: 'description', data_type: '', active: 1, predefined: 1}, ]); diff --git a/server/src/database/seeds/seed_resources_fields.js b/server/src/database/seeds/seed_resources_fields.js index 574e49a6e..b024f2cf3 100644 --- a/server/src/database/seeds/seed_resources_fields.js +++ b/server/src/database/seeds/seed_resources_fields.js @@ -8,6 +8,7 @@ exports.seed = (knex) => { id: 1, resource_id: 1, label_name: 'Account Name', + key: 'name', data_type: 'textbox', predefined: 1, columnable: true, @@ -16,6 +17,7 @@ exports.seed = (knex) => { id: 2, resource_id: 1, label_name: 'Code', + key: 'code', data_type: 'textbox', predefined: 1, columnable: true, @@ -24,23 +26,17 @@ exports.seed = (knex) => { id: 3, resource_id: 1, label_name: 'Type', + key: 'type', data_type: 'options', predefined: 1, columnable: true, }, - { - id: 4, - resource_id: 1, - label_name: 'Type', - data_type: 'normal', - predefined: 1, - columnable: true, - }, { id: 5, resource_id: 1, label_name: 'Description', data_type: 'textarea', + key: 'description', predefined: 1, columnable: true, },