From 571ca6f387119e8493baf92bf659d38f8aaf302e Mon Sep 17 00:00:00 2001 From: elforjani3 Date: Thu, 31 Dec 2020 12:24:37 +0200 Subject: [PATCH 1/5] feat: account suggest field. --- client/src/components/AccountsSuggestField.js | 130 ++++++++++++++++++ .../DataTableCells/AccountsListFieldCell.js | 5 +- 2 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 client/src/components/AccountsSuggestField.js diff --git a/client/src/components/AccountsSuggestField.js b/client/src/components/AccountsSuggestField.js new file mode 100644 index 000000000..7fba6a5cb --- /dev/null +++ b/client/src/components/AccountsSuggestField.js @@ -0,0 +1,130 @@ +import React, { useState, useCallback, useEffect, useMemo } from 'react'; +import { MenuItem } from '@blueprintjs/core'; +import { Suggest } from '@blueprintjs/select'; +import { isEmpty } from 'lodash'; + +import classNames from 'classnames'; +import { CLASSES } from 'common/classes'; + +import { FormattedMessage as T } from 'react-intl'; + +export default function AccountsSuggestField({ + accounts, + initialAccountId, + selectedAccountId, + defaultSelectText = 'Select account', + onAccountSelected, + + filterByRootTypes = [], + filterByTypes = [], + filterByNormal, + popoverFill = false, + + ...suggestProps +}) { + // Filters accounts based on filter props. + const filteredAccounts = useMemo(() => { + let filteredAccounts = [...accounts]; + + if (!isEmpty(filterByRootTypes)) { + filteredAccounts = filteredAccounts.filter( + (account) => filterByRootTypes.indexOf(account.type.root_type) !== -1, + ); + } + if (!isEmpty(filterByTypes)) { + filteredAccounts = filteredAccounts.filter( + (account) => filterByTypes.indexOf(account.type.key) !== -1, + ); + } + if (!isEmpty(filterByNormal)) { + filteredAccounts = filteredAccounts.filter( + (account) => + filterByTypes.indexOf(account.type.normal) === filterByNormal, + ); + } + return filteredAccounts; + }, [accounts, filterByRootTypes, filterByTypes, filterByNormal]); + + // Find initial account object to set it as default account in initial render. + const initialAccount = useMemo( + () => filteredAccounts.find((a) => a.id === initialAccountId), + [initialAccountId, filteredAccounts], + ); + + const [selectedAccount, setSelectedAccount] = useState( + initialAccount || null, + ); + + useEffect(() => { + if (typeof selectedAccountId !== 'undefined') { + const account = selectedAccountId + ? filteredAccounts.find((a) => a.id === selectedAccountId) + : null; + setSelectedAccount(account); + } + }, [selectedAccountId, filteredAccounts, setSelectedAccount]); + + // Filters accounts items. + const filterAccountsPredicater = useCallback( + (query, account, _index, exactMatch) => { + const normalizedTitle = account.name.toLowerCase(); + const normalizedQuery = query.toLowerCase(); + + if (exactMatch) { + return normalizedTitle === normalizedQuery; + } else { + return ( + `${account.code} ${normalizedTitle}`.indexOf(normalizedQuery) >= 0 + ); + } + }, + [], + ); + + // Account item of select accounts field. + const accountItem = useCallback((item, { handleClick, modifiers, query }) => { + return ( + + ); + }, []); + + const handleInputValueRenderer = (inputValue) => { + if (inputValue) { + return inputValue.name.toString(); + } + return ''; + }; + + const onAccountSelect = useCallback( + (account) => { + setSelectedAccount({ ...account }); + onAccountSelected && onAccountSelected(account); + }, + [setSelectedAccount, onAccountSelected], + ); + + return ( + } />} + itemRenderer={accountItem} + itemPredicate={filterAccountsPredicater} + onItemSelect={onAccountSelect} + selectedItem={selectedAccount} + inputProps={{ placeholder: defaultSelectText }} + resetOnClose={true} + fill={true} + popoverProps={{ minimal: true }} + {...suggestProps} + inputValueRenderer={handleInputValueRenderer} + className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, { + [CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill, + })} + /> + ); +} diff --git a/client/src/components/DataTableCells/AccountsListFieldCell.js b/client/src/components/DataTableCells/AccountsListFieldCell.js index 4757d6e01..c5c2a6dcc 100644 --- a/client/src/components/DataTableCells/AccountsListFieldCell.js +++ b/client/src/components/DataTableCells/AccountsListFieldCell.js @@ -1,5 +1,6 @@ import React, { useCallback, useMemo } from 'react'; -import AccountsSelectList from 'components/AccountsSelectList'; +import AccountsSuggestField from 'components/AccountsSuggestField'; +// import AccountsSelectList from 'components/AccountsSelectList'; import classNames from 'classnames'; import { FormGroup, Classes, Intent } from '@blueprintjs/core'; @@ -42,7 +43,7 @@ const AccountCellRenderer = ({ Classes.FILL, )} > - Date: Thu, 31 Dec 2020 12:25:21 +0200 Subject: [PATCH 2/5] feat:contacts suggest field. --- client/src/components/ContactsSuggestField.js | 106 ++++++++++++++++++ .../DataTableCells/ContactsListFieldCell.js | 5 +- 2 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 client/src/components/ContactsSuggestField.js diff --git a/client/src/components/ContactsSuggestField.js b/client/src/components/ContactsSuggestField.js new file mode 100644 index 000000000..cb8187920 --- /dev/null +++ b/client/src/components/ContactsSuggestField.js @@ -0,0 +1,106 @@ +import React, { useCallback, useState, useEffect, useMemo } from 'react'; +import { MenuItem } from '@blueprintjs/core'; +import { Suggest } from '@blueprintjs/select'; + +import { FormattedMessage as T } from 'react-intl'; +import classNames from 'classnames'; +import { CLASSES } from 'common/classes'; + +export default function ContactsSuggestField({ + contactsList, + initialContactId, + selectedContactId, + defaultTextSelect = 'Select contact', + onContactSelected, + + selectedContactType = [], + popoverFill = false, + + ...suggestProps +}) { + // filteredContacts + const contacts = useMemo( + () => + contactsList.map((contact) => ({ + ...contact, + _id: `${contact.id}_${contact.contact_type}`, + })), + [contactsList], + ); + + const initialContact = useMemo( + () => contacts.find((a) => a.id === initialContactId), + [initialContactId, contacts], + ); + + const [selecetedContact, setSelectedContact] = useState( + initialContact || null, + ); + + useEffect(() => { + if (typeof selectedContactId !== 'undefined') { + const contact = selectedContactId + ? contacts.find((a) => a.id === selectedContactId) + : null; + setSelectedContact(contact); + } + }, [selectedContactId, contacts, setSelectedContact]); + + const contactRenderer = useCallback( + (contact, { handleClick }) => ( + + ), + [], + ); + + const onContactSelect = useCallback( + (contact) => { + setSelectedContact({ ...contact }); + onContactSelected && onContactSelected(contact); + }, + [setSelectedContact, onContactSelected], + ); + + const handleInputValueRenderer = (inputValue) => { + if (inputValue) { + return inputValue.display_name.toString(); + } + }; + + const filterContacts = (query, contact, index, exactMatch) => { + const normalizedTitle = contact.display_name.toLowerCase(); + const normalizedQuery = query.toLowerCase(); + if (exactMatch) { + return normalizedTitle === normalizedQuery; + } else { + return ( + `${contact.display_name} ${normalizedTitle}`.indexOf(normalizedQuery) >= + 0 + ); + } + }; + + return ( + } />} + itemRenderer={contactRenderer} + itemPredicate={filterContacts} + onItemSelect={onContactSelect} + selectedItem={selecetedContact} + inputProps={{ placeholder: defaultTextSelect }} + resetOnClose={true} + // fill={true} + popoverProps={{ minimal: true }} + {...suggestProps} + inputValueRenderer={handleInputValueRenderer} + className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, { + [CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill, + })} + /> + ); +} diff --git a/client/src/components/DataTableCells/ContactsListFieldCell.js b/client/src/components/DataTableCells/ContactsListFieldCell.js index 16eb5286b..14676e0f8 100644 --- a/client/src/components/DataTableCells/ContactsListFieldCell.js +++ b/client/src/components/DataTableCells/ContactsListFieldCell.js @@ -1,7 +1,8 @@ -import React, { useState, useCallback, useMemo } from 'react'; +import React, { useCallback } from 'react'; import { FormGroup, Intent, Classes } from '@blueprintjs/core'; import classNames from 'classnames'; import { ContactSelecetList } from 'components'; +import ContactsSuggestField from 'components/ContactsSuggestField'; export default function ContactsListCellRenderer({ column: { id }, @@ -30,7 +31,7 @@ export default function ContactsListCellRenderer({ Classes.FILL, )} > - Date: Thu, 31 Dec 2020 12:25:46 +0200 Subject: [PATCH 3/5] feat: items suggest filed. --- .../DataTableCells/ItemsListCell.js | 8 +- client/src/components/ItemsSuggestField.js | 110 ++++++++++++++++++ client/src/lang/en/index.js | 1 + 3 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 client/src/components/ItemsSuggestField.js diff --git a/client/src/components/DataTableCells/ItemsListCell.js b/client/src/components/DataTableCells/ItemsListCell.js index 5a64203bd..ba77db9c3 100644 --- a/client/src/components/DataTableCells/ItemsListCell.js +++ b/client/src/components/DataTableCells/ItemsListCell.js @@ -1,9 +1,9 @@ -import React, { useCallback, useMemo } from 'react'; -import ItemsListField from 'components/ItemsListField'; +import React, { useCallback } from 'react'; +// import ItemsListField from 'components/ItemsListField'; +import ItemsSuggestField from 'components/ItemsSuggestField'; import classNames from 'classnames'; import { FormGroup, Classes, Intent } from '@blueprintjs/core'; - export default function ItemsListCell({ column: { id, filterSellable, filterPurchasable }, row: { index }, @@ -24,7 +24,7 @@ export default function ItemsListCell({ intent={error ? Intent.DANGER : null} className={classNames('form-group--select-list', Classes.FILL)} > - { + let filteredItems = [...items]; + + if (sellable) { + filteredItems = filteredItems.filter((item) => item.sellable); + } + if (purchasable) { + filteredItems = filteredItems.filter((item) => item.purchasable); + } + return filteredItems; + }, [items, sellable, purchasable]); + + // Find initial item object. + const initialItem = useMemo( + () => filteredItems.some((a) => a.id === initialItemId), + [initialItemId], + ); + + const [selectedItem, setSelectedItem] = useState(initialItem || null); + + const onItemSelect = useCallback( + (item) => { + setSelectedItem({ ...item }); + onItemSelected && onItemSelected(item); + }, + [setSelectedItem, onItemSelected], + ); + + const itemRenderer = useCallback((item, { modifiers, handleClick }) => ( + + )); + + useEffect(() => { + if (typeof selectedItemId !== 'undefined') { + const item = selectedItemId + ? filteredItems.find((a) => a.id === selectedItemId) + : null; + setSelectedItem(item); + } + }, [selectedItemId, filteredItems, setSelectedItem]); + + const handleInputValueRenderer = (inputValue) => { + if (inputValue) { + return inputValue.name.toString(); + } + return ''; + }; + + // Filters items. + const filterItemsPredicater = useCallback( + (query, item, _index, exactMatch) => { + const normalizedTitle = item.name.toLowerCase(); + const normalizedQuery = query.toLowerCase(); + + if (exactMatch) { + return normalizedTitle === normalizedQuery; + } else { + return normalizedTitle.indexOf(normalizedQuery) >= 0; + } + }, + [], + ); + return ( + } />} + itemRenderer={itemRenderer} + itemPredicate={filterItemsPredicater} + inputValueRenderer={handleInputValueRenderer} + selectedItem={selectedItem} + onItemSelect={onItemSelect} + inputProps={{ placeholder: defautlSelectText }} + resetOnClose={true} + fill={true} + {...suggestProps} + popoverProps={{ minimal: true }} + className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, { + [CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill, + })} + /> + ); +} diff --git a/client/src/lang/en/index.js b/client/src/lang/en/index.js index 757673bac..936b3ce89 100644 --- a/client/src/lang/en/index.js +++ b/client/src/lang/en/index.js @@ -934,4 +934,5 @@ export default { opening_average_cost: 'Opening average cost', opening_cost_: 'Opening cost ', opening_date_: 'Opening date ', + no_results:'No results.' }; From bb8b2c4743e067388bd1beb634d146f928d991f6 Mon Sep 17 00:00:00 2001 From: elforjani3 Date: Thu, 31 Dec 2020 14:48:54 +0200 Subject: [PATCH 4/5] fix: suggestProps --- client/src/components/AccountsSuggestField.js | 2 +- client/src/components/ContactsSuggestField.js | 2 +- client/src/components/ItemsSuggestField.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/components/AccountsSuggestField.js b/client/src/components/AccountsSuggestField.js index 7fba6a5cb..c0757d8b0 100644 --- a/client/src/components/AccountsSuggestField.js +++ b/client/src/components/AccountsSuggestField.js @@ -120,11 +120,11 @@ export default function AccountsSuggestField({ resetOnClose={true} fill={true} popoverProps={{ minimal: true }} - {...suggestProps} inputValueRenderer={handleInputValueRenderer} className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, { [CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill, })} + {...suggestProps} /> ); } diff --git a/client/src/components/ContactsSuggestField.js b/client/src/components/ContactsSuggestField.js index cb8187920..4269d21ff 100644 --- a/client/src/components/ContactsSuggestField.js +++ b/client/src/components/ContactsSuggestField.js @@ -96,11 +96,11 @@ export default function ContactsSuggestField({ resetOnClose={true} // fill={true} popoverProps={{ minimal: true }} - {...suggestProps} inputValueRenderer={handleInputValueRenderer} className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, { [CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill, })} + {...suggestProps} /> ); } diff --git a/client/src/components/ItemsSuggestField.js b/client/src/components/ItemsSuggestField.js index 5c91f21e4..f5f898943 100644 --- a/client/src/components/ItemsSuggestField.js +++ b/client/src/components/ItemsSuggestField.js @@ -100,11 +100,11 @@ export default function ItemsSuggestField({ inputProps={{ placeholder: defautlSelectText }} resetOnClose={true} fill={true} - {...suggestProps} popoverProps={{ minimal: true }} className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, { [CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill, })} + {...suggestProps} /> ); } From 5b48e16031b5db94727f8807386556fb4a5827cf Mon Sep 17 00:00:00 2001 From: elforjani3 Date: Thu, 31 Dec 2020 14:51:50 +0200 Subject: [PATCH 5/5] fix: edit payment Receive path --- .../src/containers/Sales/PaymentReceive/PaymentReceiveList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/containers/Sales/PaymentReceive/PaymentReceiveList.js b/client/src/containers/Sales/PaymentReceive/PaymentReceiveList.js index 8b3d36a88..8161a7db0 100644 --- a/client/src/containers/Sales/PaymentReceive/PaymentReceiveList.js +++ b/client/src/containers/Sales/PaymentReceive/PaymentReceiveList.js @@ -87,7 +87,7 @@ function PaymentReceiveList({ }, [deletePaymentReceive, requestDeletePaymentReceive, formatMessage]); const handleEditPaymentReceive = useCallback((payment) => { - history.push(`/payment-receive/${payment.id}/edit`); + history.push(`/payment-receives/${payment.id}/edit`); }); // Calculates the selected rows count.