chrone: sperate client and server to different repos.

This commit is contained in:
a.bouhuolia
2021-09-21 17:13:53 +02:00
parent e011b2a82b
commit 18df5530c7
10015 changed files with 17686 additions and 97524 deletions

View File

@@ -0,0 +1,123 @@
import React from 'react';
import { Field, ErrorMessage, FastField } from 'formik';
import { FormGroup, InputGroup } from '@blueprintjs/core';
import { inputIntent } from 'utils';
import { Row, Col, MoneyInputGroup } from 'components';
import { FormattedMessage as T } from 'components';
import { useAutofocus } from 'hooks';
import { decrementQuantity } from './utils';
import { toSafeNumber } from 'utils';
/**
* Decrement adjustment fields.
*/
function DecrementAdjustmentFields() {
const decrementFieldRef = useAutofocus();
return (
<Row className={'row--decrement-fields'}>
{/*------------ Quantity on hand -----------*/}
<Col className={'col--quantity-on-hand'}>
<FastField name={'quantity_on_hand'}>
{({ field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'qty_on_hand'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="quantity_on_hand" />}
>
<InputGroup
disabled={true}
medium={'true'}
intent={inputIntent({ error, touched })}
{...field}
/>
</FormGroup>
)}
</FastField>
</Col>
<Col className={'col--sign'}>
<span></span>
</Col>
{/*------------ Decrement -----------*/}
<Col className={'col--decrement'}>
<Field name={'quantity'}>
{({
form: { values, setFieldValue },
field,
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'decrement'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="quantity" />}
fill={true}
>
<MoneyInputGroup
value={field.value}
allowDecimals={false}
allowNegativeValue={true}
inputRef={(ref) => (decrementFieldRef.current = ref)}
onChange={(value) => {
setFieldValue('quantity', value);
}}
onBlurValue={(value) => {
setFieldValue(
'new_quantity',
decrementQuantity(
toSafeNumber(value),
toSafeNumber(values.quantity_on_hand),
),
);
}}
intent={inputIntent({ error, touched })}
/>
</FormGroup>
)}
</Field>
</Col>
<Col className={'col--sign'}>
<span>=</span>
</Col>
{/*------------ New quantity -----------*/}
<Col className={'col--quantity'}>
<Field name={'new_quantity'}>
{({
form: { values, setFieldValue },
field,
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'new_quantity'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="new_quantity" />}
>
<MoneyInputGroup
value={field.value}
allowDecimals={false}
allowNegativeValue={true}
onChange={(value) => {
setFieldValue('new_quantity', value);
}}
onBlurValue={(value) => {
setFieldValue(
'quantity',
decrementQuantity(
toSafeNumber(value),
toSafeNumber(values.quantity_on_hand),
),
);
}}
intent={inputIntent({ error, touched })}
/>
</FormGroup>
)}
</Field>
</Col>
</Row>
);
}
export default DecrementAdjustmentFields;

View File

