feat(webapp): wip tax rates management

This commit is contained in:
Ahmed Bouhuolia
2023-09-14 23:35:54 +02:00
parent b98b73ad98
commit 8a64198433
34 changed files with 1205 additions and 14 deletions

View File

@@ -0,0 +1,16 @@
// @ts-nocheck
import * as Yup from 'yup';
const getSchema = () =>
Yup.object().shape({
name: Yup.string().required().label('Name'),
code: Yup.string().required().label('Code'),
active: Yup.boolean().optional().label('Active'),
describtion: Yup.string().optional().label('Description'),
rate: Yup.number().required().label('Rate'),
is_compound: Yup.boolean().optional().label('Is Compound'),
is_non_recoverable: Yup.boolean().optional().label('Is Non Recoverable'),
});
export const CreateTaxRateFormSchema = getSchema;
export const EditTaxRateFormSchema = getSchema;

View File

@@ -0,0 +1,39 @@
// @ts-nocheck
import React, { lazy } from 'react';
import styled from 'styled-components';
import { Dialog, DialogSuspense } from '@/components';
import withDialogRedux from '@/components/DialogReduxConnect';
import { compose } from '@/utils';
const TaxRateFormDialogContent = lazy(
() => import('./TaxRateFormDialogContent'),
);
const TaxRateDialog = styled(Dialog)`
max-width: 450px;
`;
/**
* Account form dialog.
*/
function TaxRateFormDialog({
dialogName,
payload = { action: '', id: null },
isOpen,
}) {
return (
<TaxRateDialog
name={dialogName}
title={payload.action === 'edit' ? 'Edit Tax Rate' : 'Create Tax Rate'}
autoFocus={true}
canEscapeKeyClose={true}
isOpen={isOpen}
>
<DialogSuspense>
<TaxRateFormDialogContent dialogName={dialogName} payload={payload} />
</DialogSuspense>
</TaxRateDialog>
);
}
export default compose(withDialogRedux())(TaxRateFormDialog);

View File

@@ -0,0 +1,37 @@
// @ts-nocheck
import React, { useState } from 'react';
import { DialogContent } from '@/components';
import { useTaxRates } from '@/hooks/query/taxRates';
const TaxRateFormDialogContext = React.createContext();
/**
* Money in dialog provider.
*/
function TaxRateFormDialogBoot({ ...props }) {
const {
data: taxRates,
isLoading: isTaxRatesLoading,
isSuccess: isTaxRatesSuccess,
} = useTaxRates({});
// Provider data.
const provider = {
taxRates,
isTaxRatesLoading,
isTaxRatesSuccess,
};
const isLoading = isTaxRatesLoading;
return (
<DialogContent isLoading={isLoading}>
<TaxRateFormDialogContext.Provider value={provider} {...props} />
</DialogContent>
);
}
const useTaxRateFormDialogContext = () =>
React.useContext(TaxRateFormDialogContext);
export { TaxRateFormDialogBoot, useTaxRateFormDialogContext };

View File

@@ -0,0 +1,15 @@
// @ts-nocheck
import React from 'react';
import TaxRateFormDialogForm from './TaxRateFormDialogForm';
import { TaxRateFormDialogBoot } from './TaxRateFormDialogBoot';
/**
* Account dialog content.
*/
export default function TaxRateFormDialogContent({ dialogName, payload }) {
return (
<TaxRateFormDialogBoot dialogName={dialogName} payload={payload}>
<TaxRateFormDialogForm />
</TaxRateFormDialogBoot>
);
}

View File

