refactoring: invoice, receipt, payment receive, estimate and journal number dialogs.

This commit is contained in:
a.bouhuolia
2021-02-23 10:52:25 +02:00
parent 6e00c2ef7d
commit 236bb896db
37 changed files with 467 additions and 204 deletions

View File

@@ -5,11 +5,7 @@ import InviteUserDialog from 'containers/Dialogs/InviteUserDialog';
import ItemCategoryDialog from 'containers/Dialogs/ItemCategoryDialog';
import CurrencyFormDialog from 'containers/Dialogs/CurrencyFormDialog';
import ExchangeRateFormDialog from 'containers/Dialogs/ExchangeRateFormDialog';
import JournalNumberDialog from 'containers/Dialogs/JournalNumberDialog';
import PaymentReceiveNumberDialog from 'containers/Dialogs/PaymentReceiveNumberDialog';
import EstimateNumberDialog from 'containers/Dialogs/EstimateNumberDialog';
import ReceiptNumberDialog from 'containers/Dialogs/ReceiptNumberDialog';
import InvoiceNumberDialog from 'containers/Dialogs/InvoiceNumberDialog';
import InventoryAdjustmentDialog from 'containers/Dialogs/InventoryAdjustmentFormDialog';
import PaymentViaVoucherDialog from 'containers/Dialogs/PaymentViaVoucherDialog';
@@ -20,11 +16,6 @@ export default function DialogsContainer() {
return (
<div>
<AccountDialog dialogName={'account-form'} />
<JournalNumberDialog dialogName={'journal-number-form'} />
<PaymentReceiveNumberDialog dialogName={'payment-receive-number-form'} />
<EstimateNumberDialog dialogName={'estimate-number-form'} />
<ReceiptNumberDialog dialogName={'receipt-number-form'} />
<InvoiceNumberDialog dialogName={'invoice-number-form'} />
<CurrencyFormDialog dialogName={'currency-form'} />
<InviteUserDialog dialogName={'invite-user'} />
<ExchangeRateFormDialog dialogName={'exchangeRate-form'} />

View File

@@ -15,6 +15,7 @@ import MakeJournalEntriesHeader from './MakeJournalEntriesHeader';
import MakeJournalFormFloatingActions from './MakeJournalFormFloatingActions';
import MakeJournalEntriesField from './MakeJournalEntriesField';
import MakeJournalFormFooter from './MakeJournalFormFooter';
import MakeJournalFormDialogs from './MakeJournalFormDialogs';
import withSettings from 'containers/Settings/withSettings';
@@ -167,6 +168,8 @@ function MakeJournalEntriesForm({
<MakeJournalEntriesField />
<MakeJournalFormFooter />
<MakeJournalFormFloatingActions />
<MakeJournalFormDialogs />
</Form>
</Formik>
</div>

View File

@@ -0,0 +1,28 @@
import React from 'react';
import { useFormikContext } from 'formik';
import JournalNumberDialog from 'containers/Dialogs/JournalNumberDialog';
import { transactionNumber } from 'utils';
/**
* Make journal form dialogs.
*/
export default function MakeJournalFormDialogs() {
const { setFieldValue } = useFormikContext();
// Update the form once the journal number form submit confirm.
const handleConfirm = (values) => {
setFieldValue(
'journal_number',
transactionNumber(values.number_prefix, values.next_number),
);
};
return (
<>
<JournalNumberDialog
dialogName={'journal-number-form'}
onConfirm={handleConfirm}
/>
</>
);
}

View File

@@ -1,13 +1,12 @@
import React, { useCallback } from 'react';
import { DialogContent } from 'components';
import { useQuery, queryCache } from 'react-query';
import { useSaveSettings, useSettingsEstimates } from 'hooks/query';
import ReferenceNumberForm from 'containers/JournalNumber/ReferenceNumberForm';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withSettings from 'containers/Settings/withSettings';
import withSettingsActions from 'containers/Settings/withSettingsActions';
import { compose, optionsMapToArray } from 'utils';
import { compose, optionsMapToArray, saveInvoke } from 'utils';
/**
* Estimate number dialog's content.
@@ -18,28 +17,26 @@ function EstimateNumberDialogContent({
nextNumber,
numberPrefix,
// #withSettingsActions
requestFetchOptions,
requestSubmitOptions,
// #withDialogActions
closeDialog,
// #ownProps
onConfirm,
}) {
const fetchSettings = useQuery(['settings'], () => requestFetchOptions({}));
const { isLoading: isSettingsLoading } = useSettingsEstimates();
const { mutateAsync: saveSettingsMutate } = useSaveSettings();
const handleSubmitForm = (values, { setSubmitting }) => {
const options = optionsMapToArray(values).map((option) => {
return { key: option.key, ...option, group: 'sales_estimates' };
});
requestSubmitOptions({ options })
const options = optionsMapToArray(values).map((option) => ({
key: option.key,
...option,
group: 'sales_estimates',
}));
saveSettingsMutate({ options })
.then(() => {
setSubmitting(false);
closeDialog('estimate-number-form');
setTimeout(() => {
queryCache.invalidateQueries('settings');
// setEstimateNumberChanged(true);
}, 250);
saveInvoke(onConfirm, values);
})
.catch(() => {
setSubmitting(false);
@@ -51,7 +48,7 @@ function EstimateNumberDialogContent({
}, [closeDialog]);
return (
<DialogContent isLoading={fetchSettings.isFetching}>
<DialogContent isLoading={isSettingsLoading}>
<ReferenceNumberForm
initialNumber={nextNumber}
initialPrefix={numberPrefix}
@@ -64,7 +61,6 @@ function EstimateNumberDialogContent({
export default compose(
withDialogActions,
withSettingsActions,
withSettings(({ estimatesSettings }) => ({
nextNumber: estimatesSettings?.nextNumber,
numberPrefix: estimatesSettings?.numberPrefix,

View File

@@ -2,13 +2,18 @@ import React, { lazy } from 'react';
import { FormattedMessage as T } from 'react-intl';
import { Dialog, DialogSuspense } from 'components';
import withDialogRedux from 'components/DialogReduxConnect';
import { compose } from 'utils';
import { saveInvoke, compose } from 'utils';
const EstimateNumberDialogContent = lazy(() =>
import('./EstimateNumberDialogContent'),
);
function EstimateNumberDialog({ dialogName, paylaod = { id: null }, isOpen }) {
function EstimateNumberDialog({
dialogName,
paylaod = { id: null },
isOpen,
onConfirm
}) {
return (
<Dialog
name={dialogName}
@@ -19,7 +24,9 @@ function EstimateNumberDialog({ dialogName, paylaod = { id: null }, isOpen }) {
className={'dialog--journal-number-settings'}
>
<DialogSuspense>
<EstimateNumberDialogContent EstimateNumberId={paylaod.id} />
<EstimateNumberDialogContent
estimateNumberId={paylaod.id}
onConfirm={(values) => saveInvoke(onConfirm, values)}/>
</DialogSuspense>
</Dialog>
);

View File

@@ -16,24 +16,30 @@ import { compose, optionsMapToArray } from 'utils';
*/
function InvoiceNumberDialogContent({
// #ownProps
onConfirm,
// #withSettings
nextNumber,
numberPrefix,
// #withDialogActions
closeDialog,
}) {
const { mutateAsync: saveSettings } = useSaveSettings();
const handleSubmitForm = (values, { setSubmitting }) => {
const options = optionsMapToArray(values).map((option) => {
return { key: option.key, ...option, group: 'sales_invoices' };
});
const options = optionsMapToArray(values).map((option) => ({
key: option.key,
...option,
group: 'sales_invoices',
}));
saveSettings({ options })
.then(() => {
setSubmitting(false);
closeDialog('invoice-number-form');
onConfirm(values);
})
.catch(() => {
setSubmitting(false);
@@ -64,5 +70,4 @@ export default compose(
nextNumber: invoiceSettings?.nextNumber,
numberPrefix: invoiceSettings?.numberPrefix,
})),
// withInvoicesActions,
)(InvoiceNumberDialogContent);

View File

@@ -1,6 +1,6 @@
import React, { createContext, useContext } from 'react';
import { DialogContent } from 'components';
import { useSettings } from 'hooks/query';
import { useSettingsInvoices } from 'hooks/query';
const InvoiceNumberDialogContext = createContext();
@@ -8,15 +8,15 @@ const InvoiceNumberDialogContext = createContext();
* Invoice number dialog provider.
*/
function InvoiceNumberDialogProvider({ query, ...props }) {
const { isLoading } = useSettings();
const { isLoading: isSettingsLoading } = useSettingsInvoices();
// Provider payload.
const provider = {
isSettingsLoading,
};
return (
<DialogContent isLoading={isLoading}>
<DialogContent isLoading={isSettingsLoading}>
<InvoiceNumberDialogContext.Provider value={provider} {...props} />
</DialogContent>
);

View File

@@ -2,13 +2,19 @@ import React, { lazy } from 'react';
import { FormattedMessage as T } from 'react-intl';
import { Dialog, DialogSuspense } from 'components';
import withDialogRedux from 'components/DialogReduxConnect';
import { compose } from 'utils';
import { compose, saveInvoke } from 'utils';
const InvoiceNumberDialogContent = lazy(() =>
import('./InvoiceNumberDialogContent'),
);
function InvoiceNumberDialog({ dialogName, payload = { id: null }, isOpen }) {
function InvoiceNumberDialog({
dialogName,
payload = { id: null },
isOpen,
onConfirm,
}) {
return (
<Dialog
title={<T id={'invoice_number_settings'} />}
@@ -18,7 +24,9 @@ function InvoiceNumberDialog({ dialogName, payload = { id: null }, isOpen }) {
isOpen={isOpen}
>
<DialogSuspense>
<InvoiceNumberDialogContent InvoiceNumberId={payload.id} />
<InvoiceNumberDialogContent
InvoiceNumberId={payload.id}
onConfirm={(values) => saveInvoke(onConfirm, values)} />
</DialogSuspense>
</Dialog>
);

View File

@@ -1,15 +1,12 @@
import React, { useCallback } from 'react';
import { DialogContent } from 'components';
import { useQuery, queryCache } from 'react-query';
import { useSaveSettings, useSettingsManualJournals } from 'hooks/query';
import ReferenceNumberForm from 'containers/JournalNumber/ReferenceNumberForm';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withSettingsActions from 'containers/Settings/withSettingsActions';
import withSettings from 'containers/Settings/withSettings';
// import withManualJournalsActions from 'containers/Accounting/withManualJournalsActions';
import { compose, optionsMapToArray } from 'utils';
import { saveInvoke, compose, optionsMapToArray } from 'utils';
import 'style/pages/ManualJournal/JournalNumberDialog.scss'
@@ -21,28 +18,25 @@ function JournalNumberDialogContent({
nextNumber,
numberPrefix,
// #withSettingsActions
requestFetchOptions,
requestSubmitOptions,
// #withDialogActions
closeDialog,
}) {
const fetchSettings = useQuery(
['settings'],
() => requestFetchOptions({}),
);
// #ownProps
onConfirm
}) {
const { isLoading: isSettingsLoading } = useSettingsManualJournals();
const { mutateAsync: saveSettingsMutate } = useSaveSettings();
// Handle the form submit.
const handleSubmitForm = (values, { setSubmitting }) => {
const options = optionsMapToArray(values).map((option) => ({
key: option.key, ...option, group: 'manual_journals',
}));
requestSubmitOptions({ options }).then(() => {
saveSettingsMutate({ options }).then(() => {
setSubmitting(false);
closeDialog('journal-number-form');
saveInvoke(onConfirm, values);
}).catch(() => {
setSubmitting(false);
});
@@ -53,7 +47,7 @@ function JournalNumberDialogContent({
}, [closeDialog]);
return (
<DialogContent isLoading={fetchSettings.isFetching}>
<DialogContent isLoading={isSettingsLoading}>
<ReferenceNumberForm
initialNumber={nextNumber}
initialPrefix={numberPrefix}
@@ -66,10 +60,8 @@ function JournalNumberDialogContent({
export default compose(
withDialogActions,
withSettingsActions,
withSettings(({ manualJournalsSettings }) => ({
nextNumber: manualJournalsSettings?.nextNumber,
numberPrefix: manualJournalsSettings?.numberPrefix,
})),
// withManualJournalsActions,
)(JournalNumberDialogContent);

View File

@@ -2,7 +2,7 @@ import React, { lazy } from 'react';
import { FormattedMessage as T } from 'react-intl';
import { Dialog, DialogSuspense } from 'components';
import withDialogRedux from 'components/DialogReduxConnect';
import { compose } from 'utils';
import { saveInvoke, compose } from 'utils';
const JournalNumberDialogContent = lazy(() => import('./JournalNumberDialogContent'));
@@ -10,7 +10,12 @@ function JournalNumberDialog({
dialogName,
payload = { id: null },
isOpen,
onConfirm
}) {
const handleConfirm = (values) => {
saveInvoke(onConfirm, values)
};
return (
<Dialog
name={dialogName}
@@ -23,6 +28,7 @@ function JournalNumberDialog({
<DialogSuspense>
<JournalNumberDialogContent
journalNumberId={payload.id}
onConfirm={handleConfirm}
/>
</DialogSuspense>
</Dialog>

View File

@@ -1,15 +1,14 @@
import React, { useCallback } from 'react';
import { DialogContent } from 'components';
import { useQuery } from 'react-query';
import { useSaveSettings, useSettingsPaymentReceives } from 'hooks/query';
import ReferenceNumberForm from 'containers/JournalNumber/ReferenceNumberForm';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withSettingsActions from 'containers/Settings/withSettingsActions';
import withSettings from 'containers/Settings/withSettings';
// import withPaymentReceivesActions from 'containers/Sales/PaymentReceive/withPaymentReceivesActions';
import { compose, optionsMapToArray } from 'utils';
import { saveInvoke, compose, optionsMapToArray } from 'utils';
/**
* payment receive number dialog's content.
@@ -20,31 +19,28 @@ function PaymentNumberDialogContent({
nextNumber,
numberPrefix,
// #withSettingsActions
requestFetchOptions,
requestSubmitOptions,
// #withDialogActions
closeDialog,
// #withPaymentReceivesActions
// setPaymentReceiveNumberChanged,
// #ownProps
onConfirm,
}) {
const fetchSettings = useQuery(['settings'], () => requestFetchOptions({}));
const { isLoading: isSettingsLoading } = useSettingsPaymentReceives();
const { mutateAsync: saveSettingsMutate } = useSaveSettings();
const handleSubmitForm = (values, { setSubmitting }) => {
const options = optionsMapToArray(values).map((option) => {
return { key: option.key, ...option, group: 'payment_receives' };
});
const options = optionsMapToArray(values).map((option) => ({
key: option.key,
...option,
group: 'payment_receives',
}));
requestSubmitOptions({ options })
saveSettingsMutate({ options })
.then(() => {
setSubmitting(false);
closeDialog('payment-receive-number-form');
setTimeout(() => {
// setPaymentReceiveNumberChanged(true);
}, 250);
saveInvoke(onConfirm, values);
})
.catch(() => {
setSubmitting(false);
@@ -55,9 +51,8 @@ function PaymentNumberDialogContent({
closeDialog('payment-receive-number-form');
}, [closeDialog]);
return (
<DialogContent isLoading={fetchSettings.isFetching}>
<DialogContent isLoading={isSettingsLoading}>
<ReferenceNumberForm
initialNumber={nextNumber}
initialPrefix={numberPrefix}
@@ -75,5 +70,4 @@ export default compose(
nextNumber: paymentReceiveSettings?.nextNumber,
numberPrefix: paymentReceiveSettings?.numberPrefix,
})),
// withPaymentReceivesActions,
)(PaymentNumberDialogContent);

View File

@@ -2,16 +2,20 @@ import React, { lazy } from 'react';
import { FormattedMessage as T } from 'react-intl';
import { Dialog, DialogSuspense } from 'components';
import withDialogRedux from 'components/DialogReduxConnect';
import { compose } from 'utils';
import { saveInvoke, compose } from 'utils';
const PaymentReceiveNumbereDialogConetnet = lazy(() =>
import('./PaymentReceiveNumberDialogContent'),
);
/**
* Payment receive number dialog.
*/
function PaymentReceiveNumberDialog({
dialogName,
payload = { id: null },
isOpen,
onConfirm
}) {
return (
<Dialog
@@ -24,6 +28,7 @@ function PaymentReceiveNumberDialog({
<DialogSuspense>
<PaymentReceiveNumbereDialogConetnet
paymentReceiveNumberId={payload.id}
onConfirm={(values) => saveInvoke(onConfirm, values)}
/>
</DialogSuspense>
</Dialog>

View File

@@ -1,49 +1,45 @@
import React, { useCallback } from 'react';
import { DialogContent } from 'components';
import { useQuery } from 'react-query';
import { useSettingsReceipts, useSaveSettings } from 'hooks/query';
import ReferenceNumberForm from 'containers/JournalNumber/ReferenceNumberForm';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withSettings from 'containers/Settings/withSettings';
import withSettingsActions from 'containers/Settings/withSettingsActions';
import { compose, optionsMapToArray } from 'utils';
import { compose, optionsMapToArray, saveInvoke } from 'utils';
/**
* Receipt number dialog's content.
*/
function ReceiptNumberDialogContent({
// #ownProps
receiptId,
onConfirm,
// #withSettings
nextNumber,
numberPrefix,
// #withSettingsActions
requestFetchOptions,
requestSubmitOptions,
// #withDialogActions
closeDialog,
// #withReceiptActions
setReceiptNumberChanged,
}) {
const fetchSettings = useQuery(['settings'], () => requestFetchOptions({}));
const { isLoading: isSettingsLoading } = useSettingsReceipts();
const { mutateAsync: saveSettingsMutate } = useSaveSettings();
const handleSubmitForm = (values, { setSubmitting }) => {
const options = optionsMapToArray(values).map((option) => {
return { key: option.key, ...option, group: 'sales_receipts' };
});
const options = optionsMapToArray(values).map((option) => ({
key: option.key,
...option,
group: 'sales_receipts',
}));
requestSubmitOptions({ options })
saveSettingsMutate({ options })
.then(() => {
setSubmitting(false);
closeDialog('receipt-number-form');
setTimeout(() => {
setReceiptNumberChanged(true);
}, 250);
saveInvoke(onConfirm, values)
})
.catch(() => {
setSubmitting(false);
@@ -55,7 +51,7 @@ function ReceiptNumberDialogContent({
}, [closeDialog]);
return (
<DialogContent isLoading={fetchSettings.isFetching}>
<DialogContent isLoading={isSettingsLoading}>
<ReferenceNumberForm
initialNumber={nextNumber}
initialPrefix={numberPrefix}
@@ -68,7 +64,6 @@ function ReceiptNumberDialogContent({
export default compose(
withDialogActions,
withSettingsActions,
withSettings(({ receiptSettings }) => ({
nextNumber: receiptSettings?.nextNumber,
numberPrefix: receiptSettings?.numberPrefix,

View File

@@ -2,13 +2,25 @@ import React, { lazy } from 'react';
import { FormattedMessage as T } from 'react-intl';
import { Dialog, DialogSuspense } from 'components';
import withDialogRedux from 'components/DialogReduxConnect';
import { compose } from 'utils';
import { compose, saveInvoke } from 'utils';
const ReceiptNumberDialogContent = lazy(() =>
import('./ReceiptNumberDialogContent'),
);
function ReceiptNumberDialog({ dialogName, paylaod = { id: null }, isOpen }) {
/**
* Sale receipt number dialog.
*/
function ReceiptNumberDialog({
dialogName,
paylaod = { id: null },
isOpen,
onConfirm,
}) {
const handleConfirm = (values) => {
saveInvoke(onConfirm, values);
};
return (
<Dialog
name={dialogName}
@@ -18,7 +30,10 @@ function ReceiptNumberDialog({ dialogName, paylaod = { id: null }, isOpen }) {
isOpen={isOpen}
>
<DialogSuspense>
<ReceiptNumberDialogContent ReceiptNumberId={paylaod.id} />
<ReceiptNumberDialogContent
receiptId={paylaod.id}
onConfirm={handleConfirm}
/>
</DialogSuspense>
</Dialog>
);

View File

@@ -1,27 +1,25 @@
import React from 'react';
import { FormattedMessage as T, useIntl } from 'react-intl';
import classNames from 'classnames';
import { shortcutBox } from 'common/homepageOptions';
import { Icon } from 'components';
function ShortcutBox({ title, iconColor, description }) {
return (
<div className={'shortcut-box'}>
<div className={'shortcut-box__header'}>
<span
className={'header--icon'}
style={{ backgroundColor: `${iconColor}` }}
>
<Icon icon={'clock'} iconSize={24} />
</span>
<span>
<a href={'#'}>
<a href={'#'}>
<div className={'shortcut-box__header'}>
<span
className={'header--icon'}
style={{ backgroundColor: `${iconColor}` }}
>
<Icon icon={'clock'} iconSize={24} />
</span>
<span>
<Icon icon={'arrow-top-right'} iconSize={24} />
</a>
</span>
</div>
<div className={'shortcut-box__title'}>{title}</div>
<div className={'shortcut-box__description'}>{description}</div>
</span>
</div>
<div className={'shortcut-box__title'}>{title}</div>
<div className={'shortcut-box__description'}>{description}</div>
</a>
</div>
);
}

View File

@@ -8,7 +8,7 @@ import {
Position,
} from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl';
import { ErrorMessage, FastField, useFormikContext } from 'formik';
import { ErrorMessage, FastField } from 'formik';
import {
CategoriesSelectList,
Hint,
@@ -26,13 +26,11 @@ import { handleStringChange, inputIntent } from 'utils';
* Item form primary section.
*/
export default function ItemFormPrimarySection() {
const { itemsCategories } = useItemFormContext();
// Item form context.
const { isNewMode, item, itemsCategories } = useItemFormContext();
const nameFieldRef = useRef(null);
// Formik context.
const { values: { type } } = useFormikContext();
useEffect(() => {
// Auto focus item name field once component mount.
if (nameFieldRef.current) {
@@ -87,7 +85,7 @@ export default function ItemFormPrimarySection() {
form.setFieldValue('type', _value);
})}
selectedValue={value}
disabled={type === 'inventory'}
disabled={!isNewMode && item.type === 'inventory'}
>
<Radio label={<T id={'service'} />} value="service" />
<Radio label={<T id={'non_inventory'} />} value="non-inventory" />

View File

@@ -58,9 +58,8 @@ export default function ReferenceNumberForm({
tincidunt porta quam,
</p>
<Row>
{/* prefix */}
<Col>
{/* ------------- Prefix ------------- */}
<Col xs={6}>
<FormGroup
label={<T id={'prefix'} />}
className={'form-group--'}
@@ -75,8 +74,9 @@ export default function ReferenceNumberForm({
/>
</FormGroup>
</Col>
{/* next_number */}
<Col>
{/* ------------- Next number ------------- */}
<Col xs={6}>
<FormGroup
label={<T id={'next_number'} />}
className={'form-group--'}

View File

@@ -16,6 +16,7 @@ import EstimateFormHeader from './EstimateFormHeader';
import EstimateItemsEntriesField from './EstimateItemsEntriesField';
import EstimateFloatingActions from './EstimateFloatingActions';
import EstimateFormFooter from './EstimateFormFooter';
import EstimateFormDialogs from './EstimateFormDialogs';
import withSettings from 'containers/Settings/withSettings';
@@ -158,12 +159,11 @@ function EstimateForm({
>
<Form>
<EstimateFormHeader />
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
<EstimateItemsEntriesField />
</div>
<EstimateItemsEntriesField />
<EstimateFormFooter />
<EstimateFloatingActions />
<EstimateFormDialogs />
</Form>
</Formik>
</div>

View File

@@ -0,0 +1,28 @@
import React from 'react';
import { useFormikContext } from 'formik';
import EstimateNumberDialog from 'containers/Dialogs/EstimateNumberDialog';
import { transactionNumber } from 'utils';
/**
* Estimate form dialogs.
*/
export default function EstimateFormDialogs() {
const { setFieldValue } = useFormikContext();
// Update the form once the invoice number form submit confirm.
const handleEstimateNumberFormConfirm = (values) => {
setFieldValue(
'estimate_number',
transactionNumber(values.number_prefix, values.next_number),
);
};
return (
<>
<EstimateNumberDialog
dialogName={'estimate-number-form'}
onConfirm={handleEstimateNumberFormConfirm}
/>
</>
);
}

View File

@@ -32,7 +32,7 @@ function EstimateFormHeader({
}) {
const { customers } = useEstimateFormContext();
const handleEstimateNumberChange = () => {
const handleEstimateNumberBtnClick = () => {
openDialog('estimate-number-form', {});
};
@@ -135,7 +135,7 @@ function EstimateFormHeader({
/>
<InputPrependButton
buttonProps={{
onClick: handleEstimateNumberChange,
onClick: handleEstimateNumberBtnClick,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}

View File

@@ -1,5 +1,7 @@
import React from 'react';
import { FastField } from 'formik';
import classNames from 'classnames';
import { CLASSES } from 'common/classes';
import ItemsEntriesTable from 'containers/Entries/ItemsEntriesTable';
import { useEstimateFormContext } from './EstimateFormProvider';
@@ -10,18 +12,20 @@ export default function EstimateFormItemsEntriesField() {
const { items } = useEstimateFormContext();
return (
<FastField name={'entries'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<ItemsEntriesTable
entries={value}
onUpdateData={(entries) => {
form.setFieldValue('entries', entries);
}}
items={items}
errors={error}
linesNumber={4}
/>
)}
</FastField>
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
<FastField name={'entries'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<ItemsEntriesTable
entries={value}
onUpdateData={(entries) => {
form.setFieldValue('entries', entries);
}}
items={items}
errors={error}
linesNumber={4}
/>
)}
</FastField>
</div>
);
}

View File

@@ -15,6 +15,7 @@ import InvoiceFormHeader from './InvoiceFormHeader';
import InvoiceItemsEntriesEditorField from './InvoiceItemsEntriesEditorField';
import InvoiceFloatingActions from './InvoiceFloatingActions';
import InvoiceFormFooter from './InvoiceFormFooter';
import InvoiceFormDialogs from './InvoiceFormDialogs';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withMediaActions from 'containers/Media/withMediaActions';
@@ -159,6 +160,7 @@ function InvoiceForm({
</div>
<InvoiceFormFooter />
<InvoiceFloatingActions />
<InvoiceFormDialogs />
</Form>
</Formik>
</div>

View File

@@ -0,0 +1,27 @@
import React from 'react';
import InvoiceNumberDialog from 'containers/Dialogs/InvoiceNumberDialog';
import { useFormikContext } from 'formik';
import { transactionNumber } from 'utils';
/**
* Invoice form dialogs.
*/
export default function InvoiceFormDialogs() {
const { setFieldValue } = useFormikContext();
// Update the form once the invoice number form submit confirm.
const handleInvoiceNumberFormConfirm = (values) => {
setFieldValue(
'invoice_no',
transactionNumber(values.number_prefix, values.next_number),
);
};
return (
<>
<InvoiceNumberDialog
dialogName={'invoice-number-form'}
onConfirm={handleInvoiceNumberFormConfirm}
/>
</>
);
}

View File

@@ -6,6 +6,7 @@ import {
useCustomers,
useCreateInvoice,
useEditInvoice,
useSettingsInvoices,
} from 'hooks/query';
const InvoiceFormContext = createContext();
@@ -14,7 +15,7 @@ const InvoiceFormContext = createContext();
* Accounts chart data provider.
*/
function InvoiceFormProvider({ invoiceId, ...props }) {
const { data: invoice, isFetching: isInvoiceLoading } = useInvoice(
const { data: invoice, isLoading: isInvoiceLoading } = useInvoice(
invoiceId,
{
enabled: !!invoiceId,
@@ -24,21 +25,24 @@ function InvoiceFormProvider({ invoiceId, ...props }) {
// Handle fetching the items table based on the given query.
const {
data: { items },
isFetching: isItemsLoading,
isLoading: isItemsLoading,
} = useItems();
// Handle fetch customers data table or list
const {
data: { customers },
isFetching: isCustomersLoading,
isLoading: isCustomersLoading,
} = useCustomers();
// Handle fetching settings.
const { isLoading: isSettingsLoading } = useSettingsInvoices();
// Create and edit invoice mutations.
const { mutateAsync: createInvoiceMutate } = useCreateInvoice();
const { mutateAsync: editInvoiceMutate } = useEditInvoice();
// Form submit payload.
const [submitPayload, setSubmitPayload] = useState({});
const [submitPayload, setSubmitPayload] = useState();
// Detarmines whether the form in new mode.
const isNewMode = !invoiceId;
@@ -53,16 +57,22 @@ function InvoiceFormProvider({ invoiceId, ...props }) {
isInvoiceLoading,
isItemsLoading,
isCustomersLoading,
isSettingsLoading,
createInvoiceMutate,
editInvoiceMutate,
setSubmitPayload,
isNewMode
isNewMode,
};
return (
<DashboardInsider
loading={isInvoiceLoading || isItemsLoading || isCustomersLoading}
loading={
isInvoiceLoading ||
isItemsLoading ||
isCustomersLoading ||
isSettingsLoading
}
name={'invoice-form'}
>
<InvoiceFormContext.Provider value={provider} {...props} />

View File

@@ -12,6 +12,7 @@ import PaymentReceiveFormBody from './PaymentReceiveFormBody';
import PaymentReceiveFloatingActions from './PaymentReceiveFloatingActions';
import PaymentReceiveFormFooter from './PaymentReceiveFormFooter';
import PaymentReceiveFormAlerts from './PaymentReceiveFormAlerts';
import PaymentReceiveFormDialogs from './PaymentReceiveFormDialogs';
import { PaymentReceiveInnerProvider } from './PaymentReceiveInnerProvider';
import withSettings from 'containers/Settings/withSettings';
@@ -164,7 +165,9 @@ function PaymentReceiveForm({
<PaymentReceiveFormFooter />
<PaymentReceiveFloatingActions />
{/* Alerts & Dialogs */}
<PaymentReceiveFormAlerts />
<PaymentReceiveFormDialogs />
</PaymentReceiveInnerProvider>
</Form>
</Formik>

View File

@@ -0,0 +1,27 @@
import React from 'react';
import { useFormikContext } from 'formik';
import PaymentReceiveNumberDialog from 'containers/Dialogs/PaymentReceiveNumberDialog';
import { transactionNumber } from 'utils';
/**
* Payment receive form dialogs.
*/
export default function PaymentReceiveFormDialogs() {
const { setFieldValue } = useFormikContext();
const handleUpdatePaymentNumber = (values) => {
setFieldValue(
'payment_receive_no',
transactionNumber(values.number_prefix, values.next_number),
);
};
return (
<>
<PaymentReceiveNumberDialog
dialogName={'payment-receive-number-form'}
onConfirm={handleUpdatePaymentNumber}
/>
</>
);
}

View File

@@ -32,7 +32,7 @@ import {
Money,
} from 'components';
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withSettings from 'containers/Settings/withSettings';
import { amountPaymentEntries, fullAmountPaymentEntries } from './utils';
@@ -41,7 +41,12 @@ import { toSafeInteger } from 'lodash';
/**
* Payment receive header fields.
*/
function PaymentReceiveHeaderFields({ baseCurrency }) {
function PaymentReceiveHeaderFields({
baseCurrency,
// #withDialogActions
openDialog,
}) {
// Payment receive form context.
const { customers, accounts, isNewMode } = usePaymentReceiveFormContext();
@@ -73,6 +78,12 @@ function PaymentReceiveHeaderFields({ baseCurrency }) {
setFieldValue('entries', newEntries);
};
// Handle click open payment receive number dialog.
const handleClickOpenDialog = () => {
openDialog('payment-receive-number-form')
};
return (
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
{/* ------------- Customer name ------------- */}
@@ -185,7 +196,7 @@ function PaymentReceiveHeaderFields({ baseCurrency }) {
/>
<InputPrependButton
buttonProps={{
// onClick: handlePaymentReceiveNumberChange,
onClick: handleClickOpenDialog,
icon: <Icon icon={'settings-18'} />,
}}
tooltip={true}
@@ -253,4 +264,5 @@ export default compose(
withSettings(({ organizationSettings }) => ({
baseCurrency: organizationSettings?.baseCurrency,
})),
withDialogActions
)(PaymentReceiveHeaderFields);

View File

@@ -21,6 +21,7 @@ import ReceiptFromHeader from './ReceiptFormHeader';
import ReceiptItemsEntriesEditor from './ReceiptItemsEntriesEditor';
import ReceiptFormFloatingActions from './ReceiptFormFloatingActions';
import ReceiptFormFooter from './ReceiptFormFooter';
import ReceiptFormDialogs from './ReceiptFormDialogs';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withSettings from 'containers/Settings/withSettings';
@@ -170,6 +171,8 @@ function ReceiptForm({
<ReceiptItemsEntriesEditor />
<ReceiptFormFooter />
<ReceiptFormFloatingActions />
<ReceiptFormDialogs />
</Form>
</Formik>
</div>

View File

@@ -0,0 +1,28 @@
import React from 'react';
import { useFormikContext } from 'formik';
import ReceiptNumberDialog from 'containers/Dialogs/ReceiptNumberDialog';
import { transactionNumber } from 'utils';
/**
* Receipt form dialogs.
*/
export default function ReceiptFormDialogs() {
const { setFieldValue } = useFormikContext();
// Update the form once the receipt number form submit confirm.
const handleReceiptNumberFormConfirm = (values) => {
setFieldValue(
'receipt_number',
transactionNumber(values.number_prefix, values.next_number),
);
};
return (
<>
<ReceiptNumberDialog
dialogName={'receipt-number-form'}
onConfirm={handleReceiptNumberFormConfirm}
/>
</>
);
}

View File

@@ -39,7 +39,7 @@ function ReceiptFormProvider({ receiptId, ...props }) {
} = useItems();
// Fetch receipt settings.
const { isFetching: isSettingLoading } = useSettings();
const { isLoading: isSettingLoading } = useSettings();
const { mutateAsync: createReceiptMutate } = useCreateReceipt();
const { mutateAsync: editReceiptMutate } = useEditReceipt();

View File

@@ -11,9 +11,11 @@ export function useCreateBill(props) {
const apiRequest = useApiRequest();
return useMutation((values) => apiRequest.post('purchases/bills', values), {
onSuccess: () => {
onSuccess: (res, values) => {
queryClient.invalidateQueries('BILLS');
queryClient.invalidateQueries('BILL');
queryClient.invalidateQueries(['VENDORS']);
queryClient.invalidateQueries(['VENDOR', values.vendor_id]);
},
...props,
});
@@ -29,9 +31,11 @@ export function useEditBill(props) {
return useMutation(
([id, values]) => apiRequest.post(`purchases/bills/${id}`, values),
{
onSuccess: () => {
onSuccess: (res, [id, values]) => {
queryClient.invalidateQueries('BILLS');
queryClient.invalidateQueries('BILL');
queryClient.invalidateQueries(['VENDORS']);
queryClient.invalidateQueries(['VENDOR', values.vendor_id]);
},
...props,
},
@@ -46,9 +50,10 @@ export function useDeleteBill(props) {
const apiRequest = useApiRequest();
return useMutation((id) => apiRequest.delete(`purchases/bills/${id}`), {
onSuccess: () => {
onSuccess: (res, id) => {
queryClient.invalidateQueries('BILLS');
queryClient.invalidateQueries('BILL');
queryClient.invalidateQueries(['VENDORS']);
},
...props,
});
@@ -122,6 +127,7 @@ export function useOpenBill(props) {
{
onSuccess: () => {
queryClient.invalidateQueries('BILLS');
queryClient.invalidateQueries(['VENDORS']);
},
...props,
},

View File

@@ -13,6 +13,7 @@ export function useCreateEstimate(props) {
return useMutation((values) => apiRequest.post('sales/estimates', values), {
onSuccess: () => {
queryClient.invalidateQueries('SALE_ESTIMATES');
queryClient.invalidateQueries(['SETTINGS', 'ESTIMATES']);
},
...props,
});

View File

@@ -10,7 +10,7 @@ export const useAuthInviteAccept = (props) => {
return useMutation(
([values, token]) => apiRequest.post(`invite/accept/${token}`, values),
props,
)
);
}
/**

View File

@@ -11,8 +11,11 @@ export function useCreateInvoice(props) {
const apiRequest = useApiRequest();
return useMutation((values) => apiRequest.post('sales/invoices', values), {
onSuccess: () => {
onSuccess: (values) => {
queryClient.invalidateQueries('SALE_INVOICES');
queryClient.invalidateQueries(['SETTINGS', 'INVOICES']);
queryClient.invalidateQueries('CUSTOMERS');
queryClient.invalidateQueries(['CUSTOMER', values.customer_id]);
},
...props,
});
@@ -28,9 +31,11 @@ export function useEditInvoice(props) {
return useMutation(
([id, values]) => apiRequest.post(`sales/invoices/${id}`, values),
{
onSuccess: (res, id) => {
onSuccess: (res, [id, values]) => {
queryClient.invalidateQueries('SALE_INVOICES');
queryClient.invalidateQueries(['SALE_INVOICE', id]);
queryClient.invalidateQueries('CUSTOMERS');
queryClient.invalidateQueries(['CUSTOMER', values.customer_id]);
},
...props,
},
@@ -48,6 +53,7 @@ export function useDeleteInvoice(props) {
onSuccess: (res, id) => {
queryClient.invalidateQueries('SALE_INVOICES');
queryClient.invalidateQueries(['SALE_INVOICE', id]);
queryClient.invalidateQueries('CUSTOMERS');
},
...props,
});
@@ -97,6 +103,7 @@ export function useDeliverInvoice(props) {
onSuccess: (res, id) => {
queryClient.invalidateQueries('SALE_INVOICES');
queryClient.invalidateQueries(['SALE_INVOICE', id]);
queryClient.invalidateQueries('CUSTOMERS');
},
...props,
});

View File

@@ -3,34 +3,6 @@ import { useDispatch } from 'react-redux';
import useApiRequest from '../useRequest';
import t from 'store/types';
/**
* Retrieve settings.
*/
export function useSettings(query, props) {
const dispatch = useDispatch();
const apiRequest = useApiRequest();
const settings = useQuery(
['SETTINGS', query],
async () => {
const {
data: { settings },
} = await apiRequest.get('settings', { params: query });
return settings;
},
{
initialData: [],
...props,
},
);
dispatch({
type: t.SETTING_SET,
options: settings.data,
});
return settings;
}
/**
* Saves the settings.
*/
@@ -45,3 +17,90 @@ export function useSaveSettings(props) {
...props,
});
}
function useSettingsQuery(key, query, props) {
const dispatch = useDispatch();
const apiRequest = useApiRequest();
return useQuery(
key,
() => apiRequest.get('settings', { params: query }),
{
select: (res) => res.data.settings,
initialDataUpdatedAt: 0,
initialData: {
data: {
settings: [],
},
},
onSuccess: (settings) => {
dispatch({ type: t.SETTING_SET, options: settings });
},
...props,
},
);
}
/**
* Retrieve the all settings of the organization.
*/
export function useSettings() {
return useSettingsQuery(['SETTINGS', 'ALL'], {});
}
/**
* Retrieve invoices settings.
*/
export function useSettingsInvoices(props) {
return useSettingsQuery(
['SETTINGS', 'INVOICES'],
{ group: 'sale_invoices' },
props,
);
}
/**
* Retrieve invoices settings.
*/
export function useSettingsEstimates(props) {
return useSettingsQuery(
['SETTINGS', 'ESTIMATES'],
{ group: 'sale_estimates' },
props,
);
}
/**
* Retrieve payment receives settings.
*/
export function useSettingsPaymentReceives(props) {
return useSettingsQuery(
['SETTINGS', 'PAYMENT_RECEIVES'],
{ group: 'payment_receives' },
props,
);
}
/**
* Retrieve sale receipts settings.
* @param {*} props
*/
export function useSettingsReceipts(props) {
return useSettingsQuery(
['SETTINGS', 'RECEIPTS'],
{ group: 'sale_receipts' },
props,
);
}
/**
* Retrieve sale receipts settings.
* @param {*} props
*/
export function useSettingsManualJournals(props) {
return useSettingsQuery(
['SETTINGS', 'MANUAL_JOURNALS'],
{ group: 'sale_receipts' },
props,
);
}

View File

@@ -1,9 +1,10 @@
.homepage {
&__container {
display: flex;
height: 100%;
margin: 22px 32px;
background-color: transparent;
align-items: flex-start;
.shortcut-boxes {
flex: 2;
@@ -12,23 +13,27 @@
background-color: #fff;
border-bottom: 1px solid #d2dce2;
border-right: 1px solid #d2dce2;
max-width: 800px;
.shortcut-box {
width: 50%;
// height: 174px;
padding: 16px;
border-left: 1px solid #d2dce2;
border-top: 1px solid #d2dce2;
background-color: transparent;
transition: 0.2s;
a{
text-decoration: none;
}
&__header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 14px;
span > a {
span {
color: #d6d6e0;
}
.header--icon {
@@ -62,7 +67,7 @@
display: flex;
flex-direction: column;
padding-bottom: 32px;
width: 100%;
max-width: 400px;
&__title {
font-size: 16px;

View File

@@ -34,7 +34,7 @@ $sidebar-width: 100%;
$sidebar-menu-item-color: rgb(255, 255, 255);
$sidebar-menu-item-color-active: rgb(255, 255, 255);
$sidebar-popover-submenu-bg: rgb(1, 20, 62);
$sidebar-menu-label-color: rgba(255, 255, 255, 0.45);
$sidebar-menu-label-color: rgba(255, 255, 255, 0.5);
$sidebar-submenu-item-color: rgba(255, 255, 255, 0.85);
$sidebar-submenu-item-hover-color: rgb(255, 255, 255);
$sidebar-logo-opacity: 0.5;