feat: optimize customer form performance.

feat: optimize item category form performance.
This commit is contained in:
Ahmed Bouhuolia
2020-11-11 17:18:54 +02:00
parent dcc431b9a9
commit a1e8dbf1c7
16 changed files with 727 additions and 987 deletions

View File

@@ -0,0 +1,216 @@
import React, { useCallback } from 'react';
import {
Button,
Classes,
FormGroup,
InputGroup,
Intent,
TextArea,
MenuItem,
} from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl';
import classNames from 'classnames';
import { ErrorMessage, Form, FastField } from 'formik';
import {
ListSelect,
AccountsSelectList,
FieldRequiredHint,
Hint,
} from 'components';
import { inputIntent } from 'utils';
export default function ItemCategoryForm({
itemCategoryId,
accountsList,
categoriesList,
isSubmitting,
}) {
// Filters Item Categories list.
const filterItemCategories = useCallback(
(query, category, _index, exactMatch) => {
const normalizedTitle = category.name.toLowerCase();
const normalizedQuery = query.toLowerCase();
if (exactMatch) {
return normalizedTitle === normalizedQuery;
} else {
return normalizedTitle.indexOf(normalizedQuery) >= 0;
}
},
[],
);
const parentCategoryItem = useCallback(
(category, { handleClick, modifiers, query }) => {
return (
<MenuItem
text={category.name}
key={category.id}
onClick={handleClick}
/>
);
},
[],
);
return (
<Form>
<div className={Classes.DIALOG_BODY}>
{/* ----------- Category name ----------- */}
<FastField name={'name'}>
{({ field, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'category_name'} />}
labelInfo={FieldRequiredHint}
className={'form-group--category-name'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="name" />}
inline={true}
>
<InputGroup medium={true} {...field} />
</FormGroup>
)}
</FastField>
{/* ----------- Parent Category ----------- */}
<FastField name={'parent_account_id'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'parent_category'} />}
labelInfo={Hint}
className={classNames(
'form-group--select-list',
'form-group--parent-category',
Classes.FILL,
)}
inline={true}
helperText={<ErrorMessage name="parent_category_id" />}
intent={inputIntent({ error, touched })}
>
<ListSelect
items={categoriesList}
noResults={<MenuItem disabled={true} text="No results." />}
itemRenderer={parentCategoryItem}
itemPredicate={filterItemCategories}
popoverProps={{ minimal: true }}
onItemSelect={(parentCategory) => {
form.setFieldValue('parent_category_id', parentCategory.id);
}}
selectedItem={value}
selectedItemProp={'id'}
defaultText={<T id={'select_parent_category'} />}
labelProp={'name'}
/>
</FormGroup>
)}
</FastField>
{/* ----------- Description ----------- */}
<FastField name={'description`'}>
{({ field, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'description'} />}
className={'form-group--description'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="description" />}
inline={true}
>
<TextArea growVertically={true} large={true} {...field} />
</FormGroup>
)}
</FastField>
{/* ----------- Cost account ----------- */}
<FastField name={'cost_account_id'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'cost_account'} />}
inline={true}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="cost_account_id" />}
className={classNames(
'form-group--cost-account',
'form-group--select-list',
Classes.FILL,
)}
>
<AccountsSelectList
accounts={accountsList}
onAccountSelected={(account) => {
form.setFieldValue(account.id);
}}
defaultSelectText={<T id={'select_account'} />}
selectedAccountId={value}
filterByTypes={['cost_of_goods_sold']}
/>
</FormGroup>
)}
</FastField>
{/* ----------- Sell account ----------- */}
<FastField name={'sell_account_id'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'sell_account'} />}
inline={true}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="sell_account_id" />}
className={classNames(
'form-group--sell-account',
'form-group--select-list',
Classes.FILL,
)}
>
<AccountsSelectList
accounts={accountsList}
onAccountSelected={(account) => {
form.setFieldValue('sell_account_id', account.id);
}}
defaultSelectText={<T id={'select_account'} />}
selectedAccountId={value}
filterByTypes={['income']}
/>
</FormGroup>
)}
</FastField>
{/* ----------- inventory account ----------- */}
<FastField name={'inventory_account_id'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'inventory_account'} />}
inline={true}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="inventory_account_id" />}
className={classNames(
'form-group--sell-account',
'form-group--select-list',
Classes.FILL,
)}
>
<AccountsSelectList
accounts={accountsList}
onAccountSelected={(account) => {
form.setFieldValue('inventory_account_id', account.id);
}}
defaultSelectText={<T id={'select_account'} />}
selectedAccountId={value}
/>
</FormGroup>
)}
</FastField>
</div>
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button>
<T id={'close'} />
</Button>
<Button intent={Intent.PRIMARY} type="submit" disabled={isSubmitting}>
{itemCategoryId ? <T id={'edit'} /> : <T id={'submit'} />}
</Button>
</div>
</div>
</Form>
);
}