@@ -0,0 +1,145 @@
import React from 'react';
import { Field, FastField, ErrorMessage } from 'formik';
import { FormGroup, InputGroup } from '@blueprintjs/core';
import { useAutofocus } from 'hooks';
import { Row, Col, MoneyInputGroup } from 'components';
import { inputIntent, toSafeNumber } from 'utils';
import { FormattedMessage as T } from 'components';
import { decrementQuantity, incrementQuantity } from './utils';
export default function IncrementAdjustmentFields() {
const incrementFieldRef = useAutofocus();
return (
<Row>
{/*------------ Quantity on hand -----------*/}
<Col className={'col--quantity-on-hand'}>
<FastField name={'quantity_on_hand'}>
{({ field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'qty_on_hand'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="quantity_on_hand" />}
>
<InputGroup
disabled={true}
medium={'true'}
intent={inputIntent({ error, touched })}
{...field}
/>
</FormGroup>
)}
</FastField>
</Col>
{/*------------ Sign -----------*/}
<Col className={'col--sign'}>
<span>+</span>
</Col>
{/*------------ Increment -----------*/}
<Col className={'col--quantity'}>
<Field name={'quantity'}>
{({
form: { values, setFieldValue },
field,
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'increment'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="quantity" />}
fill={true}
>
<MoneyInputGroup
value={field.value}
allowDecimals={false}
allowNegativeValue={true}
inputRef={(ref) => (incrementFieldRef.current = ref)}
onChange={(value) => {
setFieldValue('quantity', value);
}}
onBlurValue={(value) => {
setFieldValue(
'new_quantity',
incrementQuantity(
toSafeNumber(value),
toSafeNumber(values.quantity_on_hand),
),
);
}}
intent={inputIntent({ error, touched })}
/>
</FormGroup>
)}
</Field>
</Col>
{/*------------ Cost -----------*/}
<Col className={'col--cost'}>
<FastField name={'cost'}>
{({
form: { setFieldValue },
field: { value },
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'cost'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="cost" />}
>
<MoneyInputGroup
value={value}
onChange={(value) => {
setFieldValue('cost', value);
}}
intent={inputIntent({ error, touched })}
/>
</FormGroup>
)}
</FastField>
</Col>
{/*------------ Sign -----------*/}
<Col className={'col--sign'}>
<span>=</span>
</Col>
{/*------------ New quantity -----------*/}
<Col className={'col--quantity-on-hand'}>
<Field name={'new_quantity'}>
{({
form: { values, setFieldValue },
field,
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'new_quantity'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="new_quantity" />}
>
<MoneyInputGroup
value={field.value}
allowDecimals={false}
allowNegativeValue={true}
onChange={(value) => {
setFieldValue('new_quantity', value);
}}
onBlurValue={(value) => {
setFieldValue(
'quantity',
decrementQuantity(
toSafeNumber(value),
toSafeNumber(values.quantity_on_hand),
),
);
}}
intent={inputIntent({ error, touched })}
/>
</FormGroup>
)}
</Field>
</Col>
</Row>
);
}

View File

@@ -0,0 +1,77 @@
import React from 'react';
import { Intent, Button, Classes } from '@blueprintjs/core';
import { useFormikContext } from 'formik';
import { FormattedMessage as T } from 'components';
import { useInventoryAdjContext } from './InventoryAdjustmentFormProvider';
import withDialogActions from 'containers/Dialog/withDialogActions';
import { compose } from 'utils';
/**
* Inventory adjustment floating actions.
*/
function InventoryAdjustmentFloatingActions({
// #withDialogActions
closeDialog,
}) {
// Formik context.
const { isSubmitting, submitForm } = useFormikContext();
// Inventory adjustment dialog context.
const { dialogName, setSubmitPayload, submitPayload } =
useInventoryAdjContext();
// handle submit as draft button click.
const handleSubmitDraftBtnClick = (event) => {
setSubmitPayload({ publish: false });
submitForm();
};
// Handle submit make adjustment button click.
const handleSubmitMakeAdjustmentBtnClick = (event) => {
setSubmitPayload({ publish: true });
submitForm();
};
// Handle close button click.
const handleCloseBtnClick = (event) => {
closeDialog(dialogName);
};
return (
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button
disabled={isSubmitting}
onClick={handleCloseBtnClick}
style={{ minWidth: '75px' }}
>
<T id={'close'} />
</Button>
<Button
disabled={isSubmitting}
loading={isSubmitting && !submitPayload.publish}
style={{ minWidth: '75px' }}
type="submit"
onClick={handleSubmitDraftBtnClick}
>
{<T id={'save_as_draft'} />}
</Button>
<Button
intent={Intent.PRIMARY}
disabled={isSubmitting}
loading={isSubmitting && submitPayload.publish}
style={{ minWidth: '75px' }}
type="submit"
onClick={handleSubmitMakeAdjustmentBtnClick}
>
{<T id={'make_adjustment'} />}
</Button>
</div>
</div>
);
}
export default compose(withDialogActions)(InventoryAdjustmentFloatingActions);

View File

