From aac138aa1840eb5a033887e0bce8d89ee1aad285 Mon Sep 17 00:00:00 2001
From: elforjani3
Date: Wed, 24 Jun 2020 00:28:15 +0200
Subject: [PATCH] WIP / Feature & Fix Expense /Customer
---
client/src/components/AccountsSelectList.js | 40 +-
.../DataTableCells/AccountsListFieldCell.js | 41 ++-
.../Accounting/MakeJournalEntriesPage.js | 34 +-
client/src/containers/Customers/Customer.js | 24 +-
.../containers/Customers/CustomerFooter.js | 57 +++
.../src/containers/Customers/CustomerForm.js | 93 ++---
.../src/containers/Customers/CustomersList.js | 12 +-
.../Customers/withCustomerDetail.js | 2 +-
.../Customers/withCustomersActions.js | 3 +-
.../containers/Expenses/ExpenseDataTable.js | 49 ++-
.../src/containers/Expenses/ExpenseFooter.js | 36 +-
client/src/containers/Expenses/ExpenseForm.js | 83 +++--
.../containers/Expenses/ExpenseFormHeader.js | 10 +-
.../src/containers/Expenses/ExpenseTable.js | 43 ++-
client/src/containers/Expenses/Expenses.js | 11 +-
.../src/containers/Expenses/ExpensesList.js | 8 +-
.../containers/Expenses/withExpenseDetail.js | 2 +-
.../containers/Items/ItemCategoriesList.js | 6 +-
client/src/containers/Items/ItemForm.js | 344 ++++++++++--------
client/src/containers/Items/ItemFormPage.js | 59 +--
client/src/containers/Items/ItemsFooter.js | 56 +++
client/src/lang/en/index.js | 3 +
client/src/lang/en/locale.js | 2 +-
.../src/store/customers/customers.actions.js | 16 +
.../src/store/customers/customers.reducer.js | 11 +
client/src/store/customers/customers.type.js | 3 +-
client/src/store/expenses/expenses.actions.js | 2 +-
client/src/store/items/items.actions.js | 120 +++---
client/src/store/items/items.reducer.js | 32 +-
29 files changed, 762 insertions(+), 440 deletions(-)
create mode 100644 client/src/containers/Customers/CustomerFooter.js
create mode 100644 client/src/containers/Items/ItemsFooter.js
diff --git a/client/src/components/AccountsSelectList.js b/client/src/components/AccountsSelectList.js
index 374010f77..8054fed8f 100644
--- a/client/src/components/AccountsSelectList.js
+++ b/client/src/components/AccountsSelectList.js
@@ -1,43 +1,49 @@
-import React, {useCallback, useState} from 'react';
-import {
- MenuItem,
- Button,
-} from '@blueprintjs/core';
-import {Select} from '@blueprintjs/select';
+import React, { useCallback, useState } from 'react';
+import { MenuItem, Button } from '@blueprintjs/core';
+import { Select } from '@blueprintjs/select';
export default function AccountsSelectList({
accounts,
onAccountSelected,
- error,
+ error = [],
initialAccount,
- defautlSelectText = 'Select account'
+ defautlSelectText = 'Select account',
}) {
const [selectedAccount, setSelectedAccount] = useState(
- initialAccount || null
+ initialAccount || null,
);
// Account item of select accounts field.
const accountItem = useCallback((item, { handleClick, modifiers, query }) => {
return (
-
+
);
}, []);
- const onAccountSelect = useCallback((account) => {
- setSelectedAccount({ ...account });
- onAccountSelected && onAccountSelected(account);
- }, [setSelectedAccount, onAccountSelected]);
+ const onAccountSelect = useCallback(
+ (account) => {
+ setSelectedAccount({ ...account });
+ onAccountSelected && onAccountSelected(account);
+ },
+ [setSelectedAccount, onAccountSelected],
+ );
return (
}
+ noResults={}
itemRenderer={accountItem}
popoverProps={{ minimal: true }}
filterable={true}
- onItemSelect={onAccountSelect}>
+ onItemSelect={onAccountSelect}
+ >
);
-}
\ No newline at end of file
+}
diff --git a/client/src/components/DataTableCells/AccountsListFieldCell.js b/client/src/components/DataTableCells/AccountsListFieldCell.js
index 6cee2c770..c5853b4dd 100644
--- a/client/src/components/DataTableCells/AccountsListFieldCell.js
+++ b/client/src/components/DataTableCells/AccountsListFieldCell.js
@@ -1,11 +1,7 @@
-import React, {useCallback, useMemo} from 'react';
+import React, { useCallback, useMemo } from 'react';
import AccountsSelectList from 'components/AccountsSelectList';
import classNames from 'classnames';
-import {
- FormGroup,
- Classes,
- Intent,
-} from '@blueprintjs/core';
+import { FormGroup, Classes, Intent } from '@blueprintjs/core';
// Account cell renderer.
const AccountCellRenderer = ({
@@ -14,31 +10,38 @@ const AccountCellRenderer = ({
cell: { value: initialValue },
payload: { accounts, updateData, errors },
}) => {
- const handleAccountSelected = useCallback((account) => {
- updateData(index, id, account.id);
- }, [updateData, index, id]);
+ const handleAccountSelected = useCallback(
+ (account) => {
+ updateData(index, id, account.id);
+ },
+ [updateData, index, id],
+ );
- const { account_id = false } = (errors[index] || {});
+ const { account_id = false, expense_account_id = false } =
+ errors[index] || {};
- const initialAccount = useMemo(() =>
- accounts.find(a => a.id === initialValue),
- [accounts, initialValue]);
+ const initialAccount = useMemo(
+ () => accounts.find((a) => a.id === initialValue),
+ [accounts, initialValue],
+ );
return (
+ Classes.FILL,
+ )}
+ >
+ error={[account_id, expense_account_id]}
+ initialAccount={initialAccount}
+ />
);
};
-export default AccountCellRenderer;
\ No newline at end of file
+export default AccountCellRenderer;
diff --git a/client/src/containers/Accounting/MakeJournalEntriesPage.js b/client/src/containers/Accounting/MakeJournalEntriesPage.js
index 3fb6a708c..9b907201d 100644
--- a/client/src/containers/Accounting/MakeJournalEntriesPage.js
+++ b/client/src/containers/Accounting/MakeJournalEntriesPage.js
@@ -9,8 +9,7 @@ import withCustomersActions from 'containers/Customers/withCustomersActions';
import withAccountsActions from 'containers/Accounts/withAccountsActions';
import withManualJournalsActions from 'containers/Accounting/withManualJournalsActions';
-import {compose} from 'utils';
-
+import { compose } from 'utils';
function MakeJournalEntriesPage({
// #withCustomersActions
@@ -25,20 +24,25 @@ function MakeJournalEntriesPage({
const history = useHistory();
const { id } = useParams();
- const fetchAccounts = useQuery('accounts-list',
- (key) => requestFetchAccounts());
+ const fetchAccounts = useQuery('accounts-list', (key) =>
+ requestFetchAccounts(),
+ );
- const fetchCustomers = useQuery('customers-list',
- (key) => requestFetchCustomers());
+ const fetchCustomers = useQuery('customers-list', (key) =>
+ requestFetchCustomers(),
+ );
const fetchJournal = useQuery(
id && ['manual-journal', id],
- (key, journalId) => requestFetchManualJournal(journalId));
+ (key, journalId) => requestFetchManualJournal(journalId),
+ );
- const handleFormSubmit = useCallback((payload) => {
- payload.redirect &&
- history.push('/manual-journals');
- }, [history]);
+ const handleFormSubmit = useCallback(
+ (payload) => {
+ payload.redirect && history.push('/manual-journals');
+ },
+ [history],
+ );
const handleCancel = useCallback(() => {
history.push('/manual-journals');
@@ -51,11 +55,13 @@ function MakeJournalEntriesPage({
fetchAccounts.isFetching ||
fetchCustomers.isFetching
}
- name={'make-journal-page'}>
+ name={'make-journal-page'}
+ >
+ onCancelForm={handleCancel}
+ />
);
}
@@ -64,4 +70,4 @@ export default compose(
withAccountsActions,
withCustomersActions,
withManualJournalsActions,
-)(MakeJournalEntriesPage);
\ No newline at end of file
+)(MakeJournalEntriesPage);
diff --git a/client/src/containers/Customers/Customer.js b/client/src/containers/Customers/Customer.js
index e4a8e237b..7d8b0f70f 100644
--- a/client/src/containers/Customers/Customer.js
+++ b/client/src/containers/Customers/Customer.js
@@ -25,15 +25,31 @@ function Customer({
requestFetchCustomers({}),
);
- const fetchCustomerDatails =useQuery(id && ['customer-detail',id],()=>requestFetchCustomers())
+ const fetchCustomerDatails = useQuery(id && ['customer-detail', id], () =>
+ requestFetchCustomers(),
+ );
+
+ const handleFormSubmit = useCallback(
+ (payload) => {
+ payload.redirect && history.push('/customers');
+ },
+ [history],
+ );
+
+ const handleCancel = useCallback(() => {
+ history.goBack();
+ }, [history]);
return (
-
+
);
}
diff --git a/client/src/containers/Customers/CustomerFooter.js b/client/src/containers/Customers/CustomerFooter.js
new file mode 100644
index 000000000..f1c0aaf51
--- /dev/null
+++ b/client/src/containers/Customers/CustomerFooter.js
@@ -0,0 +1,57 @@
+import React from 'react';
+import { Intent, Button } from '@blueprintjs/core';
+import { FormattedMessage as T } from 'react-intl';
+
+export default function CustomerFloatingFooter({
+ formik: { isSubmitting, resetForm },
+ onSubmitClick,
+ onCancelClick,
+
+ customer,
+}) {
+ return (
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/client/src/containers/Customers/CustomerForm.js b/client/src/containers/Customers/CustomerForm.js
index 4de5b6b47..f535623ac 100644
--- a/client/src/containers/Customers/CustomerForm.js
+++ b/client/src/containers/Customers/CustomerForm.js
@@ -31,6 +31,7 @@ import withCustomers from 'containers/Customers//withCustomers';
import useMedia from 'hooks/useMedia';
import { compose } from 'utils';
+import CustomerFloatingFooter from './CustomerFooter';
function CustomerForm({
// #withDashboardActions
@@ -40,7 +41,7 @@ function CustomerForm({
customers,
//#withCustomerDetail
- customerDetail,
+ customer,
//#withCustomersActions
requestSubmitCustomer,
@@ -50,9 +51,13 @@ function CustomerForm({
// #withMediaActions
requestSubmitMedia,
requestDeleteMedia,
+ //#Props
+ onFormSubmit,
+ onCancelForm,
}) {
const { formatMessage } = useIntl();
const history = useHistory();
+ const [payload, setPayload] = useState({});
const {
setFiles,
@@ -127,22 +132,30 @@ function CustomerForm({
const initialValues = useMemo(
() => ({
- ...(customerDetail
+ ...(customer
? {
- ...pick(customerDetail, Object.keys(defaultInitialValues)),
+ ...pick(customer, Object.keys(defaultInitialValues)),
}
: {
...defaultInitialValues,
}),
}),
- [customerDetail, defaultInitialValues],
+ [customer, defaultInitialValues],
+ );
+
+ const saveInvokeSubmit = useCallback(
+ (payload) => {
+ onFormSubmit && onFormSubmit(payload);
+ },
+
+ [onFormSubmit],
);
useEffect(() => {
- customerDetail && customerDetail.id
+ customer && customer.id
? changePageTitle(formatMessage({ id: 'edit_customer_details' }))
: changePageTitle(formatMessage({ id: 'new_customer' }));
- }, [changePageTitle, customerDetail, formatMessage]);
+ }, [changePageTitle, customer, formatMessage]);
const formik = useFormik({
enableReinitialize: true,
@@ -152,9 +165,9 @@ function CustomerForm({
},
onSubmit: (values, { setSubmitting, resetForm, setErrors }) => {
- const formValues = { ...values };
- if (customerDetail && customerDetail.id) {
- requestEditCustomer(customerDetail.id, formValues)
+ const formValues = { ...values, status: payload.publish };
+ if (customer && customer.id) {
+ requestEditCustomer(customer.id, formValues)
.then((response) => {
AppToaster.show({
message: formatMessage({
@@ -163,8 +176,9 @@ function CustomerForm({
intent: Intent.SUCCESS,
});
setSubmitting(false);
- history.push('/customers');
+ // history.push('/customers');
resetForm();
+ saveInvokeSubmit({ action: 'update', ...payload });
})
.catch((errors) => {
setSubmitting(false);
@@ -178,7 +192,9 @@ function CustomerForm({
}),
intent: Intent.SUCCESS,
});
- history.push('/customers');
+ // history.push('/customers');
+ setSubmitting(false);
+ saveInvokeSubmit({ action: 'new', ...payload });
})
.catch((errors) => {
setSubmitting(false);
@@ -202,8 +218,8 @@ function CustomerForm({
);
const initialAttachmentFiles = useMemo(() => {
- return customerDetail && customerDetail.media
- ? customerDetail.media.map((attach) => ({
+ return customer && customer.media
+ ? customer.media.map((attach) => ({
preview: attach.attachment_file,
upload: true,
metadata: { ...attach },
@@ -224,11 +240,20 @@ function CustomerForm({
},
[setDeletedFiles, deletedFiles],
);
+ const handleSubmitClick = useCallback(
+ (payload) => {
+ setPayload(payload);
+ formik.handleSubmit();
+ },
+ [setPayload, formik],
+ );
- const handleCancelClickBtn = () => {
- history.goBack();
- };
-
+ const handleCancelClick = useCallback(
+ (payload) => {
+ onCancelForm && onCancelForm(payload);
+ },
+ [onCancelForm],
+ );
return (
+
+
);
}
diff --git a/client/src/containers/Customers/CustomersList.js b/client/src/containers/Customers/CustomersList.js
index 7c2098c3c..9d47c649b 100644
--- a/client/src/containers/Customers/CustomersList.js
+++ b/client/src/containers/Customers/CustomersList.js
@@ -137,21 +137,21 @@ function CustomersList({
[fetchCustomers],
);
- // Handle items bulk delete button click.,
+ // Handle Customers bulk delete button click.,
const handleBulkDelete = useCallback(
- (itemsIds) => {
- setBulkDelete(itemsIds);
+ (customersIds) => {
+ setBulkDelete(customersIds);
},
[setBulkDelete],
);
- // Handle cancel accounts bulk delete.
+ // Handle cancel cusomters bulk delete.
const handleCancelBulkDelete = useCallback(() => {
setBulkDelete(false);
}, []);
- // Handle confirm items bulk delete.
+ // Handle confirm customers bulk delete.
const handleConfirmBulkDelete = useCallback(() => {
requestDeleteBulkCustomers(bulkDelete)
.then(() => {
@@ -163,7 +163,7 @@ function CustomersList({
intent: Intent.SUCCESS,
});
})
- .catch((errors) => {
+ .catch((error) => {
setBulkDelete(false);
});
}, [requestDeleteBulkCustomers, bulkDelete, formatMessage]);
diff --git a/client/src/containers/Customers/withCustomerDetail.js b/client/src/containers/Customers/withCustomerDetail.js
index 755c2fc1a..bd7cc8a65 100644
--- a/client/src/containers/Customers/withCustomerDetail.js
+++ b/client/src/containers/Customers/withCustomerDetail.js
@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import { getCustomerById } from 'store/customers/customers.reducer';
const mapStateToProps = (state, props) => ({
- customerDetail: getCustomerById(state, props.customerId),
+ customer: getCustomerById(state, props.customerId),
});
export default connect(mapStateToProps);
diff --git a/client/src/containers/Customers/withCustomersActions.js b/client/src/containers/Customers/withCustomersActions.js
index 66900f4f8..590004dd3 100644
--- a/client/src/containers/Customers/withCustomersActions.js
+++ b/client/src/containers/Customers/withCustomersActions.js
@@ -4,13 +4,14 @@ import {
submitCustomer,
editCustomer,
deleteCustomer,
+ deleteBulkCustomers
} from 'store/customers/customers.actions';
import t from 'store/types';
export const mapDispatchToProps = (dispatch) => ({
requestFetchCustomers: (query) => dispatch(fetchCustomers({ query })),
requestDeleteCustomer: (id) => dispatch(deleteCustomer({ id })),
- // requestDeleteBulkCustomers:(ids)=>dispatch(deleteBulkCustomers({ids})),
+ requestDeleteBulkCustomers:(ids)=>dispatch(deleteBulkCustomers({ids})),
requestSubmitCustomer: (form) => dispatch(submitCustomer({ form })),
requestEditCustomer: (id, form) => dispatch(editCustomer({ id, form })),
diff --git a/client/src/containers/Expenses/ExpenseDataTable.js b/client/src/containers/Expenses/ExpenseDataTable.js
index bc5579a0d..0aeaad5b0 100644
--- a/client/src/containers/Expenses/ExpenseDataTable.js
+++ b/client/src/containers/Expenses/ExpenseDataTable.js
@@ -33,15 +33,15 @@ function ExpenseDataTable({
//#withExpenes
expenses,
expensesLoading,
-
+
// #withDashboardActions
changeCurrentView,
changePageSubtitle,
setTopbarEditView,
-
+
// #withView
viewMeta,
-
+
// #ownProps
loading,
onFetchData,
@@ -98,10 +98,9 @@ function ExpenseDataTable({
const actionMenuList = useCallback(
(expense) => (
),
- [handleEditExpense, handleDeleteExpense, handlePublishExpense, formatMessage],
+ [
+ handleEditExpense,
+ handleDeleteExpense,
+ handlePublishExpense,
+ formatMessage,
+ ],
);
- const onRowContextMenu = useCallback((cell) => {
- return actionMenuList(cell.row.original);
- }, [actionMenuList]);
+ const onRowContextMenu = useCallback(
+ (cell) => {
+ return actionMenuList(cell.row.original);
+ },
+ [actionMenuList],
+ );
- const expenseAccountAccessor = (expense) => {
- if (expense.categories.length === 1) {
- return expense.categories[0].expense_account.name;
- } else if (expense.categories.length > 1) {
- const mutliCategories = expense.categories.map(category =>
- (- {category.expense_account.name} ${ category.amount }
)
+ const expenseAccountAccessor = (_expense) => {
+ if (_expense.categories.length === 1) {
+ return _expense.categories[0].expense_account.name;
+ } else if (_expense.categories.length > 1) {
+ const mutliCategories = _expense.categories.map((category) => (
+
+ - {category.expense_account.name} ${category.amount}
+
+ ));
+ return (
+ {'- Multi Categories -'}
);
- return { '- Multi Categories -' };
}
- }
+ };
const columns = useMemo(
() => [
@@ -179,7 +190,7 @@ function ExpenseDataTable({
id: 'publish',
Header: formatMessage({ id: 'publish' }),
accessor: (r) => {
- return !r.published ? (
+ return r.published ? (
diff --git a/client/src/containers/Expenses/ExpenseFooter.js b/client/src/containers/Expenses/ExpenseFooter.js
index 8bd219518..d1506b9ff 100644
--- a/client/src/containers/Expenses/ExpenseFooter.js
+++ b/client/src/containers/Expenses/ExpenseFooter.js
@@ -2,42 +2,56 @@ import React from 'react';
import { Intent, Button } from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl';
-export default function ExpenseFooter({
+export default function ExpenseFloatingFooter({
formik: { isSubmitting },
onSubmitClick,
onCancelClick,
+
+ expense,
}) {
return (
+
+
+
);
-};
+}
diff --git a/client/src/containers/Expenses/ExpenseForm.js b/client/src/containers/Expenses/ExpenseForm.js
index 3bc924c34..b9f7bb559 100644
--- a/client/src/containers/Expenses/ExpenseForm.js
+++ b/client/src/containers/Expenses/ExpenseForm.js
@@ -11,7 +11,7 @@ import moment from 'moment';
import { Intent, FormGroup, TextArea } from '@blueprintjs/core';
import { FormattedMessage as T, useIntl } from 'react-intl';
import { pick } from 'lodash';
-import { useQuery } from 'react-query';
+import { useQuery, queryCache } from 'react-query';
import { Col, Row } from 'react-grid-system';
import ExpenseFormHeader from './ExpenseFormHeader';
@@ -45,12 +45,14 @@ function ExpenseForm({
//#withExpenseDetail
// @todo expenseDetail to expense
- expenseDetail,
+ expense,
// #own Props
expenseId,
onFormSubmit,
onCancelForm,
+ onClickAddNewRow,
+ onClickRemoveRow,
}) {
const { formatMessage } = useIntl();
const [payload, setPayload] = useState({});
@@ -75,14 +77,14 @@ function ExpenseForm({
};
useEffect(() => {
- if (expenseDetail && expenseDetail.id) {
+ if (expense && expense.id) {
changePageTitle(formatMessage({ id: 'edit_expense' }));
// changePageSubtitle(`No. ${expenseDetail.payment_account_id}`);
} else {
changePageTitle(formatMessage({ id: 'new_expense' }));
}
// @todo not functions just states.
- }, [changePageTitle, changePageSubtitle, expenseDetail, formatMessage]);
+ }, [changePageTitle, changePageSubtitle, expense, formatMessage]);
const validationSchema = Yup.object().shape({
beneficiary: Yup.string()
@@ -106,7 +108,13 @@ function ExpenseForm({
index: Yup.number().nullable(),
amount: Yup.number().nullable(),
// @todo expense_account_id is required.
- expense_account_id: Yup.number().nullable(),
+ // expense_account_id: Yup.number().nullable(),
+ expense_account_id: Yup.number()
+ .nullable()
+ .when(['amount'], {
+ is: (amount) => amount,
+ then: Yup.number().required(),
+ }),
description: Yup.string().nullable(),
}),
),
@@ -116,6 +124,7 @@ function ExpenseForm({
(payload) => {
onFormSubmit && onFormSubmit(payload);
},
+
[onFormSubmit],
);
@@ -147,32 +156,43 @@ function ExpenseForm({
[defaultCategory],
);
+ const orderingCategoriesIndex = (categories) => {
+ return categories.map((category, index) => ({
+ ...category,
+ index: index + 1,
+ }));
+ };
+
const initialValues = useMemo(
() => ({
- ...(expenseDetail
+ ...(expense
? {
- ...pick(expenseDetail, Object.keys(defaultInitialValues)),
- categories: expenseDetail.categories.map((category) => ({
+ ...pick(expense, Object.keys(defaultInitialValues)),
+ categories: expense.categories.map((category) => ({
...pick(category, Object.keys(defaultCategory)),
- }),
- ),
+ })),
}
: {
...defaultInitialValues,
+ categories: orderingCategoriesIndex(
+ defaultInitialValues.categories,
+ ),
}),
}),
- [expenseDetail, defaultInitialValues, defaultCategory],
+ [expense, defaultInitialValues, defaultCategory],
);
+ console.log(initialValues.categories, 'ERR');
+
const initialAttachmentFiles = useMemo(() => {
- return expenseDetail && expenseDetail.media
- ? expenseDetail.media.map((attach) => ({
+ return expense && expense.media
+ ? expense.media.map((attach) => ({
preview: attach.attachment_file,
uploaded: true,
metadata: { ...attach },
}))
: [];
- }, [expenseDetail]);
+ }, [expense]);
const formik = useFormik({
enableReinitialize: true,
@@ -182,11 +202,12 @@ function ExpenseForm({
},
onSubmit: async (values, { setSubmitting, setErrors, resetForm }) => {
const categories = values.categories.filter(
- (category) => category.amount || category.index,
+ (category) =>
+ category.amount && category.index && category.expense_account_id,
);
const form = {
...values,
- published: payload.publish,
+ publish: payload.publish,
categories,
};
@@ -194,8 +215,8 @@ function ExpenseForm({
new Promise((resolve, reject) => {
const requestForm = { ...form, media_ids: mdeiaIds };
- if (expenseDetail && expenseDetail.id) {
- requestEditExpense(expenseDetail.id, requestForm)
+ if (expense && expense.id) {
+ requestEditExpense(expense.id, requestForm)
.then((response) => {
AppToaster.show({
message: formatMessage(
@@ -208,10 +229,8 @@ function ExpenseForm({
saveInvokeSubmit({ action: 'update', ...payload });
clearSavedMediaIds([]);
resetForm();
- resolve(response);
})
.catch((errors) => {
- // @todo handle errors.
if (errors.find((e) => e.type === 'TOTAL.AMOUNT.EQUALS.ZERO')) {
}
setErrors(
@@ -235,10 +254,11 @@ function ExpenseForm({
intent: Intent.SUCCESS,
});
setSubmitting(false);
+ formik.resetForm();
saveInvokeSubmit({ action: 'new', ...payload });
clearSavedMediaIds();
- resetForm();
- resolve(response);
+
+ // resolve(response);
})
.catch((errors) => {
setSubmitting(false);
@@ -263,11 +283,14 @@ function ExpenseForm({
const handleSubmitClick = useCallback(
(payload) => {
setPayload(payload);
+ // formik.resetForm();
formik.handleSubmit();
},
[setPayload, formik],
);
+ console.log(formik.values, 'VALUES');
+
const handleCancelClick = useCallback(
(payload) => {
onCancelForm && onCancelForm(payload);
@@ -285,7 +308,7 @@ function ExpenseForm({
},
[setDeletedFiles, deletedFiles],
);
- // @todo @mohamed
+
const fetchHook = useQuery('expense-form', () => requestFetchExpensesTable());
return (
@@ -318,13 +341,15 @@ function ExpenseForm({
hint={'Attachments: Maxiumum size: 20MB'}
/>
-
-
+
);
}
diff --git a/client/src/containers/Expenses/ExpenseFormHeader.js b/client/src/containers/Expenses/ExpenseFormHeader.js
index 5c2baf532..98843db77 100644
--- a/client/src/containers/Expenses/ExpenseFormHeader.js
+++ b/client/src/containers/Expenses/ExpenseFormHeader.js
@@ -24,6 +24,7 @@ function ExpenseFormHeader({
formik: { errors, touched, setFieldValue, getFieldProps, values },
currenciesList,
accounts,
+ accountsTypes,
}) {
const [selectedItems, setSelectedItems] = useState({});
@@ -103,7 +104,6 @@ function ExpenseFormHeader({
const onItemsSelect = useCallback(
(filedName) => {
return (filed) => {
- // @todo @mohamed
setSelectedItems({
...selectedItems,
[filedName]: filed,
@@ -234,10 +234,7 @@ function ExpenseFormHeader({
}
- className={classNames(
- 'form-group--ref_no',
- Classes.FILL,
- )}
+ className={classNames('form-group--ref_no', Classes.FILL)}
intent={
errors.reference_no && touched.reference_no && Intent.DANGER
}
@@ -260,8 +257,9 @@ function ExpenseFormHeader({
}
export default compose(
- withAccounts(({ accounts }) => ({
+ withAccounts(({ accounts, accountsTypes }) => ({
accounts,
+ accountsTypes,
})),
withCurrencies(({ currenciesList }) => ({
currenciesList,
diff --git a/client/src/containers/Expenses/ExpenseTable.js b/client/src/containers/Expenses/ExpenseTable.js
index a4d06926c..733ac2912 100644
--- a/client/src/containers/Expenses/ExpenseTable.js
+++ b/client/src/containers/Expenses/ExpenseTable.js
@@ -41,15 +41,20 @@ function ExpenseTable({
(rowIndex, columnId, value) => {
const newRows = rows.map((row, index) => {
if (index === rowIndex) {
- return { ...rows[rowIndex], [columnId]: value };
+ return {
+ ...rows[rowIndex],
+ [columnId]: value,
+ };
}
return { ...row };
});
setRow(newRows);
setFieldValue(
'categories',
- newRows.map((row) => ({
+
+ newRows.map((row, index) => ({
...omit(row, ['rowType']),
+ index: index + 1,
})),
);
},
@@ -60,15 +65,28 @@ function ExpenseTable({
const handleRemoveRow = useCallback(
(rowIndex) => {
const removeIndex = parseInt(rowIndex, 10);
- const newRows = rows.filter((row, index) => index !== removeIndex);
+ const newRows = rows.filter((row, index) => index !== removeIndex);
setRow([...newRows]);
+
setFieldValue(
'categories',
+
newRows
.filter((row) => row.rowType === 'editor')
- .map((row) => ({ ...omit(row, ['rowType']) })),
+ .map((row, index) => ({
+ ...omit(row, ['rowType']),
+ index: index + 1,
+ })),
);
+ // const newRows = rows.filter((row, index) => index !== removeIndex);
+ // setRow([...newRows]);
+ // setFieldValue(
+ // 'categories',
+ // newRows
+ // .filter((row) => row.rowType === 'editor')
+ // .map((row) => ({ ...omit(row, ['rowType']) })),
+ // );
onClickRemoveRow && onClickRemoveRow(removeIndex);
},
[rows, setFieldValue, onClickRemoveRow],
@@ -82,7 +100,7 @@ function ExpenseTable({
data,
payload,
}) => {
- if (data.length <= index + 2) {
+ if (data.length <= index + 1) {
return '';
}
const onClickRemoveRole = () => {
@@ -104,7 +122,7 @@ function ExpenseTable({
// Total text cell renderer.
const TotalExpenseCellRenderer = (chainedComponent) => (props) => {
- if (props.data.length === props.row.index + 2) {
+ if (props.data.length <= props.row.index + 1) {
return (
{formatMessage({ id: 'total_currency' }, { currency: 'USD' })}
@@ -115,14 +133,14 @@ function ExpenseTable({
};
const NoteCellRenderer = (chainedComponent) => (props) => {
- if (props.data.length === props.row.index + 2) {
+ if (props.data.length === props.row.index + 1) {
return '';
}
return chainedComponent(props);
};
const TotalAmountCellRenderer = (chainedComponent, type) => (props) => {
- if (props.data.length === props.row.index + 2) {
+ if (props.data.length === props.row.index + 1) {
const total = props.data.reduce((total, entry) => {
const amount = parseInt(entry[type], 10);
const computed = amount ? total + amount : total;
@@ -147,7 +165,12 @@ function ExpenseTable({
disableSortBy: true,
},
{
- Header: (<>{ formatMessage({ id: 'expense_category' }) }>),
+ Header: (
+ <>
+ {formatMessage({ id: 'expense_category' })}
+
+ >
+ ),
id: 'expense_account_id',
accessor: 'expense_account_id',
Cell: TotalExpenseCellRenderer(AccountsListFieldCell),
@@ -193,7 +216,7 @@ function ExpenseTable({
const rowClassNames = useCallback(
(row) => ({
- 'row--total': rows.length === row.index + 2,
+ 'row--total': rows.length === row.index + 1,
}),
[rows],
);
diff --git a/client/src/containers/Expenses/Expenses.js b/client/src/containers/Expenses/Expenses.js
index f372fa43e..b6643109a 100644
--- a/client/src/containers/Expenses/Expenses.js
+++ b/client/src/containers/Expenses/Expenses.js
@@ -14,6 +14,7 @@ import { compose } from 'utils';
function Expenses({
// #withwithAccountsActions
requestFetchAccounts,
+ requestFetchAccountTypes,
// #withExpensesActions
requestFetchExpense,
@@ -24,18 +25,15 @@ function Expenses({
const history = useHistory();
const { id } = useParams();
- // @todo
- const fetchAccounts = useQuery('accounts-expense-list', (key) =>
+ const fetchAccounts = useQuery('accounts-list', (key) =>
requestFetchAccounts(),
);
- // @todo
const fetchExpense = useQuery(id && ['expense', id], (key, expense_Id) =>
requestFetchExpense(expense_Id),
);
- // @todo
- const fetchCurrencies = useQuery('currencies-expense-list', () =>
+ const fetchCurrencies = useQuery('currencies', () =>
requestFetchCurrencies(),
);
const handleFormSubmit = useCallback(
@@ -46,7 +44,7 @@ function Expenses({
);
const handleCancel = useCallback(() => {
- history.push('/expenses-list');
+ history.goBack();
}, [history]);
return (
@@ -56,6 +54,7 @@ function Expenses({
fetchAccounts.isFetching ||
fetchCurrencies.isFetching
}
+ name={'expense-form'}
>
requestFetchExpensesTable(),
);
@@ -210,7 +210,7 @@ function ExpensesList({
- {/* }
confirmButtonText={
@@ -223,10 +223,10 @@ function ExpensesList({
>
- */}
+
);
diff --git a/client/src/containers/Expenses/withExpenseDetail.js b/client/src/containers/Expenses/withExpenseDetail.js
index 62a44c352..4644b5510 100644
--- a/client/src/containers/Expenses/withExpenseDetail.js
+++ b/client/src/containers/Expenses/withExpenseDetail.js
@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import { getExpenseById } from 'store/expenses/expenses.reducer';
const mapStateToProps = (state, props) => ({
- expenseDetail: getExpenseById(state, props.expenseId),
+ expense: getExpenseById(state, props.expenseId),
});
export default connect(mapStateToProps);
diff --git a/client/src/containers/Items/ItemCategoriesList.js b/client/src/containers/Items/ItemCategoriesList.js
index 12f7bbd6d..c7d229d1c 100644
--- a/client/src/containers/Items/ItemCategoriesList.js
+++ b/client/src/containers/Items/ItemCategoriesList.js
@@ -49,7 +49,7 @@ const ItemCategoryList = ({
}, [id, changePageTitle, formatMessage]);
const fetchCategories = useQuery(
- ['items-categories-table', filter],
+ ['items-categories-list', filter],
(key, query) => requestFetchItemCategories(query),
);
@@ -183,7 +183,9 @@ const ItemCategoryList = ({
>
diff --git a/client/src/containers/Items/ItemForm.js b/client/src/containers/Items/ItemForm.js
index a3d26f832..e3b864c58 100644
--- a/client/src/containers/Items/ItemForm.js
+++ b/client/src/containers/Items/ItemForm.js
@@ -1,4 +1,4 @@
-import React, { useState, useMemo, useCallback,useEffect } from 'react';
+import React, { useState, useMemo, useCallback, useEffect } from 'react';
import * as Yup from 'yup';
import { useFormik } from 'formik';
import {
@@ -26,16 +26,17 @@ import Dragzone from 'components/Dragzone';
import { ListSelect } from 'components';
import withItemsActions from 'containers/Items/withItemsActions';
-import withItemCategories from 'containers/Items/withItemCategories'
+import withItemCategories from 'containers/Items/withItemCategories';
import withAccounts from 'containers/Accounts/withAccounts';
import withMediaActions from 'containers/Media/withMediaActions';
import useMedia from 'hooks/useMedia';
-import withItemDetail from 'containers/Items/withItemDetail'
+import withItemDetail from 'containers/Items/withItemDetail';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withAccountDetail from 'containers/Accounts/withAccountDetail';
import { compose } from 'utils';
-
+import { resolve } from 'p-progress';
+import ItemFloatingFooter from './ItemsFooter';
const ItemForm = ({
// #withItemActions
@@ -45,7 +46,7 @@ const ItemForm = ({
accounts,
itemDetail,
onFormSubmit,
- onCancelForm,
+ onCancelForm,
// #withDashboardActions
changePageTitle,
@@ -55,10 +56,10 @@ const ItemForm = ({
// #withMediaActions
requestSubmitMedia,
- requestDeleteMedia,
+ requestDeleteMedia,
}) => {
const [payload, setPayload] = useState({});
-
+
const history = useHistory();
const { formatMessage } = useIntl();
const {
@@ -72,22 +73,34 @@ const ItemForm = ({
deleteCallback: requestDeleteMedia,
});
- const ItemTypeDisplay = useMemo(() => [
- { value: null, label: formatMessage({id:'select_item_type'}) },
- { value: 'service', label: formatMessage({id:'service'}) },
- { value: 'inventory', label: formatMessage({id:'inventory'}) },
- { value: 'non-inventory', label: formatMessage({id:'non_inventory'}) },
- ], [formatMessage]);
+ const ItemTypeDisplay = useMemo(
+ () => [
+ { value: null, label: formatMessage({ id: 'select_item_type' }) },
+ { value: 'service', label: formatMessage({ id: 'service' }) },
+ { value: 'inventory', label: formatMessage({ id: 'inventory' }) },
+ { value: 'non-inventory', label: formatMessage({ id: 'non_inventory' }) },
+ ],
+ [formatMessage],
+ );
const validationSchema = Yup.object().shape({
active: Yup.boolean(),
- name: Yup.string().required().label(formatMessage({id:'item_name_'})),
- type: Yup.string().trim().required().label(formatMessage({id:'item_type_'})),
+ name: Yup.string()
+ .required()
+ .label(formatMessage({ id: 'item_name_' })),
+ type: Yup.string()
+ .trim()
+ .required()
+ .label(formatMessage({ id: 'item_type_' })),
sku: Yup.string().trim(),
cost_price: Yup.number(),
sell_price: Yup.number(),
- cost_account_id: Yup.number().required().label(formatMessage({id:'cost_account_id'})),
- sell_account_id: Yup.number().required().label(formatMessage({id:'sell_account_id'})),
+ cost_account_id: Yup.number()
+ .required()
+ .label(formatMessage({ id: 'cost_account_id' })),
+ sell_account_id: Yup.number()
+ .required()
+ .label(formatMessage({ id: 'sell_account_id' })),
inventory_account_id: Yup.number().when('type', {
is: (value) => value === 'inventory',
then: Yup.number().required(),
@@ -111,26 +124,33 @@ const ItemForm = ({
category_id: null,
note: '',
}),
- []
+ [],
+ );
+ const initialValues = useMemo(
+ () => ({
+ ...(itemDetail
+ ? {
+ ...pick(itemDetail, Object.keys(defaultInitialValues)),
+ }
+ : {
+ ...defaultInitialValues,
+ }),
+ }),
+ [itemDetail, defaultInitialValues],
);
- const initialValues = useMemo(() => ({
- ...(itemDetail) ? {
- ...pick(itemDetail, Object.keys(defaultInitialValues)),
-
- } : {
- ...defaultInitialValues,
- }
- }), [itemDetail, defaultInitialValues]);
- const saveInvokeSubmit = useCallback((payload) => {
- onFormSubmit && onFormSubmit(payload)
- }, [onFormSubmit]);
+ const saveInvokeSubmit = useCallback(
+ (payload) => {
+ onFormSubmit && onFormSubmit(payload);
+ },
+ [onFormSubmit],
+ );
useEffect(() => {
- itemDetail && itemDetail.id ?
- changePageTitle(formatMessage({id:'edit_item_details'})) :
- changePageTitle(formatMessage({id:'new_item'}));
- }, [changePageTitle,itemDetail,formatMessage]);
+ itemDetail && itemDetail.id
+ ? changePageTitle(formatMessage({ id: 'edit_item_details' }))
+ : changePageTitle(formatMessage({ id: 'new_item' }));
+ }, [changePageTitle, itemDetail, formatMessage]);
const {
getFieldProps,
@@ -140,65 +160,95 @@ const ItemForm = ({
errors,
handleSubmit,
isSubmitting,
+ resetForm
} = useFormik({
enableReinitialize: true,
validationSchema: validationSchema,
initialValues: {
...initialValues,
},
- onSubmit: (values, { setSubmitting,resetForm,setErrors }) => {
-
+ onSubmit: (values, { setSubmitting, resetForm, setErrors }) => {
const saveItem = (mediaIds) => {
- const formValues = { ...values, media_ids: mediaIds };
- if(itemDetail && itemDetail.id ){
-
- requestEditItem(itemDetail.id,formValues)
- .then((response)=>{
- AppToaster.show({
- message:formatMessage({
- id:'the_item_has_been_successfully_edited',
- },{
- number:itemDetail.id
- }),
- intent:Intent.SUCCESS
- });
- setSubmitting(false);
- saveInvokeSubmit({action:'update',...payload})
- history.push('/items');
- resetForm();
- }).catch((errors)=>{
- setSubmitting(false)
- });
-
- }else{
-
- requestSubmitItem(formValues).then((response) => {
- AppToaster.show({
- message: formatMessage({
- id: 'service_has_been_successful_created',
- }, {
- name: values.name,
- service: formatMessage({ id: 'item' }),
- }),
- intent: Intent.SUCCESS,
- });
- queryCache.removeQueries(['items-table']);
- history.push('/items');
- });
+ const formValues = {
+ ...values,
+ status: payload.publish,
+ media_ids: mediaIds,
};
+ if (itemDetail && itemDetail.id) {
+ requestEditItem(itemDetail.id, formValues)
+ .then((response) => {
+ AppToaster.show({
+ message: formatMessage(
+ {
+ id: 'the_item_has_been_successfully_edited',
+ },
+ {
+ number: itemDetail.id,
+ },
+ ),
+ intent: Intent.SUCCESS,
+ });
+ setSubmitting(false);
+ saveInvokeSubmit({ action: 'update', ...payload });
+ // history.push('/items');
+ resetForm();
+ resolve(response);
+ })
+ .catch((errors) => {
+ setSubmitting(false);
+ });
+ } else {
+ requestSubmitItem(formValues)
+ .then((response) => {
+ AppToaster.show({
+ message: formatMessage(
+ {
+ id: 'service_has_been_successful_created',
+ },
+ {
+ name: values.name,
+ service: formatMessage({ id: 'item' }),
+ },
+ ),
+ intent: Intent.SUCCESS,
+ });
+ setSubmitting(false);
+ resetForm();
+ saveInvokeSubmit({ action: 'new', ...payload });
+ queryCache.removeQueries(['items-table']);
+ // history.push('/items');
+ resolve(response);
+ })
+ .catch((errors) => {
+ setSubmitting(false);
+ });
}
-
-
+ };
Promise.all([saveMedia(), deleteMedia()]).then(
([savedMediaResponses]) => {
const mediaIds = savedMediaResponses.map((res) => res.data.media.id);
return saveItem(mediaIds);
- }
+ },
);
},
});
+ const handleSubmitClick = useCallback(
+ (payload) => {
+ setPayload(payload);
+ handleSubmit();
+ },
+ [setPayload, handleSubmit],
+ );
+
+ const handleCancelClick = useCallback(
+ (payload) => {
+ onCancelForm && onCancelForm(payload);
+ },
+ [onCancelForm],
+ );
+
const accountItem = useCallback(
(item, { handleClick }) => (
),
- []
+ [],
);
-
// Filter Account Items
const filterAccounts = (query, account, _index, exactMatch) => {
const normalizedTitle = account.name.toLowerCase();
@@ -223,38 +272,38 @@ const ItemForm = ({
}
};
- const onItemAccountSelect = useCallback((filedName) => {
- return (account) => {
- setFieldValue(filedName, account.id);
- };
- }, [setFieldValue]);
+ const onItemAccountSelect = useCallback(
+ (filedName) => {
+ return (account) => {
+ setFieldValue(filedName, account.id);
+ };
+ },
+ [setFieldValue],
+ );
const categoryItem = useCallback(
(item, { handleClick }) => (
),
- []
+ [],
);
- const requiredSpan = useMemo(() => *, []);
- const infoIcon = useMemo(() => , []);
+ const requiredSpan = useMemo(() => *, []);
+ const infoIcon = useMemo(() => , []);
const handleMoneyInputChange = (fieldKey) => (e, value) => {
setFieldValue(fieldKey, value);
};
-
- const initialAttachmentFiles =useMemo(()=>{
+ const initialAttachmentFiles = useMemo(() => {
return itemDetail && itemDetail.media
- ? itemDetail.media.map((attach)=>({
-
- preview:attach.attachment_file,
- upload:true,
- metadata:{...attach}
-
- })):[];
-
- },[itemDetail])
+ ? itemDetail.media.map((attach) => ({
+ preview: attach.attachment_file,
+ upload: true,
+ metadata: { ...attach },
+ }))
+ : [];
+ }, [itemDetail]);
const handleDropFiles = useCallback((_files) => {
setFiles(_files.filter((file) => file.uploaded === false));
}, []);
@@ -267,20 +316,15 @@ const ItemForm = ({
}
});
},
- [setDeletedFiles, deletedFiles,]
+ [setDeletedFiles, deletedFiles],
);
- const handleCancelClickBtn = () => {
- history.goBack();
- };
-
return (
-