diff --git a/client/src/components/AccountsTypesSelect.js b/client/src/components/AccountsTypesSelect.js
index 2563dbfe0..1803f49f6 100644
--- a/client/src/components/AccountsTypesSelect.js
+++ b/client/src/components/AccountsTypesSelect.js
@@ -1,5 +1,7 @@
import React, { useCallback } from 'react';
+import classNames from 'classnames';
import { ListSelect } from 'components';
+import { CLASSES } from 'common/classes';
export default function AccountsTypesSelect({
accountsTypes,
@@ -7,6 +9,7 @@ export default function AccountsTypesSelect({
defaultSelectText = 'Select account type',
onTypeSelected,
disabled = false,
+ popoverFill = false,
...restProps
}) {
// Filters accounts types items.
@@ -41,6 +44,9 @@ export default function AccountsTypesSelect({
onItemSelect={handleItemSelected}
itemPredicate={filterAccountTypeItems}
disabled={disabled}
+ className={classNames('form-group--select-list', {
+ [CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill,
+ })}
{...restProps}
/>
);
diff --git a/client/src/containers/Dialogs/AccountFormDialog/AccountForm.schema.js b/client/src/containers/Dialogs/AccountFormDialog/AccountForm.schema.js
index c2633b931..c0ce79944 100644
--- a/client/src/containers/Dialogs/AccountFormDialog/AccountForm.schema.js
+++ b/client/src/containers/Dialogs/AccountFormDialog/AccountForm.schema.js
@@ -8,7 +8,7 @@ const Schema = Yup.object().shape({
.min(3)
.max(DATATYPES_LENGTH.STRING)
.label(formatMessage({ id: 'account_name_' })),
- code: Yup.string().digits().min(3).max(6),
+ code: Yup.string().nullable().min(3).max(6),
account_type_id: Yup.number()
.nullable()
.required()
diff --git a/client/src/containers/Dialogs/AccountFormDialog/AccountFormDialogContent.js b/client/src/containers/Dialogs/AccountFormDialog/AccountFormDialogContent.js
index a0ea4f5cc..310343e1a 100644
--- a/client/src/containers/Dialogs/AccountFormDialog/AccountFormDialogContent.js
+++ b/client/src/containers/Dialogs/AccountFormDialog/AccountFormDialogContent.js
@@ -1,49 +1,37 @@
import React, { useCallback, useMemo, useEffect } from 'react';
-import {
- Button,
- Classes,
- FormGroup,
- InputGroup,
- Intent,
- TextArea,
- Checkbox,
- Position,
-} from '@blueprintjs/core';
-import { useFormik } from 'formik';
+import { Intent } from '@blueprintjs/core';
+import { Formik } from 'formik';
import { FormattedMessage as T, useIntl } from 'react-intl';
import { pick, omit } from 'lodash';
import { useQuery, queryCache } from 'react-query';
-import classNames from 'classnames';
-import Yup from 'services/yup';
-import {
- If,
- ErrorMessage,
- AppToaster,
- FieldRequiredHint,
- Hint,
- AccountsSelectList,
- AccountsTypesSelect,
- DialogContent,
-} from 'components';
+import { AppToaster, DialogContent } from 'components';
+
+import AccountFormDialogFields from './AccountFormDialogFields';
import withAccountsActions from 'containers/Accounts/withAccountsActions';
import withAccountDetail from 'containers/Accounts/withAccountDetail';
-import withAccounts from 'containers/Accounts/withAccounts';
import withDialogActions from 'containers/Dialog/withDialogActions';
import {
EditAccountFormSchema,
CreateAccountFormSchema,
} from './AccountForm.schema';
-import { compose } from 'utils';
+
+import { compose, transformToForm } from 'utils';
+import { transformApiErrors, transformAccountToForm } from './utils';
+
+const defaultInitialValues = {
+ account_type_id: '',
+ parent_account_id: '',
+ name: '',
+ code: '',
+ description: '',
+ subaccount: false,
+};
/**
* Account form dialog content.
*/
function AccountFormDialogContent({
- // #withAccounts
- accountsTypes,
- accounts,
-
// #withAccountDetail
account,
@@ -57,132 +45,80 @@ function AccountFormDialogContent({
closeDialog,
// #ownProp
+ dialogName,
accountId,
action,
- dialogName,
parentAccountId,
accountTypeId,
}) {
const { formatMessage } = useIntl();
const isNewMode = !accountId;
+ // Form validation schema in create and edit mode.
const validationSchema = isNewMode
? CreateAccountFormSchema
: EditAccountFormSchema;
-
- const initialValues = useMemo(
- () => ({
- account_type_id: '',
- name: '',
- code: '',
- description: '',
- }),
- [],
- );
- const transformApiErrors = (errors) => {
- const fields = {};
- if (errors.find((e) => e.type === 'NOT_UNIQUE_CODE')) {
- fields.code = formatMessage({ id: 'account_code_is_not_unique' });
+
+ // Callbacks handles form submit.
+ const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
+ const form = omit(values, ['subaccount']);
+ const toastAccountName = values.code
+ ? `${values.code} - ${values.name}`
+ : values.name;
+
+ // Handle request success.
+ const handleSuccess = () => {
+ closeDialog(dialogName);
+ queryCache.invalidateQueries('accounts-table');
+ queryCache.invalidateQueries('accounts-list');
+
+ AppToaster.show({
+ message: formatMessage(
+ {
+ id: isNewMode
+ ? 'service_has_been_successful_created'
+ : 'service_has_been_successful_edited',
+ },
+ {
+ name: toastAccountName,
+ service: formatMessage({ id: 'account' }),
+ },
+ ),
+ intent: Intent.SUCCESS,
+ });
+ };
+ // Handle request error.
+ const handleError = (errors) => {
+ const errorsTransformed = transformApiErrors(errors);
+ setErrors({ ...errorsTransformed });
+ setSubmitting(false);
+ };
+ if (accountId) {
+ requestEditAccount(accountId, form)
+ .then(handleSuccess)
+ .catch(handleError);
+ } else {
+ requestSubmitAccount({ form }).then(handleSuccess).catch(handleError);
}
- if (errors.find((e) => e.type === 'ACCOUNT.NAME.NOT.UNIQUE')) {
- fields.name = formatMessage({ id: 'account_name_is_already_used' });
- }
- return fields;
};
- // Formik
- const {
- errors,
- values,
- touched,
- setFieldValue,
- resetForm,
- handleSubmit,
- isSubmitting,
- getFieldProps,
- } = useFormik({
- enableReinitialize: true,
- initialValues: {
- ...initialValues,
- ...(accountId && pick(account, Object.keys(initialValues))),
- },
- validationSchema,
- onSubmit: (values, { setSubmitting, setErrors }) => {
- const form = omit(values, ['subaccount']);
- const toastAccountName = values.code
- ? `${values.code} - ${values.name}`
- : values.name;
-
- const afterSubmit = () => {
- closeDialog(dialogName);
- queryCache.invalidateQueries('accounts-table');
- queryCache.invalidateQueries('accounts-list');
- };
- const afterErrors = (errors) => {
- const errorsTransformed = transformApiErrors(errors);
- setErrors({ ...errorsTransformed });
- setSubmitting(false);
- };
- if (accountId) {
- requestEditAccount(accountId, form)
- .then((response) => {
- afterSubmit(response);
-
- AppToaster.show({
- message: formatMessage(
- { id: 'service_has_been_successful_edited' },
- {
- name: toastAccountName,
- service: formatMessage({ id: 'account' }),
- },
- ),
- intent: Intent.SUCCESS,
- });
- })
- .catch(afterErrors);
- } else {
- requestSubmitAccount({ form })
- .then((response) => {
- afterSubmit(response);
-
- AppToaster.show({
- message: formatMessage(
- { id: 'service_has_been_successful_created' },
- {
- name: toastAccountName,
- service: formatMessage({ id: 'account' }),
- },
- ),
- intent: Intent.SUCCESS,
- position: Position.BOTTOM,
- });
- })
- .catch(afterErrors);
- }
- },
- });
-
- useEffect(() => {
- if (values.parent_account_id) {
- setFieldValue('subaccount', true);
- }
- }, [values.parent_account_id]);
-
- // Reset `parent account id` after change `account type`.
- useEffect(() => {
- setFieldValue('parent_account_id', null);
- }, [values.account_type_id]);
-
- // Filtered accounts based on the given account type.
- const filteredAccounts = useMemo(
- () =>
- accounts.filter(
- (account) =>
- account.account_type_id === values.account_type_id ||
- !values.account_type_id,
- ),
- [accounts, values.account_type_id],
- );
+ // Form initial values in create and edit mode.
+ const initialValues = {
+ ...defaultInitialValues,
+ /**
+ * We only care about the fields in the form. Previously unfilled optional
+ * values such as `notes` come back from the API as null, so remove those
+ * as well.
+ */
+ ...transformToForm(
+ transformAccountToForm(account, {
+ action,
+ parentAccountId,
+ accountTypeId,
+ }),
+ defaultInitialValues,
+ ),
+ };
// Handles dialog close.
const handleClose = useCallback(() => {
@@ -195,9 +131,9 @@ function AccountFormDialogContent({
);
// Fetches accounts types.
- const fetchAccountsTypes = useQuery('accounts-types-list', async () => {
- await requestFetchAccountTypes();
- });
+ const fetchAccountsTypes = useQuery('accounts-types-list', () =>
+ requestFetchAccountTypes(),
+ );
// Fetch the given account id on edit mode.
const fetchAccount = useQuery(
@@ -211,176 +147,15 @@ function AccountFormDialogContent({
fetchAccountsTypes.isFetching ||
fetchAccount.isFetching;
- // Fetch requests on dialog opening.
- const onDialogOpening = useCallback(() => {
- fetchAccountsList.refetch();
- fetchAccountsTypes.refetch();
-
- if (action === 'edit' && accountId) {
- fetchAccount.refetch();
- }
- if (action === 'new_child') {
- setFieldValue('parent_account_id', parentAccountId);
- setFieldValue('account_type_id', accountTypeId);
- }
- }, [
- parentAccountId,
- accountTypeId,
- fetchAccount,
- fetchAccountsList,
- fetchAccountsTypes,
- ]);
-
- // Handle account type change.
- const onChangeAccountType = useCallback(
- (accountType) => {
- setFieldValue('account_type_id', accountType.id);
- },
- [setFieldValue],
- );
-
- // Handles change sub-account.
- const onChangeSubaccount = useCallback(
- (account) => {
- setFieldValue('parent_account_id', account.id);
- },
- [setFieldValue],
- );
-
- // Handle dialog on closed.
- const onDialogClosed = useCallback(() => {
- resetForm();
- }, [resetForm]);
-
return (