@@ -0,0 +1,86 @@
import React from 'react';
import moment from 'moment';
import { Intent } from '@blueprintjs/core';
import { Formik } from 'formik';
import { omit, get } from 'lodash';
import intl from 'react-intl-universal';
import 'style/pages/Items/ItemAdjustmentDialog.scss';
import { AppToaster } from 'components';
import { CreateInventoryAdjustmentFormSchema } from './InventoryAdjustmentForm.schema';
import InventoryAdjustmentFormContent from './InventoryAdjustmentFormContent';
import { useInventoryAdjContext } from './InventoryAdjustmentFormProvider';
import withDialogActions from 'containers/Dialog/withDialogActions';
import { compose } from 'utils';
const defaultInitialValues = {
date: moment(new Date()).format('YYYY-MM-DD'),
type: 'decrement',
adjustment_account_id: '',
item_id: '',
reason: '',
cost: '',
quantity: '',
reference_no: '',
quantity_on_hand: '',
publish: '',
};
/**
* Inventory adjustment form.
*/
function InventoryAdjustmentForm({
// #withDialogActions
closeDialog,
}) {
const {
dialogName,
item,
itemId,
submitPayload,
createInventoryAdjMutate,
} = useInventoryAdjContext();
// Initial form values.
const initialValues = {
...defaultInitialValues,
item_id: itemId,
quantity_on_hand: get(item, 'quantity_on_hand', 0),
};
// Handles the form submit.
const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
const form = {
...omit(values, ['quantity_on_hand', 'new_quantity', 'action']),
publish: submitPayload.publish,
};
setSubmitting(true);
createInventoryAdjMutate(form)
.then(() => {
closeDialog(dialogName);
AppToaster.show({
message: intl.get('the_make_adjustment_has_been_created_successfully'),
intent: Intent.SUCCESS,
});
})
.finally(() => {
setSubmitting(true);
});
};
return (
<Formik
validationSchema={CreateInventoryAdjustmentFormSchema}
initialValues={initialValues}
onSubmit={handleFormSubmit}
>
<InventoryAdjustmentFormContent />
</Formik>
);
}
export default compose(withDialogActions)(InventoryAdjustmentForm);

View File

@@ -0,0 +1,28 @@
import * as Yup from 'yup';
import intl from 'react-intl-universal';
import { DATATYPES_LENGTH } from 'common/dataTypes';
const Schema = Yup.object().shape({
date: Yup.date().required().label(intl.get('date')),
type: Yup.string().required(),
adjustment_account_id: Yup.string()
.required()
.label(intl.get('adjustment_account')),
item_id: Yup.number().required(),
reason: Yup.string()
.required()
.min(3)
.max(DATATYPES_LENGTH.TEXT)
.label(intl.get('reason')),
quantity_on_hand: Yup.number().required().label(intl.get('qty')),
quantity: Yup.number().integer().min(1).required(),
cost: Yup.number().when(['type'], {
is: (type) => type === 'increment',
then: Yup.number().required(),
}),
reference_no: Yup.string(),
new_quantity: Yup.number().required(),
publish: Yup.boolean(),
});
export const CreateInventoryAdjustmentFormSchema = Schema;

View File

@@ -0,0 +1,16 @@
import React from 'react';
import { Form } from 'formik';
import InventoryAdjustmentFormDialogFields from './InventoryAdjustmentFormDialogFields';
import InventoryAdjustmentFloatingActions from './InventoryAdjustmentFloatingActions';
/**
* Inventory adjustment form content.
*/
export default function InventoryAdjustmentFormContent() {
return (
<Form>
<InventoryAdjustmentFormDialogFields />
<InventoryAdjustmentFloatingActions />
</Form>
);
}

View File

@@ -0,0 +1,21 @@
import React from 'react';
import 'style/pages/Items/ItemAdjustmentDialog.scss';
import { InventoryAdjustmentFormProvider } from './InventoryAdjustmentFormProvider';
import InventoryAdjustmentForm from './InventoryAdjustmentForm';
/**
* Inventory adjustment form dialog content.
*/
export default function InventoryAdjustmentFormDialogContent({
// #ownProps
dialogName,
itemId
}) {
return (
<InventoryAdjustmentFormProvider itemId={itemId} dialogName={dialogName}>
<InventoryAdjustmentForm />
</InventoryAdjustmentFormProvider>
);
}

