Merge remote-tracking branch 'origin/_tasks'

This commit is contained in:
Ahmed Bouhuolia
2020-07-01 15:01:08 +02:00
30 changed files with 533 additions and 266 deletions

View File

@@ -39,10 +39,10 @@ function ExpensesDataTable({
changeCurrentView,
changePageSubtitle,
setTopbarEditView,
// #withView
viewMeta,
// #ownProps
loading,
onFetchData,
@@ -99,10 +99,9 @@ function ExpensesDataTable({
const actionMenuList = useCallback(
(expense) => (
<Menu>
<MenuItem
text={formatMessage({ id: 'view_details' })} />
<MenuItem text={formatMessage({ id: 'view_details' })} />
<MenuDivider />
<If condition={expense.published}>
<If condition={!expense.published}>
<MenuItem
text={formatMessage({ id: 'publish_expense' })}
onClick={handlePublishExpense(expense)}
@@ -120,23 +119,35 @@ function ExpensesDataTable({
/>
</Menu>
),
[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 =>
(<div>- {category.expense_account.name} ${ category.amount }</div>)
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) => (
<div>
- {category.expense_account.name} ${category.amount}
</div>
));
return (
<Tooltip content={mutliCategories}>{'- Multi Categories -'}</Tooltip>
);
return <Tooltip content={mutliCategories}>{ '- Multi Categories -' }</Tooltip>;
}
}
};
const columns = useMemo(
() => [
@@ -180,7 +191,7 @@ function ExpensesDataTable({
id: 'publish',
Header: formatMessage({ id: 'publish' }),
accessor: (r) => {
return !r.published ? (
return r.published ? (
<Tag minimal={true}>
<T id={'published'} />
</Tag>
@@ -279,5 +290,5 @@ export default compose(
expensesLoading,
expensesPagination,
})),
withViewDetails,
withViewDetails(),
)(ExpensesDataTable);

View File

@@ -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 (
<div className={'form__floating-footer'}>
<Button
disabled={isSubmitting}
intent={Intent.PRIMARY}
className={'ml1'}
name={'save'}
disabled={isSubmitting}
type="submit"
onClick={() => {
onSubmitClick({ publish: true, redirect: true });
}}
>
<T id={'save'} />
{expense && expense.id ? <T id={'edit'} /> : <T id={'save'} />}
</Button>
<Button
disabled={isSubmitting}
className={'button-secondary ml1'}
intent={Intent.PRIMARY}
className={'ml1'}
name={'save_and_new'}
onClick={() => {
onSubmitClick({ publish: true, redirect: false });
}}
>
<T id={'save_as_draft'} />
<T id={'save_new'} />
</Button>
<Button
className={'button-secondary ml1'}
className={'ml1'}
disabled={isSubmitting}
onClick={() => {
onCancelClick && onCancelClick({ publish: false, redirect: false });
onSubmitClick({ publish: false, redirect: false });
}}
>
<T id={'cancel'} />
<T id={'save_as_draft'} />
</Button>
<Button
className={'ml1'}
onClick={() => {
onCancelClick && onCancelClick();
}}
>
<T id={'close'} />
</Button>
</div>
);
};
}

View File

@@ -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'}
/>
</div>
<ExpenseFloatingFooter
formik={formik}
onSubmitClick={handleSubmitClick}
onCancelClick={handleCancelClick}
/>
</form>
<ExpenseFloatingFooter
formik={formik}
onSubmitClick={handleSubmitClick}
expense={expense}
onCancelClick={handleCancelClick}
/>
</div>
);
}
@@ -329,5 +342,5 @@ export default compose(
withAccountsActions,
withDashboardActions,
withMediaActions,
withExpneseDetail(),
withExpenseDetail(),
)(ExpenseForm);

View File

@@ -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,

View File

@@ -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 (
<span>
{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' }) }<Hint /></>),
Header: (
<>
{formatMessage({ id: 'expense_category' })}
<Hint />
</>
),
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],
);

View File

@@ -96,11 +96,11 @@ const withExpensesViewTabs = connect(mapStateToProps);
export default compose(
withRouter,
withViewDetails(),
withExpensesViewTabs,
withExpensesActions,
withDashboardActions,
withViewDetails(),
withExpenses(({ expensesViews }) => ({
expensesViews,
})),
withExpensesActions,
withDashboardActions,
)(ExpenseViewTabs);

View File

@@ -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'}
>
<ExpenseForm
onFormSubmit={handleFormSubmit}

View File

@@ -19,7 +19,6 @@ import withViewsActions from 'containers/Views/withViewsActions';
import { compose } from 'utils';
function ExpensesList({
// #withDashboardActions
changePageTitle,
@@ -223,7 +222,7 @@ function ExpensesList({
</p>
</Alert>
{/* <Alert
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={
<T id={'delete_count'} values={{ count: selectedRowsCount }} />
@@ -236,10 +235,10 @@ function ExpensesList({
>
<p>
<T
id={'once_delete_these_journalss_you_will_not_able_restore_them'}
id={'once_delete_these_expenses_you_will_not_able_restore_them'}
/>
</p>
</Alert> */}
</Alert>
</DashboardPageContent>
</DashboardInsider>
);
@@ -248,7 +247,7 @@ function ExpensesList({
export default compose(
withDashboardActions,
withExpensesActions,
withExpenses(({ expensesTableQuery }) => ({ expensesTableQuery })),
withViewsActions,
withResourceActions
withResourceActions,
withExpenses(({ expensesTableQuery }) => ({ expensesTableQuery })),
)(ExpensesList);

View File

@@ -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'),

View File

@@ -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 })),