diff --git a/packages/webapp/src/components/DialogsContainer.tsx b/packages/webapp/src/components/DialogsContainer.tsx index 9e334af78..cad11bebb 100644 --- a/packages/webapp/src/components/DialogsContainer.tsx +++ b/packages/webapp/src/components/DialogsContainer.tsx @@ -47,6 +47,7 @@ import ProjectExpenseForm from '@/containers/Projects/containers/ProjectExpenseF import EstimatedExpenseFormDialog from '@/containers/Projects/containers/EstimatedExpenseFormDialog'; import ProjectInvoicingFormDialog from '@/containers/Projects/containers/ProjectInvoicingFormDialog'; import ProjectBillableEntriesFormDialog from '@/containers/Projects/containers/ProjectBillableEntriesFormDialog'; +import TaxRateFormDialog from '@/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialog'; import { DialogsName } from '@/constants/dialogs'; /** @@ -134,7 +135,10 @@ export default function DialogsContainer() { - + + ); } diff --git a/packages/webapp/src/components/DrawersContainer.tsx b/packages/webapp/src/components/DrawersContainer.tsx index 2d1a372e8..ef96dc608 100644 --- a/packages/webapp/src/components/DrawersContainer.tsx +++ b/packages/webapp/src/components/DrawersContainer.tsx @@ -22,6 +22,7 @@ import VendorCreditDetailDrawer from '@/containers/Drawers/VendorCreditDetailDra import RefundCreditNoteDetailDrawer from '@/containers/Drawers/RefundCreditNoteDetailDrawer'; import RefundVendorCreditDetailDrawer from '@/containers/Drawers/RefundVendorCreditDetailDrawer'; import WarehouseTransferDetailDrawer from '@/containers/Drawers/WarehouseTransferDetailDrawer'; +import TaxRateDetailsDrawer from '@/containers/TaxRates/drawers/TaxRateDetailsDrawer/TaxRateDetailsDrawer'; import { DRAWERS } from '@/constants/drawers'; @@ -43,16 +44,25 @@ export default function DrawersContainer() { - - + + - - + + + ); } diff --git a/packages/webapp/src/constants/abilityOption.tsx b/packages/webapp/src/constants/abilityOption.tsx index 85168e352..fe0e788d4 100644 --- a/packages/webapp/src/constants/abilityOption.tsx +++ b/packages/webapp/src/constants/abilityOption.tsx @@ -20,7 +20,8 @@ export const AbilitySubject = { SubscriptionBilling: 'SubscriptionBilling', CreditNote: 'CreditNote', VendorCredit: 'VendorCredit', - Project:'Project' + Project:'Project', + TaxRate: 'TaxRate', }; export const ItemAction = { @@ -186,3 +187,11 @@ export const SubscriptionBillingAbility = { View: 'view', Payment: 'payment', }; + + +export const TaxRateAction = { + View: 'View', + Create: 'Create', + Edit: 'Edit', + Delete: 'Delete', +}; diff --git a/packages/webapp/src/constants/dialogs.ts b/packages/webapp/src/constants/dialogs.ts index f8bf10668..115c25af2 100644 --- a/packages/webapp/src/constants/dialogs.ts +++ b/packages/webapp/src/constants/dialogs.ts @@ -46,5 +46,6 @@ export enum DialogsName { EstimateExpenseForm = 'estimate-expense-form', ProjectInvoicingForm = 'project-invoicing-form', ProjectBillableEntriesForm = 'project-billable-entries', - InvoiceNumberSettings = 'InvoiceNumberSettings' + InvoiceNumberSettings = 'InvoiceNumberSettings', + TaxRateForm = 'tax-rate-form', } diff --git a/packages/webapp/src/constants/drawers.ts b/packages/webapp/src/constants/drawers.ts index 6663990be..59237e4b4 100644 --- a/packages/webapp/src/constants/drawers.ts +++ b/packages/webapp/src/constants/drawers.ts @@ -22,4 +22,5 @@ export enum DRAWERS { REFUND_CREDIT_NOTE_DETAILS = 'refund-credit-detail-drawer', REFUND_VENDOR_CREDIT_DETAILS = 'refund-vendor-detail-drawer', WAREHOUSE_TRANSFER_DETAILS = 'warehouse-transfer-detail-drawer', + TAX_RATE_DETAILS = 'tax-rate-detail-drawer', } diff --git a/packages/webapp/src/constants/sidebarMenu.tsx b/packages/webapp/src/constants/sidebarMenu.tsx index 53189dd1f..108ed860b 100644 --- a/packages/webapp/src/constants/sidebarMenu.tsx +++ b/packages/webapp/src/constants/sidebarMenu.tsx @@ -406,6 +406,11 @@ export const SidebarMenu = [ href: '/transactions-locking', type: ISidebarMenuItemType.Link, }, + { + text: 'Tax Rates', + href: '/tax-rates', + type: ISidebarMenuItemType.Link, + }, ], }, { diff --git a/packages/webapp/src/containers/AlertsContainer/registered.tsx b/packages/webapp/src/containers/AlertsContainer/registered.tsx index 89d12ac3d..417583f60 100644 --- a/packages/webapp/src/containers/AlertsContainer/registered.tsx +++ b/packages/webapp/src/containers/AlertsContainer/registered.tsx @@ -25,6 +25,7 @@ import WarehousesAlerts from '@/containers/Preferences/Warehouses/WarehousesAler import WarehousesTransfersAlerts from '@/containers/WarehouseTransfers/WarehousesTransfersAlerts'; import BranchesAlerts from '@/containers/Preferences/Branches/BranchesAlerts'; import ProjectAlerts from '@/containers/Projects/containers/ProjectAlerts'; +import TaxRatesAlerts from '@/containers/TaxRates/alerts'; export default [ ...AccountsAlerts, @@ -53,4 +54,5 @@ export default [ ...WarehousesTransfersAlerts, ...BranchesAlerts, ...ProjectAlerts, + ...TaxRatesAlerts ]; diff --git a/packages/webapp/src/containers/Drawers/InvoiceDetailDrawer/InvoiceDetailHeader.tsx b/packages/webapp/src/containers/Drawers/InvoiceDetailDrawer/InvoiceDetailHeader.tsx index 1bc44fbce..a79b6b22c 100644 --- a/packages/webapp/src/containers/Drawers/InvoiceDetailDrawer/InvoiceDetailHeader.tsx +++ b/packages/webapp/src/containers/Drawers/InvoiceDetailDrawer/InvoiceDetailHeader.tsx @@ -25,14 +25,12 @@ import { InvoiceDetailsStatus } from './utils'; export default function InvoiceDetailHeader() { const { invoice } = useInvoiceDetailDrawerContext(); - const handleCustomerLinkClick = () => {}; - return ( -

{invoice.formatted_amount}

+

{invoice.total_formatted}

@@ -75,11 +73,11 @@ export default function InvoiceDetailHeader() { textAlign={'right'} > - {invoice.formatted_due_amount} + {invoice.due_amount_formatted} - {invoice.formatted_payment_amount} + {invoice.payment_amount_formatted} { + closeAlert(name); + }; + + // Handle confirm delete item. + const handleConfirmDeleteItem = () => { + deleteTaxRate(taxRateId) + .then(() => { + AppToaster.show({ + message: 'The tax rate has been deleted successfully.', + intent: Intent.SUCCESS, + }); + closeDrawer(DRAWERS.TAX_RATE_DETAILS); + }) + .catch( + ({ + response: { + data: { errors }, + }, + }) => { + // handleDeleteErrors(errors); + }, + ) + .finally(() => { + closeAlert(name); + }); + }; + + return ( + } + confirmButtonText={} + icon="trash" + intent={Intent.DANGER} + isOpen={isOpen} + onCancel={handleCancelItemDelete} + onConfirm={handleConfirmDeleteItem} + loading={isLoading} + > +

+ Once you delete this tax rate, you won't be able to restore the item + later. +

+ +

+ Are you sure you want to delete ? If you're not sure, you can inactivate + it instead. +

+
+ ); +} + +export default compose( + withAlertStoreConnect(), + withAlertActions, + withItemsActions, + withDrawerActions, +)(TaxRateDeleteAlert); diff --git a/packages/webapp/src/containers/TaxRates/alerts/index.ts b/packages/webapp/src/containers/TaxRates/alerts/index.ts new file mode 100644 index 000000000..31680d125 --- /dev/null +++ b/packages/webapp/src/containers/TaxRates/alerts/index.ts @@ -0,0 +1,11 @@ +// @ts-nocheck +import React from 'react'; + +const TaxRateDeleteAlert = React.lazy(() => import('./TaxRateDeleteAlert')); + +/** + * Project alerts. + */ +export default [ + { name: 'tax-rate-delete', component: TaxRateDeleteAlert }, +]; diff --git a/packages/webapp/src/containers/TaxRates/containers/TaxRatesLandingActionsBar.tsx b/packages/webapp/src/containers/TaxRates/containers/TaxRatesLandingActionsBar.tsx new file mode 100644 index 000000000..2f370e0ce --- /dev/null +++ b/packages/webapp/src/containers/TaxRates/containers/TaxRatesLandingActionsBar.tsx @@ -0,0 +1,61 @@ +// @ts-nocheck +import React from 'react'; +import { NavbarGroup, NavbarDivider, Button, Classes } from '@blueprintjs/core'; +import { + DashboardActionsBar, + FormattedMessage as T, + Can, + Icon, +} from '@/components'; +import { AbilitySubject, TaxRateAction } from '@/constants/abilityOption'; +import { useTaxRatesLandingContext } from './TaxRatesLandingProvider'; + +import withDialogActions from '@/containers/Dialog/withDialogActions'; + +import { DialogsName } from '@/constants/dialogs'; +import { compose } from '@/utils'; + +/** + * Tax rates actions bar. + */ +function TaxRatesActionsBar({ + // #withDialogActions + openDialog, +}) { + // Items list context. + const {} = useTaxRatesLandingContext(); + + // Handle `new item` button click. + const onClickNewItem = () => { + openDialog(DialogsName.TaxRateForm); + }; + + return ( + + + + + + + + } + /> + ); +} diff --git a/packages/webapp/src/containers/TaxRates/containers/TaxRatesLandingProvider.tsx b/packages/webapp/src/containers/TaxRates/containers/TaxRatesLandingProvider.tsx new file mode 100644 index 000000000..e49b1b3f3 --- /dev/null +++ b/packages/webapp/src/containers/TaxRates/containers/TaxRatesLandingProvider.tsx @@ -0,0 +1,41 @@ +// @ts-nocheck +import React from 'react'; +import { isEmpty } from 'lodash'; +import { DashboardInsider } from '@/components/Dashboard'; +import { useTaxRates } from '@/hooks/query/taxRates'; + +const TaxRatesLandingContext = React.createContext(); + +/** + * Cash Flow data provider. + */ +function TaxRatesLandingProvider({ tableState, ...props }) { + // Fetch cash flow list . + const { + data: taxRates, + isFetching: isTaxRatesFetching, + isLoading: isTaxRatesLoading, + } = useTaxRates({}, { keepPreviousData: true }); + + // Detarmines whether the table should show empty state. + const isEmptyStatus = isEmpty(taxRates) && !isTaxRatesLoading; + + // Provider payload. + const provider = { + taxRates, + isTaxRatesFetching, + isTaxRatesLoading, + isEmptyStatus + }; + + return ( + + + + ); +} + +const useTaxRatesLandingContext = () => + React.useContext(TaxRatesLandingContext); + +export { TaxRatesLandingProvider, useTaxRatesLandingContext }; diff --git a/packages/webapp/src/containers/TaxRates/containers/TaxRatesLandingTable.tsx b/packages/webapp/src/containers/TaxRates/containers/TaxRatesLandingTable.tsx new file mode 100644 index 000000000..c20ad9bc7 --- /dev/null +++ b/packages/webapp/src/containers/TaxRates/containers/TaxRatesLandingTable.tsx @@ -0,0 +1,110 @@ +// @ts-nocheck +import React, { useCallback } from 'react'; +import { useHistory } from 'react-router-dom'; +import { + DataTable, + DashboardContentTable, + TableSkeletonHeader, + TableSkeletonRows, +} from '@/components'; + +import withAlertsActions from '@/containers/Alert/withAlertActions'; +import withDrawerActions from '@/containers/Drawer/withDrawerActions'; +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'; +import { TaxRatesTableActionsMenu } from './_components'; + +import { compose } from '@/utils'; +import { DRAWERS } from '@/constants/drawers'; +import { DialogsName } from '@/constants/dialogs'; + +/** + * Invoices datatable. + */ +function TaxRatesDataTable({ + // #withAlertsActions + openAlert, + + // #withDrawerActions + openDrawer, + + // #withDialogAction + openDialog, +}) { + // Invoices list context. + const { taxRates, isTaxRatesLoading, isEmptyStatus } = + useTaxRatesLandingContext(); + + // Invoices table columns. + const columns = useTaxRatesTableColumns(); + + // Handle delete tax rate. + const handleDeleteTaxRate = ({ id }) => { + openAlert('tax-rate-delete', { taxRateId: id }); + }; + // Handle edit tax rate. + const handleEditTaxRate = (taxRate) => { + openDialog(DialogsName.TaxRateForm, { id: taxRate.id }); + }; + // Handle view details tax rate. + const handleViewDetails = (taxRate) => { + openDrawer(DRAWERS.TAX_RATE_DETAILS, { taxRateId: taxRate.id }); + }; + // Handle table cell click. + const handleCellClick = (cell, event) => { + openDrawer(DRAWERS.TAX_RATE_DETAILS, { taxRateId: cell.row.original.id }); + }; + // Display invoice empty status instead of the table. + if (isEmptyStatus) { + return ; + } + + return ( + + + + ); +} + +export default compose( + withDashboardActions, + withAlertsActions, + withDrawerActions, + withDialogActions, + withSettings(({ invoiceSettings }) => ({ + invoicesTableSize: invoiceSettings?.tableSize, + })), +)(TaxRatesDataTable); diff --git a/packages/webapp/src/containers/TaxRates/containers/_components.tsx b/packages/webapp/src/containers/TaxRates/containers/_components.tsx new file mode 100644 index 000000000..5c3e50a34 --- /dev/null +++ b/packages/webapp/src/containers/TaxRates/containers/_components.tsx @@ -0,0 +1,42 @@ +// @ts-nocheck +import React from 'react'; +import { Can, Icon } from '@/components'; +import { AbilitySubject, TaxRateAction } from '@/constants/abilityOption'; +import { safeCallback } from '@/utils'; +import { Intent, Menu, MenuDivider, MenuItem } from '@blueprintjs/core'; + +/** + * Tax rates table actions menu. + * @returns {JSX.Element} + */ +export function TaxRatesTableActionsMenu({ + payload: { onEdit, onDelete, onViewDetails }, + row: { original }, +}) { + return ( + + } + text={'View Details'} + onClick={safeCallback(onViewDetails, original)} + /> + + + } + text={'Edit Tax Rate'} + onClick={safeCallback(onEdit, original)} + /> + + + + } + /> + + + ); +} diff --git a/packages/webapp/src/containers/TaxRates/containers/_utils.tsx b/packages/webapp/src/containers/TaxRates/containers/_utils.tsx new file mode 100644 index 000000000..2eb2fbb21 --- /dev/null +++ b/packages/webapp/src/containers/TaxRates/containers/_utils.tsx @@ -0,0 +1,53 @@ +// @ts-nocheck +import React from 'react'; +import { Button, Intent, Tag, Icon } from '@blueprintjs/core'; +import { Align } from '@/constants'; +import { FormatDateCell } from '@/components'; + +const codeAccessor = (taxRate) => { + return ( + + {taxRate.code} + + ); +}; + +const statusAccessor = (taxRate) => { + return ( + + Active + + ); +}; + +export const useTaxRatesTableColumns = () => { + return [ + { + Header: 'Name', + accessor: 'name', + width: 40, + }, + { + Header: 'Code', + accessor: codeAccessor, + width: 40, + }, + { + Header: 'Rate', + accessor: 'rate_formatted', + align: Align.Right, + width: 30, + }, + { + Header: 'Description', + accessor: () => Specital tax for certain goods and services., + width: 120, + }, + { + Header: 'Status', + accessor: statusAccessor, + width: 30, + align: Align.Right, + }, + ]; +}; diff --git a/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateForm.schema.ts b/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateForm.schema.ts new file mode 100644 index 000000000..929197460 --- /dev/null +++ b/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateForm.schema.ts @@ -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; diff --git a/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialog.tsx b/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialog.tsx new file mode 100644 index 000000000..4a48c4f16 --- /dev/null +++ b/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialog.tsx @@ -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 ( + + + + + + ); +} + +export default compose(withDialogRedux())(TaxRateFormDialog); diff --git a/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialogBoot.tsx b/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialogBoot.tsx new file mode 100644 index 000000000..5fc9dbbfe --- /dev/null +++ b/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialogBoot.tsx @@ -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 ( + + + + ); +} + +const useTaxRateFormDialogContext = () => + React.useContext(TaxRateFormDialogContext); + +export { TaxRateFormDialogBoot, useTaxRateFormDialogContext }; diff --git a/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialogContent.tsx b/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialogContent.tsx new file mode 100644 index 000000000..ecfd37a4f --- /dev/null +++ b/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialogContent.tsx @@ -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 ( + + + + ); +} diff --git a/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialogForm.tsx b/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialogForm.tsx new file mode 100644 index 000000000..74933746b --- /dev/null +++ b/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialogForm.tsx @@ -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 ( + +
+
+ +
+ + +
+ ); +} + +export default compose(withDialogActions)(TaxRateFormDialogForm); diff --git a/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialogFormContent.tsx b/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialogFormContent.tsx new file mode 100644 index 000000000..0c3bc540c --- /dev/null +++ b/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialogFormContent.tsx @@ -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 ( +
+ Required} + subLabel={ + 'The name as you would like it to appear in customers invoices.' + } + > + + + + Required} + > + + + + Required} + > + %} + fill={false} + /> + + + + } + > + + + + + + + + + + +
+ ); +} + +const RateFormGroup = styled(FInputGroup)` + max-width: 100px; +`; + +const CompoundFormGroup = styled(FFormGroup)` + margin-bottom: 0; +`; diff --git a/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialogFormFooter.tsx b/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialogFormFooter.tsx new file mode 100644 index 000000000..b49bdc7eb --- /dev/null +++ b/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/TaxRateFormDialogFormFooter.tsx @@ -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 ( +
+
+ + + +
+
+ ); +} + +export const TaxRateFormDialogFormFooter = R.compose(withDialogActions)( + TaxRateFormDialogFormFooterRoot, +); diff --git a/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/utils.ts b/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/utils.ts new file mode 100644 index 000000000..7a746cb6d --- /dev/null +++ b/packages/webapp/src/containers/TaxRates/dialogs/TaxRateFormDialog/utils.ts @@ -0,0 +1,7 @@ +export const transformApiErrors = () => { + return {}; +}; + +export const transformFormToReq = () => { + return {}; +}; diff --git a/packages/webapp/src/containers/TaxRates/drawers/TaxRateDetailsDrawer/TaxRateDetailsContent.tsx b/packages/webapp/src/containers/TaxRates/drawers/TaxRateDetailsDrawer/TaxRateDetailsContent.tsx new file mode 100644 index 000000000..a831d3320 --- /dev/null +++ b/packages/webapp/src/containers/TaxRates/drawers/TaxRateDetailsDrawer/TaxRateDetailsContent.tsx @@ -0,0 +1,29 @@ +// @ts-nocheck +import React from 'react'; +import TaxRateDetailsContentActionsBar from './TaxRateDetailsContentActionsBar'; +import { TaxRateDetailsContentBoot } from './TaxRateDetailsContentBoot'; +import { DrawerBody, DrawerHeaderContent } from '@/components'; +import TaxRateDetailsContentDetails from './TaxRateDetailsContentDetails'; +import { DRAWERS } from '@/constants/drawers'; + +interface TaxRateDetailsContentProps { + taxRateid: number; +} + +export default function TaxRateDetailsContent({ + taxRateId, +}: TaxRateDetailsContentProps) { + return ( + + + + + + + + + ); +} diff --git a/packages/webapp/src/containers/TaxRates/drawers/TaxRateDetailsDrawer/TaxRateDetailsContentActionsBar.tsx b/packages/webapp/src/containers/TaxRates/drawers/TaxRateDetailsDrawer/TaxRateDetailsContentActionsBar.tsx new file mode 100644 index 000000000..e87d6f63b --- /dev/null +++ b/packages/webapp/src/containers/TaxRates/drawers/TaxRateDetailsDrawer/TaxRateDetailsContentActionsBar.tsx @@ -0,0 +1,71 @@ +// @ts-nocheck +import React from 'react'; +import { + Button, + Classes, + Intent, + NavbarDivider, + NavbarGroup, +} from '@blueprintjs/core'; +import * as R from 'ramda'; +import { Can, DashboardActionsBar, Icon } from '@/components'; +import { AbilitySubject, TaxRateAction } from '@/constants/abilityOption'; +import withDrawerActions from '@/containers/Drawer/withDrawerActions'; +import withAlertsActions from '@/containers/Alert/withAlertActions'; +import withDialogActions from '@/containers/Dialog/withDialogActions'; +import { useTaxRateDetailsContext } from './TaxRateDetailsContentBoot'; +import { DialogsName } from '@/constants/dialogs'; + +/** + * Tax rate details content actions bar. + * @returns {JSX.Element} + */ +function TaxRateDetailsContentActionsBar({ + // #withDrawerActions + openDialog, + + // #withAlertsActions + openAlert, +}) { + const { taxRateId } = useTaxRateDetailsContext(); + + // Handle edit tax rate. + const handleEditTaxRate = () => { + openDialog(DialogsName.TaxRateForm, { id: taxRateId }); + }; + // Handle delete tax rate. + const handleDeleteTaxRate = () => { + openAlert('tax-rate-delete', { taxRateId }); + }; + + return ( + + + +