View File

@@ -0,0 +1,175 @@
import React from 'react';
import { FastField, ErrorMessage, Field } from 'formik';
import {
Classes,
FormGroup,
InputGroup,
TextArea,
Position,
} from '@blueprintjs/core';
import classNames from 'classnames';
import { FormattedMessage as T } from 'components';
import intl from 'react-intl-universal';
import { DateInput } from '@blueprintjs/datetime';
import { useAutofocus } from 'hooks';
import { ListSelect, FieldRequiredHint, Col, Row } from 'components';
import {
inputIntent,
momentFormatter,
tansformDateValue,
handleDateChange,
toSafeNumber,
} from 'utils';
import { CLASSES } from 'common/classes';
import adjustmentType from 'common/adjustmentType';
import AccountsSuggestField from 'components/AccountsSuggestField';
import { useInventoryAdjContext } from './InventoryAdjustmentFormProvider';
import { diffQuantity } from './utils';
import InventoryAdjustmentQuantityFields from './InventoryAdjustmentQuantityFields';
/**
* Inventory adjustment form dialogs fields.
*/
export default function InventoryAdjustmentFormDialogFields() {
const dateFieldRef = useAutofocus();
// Inventory adjustment dialog context.
const { accounts } = useInventoryAdjContext();
// Intl context.
return (
<div className={Classes.DIALOG_BODY}>
<Row>
<Col xs={5}>
{/*------------ Date -----------*/}
<FastField name={'date'}>
{({ form, field: { value }, meta: { error, touched } }) => (
<FormGroup
label={<T id={'date'} />}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="date" />}
minimal={true}
className={classNames(CLASSES.FILL, 'form-group--date')}
>
<DateInput
{...momentFormatter('YYYY/MM/DD')}
onChange={handleDateChange((formattedDate) => {
form.setFieldValue('date', formattedDate);
})}
value={tansformDateValue(value)}
popoverProps={{
position: Position.BOTTOM,
minimal: true,
}}
intent={inputIntent({ error, touched })}
inputRef={(ref) => (dateFieldRef.current = ref)}
/>
</FormGroup>
)}
</FastField>
</Col>
<Col xs={5}>
{/*------------ Adjustment type -----------*/}
<Field name={'type'}>
{({
form: { values, setFieldValue },
field: { value },
meta: { error, touched },
}) => (
<FormGroup
label={<T id={'adjustment_type'} />}
labelInfo={<FieldRequiredHint />}
helperText={<ErrorMessage name="type" />}
intent={inputIntent({ error, touched })}
className={classNames(CLASSES.FILL, 'form-group--type')}
>
<ListSelect
items={adjustmentType}
onItemSelect={(type) => {
const result = diffQuantity(
toSafeNumber(values.quantity),
toSafeNumber(values.quantity_on_hand),
type.value,
);
setFieldValue('type', type.value);
setFieldValue('new_quantity', result);
}}
filterable={false}
selectedItem={value}
selectedItemProp={'value'}
textProp={'name'}
popoverProps={{ minimal: true }}
intent={inputIntent({ error, touched })}
/>
</FormGroup>
)}
</Field>
</Col>
</Row>
<InventoryAdjustmentQuantityFields />
{/*------------ Adjustment account -----------*/}
<FastField name={'adjustment_account_id'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'adjustment_account'} />}
labelInfo={<FieldRequiredHint />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="adjustment_account_id" />}
className={'form-group--adjustment-account'}
>
<AccountsSuggestField
accounts={accounts}
onAccountSelected={({ id }) =>
form.setFieldValue('adjustment_account_id', id)
}
inputProps={{
placeholder: intl.get('select_adjustment_account'),
intent: inputIntent({ error, touched }),
}}
/>
</FormGroup>
)}
</FastField>
{/*------------ Reference -----------*/}
<FastField name={'reference_no'}>
{({ form, field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'reference_no'} />}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name="reference_no" />}
className={'form-group--reference-no'}
>
<InputGroup intent={inputIntent({ error, touched })} {...field} />
</FormGroup>
)}
</FastField>
{/*------------ Adjustment reasons -----------*/}
<FastField name={'reason'}>
{({ field, meta: { error, touched } }) => (
<FormGroup
label={<T id={'adjustment_reasons'} />}
labelInfo={<FieldRequiredHint />}
className={'form-group--adjustment-reasons'}
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'reason'} />}
>
<TextArea
growVertically={true}
large={true}
intent={inputIntent({ error, touched })}
{...field}
/>
</FormGroup>
)}
</FastField>
</div>
);
}

