mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 15:20:34 +00:00
feat: hook up the invice customize api
This commit is contained in:
@@ -26,20 +26,21 @@ export class PdfTemplatesController extends BaseController {
|
|||||||
'/:template_id',
|
'/:template_id',
|
||||||
[
|
[
|
||||||
param('template_id').exists().isInt().toInt(),
|
param('template_id').exists().isInt().toInt(),
|
||||||
|
check('template_name').exists(),
|
||||||
check('attributes').exists(),
|
check('attributes').exists(),
|
||||||
],
|
],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
this.editPdfTemplate.bind(this)
|
this.editPdfTemplate.bind(this)
|
||||||
);
|
);
|
||||||
|
router.get('/', this.getPdfTemplates.bind(this));
|
||||||
router.get(
|
router.get(
|
||||||
'/:template_id',
|
'/:template_id',
|
||||||
[param('template_id').exists().isInt().toInt()],
|
[param('template_id').exists().isInt().toInt()],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
this.getPdfTemplate.bind(this)
|
this.getPdfTemplate.bind(this)
|
||||||
);
|
);
|
||||||
router.get('/', this.getPdfTemplates.bind(this));
|
|
||||||
router.post(
|
router.post(
|
||||||
'/invoices',
|
'/',
|
||||||
[check('template_name').exists(), check('attributes').exists()],
|
[check('template_name').exists(), check('attributes').exists()],
|
||||||
this.validationResult,
|
this.validationResult,
|
||||||
this.createPdfInvoiceTemplate.bind(this)
|
this.createPdfInvoiceTemplate.bind(this)
|
||||||
@@ -70,13 +71,13 @@ export class PdfTemplatesController extends BaseController {
|
|||||||
async editPdfTemplate(req: Request, res: Response, next: NextFunction) {
|
async editPdfTemplate(req: Request, res: Response, next: NextFunction) {
|
||||||
const { tenantId } = req;
|
const { tenantId } = req;
|
||||||
const { template_id: templateId } = req.params;
|
const { template_id: templateId } = req.params;
|
||||||
const { attributes } = this.matchedBodyData(req);
|
const editTemplateDTO = this.matchedBodyData(req);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await this.pdfTemplateApplication.editPdfTemplate(
|
const result = await this.pdfTemplateApplication.editPdfTemplate(
|
||||||
tenantId,
|
tenantId,
|
||||||
Number(templateId),
|
Number(templateId),
|
||||||
attributes
|
editTemplateDTO
|
||||||
);
|
);
|
||||||
return res.status(200).send(result);
|
return res.status(200).send(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -30,11 +30,14 @@ export class EditPdfTemplate {
|
|||||||
const { PdfTemplate } = this.tenancy.models(tenantId);
|
const { PdfTemplate } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
return this.uow.withTransaction(tenantId, async (trx) => {
|
return this.uow.withTransaction(tenantId, async (trx) => {
|
||||||
await PdfTemplate.query(trx)
|
await this.eventPublisher.emitAsync(events.pdfTemplate.onEditing, {
|
||||||
.patch({
|
tenantId,
|
||||||
...editTemplateDTO,
|
templateId,
|
||||||
})
|
});
|
||||||
.where('id', templateId);
|
await PdfTemplate.query(trx).where('id', templateId).update({
|
||||||
|
templateName: editTemplateDTO.templateName,
|
||||||
|
attributes: editTemplateDTO.attributes,
|
||||||
|
});
|
||||||
|
|
||||||
await this.eventPublisher.emitAsync(events.pdfTemplate.onEdited, {
|
await this.eventPublisher.emitAsync(events.pdfTemplate.onEdited, {
|
||||||
tenantId,
|
tenantId,
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { createContext, useContext } from 'react';
|
import React, { createContext, useContext } from 'react';
|
||||||
|
|
||||||
const DrawerContext = createContext();
|
interface DrawerContextValue {
|
||||||
|
name: string;
|
||||||
|
payload: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const DrawerContext = createContext<DrawerContextValue>(
|
||||||
|
{} as DrawerContextValue,
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Account form provider.
|
* Account form provider.
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export function ElementCustomizeFieldsMain() {
|
|||||||
|
|
||||||
function ElementCustomizeFooterActionsRoot({ closeDrawer }) {
|
function ElementCustomizeFooterActionsRoot({ closeDrawer }) {
|
||||||
const { name } = useDrawerContext();
|
const { name } = useDrawerContext();
|
||||||
const { submitForm } = useFormikContext();
|
const { submitForm, isSubmitting } = useFormikContext();
|
||||||
|
|
||||||
const handleSubmitBtnClick = () => {
|
const handleSubmitBtnClick = () => {
|
||||||
submitForm();
|
submitForm();
|
||||||
@@ -62,6 +62,7 @@ function ElementCustomizeFooterActionsRoot({ closeDrawer }) {
|
|||||||
onClick={handleSubmitBtnClick}
|
onClick={handleSubmitBtnClick}
|
||||||
intent={Intent.PRIMARY}
|
intent={Intent.PRIMARY}
|
||||||
style={{ minWidth: 75 }}
|
style={{ minWidth: 75 }}
|
||||||
|
loading={isSubmitting}
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import React, { createContext, useContext } from 'react';
|
||||||
|
import {
|
||||||
|
GetPdfTemplateResponse,
|
||||||
|
useGetPdfTemplate,
|
||||||
|
} from '@/hooks/query/pdf-templates';
|
||||||
|
import { Spinner } from '@blueprintjs/core';
|
||||||
|
|
||||||
|
interface PdfTemplateContextValue {
|
||||||
|
templateId: number | string;
|
||||||
|
pdfTemplate: GetPdfTemplateResponse | undefined;
|
||||||
|
isPdfTemplateLoading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface BrandingTemplateProps {
|
||||||
|
templateId: number;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PdfTemplateContext = createContext<PdfTemplateContextValue>(
|
||||||
|
{} as PdfTemplateContextValue,
|
||||||
|
);
|
||||||
|
|
||||||
|
export const BrandingTemplateBoot = ({
|
||||||
|
templateId,
|
||||||
|
children,
|
||||||
|
}: BrandingTemplateProps) => {
|
||||||
|
const { data: pdfTemplate, isLoading: isPdfTemplateLoading } =
|
||||||
|
useGetPdfTemplate(templateId, {
|
||||||
|
enabled: !!templateId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const value = {
|
||||||
|
templateId,
|
||||||
|
pdfTemplate,
|
||||||
|
isPdfTemplateLoading,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isPdfTemplateLoading) {
|
||||||
|
return <Spinner size={20} />
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<PdfTemplateContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
</PdfTemplateContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useBrandingTemplateBoot = () => {
|
||||||
|
return useContext<PdfTemplateContextValue>(PdfTemplateContext);
|
||||||
|
};
|
||||||
@@ -2,13 +2,7 @@
|
|||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
import { Button, Classes, Intent } from '@blueprintjs/core';
|
import { Button, Classes, Intent } from '@blueprintjs/core';
|
||||||
import { BrandingTemplatesBoot } from './BrandingTemplatesBoot';
|
import { BrandingTemplatesBoot } from './BrandingTemplatesBoot';
|
||||||
import {
|
import { Box, Card, DrawerHeaderContent, Group } from '@/components';
|
||||||
Box,
|
|
||||||
Card,
|
|
||||||
DrawerBody,
|
|
||||||
DrawerHeaderContent,
|
|
||||||
Group,
|
|
||||||
} from '@/components';
|
|
||||||
import { DRAWERS } from '@/constants/drawers';
|
import { DRAWERS } from '@/constants/drawers';
|
||||||
import { BrandingTemplatesTable } from './BrandingTemplatesTable';
|
import { BrandingTemplatesTable } from './BrandingTemplatesTable';
|
||||||
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ function BrandingTemplateTableRoot({
|
|||||||
}: BrandingTemplatesTableProps) {
|
}: BrandingTemplatesTableProps) {
|
||||||
// Table columns.
|
// Table columns.
|
||||||
const columns = useBrandingTemplatesColumns();
|
const columns = useBrandingTemplatesColumns();
|
||||||
|
|
||||||
const { isPdfTemplatesLoading, pdfTemplates } = useBrandingTemplatesBoot();
|
const { isPdfTemplatesLoading, pdfTemplates } = useBrandingTemplatesBoot();
|
||||||
|
|
||||||
const handleEditTemplate = (template) => {
|
const handleEditTemplate = (template) => {
|
||||||
@@ -70,7 +69,7 @@ const useBrandingTemplatesColumns = () => {
|
|||||||
Header: 'Template Name',
|
Header: 'Template Name',
|
||||||
accessor: (row) => (
|
accessor: (row) => (
|
||||||
<Group spacing={10}>
|
<Group spacing={10}>
|
||||||
{row.template_name} <Tag round>Default</Tag>
|
{row.template_name} {row.default && <Tag round>Default</Tag>}
|
||||||
</Group>
|
</Group>
|
||||||
),
|
),
|
||||||
width: 65,
|
width: 65,
|
||||||
|
|||||||
@@ -1,98 +1,19 @@
|
|||||||
import React from 'react';
|
// @ts-nocheck
|
||||||
import * as R from 'ramda';
|
import { Classes } from '@blueprintjs/core';
|
||||||
import { AppToaster, Box } from '@/components';
|
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||||
import { Classes, Intent } from '@blueprintjs/core';
|
import { BrandingTemplateBoot } from '../BrandingTemplates/BrandingTemplateBoot';
|
||||||
import { useFormikContext } from 'formik';
|
import { InvoiceCustomizeContent } from './InvoiceCustomizeContent';
|
||||||
import {
|
import { Box } from '@/components';
|
||||||
InvoicePaperTemplate,
|
|
||||||
InvoicePaperTemplateProps,
|
|
||||||
} from './InvoicePaperTemplate';
|
|
||||||
import { ElementCustomize } from '../../../ElementCustomize/ElementCustomize';
|
|
||||||
import { InvoiceCustomizeGeneralField } from './InvoiceCustomizeGeneralFields';
|
|
||||||
import { InvoiceCustomizeContentFields } from './InvoiceCutomizeContentFields';
|
|
||||||
import { InvoiceCustomizeValues } from './types';
|
|
||||||
import { initialValues } from './constants';
|
|
||||||
import {
|
|
||||||
useCreatePdfTemplate,
|
|
||||||
useEditPdfTemplate,
|
|
||||||
} from '@/hooks/query/pdf-templates';
|
|
||||||
import { transformToEditRequest, transformToNewRequest } from './utils';
|
|
||||||
|
|
||||||
export default function InvoiceCustomizeContent() {
|
export default function InvoiceCustomize() {
|
||||||
const { mutateAsync: createPdfTemplate } = useCreatePdfTemplate();
|
const { payload } = useDrawerContext();
|
||||||
const { mutateAsync: editPdfTemplate } = useEditPdfTemplate();
|
const templateId = payload.templateId;
|
||||||
|
|
||||||
const templateId: number = 1;
|
|
||||||
|
|
||||||
const handleFormSubmit = (values: InvoiceCustomizeValues) => {
|
|
||||||
const handleSuccess = (message: string) => {
|
|
||||||
AppToaster.show({
|
|
||||||
intent: Intent.SUCCESS,
|
|
||||||
message,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const handleError = (message: string) => {
|
|
||||||
AppToaster.show({
|
|
||||||
intent: Intent.DANGER,
|
|
||||||
message,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
if (templateId) {
|
|
||||||
const reqValues = transformToEditRequest(values, templateId);
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// Create new template
|
|
||||||
createPdfTemplate(reqValues)
|
|
||||||
.then(() => handleSuccess('PDF template created successfully!'))
|
|
||||||
.catch(() =>
|
|
||||||
handleError('An error occurred while creating the PDF template.'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box className={Classes.DRAWER_BODY}>
|
<Box className={Classes.DRAWER_BODY}>
|
||||||
<ElementCustomize<InvoiceCustomizeValues>
|
<BrandingTemplateBoot templateId={templateId}>
|
||||||
initialValues={initialValues}
|
<InvoiceCustomizeContent />
|
||||||
onSubmit={handleFormSubmit}
|
</BrandingTemplateBoot>
|
||||||
>
|
|
||||||
<ElementCustomize.PaperTemplate>
|
|
||||||
<InvoicePaperTemplateFormConnected />
|
|
||||||
</ElementCustomize.PaperTemplate>
|
|
||||||
|
|
||||||
<ElementCustomize.FieldsTab id={'general'} label={'General'}>
|
|
||||||
<InvoiceCustomizeGeneralField />
|
|
||||||
</ElementCustomize.FieldsTab>
|
|
||||||
|
|
||||||
<ElementCustomize.FieldsTab id={'content'} label={'Content'}>
|
|
||||||
<InvoiceCustomizeContentFields />
|
|
||||||
</ElementCustomize.FieldsTab>
|
|
||||||
|
|
||||||
<ElementCustomize.FieldsTab id={'totals'} label={'Totals'}>
|
|
||||||
asdfasdfdsaf #3
|
|
||||||
</ElementCustomize.FieldsTab>
|
|
||||||
</ElementCustomize>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const withFormikProps = <P extends object>(
|
|
||||||
Component: React.ComponentType<P>,
|
|
||||||
) => {
|
|
||||||
return (props: Omit<P, keyof InvoicePaperTemplateProps>) => {
|
|
||||||
const { values } = useFormikContext<InvoicePaperTemplateProps>();
|
|
||||||
|
|
||||||
return <Component {...(props as P)} {...values} />;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const InvoicePaperTemplateFormConnected =
|
|
||||||
R.compose(withFormikProps)(InvoicePaperTemplate);
|
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import * as R from 'ramda';
|
||||||
|
import { AppToaster } from '@/components';
|
||||||
|
import { Intent } from '@blueprintjs/core';
|
||||||
|
import { FormikHelpers, useFormikContext } from 'formik';
|
||||||
|
import {
|
||||||
|
InvoicePaperTemplate,
|
||||||
|
InvoicePaperTemplateProps,
|
||||||
|
} from './InvoicePaperTemplate';
|
||||||
|
import { ElementCustomize } from '../../../ElementCustomize/ElementCustomize';
|
||||||
|
import { InvoiceCustomizeGeneralField } from './InvoiceCustomizeGeneralFields';
|
||||||
|
import { InvoiceCustomizeContentFields } from './InvoiceCutomizeContentFields';
|
||||||
|
import { InvoiceCustomizeValues } from './types';
|
||||||
|
import {
|
||||||
|
useCreatePdfTemplate,
|
||||||
|
useEditPdfTemplate,
|
||||||
|
} from '@/hooks/query/pdf-templates';
|
||||||
|
import {
|
||||||
|
transformToEditRequest,
|
||||||
|
transformToNewRequest,
|
||||||
|
useInvoiceCustomizeInitialValues,
|
||||||
|
} from './utils';
|
||||||
|
import { InvoiceCustomizeSchema } from './InvoiceCustomizeForm.schema';
|
||||||
|
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||||
|
import { useDrawerActions } from '@/hooks/state';
|
||||||
|
|
||||||
|
export function InvoiceCustomizeContent() {
|
||||||
|
const { mutateAsync: createPdfTemplate } = useCreatePdfTemplate();
|
||||||
|
const { mutateAsync: editPdfTemplate } = useEditPdfTemplate();
|
||||||
|
|
||||||
|
const { payload, name } = useDrawerContext();
|
||||||
|
const { closeDrawer } = useDrawerActions();
|
||||||
|
|
||||||
|
const templateId = payload?.templateId || null;
|
||||||
|
|
||||||
|
const handleFormSubmit = (
|
||||||
|
values: InvoiceCustomizeValues,
|
||||||
|
{ 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 (
|
||||||
|
<ElementCustomize<InvoiceCustomizeValues>
|
||||||
|
initialValues={initialValues}
|
||||||
|
validationSchema={InvoiceCustomizeSchema}
|
||||||
|
onSubmit={handleFormSubmit}
|
||||||
|
>
|
||||||
|
<ElementCustomize.PaperTemplate>
|
||||||
|
<InvoicePaperTemplateFormConnected />
|
||||||
|
</ElementCustomize.PaperTemplate>
|
||||||
|
|
||||||
|
<ElementCustomize.FieldsTab id={'general'} label={'General'}>
|
||||||
|
<InvoiceCustomizeGeneralField />
|
||||||
|
</ElementCustomize.FieldsTab>
|
||||||
|
|
||||||
|
<ElementCustomize.FieldsTab id={'content'} label={'Content'}>
|
||||||
|
<InvoiceCustomizeContentFields />
|
||||||
|
</ElementCustomize.FieldsTab>
|
||||||
|
|
||||||
|
<ElementCustomize.FieldsTab id={'totals'} label={'Totals'}>
|
||||||
|
asdfasdfdsaf #3
|
||||||
|
</ElementCustomize.FieldsTab>
|
||||||
|
</ElementCustomize>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const withFormikProps = <P extends object>(
|
||||||
|
Component: React.ComponentType<P>,
|
||||||
|
) => {
|
||||||
|
return (props: Omit<P, keyof InvoicePaperTemplateProps>) => {
|
||||||
|
const { values } = useFormikContext<InvoicePaperTemplateProps>();
|
||||||
|
|
||||||
|
return <Component {...(props as P)} {...values} />;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const InvoicePaperTemplateFormConnected =
|
||||||
|
R.compose(withFormikProps)(InvoicePaperTemplate);
|
||||||
@@ -4,9 +4,7 @@ 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 InvoiceCustomize = React.lazy(
|
const InvoiceCustomize = React.lazy(() => import('./InvoiceCustomize'));
|
||||||
() => import('./InvoiceCustomize'),
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoice customize drawer.
|
* Invoice customize drawer.
|
||||||
@@ -16,10 +14,10 @@ function InvoiceCustomizeDrawerRoot({
|
|||||||
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>
|
||||||
<InvoiceCustomize />
|
<InvoiceCustomize />
|
||||||
</DrawerSuspense>
|
</DrawerSuspense>
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import * as Yup from 'yup';
|
||||||
|
|
||||||
|
export const InvoiceCustomizeSchema = Yup.object().shape({
|
||||||
|
templateName: Yup.string().required('Template Name is required'),
|
||||||
|
});
|
||||||
@@ -1,61 +1,84 @@
|
|||||||
// @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,
|
||||||
|
FieldRequiredHint,
|
||||||
|
FInputGroup,
|
||||||
|
FSwitch,
|
||||||
|
Group,
|
||||||
|
Stack,
|
||||||
|
} from '@/components';
|
||||||
import { FColorInput } from '@/components/Forms/FColorInput';
|
import { FColorInput } from '@/components/Forms/FColorInput';
|
||||||
import { CreditCardIcon } from '@/icons/CreditCardIcon';
|
import { CreditCardIcon } from '@/icons/CreditCardIcon';
|
||||||
|
import { Overlay } from './Overlay';
|
||||||
|
import { useIsTemplateNamedFilled } from './utils';
|
||||||
|
|
||||||
export function InvoiceCustomizeGeneralField() {
|
export function InvoiceCustomizeGeneralField() {
|
||||||
|
const isTemplateNameFilled = useIsTemplateNamedFilled();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack style={{ padding: 20, flex: '1 1 auto' }}>
|
<Stack style={{ padding: 20, flex: '1 1 auto' }}>
|
||||||
<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 time
you
|
Set your invoice details to be automatically applied every timeyou
|
||||||
create a new invoice.
|
create a new invoice.
|
||||||
</p>
|
</p>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Stack spacing={0}>
|
<FFormGroup
|
||||||
<FFormGroup
|
name={'templateName'}
|
||||||
name={'primaryColor'}
|
label={'Template Name'}
|
||||||
label={'Primary Color'}
|
labelInfo={<FieldRequiredHint />}
|
||||||
style={{ justifyContent: 'space-between' }}
|
fastField
|
||||||
inline
|
style={{ marginBottom: 10 }}
|
||||||
fastField
|
>
|
||||||
>
|
<FInputGroup name={'templateName'} fastField />
|
||||||
<FColorInput
|
</FFormGroup>
|
||||||
|
|
||||||
|
<Overlay visible={!isTemplateNameFilled}>
|
||||||
|
<Stack spacing={0}>
|
||||||
|
<FFormGroup
|
||||||
name={'primaryColor'}
|
name={'primaryColor'}
|
||||||
inputProps={{ style: { maxWidth: 120 } }}
|
label={'Primary Color'}
|
||||||
|
style={{ justifyContent: 'space-between' }}
|
||||||
|
inline
|
||||||
fastField
|
fastField
|
||||||
/>
|
>
|
||||||
</FFormGroup>
|
<FColorInput
|
||||||
|
name={'primaryColor'}
|
||||||
|
inputProps={{ style: { maxWidth: 120 } }}
|
||||||
|
fastField
|
||||||
|
/>
|
||||||
|
</FFormGroup>
|
||||||
|
|
||||||
<FFormGroup
|
<FFormGroup
|
||||||
name={'secondaryColor'}
|
|
||||||
label={'Secondary Color'}
|
|
||||||
style={{ justifyContent: 'space-between' }}
|
|
||||||
inline
|
|
||||||
fastField
|
|
||||||
>
|
|
||||||
<FColorInput
|
|
||||||
name={'secondaryColor'}
|
name={'secondaryColor'}
|
||||||
inputProps={{ style: { maxWidth: 120 } }}
|
label={'Secondary Color'}
|
||||||
|
style={{ justifyContent: 'space-between' }}
|
||||||
|
inline
|
||||||
fastField
|
fastField
|
||||||
/>
|
>
|
||||||
</FFormGroup>
|
<FColorInput
|
||||||
|
name={'secondaryColor'}
|
||||||
|
inputProps={{ style: { maxWidth: 120 } }}
|
||||||
|
fastField
|
||||||
|
/>
|
||||||
|
</FFormGroup>
|
||||||
|
|
||||||
<FFormGroup name={'showCompanyLogo'} label={'Logo'} fastField>
|
<FFormGroup name={'showCompanyLogo'} label={'Logo'} fastField>
|
||||||
<FSwitch
|
<FSwitch
|
||||||
name={'showCompanyLogo'}
|
name={'showCompanyLogo'}
|
||||||
label={'Display company logo in the paper'}
|
label={'Display company logo in the paper'}
|
||||||
style={{ fontSize: 14 }}
|
style={{ fontSize: 14 }}
|
||||||
large
|
large
|
||||||
fastField
|
fastField
|
||||||
/>
|
/>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<InvoiceCustomizePaymentManage />
|
<InvoiceCustomizePaymentManage />
|
||||||
|
</Overlay>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export function InvoiceCustomizeContentFields() {
|
|||||||
<Stack spacing={10}>
|
<Stack spacing={10}>
|
||||||
<h3>General Branding</h3>
|
<h3>General Branding</h3>
|
||||||
<p className={Classes.TEXT_MUTED}>
|
<p className={Classes.TEXT_MUTED}>
|
||||||
Set your invoice details to be automatically applied every time
you
|
Set your invoice details to be automatically applied every timeyou
|
||||||
create a new invoice.
|
create a new invoice.
|
||||||
</p>
|
</p>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
|
||||||
|
.root {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.visible::before{
|
||||||
|
background: rgba(255, 255, 255, 0.75);
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
&::before{
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: -1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import clsx from 'classnames';
|
||||||
|
import { Box } from '@/components';
|
||||||
|
import styles from './Overlay.module.scss';
|
||||||
|
|
||||||
|
export interface OverlayProps {
|
||||||
|
visible?: boolean;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Overlay({ children, visible }: OverlayProps) {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
className={clsx(styles.root, {
|
||||||
|
[styles.visible]: visible,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
export const initialValues = {
|
export const initialValues = {
|
||||||
|
templateName: '',
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
primaryColor: '#2c3dd8',
|
primaryColor: '#2c3dd8',
|
||||||
secondaryColor: '#2c3dd8',
|
secondaryColor: '#2c3dd8',
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
export interface InvoiceCustomizeValues {
|
export interface InvoiceCustomizeValues {
|
||||||
|
templateName: string;
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
primaryColor?: string;
|
primaryColor?: string;
|
||||||
secondaryColor?: string;
|
secondaryColor?: string;
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
|
import { useFormikContext } from 'formik';
|
||||||
import { InvoiceCustomizeValues } from './types';
|
import { InvoiceCustomizeValues } from './types';
|
||||||
import { CreatePdfTemplateValues, EditPdfTemplateValues } from '@/hooks/query/pdf-templates';
|
import {
|
||||||
|
CreatePdfTemplateValues,
|
||||||
|
EditPdfTemplateValues,
|
||||||
|
} from '@/hooks/query/pdf-templates';
|
||||||
|
import { useBrandingTemplateBoot } from '../BrandingTemplates/BrandingTemplateBoot';
|
||||||
|
import { transformToForm } from '@/utils';
|
||||||
|
import { initialValues } from './constants';
|
||||||
|
|
||||||
export const transformToEditRequest = (
|
export const transformToEditRequest = (
|
||||||
values: InvoiceCustomizeValues,
|
values: InvoiceCustomizeValues,
|
||||||
templateId: number,
|
|
||||||
): EditPdfTemplateValues => {
|
): EditPdfTemplateValues => {
|
||||||
return {
|
return {
|
||||||
templateId,
|
templateName: values.templateName,
|
||||||
templateName: 'Template Name',
|
|
||||||
attributes: omit(values, ['templateName']),
|
attributes: omit(values, ['templateName']),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -18,7 +23,29 @@ export const transformToNewRequest = (
|
|||||||
): CreatePdfTemplateValues => {
|
): CreatePdfTemplateValues => {
|
||||||
return {
|
return {
|
||||||
resource: 'SaleInvoice',
|
resource: 'SaleInvoice',
|
||||||
templateName: 'Template Name',
|
templateName: values.templateName,
|
||||||
attributes: omit(values, ['templateName']),
|
attributes: omit(values, ['templateName']),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useIsTemplateNamedFilled = () => {
|
||||||
|
const { values } = useFormikContext<InvoiceCustomizeValues>();
|
||||||
|
|
||||||
|
return values.templateName && values.templateName?.length >= 4;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useInvoiceCustomizeInitialValues = (): InvoiceCustomizeValues => {
|
||||||
|
const { pdfTemplate } = useBrandingTemplateBoot();
|
||||||
|
|
||||||
|
const defaultPdfTemplate = {
|
||||||
|
templateName: pdfTemplate?.templateName,
|
||||||
|
...pdfTemplate?.attributes,
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
...initialValues,
|
||||||
|
...(transformToForm(
|
||||||
|
defaultPdfTemplate,
|
||||||
|
initialValues,
|
||||||
|
) as InvoiceCustomizeValues),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ function InvoiceActionsBar({
|
|||||||
<Menu>
|
<Menu>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={handleCustomizeBtnClick}
|
onClick={handleCustomizeBtnClick}
|
||||||
text={'Customize Invoice'}
|
text={'Invoice Templates'}
|
||||||
/>
|
/>
|
||||||
</Menu>
|
</Menu>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,12 @@ import {
|
|||||||
UseQueryOptions,
|
UseQueryOptions,
|
||||||
UseMutationResult,
|
UseMutationResult,
|
||||||
UseQueryResult,
|
UseQueryResult,
|
||||||
|
useQueryClient,
|
||||||
} from 'react-query';
|
} from 'react-query';
|
||||||
import useApiRequest from '../useRequest';
|
import useApiRequest from '../useRequest';
|
||||||
|
import { transformToCamelCase, transfromToSnakeCase } from '@/utils';
|
||||||
|
|
||||||
|
const PdfTemplatesQueryKey = 'PdfTemplate';
|
||||||
|
|
||||||
export interface CreatePdfTemplateValues {
|
export interface CreatePdfTemplateValues {
|
||||||
templateName: string;
|
templateName: string;
|
||||||
@@ -18,7 +22,6 @@ export interface CreatePdfTemplateValues {
|
|||||||
export interface CreatePdfTemplateResponse {}
|
export interface CreatePdfTemplateResponse {}
|
||||||
|
|
||||||
export interface EditPdfTemplateValues {
|
export interface EditPdfTemplateValues {
|
||||||
templateId: string | number;
|
|
||||||
templateName: string;
|
templateName: string;
|
||||||
attributes: Record<string, any>;
|
attributes: Record<string, any>;
|
||||||
}
|
}
|
||||||
@@ -33,7 +36,14 @@ export interface DeletePdfTemplateResponse {}
|
|||||||
|
|
||||||
export interface GetPdfTemplateValues {}
|
export interface GetPdfTemplateValues {}
|
||||||
|
|
||||||
export interface GetPdfTemplateResponse {}
|
export interface GetPdfTemplateResponse {
|
||||||
|
templateName: string;
|
||||||
|
attributes: Record<string, any>;
|
||||||
|
predefined: boolean;
|
||||||
|
default: boolean;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface GetPdfTemplatesValues {}
|
export interface GetPdfTemplatesValues {}
|
||||||
|
|
||||||
@@ -52,10 +62,18 @@ export const useCreatePdfTemplate = (
|
|||||||
CreatePdfTemplateValues
|
CreatePdfTemplateValues
|
||||||
> => {
|
> => {
|
||||||
const apiRequest = useApiRequest();
|
const apiRequest = useApiRequest();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
return useMutation<CreatePdfTemplateResponse, Error, CreatePdfTemplateValues>(
|
return useMutation<CreatePdfTemplateResponse, Error, CreatePdfTemplateValues>(
|
||||||
(values) =>
|
(values) =>
|
||||||
apiRequest.post('/pdf-templates', values).then((res) => res.data),
|
apiRequest
|
||||||
options,
|
.post('/pdf-templates', transfromToSnakeCase(values))
|
||||||
|
.then((res) => res.data),
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries([PdfTemplatesQueryKey]);
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -71,6 +89,7 @@ export const useEditPdfTemplate = (
|
|||||||
Error,
|
Error,
|
||||||
{ templateId: number; values: EditPdfTemplateValues }
|
{ templateId: number; values: EditPdfTemplateValues }
|
||||||
> => {
|
> => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
const apiRequest = useApiRequest();
|
const apiRequest = useApiRequest();
|
||||||
return useMutation<
|
return useMutation<
|
||||||
EditPdfTemplateResponse,
|
EditPdfTemplateResponse,
|
||||||
@@ -79,9 +98,14 @@ export const useEditPdfTemplate = (
|
|||||||
>(
|
>(
|
||||||
({ templateId, values }) =>
|
({ templateId, values }) =>
|
||||||
apiRequest
|
apiRequest
|
||||||
.put(`/pdf-templates/${templateId}`, values)
|
.put(`/pdf-templates/${templateId}`, transfromToSnakeCase(values))
|
||||||
.then((res) => res.data),
|
.then((res) => res.data),
|
||||||
options,
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries([PdfTemplatesQueryKey]);
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -98,10 +122,16 @@ export const useDeletePdfTemplate = (
|
|||||||
{ templateId: number }
|
{ templateId: number }
|
||||||
> => {
|
> => {
|
||||||
const apiRequest = useApiRequest();
|
const apiRequest = useApiRequest();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
return useMutation<DeletePdfTemplateResponse, Error, { templateId: number }>(
|
return useMutation<DeletePdfTemplateResponse, Error, { templateId: number }>(
|
||||||
({ templateId }) =>
|
({ templateId }) =>
|
||||||
apiRequest.delete(`/pdf-templates/${templateId}`).then((res) => res.data),
|
apiRequest.delete(`/pdf-templates/${templateId}`).then((res) => res.data),
|
||||||
options,
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries([PdfTemplatesQueryKey]);
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -112,9 +142,11 @@ export const useGetPdfTemplate = (
|
|||||||
): UseQueryResult<GetPdfTemplateResponse, Error> => {
|
): UseQueryResult<GetPdfTemplateResponse, Error> => {
|
||||||
const apiRequest = useApiRequest();
|
const apiRequest = useApiRequest();
|
||||||
return useQuery<GetPdfTemplateResponse, Error>(
|
return useQuery<GetPdfTemplateResponse, Error>(
|
||||||
['pdfTemplate', templateId],
|
[PdfTemplatesQueryKey, templateId],
|
||||||
() =>
|
() =>
|
||||||
apiRequest.get(`/pdf-templates/${templateId}`).then((res) => res.data),
|
apiRequest
|
||||||
|
.get(`/pdf-templates/${templateId}`)
|
||||||
|
.then((res) => transformToCamelCase(res.data)),
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -125,7 +157,7 @@ export const useGetPdfTemplates = (
|
|||||||
): UseQueryResult<GetPdfTemplatesResponse, Error> => {
|
): UseQueryResult<GetPdfTemplatesResponse, Error> => {
|
||||||
const apiRequest = useApiRequest();
|
const apiRequest = useApiRequest();
|
||||||
return useQuery<GetPdfTemplatesResponse, Error>(
|
return useQuery<GetPdfTemplatesResponse, Error>(
|
||||||
'pdfTemplates',
|
PdfTemplatesQueryKey,
|
||||||
() => apiRequest.get('/pdf-templates').then((res) => res.data),
|
() => apiRequest.get('/pdf-templates').then((res) => res.data),
|
||||||
options,
|
options,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import {
|
|||||||
closeSidebarSubmenu,
|
closeSidebarSubmenu,
|
||||||
openDialog,
|
openDialog,
|
||||||
closeDialog,
|
closeDialog,
|
||||||
|
openDrawer,
|
||||||
|
closeDrawer,
|
||||||
} from '@/store/dashboard/dashboard.actions';
|
} from '@/store/dashboard/dashboard.actions';
|
||||||
|
|
||||||
export const useDispatchAction = (action) => {
|
export const useDispatchAction = (action) => {
|
||||||
@@ -77,3 +79,10 @@ export const useDialogActions = () => {
|
|||||||
closeDialog: useDispatchAction(closeDialog),
|
closeDialog: useDispatchAction(closeDialog),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useDrawerActions = () => {
|
||||||
|
return {
|
||||||
|
openDrawer: useDispatchAction(openDrawer),
|
||||||
|
closeDrawer: useDispatchAction(closeDrawer),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user