Files
bigcapital/client/src/containers/Items/ItemForm.js
2021-06-10 12:51:00 +02:00

190 lines
4.8 KiB
JavaScript

import React, { useMemo } from 'react';
import { Formik, Form } from 'formik';
import { Intent } from '@blueprintjs/core';
import { useHistory } from 'react-router-dom';
import intl from 'react-intl-universal';
import classNames from 'classnames';
import { defaultTo } from 'lodash';
import 'style/pages/Items/PageForm.scss';
import { CLASSES } from 'common/classes';
import AppToaster from 'components/AppToaster';
import ItemFormPrimarySection from './ItemFormPrimarySection';
import ItemFormBody from './ItemFormBody';
import ItemFormFloatingActions from './ItemFormFloatingActions';
import ItemFormInventorySection from './ItemFormInventorySection';
import withSettings from 'containers/Settings/withSettings';
import { compose, transformToForm } from 'utils';
import {
EditItemFormSchema,
CreateItemFormSchema,
transformItemFormData,
} from './ItemForm.schema';
import { useItemFormContext } from './ItemFormProvider';
const defaultInitialValues = {
active: 1,
name: '',
type: 'service',
code: '',
cost_price: '',
sell_price: '',
cost_account_id: '',
sell_account_id: '',
inventory_account_id: '',
category_id: '',
sellable: 1,
purchasable: true,
sell_description: '',
purchase_description: '',
};
/**
* Item form.
*/
function ItemForm({
// #withSettings
preferredCostAccount,
preferredSellAccount,
preferredInventoryAccount,
}) {
// Item form context.
const {
itemId,
item,
accounts,
createItemMutate,
editItemMutate,
submitPayload,
isNewMode,
} = useItemFormContext();
// History context.
const history = useHistory();
/**
* Initial values in create and edit mode.
*/
const initialValues = useMemo(
() => ({
...defaultInitialValues,
cost_account_id: defaultTo(preferredCostAccount, ''),
sell_account_id: defaultTo(preferredSellAccount, ''),
inventory_account_id: defaultTo(preferredInventoryAccount, ''),
/**
* 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(
transformItemFormData(item, defaultInitialValues),
defaultInitialValues,
),
}),
[
item,
preferredCostAccount,
preferredSellAccount,
preferredInventoryAccount,
],
);
// Transform API errors.
const transformApiErrors = (error) => {
const {
response: {
data: { errors },
},
} = error;
const fields = {};
if (errors.find((e) => e.type === 'ITEM.NAME.ALREADY.EXISTS')) {
fields.name = intl.get('the_name_used_before');
}
if (errors.find((e) => e.type === 'INVENTORY_ACCOUNT_CANNOT_MODIFIED')) {
AppToaster.show({
message: intl.get('cannot_change_item_inventory_account'),
intent: Intent.DANGER,
});
}
return fields;
};
// Handles the form submit.
const handleFormSubmit = (
values,
{ setSubmitting, resetForm, setErrors },
) => {
setSubmitting(true);
const form = { ...values };
const onSuccess = (response) => {
AppToaster.show({
message: intl.get(
isNewMode
? 'the_item_has_been_created_successfully'
: 'the_item_has_been_edited_successfully',
{
number: itemId,
},
),
intent: Intent.SUCCESS,
});
resetForm();
setSubmitting(false);
// Submit payload.
if (submitPayload.redirect) {
history.push('/items');
}
};
// Handle response error.
const onError = (errors) => {
setSubmitting(false);
if (errors) {
const _errors = transformApiErrors(errors);
setErrors({ ..._errors });
}
};
if (isNewMode) {
createItemMutate(form).then(onSuccess).catch(onError);
} else {
editItemMutate([itemId, form]).then(onSuccess).catch(onError);
}
};
return (
<div class={classNames(CLASSES.PAGE_FORM_ITEM)}>
<Formik
enableReinitialize={true}
validationSchema={isNewMode ? CreateItemFormSchema : EditItemFormSchema}
initialValues={initialValues}
onSubmit={handleFormSubmit}
>
<Form>
<div class={classNames(CLASSES.PAGE_FORM_BODY)}>
<ItemFormPrimarySection />
<ItemFormBody accounts={accounts} />
<ItemFormInventorySection accounts={accounts} />
</div>
<ItemFormFloatingActions />
</Form>
</Formik>
</div>
);
}
export default compose(
withSettings(({ itemsSettings }) => ({
preferredCostAccount: parseInt(itemsSettings?.costAccount),
preferredSellAccount: parseInt(itemsSettings?.sellAccount),
preferredInventoryAccount: parseInt(itemsSettings?.inventoryAccount),
})),
)(ItemForm);