WIP / Feature & Fix Expense /Customer

This commit is contained in:
elforjani3
2020-06-24 00:28:15 +02:00
parent c9cf54cbf9
commit aac138aa18
29 changed files with 762 additions and 440 deletions

View File

@@ -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) => (
<Menu>
<MenuItem
text={formatMessage({ id: 'view_details' })} />
<MenuItem text={formatMessage({ id: 'view_details' })} />
<MenuDivider />
<If condition={expenses.published}>
<If condition={!expense.published}>
<MenuItem
text={formatMessage({ id: 'publish_expense' })}
onClick={handlePublishExpense(expense)}
@@ -119,23 +118,35 @@ function ExpenseDataTable({
/>
</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(
() => [
@@ -179,7 +190,7 @@ function ExpenseDataTable({
id: 'publish',
Header: formatMessage({ id: 'publish' }),
accessor: (r) => {
return !r.published ? (
return r.published ? (
<Tag minimal={true}>
<T id={'published'} />
</Tag>

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,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'}
/>
</div>
<ExpenseFloatingFooter
formik={formik}
onSubmitClick={handleSubmitClick}
onCancelClick={handleCancelClick}
/>
</form>
<ExpenseFloatingFooter
formik={formik}
onSubmitClick={handleSubmitClick}
expense={expense}
onCancelClick={handleCancelClick}
// onClickAddNewRow={}
// onClickRemoveRow={}
/>
</div>
);
}

View File

@@ -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({
<Col width={200}>
<FormGroup
label={<T id={'ref_no'} />}
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,

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

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

View File

@@ -49,7 +49,7 @@ function ExpensesList({
});
const fetchExpenses = useQuery(
['expenses-table', expensesTableQuery],
['expense-form', expensesTableQuery],
() => requestFetchExpensesTable(),
);
@@ -210,7 +210,7 @@ function ExpensesList({
</p>
</Alert>
{/* <Alert
<Alert
cancelButtonText={<T id={'cancel'} />}
confirmButtonText={
<T id={'delete_count'} values={{ count: selectedRowsCount }} />
@@ -223,10 +223,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>
);

View File

@@ -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);