feat: pdf template customize

This commit is contained in:
Ahmed Bouhuolia
2024-09-12 16:50:44 +02:00
parent 632c4629de
commit 12226d469a
46 changed files with 436 additions and 191 deletions

View File

@@ -32,7 +32,12 @@ export class PdfTemplatesController extends BaseController {
this.validationResult, this.validationResult,
this.editPdfTemplate.bind(this) this.editPdfTemplate.bind(this)
); );
router.get('/', this.getPdfTemplates.bind(this)); router.get(
'/',
[query('resource').optional()],
this.validationResult,
this.getPdfTemplates.bind(this)
);
router.get( router.get(
'/:template_id', '/:template_id',
[param('template_id').exists().isInt().toInt()], [param('template_id').exists().isInt().toInt()],
@@ -41,7 +46,11 @@ export class PdfTemplatesController extends BaseController {
); );
router.post( router.post(
'/', '/',
[check('template_name').exists(), check('attributes').exists()], [
check('template_name').exists(),
check('resource').exists(),
check('attributes').exists(),
],
this.validationResult, this.validationResult,
this.createPdfInvoiceTemplate.bind(this) this.createPdfInvoiceTemplate.bind(this)
); );
@@ -54,12 +63,13 @@ export class PdfTemplatesController extends BaseController {
next: NextFunction next: NextFunction
) { ) {
const { tenantId } = req; const { tenantId } = req;
const { templateName, attributes } = this.matchedBodyData(req); const { templateName, resource, attributes } = this.matchedBodyData(req);
try { try {
const result = await this.pdfTemplateApplication.createPdfTemplate( const result = await this.pdfTemplateApplication.createPdfTemplate(
tenantId, tenantId,
templateName, templateName,
resource,
attributes attributes
); );
return res.status(201).send(result); return res.status(201).send(result);
@@ -117,10 +127,12 @@ export class PdfTemplatesController extends BaseController {
async getPdfTemplates(req: Request, res: Response, next: NextFunction) { async getPdfTemplates(req: Request, res: Response, next: NextFunction) {
const { tenantId } = req; const { tenantId } = req;
const query = this.matchedQueryData(req);
try { try {
const templates = await this.pdfTemplateApplication.getPdfTemplates( const templates = await this.pdfTemplateApplication.getPdfTemplates(
tenantId tenantId,
query
); );
return res.status(200).send(templates); return res.status(200).send(templates);
} catch (error) { } catch (error) {

View File

@@ -24,10 +24,10 @@ export class CreatePdfTemplate {
public createPdfTemplate( public createPdfTemplate(
tenantId: number, tenantId: number,
templateName: string, templateName: string,
resource: string,
invoiceTemplateDTO: ICreateInvoicePdfTemplateDTO invoiceTemplateDTO: ICreateInvoicePdfTemplateDTO
) { ) {
const { PdfTemplate } = this.tennacy.models(tenantId); const { PdfTemplate } = this.tennacy.models(tenantId);
const resource = 'SaleInvoice';
const attributes = invoiceTemplateDTO; const attributes = invoiceTemplateDTO;
return this.uow.withTransaction(tenantId, async (trx) => { return this.uow.withTransaction(tenantId, async (trx) => {

View File

@@ -26,11 +26,13 @@ export class PdfTemplateApplication {
public async createPdfTemplate( public async createPdfTemplate(
tenantId: number, tenantId: number,
templateName: string, templateName: string,
resource: string,
invoiceTemplateDTO: ICreateInvoicePdfTemplateDTO invoiceTemplateDTO: ICreateInvoicePdfTemplateDTO
) { ) {
return this.createPdfTemplateService.createPdfTemplate( return this.createPdfTemplateService.createPdfTemplate(
tenantId, tenantId,
templateName, templateName,
resource,
invoiceTemplateDTO invoiceTemplateDTO
); );
} }

View File

@@ -28,7 +28,7 @@ import { EstimateCustomizeDrawer } from '@/containers/Sales/Estimates/EstimateCu
import { ReceiptCustomizeDrawer } from '@/containers/Sales/Receipts/ReceiptCustomize/ReceiptCustomizeDrawer'; import { ReceiptCustomizeDrawer } from '@/containers/Sales/Receipts/ReceiptCustomize/ReceiptCustomizeDrawer';
import { CreditNoteCustomizeDrawer } from '@/containers/Sales/CreditNotes/CreditNoteCustomize/CreditNoteCustomizeDrawer'; import { CreditNoteCustomizeDrawer } from '@/containers/Sales/CreditNotes/CreditNoteCustomize/CreditNoteCustomizeDrawer';
import { PaymentReceivedCustomizeDrawer } from '@/containers/Sales/PaymentsReceived/PaymentReceivedCustomize/PaymentReceivedCustomizeDrawer'; import { PaymentReceivedCustomizeDrawer } from '@/containers/Sales/PaymentsReceived/PaymentReceivedCustomize/PaymentReceivedCustomizeDrawer';
import { BrandingTemplatesDrawer } from '@/containers/Sales/Invoices/BrandingTemplates/BrandingTemplatesDrawer'; import { BrandingTemplatesDrawer } from '@/containers/BrandingTemplates/BrandingTemplatesDrawer';
import { DRAWERS } from '@/constants/drawers'; import { DRAWERS } from '@/constants/drawers';

View File

@@ -29,7 +29,7 @@ import { CashflowAlerts } from '../CashFlow/CashflowAlerts';
import { BankRulesAlerts } from '../Banking/Rules/RulesList/BankRulesAlerts'; import { BankRulesAlerts } from '../Banking/Rules/RulesList/BankRulesAlerts';
import { SubscriptionAlerts } from '../Subscriptions/alerts/alerts'; import { SubscriptionAlerts } from '../Subscriptions/alerts/alerts';
import { BankAccountAlerts } from '@/containers/CashFlow/AccountTransactions/alerts'; import { BankAccountAlerts } from '@/containers/CashFlow/AccountTransactions/alerts';
import { BrandingTemplatesAlerts } from '../Sales/Invoices/BrandingTemplates/alerts/BrandingTemplatesAlerts'; import { BrandingTemplatesAlerts } from '../BrandingTemplates/alerts/BrandingTemplatesAlerts';
export default [ export default [
...AccountsAlerts, ...AccountsAlerts,

View File

@@ -0,0 +1,87 @@
import * as Yup from 'yup';
import {
ElementCustomize,
ElementCustomizeProps,
} from '../ElementCustomize/ElementCustomize';
import {
transformToEditRequest,
transformToNewRequest,
useBrandingTemplateFormInitialValues,
} from './_utils';
import { AppToaster } from '@/components';
import { Intent } from '@blueprintjs/core';
import {
useCreatePdfTemplate,
useEditPdfTemplate,
} from '@/hooks/query/pdf-templates';
import { FormikHelpers } from 'formik';
import { BrandingTemplateValues } from './types';
interface BrandingTemplateFormProps<T> extends ElementCustomizeProps<T> {
resource: string;
templateId?: number;
onSuccess?: () => void;
onError?: () => void;
defaultValues?: T;
}
export function BrandingTemplateForm<T extends BrandingTemplateValues>({
templateId,
onSuccess,
onError,
defaultValues,
resource,
...props
}: BrandingTemplateFormProps<T>) {
const { mutateAsync: createPdfTemplate } = useCreatePdfTemplate();
const { mutateAsync: editPdfTemplate } = useEditPdfTemplate();
const initialValues = useBrandingTemplateFormInitialValues<T>(defaultValues);
const handleFormSubmit = (values: T, { setSubmitting }: FormikHelpers<T>) => {
const handleSuccess = (message: string) => {
AppToaster.show({ intent: Intent.SUCCESS, message });
setSubmitting(false);
onSuccess && onSuccess();
};
const handleError = (message: string) => {
AppToaster.show({ intent: Intent.DANGER, message });
setSubmitting(false);
onError && onError();
};
if (templateId) {
const reqValues = transformToEditRequest(values);
setSubmitting(true);
// Edit existing template
editPdfTemplate({ templateId, values: reqValues })
.then(() => handleSuccess('PDF template updated successfully!'))
.catch(() =>
handleError('An error occurred while updating the PDF template.'),
);
} else {
const reqValues = transformToNewRequest(values, resource);
setSubmitting(true);
// Create new template
createPdfTemplate(reqValues)
.then(() => handleSuccess('PDF template created successfully!'))
.catch(() =>
handleError('An error occurred while creating the PDF template.'),
);
}
};
return (
<ElementCustomize<T>
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={handleFormSubmit}
{...props}
/>
);
}
export const validationSchema = Yup.object().shape({
templateName: Yup.string().required('Template Name is required'),
});

View File

@@ -1,5 +1,6 @@
import React, { createContext } from 'react'; import React, { createContext } from 'react';
import { useGetPdfTemplates } from '@/hooks/query/pdf-templates'; import { useGetPdfTemplates } from '@/hooks/query/pdf-templates';
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
interface BrandingTemplatesBootValues { interface BrandingTemplatesBootValues {
pdfTemplates: any; pdfTemplates: any;
@@ -15,8 +16,11 @@ interface BrandingTemplatesBootProps {
} }
function BrandingTemplatesBoot({ ...props }: BrandingTemplatesBootProps) { function BrandingTemplatesBoot({ ...props }: BrandingTemplatesBootProps) {
const { payload } = useDrawerContext();
const resource = payload?.resource || null;
const { data: pdfTemplates, isLoading: isPdfTemplatesLoading } = const { data: pdfTemplates, isLoading: isPdfTemplatesLoading } =
useGetPdfTemplates(); useGetPdfTemplates({ resource });
const provider = { const provider = {
pdfTemplates, pdfTemplates,

View File

@@ -16,12 +16,13 @@ function BrandingTemplatesDrawerRoot({
name, name,
// #withDrawer // #withDrawer
isOpen, isOpen,
payload: {}, payload,
}) { }) {
return ( return (
<Drawer <Drawer
isOpen={isOpen} isOpen={isOpen}
name={name} name={name}
payload={payload}
size={'600px'} size={'600px'}
style={{ borderLeftColor: '#cbcbcb' }} style={{ borderLeftColor: '#cbcbcb' }}
> >

View File

@@ -0,0 +1,52 @@
import { omit } from 'lodash';
import {
CreatePdfTemplateValues,
EditPdfTemplateValues,
} from '@/hooks/query/pdf-templates';
import { useBrandingTemplateBoot } from './BrandingTemplateBoot';
import { transformToForm } from '@/utils';
import { BrandingTemplateValues } from './types';
import { useFormikContext } from 'formik';
export const transformToEditRequest = <T extends BrandingTemplateValues>(
values: T,
): EditPdfTemplateValues => {
return {
templateName: values.templateName,
attributes: omit(values, ['templateName']),
};
};
export const transformToNewRequest = <T extends BrandingTemplateValues>(
values: T,
resource: string
): CreatePdfTemplateValues => {
return {
resource,
templateName: values.templateName,
attributes: omit(values, ['templateName']),
};
};
export const useIsTemplateNamedFilled = () => {
const { values } = useFormikContext<BrandingTemplateValues>();
return values.templateName && values.templateName?.length >= 4;
};
export const useBrandingTemplateFormInitialValues = <
T extends BrandingTemplateValues,
>(
initialValues = {},
) => {
const { pdfTemplate } = useBrandingTemplateBoot();
const defaultPdfTemplate = {
templateName: pdfTemplate?.templateName,
...pdfTemplate?.attributes,
};
return {
...initialValues,
...(transformToForm(defaultPdfTemplate, initialValues) as T),
};
};

View File

@@ -0,0 +1,5 @@
export interface BrandingTemplateValues {
templateName: string;
}

View File

@@ -1,39 +1,35 @@
import { Box } from '@/components'; import { useFormikContext } from 'formik';
import { Classes } from '@blueprintjs/core';
import { ElementCustomize } from '../../../ElementCustomize/ElementCustomize'; import { ElementCustomize } from '../../../ElementCustomize/ElementCustomize';
import { CreditNoteCustomizeGeneralField } from './CreditNoteCustomizeGeneralFields'; import { CreditNoteCustomizeGeneralField } from './CreditNoteCustomizeGeneralFields';
import { CreditNoteCustomizeContentFields } from './CreditNoteCutomizeContentFields'; import { CreditNoteCustomizeContentFields } from './CreditNoteCutomizeContentFields';
import { CreditNotePaperTemplate } from './CreditNotePaperTemplate'; import { CreditNotePaperTemplate } from './CreditNotePaperTemplate';
import { CreditNoteCustomizeValues } from './types'; import { CreditNoteCustomizeValues } from './types';
import { initialValues } from './constants'; import { initialValues } from './constants';
import { useFormikContext } from 'formik';
export default function CreditNoteCustomizeContent() { export function CreditNoteCustomizeContent() {
const handleFormSubmit = (values: CreditNoteCustomizeValues) => {}; const handleFormSubmit = (values: CreditNoteCustomizeValues) => {};
return ( return (
<Box className={Classes.DRAWER_BODY}> <ElementCustomize<CreditNoteCustomizeValues>
<ElementCustomize<CreditNoteCustomizeValues> initialValues={initialValues}
initialValues={initialValues} onSubmit={handleFormSubmit}
onSubmit={handleFormSubmit} >
> <ElementCustomize.PaperTemplate>
<ElementCustomize.PaperTemplate> <CreditNotePaperTemplateFormConnected />
<CreditNotePaperTemplateFormConnected /> </ElementCustomize.PaperTemplate>
</ElementCustomize.PaperTemplate>
<ElementCustomize.FieldsTab id={'general'} label={'General'}> <ElementCustomize.FieldsTab id={'general'} label={'General'}>
<CreditNoteCustomizeGeneralField /> <CreditNoteCustomizeGeneralField />
</ElementCustomize.FieldsTab> </ElementCustomize.FieldsTab>
<ElementCustomize.FieldsTab id={'content'} label={'Content'}> <ElementCustomize.FieldsTab id={'content'} label={'Content'}>
<CreditNoteCustomizeContentFields /> <CreditNoteCustomizeContentFields />
</ElementCustomize.FieldsTab> </ElementCustomize.FieldsTab>
<ElementCustomize.FieldsTab id={'totals'} label={'Totals'}> <ElementCustomize.FieldsTab id={'totals'} label={'Totals'}>
asdfasdfdsaf #3 asdfasdfdsaf #3
</ElementCustomize.FieldsTab> </ElementCustomize.FieldsTab>
</ElementCustomize> </ElementCustomize>
</Box>
); );
} }

View File

@@ -0,0 +1,18 @@
import { Box } from '@/components';
import { CreditNoteCustomizeContent } from './CreditNoteCustomizeContent';
import { Classes } from '@blueprintjs/core';
import { BrandingTemplateBoot } from '@/containers/BrandingTemplates/BrandingTemplateBoot';
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
export default function CreditNoteCustomizeDrawerBody() {
const { payload } = useDrawerContext();
const templateId = payload.templateId;
return (
<Box className={Classes.DRAWER_BODY}>
<BrandingTemplateBoot templateId={templateId}>
<CreditNoteCustomizeContent />
</BrandingTemplateBoot>
</Box>
);
}

View File

@@ -101,7 +101,7 @@ function CreditNotesActionsBar({
}; };
// Handle the customize button click. // Handle the customize button click.
const handleCustomizeBtnClick = () => { const handleCustomizeBtnClick = () => {
openDrawer(DRAWERS.CREDIT_NOTE_CUSTOMIZE); openDrawer(DRAWERS.BRANDING_TEMPLATES, { resource: 'CreditNote' });
} }
return ( return (
@@ -174,7 +174,7 @@ function CreditNotesActionsBar({
<Menu> <Menu>
<MenuItem <MenuItem
onClick={handleCustomizeBtnClick} onClick={handleCustomizeBtnClick}
text={'Customize Invoice'} text={'Customize Templates'}
/> />
</Menu> </Menu>
} }

View File

@@ -1,40 +1,49 @@
import React from 'react';
import { Box } from '@/components';
import { Classes } from '@blueprintjs/core'; import { Classes } from '@blueprintjs/core';
import { useFormikContext } from 'formik';
import { Box } from '@/components';
import { ElementCustomize } from '../../../ElementCustomize/ElementCustomize'; import { ElementCustomize } from '../../../ElementCustomize/ElementCustomize';
import { EstimateCustomizeGeneralField } from './EstimateCustomizeFieldsGeneral'; import { EstimateCustomizeGeneralField } from './EstimateCustomizeFieldsGeneral';
import { EstimateCustomizeContentFields } from './EstimateCustomizeFieldsContent'; import { EstimateCustomizeContentFields } from './EstimateCustomizeFieldsContent';
import { EstimatePaperTemplate } from './EstimatePaperTemplate'; import { EstimatePaperTemplate } from './EstimatePaperTemplate';
import { EstimateCustomizeValues } from './types'; import { EstimateCustomizeValues } from './types';
import { initialValues } from './constants'; import { initialValues } from './constants';
import { useFormikContext } from 'formik'; import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
import { useDrawerActions } from '@/hooks/state';
import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
export default function EstimateCustomizeContent() { export function EstimateCustomizeContent() {
const handleFormSubmit = (values: EstimateCustomizeValues) => {}; const { payload, name } = useDrawerContext();
const { closeDrawer } = useDrawerActions();
const templateId = payload?.templateId || null;
const handleSuccess = () => {
closeDrawer(name);
};
return ( return (
<Box className={Classes.DRAWER_BODY}> <BrandingTemplateForm<EstimateCustomizeValues>
<ElementCustomize<EstimateCustomizeValues> templateId={templateId}
initialValues={initialValues} defaultValues={initialValues}
onSubmit={handleFormSubmit} onSuccess={handleSuccess}
> resource={'SaleEstimate'}
<ElementCustomize.PaperTemplate> >
<EstimatePaperTemplateFormConnected /> <ElementCustomize.PaperTemplate>
</ElementCustomize.PaperTemplate> <EstimatePaperTemplateFormConnected />
</ElementCustomize.PaperTemplate>
<ElementCustomize.FieldsTab id={'general'} label={'General'}> <ElementCustomize.FieldsTab id={'general'} label={'General'}>
<EstimateCustomizeGeneralField /> <EstimateCustomizeGeneralField />
</ElementCustomize.FieldsTab> </ElementCustomize.FieldsTab>
<ElementCustomize.FieldsTab id={'content'} label={'Content'}> <ElementCustomize.FieldsTab id={'content'} label={'Content'}>
<EstimateCustomizeContentFields /> <EstimateCustomizeContentFields />
</ElementCustomize.FieldsTab> </ElementCustomize.FieldsTab>
<ElementCustomize.FieldsTab id={'totals'} label={'Totals'}> <ElementCustomize.FieldsTab id={'totals'} label={'Totals'}>
asdfasdfdsaf #3 asdfasdfdsaf #3
</ElementCustomize.FieldsTab> </ElementCustomize.FieldsTab>
</ElementCustomize> </BrandingTemplateForm>
</Box>
); );
} }

View File

@@ -4,8 +4,8 @@ import * as R from 'ramda';
import { Drawer, DrawerSuspense } from '@/components'; import { Drawer, DrawerSuspense } from '@/components';
import withDrawers from '@/containers/Drawer/withDrawers'; import withDrawers from '@/containers/Drawer/withDrawers';
const EstimateCustomizeContent = React.lazy( const EstimateCustomizeDrawerBody = React.lazy(
() => import('./EstimateCustomizeContent'), () => import('./EstimateCustomizeDrawerBody'),
); );
/** /**
@@ -22,7 +22,7 @@ function EstimateCustomizeDrawerRoot({
return ( return (
<Drawer isOpen={isOpen} name={name} size={'100%'}> <Drawer isOpen={isOpen} name={name} size={'100%'}>
<DrawerSuspense> <DrawerSuspense>
<EstimateCustomizeContent /> <EstimateCustomizeDrawerBody />
</DrawerSuspense> </DrawerSuspense>
</Drawer> </Drawer>
); );

View File

@@ -0,0 +1,18 @@
import { Box } from '@/components';
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
import { BrandingTemplateBoot } from '@/containers/BrandingTemplates/BrandingTemplateBoot';
import { Classes } from '@blueprintjs/core';
import { EstimateCustomizeContent } from './EstimateCustomizeContent';
export default function EstimateCustomizeDrawerBody() {
const { payload } = useDrawerContext();
const templateId = payload?.templateId || null;
return (
<Box className={Classes.DRAWER_BODY}>
<BrandingTemplateBoot templateId={templateId}>
<EstimateCustomizeContent />
</BrandingTemplateBoot>
</Box>
);
}

View File

@@ -1,6 +1,13 @@
// @ts-nocheck // @ts-nocheck
import { Classes, Text } from '@blueprintjs/core'; import { Classes, Text } from '@blueprintjs/core';
import { FFormGroup, FSwitch, Group, Stack } from '@/components'; import {
FFormGroup,
FInputGroup,
FSwitch,
FieldRequiredHint,
Group,
Stack,
} from '@/components';
import { FColorInput } from '@/components/Forms/FColorInput'; import { FColorInput } from '@/components/Forms/FColorInput';
// import styles from './InvoiceCustomizeFields.module.scss'; // import styles from './InvoiceCustomizeFields.module.scss';
@@ -10,11 +17,21 @@ export function EstimateCustomizeGeneralField() {
<Stack spacing={0}> <Stack spacing={0}>
<h2 style={{ fontSize: 16, marginBottom: 10 }}>General Branding</h2> <h2 style={{ fontSize: 16, marginBottom: 10 }}>General Branding</h2>
<p className={Classes.TEXT_MUTED}> <p className={Classes.TEXT_MUTED}>
Set your invoice details to be automatically applied every timeyou Set your invoice details to be automatically applied every timeyou
create a new invoice. create a new invoice.
</p> </p>
</Stack> </Stack>
<FFormGroup
name={'templateName'}
label={'Template Name'}
labelInfo={<FieldRequiredHint />}
fastField
style={{ marginBottom: 10 }}
>
<FInputGroup name={'templateName'} fastField />
</FFormGroup>
<Stack spacing={0}> <Stack spacing={0}>
<FFormGroup <FFormGroup
name={'primaryColor'} name={'primaryColor'}

View File

@@ -1,4 +1,6 @@
export const initialValues = { export const initialValues = {
templateName: '',
// Colors // Colors
primaryColor: '#2c3dd8', primaryColor: '#2c3dd8',
secondaryColor: '#2c3dd8', secondaryColor: '#2c3dd8',

View File

@@ -1,4 +1,6 @@
export interface EstimateCustomizeValues { import { BrandingTemplateValues } from "@/containers/BrandingTemplates/types";
export interface EstimateCustomizeValues extends BrandingTemplateValues {
// Colors // Colors
primaryColor?: string; primaryColor?: string;
secondaryColor?: string; secondaryColor?: string;

View File

@@ -108,7 +108,7 @@ function EstimateActionsBar({
}; };
// Handle customize button clicl. // Handle customize button clicl.
const handleCustomizeBtnClick = () => { const handleCustomizeBtnClick = () => {
openDrawer(DRAWERS.ESTIMATE_CUSTOMIZE); openDrawer(DRAWERS.BRANDING_TEMPLATES, { resource: 'SaleEstimate' });
}; };
return ( return (
@@ -192,7 +192,7 @@ function EstimateActionsBar({
<Menu> <Menu>
<MenuItem <MenuItem
onClick={handleCustomizeBtnClick} onClick={handleCustomizeBtnClick}
text={'Customize Estimate'} text={'Customize Templates'}
/> />
</Menu> </Menu>
} }

View File

@@ -1,8 +1,8 @@
// @ts-nocheck // @ts-nocheck
import { Classes } from '@blueprintjs/core'; import { Classes } from '@blueprintjs/core';
import { useDrawerContext } from '@/components/Drawer/DrawerProvider'; import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
import { BrandingTemplateBoot } from '../BrandingTemplates/BrandingTemplateBoot';
import { InvoiceCustomizeContent } from './InvoiceCustomizeContent'; import { InvoiceCustomizeContent } from './InvoiceCustomizeContent';
import { BrandingTemplateBoot } from '@/containers/BrandingTemplates/BrandingTemplateBoot';
import { Box } from '@/components'; import { Box } from '@/components';
export default function InvoiceCustomize() { export default function InvoiceCustomize() {

View File

@@ -1,8 +1,6 @@
import React from 'react'; import React from 'react';
import * as R from 'ramda'; import * as R from 'ramda';
import { AppToaster } from '@/components'; import { useFormikContext } from 'formik';
import { Intent } from '@blueprintjs/core';
import { FormikHelpers, useFormikContext } from 'formik';
import { import {
InvoicePaperTemplate, InvoicePaperTemplate,
InvoicePaperTemplateProps, InvoicePaperTemplateProps,
@@ -11,70 +9,29 @@ import { ElementCustomize } from '../../../ElementCustomize/ElementCustomize';
import { InvoiceCustomizeGeneralField } from './InvoiceCustomizeGeneralFields'; import { InvoiceCustomizeGeneralField } from './InvoiceCustomizeGeneralFields';
import { InvoiceCustomizeContentFields } from './InvoiceCutomizeContentFields'; import { InvoiceCustomizeContentFields } from './InvoiceCutomizeContentFields';
import { InvoiceCustomizeValues } from './types'; import { InvoiceCustomizeValues } from './types';
import {
useCreatePdfTemplate,
useEditPdfTemplate,
} from '@/hooks/query/pdf-templates';
import {
transformToEditRequest,
transformToNewRequest,
useInvoiceCustomizeInitialValues,
} from './utils';
import { InvoiceCustomizeSchema } from './InvoiceCustomizeForm.schema'; import { InvoiceCustomizeSchema } from './InvoiceCustomizeForm.schema';
import { useDrawerContext } from '@/components/Drawer/DrawerProvider'; import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
import { useDrawerActions } from '@/hooks/state'; import { useDrawerActions } from '@/hooks/state';
import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
import { initialValues } from './constants';
export function InvoiceCustomizeContent() { export function InvoiceCustomizeContent() {
const { mutateAsync: createPdfTemplate } = useCreatePdfTemplate();
const { mutateAsync: editPdfTemplate } = useEditPdfTemplate();
const { payload, name } = useDrawerContext(); const { payload, name } = useDrawerContext();
const { closeDrawer } = useDrawerActions(); const { closeDrawer } = useDrawerActions();
const templateId = payload?.templateId || null; const templateId = payload?.templateId || null;
const handleFormSubmit = ( const handleSuccess = () => {
values: InvoiceCustomizeValues, closeDrawer(name);
{ setSubmitting }: FormikHelpers<InvoiceCustomizeValues>,
) => {
const handleSuccess = (message: string) => {
AppToaster.show({ intent: Intent.SUCCESS, message });
setSubmitting(false);
closeDrawer(name);
};
const handleError = (message: string) => {
AppToaster.show({ intent: Intent.DANGER, message });
setSubmitting(false);
};
if (templateId) {
const reqValues = transformToEditRequest(values);
setSubmitting(true);
// Edit existing template
editPdfTemplate({ templateId, values: reqValues })
.then(() => handleSuccess('PDF template updated successfully!'))
.catch(() =>
handleError('An error occurred while updating the PDF template.'),
);
} else {
const reqValues = transformToNewRequest(values);
setSubmitting(true);
// Create new template
createPdfTemplate(reqValues)
.then(() => handleSuccess('PDF template created successfully!'))
.catch(() =>
handleError('An error occurred while creating the PDF template.'),
);
}
}; };
const initialValues = useInvoiceCustomizeInitialValues();
return ( return (
<ElementCustomize<InvoiceCustomizeValues> <BrandingTemplateForm<InvoiceCustomizeValues>
initialValues={initialValues} templateId={templateId}
defaultValues={initialValues}
validationSchema={InvoiceCustomizeSchema} validationSchema={InvoiceCustomizeSchema}
onSubmit={handleFormSubmit} onSuccess={handleSuccess}
resource={'SaleInvoice'}
> >
<ElementCustomize.PaperTemplate> <ElementCustomize.PaperTemplate>
<InvoicePaperTemplateFormConnected /> <InvoicePaperTemplateFormConnected />
@@ -91,7 +48,7 @@ export function InvoiceCustomizeContent() {
<ElementCustomize.FieldsTab id={'totals'} label={'Totals'}> <ElementCustomize.FieldsTab id={'totals'} label={'Totals'}>
asdfasdfdsaf #3 asdfasdfdsaf #3
</ElementCustomize.FieldsTab> </ElementCustomize.FieldsTab>
</ElementCustomize> </BrandingTemplateForm>
); );
} }

View File

@@ -1,6 +1,6 @@
export interface InvoiceCustomizeValues { import { BrandingTemplateValues } from "@/containers/BrandingTemplates/types";
templateName: string;
export interface InvoiceCustomizeValues extends BrandingTemplateValues {
// Colors // Colors
primaryColor?: string; primaryColor?: string;
secondaryColor?: string; secondaryColor?: string;

View File

@@ -5,9 +5,9 @@ import {
CreatePdfTemplateValues, CreatePdfTemplateValues,
EditPdfTemplateValues, EditPdfTemplateValues,
} from '@/hooks/query/pdf-templates'; } from '@/hooks/query/pdf-templates';
import { useBrandingTemplateBoot } from '../BrandingTemplates/BrandingTemplateBoot';
import { transformToForm } from '@/utils'; import { transformToForm } from '@/utils';
import { initialValues } from './constants'; import { initialValues } from './constants';
import { useBrandingTemplateBoot } from '@/containers/BrandingTemplates/BrandingTemplateBoot';
export const transformToEditRequest = ( export const transformToEditRequest = (
values: InvoiceCustomizeValues, values: InvoiceCustomizeValues,

View File

@@ -109,7 +109,7 @@ function InvoiceActionsBar({
// Handles the invoice customize button click. // Handles the invoice customize button click.
const handleCustomizeBtnClick = () => { const handleCustomizeBtnClick = () => {
openDrawer(DRAWERS.BRANDING_TEMPLATES); openDrawer(DRAWERS.BRANDING_TEMPLATES, { resource: 'SaleInvoice' });
}; };
return ( return (
@@ -190,7 +190,7 @@ function InvoiceActionsBar({
<Menu> <Menu>
<MenuItem <MenuItem
onClick={handleCustomizeBtnClick} onClick={handleCustomizeBtnClick}
text={'Invoice Templates'} text={'Customize Templates'}
/> />
</Menu> </Menu>
} }

View File

@@ -0,0 +1,18 @@
import { Box } from '@/components';
import { Classes } from '@blueprintjs/core';
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
import { PaymentReceivedCustomizeContent } from './PaymentReceivedCustomizeContent';
import { BrandingTemplateBoot } from '@/containers/BrandingTemplates/BrandingTemplateBoot';
export default function PaymentReceivedCustomize() {
const { payload } = useDrawerContext();
const templateId = payload.templateId;
return (
<Box className={Classes.DRAWER_BODY}>
<BrandingTemplateBoot templateId={templateId}>
<PaymentReceivedCustomizeContent />
</BrandingTemplateBoot>
</Box>
);
}

View File

@@ -1,40 +1,47 @@
import React from 'react';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import { Classes } from '@blueprintjs/core';
import { Box } from '@/components';
import { ElementCustomize } from '../../../ElementCustomize/ElementCustomize'; import { ElementCustomize } from '../../../ElementCustomize/ElementCustomize';
import { PaymentReceivedCustomizeGeneralField } from './PaymentReceivedCustomizeFieldsGeneral'; import { PaymentReceivedCustomizeGeneralField } from './PaymentReceivedCustomizeFieldsGeneral';
import { PaymentReceivedCustomizeContentFields } from './PaymentReceivedCustomizeFieldsContent'; import { PaymentReceivedCustomizeContentFields } from './PaymentReceivedCustomizeFieldsContent';
import { PaymentReceivedCustomizeValues } from './types'; import { PaymentReceivedCustomizeValues } from './types';
import { PaymentReceivedPaperTemplate } from './PaymentReceivedPaperTemplate'; import { PaymentReceivedPaperTemplate } from './PaymentReceivedPaperTemplate';
import { initialValues } from './constants'; import { initialValues } from './constants';
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
import { useDrawerActions } from '@/hooks/state';
import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
export default function PaymentReceivedCustomizeContent() { export function PaymentReceivedCustomizeContent() {
const handleFormSubmit = (values: PaymentReceivedCustomizeValues) => {}; const { payload, name } = useDrawerContext();
const { closeDrawer } = useDrawerActions();
const templateId = payload?.templateId || null;
const handleSuccess = () => {
closeDrawer(name);
};
return ( return (
<Box className={Classes.DRAWER_BODY}> <BrandingTemplateForm<PaymentReceivedCustomizeValues>
<ElementCustomize<PaymentReceivedCustomizeValues> templateId={templateId}
initialValues={initialValues} defaultValues={initialValues}
onSubmit={handleFormSubmit} onSuccess={handleSuccess}
> resource={'PaymentReceive'}
<ElementCustomize.PaperTemplate> >
<PaymentReceivedPaperTemplateFormConnected /> <ElementCustomize.PaperTemplate>
</ElementCustomize.PaperTemplate> <PaymentReceivedPaperTemplateFormConnected />
</ElementCustomize.PaperTemplate>
<ElementCustomize.FieldsTab id={'general'} label={'General'}> <ElementCustomize.FieldsTab id={'general'} label={'General'}>
<PaymentReceivedCustomizeGeneralField /> <PaymentReceivedCustomizeGeneralField />
</ElementCustomize.FieldsTab> </ElementCustomize.FieldsTab>
<ElementCustomize.FieldsTab id={'content'} label={'Content'}> <ElementCustomize.FieldsTab id={'content'} label={'Content'}>
<PaymentReceivedCustomizeContentFields /> <PaymentReceivedCustomizeContentFields />
</ElementCustomize.FieldsTab> </ElementCustomize.FieldsTab>
<ElementCustomize.FieldsTab id={'totals'} label={'Totals'}> <ElementCustomize.FieldsTab id={'totals'} label={'Totals'}>
asdfasdfdsaf #3 asdfasdfdsaf #3
</ElementCustomize.FieldsTab> </ElementCustomize.FieldsTab>
</ElementCustomize> </BrandingTemplateForm>
</Box>
); );
} }

View File

@@ -4,8 +4,8 @@ import * as R from 'ramda';
import { Drawer, DrawerSuspense } from '@/components'; import { Drawer, DrawerSuspense } from '@/components';
import withDrawers from '@/containers/Drawer/withDrawers'; import withDrawers from '@/containers/Drawer/withDrawers';
const PaymentReceivedCustomizeContent = React.lazy( const PaymentReceivedCustomize = React.lazy(
() => import('./PaymentReceivedCustomizeContent'), () => import('./PaymentReceivedCustomize'),
); );
/** /**
@@ -16,12 +16,12 @@ function PaymentReceivedCustomizeDrawerRoot({
name, name,
// #withDrawer // #withDrawer
isOpen, isOpen,
payload: {}, payload
}) { }) {
return ( return (
<Drawer isOpen={isOpen} name={name} size={'100%'}> <Drawer isOpen={isOpen} name={name} size={'100%'} payload={payload}>
<DrawerSuspense> <DrawerSuspense>
<PaymentReceivedCustomizeContent /> <PaymentReceivedCustomize />
</DrawerSuspense> </DrawerSuspense>
</Drawer> </Drawer>
); );

View File

@@ -1,4 +1,6 @@
export const initialValues = { export const initialValues = {
templateName: '',
// Colors // Colors
primaryColor: '#2c3dd8', primaryColor: '#2c3dd8',
secondaryColor: '#2c3dd8', secondaryColor: '#2c3dd8',

View File

@@ -1,4 +1,6 @@
export interface PaymentReceivedCustomizeValues { import { BrandingTemplateValues } from '@/containers/BrandingTemplates/types';
export interface PaymentReceivedCustomizeValues extends BrandingTemplateValues {
// Colors // Colors
primaryColor?: string; primaryColor?: string;
secondaryColor?: string; secondaryColor?: string;

View File

@@ -113,7 +113,7 @@ function PaymentsReceivedActionsBar({
}; };
// Handle the customize button click. // Handle the customize button click.
const handleCustomizeBtnClick = () => { const handleCustomizeBtnClick = () => {
openDrawer(DRAWERS.PAYMENT_RECEIVED_CUSTOMIZE); openDrawer(DRAWERS.BRANDING_TEMPLATES, { resource: 'PaymentReceive' });
}; };
return ( return (
@@ -195,7 +195,7 @@ function PaymentsReceivedActionsBar({
<Menu> <Menu>
<MenuItem <MenuItem
onClick={handleCustomizeBtnClick} onClick={handleCustomizeBtnClick}
text={'Customize Invoice'} text={'Customize Templates'}
/> />
</Menu> </Menu>
} }

View File

@@ -1,5 +1,3 @@
import { Box } from '@/components';
import { Classes } from '@blueprintjs/core';
import { ElementCustomize } from '../../../ElementCustomize/ElementCustomize'; import { ElementCustomize } from '../../../ElementCustomize/ElementCustomize';
import { ReceiptCustomizeGeneralField } from './ReceiptCustomizeFieldsGeneral'; import { ReceiptCustomizeGeneralField } from './ReceiptCustomizeFieldsGeneral';
import { ReceiptCustomizeFieldsContent } from './ReceiptCustomizeFieldsContent'; import { ReceiptCustomizeFieldsContent } from './ReceiptCustomizeFieldsContent';
@@ -7,33 +5,43 @@ import { ReceiptPaperTemplate } from './ReceiptPaperTemplate';
import { ReceiptCustomizeValues } from './types'; import { ReceiptCustomizeValues } from './types';
import { initialValues } from './constants'; import { initialValues } from './constants';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
import { useDrawerActions } from '@/hooks/state';
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
export default function ReceiptCustomizeContent() { export function ReceiptCustomizeContent() {
const handleFormSubmit = (values: ReceiptCustomizeValues) => {}; const { payload, name } = useDrawerContext();
const { closeDrawer } = useDrawerActions();
const templateId = payload?.templateId || null;
const handleFormSuccess = () => {
closeDrawer(name);
};
return ( return (
<Box className={Classes.DRAWER_BODY}> <BrandingTemplateForm<ReceiptCustomizeValues>
<ElementCustomize<ReceiptCustomizeValues> templateId={templateId}
initialValues={initialValues} initialValues={initialValues}
onSubmit={handleFormSubmit} onSuccess={handleFormSuccess}
> resource={'SaleReceipt'}
<ElementCustomize.PaperTemplate> >
<ReceiptPaperTemplateFormConnected /> <ElementCustomize.PaperTemplate>
</ElementCustomize.PaperTemplate> <ReceiptPaperTemplateFormConnected />
</ElementCustomize.PaperTemplate>
<ElementCustomize.FieldsTab id={'general'} label={'General'}> <ElementCustomize.FieldsTab id={'general'} label={'General'}>
<ReceiptCustomizeGeneralField /> <ReceiptCustomizeGeneralField />
</ElementCustomize.FieldsTab> </ElementCustomize.FieldsTab>
<ElementCustomize.FieldsTab id={'content'} label={'Content'}> <ElementCustomize.FieldsTab id={'content'} label={'Content'}>
<ReceiptCustomizeFieldsContent /> <ReceiptCustomizeFieldsContent />
</ElementCustomize.FieldsTab> </ElementCustomize.FieldsTab>
<ElementCustomize.FieldsTab id={'totals'} label={'Totals'}> <ElementCustomize.FieldsTab id={'totals'} label={'Totals'}>
asdfasdfdsaf #3 asdfasdfdsaf #3
</ElementCustomize.FieldsTab> </ElementCustomize.FieldsTab>
</ElementCustomize> </BrandingTemplateForm>
</Box>
); );
} }

View File

@@ -4,8 +4,8 @@ import * as R from 'ramda';
import { Drawer, DrawerSuspense } from '@/components'; import { Drawer, DrawerSuspense } from '@/components';
import withDrawers from '@/containers/Drawer/withDrawers'; import withDrawers from '@/containers/Drawer/withDrawers';
const ReceiptCustomizeContent = React.lazy( const ReceiptCustomizeDrawerBody = React.lazy(
() => import('./ReceiptCustomizeContent'), () => import('./ReceiptCustomizeDrawerBody'),
); );
/** /**
@@ -16,12 +16,12 @@ function ReceiptCustomizeDrawerRoot({
name, name,
// #withDrawer // #withDrawer
isOpen, isOpen,
payload: {}, payload,
}) { }) {
return ( return (
<Drawer isOpen={isOpen} name={name} size={'100%'}> <Drawer isOpen={isOpen} name={name} size={'100%'} payload={payload}>
<DrawerSuspense> <DrawerSuspense>
<ReceiptCustomizeContent /> <ReceiptCustomizeDrawerBody />
</DrawerSuspense> </DrawerSuspense>
</Drawer> </Drawer>
); );

View File

@@ -0,0 +1,18 @@
import { Box } from '@/components';
import { Classes } from '@blueprintjs/core';
import { ReceiptCustomizeContent } from './ReceiptCustomizeContent';
import { BrandingTemplateBoot } from '@/containers/BrandingTemplates/BrandingTemplateBoot';
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
export default function ReceiptCustomizeDrawerBody() {
const { payload } = useDrawerContext();
const templateId = payload.templateId;
return (
<Box className={Classes.DRAWER_BODY}>
<BrandingTemplateBoot templateId={templateId}>
<ReceiptCustomizeContent />
</BrandingTemplateBoot>
</Box>
);
}

View File

@@ -1,4 +1,6 @@
export const initialValues = { export const initialValues = {
templateName: '',
// Colors // Colors
primaryColor: '#2c3dd8', primaryColor: '#2c3dd8',
secondaryColor: '#2c3dd8', secondaryColor: '#2c3dd8',

View File

@@ -1,4 +1,6 @@
export interface ReceiptCustomizeValues { import { BrandingTemplateValues } from "@/containers/BrandingTemplates/types";
export interface ReceiptCustomizeValues extends BrandingTemplateValues {
// Colors // Colors
primaryColor?: string; primaryColor?: string;
secondaryColor?: string; secondaryColor?: string;

View File

@@ -115,7 +115,7 @@ function ReceiptActionsBar({
}; };
// Handle customize button click. // Handle customize button click.
const handleCustomizeBtnClick = () => { const handleCustomizeBtnClick = () => {
openDrawer(DRAWERS.RECEIPT_CUSTOMIZE); openDrawer(DRAWERS.BRANDING_TEMPLATES, { resource: 'SaleReceipt' });
}; };
return ( return (
@@ -198,7 +198,7 @@ function ReceiptActionsBar({
<Menu> <Menu>
<MenuItem <MenuItem
onClick={handleCustomizeBtnClick} onClick={handleCustomizeBtnClick}
text={'Customize Receipt'} text={'Customize Template'}
/> />
</Menu> </Menu>
} }

View File

@@ -153,12 +153,16 @@ export const useGetPdfTemplate = (
// Hook for getting multiple PDF templates // Hook for getting multiple PDF templates
export const useGetPdfTemplates = ( export const useGetPdfTemplates = (
query?: { resource: string },
options?: UseQueryOptions<GetPdfTemplatesResponse, Error>, options?: UseQueryOptions<GetPdfTemplatesResponse, Error>,
): UseQueryResult<GetPdfTemplatesResponse, Error> => { ): UseQueryResult<GetPdfTemplatesResponse, Error> => {
const apiRequest = useApiRequest(); const apiRequest = useApiRequest();
return useQuery<GetPdfTemplatesResponse, Error>( return useQuery<GetPdfTemplatesResponse, Error>(
PdfTemplatesQueryKey, [PdfTemplatesQueryKey, query],
() => apiRequest.get('/pdf-templates').then((res) => res.data), () =>
apiRequest
.get('/pdf-templates', { params: query })
.then((res) => res.data),
options, options,
); );
}; };