@@ -0,0 +1,124 @@
// @ts-nocheck
import React, { useCallback } from 'react';
import { Classes, Intent } from '@blueprintjs/core';
import { Form, Formik } from 'formik';
import { AppToaster } from '@/components';
import TaxRateFormDialogFormContent from './TaxRateFormDialogFormContent';
import withDialogActions from '@/containers/Dialog/withDialogActions';
import {
CreateTaxRateFormSchema,
EditTaxRateFormSchema,
} from './TaxRateForm.schema';
import { transformApiErrors, transformFormToReq } from './utils';
import { useCreateTaxRate, useEditTaxRate } from '@/hooks/query/taxRates';
import { useTaxRateFormDialogContext } from './TaxRateFormDialogBoot';
import { TaxRateFormDialogFormFooter } from './TaxRateFormDialogFormFooter';
import { compose, transformToForm } from '@/utils';
// Default initial form values.
const defaultInitialValues = {
name: '',
code: '',
rate: '',
description: '',
is_compound: false,
is_non_recoverable: false,
};
/**
* Tax rate form dialog content.
*/
function TaxRateFormDialogForm({
// #withDialogActions
closeDialog,
}) {
// Account form context.
const {
account,
payload,
isNewMode,
dialogName,
} = useTaxRateFormDialogContext();
// Form validation schema in create and edit mode.
const validationSchema = isNewMode
? CreateTaxRateFormSchema
: EditTaxRateFormSchema;
const { mutateAsync: createTaxRateMutate } = useCreateTaxRate();
const { mutateAsync: editTaxRateMutate } = useEditTaxRate();
// Callbacks handles form submit.
const handleFormSubmit = (values, { setSubmitting, setErrors }) => {
const form = transformFormToReq(values);
// Handle request success.
const handleSuccess = () => {
closeDialog(dialogName);
AppToaster.show({
message: 'The tax rate has been created successfully.',
intent: Intent.SUCCESS,
});
};
// Handle request error.
const handleError = (error) => {
const {
response: {
data: { errors },
},
} = error;
const errorsTransformed = transformApiErrors(errors);
setErrors({ ...errorsTransformed });
setSubmitting(false);
};
if (payload.accountId) {
editTaxRateMutate([payload.accountId, form])
.then(handleSuccess)
.catch(handleError);
} else {
createTaxRateMutate({ ...form })
.then(handleSuccess)
.catch(handleError);
}
};
// Form initial values in create and edit mode.
const initialValues = {
...defaultInitialValues,
/**
* We only care about the fields in the form. Previously unfilled optional
* values such as `notes` come back from the API as null, so remove those
* as well.
*/
...transformToForm(account, defaultInitialValues),
};
// Handles dialog close.
const handleClose = () => {
closeDialog(dialogName);
};
return (
<Formik
validationSchema={validationSchema}
initialValues={initialValues}
onSubmit={handleFormSubmit}
>
<Form>
<div className={Classes.DIALOG_BODY}>
<TaxRateFormDialogFormContent
dialogName={dialogName}
action={payload?.action}
onClose={handleClose}
/>
</div>
<TaxRateFormDialogFormFooter />
</Form>
</Formik>
);
}
export default compose(withDialogActions)(TaxRateFormDialogForm);

View File

@@ -0,0 +1,77 @@
import {
FCheckbox,
FFormGroup,
FInputGroup,
FieldHint,
Hint,
} from '@/components';
import { Tag } from '@blueprintjs/core';
import React from 'react';
import styled from 'styled-components';
/**
*
* @returns
*/
export default function TaxRateFormDialogContent() {
return (
<div>
<FFormGroup
name={'name'}
label={'Name'}
labelInfo={<Tag minimal>Required</Tag>}
subLabel={
'The name as you would like it to appear in customers invoices.'
}
>
<FInputGroup name={'name'} />
</FFormGroup>
<FFormGroup
name={'code'}
label={'Code'}
labelInfo={<Tag minimal>Required</Tag>}
>
<FInputGroup name={'code'} />
</FFormGroup>
<FFormGroup
name={'rate'}
label={'Rate (%)'}
labelInfo={<Tag minimal>Required</Tag>}
>
<RateFormGroup
name={'rate'}
rightElement={<Tag minimal>%</Tag>}
fill={false}
/>
</FFormGroup>
<FFormGroup
name={'description'}
label={'Description'}
labelInfo={
<FieldHint content="This description is for internal use only and will not be visiable to your customers." />
}
>
<FInputGroup name={'description'} />
</FFormGroup>
<CompoundFormGroup name={'is_compound'}>
<FCheckbox label={'Is compound'} name={'is_compound'} />
</CompoundFormGroup>
<CompoundFormGroup name={'is_non_recoverable'}>
<FCheckbox label={'Is non recoverable'} name={'is_non_recoverable'} />
</CompoundFormGroup>
</div>
);
}
const RateFormGroup = styled(FInputGroup)`
max-width: 100px;
`;
const CompoundFormGroup = styled(FFormGroup)`
margin-bottom: 0;
`;

View File

@@ -0,0 +1,41 @@
import React from 'react';
import * as R from 'ramda';
import { useFormikContext } from 'formik';
import { Button, Classes, Intent } from '@blueprintjs/core';
import { DialogsName } from '@/constants/dialogs';
import withDialogActions from '@/containers/Dialog/withDialogActions';
function TaxRateFormDialogFormFooterRoot({ closeDialog }) {
const { isSubmitting } = useFormikContext();
const handleClose = () => {
closeDialog(DialogsName.TaxRateForm);
};
return (
<div className={Classes.DIALOG_FOOTER}>
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
<Button
disabled={isSubmitting}
onClick={handleClose}
style={{ minWidth: '75px' }}
>
Close
</Button>
<Button
intent={Intent.PRIMARY}
loading={isSubmitting}
style={{ minWidth: '95px' }}
type="submit"
>
Submit
</Button>
</div>
</div>
);
}
export const TaxRateFormDialogFormFooter = R.compose(withDialogActions)(
TaxRateFormDialogFormFooterRoot,
);

View File

@@ -0,0 +1,7 @@
export const transformApiErrors = () => {
return {};
};
export const transformFormToReq = () => {
return {};
};