diff --git a/src/components/BranchSuggestField.js b/src/components/BranchSuggestField.js new file mode 100644 index 000000000..1f4241692 --- /dev/null +++ b/src/components/BranchSuggestField.js @@ -0,0 +1,115 @@ +import React from 'react'; +import intl from 'react-intl-universal'; +import { MenuItem } from '@blueprintjs/core'; +import { Suggest } from '@blueprintjs/select'; +import { FormattedMessage as T } from 'components'; + +import classNames from 'classnames'; +import { CLASSES } from 'common/classes'; + +/** + * branch suggest field. + * @returns + */ +export default function BranchSuggestField({ + branches, + initialBranchId, + selectedBranchId, + defaultSelectText = intl.get('select_branch'), + popoverFill = false, + onBranchSelected, + ...suggestProps +}) { + const initialBranch = React.useMemo( + () => branches.some((b) => b.id === initialBranchId), + [initialBranchId, branches], + ); + + const [selectedBranch, setSelectedBranch] = React.useState( + initialBranch || null, + ); + + /** + * + * @param {*} branch + * @returns + */ + const branchItemRenderer = (branch, { handleClick, modifiers, query }) => { + return ( + + ); + }; + + /** + * + * @param {*} query + * @param {*} branch + * @param {*} _index + * @param {*} exactMatch + * @returns + */ + const branchItemPredicate = (query, branch, _index, exactMatch) => { + const normalizedTitle = branch.name.toLowerCase(); + const normalizedQuery = query.toLowerCase(); + + if (exactMatch) { + return normalizedTitle === normalizedQuery; + } else { + return `${branch.code}. ${normalizedTitle}`.indexOf(normalizedQuery) >= 0; + } + }; + + /** + * + * @param {*} branch + * @returns + */ + const brnachItemSelect = React.useCallback( + (branch) => { + if (branch.id) { + setSelectedBranch({ ...branch }); + onBranchSelected && onBranchSelected(branch); + } + }, + [setSelectedBranch, onBranchSelected], + ); + + /** + * + * @param {*} inputVaue + * @returns + */ + const branchInputValueRenderer = (inputValue) => { + if (inputValue) { + return inputValue.name.toString(); + } + return ''; + }; + + return ( + } />} + itemRenderer={branchItemRenderer} + itemPredicate={branchItemPredicate} + onItemSelect={brnachItemSelect} + selectedItem={selectedBranch} + inputProps={{ placeholder: defaultSelectText }} + resetOnClose={true} + fill={true} + popoverProps={{ minimal: true, boundary: 'window' }} + inputValueRenderer={branchInputValueRenderer} + className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, { + [CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill, + })} + {...suggestProps} + /> + ); +} diff --git a/src/components/DataTableCells/BranchesListFieldCell.js b/src/components/DataTableCells/BranchesListFieldCell.js new file mode 100644 index 000000000..2fb82ade6 --- /dev/null +++ b/src/components/DataTableCells/BranchesListFieldCell.js @@ -0,0 +1,40 @@ +import React from 'react'; +import { FormGroup, Intent, Classes } from '@blueprintjs/core'; +import classNames from 'classnames'; +import BranchSuggestField from '../BranchSuggestField'; + +/** + * Branches list field cell. + * @returns + */ +export default function BranchesListFieldCell({ + column: { id }, + row: { index, original }, + payload: { branches, updateData, errors }, +}) { + const handleBranchSelected = React.useCallback( + (branch) => { + updateData(index, 'brnach_id', branch.id); + }, + [updateData, index], + ); + + const error = errors?.[index]?.[id]; + + return ( + + + + ); +} diff --git a/src/components/DataTableCells/index.js b/src/components/DataTableCells/index.js index fed349eed..98d4aec24 100644 --- a/src/components/DataTableCells/index.js +++ b/src/components/DataTableCells/index.js @@ -9,6 +9,7 @@ import NumericInputCell from './NumericInputCell'; import CheckBoxFieldCell from './CheckBoxFieldCell'; import SwitchFieldCell from './SwitchFieldCell'; import TextAreaCell from './TextAreaCell'; +import BranchesListFieldCell from './BranchesListFieldCell'; export { AccountsListFieldCell, @@ -23,4 +24,5 @@ export { CheckBoxFieldCell, SwitchFieldCell, TextAreaCell, + BranchesListFieldCell, }; diff --git a/src/containers/Accounting/MakeJournal/MakeJournalEntries.schema.js b/src/containers/Accounting/MakeJournal/MakeJournalEntries.schema.js index ae4827556..7c7da60dd 100644 --- a/src/containers/Accounting/MakeJournal/MakeJournalEntries.schema.js +++ b/src/containers/Accounting/MakeJournal/MakeJournalEntries.schema.js @@ -16,6 +16,7 @@ const Schema = Yup.object().shape({ date: Yup.date().required().label(intl.get('date')), currency_code: Yup.string().max(3), publish: Yup.boolean(), + branch_id: Yup.string(), reference: Yup.string().nullable().min(1).max(DATATYPES_LENGTH.STRING), description: Yup.string().min(1).max(DATATYPES_LENGTH.STRING).nullable(), exchange_rate: Yup.number(), diff --git a/src/containers/Accounting/MakeJournal/MakeJournalEntriesField.js b/src/containers/Accounting/MakeJournal/MakeJournalEntriesField.js index 8367dc2bd..ccb3572eb 100644 --- a/src/containers/Accounting/MakeJournal/MakeJournalEntriesField.js +++ b/src/containers/Accounting/MakeJournal/MakeJournalEntriesField.js @@ -10,7 +10,7 @@ import { useMakeJournalFormContext } from './MakeJournalProvider'; * Make journal entries field. */ export default function MakeJournalEntriesField() { - const { accounts, contacts } = useMakeJournalFormContext(); + const { accounts, contacts ,branches } = useMakeJournalFormContext(); return (
@@ -18,6 +18,7 @@ export default function MakeJournalEntriesField() { name={'entries'} contacts={contacts} accounts={accounts} + branches={branches} shouldUpdate={entriesFieldShouldUpdate} > {({ diff --git a/src/containers/Accounting/MakeJournal/MakeJournalEntriesForm.js b/src/containers/Accounting/MakeJournal/MakeJournalEntriesForm.js index 538cffe28..09a6ebf42 100644 --- a/src/containers/Accounting/MakeJournal/MakeJournalEntriesForm.js +++ b/src/containers/Accounting/MakeJournal/MakeJournalEntriesForm.js @@ -17,6 +17,7 @@ import MakeJournalFormFloatingActions from './MakeJournalFormFloatingActions'; import MakeJournalEntriesField from './MakeJournalEntriesField'; import MakeJournalFormFooter from './MakeJournalFormFooter'; import MakeJournalFormDialogs from './MakeJournalFormDialogs'; +import MakeJournalFormTopBar from './MakeJournalFormTopBar'; import withSettings from 'containers/Settings/withSettings'; import withCurrentOrganization from 'containers/Organization/withCurrentOrganization'; @@ -169,6 +170,7 @@ function MakeJournalEntriesForm({ onSubmit={handleSubmit} >
+ diff --git a/src/containers/Accounting/MakeJournal/MakeJournalEntriesTable.js b/src/containers/Accounting/MakeJournal/MakeJournalEntriesTable.js index 89b2fbd8f..e34c299f9 100644 --- a/src/containers/Accounting/MakeJournal/MakeJournalEntriesTable.js +++ b/src/containers/Accounting/MakeJournal/MakeJournalEntriesTable.js @@ -25,11 +25,12 @@ export default function MakeJournalEntriesTable({ minLinesNumber = 4, currencyCode, }) { - const { accounts, contacts } = useMakeJournalFormContext(); + const { accounts, contacts, branches } = useMakeJournalFormContext(); // Memorized data table columns. const columns = useJournalTableEntriesColumns(); + // Handles update datatable data. const handleUpdateData = (rowIndex, columnId, value) => { const newRows = compose( @@ -69,6 +70,7 @@ export default function MakeJournalEntriesTable({ updateData: handleUpdateData, removeRow: handleRemoveRow, contacts, + branches, autoFocus: ['account_id', 0], currencyCode, }} diff --git a/src/containers/Accounting/MakeJournal/MakeJournalFormTopBar.js b/src/containers/Accounting/MakeJournal/MakeJournalFormTopBar.js new file mode 100644 index 000000000..52758a952 --- /dev/null +++ b/src/containers/Accounting/MakeJournal/MakeJournalFormTopBar.js @@ -0,0 +1,68 @@ +import React from 'react'; +import intl from 'react-intl-universal'; +import { Button, Alignment, NavbarGroup, Classes } from '@blueprintjs/core'; +import styled from 'styled-components'; +import { useSetPrimaryBranchToForm } from './utils'; +import { useFeatureCan } from 'hooks/state'; +import { + Icon, + BranchSelect, + FeatureCan, + FormTopbar, + DetailsBarSkeletonBase, +} from 'components'; +import { useMakeJournalFormContext } from './MakeJournalProvider'; +import { Features } from 'common'; + +/** + * Make journal form topbar. + * @returns + */ +export default function MakeJournalFormTopBar() { + // Features guard. + const { featureCan } = useFeatureCan(); + + // Sets the primary branch to form. + useSetPrimaryBranchToForm(); + + // Can't display the navigation bar if branches feature is not enabled. + if (!featureCan(Features.Branches)) { + return null; + } + + return ( + + + + + + + + ); +} + +function MakeJournalFormSelectBranch() { + // Invoice form context. + const { branches, isBranchesLoading } = useMakeJournalFormContext(); + + return isBranchesLoading ? ( + + ) : ( + + ); +} +function MakeJournalBranchSelectButton({ label }) { + return ( +