diff --git a/client/src/containers/Accounting/MakeJournalEntriesTable.js b/client/src/containers/Accounting/MakeJournalEntriesTable.js
index 516698080..14b6dcae1 100644
--- a/client/src/containers/Accounting/MakeJournalEntriesTable.js
+++ b/client/src/containers/Accounting/MakeJournalEntriesTable.js
@@ -292,7 +292,7 @@ export default compose(
withAccounts(({ accountsList }) => ({
accountsList,
})),
- withCustomers(({ customersItems }) => ({
- customers: customersItems,
+ withCustomers(({ customers }) => ({
+ customers,
})),
)(MakeJournalEntriesTable);
diff --git a/client/src/containers/Items/ItemForm.js b/client/src/containers/Items/ItemForm.js
index 1576aee72..09341f0cc 100644
--- a/client/src/containers/Items/ItemForm.js
+++ b/client/src/containers/Items/ItemForm.js
@@ -1,9 +1,7 @@
import React, { useMemo, useCallback, useEffect } from 'react';
import * as Yup from 'yup';
-import { useFormik } from 'formik';
-import {
- Intent
-} from '@blueprintjs/core';
+import { useFormik, Formik, Form } from 'formik';
+import { Intent } from '@blueprintjs/core';
import { queryCache } from 'react-query';
import { useHistory } from 'react-router-dom';
import { pick, pickBy } from 'lodash';
@@ -41,7 +39,6 @@ const defaultInitialValues = {
purchasable: true,
};
-
/**
* Item form.
*/
@@ -88,18 +85,16 @@ function ItemForm({
.required()
.label(formatMessage({ id: 'item_type_' })),
sku: Yup.string().trim(),
- cost_price: Yup.number()
- .when(['purchasable'], {
- is: true,
- then: Yup.number().required(),
- otherwise: Yup.number().nullable(true),
- }),
- sell_price: Yup.number()
- .when(['sellable'], {
- is: true,
- then: Yup.number().required(),
- otherwise: Yup.number().nullable(true),
- }),
+ cost_price: Yup.number().when(['purchasable'], {
+ is: true,
+ then: Yup.number().required(),
+ otherwise: Yup.number().nullable(true),
+ }),
+ sell_price: Yup.number().when(['sellable'], {
+ is: true,
+ then: Yup.number().required(),
+ otherwise: Yup.number().nullable(true),
+ }),
cost_account_id: Yup.number()
.when(['purchasable'], {
is: true,
@@ -145,21 +140,24 @@ function ItemForm({
);
useEffect(() => {
- (!isNewMode)
+ !isNewMode
? changePageTitle(formatMessage({ id: 'edit_item_details' }))
: changePageTitle(formatMessage({ id: 'new_item' }));
}, [changePageTitle, isNewMode, formatMessage]);
const transformApiErrors = (errors) => {
const fields = {};
- if (errors.find(e => e.type === 'ITEM.NAME.ALREADY.EXISTS')) {
- fields.name = formatMessage({ id: 'the_name_used_before' })
+ if (errors.find((e) => e.type === 'ITEM.NAME.ALREADY.EXISTS')) {
+ fields.name = formatMessage({ id: 'the_name_used_before' });
}
return fields;
- }
+ };
// Handles the form submit.
- const handleFormSubmit = (values, { setSubmitting, resetForm, setErrors }) => {
+ const handleFormSubmit = (
+ values,
+ { setSubmitting, resetForm, setErrors },
+ ) => {
setSubmitting(true);
const form = { ...values };
@@ -167,9 +165,9 @@ function ItemForm({
AppToaster.show({
message: formatMessage(
{
- id: (isNewMode) ?
- 'service_has_been_successful_created' :
- 'the_item_has_been_successfully_edited',
+ id: isNewMode
+ ? 'service_has_been_successful_created'
+ : 'the_item_has_been_successfully_edited',
},
{
number: itemId,
@@ -196,28 +194,13 @@ function ItemForm({
}
};
- const {
- getFieldProps,
- setFieldValue,
- values,
- touched,
- errors,
- handleSubmit,
- isSubmitting,
- } = useFormik({
- enableReinitialize: true,
- validationSchema: validationSchema,
- initialValues,
- onSubmit: handleFormSubmit
- });
-
- useEffect(() => {
- if (values.item_type) {
- changePageSubtitle(formatMessage({ id: values.item_type }));
- } else {
- changePageSubtitle('');
- }
- }, [values.item_type, changePageSubtitle, formatMessage]);
+ // useEffect(() => {
+ // if (values.item_type) {
+ // changePageSubtitle(formatMessage({ id: values.item_type }));
+ // } else {
+ // changePageSubtitle('');
+ // }
+ // }, [values.item_type, changePageSubtitle, formatMessage]);
const initialAttachmentFiles = useMemo(() => {
return itemDetail && itemDetail.media
@@ -228,10 +211,13 @@ function ItemForm({
}))
: [];
}, [itemDetail]);
-
- const handleDropFiles = useCallback((_files) => {
- setFiles(_files.filter((file) => file.uploaded === false));
- }, [setFiles]);
+
+ const handleDropFiles = useCallback(
+ (_files) => {
+ setFiles(_files.filter((file) => file.uploaded === false));
+ },
+ [setFiles],
+ );
const handleDeleteFile = useCallback(
(_deletedFiles) => {
@@ -250,39 +236,30 @@ function ItemForm({
return (
-
+
+ {({ isSubmitting }) => (
+
+ )}
+
);
-};
+}
export default compose(
withItemsActions,
diff --git a/client/src/containers/Items/ItemFormBody.js b/client/src/containers/Items/ItemFormBody.js
index c9b07d671..12b92040c 100644
--- a/client/src/containers/Items/ItemFormBody.js
+++ b/client/src/containers/Items/ItemFormBody.js
@@ -1,15 +1,14 @@
import React from 'react';
+import { FastField, ErrorMessage } from 'formik';
import {
FormGroup,
Intent,
- InputGroup,
Classes,
Checkbox,
} from '@blueprintjs/core';
import {
AccountsSelectList,
MoneyInputGroup,
- ErrorMessage,
Col,
Row,
Hint,
@@ -18,161 +17,158 @@ import { FormattedMessage as T } from 'react-intl';
import classNames from 'classnames';
import withAccounts from 'containers/Accounts/withAccounts';
-import { compose } from 'utils';
+import { compose, inputIntent } from 'utils';
/**
* Item form body.
*/
-function ItemFormBody({
- getFieldProps,
- touched,
- errors,
- values,
- setFieldValue,
-
- accountsList,
-}) {
+function ItemFormBody({ accountsList }) {
return (
- {/*------------- Sellable checkbox ------------- */}
-
-
-
-
- }
- checked={values.sellable}
- {...getFieldProps('sellable')}
- />
-
+ {/*------------- Purchasable checbox ------------- */}
+
+ {({ field, field: { value } }) => (
+
+
+
+
+ }
+ defaultChecked={value}
+ {...field}
+ />
+
+ )}
+
{/*------------- Selling price ------------- */}
- }
- className={'form-group--item-selling-price'}
- intent={errors.sell_price && touched.sell_price && Intent.DANGER}
- helperText={
-
- }
- inline={true}
- >
- {
- setFieldValue('sell_price', value);
- }}
- inputGroupProps={{
- medium: true,
- intent:
- errors.sell_price && touched.sell_price && Intent.DANGER,
- }}
- disabled={!values.sellable}
- />
-
-
- {/*------------- Selling account ------------- */}
- }
- labelInfo={}
- inline={true}
- intent={
- errors.sell_account_id && touched.sell_account_id && Intent.DANGER
- }
- helperText={
-
- }
- className={classNames(
- 'form-group--sell-account',
- 'form-group--select-list',
- Classes.FILL,
+
+ {({ form, field, field: { value }, meta: { error, touched } }) => (
+ }
+ className={'form-group--sell_price'}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ inline={true}
+ >
+
+
)}
- >
- {
- setFieldValue('sell_account_id', account.id);
- }}
- defaultSelectText={}
- selectedAccountId={values.sell_account_id}
- disabled={!values.sellable}
- filterByTypes={['income']}
- />
-
+
+
+ {/*------------- Selling account ------------- */}
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ }
+ labelInfo={}
+ inline={true}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ className={classNames(
+ 'form-group--sell-account',
+ 'form-group--select-list',
+ Classes.FILL,
+ )}
+ >
+ {
+ form.setFieldValue('sell_account_id', account.id);
+ }}
+ defaultSelectText={}
+ selectedAccountId={value}
+ disabled={!form.values.sellable}
+ filterByTypes={['income']}
+ />
+
+ )}
+
- {/*------------- Purchasable checbox ------------- */}
-
-
-
-
- }
- defaultChecked={values.purchasable}
- {...getFieldProps('purchasable')}
- />
-
+ {/*------------- Sellable checkbox ------------- */}
+
+ {({ field, field: { value }, meta: { error, touched } }) => (
+
+
+
+
+ }
+ checked={value}
+ {...field}
+ />
+
+ )}
+
{/*------------- Cost price ------------- */}
- }
- className={'form-group--item-cost-price'}
- intent={errors.cost_price && touched.cost_price && Intent.DANGER}
- helperText={
-
- }
- inline={true}
- >
- {
- setFieldValue('cost_price', value);
- }}
- inputGroupProps={{
- medium: true,
- intent:
- errors.cost_price && touched.cost_price && Intent.DANGER,
- }}
- disabled={!values.purchasable}
- />
-
+
+ {({ field, form, field: { value }, meta: { error, touched } }) => (
+ }
+ className={'form-group--item-cost-price'}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ inline={true}
+ >
+
+
+ )}
+
{/*------------- Cost account ------------- */}
- }
- labelInfo={}
- inline={true}
- intent={
- errors.cost_account_id && touched.cost_account_id && Intent.DANGER
- }
- helperText={
-
- }
- className={classNames(
- 'form-group--cost-account',
- 'form-group--select-list',
- Classes.FILL,
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ }
+ labelInfo={}
+ inline={true}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ className={classNames(
+ 'form-group--cost-account',
+ 'form-group--select-list',
+ Classes.FILL,
+ )}
+ >
+ {
+ form.setFieldValue('cost_account_id', account.id);
+ }}
+ defaultSelectText={}
+ selectedAccountId={value}
+ disabled={!form.values.purchasable}
+ filterByTypes={['cost_of_goods_sold']}
+ />
+
)}
- >
- {
- setFieldValue('cost_account_id', account.id);
- }}
- defaultSelectText={}
- selectedAccountId={values.cost_account_id}
- disabled={!values.purchasable}
- filterByTypes={['cost_of_goods_sold']}
- />
-
+
diff --git a/client/src/containers/Items/ItemFormFloatingActions.js b/client/src/containers/Items/ItemFormFloatingActions.js
index 82297b60d..7e0a7eff0 100644
--- a/client/src/containers/Items/ItemFormFloatingActions.js
+++ b/client/src/containers/Items/ItemFormFloatingActions.js
@@ -1,24 +1,15 @@
-import React from 'react';
-import {
- Button,
- Intent,
- FormGroup,
- Checkbox
-} from '@blueprintjs/core';
+import React, { memo } from 'react';
+import { Button, Intent, FormGroup, Checkbox } from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl';
import { saveInvoke } from 'utils';
import classNames from 'classnames';
-
+import { ErrorMessage, FastField } from 'formik';
import { CLASSES } from 'common/classes';
/**
* Item form floating actions.
*/
-export default function ItemFormFloatingActions({
- isSubmitting,
- itemId,
- onCancelClick
-}) {
+export default function ItemFormFloatingActions({ isSubmitting, itemId, onCancelClick }) {
const handleCancelBtnClick = (event) => {
saveInvoke(onCancelClick, event.currentTarget.value);
};
@@ -37,18 +28,24 @@ export default function ItemFormFloatingActions({
{/*----------- Active ----------*/}
-
- }
- // defaultChecked={values.active}
- // {...getFieldProps('active')}
- />
-
+
+ {({ field, field: { value } }) => (
+
+ }
+ defaultChecked={value}
+ {...field}
+ />
+
+ )}
+
);
}
+
+// function areEqual(prevProps, nextProps) {
+// return prevProps.isSubmitting === nextProps.isSubmitting;
+// }
+
+// export default memo(ItemFormFloatingActions, areEqual);
diff --git a/client/src/containers/Items/ItemFormInventorySection.js b/client/src/containers/Items/ItemFormInventorySection.js
index 6321e7200..af246f10d 100644
--- a/client/src/containers/Items/ItemFormInventorySection.js
+++ b/client/src/containers/Items/ItemFormInventorySection.js
@@ -1,30 +1,18 @@
import React from 'react';
-import {
- FormGroup,
- Intent,
- InputGroup,
- Position,
-} from '@blueprintjs/core';
+import { Field, FastField, ErrorMessage } from 'formik';
+import { FormGroup, Intent, InputGroup, Position } from '@blueprintjs/core';
import { DateInput } from '@blueprintjs/datetime';
-import { AccountsSelectList, ErrorMessage, Col, Row, Hint } from 'components';
+import { AccountsSelectList, Col, Row, Hint } from 'components';
import { CLASSES } from 'common/classes';
import { FormattedMessage as T } from 'react-intl';
import classNames from 'classnames';
import withAccounts from 'containers/Accounts/withAccounts';
-import { compose, tansformDateValue, momentFormatter } from 'utils';
+import { compose, tansformDateValue, momentFormatter, inputIntent } from 'utils';
/**
* Item form inventory sections.
*/
-function ItemFormInventorySection({
- errors,
- touched,
- setFieldValue,
- values,
- getFieldProps,
-
- accountsList,
-}) {
+function ItemFormInventorySection({ accountsList }) {
return (
@@ -34,83 +22,91 @@ function ItemFormInventorySection({
{/*------------- Inventory account ------------- */}
- }
- inline={true}
- intent={
- errors.inventory_account_id &&
- touched.inventory_account_id &&
- Intent.DANGER
- }
- helperText={
-
- }
- className={classNames(
- 'form-group--item-inventory_account',
- 'form-group--select-list',
- CLASSES.FILL,
+
+ {({ form, field: { value }, meta: { touched, error } }) => (
+ }
+ inline={true}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ className={classNames(
+ 'form-group--item-inventory_account',
+ 'form-group--select-list',
+ CLASSES.FILL,
+ )}
+ >
+ {
+ form.setFieldValue('inventory_account_id', account.id);
+ }}
+ defaultSelectText={}
+ selectedAccountId={value}
+ />
+
)}
- >
- {
- setFieldValue('inventory_account_id', account.id);
- }}
- defaultSelectText={}
- selectedAccountId={values.inventory_account_id}
- />
-
+
- }
- labelInfo={}
- className={'form-group--opening_quantity'}
- inline={true}
- >
-
-
-
- }
- labelInfo={}
- className={classNames(
- 'form-group--select-list',
- 'form-group--opening_date',
- CLASSES.FILL,
+
+ {({ field, field: { value }, meta: { touched, error } }) => (
+ }
+ labelInfo={}
+ className={'form-group--opening_quantity'}
+ intent={inputIntent({ error, touched })}
+ inline={true}
+ >
+
+
)}
- inline={true}
- >
- {
- setFieldValue('opening_date', value);
- }}
- popoverProps={{ position: Position.BOTTOM, minimal: true }}
- />
-
+
+
+
+ {({ form, field: { value }, meta: { touched, error } }) => (
+ }
+ labelInfo={}
+ className={classNames(
+ 'form-group--select-list',
+ 'form-group--opening_date',
+ CLASSES.FILL,
+ )}
+ intent={inputIntent({ error, touched })}
+ inline={true}
+ >
+ {
+ form.setFieldValue('opening_date', value);
+ }}
+ popoverProps={{ position: Position.BOTTOM, minimal: true }}
+ />
+
+ )}
+
- }
- className={'form-group--opening_average_rate'}
- inline={true}
- >
-
-
+
+ {({ field, field: { value }, meta: { touched, error } }) => (
+ }
+ className={'form-group--opening_average_rate'}
+ intent={inputIntent({ error, touched })}
+ inline={true}
+ >
+
+
+ )}
+
diff --git a/client/src/containers/Items/ItemFormPrimarySection.js b/client/src/containers/Items/ItemFormPrimarySection.js
index dd90e3c7d..e63ec625d 100644
--- a/client/src/containers/Items/ItemFormPrimarySection.js
+++ b/client/src/containers/Items/ItemFormPrimarySection.js
@@ -7,12 +7,11 @@ import {
Classes,
Radio,
Position,
- Tooltip,
} from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl';
+import { ErrorMessage, FastField } from 'formik';
import {
CategoriesSelectList,
- ErrorMessage,
Hint,
Col,
Row,
@@ -24,126 +23,139 @@ import { CLASSES } from 'common/classes';
import withItemCategories from 'containers/Items/withItemCategories';
import withAccounts from 'containers/Accounts/withAccounts';
-import { compose, handleStringChange } from 'utils';
+import { compose, handleStringChange, inputIntent } from 'utils';
/**
* Item form primary section.
*/
function ItemFormPrimarySection({
- getFieldProps,
- setFieldValue,
- errors,
- touched,
- values,
-
// #withItemCategories
categoriesList,
}) {
-
const itemTypeHintContent = (
<>
- {'Service: '}{'Services that you provide to customers. '}
- {'Inventory: '}{'Products you buy and/or sell and that you track quantities of.'}
- {'Non-Inventory: '}{'Products you buy and/or sell but don’t need to (or can’t) track quantities of, for example, nuts and bolts used in an installation.'}
- >);
+
+ {'Service: '}
+ {'Services that you provide to customers. '}
+
+
+ {'Inventory: '}
+ {'Products you buy and/or sell and that you track quantities of.'}
+
+
+ {'Non-Inventory: '}
+ {
+ 'Products you buy and/or sell but don’t need to (or can’t) track quantities of, for example, nuts and bolts used in an installation.'
+ }
+
+ >
+ );
return (
{/*----------- Item type ----------*/}
- }
- labelInfo={
-
-
-
-
- }
- className={'form-group--item-type'}
- intent={errors.type && touched.type && Intent.DANGER}
- helperText={}
- inline={true}
- >
- {
- setFieldValue('type', value);
- })}
- selectedValue={values.type}
- >
- }
- value="service"
- />
- }
- value="inventory"
- />
- }
- value="non-inventory"
- />
-
-
+
+ {({ form, field: { value }, meta: { touched, error } }) => (
+ }
+ labelInfo={
+
+
+
+
+ }
+ className={'form-group--item-type'}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ inline={true}
+ >
+ {
+ form.setFieldValue('type', _value);
+ })}
+ selectedValue={value}
+ >
+ } value="service" />
+ } value="inventory" />
+ }
+ value="non-inventory"
+ />
+
+
+ )}
+
{/*----------- Item name ----------*/}
- }
- labelInfo={}
- className={'form-group--item-name'}
- intent={errors.name && touched.name && Intent.DANGER}
- helperText={}
- inline={true}
- >
-
-
+
+ {({ field, meta: { error, touched } }) => (
+ }
+ labelInfo={}
+ className={'form-group--item-name'}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ inline={true}
+ >
+
+
+ )}
+
{/*----------- SKU ----------*/}
- }
- labelInfo={}
- className={'form-group--item-sku'}
- intent={errors.sku && touched.sku && Intent.DANGER}
- helperText={}
- inline={true}
- >
-
-
+
+ {({ field, meta: { error, touched } }) => (
+ }
+ labelInfo={}
+ className={' -group--item-sku'}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ inline={true}
+ >
+
+
+ )}
+
{/*----------- Item category ----------*/}
- }
- labelInfo={}
- inline={true}
- intent={errors.category_id && touched.category_id && Intent.DANGER}
- helperText={
-
- }
- className={classNames(
- 'form-group--select-list',
- 'form-group--category',
- Classes.FILL,
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ }
+ inline={true}
+ intent={inputIntent({ error, touched })}
+ helperText={}
+ className={classNames(
+ 'form-group--select-list',
+ 'form-group--category',
+ Classes.FILL,
+ )}
+ >
+ {
+ form.setFieldValue('category_id', category.id);
+ }}
+ popoverProps={{ minimal: true }}
+ />
+
)}
- >
- {
- setFieldValue('category_id', category.id);
- }}
- popoverProps={{ minimal: true }}
- />
-
+
diff --git a/client/src/utils.js b/client/src/utils.js
index 8abe4c145..b888dbc10 100644
--- a/client/src/utils.js
+++ b/client/src/utils.js
@@ -1,5 +1,8 @@
import moment from 'moment';
import _ from 'lodash';
+import {
+ Intent,
+} from '@blueprintjs/core';
import Currency from 'js-money/lib/currency';
import PProgress from 'p-progress';
import accounting from 'accounting';
@@ -284,4 +287,8 @@ export const transformToForm = (obj, emptyInitialValues) => {
obj,
(val, key) => val !== null && Object.keys(emptyInitialValues).includes(key),
)
+}
+
+export function inputIntent({ error, touched }){
+ return error && touched ? Intent.DANGER : '';
}
\ No newline at end of file