Merge remote-tracking branch 'origin/_tasks'

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

View File

@@ -1,31 +1,36 @@
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],
);
// Filters accounts items. // Filters accounts items.
const filterAccountsPredicater = useCallback( const filterAccountsPredicater = useCallback(
@@ -47,12 +52,13 @@ export default function AccountsSelectList({
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}
itemPredicate={filterAccountsPredicater} itemPredicate={filterAccountsPredicater}
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}
/> />

View File

@@ -18,7 +18,7 @@ function App({ locale }) {
const queryConfig = { const queryConfig = {
queries: { queries: {
refetchOnWindowFocus: true, refetchOnWindowFocus: false,
} }
}; };
return ( return (

View File

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

View 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>
);
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -99,10 +99,9 @@ function ExpensesDataTable({
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={expense.published}> <If condition={!expense.published}>
<MenuItem <MenuItem
text={formatMessage({ id: 'publish_expense' })} text={formatMessage({ id: 'publish_expense' })}
onClick={handlePublishExpense(expense)} onClick={handlePublishExpense(expense)}
@@ -120,23 +119,35 @@ function ExpensesDataTable({
/> />
</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(
() => [ () => [
@@ -180,7 +191,7 @@ function ExpensesDataTable({
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>
@@ -279,5 +290,5 @@ export default compose(
expensesLoading, expensesLoading,
expensesPagination, expensesPagination,
})), })),
withViewDetails, withViewDetails(),
)(ExpensesDataTable); )(ExpensesDataTable);

View File

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

View File

@@ -11,15 +11,13 @@ 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 { Col, Row } from 'react-grid-system';
import ExpenseFormHeader from './ExpenseFormHeader'; import ExpenseFormHeader from './ExpenseFormHeader';
import ExpenseTable from './ExpenseTable'; import ExpenseTable from './ExpenseTable';
import ExpenseFloatingFooter from './ExpenseFooter'; import ExpenseFloatingFooter from './ExpenseFooter';
import withExpensesActions from 'containers/Expenses/withExpensesActions'; import withExpensesActions from 'containers/Expenses/withExpensesActions';
import withExpneseDetail from 'containers/Expenses/withExpenseDetail'; import withExpenseDetail from 'containers/Expenses/withExpenseDetail';
import withAccountsActions from 'containers/Accounts/withAccountsActions'; import withAccountsActions from 'containers/Accounts/withAccountsActions';
import withDashboardActions from 'containers/Dashboard/withDashboardActions'; import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withMediaActions from 'containers/Media/withMediaActions'; import withMediaActions from 'containers/Media/withMediaActions';
@@ -44,13 +42,14 @@ function ExpenseForm({
changePageSubtitle, changePageSubtitle,
//#withExpenseDetail //#withExpenseDetail
// @todo expenseDetail to expense expense,
expenseDetail,
// #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,18 +74,17 @@ 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()
// .required()
.label(formatMessage({ id: 'beneficiary' })), .label(formatMessage({ id: 'beneficiary' })),
payment_account_id: Yup.string() payment_account_id: Yup.string()
.required() .required()
@@ -104,8 +102,12 @@ function ExpenseForm({
Yup.object().shape({ Yup.object().shape({
index: Yup.number().nullable(), index: Yup.number().nullable(),
amount: Yup.number().nullable(), amount: Yup.number().nullable(),
// @todo expense_account_id is required. expense_account_id: Yup.number()
expense_account_id: Yup.number().nullable(), .nullable()
.when(['amount'], {
is: (amount) => amount,
then: Yup.number().required(),
}),
description: Yup.string().nullable(), description: Yup.string().nullable(),
}), }),
), ),
@@ -115,6 +117,7 @@ function ExpenseForm({
(payload) => { (payload) => {
onFormSubmit && onFormSubmit(payload); onFormSubmit && onFormSubmit(payload);
}, },
[onFormSubmit], [onFormSubmit],
); );
@@ -146,32 +149,41 @@ 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],
); );
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,
@@ -181,11 +193,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,
}; };
@@ -193,8 +206,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(
@@ -207,10 +220,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(
@@ -234,10 +245,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);
@@ -260,6 +272,7 @@ function ExpenseForm({
const handleSubmitClick = useCallback( const handleSubmitClick = useCallback(
(payload) => { (payload) => {
setPayload(payload); setPayload(payload);
formik.resetForm();
formik.handleSubmit(); formik.handleSubmit();
}, },
[setPayload, formik], [setPayload, formik],
@@ -313,13 +326,13 @@ 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}
/>
</div> </div>
); );
} }
@@ -329,5 +342,5 @@ export default compose(
withAccountsActions, withAccountsActions,
withDashboardActions, withDashboardActions,
withMediaActions, withMediaActions,
withExpneseDetail(), withExpenseDetail(),
)(ExpenseForm); )(ExpenseForm);

View File

@@ -28,6 +28,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({});
@@ -102,7 +103,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,
@@ -256,8 +256,9 @@ function ExpenseFormHeader({
} }
export default compose( export default compose(
withAccounts(({ accounts }) => ({ withAccounts(({ accounts, accountsTypes }) => ({
accounts, accounts,
accountsTypes,
})), })),
withCurrencies(({ currenciesList }) => ({ withCurrencies(({ currenciesList }) => ({
currenciesList, currenciesList,

View File

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

View File

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

View File

@@ -14,6 +14,7 @@ import { compose } from 'utils';
function Expenses({ function Expenses({
// #withwithAccountsActions // #withwithAccountsActions
requestFetchAccounts, requestFetchAccounts,
requestFetchAccountTypes,
// #withExpensesActions // #withExpensesActions
requestFetchExpense, requestFetchExpense,
@@ -24,18 +25,17 @@ 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(
const fetchExpense = useQuery(id && ['expense', id], (key, expense_Id) => ['expense', id],
requestFetchExpense(expense_Id), (key, _id) => requestFetchExpense(_id),
{ enabled: !!id },
); );
// @todo const fetchCurrencies = useQuery('currencies', () =>
const fetchCurrencies = useQuery('currencies-expense-list', () =>
requestFetchCurrencies(), requestFetchCurrencies(),
); );
const handleFormSubmit = useCallback( const handleFormSubmit = useCallback(
@@ -46,7 +46,7 @@ function Expenses({
); );
const handleCancel = useCallback(() => { const handleCancel = useCallback(() => {
history.push('/expenses-list'); history.goBack();
}, [history]); }, [history]);
return ( return (
@@ -56,6 +56,7 @@ function Expenses({
fetchAccounts.isFetching || fetchAccounts.isFetching ||
fetchCurrencies.isFetching fetchCurrencies.isFetching
} }
name={'expense-form'}
> >
<ExpenseForm <ExpenseForm
onFormSubmit={handleFormSubmit} onFormSubmit={handleFormSubmit}

View File

@@ -19,7 +19,6 @@ import withViewsActions from 'containers/Views/withViewsActions';
import { compose } from 'utils'; import { compose } from 'utils';
function ExpensesList({ function ExpensesList({
// #withDashboardActions // #withDashboardActions
changePageTitle, changePageTitle,
@@ -223,7 +222,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 }} />
@@ -236,10 +235,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>
); );
@@ -248,7 +247,7 @@ function ExpensesList({
export default compose( export default compose(
withDashboardActions, withDashboardActions,
withExpensesActions, withExpensesActions,
withExpenses(({ expensesTableQuery }) => ({ expensesTableQuery })),
withViewsActions, withViewsActions,
withResourceActions withResourceActions,
withExpenses(({ expensesTableQuery }) => ({ expensesTableQuery })),
)(ExpensesList); )(ExpensesList);

View File

@@ -14,7 +14,6 @@ export default (mapState) => {
const mapStateToProps = (state, props) => { const mapStateToProps = (state, props) => {
const query = getExpensesTableQuery(state, props); const query = getExpensesTableQuery(state, props);
const mapped = { const mapped = {
expensesCurrentPage: getExpensesItems(state, props, query), expensesCurrentPage: getExpensesItems(state, props, query),
expensesViews: getResourceViews(state, props, 'expenses'), expensesViews: getResourceViews(state, props, 'expenses'),

View File

@@ -10,7 +10,7 @@ import {
} from 'store/expenses/expenses.actions'; } from 'store/expenses/expenses.actions';
import t from 'store/types'; import t from 'store/types';
export const mapDispatchToProps = (dispatch) => ({ const mapDispatchToProps = (dispatch) => ({
requestSubmitExpense: (form) => dispatch(submitExpense({ form })), requestSubmitExpense: (form) => dispatch(submitExpense({ form })),
requestFetchExpense: (id) => dispatch(fetchExpense({ id })), requestFetchExpense: (id) => dispatch(fetchExpense({ id })),
requestEditExpense: (id, form) => dispatch(editExpense({ id, form })), requestEditExpense: (id, form) => dispatch(editExpense({ id, form })),

View File

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

View File

@@ -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,
@@ -22,6 +21,7 @@ const ItemFormContainer = ({
// #withItemsActions // #withItemsActions
requestFetchItems, requestFetchItems,
requestFetchItem,
// #withItemCategoriesActions // #withItemCategoriesActions
requestFetchItemCategories, requestFetchItemCategories,
@@ -29,35 +29,47 @@ 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], ['item', id],
(key) => requestFetchItems()); (key, _id) => requestFetchItem(_id),
{
enabled: !!id,
},
);
const handleFormSubmit =useCallback((payload)=>{ const handleFormSubmit = useCallback(
(payload) => {
payload.redirect && history.push('/items');
},
[history],
);
payload.redirect && history.push('/items/new'); 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 +79,5 @@ export default compose(
withDashboardActions, withDashboardActions,
withAccountsActions, withAccountsActions,
withItemCategoriesActions, withItemCategoriesActions,
withItemsActions withItemsActions,
)(ItemFormContainer); )(ItemFormContainer);

View 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>
);
}

View File

@@ -1,6 +1,7 @@
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import { import {
fetchItems, fetchItems,
fetchItem,
deleteItem, deleteItem,
submitItem, submitItem,
editItem, editItem,
@@ -10,6 +11,7 @@ import t from 'store/types';
export const mapDispatchToProps = (dispatch) => ({ export const mapDispatchToProps = (dispatch) => ({
requestFetchItems: (query) => dispatch(fetchItems({ query })), requestFetchItems: (query) => dispatch(fetchItems({ query })),
requestFetchItem: (id) => dispatch(fetchItem({ id })),
requestDeleteItem: (id) => dispatch(deleteItem({ id })), requestDeleteItem: (id) => dispatch(deleteItem({ id })),
requestDeleteBulkItems:(ids)=>dispatch(deleteBulkItems({ids})), requestDeleteBulkItems:(ids)=>dispatch(deleteBulkItems({ids})),
requestSubmitItem: (form) => dispatch(submitItem({ form })), requestSubmitItem: (form) => dispatch(submitItem({ form })),

View File

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

View File

@@ -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)}\`).`

View File

@@ -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 || []);
});
});
};

View File

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

View File

@@ -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'
}; };

View File

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

View File

@@ -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 || []);
});
}); });
});
}; };

View File

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