feat: wip invoice customizer

This commit is contained in:
Ahmed Bouhuolia
2024-09-09 19:40:23 +02:00
parent 132c1dfdbe
commit dc18bde6be
18 changed files with 602 additions and 194 deletions

View File

@@ -23,10 +23,11 @@ import WarehouseTransferDetailDrawer from '@/containers/Drawers/WarehouseTransfe
import TaxRateDetailsDrawer from '@/containers/TaxRates/drawers/TaxRateDetailsDrawer/TaxRateDetailsDrawer';
import CategorizeTransactionDrawer from '@/containers/CashFlow/CategorizeTransaction/drawers/CategorizeTransactionDrawer/CategorizeTransactionDrawer';
import ChangeSubscriptionPlanDrawer from '@/containers/Subscriptions/drawers/ChangeSubscriptionPlanDrawer/ChangeSubscriptionPlanDrawer';
import { InvoiceCustomizeDrawer } from '@/containers/Sales/Invoices/InvoiceCustomize/InvoiceCustomizeDrawer';
import { EstimateCustomizeDrawer } from '@/containers/Sales/Estimates/EstimateCustomize/EstimateCustomizeDrawer';
import { ReceiptCustomizeDrawer } from '@/containers/Sales/Receipts/ReceiptCustomize/ReceiptCustomizeDrawer';
import { DRAWERS } from '@/constants/drawers';
import { InvoiceCustomizeDrawer } from '@/containers/Sales/Invoices/InvoiceCustomize/InvoiceCustomizeDrawer';
/**
* Drawers container of the dashboard.
*/
@@ -67,6 +68,8 @@ export default function DrawersContainer() {
<CategorizeTransactionDrawer name={DRAWERS.CATEGORIZE_TRANSACTION} />
<ChangeSubscriptionPlanDrawer name={DRAWERS.CHANGE_SUBSCARIPTION_PLAN} />
<InvoiceCustomizeDrawer name={DRAWERS.INVOICE_CUSTOMIZE} />
<EstimateCustomizeDrawer name={DRAWERS.ESTIMATE_CUSTOMIZE} />
<ReceiptCustomizeDrawer name={DRAWERS.RECEIPT_CUSTOMIZE} />
</div>
);
}

View File

@@ -0,0 +1,3 @@
export default function EstimateCustomizeContent() {
return <h1>Hello World</h1>;
}

View File

@@ -0,0 +1,33 @@
// @ts-nocheck
import React from 'react';
import * as R from 'ramda';
import { Drawer, DrawerSuspense } from '@/components';
import withDrawers from '@/containers/Drawer/withDrawers';
const EstimateCustomizeContent = React.lazy(
() => import('./EstimateCustomizeContent'),
);
/**
* Estimate customize drawer.
* @returns {React.ReactNode}
*/
function EstimateCustomizeDrawerRoot({
name,
// #withDrawer
isOpen,
payload: {},
}) {
return (
<Drawer isOpen={isOpen} name={name} size={'100%'}>
<DrawerSuspense>
<EstimateCustomizeContent />
</DrawerSuspense>
</Drawer>
);
}
export const EstimateCustomizeDrawer = R.compose(withDrawers())(
EstimateCustomizeDrawerRoot,
);

View File

