import React, { useState, useMemo, useCallback, useEffect } from 'react'; import { Formik, Form } from 'formik'; import { Intent } from '@blueprintjs/core'; import { queryCache } from 'react-query'; import { useHistory } from 'react-router-dom'; import { useIntl } from 'react-intl'; import classNames from 'classnames'; import { defaultTo } from 'lodash'; 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 withItemsActions from 'containers/Items/withItemsActions'; import withMediaActions from 'containers/Media/withMediaActions'; import useMedia from 'hooks/useMedia'; import withItemDetail from 'containers/Items/withItemDetail'; import withDashboardActions from 'containers/Dashboard/withDashboardActions'; import withSettings from 'containers/Settings/withSettings'; import { compose, transformToForm } from 'utils'; import { transitionItemTypeKeyToLabel } from './utils'; import { EditItemFormSchema, CreateItemFormSchema } from './ItemForm.schema'; const defaultInitialValues = { active: true, name: '', type: 'service', sku: '', cost_price: '', sell_price: '', cost_account_id: '', sell_account_id: '', inventory_account_id: '', category_id: '', note: '', sellable: true, purchasable: true, }; /** * Item form. */ function ItemForm({ // #withItemActions requestSubmitItem, requestEditItem, itemId, itemDetail, onFormSubmit, // #withDashboardActions changePageTitle, changePageSubtitle, // #withSettings preferredCostAccount, preferredSellAccount, preferredInventoryAccount, // #withMediaActions requestSubmitMedia, requestDeleteMedia, }) { const isNewMode = !itemId; // Holds data of submit button once clicked to form submit function. const [submitPayload, setSubmitPayload] = useState({}); const history = useHistory(); const { formatMessage } = useIntl(); const { setFiles, saveMedia, deletedFiles, setDeletedFiles, deleteMedia, } = useMedia({ saveCallback: requestSubmitMedia, deleteCallback: requestDeleteMedia, }); /** * 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(itemDetail, defaultInitialValues), }), [ itemDetail, preferredCostAccount, preferredSellAccount, preferredInventoryAccount, ], ); useEffect(() => { !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' }); } return fields; }; // Handles the form submit. const handleFormSubmit = ( values, { setSubmitting, resetForm, setErrors }, ) => { setSubmitting(true); const form = { ...values }; const onSuccess = (response) => { AppToaster.show({ message: formatMessage( { id: isNewMode ? 'the_item_has_been_created_successfully' : 'the_item_has_been_edited_successfully', }, { number: itemId, }, ), intent: Intent.SUCCESS, }); resetForm(); setSubmitting(false); queryCache.removeQueries(['items-table']); if (submitPayload.redirect) { history.push('/items'); } }; const onError = ({ response }) => { setSubmitting(false); if (response.data.errors) { const _errors = transformApiErrors(response.data.errors); setErrors({ ..._errors }); } }; if (isNewMode) { requestSubmitItem(form).then(onSuccess).catch(onError); } else { requestEditItem(itemId, form).then(onSuccess).catch(onError); } }; useEffect(() => { if (itemDetail && itemDetail.type) { changePageSubtitle(transitionItemTypeKeyToLabel(itemDetail.type)); } }, [itemDetail, changePageSubtitle, formatMessage]); const initialAttachmentFiles = useMemo(() => { return itemDetail && itemDetail.media ? itemDetail.media.map((attach) => ({ preview: attach.attachment_file, upload: true, metadata: { ...attach }, })) : []; }, [itemDetail]); const handleDropFiles = useCallback( (_files) => { setFiles(_files.filter((file) => file.uploaded === false)); }, [setFiles], ); const handleDeleteFile = useCallback( (_deletedFiles) => { _deletedFiles.forEach((deletedFile) => { if (deletedFile.uploaded && deletedFile.metadata.id) { setDeletedFiles([...deletedFiles, deletedFile.metadata.id]); } }); }, [setDeletedFiles, deletedFiles], ); const handleCancelBtnClick = () => { history.goBack(); }; const handleSubmitAndNewClick = () => { setSubmitPayload({ redirect: false }); }; const handleSubmitClick = () => { setSubmitPayload({ redirect: true }); }; return (
{({ isSubmitting, handleSubmit }) => (
)}
); } export default compose( withItemsActions, withItemDetail, withDashboardActions, withMediaActions, withSettings(({ itemsSettings }) => ({ preferredCostAccount: parseInt(itemsSettings?.preferredCostAccount), preferredSellAccount: parseInt(itemsSettings?.preferredSellAccount), preferredInventoryAccount: parseInt( itemsSettings?.preferredInventoryAccount, ), })), )(ItemForm);