View File

@@ -0,0 +1,51 @@
import React, { useState, createContext } from 'react';
import { DialogContent } from 'components';
import {
useItem,
useAccounts,
useCreateInventoryAdjustment,
} from 'hooks/query';
const InventoryAdjustmentContext = createContext();
/**
* Inventory adjustment dialog provider.
*/
function InventoryAdjustmentFormProvider({ itemId, dialogName, ...props }) {
// Fetches accounts list.
const { isFetching: isAccountsLoading, data: accounts } = useAccounts();
// Fetches the item details.
const { isFetching: isItemLoading, data: item } = useItem(itemId);
const {
mutateAsync: createInventoryAdjMutate,
} = useCreateInventoryAdjustment();
// Submit payload.
const [submitPayload, setSubmitPayload] = useState({});
// State provider.
const provider = {
itemId,
isAccountsLoading,
accounts,
isItemLoading,
item,
submitPayload,
dialogName,
createInventoryAdjMutate,
setSubmitPayload,
};
return (
<DialogContent isLoading={isAccountsLoading || isItemLoading}>
<InventoryAdjustmentContext.Provider value={provider} {...props} />
</DialogContent>
);
}
const useInventoryAdjContext = () => React.useContext(InventoryAdjustmentContext);
export { InventoryAdjustmentFormProvider, useInventoryAdjContext };

View File

@@ -0,0 +1,22 @@
import React from 'react';
import { useFormikContext } from 'formik';
import { Choose, If } from 'components';
import IncrementAdjustmentFields from './IncrementAdjustmentFields';
import DecrementAdjustmentFields from './DecrementAdjustmentFields';
export default function InventoryAdjustmentQuantityFields() {
const { values } = useFormikContext();
return (
<div class="adjustment-fields">
<Choose>
<Choose.When condition={values.type === 'decrement'}>
<DecrementAdjustmentFields />
</Choose.When>
<Choose.When condition={values.type === 'increment'}>
<IncrementAdjustmentFields />
</Choose.When>
</Choose>
</div>
);
}

View File

@@ -0,0 +1,38 @@
import React, { lazy } from 'react';
import { FormattedMessage as T } from 'components';
import { Dialog, DialogSuspense } from 'components';
import withDialogRedux from 'components/DialogReduxConnect';
import { compose } from 'redux';
const InventoryAdjustmentFormDialogContent = lazy(() =>
import('./InventoryAdjustmentFormDialogContent'),
);
/**
* Inventory adjustments form dialog.
*/
function InventoryAdjustmentFormDialog({
dialogName,
payload = { action: '', itemId: null },
isOpen,
}) {
return (
<Dialog
name={dialogName}
title={<T id={'make_adjustment'} />}
isOpen={isOpen}
canEscapeJeyClose={true}
autoFocus={true}
className={'dialog--adjustment-item'}
>
<DialogSuspense>
<InventoryAdjustmentFormDialogContent
dialogName={dialogName}
itemId={payload.itemId}
/>
</DialogSuspense>
</Dialog>
);
}
export default compose(withDialogRedux())(InventoryAdjustmentFormDialog);

View File

@@ -0,0 +1,14 @@
export const decrementQuantity = (newQuantity, quantityOnHand) => {
return quantityOnHand - newQuantity;
};
export const incrementQuantity = (newQuantity, quantityOnHand) => {
return quantityOnHand + newQuantity;
};
export const diffQuantity = (newQuantity, quantityOnHand, type) => {
return type === 'decrement'
? decrementQuantity(newQuantity, quantityOnHand)
: incrementQuantity(newQuantity, quantityOnHand);
};