@@ -7,6 +7,11 @@ import {
NavbarGroup,
Intent,
Alignment,
Menu,
MenuItem,
Popover,
PopoverInteractionKind,
Position,
} from '@blueprintjs/core';
import { useHistory } from 'react-router-dom';
@@ -35,6 +40,8 @@ import { useDownloadExportPdf } from '@/hooks/query/FinancialReports/use-export-
import { SaleEstimateAction, AbilitySubject } from '@/constants/abilityOption';
import { compose } from '@/utils';
import { DialogsName } from '@/constants/dialogs';
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
import { DRAWERS } from '@/constants/drawers';
/**
* Estimates list actions bar.
@@ -52,6 +59,9 @@ function EstimateActionsBar({
// #withDialogActions
openDialog,
// #withDrawerActions
openDrawer,
// #withSettingsActions
addSetting,
}) {
@@ -96,6 +106,10 @@ function EstimateActionsBar({
const handlePrintBtnClick = () => {
downloadExportPdf({ resource: 'SaleEstimate' });
};
// Handle customize button clicl.
const handleCustomizeBtnClick = () => {
openDrawer(DRAWERS.ESTIMATE_CUSTOMIZE);
};
return (
<DashboardActionsBar>
@@ -167,6 +181,25 @@ function EstimateActionsBar({
</NavbarGroup>
<NavbarGroup align={Alignment.RIGHT}>
<Popover
minimal={true}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_RIGHT}
modifiers={{
offset: { offset: '0, 4' },
}}
content={
<Menu>
<MenuItem
onClick={handleCustomizeBtnClick}
text={'Customize Estimate'}
/>
</Menu>
}
>
<Button icon={<Icon icon="cog-16" iconSize={16} />} minimal={true} />
</Popover>
<NavbarDivider />
<Button
className={Classes.MINIMAL}
icon={<Icon icon="refresh-16" iconSize={14} />}
@@ -187,4 +220,5 @@ export default compose(
estimatesTableSize: estimatesSettings?.tableSize,
})),
withDialogActions,
withDrawerActions,
)(EstimateActionsBar);

View File

@@ -0,0 +1,74 @@
import React from 'react';
import { Box, Group } from '@/components';
import { InvoiceCustomizeProvider } from './InvoiceCustomizeProvider';
import {
InvoiceCustomizeForm,
InvoiceCustomizeFormProps,
} from './InvoiceCustomizerForm';
import { InvoiceCustomizeTabsControllerProvider } from './InvoiceCustomizeTabsController';
import { InvoiceCustomizeFields } from './InvoiceCustomizeFields';
import { InvoiceCustomizePreview } from './InvoiceCustomizePreview';
import { extractChildren } from '@/utils/extract-children';
export interface InvoiceCustomizeProps<T> extends InvoiceCustomizeFormProps<T> {
children?: React.ReactNode;
}
export function InvoiceCustomize<T>({
initialValues,
validationSchema,
onSubmit,
children,
}: InvoiceCustomizeProps<T>) {
const PaperTemplate = React.useMemo(
() => extractChildren(children, InvoiceCustomize.PaperTemplate),
[children],
);
const CustomizeTabs = React.useMemo(
() => extractChildren(children, InvoiceCustomize.FieldsTab),
[children],
);
const value = { PaperTemplate, CustomizeTabs };
return (
<InvoiceCustomizeForm
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={onSubmit}
>
<InvoiceCustomizeTabsControllerProvider>
<InvoiceCustomizeProvider value={value}>
<Group spacing={0} align="stretch">
<InvoiceCustomizeFields />
<InvoiceCustomizePreview />
</Group>
</InvoiceCustomizeProvider>
</InvoiceCustomizeTabsControllerProvider>
</InvoiceCustomizeForm>
);
}
export interface InvoiceCustomizePaperTemplateProps {
children?: React.ReactNode;
}
InvoiceCustomize.PaperTemplate = ({
children,
}: InvoiceCustomizePaperTemplateProps) => {
return <Box>{children}</Box>;
};
export interface InvoiceCustomizeContentProps {
id: string;
label: string;
children?: React.ReactNode;
}
InvoiceCustomize.FieldsTab = ({
id,
label,
children,
}: InvoiceCustomizeContentProps) => {
return <Box>{children}</Box>;
};

View File

@@ -1,21 +1,65 @@
import { Box, Group } from '@/components';
import { InvoiceCustomizePreview } from './InvoiceCustomizePreview';
import { InvoiceCustomizeFields } from './InvoiceCustomizeFields';
import { InvoiceCustomizeForm } from './InvoiceCustomizerForm';
import React from 'react';
import { Box } from '@/components';
import { Classes } from '@blueprintjs/core';
import { InvoiceCustomizeTabsControllerProvider } from './InvoiceCustomizeTabsController';
import { InvoicePaperTemplate } from './InvoicePaperTemplate';
import { InvoiceCustomize } from './InvoiceCustomize';
import { InvoiceCustomizeGeneralField } from './InvoiceCustomizeGeneralFields';
import { InvoiceCustomizeContentFields } from './InvoiceCutomizeContentFields';
interface InvoiceCustomizeValues {
invoiceNumber?: string;
invoiceNumberLabel?: string;
dateIssue?: string;
dateIssueLabel?: string;
dueDate?: string;
dueDateLabel?: string;
companyName?: string;
bigtitle?: string;
itemRateLabel?: string;
itemQuantityLabel?: string;
itemTotalLabel?: string;
// Totals
showDueAmount?: boolean;
showDiscount?: boolean;
showPaymentMade?: boolean;
showTaxes?: boolean;
showSubtotal?: boolean;
showTotal?: boolean;
showBalanceDue?: boolean;
paymentMadeLabel?: string;
discountLabel?: string;
subtotalLabel?: string;
totalLabel?: string;
balanceDueLabel?: string;
}
export default function InvoiceCustomizeContent() {
return (
<Box className={Classes.DRAWER_BODY}>
<InvoiceCustomizeForm>
<Group spacing={0} align="stretch">
<InvoiceCustomizeTabsControllerProvider>
<InvoiceCustomizeFields />
<InvoiceCustomizePreview />
</InvoiceCustomizeTabsControllerProvider>
</Group>
</InvoiceCustomizeForm>
<InvoiceCustomize<InvoiceCustomizeValues>>
<InvoiceCustomize.PaperTemplate>
<InvoicePaperTemplate />
</InvoiceCustomize.PaperTemplate>
<InvoiceCustomize.FieldsTab id={'general'} label={'General'}>
<InvoiceCustomizeGeneralField />
</InvoiceCustomize.FieldsTab>
<InvoiceCustomize.FieldsTab id={'content'} label={'Content'}>
<InvoiceCustomizeContentFields />
</InvoiceCustomize.FieldsTab>
<InvoiceCustomize.FieldsTab id={'totals'} label={'Totals'}>
asdfasdfdsaf #3
</InvoiceCustomize.FieldsTab>
</InvoiceCustomize>
</Box>
);
}

View File

@@ -9,8 +9,8 @@ const InvoiceCustomizeContent = React.lazy(
);
/**
* Refund credit note detail.
* @returns
* Invoice customize drawer.
* @returns {React.ReactNode}
*/
function InvoiceCustomizeDrawerRoot({
name,
@@ -19,12 +19,7 @@ function InvoiceCustomizeDrawerRoot({
payload: {},
}) {
return (
<Drawer
isOpen={isOpen}
name={name}
size={'100%'}
>
<Drawer isOpen={isOpen} name={name} size={'100%'}>
<DrawerSuspense>
<InvoiceCustomizeContent />
</DrawerSuspense>

View File

@@ -1,16 +1,16 @@
// @ts-nocheck
import React from 'react';
import * as R from 'ramda';
import { Box, Group, Stack } from '@/components';
import { Button, Intent } from '@blueprintjs/core';
import { Group, Stack } from '@/components';
import { InvoiceCustomizeHeader } from './InvoiceCustomizeHeader';
import { InvoiceCustomizeTabs } from './InvoiceCustomizeTabs';
import { InvoiceCustomizeGeneralField } from './InvoiceCustomizeGeneralFields';
import { useInvoiceCustomizeTabsController } from './InvoiceCustomizeTabsController';
import { Button, Intent } from '@blueprintjs/core';
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
import { useFormikContext } from 'formik';
import { InvoiceCustomizeContentFields } from './InvoiceCutomizeContentFields';
import { useInvoiceCustomizeContext } from './InvoiceCustomizeProvider';
import styles from './InvoiceCustomizeFields.module.scss';
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
export function InvoiceCustomizeFields() {
return (
@@ -23,14 +23,22 @@ export function InvoiceCustomizeFields() {
export function InvoiceCustomizeFieldsMain() {
const { currentTabId } = useInvoiceCustomizeTabsController();
const { CustomizeTabs } = useInvoiceCustomizeContext();
const CustomizeTabPanel = React.useMemo(
() =>
React.Children.map(CustomizeTabs, (tab) => {
return tab.props.id === currentTabId ? tab : null;
}).filter(Boolean),
[CustomizeTabs, currentTabId],
);
return (
<Stack spacing={0} className={styles.mainFields}>
<InvoiceCustomizeHeader label={'Customize'} />
<Stack spacing={0} style={{ flex: '1 1 auto', overflow: 'auto' }}>
{currentTabId === 'general' && <InvoiceCustomizeGeneralField />}
{currentTabId === 'content' && <InvoiceCustomizeContentFields />}
{CustomizeTabPanel}
<InvoiceCustomizeFooterActions />
</Stack>
</Stack>

View File

@@ -1,17 +1,19 @@
import { Box } from '@/components';
import { PaperTemplate } from './PaperTemplate';
import { useInvoiceCustomizeContext } from './InvoiceCustomizeProvider';
export function InvoiceCustomizePreviewContent() {
const { PaperTemplate } = useInvoiceCustomizeContext();
return (
<Box
style={{
padding: '28px 24px 40px',
backgroundColor: '#F5F5F5',
overflow: 'auto',
flex: '1'
flex: '1',
}}
>
<PaperTemplate />
{PaperTemplate}
</Box>
);
}

View File

@@ -0,0 +1,32 @@
import React, { createContext, useContext } from 'react';
interface InvoiceCustomizeValue {
PaperTemplate?: React.ReactNode;
CustomizeTabs: React.ReactNode;
}
const InvoiceCustomizeContext = createContext<InvoiceCustomizeValue>(
{} as InvoiceCustomizeValue,
);
export const InvoiceCustomizeProvider: React.FC<{
value: InvoiceCustomizeValue;
children: React.ReactNode;
}> = ({ value, children }) => {
return (
<InvoiceCustomizeContext.Provider value={{ ...value }}>
{children}
</InvoiceCustomizeContext.Provider>
);
};
export const useInvoiceCustomizeContext = (): InvoiceCustomizeValue => {
const context = useContext<InvoiceCustomizeValue>(InvoiceCustomizeContext);
if (!context) {
throw new Error(
'useInvoiceCustomize must be used within an InvoiceCustomizeProvider',
);
}
return context;
};

View File

@@ -6,10 +6,17 @@ import {
useInvoiceCustomizeTabsController,
} from './InvoiceCustomizeTabsController';
import styles from './InvoiceCustomizeTabs.module.scss';
import { useInvoiceCustomizeContext } from './InvoiceCustomizeProvider';
import React from 'react';
export function InvoiceCustomizeTabs() {
const { setCurrentTabId } = useInvoiceCustomizeTabsController();
const { CustomizeTabs } = useInvoiceCustomizeContext();
const tabItems = React.Children.map(CustomizeTabs, (node) => ({
...(React.isValidElement(node) ? node.props : {}),
}));
const handleChange = (value: InvoiceCustomizeTabsEnum) => {
setCurrentTabId(value);
};
@@ -25,9 +32,9 @@ export function InvoiceCustomizeTabs() {
onChange={handleChange}
className={styles.tabsList}
>
<Tab id="general" title="General" />
<Tab id="content" title="Content" />
<Tab id="total" title="Total" />
{tabItems?.map(({ id, label }: { id: string; label: string }) => (
<Tab id={id} key={id} title={label} />
))}
</Tabs>
</Box>
</Stack>

View File

@@ -1,25 +1,25 @@
import { Formik, Form } from 'formik';
import * as Yup from 'yup';
// @ts-nocheck
import React from 'react';
import { Formik, Form, FormikHelpers } from 'formik';
const validationSchema = Yup.object().shape({
invoiceNumber: Yup.string().required('Invoice number is required'),
customerName: Yup.string().required('Customer name is required'),
amount: Yup.number()
.required('Amount is required')
.positive('Amount must be positive'),
});
interface InvoiceCustomizeFormProps {
children: React.ReactNode;
export interface InvoiceCustomizeFormProps<T> {
initialValues?: T;
validationSchema?: any;
onSubmit?: (values: T, formikHelpers: FormikHelpers<T>) => void;
children?: React.ReactNode;
}
export function InvoiceCustomizeForm({ children }: InvoiceCustomizeFormProps) {
export function InvoiceCustomizeForm<T>({
initialValues,
validationSchema,
onSubmit,
children,
}: InvoiceCustomizeFormProps<T>) {
return (
<Formik
initialValues={{ invoiceNumber: '', customerName: '', amount: '' }}
<Formik<T>
initialValues={{ ...initialValues }}
validationSchema={validationSchema}
onSubmit={(values) => {}}
onSubmit={(value, helpers) => onSubmit && onSubmit(value, helpers)}
>
<Form>{children}</Form>
</Formik>

View File

@@ -0,0 +1,235 @@
import clsx from 'classnames';
import styles from './PaperTemplate.module.scss';
interface PaperTemplateProps {
invoiceNumber?: string;
invoiceNumberLabel?: string;
dateIssue?: string;
dateIssueLabel?: string;
dueDate?: string;
dueDateLabel?: string;
companyName?: string;
bigtitle?: string;
itemRateLabel?: string;
itemQuantityLabel?: string;
itemTotalLabel?: string;
// Totals
showDueAmount?: boolean;
showDiscount?: boolean;
showPaymentMade?: boolean;
showTaxes?: boolean;
showSubtotal?: boolean;
showTotal?: boolean;
showBalanceDue?: boolean;
paymentMadeLabel?: string;
discountLabel?: string;
subtotalLabel?: string;
totalLabel?: string;
balanceDueLabel?: string;
}
export function InvoicePaperTemplate({
bigtitle = 'Invoice',
companyName = 'Bigcapital Technology, Inc.',
// dueDateLabel,
dueDate = 'September 3, 2024',
dueDateLabel = 'Date due',
dateIssue = 'September 3, 2024',
dateIssueLabel = 'Date of issue',
// dateIssue,
invoiceNumberLabel = 'Invoice number',
invoiceNumber = '346D3D40-0001',
// Entries
itemQuantityLabel = 'Quantity',
itemRateLabel = 'Rate',
itemTotalLabel = 'Total',
totalLabel = 'Total',
subtotalLabel = 'Subtotal',
discountLabel = 'Discount',
paymentMadeLabel = 'Payment Made',
balanceDueLabel = 'Balance Due',
// Totals
showTotal = true,
showSubtotal = true,
showDiscount = true,
showTaxes = true,
showPaymentMade = true,
showDueAmount = true,
showBalanceDue = true,
}: PaperTemplateProps) {
return (
<div className={styles.root}>
<div>
<h1 className={styles.bigTitle}>{bigtitle}</h1>
<div className={styles.logoWrap}>
<img
alt=""
src="https://cdn-development.mercury.com/demo-assets/avatars/mercury-demo-dark.png"
/>
</div>
</div>
<div className={styles.details}>
<div className={styles.detail}>
<div className={styles.detailLabel}>{invoiceNumberLabel}</div>
<div>{invoiceNumber}</div>
</div>
<div className={styles.detail}>
<div className={styles.detailLabel}>{dateIssueLabel}</div>
<div>{dateIssue}</div>
</div>
<div className={styles.detail}>
<div className={styles.detailLabel}>{dueDateLabel}</div>
<div>{dueDate}</div>
</div>
</div>
<div className={styles.addressRoot}>
<div className={styles.addressBillTo}>
<strong>{companyName}</strong> <br />
131 Continental Dr Suite 305 Newark,
<br />
Delaware 19713
<br />
United States
<br />
+1 762-339-5634
<br />
ahmed@bigcapital.app
</div>
<div className={styles.addressFrom}>
<strong>Billed To</strong> <br />
Bigcapital Technology, Inc. <br />
131 Continental Dr Suite 305 Newark,
<br />
Delaware 19713
<br />
United States
<br />
+1 762-339-5634
<br />
ahmed@bigcapital.app
</div>
</div>
<table className={styles.table}>
<thead>
<tr>
<th>Item</th>
<th>Description</th>
<th className={styles.rate}>{itemRateLabel}</th>
<th className={styles.total}>{itemTotalLabel}</th>
</tr>
</thead>
<tbody className={styles.tableBody}>
<tr>
<td>Simply dummy text</td>
<td>Simply dummy text of the printing and typesetting</td>
<td className={styles.rate}>1 X $100,00</td>
<td className={styles.total}>$100,00</td>
</tr>
</tbody>
</table>
<div style={{ display: 'flex' }}>
<div className={styles.totals}>
{showSubtotal && (
<div
className={clsx(
styles.totalsItem,
styles.totalBottomGrayBordered,
)}
>
<div className={styles.totalsItemLabel}>{subtotalLabel}</div>
<div className={styles.totalsItemAmount}>630.00</div>
</div>
)}
{showDiscount && (
<div className={styles.totalsItem}>
<div className={styles.totalsItemLabel}>{discountLabel}</div>
<div className={styles.totalsItemAmount}>0.00</div>
</div>
)}
{showTaxes && (
<>
<div className={styles.totalsItem}>
<div className={styles.totalsItemLabel}>
Sample Tax1 (4.70%)
</div>
<div className={styles.totalsItemAmount}>11.75</div>
</div>
<div className={styles.totalsItem}>
<div className={styles.totalsItemLabel}>
Sample Tax2 (7.00%)
</div>
<div className={styles.totalsItemAmount}>21.00</div>
</div>
</>
)}
{showTotal && (
<div
className={clsx(styles.totalsItem, styles.totalBottomBordered)}
>
<div className={styles.totalsItemLabel}>{totalLabel}</div>
<div className={styles.totalsItemAmount}>$662.75</div>
</div>
)}
{showPaymentMade && (
<div className={styles.totalsItem}>
<div className={styles.totalsItemLabel}>{paymentMadeLabel}</div>
<div className={styles.totalsItemAmount}>100.00</div>
</div>
)}
{showBalanceDue && (
<div
className={clsx(styles.totalsItem, styles.totalBottomBordered)}
>
<div className={styles.totalsItemLabel}>{balanceDueLabel}</div>
<div className={styles.totalsItemAmount}>$562.75</div>
</div>
)}
</div>
</div>
<div className={styles.paragraph}>
<div className={styles.paragraphLabel}>Terms & Conditions</div>
<div>
It is a long established fact that a reader will be distracted by the
readable content of a page when looking at its layout.
</div>
</div>
<div className={styles.paragraph}>
<div className={styles.paragraphLabel}>Statement</div>
<div>
It is a long established fact that a reader will be distracted by the
readable content of a page when looking at its layout.
</div>
</div>
</div>
);
}

View File

@@ -1,141 +0,0 @@
import clsx from 'classnames';
import styles from './PaperTemplate.module.scss';
export function PaperTemplate() {
return (
<div className={styles.root}>
<div>
<h1 className={styles.bigTitle}>Invoice</h1>
<div className={styles.logoWrap}>
<img
alt=""
src="https://cdn-development.mercury.com/demo-assets/avatars/mercury-demo-dark.png"
/>
</div>
</div>
<div className={styles.details}>
<div className={styles.detail}>
<div className={styles.detailLabel}>Invoice number</div>
<div>346D3D40-0001</div>
</div>
<div className={styles.detail}>
<div className={styles.detailLabel}>Date of issue</div>
<div>September 3, 2024</div>
</div>
<div className={styles.detail}>
<div className={styles.detailLabel}>Date due</div>
<div>October 3, 2024</div>
</div>
</div>
<div className={styles.addressRoot}>
<div className={styles.addressBillTo}>
<strong>Bigcapital Technology, Inc.</strong> <br />
131 Continental Dr Suite 305 Newark,
<br />
Delaware 19713
<br />
United States
<br />
+1 762-339-5634
<br />
ahmed@bigcapital.app
</div>
<div className={styles.addressFrom}>
<strong>Billed To</strong> <br />
Bigcapital Technology, Inc. <br />
131 Continental Dr Suite 305 Newark,
<br />
Delaware 19713
<br />
United States
<br />
+1 762-339-5634
<br />
ahmed@bigcapital.app
</div>
</div>
<table className={styles.table}>
<thead>
<tr>
<th>Item</th>
<th>Description</th>
<th className={styles.rate}>Rate</th>
<th className={styles.total}>Total</th>
</tr>
</thead>
<tbody className={styles.tableBody}>
<tr>
<td>Simply dummy text</td>
<td>Simply dummy text of the printing and typesetting</td>
<td className={styles.rate}>1 X $100,00</td>
<td className={styles.total}>$100,00</td>
</tr>
</tbody>
</table>
<div style={{ display: 'flex' }}>
<div className={styles.totals}>
<div
className={clsx(styles.totalsItem, styles.totalBottomGrayBordered)}
>
<div className={styles.totalsItemLabel}>Sub Total</div>
<div className={styles.totalsItemAmount}>630.00</div>
</div>
<div className={styles.totalsItem}>
<div className={styles.totalsItemLabel}>Discount</div>
<div className={styles.totalsItemAmount}>0.00</div>
</div>
<div className={styles.totalsItem}>
<div className={styles.totalsItemLabel}>Sample Tax1 (4.70%)</div>
<div className={styles.totalsItemAmount}>11.75</div>
</div>
<div className={styles.totalsItem}>
<div className={styles.totalsItemLabel}>Sample Tax2 (7.00%)</div>
<div className={styles.totalsItemAmount}>21.00</div>
</div>
<div className={clsx(styles.totalsItem, styles.totalBottomBordered)}>
<div className={styles.totalsItemLabel}>Total</div>
<div className={styles.totalsItemAmount}>$662.75</div>
</div>
<div className={styles.totalsItem}>
<div className={styles.totalsItemLabel}>Payment Made</div>
<div className={styles.totalsItemAmount}>100.00</div>
</div>
<div className={clsx(styles.totalsItem, styles.totalBottomBordered)}>
<div className={styles.totalsItemLabel}>Balance Due</div>
<div className={styles.totalsItemAmount}>$562.75</div>
</div>
</div>
</div>
<div className={styles.paragraph}>
<div className={styles.paragraphLabel}>Terms & Conditions</div>
<div>
It is a long established fact that a reader will be distracted by the
readable content of a page when looking at its layout.
</div>
</div>
<div className={styles.paragraph}>
<div className={styles.paragraphLabel}>Statement</div>
<div>
It is a long established fact that a reader will be distracted by the
readable content of a page when looking at its layout.
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,3 @@
export default function ReceiptCustomizeContent() {
return <h1>asdasdasd</h1>;
}

View File

@@ -0,0 +1,32 @@
// @ts-nocheck
import React from 'react';
import * as R from 'ramda';
import { Drawer, DrawerSuspense } from '@/components';
import withDrawers from '@/containers/Drawer/withDrawers';
const ReceiptCustomizeContent = React.lazy(
() => import('./ReceiptCustomizeContent'),
);
/**
* Receipt customize drawer.
* @returns {React.ReactNode}
*/
function ReceiptCustomizeDrawerRoot({
name,
// #withDrawer
isOpen,
payload: {},
}) {
return (
<Drawer isOpen={isOpen} name={name} size={'100%'}>
<DrawerSuspense>
<ReceiptCustomizeContent />
</DrawerSuspense>
</Drawer>
);
}
export const ReceiptCustomizeDrawer = R.compose(withDrawers())(
ReceiptCustomizeDrawerRoot,
);

View File

@@ -7,6 +7,11 @@ import {
NavbarGroup,
Intent,
Alignment,
Popover,
PopoverInteractionKind,
Position,
Menu,
MenuItem,
} from '@blueprintjs/core';
import { useHistory } from 'react-router-dom';
@@ -38,6 +43,8 @@ import { SaleReceiptAction, AbilitySubject } from '@/constants/abilityOption';
import { DialogsName } from '@/constants/dialogs';
import { compose } from '@/utils';
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
import { DRAWERS } from '@/constants/drawers';
/**
* Receipts actions bar.
@@ -55,6 +62,9 @@ function ReceiptActionsBar({
// #withDialogActions
openDialog,
// #withDrawerActions
openDrawer,
// #withSettingsActions
addSetting,
}) {
@@ -103,6 +113,10 @@ function ReceiptActionsBar({
const handlePrintButtonClick = () => {
downloadExportPdf({ resource: 'SaleReceipt' });
};
// Handle customize button click.
const handleCustomizeBtnClick = () => {
openDrawer(DRAWERS.RECEIPT_CUSTOMIZE);
};
return (
<DashboardActionsBar>
@@ -173,6 +187,25 @@ function ReceiptActionsBar({
<NavbarDivider />
</NavbarGroup>
<NavbarGroup align={Alignment.RIGHT}>
<Popover
minimal={true}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM_RIGHT}
modifiers={{
offset: { offset: '0, 4' },
}}
content={
<Menu>
<MenuItem
onClick={handleCustomizeBtnClick}
text={'Customize Receipt'}
/>
</Menu>
}
>
<Button icon={<Icon icon="cog-16" iconSize={16} />} minimal={true} />
</Popover>
<NavbarDivider />
<Button
className={Classes.MINIMAL}
icon={<Icon icon="refresh-16" iconSize={14} />}
@@ -193,4 +226,5 @@ export default compose(
receiptsTableSize: receiptSettings?.tableSize,
})),
withDialogActions,
withDrawerActions,
)(ReceiptActionsBar);

View File

@@ -0,0 +1,10 @@
import React from 'react';
export const extractChildren = <T extends React.ReactNode>(
children: React.ReactNode,
type: React.ElementType,
): T[] => {
return React.Children.toArray(children).filter(
(node) => React.isValidElement(node) && node.type === type,
) as T[];
};