mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 15:20:34 +00:00
fix(preferences): currencies preferences.
This commit is contained in:
@@ -2,7 +2,9 @@ import React from 'react';
|
|||||||
import { Classes, FormGroup, InputGroup } from '@blueprintjs/core';
|
import { Classes, FormGroup, InputGroup } from '@blueprintjs/core';
|
||||||
import { FastField } from 'formik';
|
import { FastField } from 'formik';
|
||||||
import { FormattedMessage as T } from 'react-intl';
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
import { useCurrencyFormContext } from './CurrencyFormProvider';
|
import { useCurrencyFormContext } from './CurrencyFormProvider';
|
||||||
import { ErrorMessage, FieldRequiredHint, ListSelect } from 'components';
|
import { ErrorMessage, FieldRequiredHint, ListSelect } from 'components';
|
||||||
|
|
||||||
@@ -25,7 +27,7 @@ export default function CurrencyFormFields() {
|
|||||||
field: { value },
|
field: { value },
|
||||||
meta: { error, touched },
|
meta: { error, touched },
|
||||||
}) => (
|
}) => (
|
||||||
<FormGroup label={'Currency code'}>
|
<FormGroup label={'Currency code'} className={classNames(CLASSES.FILL, 'form-group--type')}>
|
||||||
<ListSelect
|
<ListSelect
|
||||||
items={currenciesOptions}
|
items={currenciesOptions}
|
||||||
selectedItemProp={'currency_code'}
|
selectedItemProp={'currency_code'}
|
||||||
@@ -38,6 +40,7 @@ export default function CurrencyFormFields() {
|
|||||||
setFieldValue('currency_sign', currency.symbol);
|
setFieldValue('currency_sign', currency.symbol);
|
||||||
}}
|
}}
|
||||||
disabled={isEditMode}
|
disabled={isEditMode}
|
||||||
|
popoverProps={{ minimal: true }}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ function CurrencyFormDialog({
|
|||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
canEscapeKeyClose={true}
|
canEscapeKeyClose={true}
|
||||||
style={{ width: '450px' }}
|
style={{ width: '400px' }}
|
||||||
>
|
>
|
||||||
<DialogSuspense>
|
<DialogSuspense>
|
||||||
<CurrencyFormDialogContent
|
<CurrencyFormDialogContent
|
||||||
|
|||||||
@@ -11,11 +11,13 @@ import {
|
|||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { AccountsSelectList, FieldRequiredHint } from 'components';
|
import { AccountsSelectList, FieldRequiredHint } from 'components';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import { ACCOUNT_PARENT_TYPE } from 'common/accountTypes';
|
|
||||||
import { handleStringChange, inputIntent } from 'utils';
|
import { handleStringChange, inputIntent } from 'utils';
|
||||||
|
|
||||||
import { useAccountantFormContext } from './AccountantFormProvider';
|
import { useAccountantFormContext } from './AccountantFormProvider';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accountant form.
|
||||||
|
*/
|
||||||
export default function AccountantForm() {
|
export default function AccountantForm() {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { Formik } from 'formik';
|
import { Formik } from 'formik';
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import { CLASSES } from 'common/classes';
|
|
||||||
import { AppToaster } from 'components';
|
import { AppToaster } from 'components';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
@@ -50,6 +49,7 @@ function AccountantFormPage({
|
|||||||
|
|
||||||
const handleFormSubmit = (values, { setSubmitting }) => {
|
const handleFormSubmit = (values, { setSubmitting }) => {
|
||||||
const options = transformToOptions(values);
|
const options = transformToOptions(values);
|
||||||
|
|
||||||
setSubmitting(true);
|
setSubmitting(true);
|
||||||
const onSuccess = () => {
|
const onSuccess = () => {
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
@@ -68,21 +68,12 @@ function AccountantFormPage({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<Formik
|
||||||
className={classNames(
|
initialValues={initialValues}
|
||||||
CLASSES.PREFERENCES_PAGE_INSIDE_CONTENT,
|
validationSchema={AccountantSchema}
|
||||||
CLASSES.PREFERENCES_PAGE_INSIDE_CONTENT_ACCOUNTANT,
|
onSubmit={handleFormSubmit}
|
||||||
)}
|
component={AccountantForm}
|
||||||
>
|
/>
|
||||||
<div className={classNames(CLASSES.CARD)}>
|
|
||||||
<Formik
|
|
||||||
initialValues={initialValues}
|
|
||||||
validationSchema={AccountantSchema}
|
|
||||||
onSubmit={handleFormSubmit}
|
|
||||||
component={AccountantForm}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { LoadingIndicator } from 'components';
|
import classNames from 'classnames';
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
import { useAccounts, useSaveSettings, useSettings } from 'hooks/query';
|
import { useAccounts, useSaveSettings, useSettings } from 'hooks/query';
|
||||||
|
import PreferencesPageLoader from '../PreferencesPageLoader';
|
||||||
|
|
||||||
const AccountantFormContext = React.createContext();
|
const AccountantFormContext = React.createContext();
|
||||||
|
|
||||||
@@ -24,10 +26,23 @@ function AccountantFormProvider({ ...props }) {
|
|||||||
saveSettingMutate,
|
saveSettingMutate,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isLoading = isSettingsLoading || isAccountsLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoadingIndicator loading={isSettingsLoading || isAccountsLoading}>
|
<div
|
||||||
<AccountantFormContext.Provider value={provider} {...props} />
|
className={classNames(
|
||||||
</LoadingIndicator>
|
CLASSES.PREFERENCES_PAGE_INSIDE_CONTENT,
|
||||||
|
CLASSES.PREFERENCES_PAGE_INSIDE_CONTENT_ACCOUNTANT,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className={classNames(CLASSES.CARD)}>
|
||||||
|
{isLoading ? (
|
||||||
|
<PreferencesPageLoader />
|
||||||
|
) : (
|
||||||
|
<AccountantFormContext.Provider value={provider} {...props} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,220 +36,224 @@ export default function PreferencesGeneralForm({}) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Form>
|
<Form>
|
||||||
<FastField name={'name'}>
|
<FastField name={'name'}>
|
||||||
{({ field, meta: { error, touched } }) => (
|
{({ field, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'organization_name'} />}
|
label={<T id={'organization_name'} />}
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
inline={true}
|
inline={true}
|
||||||
intent={inputIntent({ error, touched })}
|
intent={inputIntent({ error, touched })}
|
||||||
className={'form-group--org-name'}
|
className={'form-group--org-name'}
|
||||||
helperText={'Shown on sales forms and purchase orders.'}
|
helperText={'Shown on sales forms and purchase orders.'}
|
||||||
>
|
>
|
||||||
<InputGroup medium={'true'} {...field} />
|
<InputGroup medium={'true'} {...field} />
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
<FastField name={'financial_date_start'}>
|
<FastField name={'financial_date_start'}>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'financial_starting_date'} />}
|
label={<T id={'financial_starting_date'} />}
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
inline={true}
|
inline={true}
|
||||||
intent={inputIntent({ error, touched })}
|
intent={inputIntent({ error, touched })}
|
||||||
className={classNames('form-group--select-list', CLASSES.FILL)}
|
className={classNames('form-group--select-list', CLASSES.FILL)}
|
||||||
helperText={'For reporting, you can specify any month as the start of your financial year (also called your financial reporting year or accounting year).'}
|
helperText={
|
||||||
>
|
'For reporting, you can specify any month as the start of your financial year (also called your financial reporting year or accounting year).'
|
||||||
<DateInput
|
|
||||||
{...momentFormatter('MMMM Do YYYY')}
|
|
||||||
value={tansformDateValue(value)}
|
|
||||||
onChange={handleDateChange((formattedDate) => {
|
|
||||||
form.setFieldValue('financial_date_start', formattedDate);
|
|
||||||
})}
|
|
||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
|
||||||
</FastField>
|
|
||||||
|
|
||||||
<FastField name={'industry'}>
|
|
||||||
{({ field, meta: { error, touched } }) => (
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'organization_industry'} />}
|
|
||||||
inline={true}
|
|
||||||
intent={inputIntent({ error, touched })}
|
|
||||||
helperText={<ErrorMessage name="industry" />}
|
|
||||||
className={'form-group--org-industry'}
|
|
||||||
>
|
|
||||||
<InputGroup medium={'true'} {...field} />
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
|
||||||
</FastField>
|
|
||||||
|
|
||||||
<FastField name={'location'}>
|
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'business_location'} />}
|
|
||||||
className={classNames(
|
|
||||||
'form-group--business-location',
|
|
||||||
CLASSES.FILL,
|
|
||||||
)}
|
|
||||||
inline={true}
|
|
||||||
helperText={<ErrorMessage name="location" />}
|
|
||||||
intent={inputIntent({ error, touched })}
|
|
||||||
>
|
|
||||||
<ListSelect
|
|
||||||
items={countriesOptions}
|
|
||||||
onItemSelect={({ value }) => {
|
|
||||||
form.setFieldValue('location', value);
|
|
||||||
}}
|
|
||||||
selectedItem={value}
|
|
||||||
selectedItemProp={'value'}
|
|
||||||
defaultText={<T id={'select_business_location'} />}
|
|
||||||
textProp={'name'}
|
|
||||||
popoverProps={{ minimal: true }}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
|
||||||
</FastField>
|
|
||||||
|
|
||||||
<FastField name={'base_currency'}>
|
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'base_currency'} />}
|
|
||||||
labelInfo={<FieldRequiredHint />}
|
|
||||||
className={classNames('form-group--base-currency', CLASSES.FILL)}
|
|
||||||
inline={true}
|
|
||||||
intent={inputIntent({ error, touched })}
|
|
||||||
helperText={"You can't change the base currency as there are transactions recorded in your organization."}
|
|
||||||
>
|
|
||||||
<ListSelect
|
|
||||||
items={currencies}
|
|
||||||
onItemSelect={(currency) => {
|
|
||||||
form.setFieldValue('base_currency', currency.code);
|
|
||||||
}}
|
|
||||||
selectedItem={value}
|
|
||||||
selectedItemProp={'code'}
|
|
||||||
defaultText={<T id={'select_base_currency'} />}
|
|
||||||
textProp={'name'}
|
|
||||||
labelProp={'code'}
|
|
||||||
popoverProps={{ minimal: true }}
|
|
||||||
/>
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
|
||||||
</FastField>
|
|
||||||
|
|
||||||
<FastField name={'fiscal_year'}>
|
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'fiscal_year'} />}
|
|
||||||
labelInfo={<FieldRequiredHint />}
|
|
||||||
className={classNames('form-group--fiscal-year', CLASSES.FILL)}
|
|
||||||
inline={true}
|
|
||||||
helperText={<ErrorMessage name="fiscal_year" />}
|
|
||||||
intent={inputIntent({ error, touched })}
|
|
||||||
>
|
|
||||||
<ListSelect
|
|
||||||
items={fiscalYearOptions}
|
|
||||||
onItemSelect={({ value }) =>
|
|
||||||
form.setFieldValue('fiscal_year', value)
|
|
||||||
}
|
}
|
||||||
selectedItem={value}
|
>
|
||||||
selectedItemProp={'value'}
|
<DateInput
|
||||||
defaultText={<T id={'select_fiscal_year'} />}
|
{...momentFormatter('MMMM Do YYYY')}
|
||||||
textProp={'name'}
|
value={tansformDateValue(value)}
|
||||||
popoverProps={{ minimal: true }}
|
onChange={handleDateChange((formattedDate) => {
|
||||||
/>
|
form.setFieldValue('financial_date_start', formattedDate);
|
||||||
</FormGroup>
|
})}
|
||||||
)}
|
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
||||||
</FastField>
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
|
||||||
<FastField name={'language'}>
|
<FastField name={'industry'}>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ field, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'language'} />}
|
label={<T id={'organization_industry'} />}
|
||||||
labelInfo={<FieldRequiredHint />}
|
inline={true}
|
||||||
inline={true}
|
intent={inputIntent({ error, touched })}
|
||||||
className={classNames('form-group--language', CLASSES.FILL)}
|
helperText={<ErrorMessage name="industry" />}
|
||||||
intent={inputIntent({ error, touched })}
|
className={'form-group--org-industry'}
|
||||||
helperText={<ErrorMessage name="language" />}
|
>
|
||||||
>
|
<InputGroup medium={'true'} {...field} />
|
||||||
<ListSelect
|
</FormGroup>
|
||||||
items={languages}
|
)}
|
||||||
selectedItemProp={'value'}
|
</FastField>
|
||||||
textProp={'name'}
|
|
||||||
defaultText={<T id={'select_language'} />}
|
<FastField name={'location'}>
|
||||||
selectedItem={value}
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
onItemSelect={(item) =>
|
<FormGroup
|
||||||
form.setFieldValue('language', item.value)
|
label={<T id={'business_location'} />}
|
||||||
|
className={classNames(
|
||||||
|
'form-group--business-location',
|
||||||
|
CLASSES.FILL,
|
||||||
|
)}
|
||||||
|
inline={true}
|
||||||
|
helperText={<ErrorMessage name="location" />}
|
||||||
|
intent={inputIntent({ error, touched })}
|
||||||
|
>
|
||||||
|
<ListSelect
|
||||||
|
items={countriesOptions}
|
||||||
|
onItemSelect={({ value }) => {
|
||||||
|
form.setFieldValue('location', value);
|
||||||
|
}}
|
||||||
|
selectedItem={value}
|
||||||
|
selectedItemProp={'value'}
|
||||||
|
defaultText={<T id={'select_business_location'} />}
|
||||||
|
textProp={'name'}
|
||||||
|
popoverProps={{ minimal: true }}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
|
||||||
|
<FastField name={'base_currency'}>
|
||||||
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'base_currency'} />}
|
||||||
|
labelInfo={<FieldRequiredHint />}
|
||||||
|
className={classNames('form-group--base-currency', CLASSES.FILL)}
|
||||||
|
inline={true}
|
||||||
|
intent={inputIntent({ error, touched })}
|
||||||
|
helperText={
|
||||||
|
"You can't change the base currency as there are transactions recorded in your organization."
|
||||||
}
|
}
|
||||||
popoverProps={{ minimal: true }}
|
>
|
||||||
/>
|
<ListSelect
|
||||||
</FormGroup>
|
items={currencies}
|
||||||
)}
|
onItemSelect={(currency) => {
|
||||||
</FastField>
|
form.setFieldValue('base_currency', currency.code);
|
||||||
|
}}
|
||||||
|
selectedItem={value}
|
||||||
|
selectedItemProp={'code'}
|
||||||
|
defaultText={<T id={'select_base_currency'} />}
|
||||||
|
textProp={'name'}
|
||||||
|
labelProp={'code'}
|
||||||
|
popoverProps={{ minimal: true }}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
|
||||||
<FastField name={'time_zone'}>
|
<FastField name={'fiscal_year'}>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'time_zone'} />}
|
label={<T id={'fiscal_year'} />}
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
inline={true}
|
className={classNames('form-group--fiscal-year', CLASSES.FILL)}
|
||||||
className={classNames(
|
inline={true}
|
||||||
'form-group--time-zone',
|
helperText={<ErrorMessage name="fiscal_year" />}
|
||||||
CLASSES.FORM_GROUP_LIST_SELECT,
|
intent={inputIntent({ error, touched })}
|
||||||
CLASSES.FILL,
|
>
|
||||||
)}
|
<ListSelect
|
||||||
intent={inputIntent({ error, touched })}
|
items={fiscalYearOptions}
|
||||||
helperText={<ErrorMessage name="time_zone" />}
|
onItemSelect={({ value }) =>
|
||||||
>
|
form.setFieldValue('fiscal_year', value)
|
||||||
<TimezonePicker
|
}
|
||||||
value={value}
|
selectedItem={value}
|
||||||
onChange={(timezone) => {
|
selectedItemProp={'value'}
|
||||||
form.setFieldValue('time_zone', timezone);
|
defaultText={<T id={'select_fiscal_year'} />}
|
||||||
}}
|
textProp={'name'}
|
||||||
valueDisplayFormat="composite"
|
popoverProps={{ minimal: true }}
|
||||||
placeholder={<T id={'select_time_zone'} />}
|
/>
|
||||||
/>
|
</FormGroup>
|
||||||
</FormGroup>
|
)}
|
||||||
)}
|
</FastField>
|
||||||
</FastField>
|
|
||||||
|
|
||||||
<FastField name={'date_format'}>
|
<FastField name={'language'}>
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<FormGroup
|
<FormGroup
|
||||||
label={<T id={'date_format'} />}
|
label={<T id={'language'} />}
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
inline={true}
|
inline={true}
|
||||||
className={classNames('form-group--date-format', CLASSES.FILL)}
|
className={classNames('form-group--language', CLASSES.FILL)}
|
||||||
intent={inputIntent({ error, touched })}
|
intent={inputIntent({ error, touched })}
|
||||||
helperText={<ErrorMessage name="date_format" />}
|
helperText={<ErrorMessage name="language" />}
|
||||||
>
|
>
|
||||||
<ListSelect
|
<ListSelect
|
||||||
items={dateFormatsOptions}
|
items={languages}
|
||||||
onItemSelect={(dateFormat) => {
|
selectedItemProp={'value'}
|
||||||
form.setFieldValue('date_format', dateFormat.value);
|
textProp={'name'}
|
||||||
}}
|
defaultText={<T id={'select_language'} />}
|
||||||
selectedItem={value}
|
selectedItem={value}
|
||||||
selectedItemProp={'value'}
|
onItemSelect={(item) =>
|
||||||
defaultText={<T id={'select_date_format'} />}
|
form.setFieldValue('language', item.value)
|
||||||
textProp={'name'}
|
}
|
||||||
labelProp={'label'}
|
popoverProps={{ minimal: true }}
|
||||||
popoverProps={{ minimal: true }}
|
/>
|
||||||
/>
|
</FormGroup>
|
||||||
</FormGroup>
|
)}
|
||||||
)}
|
</FastField>
|
||||||
</FastField>
|
|
||||||
|
|
||||||
<div className={'card__footer'}>
|
<FastField name={'time_zone'}>
|
||||||
<Button intent={Intent.PRIMARY} type="submit">
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
<T id={'save'} />
|
<FormGroup
|
||||||
</Button>
|
label={<T id={'time_zone'} />}
|
||||||
<Button onClick={handleCloseClick}>
|
labelInfo={<FieldRequiredHint />}
|
||||||
<T id={'close'} />
|
inline={true}
|
||||||
</Button>
|
className={classNames(
|
||||||
</div>
|
'form-group--time-zone',
|
||||||
</Form>
|
CLASSES.FORM_GROUP_LIST_SELECT,
|
||||||
|
CLASSES.FILL,
|
||||||
|
)}
|
||||||
|
intent={inputIntent({ error, touched })}
|
||||||
|
helperText={<ErrorMessage name="time_zone" />}
|
||||||
|
>
|
||||||
|
<TimezonePicker
|
||||||
|
value={value}
|
||||||
|
onChange={(timezone) => {
|
||||||
|
form.setFieldValue('time_zone', timezone);
|
||||||
|
}}
|
||||||
|
valueDisplayFormat="composite"
|
||||||
|
placeholder={<T id={'select_time_zone'} />}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
|
||||||
|
<FastField name={'date_format'}>
|
||||||
|
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||||
|
<FormGroup
|
||||||
|
label={<T id={'date_format'} />}
|
||||||
|
labelInfo={<FieldRequiredHint />}
|
||||||
|
inline={true}
|
||||||
|
className={classNames('form-group--date-format', CLASSES.FILL)}
|
||||||
|
intent={inputIntent({ error, touched })}
|
||||||
|
helperText={<ErrorMessage name="date_format" />}
|
||||||
|
>
|
||||||
|
<ListSelect
|
||||||
|
items={dateFormatsOptions}
|
||||||
|
onItemSelect={(dateFormat) => {
|
||||||
|
form.setFieldValue('date_format', dateFormat.value);
|
||||||
|
}}
|
||||||
|
selectedItem={value}
|
||||||
|
selectedItemProp={'value'}
|
||||||
|
defaultText={<T id={'select_date_format'} />}
|
||||||
|
textProp={'name'}
|
||||||
|
labelProp={'label'}
|
||||||
|
popoverProps={{ minimal: true }}
|
||||||
|
/>
|
||||||
|
</FormGroup>
|
||||||
|
)}
|
||||||
|
</FastField>
|
||||||
|
|
||||||
|
<div className={'card__footer'}>
|
||||||
|
<Button intent={Intent.PRIMARY} type="submit">
|
||||||
|
<T id={'save'} />
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleCloseClick}>
|
||||||
|
<T id={'close'} />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,7 @@ import React, { useEffect } from 'react';
|
|||||||
import { Formik } from 'formik';
|
import { Formik } from 'formik';
|
||||||
import { mapKeys, snakeCase } from 'lodash';
|
import { mapKeys, snakeCase } from 'lodash';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { CLASSES } from 'common/classes';
|
|
||||||
|
|
||||||
import { AppToaster } from 'components';
|
import { AppToaster } from 'components';
|
||||||
import GeneralForm from './GeneralForm';
|
import GeneralForm from './GeneralForm';
|
||||||
@@ -46,6 +44,7 @@ function GeneralFormPage({
|
|||||||
const options = optionsMapToArray(values).map((option) => {
|
const options = optionsMapToArray(values).map((option) => {
|
||||||
return { key: option.key, ...option, group: 'organization' };
|
return { key: option.key, ...option, group: 'organization' };
|
||||||
});
|
});
|
||||||
|
// Handle request success.
|
||||||
const onSuccess = (response) => {
|
const onSuccess = (response) => {
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: 'The general preferences has been saved.',
|
message: 'The general preferences has been saved.',
|
||||||
@@ -54,6 +53,7 @@ function GeneralFormPage({
|
|||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
resetForm();
|
resetForm();
|
||||||
};
|
};
|
||||||
|
// Handle request error.
|
||||||
const onError = (errors) => {
|
const onError = (errors) => {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
};
|
};
|
||||||
@@ -61,21 +61,12 @@ function GeneralFormPage({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<Formik
|
||||||
className={classNames(
|
initialValues={initialValues}
|
||||||
CLASSES.PREFERENCES_PAGE_INSIDE_CONTENT,
|
validationSchema={PreferencesGeneralSchema}
|
||||||
CLASSES.PREFERENCES_PAGE_INSIDE_CONTENT_GENERAL,
|
onSubmit={handleFormSubmit}
|
||||||
)}
|
component={GeneralForm}
|
||||||
>
|
/>
|
||||||
<div className={classNames(CLASSES.CARD)}>
|
|
||||||
<Formik
|
|
||||||
initialValues={initialValues}
|
|
||||||
validationSchema={PreferencesGeneralSchema}
|
|
||||||
onSubmit={handleFormSubmit}
|
|
||||||
component={GeneralForm}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import React, { createContext } from 'react';
|
import React, { createContext } from 'react';
|
||||||
import { LoadingIndicator } from 'components';
|
import classNames from 'classnames';
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
import { useSaveSettings, useSettings } from 'hooks/query';
|
import { useSaveSettings, useSettings } from 'hooks/query';
|
||||||
|
import PreferencesPageLoader from '../PreferencesPageLoader';
|
||||||
|
|
||||||
const GeneralFormContext = createContext();
|
const GeneralFormContext = createContext();
|
||||||
|
|
||||||
@@ -8,8 +10,8 @@ const GeneralFormContext = createContext();
|
|||||||
* General form provider.
|
* General form provider.
|
||||||
*/
|
*/
|
||||||
function GeneralFormProvider({ ...props }) {
|
function GeneralFormProvider({ ...props }) {
|
||||||
//Fetches Organization Settings.
|
// Fetches Organization Settings.
|
||||||
const { isFetching: isSettingsLoading } = useSettings();
|
const { isLoading: isSettingsLoading } = useSettings();
|
||||||
|
|
||||||
// Save Organization Settings.
|
// Save Organization Settings.
|
||||||
const { mutateAsync: saveSettingMutate } = useSaveSettings();
|
const { mutateAsync: saveSettingMutate } = useSaveSettings();
|
||||||
@@ -20,10 +22,23 @@ function GeneralFormProvider({ ...props }) {
|
|||||||
saveSettingMutate,
|
saveSettingMutate,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const loading = isSettingsLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoadingIndicator loading={isSettingsLoading} spinnerSize={28}>
|
<div
|
||||||
<GeneralFormContext.Provider value={provider} {...props} />
|
className={classNames(
|
||||||
</LoadingIndicator>
|
CLASSES.PREFERENCES_PAGE_INSIDE_CONTENT,
|
||||||
|
CLASSES.PREFERENCES_PAGE_INSIDE_CONTENT_GENERAL,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className={classNames(CLASSES.CARD)}>
|
||||||
|
{loading ? (
|
||||||
|
<PreferencesPageLoader />
|
||||||
|
) : (
|
||||||
|
<GeneralFormContext.Provider value={provider} {...props} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import * as Yup from 'yup';
|
import * as Yup from 'yup';
|
||||||
|
|
||||||
const Schema = Yup.object().shape({
|
const Schema = Yup.object().shape({
|
||||||
sell_account: Yup.number().nullable().required(),
|
sell_account: Yup.number().nullable(),
|
||||||
cost_account: Yup.number().nullable().required(),
|
cost_account: Yup.number().nullable(),
|
||||||
inventory_account: Yup.number().nullable().required(),
|
inventory_account: Yup.number().nullable(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ItemPreferencesSchema = Schema;
|
export const ItemPreferencesSchema = Schema;
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { Formik } from 'formik';
|
import { Formik } from 'formik';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { CLASSES } from 'common/classes';
|
|
||||||
import { AppToaster } from 'components';
|
import { AppToaster } from 'components';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { ItemPreferencesSchema } from './Item.schema';
|
import { ItemPreferencesSchema } from './Item.schema';
|
||||||
@@ -19,24 +17,28 @@ import 'style/pages/Preferences/Accounting.scss';
|
|||||||
function ItemFormPage({
|
function ItemFormPage({
|
||||||
// #withSettings
|
// #withSettings
|
||||||
itemsSettings,
|
itemsSettings,
|
||||||
//# withDashboardActions
|
|
||||||
|
// #withDashboardActions
|
||||||
changePreferencesPageTitle,
|
changePreferencesPageTitle,
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const { saveSettingMutate } = useItemFormContext();
|
const { saveSettingMutate } = useItemFormContext();
|
||||||
|
|
||||||
const initialValues = {
|
const initialValues = {
|
||||||
|
sell_account: '',
|
||||||
|
cost_account: '',
|
||||||
|
inventory_account: '',
|
||||||
...transformGeneralSettings(itemsSettings),
|
...transformGeneralSettings(itemsSettings),
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
changePreferencesPageTitle(formatMessage({ id: 'items' }));
|
changePreferencesPageTitle(formatMessage({ id: 'items' }));
|
||||||
}, [changePreferencesPageTitle]);
|
}, [formatMessage, changePreferencesPageTitle]);
|
||||||
|
|
||||||
|
// Handle form submit.
|
||||||
const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
|
const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
|
||||||
const options = optionsMapToArray(values).map((option) => {
|
const options = optionsMapToArray(values)
|
||||||
return { key: option.key, ...option, group: 'items' };
|
.map((option) => ({ ...option, group: 'items' }));
|
||||||
});
|
|
||||||
|
|
||||||
const onSuccess = () => {
|
const onSuccess = () => {
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
@@ -55,21 +57,12 @@ function ItemFormPage({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<Formik
|
||||||
className={classNames(
|
initialValues={initialValues}
|
||||||
CLASSES.PREFERENCES_PAGE_INSIDE_CONTENT,
|
validationSchema={ItemPreferencesSchema}
|
||||||
CLASSES.PREFERENCES_PAGE_INSIDE_CONTENT_ACCOUNTANT,
|
onSubmit={handleFormSubmit}
|
||||||
)}
|
component={ItemForm}
|
||||||
>
|
/>
|
||||||
<div className={classNames(CLASSES.CARD)}>
|
|
||||||
<Formik
|
|
||||||
initialValues={initialValues}
|
|
||||||
validationSchema={ItemPreferencesSchema}
|
|
||||||
onSubmit={handleFormSubmit}
|
|
||||||
component={ItemForm}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import React, { useContext, createContext } from 'react';
|
import React, { useContext, createContext } from 'react';
|
||||||
import { LoadingIndicator } from 'components';
|
import classNames from 'classnames';
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
|
|
||||||
import { useAccounts, useSaveSettings } from 'hooks/query';
|
import { useAccounts, useSaveSettings } from 'hooks/query';
|
||||||
|
import PreferencesPageLoader from '../PreferencesPageLoader';
|
||||||
|
|
||||||
const ItemFormContext = createContext();
|
const ItemFormContext = createContext();
|
||||||
|
|
||||||
@@ -22,10 +24,23 @@ function ItemFormProvider({ ...props }) {
|
|||||||
saveSettingMutate,
|
saveSettingMutate,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isLoading = isAccountsLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoadingIndicator loading={isAccountsLoading}>
|
<div
|
||||||
<ItemFormContext.Provider value={provider} {...props} />
|
className={classNames(
|
||||||
</LoadingIndicator>
|
CLASSES.PREFERENCES_PAGE_INSIDE_CONTENT,
|
||||||
|
CLASSES.PREFERENCES_PAGE_INSIDE_CONTENT_ACCOUNTANT,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className={classNames(CLASSES.CARD)}>
|
||||||
|
{isLoading ? (
|
||||||
|
<PreferencesPageLoader />
|
||||||
|
) : (
|
||||||
|
<ItemFormContext.Provider value={provider} {...props} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
client/src/containers/Preferences/PreferencesPageLoader.js
Normal file
23
client/src/containers/Preferences/PreferencesPageLoader.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ContentLoader from 'react-content-loader';
|
||||||
|
|
||||||
|
export default function PreferencesPageLoader(props) {
|
||||||
|
return (
|
||||||
|
<ContentLoader
|
||||||
|
speed={2}
|
||||||
|
width={400}
|
||||||
|
height={250}
|
||||||
|
viewBox="0 0 400 250"
|
||||||
|
backgroundColor="#f3f3f3"
|
||||||
|
foregroundColor="#e6e6e6"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<rect x="0" y="82" rx="2" ry="2" width="200" height="20" />
|
||||||
|
<rect x="0" y="112" rx="2" ry="2" width="385" height="30" />
|
||||||
|
<rect x="0" y="0" rx="2" ry="2" width="200" height="20" />
|
||||||
|
<rect x="-1" y="30" rx="2" ry="2" width="385" height="30" />
|
||||||
|
<rect x="0" y="164" rx="2" ry="2" width="200" height="20" />
|
||||||
|
<rect x="0" y="194" rx="2" ry="2" width="385" height="30" />
|
||||||
|
</ContentLoader>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
.dialog--currency-form {
|
.dialog--currency-form {
|
||||||
.bp3-dialog-body {
|
.bp3-dialog-body {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
|
||||||
.bp3-form-group.bp3-inline {
|
.bp3-form-group.bp3-inline {
|
||||||
.bp3-label {
|
.bp3-label {
|
||||||
min-width: 140px;
|
min-width: 140px;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default class SettingsController extends BaseController {
|
|||||||
return [
|
return [
|
||||||
body('options').isArray({ min: 1 }),
|
body('options').isArray({ min: 1 }),
|
||||||
body('options.*.key').exists().trim().isLength({ min: 1 }),
|
body('options.*.key').exists().trim().isLength({ min: 1 }),
|
||||||
body('options.*.value').exists().trim().isLength({ min: 1 }),
|
body('options.*.value').exists().trim(),
|
||||||
body('options.*.group').exists().trim().isLength({ min: 1 }),
|
body('options.*.group').exists().trim().isLength({ min: 1 }),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ exports.up = (knex) => {
|
|||||||
const tenancyService = Container.get(TenancyService);
|
const tenancyService = Container.get(TenancyService);
|
||||||
const settings = tenancyService.settings(knex.userParams.tenantId);
|
const settings = tenancyService.settings(knex.userParams.tenantId);
|
||||||
|
|
||||||
|
// Orgnization settings.
|
||||||
|
settings.set({ group: 'organization', key: 'accounting_basis', value: 'accural' });
|
||||||
|
|
||||||
|
// Accounts settings.
|
||||||
|
settings.set({ group: 'accounts', key: 'account_code_unique', value: true });
|
||||||
|
|
||||||
// Manual journals settings.
|
// Manual journals settings.
|
||||||
settings.set({ group: 'manual_journals', key: 'next_number', value: '00001' });
|
settings.set({ group: 'manual_journals', key: 'next_number', value: '00001' });
|
||||||
settings.set({ group: 'manual_journals', key: 'auto_increment', value: true });
|
settings.set({ group: 'manual_journals', key: 'auto_increment', value: true });
|
||||||
|
|||||||
@@ -261,6 +261,7 @@ export default class CurrenciesService implements ICurrenciesService {
|
|||||||
await Currency.query().insert({
|
await Currency.query().insert({
|
||||||
currency_code: currencyMeta.code,
|
currency_code: currencyMeta.code,
|
||||||
currency_name: currencyMeta.name,
|
currency_name: currencyMeta.name,
|
||||||
|
currency_sign: currencyMeta.symbol,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user