mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 07:10:33 +00:00
WIP / Feature & Fix Expense /Customer
This commit is contained in:
@@ -1,40 +1,46 @@
|
|||||||
import React, {useCallback, useState} from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import {
|
import { MenuItem, Button } from '@blueprintjs/core';
|
||||||
MenuItem,
|
import { Select } from '@blueprintjs/select';
|
||||||
Button,
|
|
||||||
} from '@blueprintjs/core';
|
|
||||||
import {Select} from '@blueprintjs/select';
|
|
||||||
|
|
||||||
export default function AccountsSelectList({
|
export default function AccountsSelectList({
|
||||||
accounts,
|
accounts,
|
||||||
onAccountSelected,
|
onAccountSelected,
|
||||||
error,
|
error = [],
|
||||||
initialAccount,
|
initialAccount,
|
||||||
defautlSelectText = 'Select account'
|
defautlSelectText = 'Select account',
|
||||||
}) {
|
}) {
|
||||||
const [selectedAccount, setSelectedAccount] = useState(
|
const [selectedAccount, setSelectedAccount] = useState(
|
||||||
initialAccount || null
|
initialAccount || null,
|
||||||
);
|
);
|
||||||
// Account item of select accounts field.
|
// Account item of select accounts field.
|
||||||
const accountItem = useCallback((item, { handleClick, modifiers, query }) => {
|
const accountItem = useCallback((item, { handleClick, modifiers, query }) => {
|
||||||
return (
|
return (
|
||||||
<MenuItem text={item.name} label={item.code} key={item.id} onClick={handleClick} />
|
<MenuItem
|
||||||
|
text={item.name}
|
||||||
|
label={item.code}
|
||||||
|
key={item.id}
|
||||||
|
onClick={handleClick}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onAccountSelect = useCallback((account) => {
|
const onAccountSelect = useCallback(
|
||||||
setSelectedAccount({ ...account });
|
(account) => {
|
||||||
onAccountSelected && onAccountSelected(account);
|
setSelectedAccount({ ...account });
|
||||||
}, [setSelectedAccount, onAccountSelected]);
|
onAccountSelected && onAccountSelected(account);
|
||||||
|
},
|
||||||
|
[setSelectedAccount, onAccountSelected],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select
|
<Select
|
||||||
items={accounts}
|
items={accounts}
|
||||||
noResults={<MenuItem disabled={true} text='No results.' />}
|
noResults={<MenuItem disabled={true} text="No results." />}
|
||||||
itemRenderer={accountItem}
|
itemRenderer={accountItem}
|
||||||
popoverProps={{ minimal: true }}
|
popoverProps={{ minimal: true }}
|
||||||
filterable={true}
|
filterable={true}
|
||||||
onItemSelect={onAccountSelect}>
|
onItemSelect={onAccountSelect}
|
||||||
|
>
|
||||||
<Button
|
<Button
|
||||||
text={selectedAccount ? selectedAccount.name : defautlSelectText}
|
text={selectedAccount ? selectedAccount.name : defautlSelectText}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
import React, {useCallback, useMemo} from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import AccountsSelectList from 'components/AccountsSelectList';
|
import AccountsSelectList from 'components/AccountsSelectList';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {
|
import { FormGroup, Classes, Intent } from '@blueprintjs/core';
|
||||||
FormGroup,
|
|
||||||
Classes,
|
|
||||||
Intent,
|
|
||||||
} from '@blueprintjs/core';
|
|
||||||
|
|
||||||
// Account cell renderer.
|
// Account cell renderer.
|
||||||
const AccountCellRenderer = ({
|
const AccountCellRenderer = ({
|
||||||
@@ -14,29 +10,36 @@ const AccountCellRenderer = ({
|
|||||||
cell: { value: initialValue },
|
cell: { value: initialValue },
|
||||||
payload: { accounts, updateData, errors },
|
payload: { accounts, updateData, errors },
|
||||||
}) => {
|
}) => {
|
||||||
const handleAccountSelected = useCallback((account) => {
|
const handleAccountSelected = useCallback(
|
||||||
updateData(index, id, account.id);
|
(account) => {
|
||||||
}, [updateData, index, id]);
|
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(() =>
|
const initialAccount = useMemo(
|
||||||
accounts.find(a => a.id === initialValue),
|
() => accounts.find((a) => a.id === initialValue),
|
||||||
[accounts, initialValue]);
|
[accounts, initialValue],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
intent={account_id ? Intent.DANGER : ''}
|
intent={account_id || expense_account_id ? Intent.DANGER : ''}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'form-group--select-list',
|
'form-group--select-list',
|
||||||
'form-group--account',
|
'form-group--account',
|
||||||
Classes.FILL)}
|
Classes.FILL,
|
||||||
>
|
)}
|
||||||
|
>
|
||||||
<AccountsSelectList
|
<AccountsSelectList
|
||||||
accounts={accounts}
|
accounts={accounts}
|
||||||
onAccountSelected={handleAccountSelected}
|
onAccountSelected={handleAccountSelected}
|
||||||
error={account_id}
|
error={[account_id, expense_account_id]}
|
||||||
initialAccount={initialAccount} />
|
initialAccount={initialAccount}
|
||||||
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ import withCustomersActions from 'containers/Customers/withCustomersActions';
|
|||||||
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||||
import withManualJournalsActions from 'containers/Accounting/withManualJournalsActions';
|
import withManualJournalsActions from 'containers/Accounting/withManualJournalsActions';
|
||||||
|
|
||||||
import {compose} from 'utils';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
|
||||||
function MakeJournalEntriesPage({
|
function MakeJournalEntriesPage({
|
||||||
// #withCustomersActions
|
// #withCustomersActions
|
||||||
@@ -25,20 +24,25 @@ function MakeJournalEntriesPage({
|
|||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
|
||||||
const fetchAccounts = useQuery('accounts-list',
|
const fetchAccounts = useQuery('accounts-list', (key) =>
|
||||||
(key) => requestFetchAccounts());
|
requestFetchAccounts(),
|
||||||
|
);
|
||||||
|
|
||||||
const fetchCustomers = useQuery('customers-list',
|
const fetchCustomers = useQuery('customers-list', (key) =>
|
||||||
(key) => requestFetchCustomers());
|
requestFetchCustomers(),
|
||||||
|
);
|
||||||
|
|
||||||
const fetchJournal = useQuery(
|
const fetchJournal = useQuery(
|
||||||
id && ['manual-journal', id],
|
id && ['manual-journal', id],
|
||||||
(key, journalId) => requestFetchManualJournal(journalId));
|
(key, journalId) => requestFetchManualJournal(journalId),
|
||||||
|
);
|
||||||
|
|
||||||
const handleFormSubmit = useCallback((payload) => {
|
const handleFormSubmit = useCallback(
|
||||||
payload.redirect &&
|
(payload) => {
|
||||||
history.push('/manual-journals');
|
payload.redirect && history.push('/manual-journals');
|
||||||
}, [history]);
|
},
|
||||||
|
[history],
|
||||||
|
);
|
||||||
|
|
||||||
const handleCancel = useCallback(() => {
|
const handleCancel = useCallback(() => {
|
||||||
history.push('/manual-journals');
|
history.push('/manual-journals');
|
||||||
@@ -51,11 +55,13 @@ function MakeJournalEntriesPage({
|
|||||||
fetchAccounts.isFetching ||
|
fetchAccounts.isFetching ||
|
||||||
fetchCustomers.isFetching
|
fetchCustomers.isFetching
|
||||||
}
|
}
|
||||||
name={'make-journal-page'}>
|
name={'make-journal-page'}
|
||||||
|
>
|
||||||
<MakeJournalEntriesForm
|
<MakeJournalEntriesForm
|
||||||
onFormSubmit={handleFormSubmit}
|
onFormSubmit={handleFormSubmit}
|
||||||
manualJournalId={id}
|
manualJournalId={id}
|
||||||
onCancelForm={handleCancel} />
|
onCancelForm={handleCancel}
|
||||||
|
/>
|
||||||
</DashboardInsider>
|
</DashboardInsider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,15 +25,31 @@ function Customer({
|
|||||||
requestFetchCustomers({}),
|
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 (
|
return (
|
||||||
<DashboardInsider
|
<DashboardInsider
|
||||||
// formik={formik}
|
loading={fetchCustomerDatails.isFetching || fetchCustomers.isFetching}
|
||||||
loading={ fetchCustomerDatails.isFetching || fetchCustomers.isFetching}
|
|
||||||
name={'customer-form'}
|
name={'customer-form'}
|
||||||
>
|
>
|
||||||
<CustomerForm customerId={id} />
|
<CustomerForm
|
||||||
|
onFormSubmit={handleFormSubmit}
|
||||||
|
customerId={id}
|
||||||
|
onCancelForm={handleCancel}
|
||||||
|
/>
|
||||||
</DashboardInsider>
|
</DashboardInsider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
57
client/src/containers/Customers/CustomerFooter.js
Normal file
57
client/src/containers/Customers/CustomerFooter.js
Normal file
@@ -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 (
|
||||||
|
<div className={'form__floating-footer'}>
|
||||||
|
<Button
|
||||||
|
intent={Intent.PRIMARY}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
type="submit"
|
||||||
|
onClick={() => {
|
||||||
|
onSubmitClick({ publish: true, redirect: true });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{customer && customer.id ? <T id={'edit'} /> : <T id={'save'} />}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
disabled={isSubmitting}
|
||||||
|
intent={Intent.PRIMARY}
|
||||||
|
className={'ml1'}
|
||||||
|
name={'save_and_new'}
|
||||||
|
onClick={() => {
|
||||||
|
onSubmitClick({ publish: true, redirect: false });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<T id={'save_new'} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className={'ml1'}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
onClick={() => {
|
||||||
|
onSubmitClick({ publish: false, redirect: false });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<T id={'save_as_draft'} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className={'ml1'}
|
||||||
|
onClick={() => {
|
||||||
|
onCancelClick && onCancelClick();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<T id={'close'} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -31,6 +31,7 @@ import withCustomers from 'containers/Customers//withCustomers';
|
|||||||
import useMedia from 'hooks/useMedia';
|
import useMedia from 'hooks/useMedia';
|
||||||
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
import CustomerFloatingFooter from './CustomerFooter';
|
||||||
|
|
||||||
function CustomerForm({
|
function CustomerForm({
|
||||||
// #withDashboardActions
|
// #withDashboardActions
|
||||||
@@ -40,7 +41,7 @@ function CustomerForm({
|
|||||||
customers,
|
customers,
|
||||||
|
|
||||||
//#withCustomerDetail
|
//#withCustomerDetail
|
||||||
customerDetail,
|
customer,
|
||||||
|
|
||||||
//#withCustomersActions
|
//#withCustomersActions
|
||||||
requestSubmitCustomer,
|
requestSubmitCustomer,
|
||||||
@@ -50,9 +51,13 @@ function CustomerForm({
|
|||||||
// #withMediaActions
|
// #withMediaActions
|
||||||
requestSubmitMedia,
|
requestSubmitMedia,
|
||||||
requestDeleteMedia,
|
requestDeleteMedia,
|
||||||
|
//#Props
|
||||||
|
onFormSubmit,
|
||||||
|
onCancelForm,
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
const [payload, setPayload] = useState({});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
setFiles,
|
setFiles,
|
||||||
@@ -127,22 +132,30 @@ function CustomerForm({
|
|||||||
|
|
||||||
const initialValues = useMemo(
|
const initialValues = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...(customerDetail
|
...(customer
|
||||||
? {
|
? {
|
||||||
...pick(customerDetail, Object.keys(defaultInitialValues)),
|
...pick(customer, Object.keys(defaultInitialValues)),
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
...defaultInitialValues,
|
...defaultInitialValues,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
[customerDetail, defaultInitialValues],
|
[customer, defaultInitialValues],
|
||||||
|
);
|
||||||
|
|
||||||
|
const saveInvokeSubmit = useCallback(
|
||||||
|
(payload) => {
|
||||||
|
onFormSubmit && onFormSubmit(payload);
|
||||||
|
},
|
||||||
|
|
||||||
|
[onFormSubmit],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
customerDetail && customerDetail.id
|
customer && customer.id
|
||||||
? changePageTitle(formatMessage({ id: 'edit_customer_details' }))
|
? changePageTitle(formatMessage({ id: 'edit_customer_details' }))
|
||||||
: changePageTitle(formatMessage({ id: 'new_customer' }));
|
: changePageTitle(formatMessage({ id: 'new_customer' }));
|
||||||
}, [changePageTitle, customerDetail, formatMessage]);
|
}, [changePageTitle, customer, formatMessage]);
|
||||||
|
|
||||||
const formik = useFormik({
|
const formik = useFormik({
|
||||||
enableReinitialize: true,
|
enableReinitialize: true,
|
||||||
@@ -152,9 +165,9 @@ function CustomerForm({
|
|||||||
},
|
},
|
||||||
|
|
||||||
onSubmit: (values, { setSubmitting, resetForm, setErrors }) => {
|
onSubmit: (values, { setSubmitting, resetForm, setErrors }) => {
|
||||||
const formValues = { ...values };
|
const formValues = { ...values, status: payload.publish };
|
||||||
if (customerDetail && customerDetail.id) {
|
if (customer && customer.id) {
|
||||||
requestEditCustomer(customerDetail.id, formValues)
|
requestEditCustomer(customer.id, formValues)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: formatMessage({
|
message: formatMessage({
|
||||||
@@ -163,8 +176,9 @@ function CustomerForm({
|
|||||||
intent: Intent.SUCCESS,
|
intent: Intent.SUCCESS,
|
||||||
});
|
});
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
history.push('/customers');
|
// history.push('/customers');
|
||||||
resetForm();
|
resetForm();
|
||||||
|
saveInvokeSubmit({ action: 'update', ...payload });
|
||||||
})
|
})
|
||||||
.catch((errors) => {
|
.catch((errors) => {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
@@ -178,7 +192,9 @@ function CustomerForm({
|
|||||||
}),
|
}),
|
||||||
intent: Intent.SUCCESS,
|
intent: Intent.SUCCESS,
|
||||||
});
|
});
|
||||||
history.push('/customers');
|
// history.push('/customers');
|
||||||
|
setSubmitting(false);
|
||||||
|
saveInvokeSubmit({ action: 'new', ...payload });
|
||||||
})
|
})
|
||||||
.catch((errors) => {
|
.catch((errors) => {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
@@ -202,8 +218,8 @@ function CustomerForm({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const initialAttachmentFiles = useMemo(() => {
|
const initialAttachmentFiles = useMemo(() => {
|
||||||
return customerDetail && customerDetail.media
|
return customer && customer.media
|
||||||
? customerDetail.media.map((attach) => ({
|
? customer.media.map((attach) => ({
|
||||||
preview: attach.attachment_file,
|
preview: attach.attachment_file,
|
||||||
upload: true,
|
upload: true,
|
||||||
metadata: { ...attach },
|
metadata: { ...attach },
|
||||||
@@ -224,11 +240,20 @@ function CustomerForm({
|
|||||||
},
|
},
|
||||||
[setDeletedFiles, deletedFiles],
|
[setDeletedFiles, deletedFiles],
|
||||||
);
|
);
|
||||||
|
const handleSubmitClick = useCallback(
|
||||||
|
(payload) => {
|
||||||
|
setPayload(payload);
|
||||||
|
formik.handleSubmit();
|
||||||
|
},
|
||||||
|
[setPayload, formik],
|
||||||
|
);
|
||||||
|
|
||||||
const handleCancelClickBtn = () => {
|
const handleCancelClick = useCallback(
|
||||||
history.goBack();
|
(payload) => {
|
||||||
};
|
onCancelForm && onCancelForm(payload);
|
||||||
|
},
|
||||||
|
[onCancelForm],
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<div className={'customer-form'}>
|
<div className={'customer-form'}>
|
||||||
<form onSubmit={formik.handleSubmit}>
|
<form onSubmit={formik.handleSubmit}>
|
||||||
@@ -396,34 +421,14 @@ function CustomerForm({
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<CustomersTabs formik={formik} />
|
<CustomersTabs formik={formik} />
|
||||||
|
|
||||||
<div class="form__floating-footer">
|
|
||||||
<Button intent={Intent.PRIMARY} disabled={isSubmitting} type="submit">
|
|
||||||
{customerDetail && customerDetail.id ? (
|
|
||||||
<T id={'edit'} />
|
|
||||||
) : (
|
|
||||||
<T id={'save'} />
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
disabled={isSubmitting}
|
|
||||||
intent={Intent.PRIMARY}
|
|
||||||
className={'ml1'}
|
|
||||||
name={'save_and_new'}
|
|
||||||
>
|
|
||||||
<T id={'save_new'} />
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button className={'ml1'} disabled={isSubmitting}>
|
|
||||||
<T id={'save_as_draft'} />
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button className={'ml1'} onClick={handleCancelClickBtn}>
|
|
||||||
<T id={'close'} />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<CustomerFloatingFooter
|
||||||
|
formik={formik}
|
||||||
|
onSubmitClick={handleSubmitClick}
|
||||||
|
customer={customer}
|
||||||
|
onCancelClick={handleCancelClick}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,21 +137,21 @@ function CustomersList({
|
|||||||
[fetchCustomers],
|
[fetchCustomers],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle items bulk delete button click.,
|
// Handle Customers bulk delete button click.,
|
||||||
|
|
||||||
const handleBulkDelete = useCallback(
|
const handleBulkDelete = useCallback(
|
||||||
(itemsIds) => {
|
(customersIds) => {
|
||||||
setBulkDelete(itemsIds);
|
setBulkDelete(customersIds);
|
||||||
},
|
},
|
||||||
[setBulkDelete],
|
[setBulkDelete],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Handle cancel accounts bulk delete.
|
// Handle cancel cusomters bulk delete.
|
||||||
const handleCancelBulkDelete = useCallback(() => {
|
const handleCancelBulkDelete = useCallback(() => {
|
||||||
setBulkDelete(false);
|
setBulkDelete(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Handle confirm items bulk delete.
|
// Handle confirm customers bulk delete.
|
||||||
const handleConfirmBulkDelete = useCallback(() => {
|
const handleConfirmBulkDelete = useCallback(() => {
|
||||||
requestDeleteBulkCustomers(bulkDelete)
|
requestDeleteBulkCustomers(bulkDelete)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@@ -163,7 +163,7 @@ function CustomersList({
|
|||||||
intent: Intent.SUCCESS,
|
intent: Intent.SUCCESS,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((errors) => {
|
.catch((error) => {
|
||||||
setBulkDelete(false);
|
setBulkDelete(false);
|
||||||
});
|
});
|
||||||
}, [requestDeleteBulkCustomers, bulkDelete, formatMessage]);
|
}, [requestDeleteBulkCustomers, bulkDelete, formatMessage]);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
|
|||||||
import { getCustomerById } from 'store/customers/customers.reducer';
|
import { getCustomerById } from 'store/customers/customers.reducer';
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => ({
|
const mapStateToProps = (state, props) => ({
|
||||||
customerDetail: getCustomerById(state, props.customerId),
|
customer: getCustomerById(state, props.customerId),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps);
|
export default connect(mapStateToProps);
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ import {
|
|||||||
submitCustomer,
|
submitCustomer,
|
||||||
editCustomer,
|
editCustomer,
|
||||||
deleteCustomer,
|
deleteCustomer,
|
||||||
|
deleteBulkCustomers
|
||||||
} from 'store/customers/customers.actions';
|
} from 'store/customers/customers.actions';
|
||||||
import t from 'store/types';
|
import t from 'store/types';
|
||||||
|
|
||||||
export const mapDispatchToProps = (dispatch) => ({
|
export const mapDispatchToProps = (dispatch) => ({
|
||||||
requestFetchCustomers: (query) => dispatch(fetchCustomers({ query })),
|
requestFetchCustomers: (query) => dispatch(fetchCustomers({ query })),
|
||||||
requestDeleteCustomer: (id) => dispatch(deleteCustomer({ id })),
|
requestDeleteCustomer: (id) => dispatch(deleteCustomer({ id })),
|
||||||
// requestDeleteBulkCustomers:(ids)=>dispatch(deleteBulkCustomers({ids})),
|
requestDeleteBulkCustomers:(ids)=>dispatch(deleteBulkCustomers({ids})),
|
||||||
requestSubmitCustomer: (form) => dispatch(submitCustomer({ form })),
|
requestSubmitCustomer: (form) => dispatch(submitCustomer({ form })),
|
||||||
requestEditCustomer: (id, form) => dispatch(editCustomer({ id, form })),
|
requestEditCustomer: (id, form) => dispatch(editCustomer({ id, form })),
|
||||||
|
|
||||||
|
|||||||
@@ -98,10 +98,9 @@ function ExpenseDataTable({
|
|||||||
const actionMenuList = useCallback(
|
const actionMenuList = useCallback(
|
||||||
(expense) => (
|
(expense) => (
|
||||||
<Menu>
|
<Menu>
|
||||||
<MenuItem
|
<MenuItem text={formatMessage({ id: 'view_details' })} />
|
||||||
text={formatMessage({ id: 'view_details' })} />
|
|
||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
<If condition={expenses.published}>
|
<If condition={!expense.published}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
text={formatMessage({ id: 'publish_expense' })}
|
text={formatMessage({ id: 'publish_expense' })}
|
||||||
onClick={handlePublishExpense(expense)}
|
onClick={handlePublishExpense(expense)}
|
||||||
@@ -119,23 +118,35 @@ function ExpenseDataTable({
|
|||||||
/>
|
/>
|
||||||
</Menu>
|
</Menu>
|
||||||
),
|
),
|
||||||
[handleEditExpense, handleDeleteExpense, handlePublishExpense, formatMessage],
|
[
|
||||||
|
handleEditExpense,
|
||||||
|
handleDeleteExpense,
|
||||||
|
handlePublishExpense,
|
||||||
|
formatMessage,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const onRowContextMenu = useCallback((cell) => {
|
const onRowContextMenu = useCallback(
|
||||||
return actionMenuList(cell.row.original);
|
(cell) => {
|
||||||
}, [actionMenuList]);
|
return actionMenuList(cell.row.original);
|
||||||
|
},
|
||||||
|
[actionMenuList],
|
||||||
|
);
|
||||||
|
|
||||||
const expenseAccountAccessor = (expense) => {
|
const expenseAccountAccessor = (_expense) => {
|
||||||
if (expense.categories.length === 1) {
|
if (_expense.categories.length === 1) {
|
||||||
return expense.categories[0].expense_account.name;
|
return _expense.categories[0].expense_account.name;
|
||||||
} else if (expense.categories.length > 1) {
|
} else if (_expense.categories.length > 1) {
|
||||||
const mutliCategories = expense.categories.map(category =>
|
const mutliCategories = _expense.categories.map((category) => (
|
||||||
(<div>- {category.expense_account.name} ${ category.amount }</div>)
|
<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(
|
const columns = useMemo(
|
||||||
() => [
|
() => [
|
||||||
@@ -179,7 +190,7 @@ function ExpenseDataTable({
|
|||||||
id: 'publish',
|
id: 'publish',
|
||||||
Header: formatMessage({ id: 'publish' }),
|
Header: formatMessage({ id: 'publish' }),
|
||||||
accessor: (r) => {
|
accessor: (r) => {
|
||||||
return !r.published ? (
|
return r.published ? (
|
||||||
<Tag minimal={true}>
|
<Tag minimal={true}>
|
||||||
<T id={'published'} />
|
<T id={'published'} />
|
||||||
</Tag>
|
</Tag>
|
||||||
|
|||||||
@@ -2,42 +2,56 @@ import React from 'react';
|
|||||||
import { Intent, Button } from '@blueprintjs/core';
|
import { Intent, Button } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
|
|
||||||
export default function ExpenseFooter({
|
export default function ExpenseFloatingFooter({
|
||||||
formik: { isSubmitting },
|
formik: { isSubmitting },
|
||||||
onSubmitClick,
|
onSubmitClick,
|
||||||
onCancelClick,
|
onCancelClick,
|
||||||
|
|
||||||
|
expense,
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={'form__floating-footer'}>
|
<div className={'form__floating-footer'}>
|
||||||
<Button
|
<Button
|
||||||
disabled={isSubmitting}
|
|
||||||
intent={Intent.PRIMARY}
|
intent={Intent.PRIMARY}
|
||||||
className={'ml1'}
|
disabled={isSubmitting}
|
||||||
name={'save'}
|
type="submit"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onSubmitClick({ publish: true, redirect: true });
|
onSubmitClick({ publish: true, redirect: true });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<T id={'save'} />
|
{expense && expense.id ? <T id={'edit'} /> : <T id={'save'} />}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
disabled={isSubmitting}
|
disabled={isSubmitting}
|
||||||
className={'button-secondary ml1'}
|
intent={Intent.PRIMARY}
|
||||||
|
className={'ml1'}
|
||||||
|
name={'save_and_new'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onSubmitClick({ publish: true, redirect: false });
|
onSubmitClick({ publish: true, redirect: false });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<T id={'save_as_draft'} />
|
<T id={'save_new'} />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
className={'button-secondary ml1'}
|
className={'ml1'}
|
||||||
|
disabled={isSubmitting}
|
||||||
onClick={() => {
|
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>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import moment from 'moment';
|
|||||||
import { Intent, FormGroup, TextArea } from '@blueprintjs/core';
|
import { Intent, FormGroup, TextArea } from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery, queryCache } from 'react-query';
|
||||||
import { Col, Row } from 'react-grid-system';
|
import { Col, Row } from 'react-grid-system';
|
||||||
|
|
||||||
import ExpenseFormHeader from './ExpenseFormHeader';
|
import ExpenseFormHeader from './ExpenseFormHeader';
|
||||||
@@ -45,12 +45,14 @@ function ExpenseForm({
|
|||||||
|
|
||||||
//#withExpenseDetail
|
//#withExpenseDetail
|
||||||
// @todo expenseDetail to expense
|
// @todo expenseDetail to expense
|
||||||
expenseDetail,
|
expense,
|
||||||
|
|
||||||
// #own Props
|
// #own Props
|
||||||
expenseId,
|
expenseId,
|
||||||
onFormSubmit,
|
onFormSubmit,
|
||||||
onCancelForm,
|
onCancelForm,
|
||||||
|
onClickAddNewRow,
|
||||||
|
onClickRemoveRow,
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const [payload, setPayload] = useState({});
|
const [payload, setPayload] = useState({});
|
||||||
@@ -75,14 +77,14 @@ function ExpenseForm({
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (expenseDetail && expenseDetail.id) {
|
if (expense && expense.id) {
|
||||||
changePageTitle(formatMessage({ id: 'edit_expense' }));
|
changePageTitle(formatMessage({ id: 'edit_expense' }));
|
||||||
// changePageSubtitle(`No. ${expenseDetail.payment_account_id}`);
|
// changePageSubtitle(`No. ${expenseDetail.payment_account_id}`);
|
||||||
} else {
|
} else {
|
||||||
changePageTitle(formatMessage({ id: 'new_expense' }));
|
changePageTitle(formatMessage({ id: 'new_expense' }));
|
||||||
}
|
}
|
||||||
// @todo not functions just states.
|
// @todo not functions just states.
|
||||||
}, [changePageTitle, changePageSubtitle, expenseDetail, formatMessage]);
|
}, [changePageTitle, changePageSubtitle, expense, formatMessage]);
|
||||||
|
|
||||||
const validationSchema = Yup.object().shape({
|
const validationSchema = Yup.object().shape({
|
||||||
beneficiary: Yup.string()
|
beneficiary: Yup.string()
|
||||||
@@ -106,7 +108,13 @@ function ExpenseForm({
|
|||||||
index: Yup.number().nullable(),
|
index: Yup.number().nullable(),
|
||||||
amount: Yup.number().nullable(),
|
amount: Yup.number().nullable(),
|
||||||
// @todo expense_account_id is required.
|
// @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(),
|
description: Yup.string().nullable(),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -116,6 +124,7 @@ function ExpenseForm({
|
|||||||
(payload) => {
|
(payload) => {
|
||||||
onFormSubmit && onFormSubmit(payload);
|
onFormSubmit && onFormSubmit(payload);
|
||||||
},
|
},
|
||||||
|
|
||||||
[onFormSubmit],
|
[onFormSubmit],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -147,32 +156,43 @@ function ExpenseForm({
|
|||||||
[defaultCategory],
|
[defaultCategory],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const orderingCategoriesIndex = (categories) => {
|
||||||
|
return categories.map((category, index) => ({
|
||||||
|
...category,
|
||||||
|
index: index + 1,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
const initialValues = useMemo(
|
const initialValues = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...(expenseDetail
|
...(expense
|
||||||
? {
|
? {
|
||||||
...pick(expenseDetail, Object.keys(defaultInitialValues)),
|
...pick(expense, Object.keys(defaultInitialValues)),
|
||||||
categories: expenseDetail.categories.map((category) => ({
|
categories: expense.categories.map((category) => ({
|
||||||
...pick(category, Object.keys(defaultCategory)),
|
...pick(category, Object.keys(defaultCategory)),
|
||||||
}),
|
})),
|
||||||
),
|
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
...defaultInitialValues,
|
...defaultInitialValues,
|
||||||
|
categories: orderingCategoriesIndex(
|
||||||
|
defaultInitialValues.categories,
|
||||||
|
),
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
[expenseDetail, defaultInitialValues, defaultCategory],
|
[expense, defaultInitialValues, defaultCategory],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log(initialValues.categories, 'ERR');
|
||||||
|
|
||||||
const initialAttachmentFiles = useMemo(() => {
|
const initialAttachmentFiles = useMemo(() => {
|
||||||
return expenseDetail && expenseDetail.media
|
return expense && expense.media
|
||||||
? expenseDetail.media.map((attach) => ({
|
? expense.media.map((attach) => ({
|
||||||
preview: attach.attachment_file,
|
preview: attach.attachment_file,
|
||||||
uploaded: true,
|
uploaded: true,
|
||||||
metadata: { ...attach },
|
metadata: { ...attach },
|
||||||
}))
|
}))
|
||||||
: [];
|
: [];
|
||||||
}, [expenseDetail]);
|
}, [expense]);
|
||||||
|
|
||||||
const formik = useFormik({
|
const formik = useFormik({
|
||||||
enableReinitialize: true,
|
enableReinitialize: true,
|
||||||
@@ -182,11 +202,12 @@ function ExpenseForm({
|
|||||||
},
|
},
|
||||||
onSubmit: async (values, { setSubmitting, setErrors, resetForm }) => {
|
onSubmit: async (values, { setSubmitting, setErrors, resetForm }) => {
|
||||||
const categories = values.categories.filter(
|
const categories = values.categories.filter(
|
||||||
(category) => category.amount || category.index,
|
(category) =>
|
||||||
|
category.amount && category.index && category.expense_account_id,
|
||||||
);
|
);
|
||||||
const form = {
|
const form = {
|
||||||
...values,
|
...values,
|
||||||
published: payload.publish,
|
publish: payload.publish,
|
||||||
categories,
|
categories,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -194,8 +215,8 @@ function ExpenseForm({
|
|||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
const requestForm = { ...form, media_ids: mdeiaIds };
|
const requestForm = { ...form, media_ids: mdeiaIds };
|
||||||
|
|
||||||
if (expenseDetail && expenseDetail.id) {
|
if (expense && expense.id) {
|
||||||
requestEditExpense(expenseDetail.id, requestForm)
|
requestEditExpense(expense.id, requestForm)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: formatMessage(
|
message: formatMessage(
|
||||||
@@ -208,10 +229,8 @@ function ExpenseForm({
|
|||||||
saveInvokeSubmit({ action: 'update', ...payload });
|
saveInvokeSubmit({ action: 'update', ...payload });
|
||||||
clearSavedMediaIds([]);
|
clearSavedMediaIds([]);
|
||||||
resetForm();
|
resetForm();
|
||||||
resolve(response);
|
|
||||||
})
|
})
|
||||||
.catch((errors) => {
|
.catch((errors) => {
|
||||||
// @todo handle errors.
|
|
||||||
if (errors.find((e) => e.type === 'TOTAL.AMOUNT.EQUALS.ZERO')) {
|
if (errors.find((e) => e.type === 'TOTAL.AMOUNT.EQUALS.ZERO')) {
|
||||||
}
|
}
|
||||||
setErrors(
|
setErrors(
|
||||||
@@ -235,10 +254,11 @@ function ExpenseForm({
|
|||||||
intent: Intent.SUCCESS,
|
intent: Intent.SUCCESS,
|
||||||
});
|
});
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
formik.resetForm();
|
||||||
saveInvokeSubmit({ action: 'new', ...payload });
|
saveInvokeSubmit({ action: 'new', ...payload });
|
||||||
clearSavedMediaIds();
|
clearSavedMediaIds();
|
||||||
resetForm();
|
|
||||||
resolve(response);
|
// resolve(response);
|
||||||
})
|
})
|
||||||
.catch((errors) => {
|
.catch((errors) => {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
@@ -263,11 +283,14 @@ function ExpenseForm({
|
|||||||
const handleSubmitClick = useCallback(
|
const handleSubmitClick = useCallback(
|
||||||
(payload) => {
|
(payload) => {
|
||||||
setPayload(payload);
|
setPayload(payload);
|
||||||
|
// formik.resetForm();
|
||||||
formik.handleSubmit();
|
formik.handleSubmit();
|
||||||
},
|
},
|
||||||
[setPayload, formik],
|
[setPayload, formik],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
console.log(formik.values, 'VALUES');
|
||||||
|
|
||||||
const handleCancelClick = useCallback(
|
const handleCancelClick = useCallback(
|
||||||
(payload) => {
|
(payload) => {
|
||||||
onCancelForm && onCancelForm(payload);
|
onCancelForm && onCancelForm(payload);
|
||||||
@@ -285,7 +308,7 @@ function ExpenseForm({
|
|||||||
},
|
},
|
||||||
[setDeletedFiles, deletedFiles],
|
[setDeletedFiles, deletedFiles],
|
||||||
);
|
);
|
||||||
// @todo @mohamed
|
|
||||||
const fetchHook = useQuery('expense-form', () => requestFetchExpensesTable());
|
const fetchHook = useQuery('expense-form', () => requestFetchExpensesTable());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -318,13 +341,15 @@ function ExpenseForm({
|
|||||||
hint={'Attachments: Maxiumum size: 20MB'}
|
hint={'Attachments: Maxiumum size: 20MB'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ExpenseFloatingFooter
|
|
||||||
formik={formik}
|
|
||||||
onSubmitClick={handleSubmitClick}
|
|
||||||
onCancelClick={handleCancelClick}
|
|
||||||
/>
|
|
||||||
</form>
|
</form>
|
||||||
|
<ExpenseFloatingFooter
|
||||||
|
formik={formik}
|
||||||
|
onSubmitClick={handleSubmitClick}
|
||||||
|
expense={expense}
|
||||||
|
onCancelClick={handleCancelClick}
|
||||||
|
// onClickAddNewRow={}
|
||||||
|
// onClickRemoveRow={}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ function ExpenseFormHeader({
|
|||||||
formik: { errors, touched, setFieldValue, getFieldProps, values },
|
formik: { errors, touched, setFieldValue, getFieldProps, values },
|
||||||
currenciesList,
|
currenciesList,
|
||||||
accounts,
|
accounts,
|
||||||
|
accountsTypes,
|
||||||
}) {
|
}) {
|
||||||
const [selectedItems, setSelectedItems] = useState({});
|
const [selectedItems, setSelectedItems] = useState({});
|
||||||
|
|
||||||
@@ -103,7 +104,6 @@ function ExpenseFormHeader({
|
|||||||
const onItemsSelect = useCallback(
|
const onItemsSelect = useCallback(
|
||||||
(filedName) => {
|
(filedName) => {
|
||||||
return (filed) => {
|
return (filed) => {
|
||||||
// @todo @mohamed
|
|
||||||
setSelectedItems({
|
setSelectedItems({
|
||||||
...selectedItems,
|
...selectedItems,
|
||||||
[filedName]: filed,
|
[filedName]: filed,
|
||||||
@@ -234,10 +234,7 @@ function ExpenseFormHeader({
|
|||||||
<Col width={200}>
|
<Col width={200}>
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'ref_no'} />}
|
label={<T id={'ref_no'} />}
|
||||||
className={classNames(
|
className={classNames('form-group--ref_no', Classes.FILL)}
|
||||||
'form-group--ref_no',
|
|
||||||
Classes.FILL,
|
|
||||||
)}
|
|
||||||
intent={
|
intent={
|
||||||
errors.reference_no && touched.reference_no && Intent.DANGER
|
errors.reference_no && touched.reference_no && Intent.DANGER
|
||||||
}
|
}
|
||||||
@@ -260,8 +257,9 @@ function ExpenseFormHeader({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
withAccounts(({ accounts }) => ({
|
withAccounts(({ accounts, accountsTypes }) => ({
|
||||||
accounts,
|
accounts,
|
||||||
|
accountsTypes,
|
||||||
})),
|
})),
|
||||||
withCurrencies(({ currenciesList }) => ({
|
withCurrencies(({ currenciesList }) => ({
|
||||||
currenciesList,
|
currenciesList,
|
||||||
|
|||||||
@@ -41,15 +41,20 @@ function ExpenseTable({
|
|||||||
(rowIndex, columnId, value) => {
|
(rowIndex, columnId, value) => {
|
||||||
const newRows = rows.map((row, index) => {
|
const newRows = rows.map((row, index) => {
|
||||||
if (index === rowIndex) {
|
if (index === rowIndex) {
|
||||||
return { ...rows[rowIndex], [columnId]: value };
|
return {
|
||||||
|
...rows[rowIndex],
|
||||||
|
[columnId]: value,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
return { ...row };
|
return { ...row };
|
||||||
});
|
});
|
||||||
setRow(newRows);
|
setRow(newRows);
|
||||||
setFieldValue(
|
setFieldValue(
|
||||||
'categories',
|
'categories',
|
||||||
newRows.map((row) => ({
|
|
||||||
|
newRows.map((row, index) => ({
|
||||||
...omit(row, ['rowType']),
|
...omit(row, ['rowType']),
|
||||||
|
index: index + 1,
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -60,15 +65,28 @@ function ExpenseTable({
|
|||||||
const handleRemoveRow = useCallback(
|
const handleRemoveRow = useCallback(
|
||||||
(rowIndex) => {
|
(rowIndex) => {
|
||||||
const removeIndex = parseInt(rowIndex, 10);
|
const removeIndex = parseInt(rowIndex, 10);
|
||||||
const newRows = rows.filter((row, index) => index !== removeIndex);
|
|
||||||
|
|
||||||
|
const newRows = rows.filter((row, index) => index !== removeIndex);
|
||||||
setRow([...newRows]);
|
setRow([...newRows]);
|
||||||
|
|
||||||
setFieldValue(
|
setFieldValue(
|
||||||
'categories',
|
'categories',
|
||||||
|
|
||||||
newRows
|
newRows
|
||||||
.filter((row) => row.rowType === 'editor')
|
.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);
|
onClickRemoveRow && onClickRemoveRow(removeIndex);
|
||||||
},
|
},
|
||||||
[rows, setFieldValue, onClickRemoveRow],
|
[rows, setFieldValue, onClickRemoveRow],
|
||||||
@@ -82,7 +100,7 @@ function ExpenseTable({
|
|||||||
data,
|
data,
|
||||||
payload,
|
payload,
|
||||||
}) => {
|
}) => {
|
||||||
if (data.length <= index + 2) {
|
if (data.length <= index + 1) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
const onClickRemoveRole = () => {
|
const onClickRemoveRole = () => {
|
||||||
@@ -104,7 +122,7 @@ function ExpenseTable({
|
|||||||
|
|
||||||
// Total text cell renderer.
|
// Total text cell renderer.
|
||||||
const TotalExpenseCellRenderer = (chainedComponent) => (props) => {
|
const TotalExpenseCellRenderer = (chainedComponent) => (props) => {
|
||||||
if (props.data.length === props.row.index + 2) {
|
if (props.data.length <= props.row.index + 1) {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{formatMessage({ id: 'total_currency' }, { currency: 'USD' })}
|
{formatMessage({ id: 'total_currency' }, { currency: 'USD' })}
|
||||||
@@ -115,14 +133,14 @@ function ExpenseTable({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const NoteCellRenderer = (chainedComponent) => (props) => {
|
const NoteCellRenderer = (chainedComponent) => (props) => {
|
||||||
if (props.data.length === props.row.index + 2) {
|
if (props.data.length === props.row.index + 1) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
return chainedComponent(props);
|
return chainedComponent(props);
|
||||||
};
|
};
|
||||||
|
|
||||||
const TotalAmountCellRenderer = (chainedComponent, type) => (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 total = props.data.reduce((total, entry) => {
|
||||||
const amount = parseInt(entry[type], 10);
|
const amount = parseInt(entry[type], 10);
|
||||||
const computed = amount ? total + amount : total;
|
const computed = amount ? total + amount : total;
|
||||||
@@ -147,7 +165,12 @@ function ExpenseTable({
|
|||||||
disableSortBy: true,
|
disableSortBy: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: (<>{ formatMessage({ id: 'expense_category' }) }<Hint /></>),
|
Header: (
|
||||||
|
<>
|
||||||
|
{formatMessage({ id: 'expense_category' })}
|
||||||
|
<Hint />
|
||||||
|
</>
|
||||||
|
),
|
||||||
id: 'expense_account_id',
|
id: 'expense_account_id',
|
||||||
accessor: 'expense_account_id',
|
accessor: 'expense_account_id',
|
||||||
Cell: TotalExpenseCellRenderer(AccountsListFieldCell),
|
Cell: TotalExpenseCellRenderer(AccountsListFieldCell),
|
||||||
@@ -193,7 +216,7 @@ function ExpenseTable({
|
|||||||
|
|
||||||
const rowClassNames = useCallback(
|
const rowClassNames = useCallback(
|
||||||
(row) => ({
|
(row) => ({
|
||||||
'row--total': rows.length === row.index + 2,
|
'row--total': rows.length === row.index + 1,
|
||||||
}),
|
}),
|
||||||
[rows],
|
[rows],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { compose } from 'utils';
|
|||||||
function Expenses({
|
function Expenses({
|
||||||
// #withwithAccountsActions
|
// #withwithAccountsActions
|
||||||
requestFetchAccounts,
|
requestFetchAccounts,
|
||||||
|
requestFetchAccountTypes,
|
||||||
|
|
||||||
// #withExpensesActions
|
// #withExpensesActions
|
||||||
requestFetchExpense,
|
requestFetchExpense,
|
||||||
@@ -24,18 +25,15 @@ function Expenses({
|
|||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
|
||||||
// @todo
|
const fetchAccounts = useQuery('accounts-list', (key) =>
|
||||||
const fetchAccounts = useQuery('accounts-expense-list', (key) =>
|
|
||||||
requestFetchAccounts(),
|
requestFetchAccounts(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// @todo
|
|
||||||
const fetchExpense = useQuery(id && ['expense', id], (key, expense_Id) =>
|
const fetchExpense = useQuery(id && ['expense', id], (key, expense_Id) =>
|
||||||
requestFetchExpense(expense_Id),
|
requestFetchExpense(expense_Id),
|
||||||
);
|
);
|
||||||
|
|
||||||
// @todo
|
const fetchCurrencies = useQuery('currencies', () =>
|
||||||
const fetchCurrencies = useQuery('currencies-expense-list', () =>
|
|
||||||
requestFetchCurrencies(),
|
requestFetchCurrencies(),
|
||||||
);
|
);
|
||||||
const handleFormSubmit = useCallback(
|
const handleFormSubmit = useCallback(
|
||||||
@@ -46,7 +44,7 @@ function Expenses({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleCancel = useCallback(() => {
|
const handleCancel = useCallback(() => {
|
||||||
history.push('/expenses-list');
|
history.goBack();
|
||||||
}, [history]);
|
}, [history]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -56,6 +54,7 @@ function Expenses({
|
|||||||
fetchAccounts.isFetching ||
|
fetchAccounts.isFetching ||
|
||||||
fetchCurrencies.isFetching
|
fetchCurrencies.isFetching
|
||||||
}
|
}
|
||||||
|
name={'expense-form'}
|
||||||
>
|
>
|
||||||
<ExpenseForm
|
<ExpenseForm
|
||||||
onFormSubmit={handleFormSubmit}
|
onFormSubmit={handleFormSubmit}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ function ExpensesList({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const fetchExpenses = useQuery(
|
const fetchExpenses = useQuery(
|
||||||
['expenses-table', expensesTableQuery],
|
['expense-form', expensesTableQuery],
|
||||||
() => requestFetchExpensesTable(),
|
() => requestFetchExpensesTable(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -210,7 +210,7 @@ function ExpensesList({
|
|||||||
</p>
|
</p>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
{/* <Alert
|
<Alert
|
||||||
cancelButtonText={<T id={'cancel'} />}
|
cancelButtonText={<T id={'cancel'} />}
|
||||||
confirmButtonText={
|
confirmButtonText={
|
||||||
<T id={'delete_count'} values={{ count: selectedRowsCount }} />
|
<T id={'delete_count'} values={{ count: selectedRowsCount }} />
|
||||||
@@ -223,10 +223,10 @@ function ExpensesList({
|
|||||||
>
|
>
|
||||||
<p>
|
<p>
|
||||||
<T
|
<T
|
||||||
id={'once_delete_these_journalss_you_will_not_able_restore_them'}
|
id={'once_delete_these_expenses_you_will_not_able_restore_them'}
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
</Alert> */}
|
</Alert>
|
||||||
</DashboardPageContent>
|
</DashboardPageContent>
|
||||||
</DashboardInsider>
|
</DashboardInsider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
|
|||||||
import { getExpenseById } from 'store/expenses/expenses.reducer';
|
import { getExpenseById } from 'store/expenses/expenses.reducer';
|
||||||
|
|
||||||
const mapStateToProps = (state, props) => ({
|
const mapStateToProps = (state, props) => ({
|
||||||
expenseDetail: getExpenseById(state, props.expenseId),
|
expense: getExpenseById(state, props.expenseId),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps);
|
export default connect(mapStateToProps);
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ const ItemCategoryList = ({
|
|||||||
}, [id, changePageTitle, formatMessage]);
|
}, [id, changePageTitle, formatMessage]);
|
||||||
|
|
||||||
const fetchCategories = useQuery(
|
const fetchCategories = useQuery(
|
||||||
['items-categories-table', filter],
|
['items-categories-list', filter],
|
||||||
(key, query) => requestFetchItemCategories(query),
|
(key, query) => requestFetchItemCategories(query),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -183,7 +183,9 @@ const ItemCategoryList = ({
|
|||||||
>
|
>
|
||||||
<p>
|
<p>
|
||||||
<FormattedHTMLMessage
|
<FormattedHTMLMessage
|
||||||
id={'once_delete_these_item_categories_you_will_not_able_restore_them'}
|
id={
|
||||||
|
'once_delete_these_item_categories_you_will_not_able_restore_them'
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|||||||
@@ -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 * as Yup from 'yup';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import {
|
import {
|
||||||
@@ -26,16 +26,17 @@ import Dragzone from 'components/Dragzone';
|
|||||||
import { ListSelect } from 'components';
|
import { ListSelect } from 'components';
|
||||||
|
|
||||||
import withItemsActions from 'containers/Items/withItemsActions';
|
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 withAccounts from 'containers/Accounts/withAccounts';
|
||||||
import withMediaActions from 'containers/Media/withMediaActions';
|
import withMediaActions from 'containers/Media/withMediaActions';
|
||||||
import useMedia from 'hooks/useMedia';
|
import useMedia from 'hooks/useMedia';
|
||||||
import withItemDetail from 'containers/Items/withItemDetail'
|
import withItemDetail from 'containers/Items/withItemDetail';
|
||||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
import withAccountDetail from 'containers/Accounts/withAccountDetail';
|
import withAccountDetail from 'containers/Accounts/withAccountDetail';
|
||||||
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
import { resolve } from 'p-progress';
|
||||||
|
import ItemFloatingFooter from './ItemsFooter';
|
||||||
|
|
||||||
const ItemForm = ({
|
const ItemForm = ({
|
||||||
// #withItemActions
|
// #withItemActions
|
||||||
@@ -72,22 +73,34 @@ const ItemForm = ({
|
|||||||
deleteCallback: requestDeleteMedia,
|
deleteCallback: requestDeleteMedia,
|
||||||
});
|
});
|
||||||
|
|
||||||
const ItemTypeDisplay = useMemo(() => [
|
const ItemTypeDisplay = useMemo(
|
||||||
{ value: null, label: formatMessage({id:'select_item_type'}) },
|
() => [
|
||||||
{ value: 'service', label: formatMessage({id:'service'}) },
|
{ value: null, label: formatMessage({ id: 'select_item_type' }) },
|
||||||
{ value: 'inventory', label: formatMessage({id:'inventory'}) },
|
{ value: 'service', label: formatMessage({ id: 'service' }) },
|
||||||
{ value: 'non-inventory', label: formatMessage({id:'non_inventory'}) },
|
{ value: 'inventory', label: formatMessage({ id: 'inventory' }) },
|
||||||
], [formatMessage]);
|
{ value: 'non-inventory', label: formatMessage({ id: 'non_inventory' }) },
|
||||||
|
],
|
||||||
|
[formatMessage],
|
||||||
|
);
|
||||||
|
|
||||||
const validationSchema = Yup.object().shape({
|
const validationSchema = Yup.object().shape({
|
||||||
active: Yup.boolean(),
|
active: Yup.boolean(),
|
||||||
name: Yup.string().required().label(formatMessage({id:'item_name_'})),
|
name: Yup.string()
|
||||||
type: Yup.string().trim().required().label(formatMessage({id:'item_type_'})),
|
.required()
|
||||||
|
.label(formatMessage({ id: 'item_name_' })),
|
||||||
|
type: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'item_type_' })),
|
||||||
sku: Yup.string().trim(),
|
sku: Yup.string().trim(),
|
||||||
cost_price: Yup.number(),
|
cost_price: Yup.number(),
|
||||||
sell_price: Yup.number(),
|
sell_price: Yup.number(),
|
||||||
cost_account_id: Yup.number().required().label(formatMessage({id:'cost_account_id'})),
|
cost_account_id: Yup.number()
|
||||||
sell_account_id: Yup.number().required().label(formatMessage({id:'sell_account_id'})),
|
.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', {
|
inventory_account_id: Yup.number().when('type', {
|
||||||
is: (value) => value === 'inventory',
|
is: (value) => value === 'inventory',
|
||||||
then: Yup.number().required(),
|
then: Yup.number().required(),
|
||||||
@@ -111,26 +124,33 @@ const ItemForm = ({
|
|||||||
category_id: null,
|
category_id: null,
|
||||||
note: '',
|
note: '',
|
||||||
}),
|
}),
|
||||||
[]
|
[],
|
||||||
|
);
|
||||||
|
const initialValues = useMemo(
|
||||||
|
() => ({
|
||||||
|
...(itemDetail
|
||||||
|
? {
|
||||||
|
...pick(itemDetail, Object.keys(defaultInitialValues)),
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
...defaultInitialValues,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
[itemDetail, defaultInitialValues],
|
||||||
);
|
);
|
||||||
const initialValues = useMemo(() => ({
|
|
||||||
...(itemDetail) ? {
|
|
||||||
...pick(itemDetail, Object.keys(defaultInitialValues)),
|
|
||||||
|
|
||||||
} : {
|
const saveInvokeSubmit = useCallback(
|
||||||
...defaultInitialValues,
|
(payload) => {
|
||||||
}
|
onFormSubmit && onFormSubmit(payload);
|
||||||
}), [itemDetail, defaultInitialValues]);
|
},
|
||||||
|
[onFormSubmit],
|
||||||
const saveInvokeSubmit = useCallback((payload) => {
|
);
|
||||||
onFormSubmit && onFormSubmit(payload)
|
|
||||||
}, [onFormSubmit]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
itemDetail && itemDetail.id ?
|
itemDetail && itemDetail.id
|
||||||
changePageTitle(formatMessage({id:'edit_item_details'})) :
|
? changePageTitle(formatMessage({ id: 'edit_item_details' }))
|
||||||
changePageTitle(formatMessage({id:'new_item'}));
|
: changePageTitle(formatMessage({ id: 'new_item' }));
|
||||||
}, [changePageTitle,itemDetail,formatMessage]);
|
}, [changePageTitle, itemDetail, formatMessage]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
getFieldProps,
|
getFieldProps,
|
||||||
@@ -140,65 +160,95 @@ const ItemForm = ({
|
|||||||
errors,
|
errors,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
isSubmitting,
|
isSubmitting,
|
||||||
|
resetForm
|
||||||
} = useFormik({
|
} = useFormik({
|
||||||
enableReinitialize: true,
|
enableReinitialize: true,
|
||||||
validationSchema: validationSchema,
|
validationSchema: validationSchema,
|
||||||
initialValues: {
|
initialValues: {
|
||||||
...initialValues,
|
...initialValues,
|
||||||
},
|
},
|
||||||
onSubmit: (values, { setSubmitting,resetForm,setErrors }) => {
|
onSubmit: (values, { setSubmitting, resetForm, setErrors }) => {
|
||||||
|
|
||||||
const saveItem = (mediaIds) => {
|
const saveItem = (mediaIds) => {
|
||||||
const formValues = { ...values, media_ids: mediaIds };
|
const formValues = {
|
||||||
if(itemDetail && itemDetail.id ){
|
...values,
|
||||||
|
status: payload.publish,
|
||||||
requestEditItem(itemDetail.id,formValues)
|
media_ids: mediaIds,
|
||||||
.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');
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
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(
|
Promise.all([saveMedia(), deleteMedia()]).then(
|
||||||
([savedMediaResponses]) => {
|
([savedMediaResponses]) => {
|
||||||
const mediaIds = savedMediaResponses.map((res) => res.data.media.id);
|
const mediaIds = savedMediaResponses.map((res) => res.data.media.id);
|
||||||
return saveItem(mediaIds);
|
return saveItem(mediaIds);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleSubmitClick = useCallback(
|
||||||
|
(payload) => {
|
||||||
|
setPayload(payload);
|
||||||
|
handleSubmit();
|
||||||
|
},
|
||||||
|
[setPayload, handleSubmit],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleCancelClick = useCallback(
|
||||||
|
(payload) => {
|
||||||
|
onCancelForm && onCancelForm(payload);
|
||||||
|
},
|
||||||
|
[onCancelForm],
|
||||||
|
);
|
||||||
|
|
||||||
const accountItem = useCallback(
|
const accountItem = useCallback(
|
||||||
(item, { handleClick }) => (
|
(item, { handleClick }) => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
@@ -208,10 +258,9 @@ const ItemForm = ({
|
|||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
[]
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
// Filter Account Items
|
// Filter Account Items
|
||||||
const filterAccounts = (query, account, _index, exactMatch) => {
|
const filterAccounts = (query, account, _index, exactMatch) => {
|
||||||
const normalizedTitle = account.name.toLowerCase();
|
const normalizedTitle = account.name.toLowerCase();
|
||||||
@@ -223,38 +272,38 @@ const ItemForm = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onItemAccountSelect = useCallback((filedName) => {
|
const onItemAccountSelect = useCallback(
|
||||||
return (account) => {
|
(filedName) => {
|
||||||
setFieldValue(filedName, account.id);
|
return (account) => {
|
||||||
};
|
setFieldValue(filedName, account.id);
|
||||||
}, [setFieldValue]);
|
};
|
||||||
|
},
|
||||||
|
[setFieldValue],
|
||||||
|
);
|
||||||
|
|
||||||
const categoryItem = useCallback(
|
const categoryItem = useCallback(
|
||||||
(item, { handleClick }) => (
|
(item, { handleClick }) => (
|
||||||
<MenuItem text={item.name} onClick={handleClick} />
|
<MenuItem text={item.name} onClick={handleClick} />
|
||||||
),
|
),
|
||||||
[]
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
const requiredSpan = useMemo(() => <span class='required'>*</span>, []);
|
const requiredSpan = useMemo(() => <span class="required">*</span>, []);
|
||||||
const infoIcon = useMemo(() => <Icon icon='info-circle' iconSize={12} />, []);
|
const infoIcon = useMemo(() => <Icon icon="info-circle" iconSize={12} />, []);
|
||||||
|
|
||||||
const handleMoneyInputChange = (fieldKey) => (e, value) => {
|
const handleMoneyInputChange = (fieldKey) => (e, value) => {
|
||||||
setFieldValue(fieldKey, value);
|
setFieldValue(fieldKey, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const initialAttachmentFiles = useMemo(() => {
|
||||||
const initialAttachmentFiles =useMemo(()=>{
|
|
||||||
return itemDetail && itemDetail.media
|
return itemDetail && itemDetail.media
|
||||||
? itemDetail.media.map((attach)=>({
|
? itemDetail.media.map((attach) => ({
|
||||||
|
preview: attach.attachment_file,
|
||||||
preview:attach.attachment_file,
|
upload: true,
|
||||||
upload:true,
|
metadata: { ...attach },
|
||||||
metadata:{...attach}
|
}))
|
||||||
|
: [];
|
||||||
})):[];
|
}, [itemDetail]);
|
||||||
|
|
||||||
},[itemDetail])
|
|
||||||
const handleDropFiles = useCallback((_files) => {
|
const handleDropFiles = useCallback((_files) => {
|
||||||
setFiles(_files.filter((file) => file.uploaded === false));
|
setFiles(_files.filter((file) => file.uploaded === false));
|
||||||
}, []);
|
}, []);
|
||||||
@@ -267,20 +316,15 @@ const ItemForm = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[setDeletedFiles, deletedFiles,]
|
[setDeletedFiles, deletedFiles],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCancelClickBtn = () => {
|
|
||||||
history.goBack();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class='item-form'>
|
<div class="item-form">
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<div class='item-form__primary-section'>
|
<div class="item-form__primary-section">
|
||||||
<Row>
|
<Row>
|
||||||
<Col xs={7}>
|
<Col xs={7}>
|
||||||
|
|
||||||
{/* Item type */}
|
{/* Item type */}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
medium={true}
|
medium={true}
|
||||||
@@ -289,7 +333,7 @@ const ItemForm = ({
|
|||||||
className={'form-group--item-type'}
|
className={'form-group--item-type'}
|
||||||
intent={errors.type && touched.type && Intent.DANGER}
|
intent={errors.type && touched.type && Intent.DANGER}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage {...{ errors, touched }} name='type' />
|
<ErrorMessage {...{ errors, touched }} name="type" />
|
||||||
}
|
}
|
||||||
inline={true}
|
inline={true}
|
||||||
>
|
>
|
||||||
@@ -307,7 +351,7 @@ const ItemForm = ({
|
|||||||
className={'form-group--item-name'}
|
className={'form-group--item-name'}
|
||||||
intent={errors.name && touched.name && Intent.DANGER}
|
intent={errors.name && touched.name && Intent.DANGER}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage {...{ errors, touched }} name='name' />
|
<ErrorMessage {...{ errors, touched }} name="name" />
|
||||||
}
|
}
|
||||||
inline={true}
|
inline={true}
|
||||||
>
|
>
|
||||||
@@ -324,7 +368,9 @@ const ItemForm = ({
|
|||||||
labelInfo={infoIcon}
|
labelInfo={infoIcon}
|
||||||
className={'form-group--item-sku'}
|
className={'form-group--item-sku'}
|
||||||
intent={errors.sku && touched.sku && Intent.DANGER}
|
intent={errors.sku && touched.sku && Intent.DANGER}
|
||||||
helperText={<ErrorMessage {...{ errors, touched }} name='sku' />}
|
helperText={
|
||||||
|
<ErrorMessage {...{ errors, touched }} name="sku" />
|
||||||
|
}
|
||||||
inline={true}
|
inline={true}
|
||||||
>
|
>
|
||||||
<InputGroup
|
<InputGroup
|
||||||
@@ -343,12 +389,12 @@ const ItemForm = ({
|
|||||||
errors.category_id && touched.category_id && Intent.DANGER
|
errors.category_id && touched.category_id && Intent.DANGER
|
||||||
}
|
}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage {...{ errors, touched }} name='category' />
|
<ErrorMessage {...{ errors, touched }} name="category" />
|
||||||
}
|
}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'form-group--select-list',
|
'form-group--select-list',
|
||||||
'form-group--category',
|
'form-group--category',
|
||||||
Classes.FILL
|
Classes.FILL,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ListSelect
|
<ListSelect
|
||||||
@@ -357,10 +403,8 @@ const ItemForm = ({
|
|||||||
itemPredicate={filterAccounts}
|
itemPredicate={filterAccounts}
|
||||||
popoverProps={{ minimal: true }}
|
popoverProps={{ minimal: true }}
|
||||||
onItemSelect={onItemAccountSelect('category_id')}
|
onItemSelect={onItemAccountSelect('category_id')}
|
||||||
|
|
||||||
selectedItem={values.category_id}
|
selectedItem={values.category_id}
|
||||||
selectedItemProp={'id'}
|
selectedItemProp={'id'}
|
||||||
|
|
||||||
defaultText={<T id={'select_category'} />}
|
defaultText={<T id={'select_category'} />}
|
||||||
labelProp={'name'}
|
labelProp={'name'}
|
||||||
/>
|
/>
|
||||||
@@ -374,7 +418,7 @@ const ItemForm = ({
|
|||||||
>
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
inline={true}
|
inline={true}
|
||||||
label={<T id={'active'}/>}
|
label={<T id={'active'} />}
|
||||||
defaultChecked={values.active}
|
defaultChecked={values.active}
|
||||||
{...getFieldProps('active')}
|
{...getFieldProps('active')}
|
||||||
/>
|
/>
|
||||||
@@ -395,14 +439,18 @@ const ItemForm = ({
|
|||||||
|
|
||||||
<Row gutterWidth={16} className={'item-form__accounts-section'}>
|
<Row gutterWidth={16} className={'item-form__accounts-section'}>
|
||||||
<Col width={404}>
|
<Col width={404}>
|
||||||
<h4><T id={'purchase_information'}/></h4>
|
<h4>
|
||||||
|
<T id={'purchase_information'} />
|
||||||
|
</h4>
|
||||||
|
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'selling_price'}/>}
|
label={<T id={'selling_price'} />}
|
||||||
className={'form-group--item-selling-price'}
|
className={'form-group--item-selling-price'}
|
||||||
intent={errors.selling_price && touched.selling_price && Intent.DANGER}
|
intent={
|
||||||
|
errors.selling_price && touched.selling_price && Intent.DANGER
|
||||||
|
}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage {...{ errors, touched }} name='selling_price' />
|
<ErrorMessage {...{ errors, touched }} name="selling_price" />
|
||||||
}
|
}
|
||||||
inline={true}
|
inline={true}
|
||||||
>
|
>
|
||||||
@@ -431,12 +479,12 @@ const ItemForm = ({
|
|||||||
Intent.DANGER
|
Intent.DANGER
|
||||||
}
|
}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage {...{ errors, touched }} name='sell_account_id' />
|
<ErrorMessage {...{ errors, touched }} name="sell_account_id" />
|
||||||
}
|
}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'form-group--sell-account',
|
'form-group--sell-account',
|
||||||
'form-group--select-list',
|
'form-group--select-list',
|
||||||
Classes.FILL
|
Classes.FILL,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ListSelect
|
<ListSelect
|
||||||
@@ -445,10 +493,8 @@ const ItemForm = ({
|
|||||||
itemPredicate={filterAccounts}
|
itemPredicate={filterAccounts}
|
||||||
popoverProps={{ minimal: true }}
|
popoverProps={{ minimal: true }}
|
||||||
onItemSelect={onItemAccountSelect('sell_account_id')}
|
onItemSelect={onItemAccountSelect('sell_account_id')}
|
||||||
|
|
||||||
selectedItem={values.sell_account_id}
|
selectedItem={values.sell_account_id}
|
||||||
selectedItemProp={'id'}
|
selectedItemProp={'id'}
|
||||||
|
|
||||||
defaultText={<T id={'select_account'} />}
|
defaultText={<T id={'select_account'} />}
|
||||||
labelProp={'name'}
|
labelProp={'name'}
|
||||||
/>
|
/>
|
||||||
@@ -456,7 +502,9 @@ const ItemForm = ({
|
|||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<Col width={404}>
|
<Col width={404}>
|
||||||
<h4><T id={'sales_information'} /></h4>
|
<h4>
|
||||||
|
<T id={'sales_information'} />
|
||||||
|
</h4>
|
||||||
|
|
||||||
{/* Cost price */}
|
{/* Cost price */}
|
||||||
<FormGroup
|
<FormGroup
|
||||||
@@ -464,7 +512,7 @@ const ItemForm = ({
|
|||||||
className={'form-group--item-cost-price'}
|
className={'form-group--item-cost-price'}
|
||||||
intent={errors.cost_price && touched.cost_price && Intent.DANGER}
|
intent={errors.cost_price && touched.cost_price && Intent.DANGER}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage {...{ errors, touched }} name='cost_price' />
|
<ErrorMessage {...{ errors, touched }} name="cost_price" />
|
||||||
}
|
}
|
||||||
inline={true}
|
inline={true}
|
||||||
>
|
>
|
||||||
@@ -490,12 +538,12 @@ const ItemForm = ({
|
|||||||
Intent.DANGER
|
Intent.DANGER
|
||||||
}
|
}
|
||||||
helperText={
|
helperText={
|
||||||
<ErrorMessage {...{ errors, touched }} name='cost_account_id' />
|
<ErrorMessage {...{ errors, touched }} name="cost_account_id" />
|
||||||
}
|
}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'form-group--cost-account',
|
'form-group--cost-account',
|
||||||
'form-group--select-list',
|
'form-group--select-list',
|
||||||
Classes.FILL
|
Classes.FILL,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ListSelect
|
<ListSelect
|
||||||
@@ -504,7 +552,6 @@ const ItemForm = ({
|
|||||||
itemPredicate={filterAccounts}
|
itemPredicate={filterAccounts}
|
||||||
popoverProps={{ minimal: true }}
|
popoverProps={{ minimal: true }}
|
||||||
onItemSelect={onItemAccountSelect('cost_account_id')}
|
onItemSelect={onItemAccountSelect('cost_account_id')}
|
||||||
|
|
||||||
defaultText={<T id={'select_account'} />}
|
defaultText={<T id={'select_account'} />}
|
||||||
labelProp={'name'}
|
labelProp={'name'}
|
||||||
selectedItem={values.cost_account_id}
|
selectedItem={values.cost_account_id}
|
||||||
@@ -521,18 +568,23 @@ const ItemForm = ({
|
|||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'inventory_account'}/>}
|
label={<T id={'inventory_account'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
intent={
|
intent={
|
||||||
errors.inventory_account_id &&
|
errors.inventory_account_id &&
|
||||||
touched.inventory_account_id &&
|
touched.inventory_account_id &&
|
||||||
Intent.DANGER
|
Intent.DANGER
|
||||||
}
|
}
|
||||||
helperText={<ErrorMessage {...{ errors, touched }} name='inventory_account_id' />}
|
helperText={
|
||||||
|
<ErrorMessage
|
||||||
|
{...{ errors, touched }}
|
||||||
|
name="inventory_account_id"
|
||||||
|
/>
|
||||||
|
}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'form-group--item-inventory_account',
|
'form-group--item-inventory_account',
|
||||||
'form-group--select-list',
|
'form-group--select-list',
|
||||||
Classes.FILL
|
Classes.FILL,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ListSelect
|
<ListSelect
|
||||||
@@ -541,15 +593,15 @@ const ItemForm = ({
|
|||||||
itemPredicate={filterAccounts}
|
itemPredicate={filterAccounts}
|
||||||
popoverProps={{ minimal: true }}
|
popoverProps={{ minimal: true }}
|
||||||
onItemSelect={onItemAccountSelect('inventory_account_id')}
|
onItemSelect={onItemAccountSelect('inventory_account_id')}
|
||||||
|
|
||||||
defaultText={<T id={'select_account'} />}
|
defaultText={<T id={'select_account'} />}
|
||||||
labelProp={'name'}
|
labelProp={'name'}
|
||||||
selectedItem={values.inventory_account_id}
|
selectedItem={values.inventory_account_id}
|
||||||
selectedItemProp={'id'} />
|
selectedItemProp={'id'}
|
||||||
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'opening_stock'}/>}
|
label={<T id={'opening_stock'} />}
|
||||||
className={'form-group--item-stock'}
|
className={'form-group--item-stock'}
|
||||||
inline={true}
|
inline={true}
|
||||||
>
|
>
|
||||||
@@ -561,28 +613,20 @@ const ItemForm = ({
|
|||||||
</FormGroup>
|
</FormGroup>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<div class='form__floating-footer'>
|
|
||||||
<Button intent={Intent.PRIMARY} disabled={isSubmitting} type='submit'>
|
|
||||||
|
|
||||||
{ itemDetail && itemDetail.id ? <T id={'edit'}/> : <T id={'save'}/> }
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button className={'ml1'} disabled={isSubmitting}>
|
|
||||||
<T id={'save_as_draft'}/>
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button className={'ml1'} onClick={handleCancelClickBtn}>
|
|
||||||
<T id={'close'} />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<ItemFloatingFooter
|
||||||
|
formik={isSubmitting}
|
||||||
|
onSubmitClick={handleSubmitClick}
|
||||||
|
onCancelClick={handleCancelClick}
|
||||||
|
itemDetail={itemDetail}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
withAccounts(({accounts})=>({
|
withAccounts(({ accounts }) => ({
|
||||||
accounts,
|
accounts,
|
||||||
})),
|
})),
|
||||||
withAccountDetail,
|
withAccountDetail,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, {useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { useParams,useHistory } from 'react-router-dom';
|
import { useParams, useHistory } from 'react-router-dom';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
|
|
||||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
@@ -12,7 +12,6 @@ import withItemsActions from './withItemsActions';
|
|||||||
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
|
||||||
const ItemFormContainer = ({
|
const ItemFormContainer = ({
|
||||||
// #withDashboardActions
|
// #withDashboardActions
|
||||||
changePageTitle,
|
changePageTitle,
|
||||||
@@ -29,35 +28,43 @@ const ItemFormContainer = ({
|
|||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const fetchAccounts = useQuery('accounts-list',
|
const fetchAccounts = useQuery('accounts-list', (key) =>
|
||||||
(key) => requestFetchAccounts());
|
requestFetchAccounts(),
|
||||||
|
);
|
||||||
|
|
||||||
const fetchCategories = useQuery('item-categories-list',
|
const fetchCategories = useQuery('item-categories-list', (key) =>
|
||||||
(key) => requestFetchItemCategories());
|
requestFetchItemCategories(),
|
||||||
|
);
|
||||||
|
|
||||||
const fetchItemDetail = useQuery(
|
const fetchItemDetail = useQuery(id && ['item-detail-list', id], (key) =>
|
||||||
id && ['item-detail-list', id],
|
requestFetchItems(),
|
||||||
(key) => requestFetchItems());
|
);
|
||||||
|
|
||||||
const handleFormSubmit =useCallback((payload)=>{
|
const handleFormSubmit = useCallback(
|
||||||
|
(payload) => {
|
||||||
|
payload.redirect && history.push('/items');
|
||||||
|
},
|
||||||
|
[history],
|
||||||
|
);
|
||||||
|
|
||||||
payload.redirect && history.push('/items/new');
|
const handleCancel = useCallback(() => {
|
||||||
|
// history.push('/items');
|
||||||
},[history])
|
history.goBack();
|
||||||
|
}, [history]);
|
||||||
const handleCancel =useCallback(()=>{
|
|
||||||
|
|
||||||
history.push('/items/new');
|
|
||||||
},[history])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardInsider
|
<DashboardInsider
|
||||||
loading={fetchItemDetail.isFetching || fetchAccounts.isFetching || fetchCategories.isFetching }
|
loading={
|
||||||
name={'item-form'}>
|
fetchItemDetail.isFetching ||
|
||||||
|
fetchAccounts.isFetching ||
|
||||||
|
fetchCategories.isFetching
|
||||||
|
}
|
||||||
|
name={'item-form'}
|
||||||
|
>
|
||||||
<ItemForm
|
<ItemForm
|
||||||
itemId={id}
|
onFormSubmit={handleFormSubmit}
|
||||||
onFormSubmit={handleFormSubmit}
|
itemId={id}
|
||||||
onCancelForm={handleCancel}
|
onCancelForm={handleCancel}
|
||||||
/>
|
/>
|
||||||
</DashboardInsider>
|
</DashboardInsider>
|
||||||
);
|
);
|
||||||
@@ -67,5 +74,5 @@ export default compose(
|
|||||||
withDashboardActions,
|
withDashboardActions,
|
||||||
withAccountsActions,
|
withAccountsActions,
|
||||||
withItemCategoriesActions,
|
withItemCategoriesActions,
|
||||||
withItemsActions
|
withItemsActions,
|
||||||
)(ItemFormContainer);
|
)(ItemFormContainer);
|
||||||
|
|||||||
56
client/src/containers/Items/ItemsFooter.js
Normal file
56
client/src/containers/Items/ItemsFooter.js
Normal file
@@ -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 (
|
||||||
|
<div class="form__floating-footer">
|
||||||
|
<Button
|
||||||
|
intent={Intent.PRIMARY}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
type="submit"
|
||||||
|
onClick={() => {
|
||||||
|
onSubmitClick({ publish: true, redirect: true });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{itemDetail && itemDetail.id ? <T id={'edit'} /> : <T id={'save'} />}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
disabled={isSubmitting}
|
||||||
|
intent={Intent.PRIMARY}
|
||||||
|
className={'ml1'}
|
||||||
|
name={'save_and_new'}
|
||||||
|
onClick={() => {
|
||||||
|
onSubmitClick({ publish: true, redirect: false });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<T id={'save_new'} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className={'ml1'}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
onClick={() => {
|
||||||
|
onSubmitClick({ publish: false, redirect: false });
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<T id={'save_as_draft'} />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className={'ml1'}
|
||||||
|
onClick={() => {
|
||||||
|
onCancelClick && onCancelClick();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<T id={'close'} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -438,6 +438,9 @@ export default {
|
|||||||
'The expense has been successfully deleted',
|
'The expense has been successfully deleted',
|
||||||
the_expenses_has_been_successfully_deleted:
|
the_expenses_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',
|
the_expense_id_has_been_published: 'The expense id has been published',
|
||||||
select_beneficiary_account: 'Select Beneficiary Account',
|
select_beneficiary_account: 'Select Beneficiary Account',
|
||||||
total_amount_equals_zero: 'Total amount equals zero',
|
total_amount_equals_zero: 'Total amount equals zero',
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export const locale = {
|
|||||||
notType: ({ path, type, value, originalValue }) => {
|
notType: ({ path, type, value, originalValue }) => {
|
||||||
let isCast = originalValue != null && originalValue !== value;
|
let isCast = originalValue != null && originalValue !== value;
|
||||||
let msg =
|
let msg =
|
||||||
`${path} must beeeeee a \`${type}\` type, ` +
|
`${path} must be a \`${type}\` type, ` +
|
||||||
`but the final value was: \`${printValue(value, true)}\`` +
|
`but the final value was: \`${printValue(value, true)}\`` +
|
||||||
(isCast
|
(isCast
|
||||||
? ` (cast from the value \`${printValue(originalValue, true)}\`).`
|
? ` (cast from the value \`${printValue(originalValue, true)}\`).`
|
||||||
|
|||||||
@@ -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 || []);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -40,6 +40,17 @@ const customersReducer = createReducer(initialState, {
|
|||||||
const { loading } = action.payload;
|
const { loading } = action.payload;
|
||||||
state.loading = !!loading;
|
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);
|
export default createTableQueryReducers('customers', customersReducer);
|
||||||
|
|||||||
@@ -4,5 +4,6 @@ export default {
|
|||||||
CUSTOMERS_PAGE_SET: 'CUSTOMERS_PAGE_SET',
|
CUSTOMERS_PAGE_SET: 'CUSTOMERS_PAGE_SET',
|
||||||
CUSTOMERS_TABLE_LOADING: 'CUSTOMERS_TABLE_LOADING',
|
CUSTOMERS_TABLE_LOADING: 'CUSTOMERS_TABLE_LOADING',
|
||||||
CUSTOMERS_TABLE_QUERIES_ADD: 'CUSTOMERS_TABLE_QUERIES_ADD',
|
CUSTOMERS_TABLE_QUERIES_ADD: 'CUSTOMERS_TABLE_QUERIES_ADD',
|
||||||
CUSTOMER_DELETE:'CUSTOMER_DELETE'
|
CUSTOMER_DELETE:'CUSTOMER_DELETE',
|
||||||
|
CUSTOMERS_BULK_DELETE:'CUSTOMERS_BULK_DELETE'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ export const deleteExpense = ({ id }) => {
|
|||||||
export const deleteBulkExpenses = ({ ids }) => {
|
export const deleteBulkExpenses = ({ ids }) => {
|
||||||
return (dispatch) =>
|
return (dispatch) =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
ApiService.delete('expenses/bulk', { params: { ids } })
|
ApiService.delete('expenses', { params: { ids } })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.EXPENSES_BULK_DELETE,
|
type: t.EXPENSES_BULK_DELETE,
|
||||||
|
|||||||
@@ -2,93 +2,101 @@ import ApiService from 'services/ApiService';
|
|||||||
import t from 'store/types';
|
import t from 'store/types';
|
||||||
|
|
||||||
export const submitItem = ({ form }) => {
|
export const submitItem = ({ form }) => {
|
||||||
return dispatch => ApiService.post(`items`, form);
|
return (dispatch) => ApiService.post(`items`, form);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const editItem = ({ id, form }) => {
|
export const editItem = ({ id, form }) => {
|
||||||
return dispatch => ApiService.post(`items/${id}`, form);
|
return (dispatch) => ApiService.post(`items/${id}`, form);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fetchItems = ({ query }) => {
|
export const fetchItems = ({ query }) => {
|
||||||
return (dispatch, getState) => new Promise((resolve, reject) => {
|
return (dispatch, getState) =>
|
||||||
const pageQuery = getState().items.tableQuery;
|
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({
|
dispatch({
|
||||||
type: t.ITEMS_TABLE_LOADING,
|
type: t.ITEMS_TABLE_LOADING,
|
||||||
payload: { loading: false },
|
payload: { loading: true },
|
||||||
});
|
});
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
type: t.SET_DASHBOARD_REQUEST_LOADING,
|
||||||
});
|
});
|
||||||
resolve(response);
|
ApiService.get(`items`, { params: { ...pageQuery, ...query } })
|
||||||
}).catch((error) => {
|
.then((response) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.SET_DASHBOARD_REQUEST_COMPLETED,
|
type: t.ITEMS_SET,
|
||||||
});
|
items: response.data.items.results,
|
||||||
reject(error);
|
});
|
||||||
|
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 }) => {
|
export const fetchItem = ({ id }) => {
|
||||||
return dispatch =>
|
return (dispatch) =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
ApiService.get(`items/${id}`)
|
ApiService.get(`items/${id}`)
|
||||||
.then(response => {
|
.then((response) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.ITEM_SET,
|
type: t.ITEM_SET,
|
||||||
item: response.data.item
|
item: response.data.item,
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch((error) => {
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const deleteItem = ({ id }) => {
|
export const deleteItem = ({ id }) => {
|
||||||
return dispatch => new Promise((resolve, reject) => {
|
return (dispatch) =>
|
||||||
ApiService.delete(`items/${id}`)
|
new Promise((resolve, reject) => {
|
||||||
.then((response) => {
|
ApiService.delete(`items/${id}`)
|
||||||
dispatch({
|
.then((response) => {
|
||||||
type: t.ITEM_DELETE,
|
dispatch({
|
||||||
payload: { id },
|
type: t.ITEM_DELETE,
|
||||||
|
payload: { id },
|
||||||
|
});
|
||||||
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error);
|
||||||
});
|
});
|
||||||
resolve(response);
|
});
|
||||||
}).catch((error) => { reject(error); });
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export const deleteBulkItems = ({ ids }) => {
|
export const deleteBulkItems = ({ ids }) => {
|
||||||
return dispatch => new Promise((resolve, reject) => {
|
return (dispatch) =>
|
||||||
ApiService.delete(`items`, { params: { ids }}).then((response) => {
|
new Promise((resolve, reject) => {
|
||||||
dispatch({
|
ApiService.delete('items', { params: { ids } })
|
||||||
type: t.ITEMS_BULK_DELETE,
|
.then((response) => {
|
||||||
payload: { ids }
|
dispatch({
|
||||||
});
|
type: t.ITEMS_BULK_DELETE,
|
||||||
resolve(response);
|
payload: { ids },
|
||||||
}).catch((error) => {
|
});
|
||||||
reject(error);
|
resolve(response);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
reject(error.response.data.errors || []);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import t from 'store/types';
|
import t from 'store/types';
|
||||||
import { createReducer } from '@reduxjs/toolkit';
|
import { createReducer } from '@reduxjs/toolkit';
|
||||||
import {
|
import { getItemsViewPages } from 'store/items/items.selectors';
|
||||||
getItemsViewPages,
|
|
||||||
} from 'store/items/items.selectors';
|
|
||||||
import { createTableQueryReducers } from 'store/queryReducers';
|
import { createTableQueryReducers } from 'store/queryReducers';
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
@@ -20,7 +18,7 @@ const itemsReducer = createReducer(initialState, {
|
|||||||
[t.ITEMS_SET]: (state, action) => {
|
[t.ITEMS_SET]: (state, action) => {
|
||||||
const _items = {};
|
const _items = {};
|
||||||
|
|
||||||
action.items.forEach(item => {
|
action.items.forEach((item) => {
|
||||||
_items[item.id] = item;
|
_items[item.id] = item;
|
||||||
});
|
});
|
||||||
state.items = {
|
state.items = {
|
||||||
@@ -43,11 +41,11 @@ const itemsReducer = createReducer(initialState, {
|
|||||||
if (typeof itemRelation === 'undefined') {
|
if (typeof itemRelation === 'undefined') {
|
||||||
state.itemsRelation[item.id] = [];
|
state.itemsRelation[item.id] = [];
|
||||||
}
|
}
|
||||||
const filteredRelation = state.itemsRelation[item.id]
|
const filteredRelation = state.itemsRelation[item.id].filter(
|
||||||
.filter((relation) => (
|
(relation) =>
|
||||||
relation.viewId === viewId &&
|
relation.viewId === viewId &&
|
||||||
relation.pageNumber === paginationMeta.page
|
relation.pageNumber === paginationMeta.page,
|
||||||
));
|
);
|
||||||
|
|
||||||
filteredRelation.push({
|
filteredRelation.push({
|
||||||
viewId,
|
viewId,
|
||||||
@@ -61,7 +59,7 @@ const itemsReducer = createReducer(initialState, {
|
|||||||
pages: {
|
pages: {
|
||||||
...viewPages,
|
...viewPages,
|
||||||
[paginationMeta.page]: {
|
[paginationMeta.page]: {
|
||||||
ids: items.map(i => i.id),
|
ids: items.map((i) => i.id),
|
||||||
meta: paginationMeta,
|
meta: paginationMeta,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -96,6 +94,18 @@ const itemsReducer = createReducer(initialState, {
|
|||||||
[t.ITEMS_SET_CURRENT_VIEW]: (state, action) => {
|
[t.ITEMS_SET_CURRENT_VIEW]: (state, action) => {
|
||||||
state.currentViewId = action.currentViewId;
|
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);
|
export default createTableQueryReducers('items', itemsReducer);
|
||||||
|
|||||||
Reference in New Issue
Block a user