fix(preferences): currencies preferences.

This commit is contained in:
a.bouhuolia
2021-03-24 13:09:56 +02:00
parent 542110fdf3
commit ac5e88f558
16 changed files with 346 additions and 285 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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