diff --git a/client/src/components/AccountsSelectList.js b/client/src/components/AccountsSelectList.js
index e037a7aad..e1bc9de96 100644
--- a/client/src/components/AccountsSelectList.js
+++ b/client/src/components/AccountsSelectList.js
@@ -1,31 +1,36 @@
-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],
+ );
// Filters accounts items.
const filterAccountsPredicater = useCallback(
@@ -47,15 +52,16 @@ export default function AccountsSelectList({
return (
}
+ noResults={}
itemRenderer={accountItem}
itemPredicate={filterAccountsPredicater}
popoverProps={{ minimal: true }}
filterable={true}
- onItemSelect={onAccountSelect}>
+ onItemSelect={onAccountSelect}
+ >
);
-}
\ No newline at end of file
+}
diff --git a/client/src/components/App.js b/client/src/components/App.js
index 42095276b..d4e0add38 100644
--- a/client/src/components/App.js
+++ b/client/src/components/App.js
@@ -18,7 +18,7 @@ function App({ locale }) {
const queryConfig = {
queries: {
- refetchOnWindowFocus: true,
+ refetchOnWindowFocus: false,
}
};
return (
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 99b7675f9..721ec1922 100644
--- a/client/src/containers/Expenses/ExpenseDataTable.js
+++ b/client/src/containers/Expenses/ExpenseDataTable.js
@@ -39,10 +39,10 @@ function ExpensesDataTable({
changeCurrentView,
changePageSubtitle,
setTopbarEditView,
-
+
// #withView
viewMeta,
-
+
// #ownProps
loading,
onFetchData,
@@ -99,10 +99,9 @@ function ExpensesDataTable({
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(
() => [
@@ -180,7 +191,7 @@ function ExpensesDataTable({
id: 'publish',
Header: formatMessage({ id: 'publish' }),
accessor: (r) => {
- return !r.published ? (
+ return r.published ? (
@@ -279,5 +290,5 @@ export default compose(
expensesLoading,
expensesPagination,
})),
- withViewDetails,
+ withViewDetails(),
)(ExpensesDataTable);
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 9ea5a7b7f..dd4dba147 100644
--- a/client/src/containers/Expenses/ExpenseForm.js
+++ b/client/src/containers/Expenses/ExpenseForm.js
@@ -11,15 +11,13 @@ 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 { Col, Row } from 'react-grid-system';
import ExpenseFormHeader from './ExpenseFormHeader';
import ExpenseTable from './ExpenseTable';
import ExpenseFloatingFooter from './ExpenseFooter';
import withExpensesActions from 'containers/Expenses/withExpensesActions';
-import withExpneseDetail from 'containers/Expenses/withExpenseDetail';
+import withExpenseDetail from 'containers/Expenses/withExpenseDetail';
import withAccountsActions from 'containers/Accounts/withAccountsActions';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withMediaActions from 'containers/Media/withMediaActions';
@@ -44,13 +42,14 @@ function ExpenseForm({
changePageSubtitle,
//#withExpenseDetail
- // @todo expenseDetail to expense
- expenseDetail,
+ expense,
// #own Props
expenseId,
onFormSubmit,
onCancelForm,
+ onClickAddNewRow,
+ onClickRemoveRow,
}) {
const { formatMessage } = useIntl();
const [payload, setPayload] = useState({});
@@ -75,18 +74,17 @@ 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()
- // .required()
.label(formatMessage({ id: 'beneficiary' })),
payment_account_id: Yup.string()
.required()
@@ -104,8 +102,12 @@ function ExpenseForm({
Yup.object().shape({
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()
+ .when(['amount'], {
+ is: (amount) => amount,
+ then: Yup.number().required(),
+ }),
description: Yup.string().nullable(),
}),
),
@@ -115,6 +117,7 @@ function ExpenseForm({
(payload) => {
onFormSubmit && onFormSubmit(payload);
},
+
[onFormSubmit],
);
@@ -146,32 +149,41 @@ 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],
);
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,
@@ -181,11 +193,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,
};
@@ -193,8 +206,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(
@@ -207,10 +220,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(
@@ -234,10 +245,11 @@ function ExpenseForm({
intent: Intent.SUCCESS,
});
setSubmitting(false);
+ formik.resetForm();
saveInvokeSubmit({ action: 'new', ...payload });
clearSavedMediaIds();
- resetForm();
- resolve(response);
+
+ // resolve(response);
})
.catch((errors) => {
setSubmitting(false);
@@ -260,6 +272,7 @@ function ExpenseForm({
const handleSubmitClick = useCallback(
(payload) => {
setPayload(payload);
+ formik.resetForm();
formik.handleSubmit();
},
[setPayload, formik],
@@ -313,13 +326,13 @@ function ExpenseForm({
hint={'Attachments: Maxiumum size: 20MB'}
/>
-
-
+
);
}
@@ -329,5 +342,5 @@ export default compose(
withAccountsActions,
withDashboardActions,
withMediaActions,
- withExpneseDetail(),
+ withExpenseDetail(),
)(ExpenseForm);
diff --git a/client/src/containers/Expenses/ExpenseFormHeader.js b/client/src/containers/Expenses/ExpenseFormHeader.js
index e82d65239..1afdea739 100644
--- a/client/src/containers/Expenses/ExpenseFormHeader.js
+++ b/client/src/containers/Expenses/ExpenseFormHeader.js
@@ -28,6 +28,7 @@ function ExpenseFormHeader({
formik: { errors, touched, setFieldValue, getFieldProps, values },
currenciesList,
accounts,
+ accountsTypes,
}) {
const [selectedItems, setSelectedItems] = useState({});
@@ -102,7 +103,6 @@ function ExpenseFormHeader({
const onItemsSelect = useCallback(
(filedName) => {
return (filed) => {
- // @todo @mohamed
setSelectedItems({
...selectedItems,
[filedName]: filed,
@@ -256,8 +256,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/ExpenseViewTabs.js b/client/src/containers/Expenses/ExpenseViewTabs.js
index 85bd248a9..41fe76ff7 100644
--- a/client/src/containers/Expenses/ExpenseViewTabs.js
+++ b/client/src/containers/Expenses/ExpenseViewTabs.js
@@ -96,11 +96,11 @@ const withExpensesViewTabs = connect(mapStateToProps);
export default compose(
withRouter,
- withViewDetails(),
withExpensesViewTabs,
+ withExpensesActions,
+ withDashboardActions,
+ withViewDetails(),
withExpenses(({ expensesViews }) => ({
expensesViews,
})),
- withExpensesActions,
- withDashboardActions,
)(ExpenseViewTabs);
diff --git a/client/src/containers/Expenses/Expenses.js b/client/src/containers/Expenses/Expenses.js
index f372fa43e..11dedaf7a 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,17 @@ 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),
+ const fetchExpense = useQuery(
+ ['expense', id],
+ (key, _id) => requestFetchExpense(_id),
+ { enabled: !!id },
);
- // @todo
- const fetchCurrencies = useQuery('currencies-expense-list', () =>
+ const fetchCurrencies = useQuery('currencies', () =>
requestFetchCurrencies(),
);
const handleFormSubmit = useCallback(
@@ -46,7 +46,7 @@ function Expenses({
);
const handleCancel = useCallback(() => {
- history.push('/expenses-list');
+ history.goBack();
}, [history]);
return (
@@ -56,6 +56,7 @@ function Expenses({
fetchAccounts.isFetching ||
fetchCurrencies.isFetching
}
+ name={'expense-form'}
>
- {/* }
confirmButtonText={
@@ -236,10 +235,10 @@ function ExpensesList({
>
- */}
+
);
@@ -248,7 +247,7 @@ function ExpensesList({
export default compose(
withDashboardActions,
withExpensesActions,
- withExpenses(({ expensesTableQuery }) => ({ expensesTableQuery })),
withViewsActions,
- withResourceActions
+ withResourceActions,
+ withExpenses(({ expensesTableQuery }) => ({ expensesTableQuery })),
)(ExpensesList);
diff --git a/client/src/containers/Expenses/withExpenses.js b/client/src/containers/Expenses/withExpenses.js
index 5ba6d1187..4eaeb6053 100644
--- a/client/src/containers/Expenses/withExpenses.js
+++ b/client/src/containers/Expenses/withExpenses.js
@@ -14,7 +14,6 @@ export default (mapState) => {
const mapStateToProps = (state, props) => {
const query = getExpensesTableQuery(state, props);
-
const mapped = {
expensesCurrentPage: getExpensesItems(state, props, query),
expensesViews: getResourceViews(state, props, 'expenses'),
diff --git a/client/src/containers/Expenses/withExpensesActions.js b/client/src/containers/Expenses/withExpensesActions.js
index e6592eda9..47ecc298b 100644
--- a/client/src/containers/Expenses/withExpensesActions.js
+++ b/client/src/containers/Expenses/withExpensesActions.js
@@ -10,7 +10,7 @@ import {
} from 'store/expenses/expenses.actions';
import t from 'store/types';
-export const mapDispatchToProps = (dispatch) => ({
+const mapDispatchToProps = (dispatch) => ({
requestSubmitExpense: (form) => dispatch(submitExpense({ form })),
requestFetchExpense: (id) => dispatch(fetchExpense({ id })),
requestEditExpense: (id, form) => dispatch(editExpense({ id, form })),
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/ItemFormPage.js b/client/src/containers/Items/ItemFormPage.js
index 71b95cb9c..bd2bc7200 100644
--- a/client/src/containers/Items/ItemFormPage.js
+++ b/client/src/containers/Items/ItemFormPage.js
@@ -1,5 +1,5 @@
-import React, {useCallback } from 'react';
-import { useParams,useHistory } from 'react-router-dom';
+import React, { useCallback } from 'react';
+import { useParams, useHistory } from 'react-router-dom';
import { useQuery } from 'react-query';
import DashboardInsider from 'components/Dashboard/DashboardInsider';
@@ -12,7 +12,6 @@ import withItemsActions from './withItemsActions';
import { compose } from 'utils';
-
const ItemFormContainer = ({
// #withDashboardActions
changePageTitle,
@@ -22,6 +21,7 @@ const ItemFormContainer = ({
// #withItemsActions
requestFetchItems,
+ requestFetchItem,
// #withItemCategoriesActions
requestFetchItemCategories,
@@ -29,35 +29,47 @@ const ItemFormContainer = ({
const { id } = useParams();
const history = useHistory();
- const fetchAccounts = useQuery('accounts-list',
- (key) => requestFetchAccounts());
+ const fetchAccounts = useQuery('accounts-list', (key) =>
+ requestFetchAccounts(),
+ );
- const fetchCategories = useQuery('item-categories-list',
- (key) => requestFetchItemCategories());
+ const fetchCategories = useQuery('item-categories-list', (key) =>
+ requestFetchItemCategories(),
+ );
- const fetchItemDetail = useQuery(
- id && ['item-detail-list', id],
- (key) => requestFetchItems());
+ const fetchItemDetail = useQuery(
+ ['item', id],
+ (key, _id) => requestFetchItem(_id),
+ {
+ enabled: !!id,
+ },
+ );
-const handleFormSubmit =useCallback((payload)=>{
+ const handleFormSubmit = useCallback(
+ (payload) => {
+ payload.redirect && history.push('/items');
+ },
+ [history],
+ );
- payload.redirect && history.push('/items/new');
-
-},[history])
-
-const handleCancel =useCallback(()=>{
-
- history.push('/items/new');
-},[history])
+ const handleCancel = useCallback(() => {
+ // history.push('/items');
+ history.goBack();
+ }, [history]);
return (
-
+
);
@@ -67,5 +79,5 @@ export default compose(
withDashboardActions,
withAccountsActions,
withItemCategoriesActions,
- withItemsActions
+ withItemsActions,
)(ItemFormContainer);
diff --git a/client/src/containers/Items/ItemsFooter.js b/client/src/containers/Items/ItemsFooter.js
new file mode 100644
index 000000000..57b0e9386
--- /dev/null
+++ b/client/src/containers/Items/ItemsFooter.js
@@ -0,0 +1,56 @@
+import React from 'react';
+import { Intent, Button } from '@blueprintjs/core';
+import { FormattedMessage as T } from 'react-intl';
+
+export default function ItemFloatingFooter({
+ formik: { isSubmitting },
+ onSubmitClick,
+ onCancelClick,
+ itemDetail,
+}) {
+ return (
+
+ );
+}
diff --git a/client/src/containers/Items/withItemsActions.js b/client/src/containers/Items/withItemsActions.js
index fc8cafe94..eb95e8cff 100644
--- a/client/src/containers/Items/withItemsActions.js
+++ b/client/src/containers/Items/withItemsActions.js
@@ -1,6 +1,7 @@
import {connect} from 'react-redux';
import {
fetchItems,
+ fetchItem,
deleteItem,
submitItem,
editItem,
@@ -10,6 +11,7 @@ import t from 'store/types';
export const mapDispatchToProps = (dispatch) => ({
requestFetchItems: (query) => dispatch(fetchItems({ query })),
+ requestFetchItem: (id) => dispatch(fetchItem({ id })),
requestDeleteItem: (id) => dispatch(deleteItem({ id })),
requestDeleteBulkItems:(ids)=>dispatch(deleteBulkItems({ids})),
requestSubmitItem: (form) => dispatch(submitItem({ form })),
diff --git a/client/src/lang/en/index.js b/client/src/lang/en/index.js
index 3d8fb8f52..8e44a0462 100644
--- a/client/src/lang/en/index.js
+++ b/client/src/lang/en/index.js
@@ -438,6 +438,9 @@ export default {
'The expense has been successfully deleted',
the_expenses_has_been_successfully_deleted:
'The expenses has been successfully deleted',
+ once_delete_these_expenses_you_will_not_able_restore_them:
+ "Once you delete these expenses, you won't be able to retrieve them later. Are you sure you want to delete them?",
+
the_expense_id_has_been_published: 'The expense id has been published',
select_beneficiary_account: 'Select Beneficiary Account',
total_amount_equals_zero: 'Total amount equals zero',
diff --git a/client/src/lang/en/locale.js b/client/src/lang/en/locale.js
index 47ba9bba1..395a78ccc 100644
--- a/client/src/lang/en/locale.js
+++ b/client/src/lang/en/locale.js
@@ -8,7 +8,7 @@ export const locale = {
notType: ({ path, type, value, originalValue }) => {
let isCast = originalValue != null && originalValue !== value;
let msg =
- `${path} must beeeeee a \`${type}\` type, ` +
+ `${path} must be a \`${type}\` type, ` +
`but the final value was: \`${printValue(value, true)}\`` +
(isCast
? ` (cast from the value \`${printValue(originalValue, true)}\`).`
diff --git a/client/src/store/customers/customers.actions.js b/client/src/store/customers/customers.actions.js
index ab705bc92..6e2a131a5 100644
--- a/client/src/store/customers/customers.actions.js
+++ b/client/src/store/customers/customers.actions.js
@@ -107,3 +107,19 @@ export const deleteCustomer = ({ id }) => {
});
};
+export const deleteBulkCustomers = ({ ids }) => {
+ return (dispatch) =>
+ new Promise((resolve, reject) => {
+ ApiService.delete('customers', { params: { ids } })
+ .then((response) => {
+ dispatch({
+ type: t.CUSTOMERS_BULK_DELETE,
+ payload: { ids },
+ });
+ resolve(response);
+ })
+ .catch((error) => {
+ reject(error.response.data.errors || []);
+ });
+ });
+};
diff --git a/client/src/store/customers/customers.reducer.js b/client/src/store/customers/customers.reducer.js
index 86d198974..a0ea60a8e 100644
--- a/client/src/store/customers/customers.reducer.js
+++ b/client/src/store/customers/customers.reducer.js
@@ -40,6 +40,17 @@ const customersReducer = createReducer(initialState, {
const { loading } = action.payload;
state.loading = !!loading;
},
+ [t.CUSTOMERS_BULK_DELETE]: (state, action) => {
+ const { ids } = action.payload;
+ const items = { ...state.items };
+
+ ids.forEach((id) => {
+ if (typeof items[id] !== 'undefined') {
+ delete items[id];
+ }
+ });
+ state.items = items;
+ },
});
export default createTableQueryReducers('customers', customersReducer);
diff --git a/client/src/store/customers/customers.type.js b/client/src/store/customers/customers.type.js
index c63620434..81b12d864 100644
--- a/client/src/store/customers/customers.type.js
+++ b/client/src/store/customers/customers.type.js
@@ -4,5 +4,6 @@ export default {
CUSTOMERS_PAGE_SET: 'CUSTOMERS_PAGE_SET',
CUSTOMERS_TABLE_LOADING: 'CUSTOMERS_TABLE_LOADING',
CUSTOMERS_TABLE_QUERIES_ADD: 'CUSTOMERS_TABLE_QUERIES_ADD',
- CUSTOMER_DELETE:'CUSTOMER_DELETE'
+ CUSTOMER_DELETE:'CUSTOMER_DELETE',
+ CUSTOMERS_BULK_DELETE:'CUSTOMERS_BULK_DELETE'
};
diff --git a/client/src/store/expenses/expenses.actions.js b/client/src/store/expenses/expenses.actions.js
index 1adad7578..4f27a56fe 100644
--- a/client/src/store/expenses/expenses.actions.js
+++ b/client/src/store/expenses/expenses.actions.js
@@ -122,7 +122,7 @@ export const deleteExpense = ({ id }) => {
export const deleteBulkExpenses = ({ ids }) => {
return (dispatch) =>
new Promise((resolve, reject) => {
- ApiService.delete('expenses/bulk', { params: { ids } })
+ ApiService.delete('expenses', { params: { ids } })
.then((response) => {
dispatch({
type: t.EXPENSES_BULK_DELETE,
diff --git a/client/src/store/items/items.actions.js b/client/src/store/items/items.actions.js
index 461bd949b..963890492 100644
--- a/client/src/store/items/items.actions.js
+++ b/client/src/store/items/items.actions.js
@@ -2,93 +2,101 @@ import ApiService from 'services/ApiService';
import t from 'store/types';
export const submitItem = ({ form }) => {
- return dispatch => ApiService.post(`items`, form);
+ return (dispatch) => ApiService.post(`items`, form);
};
export const editItem = ({ id, form }) => {
- return dispatch => ApiService.post(`items/${id}`, form);
+ return (dispatch) => ApiService.post(`items/${id}`, form);
};
export const fetchItems = ({ query }) => {
- return (dispatch, getState) => new Promise((resolve, reject) => {
- const pageQuery = getState().items.tableQuery;
+ return (dispatch, getState) =>
+ new Promise((resolve, reject) => {
+ const pageQuery = getState().items.tableQuery;
- dispatch({
- type: t.ITEMS_TABLE_LOADING,
- payload: { loading: true },
- });
- dispatch({
- type: t.SET_DASHBOARD_REQUEST_LOADING,
- });
- ApiService.get(`items`, { params: { ...pageQuery, ...query } }).then(response => {
- dispatch({
- type: t.ITEMS_SET,
- items: response.data.items.results,
- });
- dispatch({
- type: t.ITEMS_PAGE_SET,
- items: response.data.items.results,
- customViewId: response.data.customViewId,
- paginationMeta: response.data.items.pagination,
- });
dispatch({
type: t.ITEMS_TABLE_LOADING,
- payload: { loading: false },
+ payload: { loading: true },
});
dispatch({
- type: t.SET_DASHBOARD_REQUEST_COMPLETED,
+ type: t.SET_DASHBOARD_REQUEST_LOADING,
});
- resolve(response);
- }).catch((error) => {
- dispatch({
- type: t.SET_DASHBOARD_REQUEST_COMPLETED,
- });
- reject(error);
+ ApiService.get(`items`, { params: { ...pageQuery, ...query } })
+ .then((response) => {
+ dispatch({
+ type: t.ITEMS_SET,
+ items: response.data.items.results,
+ });
+ dispatch({
+ type: t.ITEMS_PAGE_SET,
+ items: response.data.items.results,
+ customViewId: response.data.customViewId,
+ paginationMeta: response.data.items.pagination,
+ });
+ dispatch({
+ type: t.ITEMS_TABLE_LOADING,
+ payload: { loading: false },
+ });
+ dispatch({
+ type: t.SET_DASHBOARD_REQUEST_COMPLETED,
+ });
+ resolve(response);
+ })
+ .catch((error) => {
+ dispatch({
+ type: t.SET_DASHBOARD_REQUEST_COMPLETED,
+ });
+ reject(error);
+ });
});
- });
};
export const fetchItem = ({ id }) => {
- return dispatch =>
+ return (dispatch) =>
new Promise((resolve, reject) => {
ApiService.get(`items/${id}`)
- .then(response => {
+ .then((response) => {
dispatch({
type: t.ITEM_SET,
- item: response.data.item
+ item: response.data.item,
});
})
- .catch(error => {
+ .catch((error) => {
reject(error);
});
});
};
export const deleteItem = ({ id }) => {
- return dispatch => new Promise((resolve, reject) => {
- ApiService.delete(`items/${id}`)
- .then((response) => {
- dispatch({
- type: t.ITEM_DELETE,
- payload: { id },
+ return (dispatch) =>
+ new Promise((resolve, reject) => {
+ ApiService.delete(`items/${id}`)
+ .then((response) => {
+ dispatch({
+ type: t.ITEM_DELETE,
+ payload: { id },
+ });
+ resolve(response);
+ })
+ .catch((error) => {
+ reject(error);
});
- resolve(response);
- }).catch((error) => { reject(error); });
- });
+ });
};
-
-
export const deleteBulkItems = ({ ids }) => {
- return dispatch => new Promise((resolve, reject) => {
- ApiService.delete(`items`, { params: { ids }}).then((response) => {
- dispatch({
- type: t.ITEMS_BULK_DELETE,
- payload: { ids }
- });
- resolve(response);
- }).catch((error) => {
- reject(error);
+ return (dispatch) =>
+ new Promise((resolve, reject) => {
+ ApiService.delete('items', { params: { ids } })
+ .then((response) => {
+ dispatch({
+ type: t.ITEMS_BULK_DELETE,
+ payload: { ids },
+ });
+ resolve(response);
+ })
+ .catch((error) => {
+ reject(error.response.data.errors || []);
+ });
});
- });
};
diff --git a/client/src/store/items/items.reducer.js b/client/src/store/items/items.reducer.js
index 25fecd60e..b52b7966c 100644
--- a/client/src/store/items/items.reducer.js
+++ b/client/src/store/items/items.reducer.js
@@ -1,8 +1,6 @@
import t from 'store/types';
import { createReducer } from '@reduxjs/toolkit';
-import {
- getItemsViewPages,
-} from 'store/items/items.selectors';
+import { getItemsViewPages } from 'store/items/items.selectors';
import { createTableQueryReducers } from 'store/queryReducers';
const initialState = {
@@ -20,7 +18,7 @@ const itemsReducer = createReducer(initialState, {
[t.ITEMS_SET]: (state, action) => {
const _items = {};
- action.items.forEach(item => {
+ action.items.forEach((item) => {
_items[item.id] = item;
});
state.items = {
@@ -43,11 +41,11 @@ const itemsReducer = createReducer(initialState, {
if (typeof itemRelation === 'undefined') {
state.itemsRelation[item.id] = [];
}
- const filteredRelation = state.itemsRelation[item.id]
- .filter((relation) => (
+ const filteredRelation = state.itemsRelation[item.id].filter(
+ (relation) => (
relation.viewId === viewId &&
relation.pageNumber === paginationMeta.page
- ));
+ ));
filteredRelation.push({
viewId,
@@ -61,10 +59,10 @@ const itemsReducer = createReducer(initialState, {
pages: {
...viewPages,
[paginationMeta.page]: {
- ids: items.map(i => i.id),
+ ids: items.map((i) => i.id),
meta: paginationMeta,
},
- },
+ },
};
},
@@ -93,9 +91,21 @@ const itemsReducer = createReducer(initialState, {
state.loading = !!loading;
},
- [t.ITEMS_SET_CURRENT_VIEW]: (state, action) => {
+ [t.ITEMS_SET_CURRENT_VIEW]: (state, action) => {
state.currentViewId = action.currentViewId;
},
+
+ [t.ITEMS_BULK_DELETE]: (state, action) => {
+ const { ids } = action.payload;
+ const items = { ...state.items };
+
+ ids.forEach((id) => {
+ if (typeof items[id] !== 'undefined') {
+ delete items[id];
+ }
+ });
+ state.items = items;
+ },
});
export default createTableQueryReducers('items', itemsReducer);