mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 21:30:31 +00:00
feat(webapp): wip tax rate form dialog
This commit is contained in:
@@ -1,12 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import {
|
||||
AppToaster,
|
||||
FormattedMessage as T,
|
||||
FormattedHTMLMessage,
|
||||
} from '@/components';
|
||||
import { AppToaster, FormattedMessage as T } from '@/components';
|
||||
|
||||
import { useDeleteTaxRate } from '@/hooks/query/taxRates';
|
||||
|
||||
@@ -40,7 +35,6 @@ function TaxRateDeleteAlert({
|
||||
const handleCancelItemDelete = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
// Handle confirm delete item.
|
||||
const handleConfirmDeleteItem = () => {
|
||||
deleteTaxRate(taxRateId)
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
// @ts-nocheck
|
||||
import React, { useCallback } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import React from 'react';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import {
|
||||
DataTable,
|
||||
DashboardContentTable,
|
||||
TableSkeletonHeader,
|
||||
TableSkeletonRows,
|
||||
AppToaster,
|
||||
} from '@/components';
|
||||
|
||||
import withAlertsActions from '@/containers/Alert/withAlertActions';
|
||||
@@ -14,10 +15,6 @@ import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||
import withDashboardActions from '@/containers/Dashboard/withDashboardActions';
|
||||
import withSettings from '@/containers/Settings/withSettings';
|
||||
|
||||
// import { useMemorizedColumnsWidths } from '@/hooks';
|
||||
// import { ActionsMenu } from './components';
|
||||
// import { useInvoicesListContext } from './InvoicesListProvider';
|
||||
|
||||
import { useTaxRatesTableColumns } from './_utils';
|
||||
import { useTaxRatesLandingContext } from './TaxRatesLandingProvider';
|
||||
import { TaxRatesLandingEmptyState } from './TaxRatesLandingEmptyState';
|
||||
@@ -26,6 +23,10 @@ import { TaxRatesTableActionsMenu } from './_components';
|
||||
import { compose } from '@/utils';
|
||||
import { DRAWERS } from '@/constants/drawers';
|
||||
import { DialogsName } from '@/constants/dialogs';
|
||||
import {
|
||||
useActivateTaxRate,
|
||||
useInactivateTaxRate,
|
||||
} from '@/hooks/query/taxRates';
|
||||
|
||||
/**
|
||||
* Invoices datatable.
|
||||
@@ -47,6 +48,9 @@ function TaxRatesDataTable({
|
||||
// Invoices table columns.
|
||||
const columns = useTaxRatesTableColumns();
|
||||
|
||||
const { mutateAsync: activateTaxRateMutate } = useActivateTaxRate();
|
||||
const { mutateAsync: inactivateTaxRateMutate } = useInactivateTaxRate();
|
||||
|
||||
// Handle delete tax rate.
|
||||
const handleDeleteTaxRate = ({ id }) => {
|
||||
openAlert('tax-rate-delete', { taxRateId: id });
|
||||
@@ -63,6 +67,38 @@ function TaxRatesDataTable({
|
||||
const handleCellClick = (cell, event) => {
|
||||
openDrawer(DRAWERS.TAX_RATE_DETAILS, { taxRateId: cell.row.original.id });
|
||||
};
|
||||
// Handles activating the given tax rate.
|
||||
const handleActivateTaxRate = (taxRate) => {
|
||||
activateTaxRateMutate(taxRate.id)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: 'The tax rate has been activated successfully.',
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
AppToaster.show({
|
||||
message: 'Something went wrong.',
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
});
|
||||
};
|
||||
// Handles inactivating the given tax rate.
|
||||
const handleInactivateTaxRate = (taxRate) => {
|
||||
inactivateTaxRateMutate(taxRate.id)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: 'The tax rate has been inactivated successfully.',
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
AppToaster.show({
|
||||
message: 'Something went wrong.',
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
});
|
||||
};
|
||||
// Display invoice empty status instead of the table.
|
||||
if (isEmptyStatus) {
|
||||
return <TaxRatesLandingEmptyState />;
|
||||
@@ -93,6 +129,8 @@ function TaxRatesDataTable({
|
||||
onViewDetails: handleViewDetails,
|
||||
onDelete: handleDeleteTaxRate,
|
||||
onEdit: handleEditTaxRate,
|
||||
onActivate: handleActivateTaxRate,
|
||||
onInactivate: handleInactivateTaxRate,
|
||||
}}
|
||||
/>
|
||||
</DashboardContentTable>
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Intent, Menu, MenuDivider, MenuItem } from '@blueprintjs/core';
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export function TaxRatesTableActionsMenu({
|
||||
payload: { onEdit, onDelete, onViewDetails },
|
||||
payload: { onEdit, onDelete, onViewDetails, onActivate, onInactivate },
|
||||
row: { original },
|
||||
}) {
|
||||
return (
|
||||
@@ -28,6 +28,21 @@ export function TaxRatesTableActionsMenu({
|
||||
onClick={safeCallback(onEdit, original)}
|
||||
/>
|
||||
</Can>
|
||||
<MenuDivider />
|
||||
{!original.active && (
|
||||
<MenuItem
|
||||
icon={<Icon icon="play-16" iconSize={16} />}
|
||||
text={'Activate Tax Rate'}
|
||||
onClick={safeCallback(onActivate, original)}
|
||||
/>
|
||||
)}
|
||||
{original.active && (
|
||||
<MenuItem
|
||||
icon={<Icon icon="pause-16" iconSize={16} />}
|
||||
text={'Inactivate Tax Rate'}
|
||||
onClick={safeCallback(onInactivate, original)}
|
||||
/>
|
||||
)}
|
||||
<Can I={TaxRateAction.Delete} a={AbilitySubject.TaxRate}>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { Button, Intent, Tag, Icon } from '@blueprintjs/core';
|
||||
import { Intent, Tag } from '@blueprintjs/core';
|
||||
import { Align } from '@/constants';
|
||||
import { FormatDateCell } from '@/components';
|
||||
|
||||
const codeAccessor = (taxRate) => {
|
||||
return (
|
||||
@@ -13,19 +12,26 @@ const codeAccessor = (taxRate) => {
|
||||
};
|
||||
|
||||
const statusAccessor = (taxRate) => {
|
||||
return (
|
||||
return taxRate.active ? (
|
||||
<Tag round={false} intent={Intent.SUCCESS}>
|
||||
Active
|
||||
</Tag>
|
||||
) : (
|
||||
<Tag round={false} intent={Intent.NONE}>
|
||||
Inactive
|
||||
</Tag>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves the tax rates table columns.
|
||||
*/
|
||||
export const useTaxRatesTableColumns = () => {
|
||||
return [
|
||||
{
|
||||
Header: 'Name',
|
||||
accessor: 'name',
|
||||
width: 40,
|
||||
width: 50,
|
||||
},
|
||||
{
|
||||
Header: 'Code',
|
||||
@@ -40,8 +46,8 @@ export const useTaxRatesTableColumns = () => {
|
||||
},
|
||||
{
|
||||
Header: 'Description',
|
||||
accessor: () => <span>Specital tax for certain goods and services.</span>,
|
||||
width: 120,
|
||||
accessor: 'description',
|
||||
width: 110,
|
||||
},
|
||||
{
|
||||
Header: 'Status',
|
||||
|
||||
@@ -7,9 +7,14 @@ const getSchema = () =>
|
||||
code: Yup.string().required().label('Code'),
|
||||
active: Yup.boolean().optional().label('Active'),
|
||||
describtion: Yup.string().optional().label('Description'),
|
||||
rate: Yup.number().required().label('Rate'),
|
||||
rate: Yup.number()
|
||||
.min(0.01, 'Enter a rate percentage of at least 0.01%')
|
||||
.max(100, 'Enter a rate percentage of at most 100%')
|
||||
.required()
|
||||
.label('Rate'),
|
||||
is_compound: Yup.boolean().optional().label('Is Compound'),
|
||||
is_non_recoverable: Yup.boolean().optional().label('Is Non Recoverable'),
|
||||
confirm_edit: Yup.boolean().optional(),
|
||||
});
|
||||
|
||||
export const CreateTaxRateFormSchema = getSchema;
|
||||
|
||||
@@ -24,13 +24,16 @@ function TaxRateFormDialog({
|
||||
return (
|
||||
<TaxRateDialog
|
||||
name={dialogName}
|
||||
title={payload.action === 'edit' ? 'Edit Tax Rate' : 'Create Tax Rate'}
|
||||
title={payload.id ? 'Edit Tax Rate' : 'Create Tax Rate'}
|
||||
autoFocus={true}
|
||||
canEscapeKeyClose={true}
|
||||
isOpen={isOpen}
|
||||
>
|
||||
<DialogSuspense>
|
||||
<TaxRateFormDialogContent dialogName={dialogName} payload={payload} />
|
||||
<TaxRateFormDialogContent
|
||||
dialogName={dialogName}
|
||||
taxRateId={payload.id}
|
||||
/>
|
||||
</DialogSuspense>
|
||||
</TaxRateDialog>
|
||||
);
|
||||
|
||||
@@ -1,28 +1,51 @@
|
||||
// @ts-nocheck
|
||||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import { DialogContent } from '@/components';
|
||||
import { useTaxRates } from '@/hooks/query/taxRates';
|
||||
import { useTaxRate, useTaxRates } from '@/hooks/query/taxRates';
|
||||
import { DialogsName } from '@/constants/dialogs';
|
||||
|
||||
const TaxRateFormDialogContext = React.createContext();
|
||||
|
||||
interface TaxRateFormDialogBootProps {
|
||||
taxRateId: number;
|
||||
children?: JSX.Element;
|
||||
}
|
||||
|
||||
interface TaxRateFormDialogBootContext {
|
||||
taxRateId: number;
|
||||
taxRate: any;
|
||||
isTaxRateLoading: boolean;
|
||||
isTaxRateSuccess: boolean;
|
||||
isNewMode: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Money in dialog provider.
|
||||
*/
|
||||
function TaxRateFormDialogBoot({ ...props }) {
|
||||
function TaxRateFormDialogBoot({
|
||||
taxRateId,
|
||||
...props
|
||||
}: TaxRateFormDialogBootProps) {
|
||||
const {
|
||||
data: taxRates,
|
||||
isLoading: isTaxRatesLoading,
|
||||
isSuccess: isTaxRatesSuccess,
|
||||
} = useTaxRates({});
|
||||
data: taxRate,
|
||||
isLoading: isTaxRateLoading,
|
||||
isSuccess: isTaxRateSuccess,
|
||||
} = useTaxRate(taxRateId, {
|
||||
enabled: !!taxRateId,
|
||||
});
|
||||
|
||||
const isNewMode = !taxRateId;
|
||||
|
||||
// Provider data.
|
||||
const provider = {
|
||||
taxRates,
|
||||
isTaxRatesLoading,
|
||||
isTaxRatesSuccess,
|
||||
taxRateId,
|
||||
taxRate,
|
||||
isTaxRateLoading,
|
||||
isTaxRateSuccess,
|
||||
isNewMode,
|
||||
dialogName: DialogsName.TaxRateForm,
|
||||
};
|
||||
|
||||
const isLoading = isTaxRatesLoading;
|
||||
const isLoading = isTaxRateLoading;
|
||||
|
||||
return (
|
||||
<DialogContent isLoading={isLoading}>
|
||||
@@ -32,6 +55,6 @@ function TaxRateFormDialogBoot({ ...props }) {
|
||||
}
|
||||
|
||||
const useTaxRateFormDialogContext = () =>
|
||||
React.useContext(TaxRateFormDialogContext);
|
||||
React.useContext<TaxRateFormDialogBootContext>(TaxRateFormDialogContext);
|
||||
|
||||
export { TaxRateFormDialogBoot, useTaxRateFormDialogContext };
|
||||
|
||||
@@ -3,12 +3,20 @@ import React from 'react';
|
||||
import TaxRateFormDialogForm from './TaxRateFormDialogForm';
|
||||
import { TaxRateFormDialogBoot } from './TaxRateFormDialogBoot';
|
||||
|
||||
interface TaxRateFormDialogContentProps {
|
||||
dialogName: string;
|
||||
taxRateId: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Account dialog content.
|
||||
*/
|
||||
export default function TaxRateFormDialogContent({ dialogName, payload }) {
|
||||
export default function TaxRateFormDialogContent({
|
||||
dialogName,
|
||||
taxRateId,
|
||||
}: TaxRateFormDialogContentProps) {
|
||||
return (
|
||||
<TaxRateFormDialogBoot dialogName={dialogName} payload={payload}>
|
||||
<TaxRateFormDialogBoot dialogName={dialogName} taxRateId={taxRateId}>
|
||||
<TaxRateFormDialogForm />
|
||||
</TaxRateFormDialogBoot>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @ts-nocheck
|
||||
import React, { useCallback } from 'react';
|
||||
import React from 'react';
|
||||
import { Classes, Intent } from '@blueprintjs/core';
|
||||
import { Form, Formik } from 'formik';
|
||||
import { AppToaster } from '@/components';
|
||||
@@ -11,21 +11,12 @@ import {
|
||||
CreateTaxRateFormSchema,
|
||||
EditTaxRateFormSchema,
|
||||
} from './TaxRateForm.schema';
|
||||
import { transformApiErrors, transformFormToReq } from './utils';
|
||||
import { transformApiErrors, transformFormToReq, transformTaxRateToForm } 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.
|
||||
@@ -35,13 +26,8 @@ function TaxRateFormDialogForm({
|
||||
closeDialog,
|
||||
}) {
|
||||
// Account form context.
|
||||
const {
|
||||
account,
|
||||
|
||||
payload,
|
||||
isNewMode,
|
||||
dialogName,
|
||||
} = useTaxRateFormDialogContext();
|
||||
const { taxRate, taxRateId, isNewMode, dialogName } =
|
||||
useTaxRateFormDialogContext();
|
||||
|
||||
// Form validation schema in create and edit mode.
|
||||
const validationSchema = isNewMode
|
||||
@@ -76,30 +62,18 @@ function TaxRateFormDialogForm({
|
||||
setErrors({ ...errorsTransformed });
|
||||
setSubmitting(false);
|
||||
};
|
||||
if (payload.accountId) {
|
||||
editTaxRateMutate([payload.accountId, form])
|
||||
if (isNewMode) {
|
||||
createTaxRateMutate({ ...form })
|
||||
.then(handleSuccess)
|
||||
.catch(handleError);
|
||||
} else {
|
||||
createTaxRateMutate({ ...form })
|
||||
editTaxRateMutate([taxRateId, { ...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);
|
||||
};
|
||||
const initialValues = transformTaxRateToForm(taxRate);
|
||||
|
||||
return (
|
||||
<Formik
|
||||
@@ -109,11 +83,7 @@ function TaxRateFormDialogForm({
|
||||
>
|
||||
<Form>
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<TaxRateFormDialogFormContent
|
||||
dialogName={dialogName}
|
||||
action={payload?.action}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
<TaxRateFormDialogFormContent />
|
||||
</div>
|
||||
<TaxRateFormDialogFormFooter />
|
||||
</Form>
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import {
|
||||
FCheckbox,
|
||||
FFormGroup,
|
||||
FInputGroup,
|
||||
FieldHint,
|
||||
Hint,
|
||||
} from '@/components';
|
||||
import { Tag } from '@blueprintjs/core';
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { Tag, Text } from '@blueprintjs/core';
|
||||
import styled from 'styled-components';
|
||||
import { FCheckbox, FFormGroup, FInputGroup, Hint } from '@/components';
|
||||
import { transformTaxRateCodeValue, useIsTaxRateChanged } from './utils';
|
||||
import { useTaxRateFormDialogContext } from './TaxRateFormDialogBoot';
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns
|
||||
* Tax rate form content.
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export default function TaxRateFormDialogContent() {
|
||||
return (
|
||||
@@ -23,27 +21,23 @@ export default function TaxRateFormDialogContent() {
|
||||
subLabel={
|
||||
'The name as you would like it to appear in customers invoices.'
|
||||
}
|
||||
fastField={true}
|
||||
>
|
||||
<FInputGroup name={'name'} />
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup
|
||||
name={'code'}
|
||||
label={'Code'}
|
||||
labelInfo={<Tag minimal>Required</Tag>}
|
||||
>
|
||||
<FInputGroup name={'code'} />
|
||||
<FInputGroup name={'name'} fastField={true} />
|
||||
</FFormGroup>
|
||||
|
||||
<TaxRateCodeField />
|
||||
<FFormGroup
|
||||
name={'rate'}
|
||||
label={'Rate (%)'}
|
||||
labelInfo={<Tag minimal>Required</Tag>}
|
||||
fastField={true}
|
||||
>
|
||||
<RateFormGroup
|
||||
name={'rate'}
|
||||
rightElement={<Tag minimal>%</Tag>}
|
||||
fill={false}
|
||||
fastField={true}
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
@@ -51,23 +45,81 @@ export default function TaxRateFormDialogContent() {
|
||||
name={'description'}
|
||||
label={'Description'}
|
||||
labelInfo={
|
||||
<FieldHint content="This description is for internal use only and will not be visiable to your customers." />
|
||||
<Hint content="This description is for internal use only and will not be visiable to your customers." />
|
||||
}
|
||||
fastField={true}
|
||||
>
|
||||
<FInputGroup name={'description'} />
|
||||
<FInputGroup name={'description'} fastField={true} />
|
||||
</FFormGroup>
|
||||
|
||||
<CompoundFormGroup name={'is_compound'}>
|
||||
<FCheckbox label={'Is compound'} name={'is_compound'} />
|
||||
<CompoundFormGroup name={'is_compound'} fastField={true}>
|
||||
<FCheckbox
|
||||
label={'Is compound'}
|
||||
name={'is_compound'}
|
||||
fastField={true}
|
||||
/>
|
||||
</CompoundFormGroup>
|
||||
|
||||
<CompoundFormGroup name={'is_non_recoverable'}>
|
||||
<FCheckbox label={'Is non recoverable'} name={'is_non_recoverable'} />
|
||||
<CompoundFormGroup name={'is_non_recoverable'} fastField={true}>
|
||||
<FCheckbox
|
||||
label={'Is non recoverable'}
|
||||
name={'is_non_recoverable'}
|
||||
fastField={true}
|
||||
/>
|
||||
</CompoundFormGroup>
|
||||
|
||||
<ConfirmEditingTaxRate />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tax rate code input group
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
function TaxRateCodeField() {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
|
||||
// Handle the field change.
|
||||
const handleChange = (event) => {
|
||||
const transformedValue = transformTaxRateCodeValue(event.target.value);
|
||||
setFieldValue('code', transformedValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<FFormGroup
|
||||
name={'code'}
|
||||
label={'Code'}
|
||||
labelInfo={<Tag minimal>Required</Tag>}
|
||||
fastField={true}
|
||||
>
|
||||
<FInputGroup name={'code'} fastField={true} onChange={handleChange} />
|
||||
</FFormGroup>
|
||||
);
|
||||
}
|
||||
|
||||
function ConfirmEditingTaxRate() {
|
||||
const isTaxRateChanged = useIsTaxRateChanged();
|
||||
const { isNewMode } = useTaxRateFormDialogContext();
|
||||
|
||||
// Can't continue if it is new mode or tax rate not changed.
|
||||
if (!isTaxRateChanged || isNewMode) return null;
|
||||
|
||||
return (
|
||||
<EditWarningWrap>
|
||||
<Text color={'#766f58'}>Please Note:</Text>
|
||||
<ConfirmEditFormGroup name={'confirm_edit'}>
|
||||
<FCheckbox
|
||||
name={'confirm_edit'}
|
||||
label={`I understand that updating the
|
||||
tax will mark the existing tax inactive, create a new tax, and update
|
||||
it in the chosen transactions.`}
|
||||
/>
|
||||
</ConfirmEditFormGroup>
|
||||
</EditWarningWrap>
|
||||
);
|
||||
}
|
||||
|
||||
const RateFormGroup = styled(FInputGroup)`
|
||||
max-width: 100px;
|
||||
`;
|
||||
@@ -75,3 +127,18 @@ const RateFormGroup = styled(FInputGroup)`
|
||||
const CompoundFormGroup = styled(FFormGroup)`
|
||||
margin-bottom: 0;
|
||||
`;
|
||||
|
||||
const EditWarningWrap = styled(`div`)`
|
||||
background: #fcf8ec;
|
||||
margin-left: -20px;
|
||||
margin-right: -20px;
|
||||
padding: 14px 20px;
|
||||
font-size: 13px;
|
||||
margin-top: 8px;
|
||||
border-top: 1px solid #f2eddf;
|
||||
border-bottom: 1px solid #f2eddf;
|
||||
`;
|
||||
|
||||
const ConfirmEditFormGroup = styled(FFormGroup)`
|
||||
margin-bottom: 0;
|
||||
`;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import * as R from 'ramda';
|
||||
import { useFormikContext } from 'formik';
|
||||
|
||||
@@ -1,7 +1,60 @@
|
||||
// @ts-nocheck
|
||||
import { useFormikContext } from 'formik';
|
||||
import * as R from 'ramda';
|
||||
import { omit } from 'lodash';
|
||||
import { transformToForm } from '@/utils';
|
||||
|
||||
// Default initial form values.
|
||||
export const defaultInitialValues = {
|
||||
name: '',
|
||||
code: '',
|
||||
rate: '',
|
||||
description: '',
|
||||
is_compound: false,
|
||||
is_non_recoverable: false,
|
||||
confirm_edit: false,
|
||||
};
|
||||
|
||||
export const transformApiErrors = () => {
|
||||
return {};
|
||||
};
|
||||
|
||||
export const transformFormToReq = () => {
|
||||
return {};
|
||||
export const transformFormToReq = (form) => {
|
||||
return omit({ ...form }, ['confirm_edit']);
|
||||
};
|
||||
|
||||
export const useIsTaxRateChanged = () => {
|
||||
const { initialValues, values } = useFormikContext();
|
||||
|
||||
return initialValues.rate !== values.rate;
|
||||
};
|
||||
|
||||
const convertFormAttrsToBoolean = (form) => {
|
||||
return {
|
||||
...form,
|
||||
is_compound: !!form.is_compound,
|
||||
is_non_recoverable: !!form.is_non_recoverable,
|
||||
};
|
||||
};
|
||||
|
||||
export const transformTaxRateToForm = (taxRate) => {
|
||||
return R.compose(convertFormAttrsToBoolean)({
|
||||
...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(taxRate, defaultInitialValues),
|
||||
});
|
||||
};
|
||||
|
||||
export const transformTaxRateCodeValue = (input: string) => {
|
||||
// Remove non-alphanumeric characters and spaces using a regular expression
|
||||
const cleanedString = input.replace(/\s+/g, '');
|
||||
|
||||
// Convert the cleaned string to uppercase
|
||||
const uppercasedString = cleanedString.toUpperCase();
|
||||
|
||||
return uppercasedString;
|
||||
};
|
||||
|
||||
@@ -27,17 +27,29 @@ export default function TaxRateDetailsContentDetails() {
|
||||
<DetailItem
|
||||
label={'Non Recoverable'}
|
||||
children={
|
||||
<Tag round={false} intent={Intent.SUCCESS} minimal>
|
||||
Enabled
|
||||
</Tag>
|
||||
taxRate.is_non_recoverable ? (
|
||||
<Tag round={false} intent={Intent.SUCCESS} minimal>
|
||||
Enabled
|
||||
</Tag>
|
||||
) : (
|
||||
<Tag round={false} intent={Intent.NONE} minimal>
|
||||
Disabled
|
||||
</Tag>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<DetailItem
|
||||
label={'Compound'}
|
||||
children={
|
||||
<Tag round={false} intent={Intent.SUCCESS} minimal>
|
||||
Enabled
|
||||
</Tag>
|
||||
taxRate.is_compound ? (
|
||||
<Tag round={false} intent={Intent.SUCCESS} minimal>
|
||||
Enabled
|
||||
</Tag>
|
||||
) : (
|
||||
<Tag round={false} intent={Intent.NONE} minimal>
|
||||
Disabled
|
||||
</Tag>
|
||||
)
|
||||
}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
|
||||
@@ -4,6 +4,11 @@ import { useRequestQuery } from '../useQueryRequest';
|
||||
import QUERY_TYPES from './types';
|
||||
import useApiRequest from '../useRequest';
|
||||
|
||||
// Common invalidate queries.
|
||||
const commonInvalidateQueries = (queryClient) => {
|
||||
queryClient.invalidateQueries(QUERY_TYPES.TAX_RATES);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves tax rates.
|
||||
* @param {number} customerId - Customer id.
|
||||
@@ -52,9 +57,8 @@ export function useEditTaxRate(props) {
|
||||
([id, values]) => apiRequest.post(`tax-rates/${id}`, values),
|
||||
{
|
||||
onSuccess: (res, id) => {
|
||||
// Invalidate specific item.
|
||||
commonInvalidateQueries(queryClient);
|
||||
queryClient.invalidateQueries([QUERY_TYPES.TAX_RATES, id]);
|
||||
queryClient.invalidateQueries([QUERY_TYPES.TAX_RATES]);
|
||||
},
|
||||
...props,
|
||||
},
|
||||
@@ -68,28 +72,58 @@ export function useCreateTaxRate(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(([values]) => apiRequest.post('tax-rates', values), {
|
||||
return useMutation((values) => apiRequest.post('tax-rates', values), {
|
||||
onSuccess: (res, id) => {
|
||||
// Invalidate specific item.
|
||||
commonInvalidateQueries(queryClient);
|
||||
queryClient.invalidateQueries([QUERY_TYPES.TAX_RATES, id]);
|
||||
queryClient.invalidateQueries([QUERY_TYPES.TAX_RATES]);
|
||||
},
|
||||
...props,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a new tax rate.
|
||||
* Delete the given tax rate.
|
||||
*/
|
||||
export function useDeleteTaxRate(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation(([id]) => apiRequest.delete(`tax-rates/${id}`), {
|
||||
return useMutation((id) => apiRequest.post(`tax-rates/${id}`), {
|
||||
onSuccess: (res, id) => {
|
||||
// Invalidate specific item.
|
||||
commonInvalidateQueries(queryClient);
|
||||
queryClient.invalidateQueries([QUERY_TYPES.TAX_RATES, id]);
|
||||
},
|
||||
...props,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate the given tax rate.
|
||||
*/
|
||||
export function useActivateTaxRate(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((id) => apiRequest.post(`tax-rates/${id}/active`), {
|
||||
onSuccess: (res, id) => {
|
||||
commonInvalidateQueries(queryClient);
|
||||
queryClient.invalidateQueries([QUERY_TYPES.TAX_RATES, id]);
|
||||
},
|
||||
...props,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inactivate the given tax rate.
|
||||
*/
|
||||
export function useInactivateTaxRate(props) {
|
||||
const queryClient = useQueryClient();
|
||||
const apiRequest = useApiRequest();
|
||||
|
||||
return useMutation((id) => apiRequest.delete(`tax-rates/${id}/inactive`), {
|
||||
onSuccess: (res, id) => {
|
||||
commonInvalidateQueries(queryClient);
|
||||
queryClient.invalidateQueries([QUERY_TYPES.TAX_RATES, id]);
|
||||
queryClient.invalidateQueries([QUERY_TYPES.TAX_RATES]);
|
||||
},
|
||||
...props,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user