mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 06:10:31 +00:00
feat: hook up the invice customize api
This commit is contained in:
@@ -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 { Button, Classes, Intent } from '@blueprintjs/core';
|
||||
import { BrandingTemplatesBoot } from './BrandingTemplatesBoot';
|
||||
import {
|
||||
Box,
|
||||
Card,
|
||||
DrawerBody,
|
||||
DrawerHeaderContent,
|
||||
Group,
|
||||
} from '@/components';
|
||||
import { Box, Card, DrawerHeaderContent, Group } from '@/components';
|
||||
import { DRAWERS } from '@/constants/drawers';
|
||||
import { BrandingTemplatesTable } from './BrandingTemplatesTable';
|
||||
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
||||
|
||||
@@ -18,7 +18,6 @@ function BrandingTemplateTableRoot({
|
||||
}: BrandingTemplatesTableProps) {
|
||||
// Table columns.
|
||||
const columns = useBrandingTemplatesColumns();
|
||||
|
||||
const { isPdfTemplatesLoading, pdfTemplates } = useBrandingTemplatesBoot();
|
||||
|
||||
const handleEditTemplate = (template) => {
|
||||
@@ -70,7 +69,7 @@ const useBrandingTemplatesColumns = () => {
|
||||
Header: 'Template Name',
|
||||
accessor: (row) => (
|
||||
<Group spacing={10}>
|
||||
{row.template_name} <Tag round>Default</Tag>
|
||||
{row.template_name} {row.default && <Tag round>Default</Tag>}
|
||||
</Group>
|
||||
),
|
||||
width: 65,
|
||||
|
||||
@@ -1,98 +1,19 @@
|
||||
import React from 'react';
|
||||
import * as R from 'ramda';
|
||||
import { AppToaster, Box } from '@/components';
|
||||
import { Classes, Intent } from '@blueprintjs/core';
|
||||
import { 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 { initialValues } from './constants';
|
||||
import {
|
||||
useCreatePdfTemplate,
|
||||
useEditPdfTemplate,
|
||||
} from '@/hooks/query/pdf-templates';
|
||||
import { transformToEditRequest, transformToNewRequest } from './utils';
|
||||
// @ts-nocheck
|
||||
import { Classes } from '@blueprintjs/core';
|
||||
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||
import { BrandingTemplateBoot } from '../BrandingTemplates/BrandingTemplateBoot';
|
||||
import { InvoiceCustomizeContent } from './InvoiceCustomizeContent';
|
||||
import { Box } from '@/components';
|
||||
|
||||
export default function InvoiceCustomizeContent() {
|
||||
const { mutateAsync: createPdfTemplate } = useCreatePdfTemplate();
|
||||
const { mutateAsync: editPdfTemplate } = useEditPdfTemplate();
|
||||
|
||||
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.'),
|
||||
);
|
||||
}
|
||||
};
|
||||
export default function InvoiceCustomize() {
|
||||
const { payload } = useDrawerContext();
|
||||
const templateId = payload.templateId;
|
||||
|
||||
return (
|
||||
<Box className={Classes.DRAWER_BODY}>
|
||||
<ElementCustomize<InvoiceCustomizeValues>
|
||||
initialValues={initialValues}
|
||||
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>
|
||||
<BrandingTemplateBoot templateId={templateId}>
|
||||
<InvoiceCustomizeContent />
|
||||
</BrandingTemplateBoot>
|
||||
</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 withDrawers from '@/containers/Drawer/withDrawers';
|
||||
|
||||
const InvoiceCustomize = React.lazy(
|
||||
() => import('./InvoiceCustomize'),
|
||||
);
|
||||
const InvoiceCustomize = React.lazy(() => import('./InvoiceCustomize'));
|
||||
|
||||
/**
|
||||
* Invoice customize drawer.
|
||||
@@ -16,10 +14,10 @@ function InvoiceCustomizeDrawerRoot({
|
||||
name,
|
||||
// #withDrawer
|
||||
isOpen,
|
||||
payload: {},
|
||||
payload,
|
||||
}) {
|
||||
return (
|
||||
<Drawer isOpen={isOpen} name={name} size={'100%'}>
|
||||
<Drawer isOpen={isOpen} name={name} size={'100%'} payload={payload}>
|
||||
<DrawerSuspense>
|
||||
<InvoiceCustomize />
|
||||
</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
|
||||
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 { CreditCardIcon } from '@/icons/CreditCardIcon';
|
||||
import { Overlay } from './Overlay';
|
||||
import { useIsTemplateNamedFilled } from './utils';
|
||||
|
||||
export function InvoiceCustomizeGeneralField() {
|
||||
const isTemplateNameFilled = useIsTemplateNamedFilled();
|
||||
|
||||
return (
|
||||
<Stack style={{ padding: 20, flex: '1 1 auto' }}>
|
||||
<Stack spacing={0}>
|
||||
<h2 style={{ fontSize: 16, marginBottom: 10 }}>General Branding</h2>
|
||||
<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.
|
||||
</p>
|
||||
</Stack>
|
||||
|
||||
<Stack spacing={0}>
|
||||
<FFormGroup
|
||||
name={'primaryColor'}
|
||||
label={'Primary Color'}
|
||||
style={{ justifyContent: 'space-between' }}
|
||||
inline
|
||||
fastField
|
||||
>
|
||||
<FColorInput
|
||||
<FFormGroup
|
||||
name={'templateName'}
|
||||
label={'Template Name'}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
fastField
|
||||
style={{ marginBottom: 10 }}
|
||||
>
|
||||
<FInputGroup name={'templateName'} fastField />
|
||||
</FFormGroup>
|
||||
|
||||
<Overlay visible={!isTemplateNameFilled}>
|
||||
<Stack spacing={0}>
|
||||
<FFormGroup
|
||||
name={'primaryColor'}
|
||||
inputProps={{ style: { maxWidth: 120 } }}
|
||||
label={'Primary Color'}
|
||||
style={{ justifyContent: 'space-between' }}
|
||||
inline
|
||||
fastField
|
||||
/>
|
||||
</FFormGroup>
|
||||
>
|
||||
<FColorInput
|
||||
name={'primaryColor'}
|
||||
inputProps={{ style: { maxWidth: 120 } }}
|
||||
fastField
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup
|
||||
name={'secondaryColor'}
|
||||
label={'Secondary Color'}
|
||||
style={{ justifyContent: 'space-between' }}
|
||||
inline
|
||||
fastField
|
||||
>
|
||||
<FColorInput
|
||||
<FFormGroup
|
||||
name={'secondaryColor'}
|
||||
inputProps={{ style: { maxWidth: 120 } }}
|
||||
label={'Secondary Color'}
|
||||
style={{ justifyContent: 'space-between' }}
|
||||
inline
|
||||
fastField
|
||||
/>
|
||||
</FFormGroup>
|
||||
>
|
||||
<FColorInput
|
||||
name={'secondaryColor'}
|
||||
inputProps={{ style: { maxWidth: 120 } }}
|
||||
fastField
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
<FFormGroup name={'showCompanyLogo'} label={'Logo'} fastField>
|
||||
<FSwitch
|
||||
name={'showCompanyLogo'}
|
||||
label={'Display company logo in the paper'}
|
||||
style={{ fontSize: 14 }}
|
||||
large
|
||||
fastField
|
||||
/>
|
||||
</FFormGroup>
|
||||
</Stack>
|
||||
<FFormGroup name={'showCompanyLogo'} label={'Logo'} fastField>
|
||||
<FSwitch
|
||||
name={'showCompanyLogo'}
|
||||
label={'Display company logo in the paper'}
|
||||
style={{ fontSize: 14 }}
|
||||
large
|
||||
fastField
|
||||
/>
|
||||
</FFormGroup>
|
||||
</Stack>
|
||||
|
||||
<InvoiceCustomizePaymentManage />
|
||||
<InvoiceCustomizePaymentManage />
|
||||
</Overlay>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ export function InvoiceCustomizeContentFields() {
|
||||
<Stack spacing={10}>
|
||||
<h3>General Branding</h3>
|
||||
<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.
|
||||
</p>
|
||||
</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 = {
|
||||
templateName: '',
|
||||
|
||||
// Colors
|
||||
primaryColor: '#2c3dd8',
|
||||
secondaryColor: '#2c3dd8',
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export interface InvoiceCustomizeValues {
|
||||
templateName: string;
|
||||
|
||||
// Colors
|
||||
primaryColor?: string;
|
||||
secondaryColor?: string;
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import { omit } from 'lodash';
|
||||
import { useFormikContext } from 'formik';
|
||||
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 = (
|
||||
values: InvoiceCustomizeValues,
|
||||
templateId: number,
|
||||
): EditPdfTemplateValues => {
|
||||
return {
|
||||
templateId,
|
||||
templateName: 'Template Name',
|
||||
templateName: values.templateName,
|
||||
attributes: omit(values, ['templateName']),
|
||||
};
|
||||
};
|
||||
@@ -18,7 +23,29 @@ export const transformToNewRequest = (
|
||||
): CreatePdfTemplateValues => {
|
||||
return {
|
||||
resource: 'SaleInvoice',
|
||||
templateName: 'Template Name',
|
||||
templateName: 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>
|
||||
<MenuItem
|
||||
onClick={handleCustomizeBtnClick}
|
||||
text={'Customize Invoice'}
|
||||
text={'Invoice Templates'}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user