View File

@@ -1,27 +1,10 @@
import React, { useState, useMemo, useCallback } from 'react';
import {
Button,
Classes,
FormGroup,
InputGroup,
Intent,
TextArea,
MenuItem,
} from '@blueprintjs/core';
import React, { useMemo } from 'react';
import { Intent } from '@blueprintjs/core';
import * as Yup from 'yup';
import { useFormik } from 'formik';
import { useQuery, queryCache } from 'react-query';
import { FormattedMessage as T, useIntl } from 'react-intl';
import classNames from 'classnames';
import {
ListSelect,
AccountsSelectList,
FieldRequiredHint,
Hint,
AppToaster,
ErrorMessage,
DialogContent,
} from 'components';
import { Formik } from 'formik';
import { AppToaster, DialogContent } from 'components';
import withItemCategories from 'containers/Items/withItemCategories';
import withItemCategoryDetail from 'containers/Items/withItemCategoryDetail';
@@ -32,6 +15,7 @@ import withAccountsActions from 'containers/Accounts/withAccountsActions';
import withDialogActions from 'containers/Dialog/withDialogActions';
import { compose, transformToForm } from 'utils';
import ItemCategoryForm from './ItemCategoryForm';
const defaultInitialValues = {
name: '',
@@ -75,7 +59,6 @@ function ItemCategoryFormDialogContent({
const isNewMode = !itemCategoryId;
const { formatMessage } = useIntl();
const [selectedParentCategory, setParentCategory] = useState(null);
// Fetches categories list.
const fetchCategoriesList = useQuery(['items-categories-list'], () =>
@@ -138,237 +121,24 @@ function ItemCategoryFormDialogContent({
}
};
// Formik
const {
values,
errors,
touched,
isSubmitting,
setFieldValue,
handleSubmit,
getFieldProps,
} = useFormik({
enableReinitialize: true,
validationSchema,
initialValues,
onSubmit: handleFormSubmit,
});
// Filters Item Categories list.
const filterItemCategories = useCallback(
(query, category, _index, exactMatch) => {
const normalizedTitle = category.name.toLowerCase();
const normalizedQuery = query.toLowerCase();
if (exactMatch) {
return normalizedTitle === normalizedQuery;
} else {
return normalizedTitle.indexOf(normalizedQuery) >= 0;
}
},
[],
);
const parentCategoryItem = useCallback(
(category, { handleClick, modifiers, query }) => {
return (
<MenuItem
text={category.name}
key={category.id}
onClick={handleClick}
/>
);
},
[],
);
// Handle the dialog closing.
const handleClose = useCallback(() => {
closeDialog(dialogName);
}, [dialogName, closeDialog]);
const onChangeParentCategory = useCallback(
(parentCategory) => {
setParentCategory(parentCategory);
setFieldValue('parent_category_id', parentCategory.id);
},
[setFieldValue],
);
const onItemAccountSelect = useCallback(
(filedName) => {
return (account) => {
setFieldValue(filedName, account.id);
};
},
[setFieldValue],
);
return (
<DialogContent
isLoading={fetchCategoriesList.isFetching || fetchAccountsList.isFetching}
>
<form onSubmit={handleSubmit}>
<div className={Classes.DIALOG_BODY}>
{/* ----------- Category name ----------- */}
<FormGroup
label={<T id={'category_name'} />}
labelInfo={FieldRequiredHint}
className={'form-group--category-name'}
intent={errors.name && touched.name && Intent.DANGER}
helperText={<ErrorMessage name="name" {...{ errors, touched }} />}
inline={true}
>
<InputGroup
medium={true}
intent={errors.name && touched.name && Intent.DANGER}
{...getFieldProps('name')}
/>
</FormGroup>
{/* ----------- Parent Category ----------- */}
<FormGroup
label={<T id={'parent_category'} />}
labelInfo={Hint}
className={classNames(
'form-group--select-list',
'form-group--parent-category',
Classes.FILL,
)}
inline={true}
helperText={
<ErrorMessage
name="parent_category_id"
{...{ errors, touched }}
/>
}
intent={
errors.parent_category_id &&
touched.parent_category_id &&
Intent.DANGER
}
>
<ListSelect
items={categoriesList}
noResults={<MenuItem disabled={true} text="No results." />}
itemRenderer={parentCategoryItem}
itemPredicate={filterItemCategories}
popoverProps={{ minimal: true }}
onItemSelect={onChangeParentCategory}
selectedItem={values.parent_category_id}
selectedItemProp={'id'}
defaultText={<T id={'select_parent_category'} />}
labelProp={'name'}
/>
</FormGroup>
{/* ----------- Description ----------- */}
<FormGroup
label={<T id={'description'} />}
className={'form-group--description'}
intent={errors.description && touched.description && Intent.DANGER}
helperText={
<ErrorMessage name="description" {...{ errors, touched }} />
}
inline={true}
>
<TextArea
growVertically={true}
large={true}
{...getFieldProps('description')}
/>
</FormGroup>
{/* ----------- Cost account ----------- */}
<FormGroup
label={<T id={'cost_account'} />}
inline={true}
intent={
errors.cost_account_id && touched.cost_account_id && Intent.DANGER
}
helperText={
<ErrorMessage {...{ errors, touched }} name="cost_account_id" />
}
className={classNames(
'form-group--cost-account',
'form-group--select-list',
Classes.FILL,
)}
>
<AccountsSelectList
accounts={accountsList}
onAccountSelected={onItemAccountSelect('cost_account_id')}
defaultSelectText={<T id={'select_account'} />}
selectedAccountId={values.cost_account_id}
filterByTypes={['cost_of_goods_sold']}
/>
</FormGroup>
{/* ----------- Sell account ----------- */}
<FormGroup
label={<T id={'sell_account'} />}
inline={true}
intent={
errors.sell_account_id && touched.sell_account_id && Intent.DANGER
}
helperText={
<ErrorMessage {...{ errors, touched }} name="sell_account_id" />
}
className={classNames(
'form-group--sell-account',
'form-group--select-list',
Classes.FILL,
)}
>
<AccountsSelectList
accounts={accountsList}
onAccountSelected={onItemAccountSelect('sell_account_id')}
defaultSelectText={<T id={'select_account'} />}
selectedAccountId={values.sell_account_id}
filterByTypes={['income']}
/>
</FormGroup>
{/* ----------- inventory account ----------- */}
<FormGroup
label={<T id={'inventory_account'} />}
inline={true}
intent={
errors.inventory_account_id &&
touched.inventory_account_id &&
Intent.DANGER
}
helperText={
<ErrorMessage
{...{ errors, touched }}
name="inventory_account_id"
/>
}
className={classNames(
'form-group--sell-account',
'form-group--select-list',
Classes.FILL,
)}
>
<AccountsSelectList
accounts={accountsList}
onAccountSelected={onItemAccountSelect('inventory_account_id')}
defaultSelectText={<T id={'select_account'} />}
selectedAccountId={values.inventory_account_id}
/>
</FormGroup>
</div>
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button onClick={handleClose}>
<T id={'close'} />
</Button>
<Button
intent={Intent.PRIMARY}
type="submit"
disabled={isSubmitting}
>
{action === 'edit' ? <T id={'edit'} /> : <T id={'submit'} />}
</Button>
</div>
</div>
</form>
<Formik
validationSchema={validationSchema}
initialValues={initialValues}
onSubmit={handleFormSubmit}
>
{({ isSubmitting }) => (
<ItemCategoryForm
itemCategoryId={itemCategoryId}
accountsList={accountsList}
categoriesList={categoriesList}
isSubmitting={isSubmitting}
/>
)}
</Formik>
</DialogContent>
);
}