feat: templates customize

This commit is contained in:
Ahmed Bouhuolia
2024-09-12 17:49:00 +02:00
parent 12226d469a
commit 411ac55986
17 changed files with 293 additions and 173 deletions

View File

@@ -1,20 +1,30 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React, { useMemo } from 'react';
import { Button, NavbarGroup, Intent } from '@blueprintjs/core'; import { Button, NavbarGroup, Intent } from '@blueprintjs/core';
import { DashboardActionsBar, Icon } from '@/components'; import { DashboardActionsBar, Icon } from '@/components';
import { DRAWERS } from '@/constants/drawers';
import withDrawerActions from '@/containers/Drawer/withDrawerActions'; import withDrawerActions from '@/containers/Drawer/withDrawerActions';
import {
getButtonLabelFromResource,
getCustomizeDrawerNameFromResource,
} from './_utils';
import { compose } from '@/utils'; import { compose } from '@/utils';
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
/** /**
* Account drawer action bar. * Account drawer action bar.
*/ */
function BrandingTemplateActionsBarRoot({ openDrawer }) { function BrandingTemplateActionsBarRoot({ openDrawer }) {
const {
payload: { resource },
} = useDrawerContext();
// Handle new child button click. // Handle new child button click.
const handleCreateBtnClick = () => { const handleCreateBtnClick = () => {
openDrawer(DRAWERS.INVOICE_CUSTOMIZE); const drawerResource = getCustomizeDrawerNameFromResource(resource);
openDrawer(drawerResource);
}; };
const label = useMemo(() => getButtonLabelFromResource(resource), [resource]);
return ( return (
<DashboardActionsBar> <DashboardActionsBar>
<NavbarGroup> <NavbarGroup>
@@ -24,7 +34,7 @@ function BrandingTemplateActionsBarRoot({ openDrawer }) {
onClick={handleCreateBtnClick} onClick={handleCreateBtnClick}
minimal minimal
> >
Create Invoice Branding {label}
</Button> </Button>
</NavbarGroup> </NavbarGroup>
</DashboardActionsBar> </DashboardActionsBar>

View File

@@ -5,8 +5,8 @@ import { BrandingTemplatesBoot } from './BrandingTemplatesBoot';
import { Box, Card, DrawerHeaderContent, Group } from '@/components'; import { Box, Card, 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 { BrandingTemplateActionsBar } from './BrandingTemplatesActionsBar'; import { BrandingTemplateActionsBar } from './BrandingTemplatesActionsBar';
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
export default function BrandingTemplateContent() { export default function BrandingTemplateContent() {
return ( return (

View File

@@ -5,10 +5,11 @@ import clsx from 'classnames';
import { DataTable, Group, TableSkeletonRows } from '@/components'; import { DataTable, Group, TableSkeletonRows } from '@/components';
import { useBrandingTemplatesBoot } from './BrandingTemplatesBoot'; import { useBrandingTemplatesBoot } from './BrandingTemplatesBoot';
import { ActionsMenu } from './_components'; import { ActionsMenu } from './_components';
import { DRAWERS } from '@/constants/drawers';
import withAlertActions from '@/containers/Alert/withAlertActions'; import withAlertActions from '@/containers/Alert/withAlertActions';
import withDrawerActions from '@/containers/Drawer/withDrawerActions'; import withDrawerActions from '@/containers/Drawer/withDrawerActions';
import { DRAWERS } from '@/constants/drawers';
import styles from './BrandTemplates.module.scss'; import styles from './BrandTemplates.module.scss';
import { getCustomizeDrawerNameFromResource } from './_utils';
interface BrandingTemplatesTableProps {} interface BrandingTemplatesTableProps {}
@@ -35,7 +36,10 @@ function BrandingTemplateTableRoot({
const templateId = cell.row.original.id; const templateId = cell.row.original.id;
const resource = cell.row.original.resource; const resource = cell.row.original.resource;
openDrawer(DRAWERS.INVOICE_CUSTOMIZE, { templateId, resource }); // Retrieves the customize drawer name from the given resource name.
const drawerName = getCustomizeDrawerNameFromResource(resource);
openDrawer(drawerName, { templateId, resource });
}; };
return ( return (

View File

@@ -1,4 +1,5 @@
import { omit } from 'lodash'; import { omit } from 'lodash';
import * as R from 'ramda';
import { import {
CreatePdfTemplateValues, CreatePdfTemplateValues,
EditPdfTemplateValues, EditPdfTemplateValues,
@@ -7,6 +8,7 @@ import { useBrandingTemplateBoot } from './BrandingTemplateBoot';
import { transformToForm } from '@/utils'; import { transformToForm } from '@/utils';
import { BrandingTemplateValues } from './types'; import { BrandingTemplateValues } from './types';
import { useFormikContext } from 'formik'; import { useFormikContext } from 'formik';
import { DRAWERS } from '@/constants/drawers';
export const transformToEditRequest = <T extends BrandingTemplateValues>( export const transformToEditRequest = <T extends BrandingTemplateValues>(
values: T, values: T,
@@ -19,7 +21,7 @@ export const transformToEditRequest = <T extends BrandingTemplateValues>(
export const transformToNewRequest = <T extends BrandingTemplateValues>( export const transformToNewRequest = <T extends BrandingTemplateValues>(
values: T, values: T,
resource: string resource: string,
): CreatePdfTemplateValues => { ): CreatePdfTemplateValues => {
return { return {
resource, resource,
@@ -28,12 +30,6 @@ export const transformToNewRequest = <T extends BrandingTemplateValues>(
}; };
}; };
export const useIsTemplateNamedFilled = () => {
const { values } = useFormikContext<BrandingTemplateValues>();
return values.templateName && values.templateName?.length >= 4;
};
export const useBrandingTemplateFormInitialValues = < export const useBrandingTemplateFormInitialValues = <
T extends BrandingTemplateValues, T extends BrandingTemplateValues,
>( >(
@@ -50,3 +46,25 @@ export const useBrandingTemplateFormInitialValues = <
...(transformToForm(defaultPdfTemplate, initialValues) as T), ...(transformToForm(defaultPdfTemplate, initialValues) as T),
}; };
}; };
export const getCustomizeDrawerNameFromResource = (resource: string) => {
const pairs = {
SaleInvoice: DRAWERS.INVOICE_CUSTOMIZE,
SaleEstimate: DRAWERS.ESTIMATE_CUSTOMIZE,
SaleReceipt: DRAWERS.RECEIPT_CUSTOMIZE,
CreditNote: DRAWERS.CREDIT_NOTE_CUSTOMIZE,
PaymentReceive: DRAWERS.PAYMENT_RECEIVED_CUSTOMIZE,
};
return R.prop(resource, pairs) || DRAWERS.INVOICE_CUSTOMIZE;
};
export const getButtonLabelFromResource = (resource: string) => {
const pairs = {
SaleInvoice: 'Create Invoice Branding',
SaleEstimate: 'Create Estimate Branding',
SaleReceipt: 'Create Receipt Branding',
CreditNote: 'Create Credit Note Branding',
PaymentReceive: 'Create Payment Branding',
};
return R.prop(resource, pairs) || 'Create Branding Template';
}

View File

@@ -0,0 +1,8 @@
import { useFormikContext } from 'formik';
import { BrandingTemplateValues } from './types';
export const useIsTemplateNamedFilled = () => {
const { values } = useFormikContext<BrandingTemplateValues>();
return values.templateName && values.templateName?.length >= 4;
};

View File

@@ -5,14 +5,26 @@ import { CreditNoteCustomizeContentFields } from './CreditNoteCutomizeContentFie
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 { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
import { useDrawerActions } from '@/hooks/state';
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
export function CreditNoteCustomizeContent() { export function CreditNoteCustomizeContent() {
const handleFormSubmit = (values: CreditNoteCustomizeValues) => {}; const { payload, name } = useDrawerContext();
const { closeDrawer } = useDrawerActions();
const templateId = payload?.templateId || null;
const handleSuccess = () => {
closeDrawer(name);
};
return ( return (
<ElementCustomize<CreditNoteCustomizeValues> <BrandingTemplateForm<CreditNoteCustomizeValues>
initialValues={initialValues} resource={'CreditNote'}
onSubmit={handleFormSubmit} templateId={templateId}
defaultValues={initialValues}
onSuccess={handleSuccess}
> >
<ElementCustomize.PaperTemplate> <ElementCustomize.PaperTemplate>
<CreditNotePaperTemplateFormConnected /> <CreditNotePaperTemplateFormConnected />
@@ -29,7 +41,7 @@ export function CreditNoteCustomizeContent() {
<ElementCustomize.FieldsTab id={'totals'} label={'Totals'}> <ElementCustomize.FieldsTab id={'totals'} label={'Totals'}>
asdfasdfdsaf #3 asdfasdfdsaf #3
</ElementCustomize.FieldsTab> </ElementCustomize.FieldsTab>
</ElementCustomize> </BrandingTemplateForm>
); );
} }

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 CreditNoteCustomizeContent = React.lazy( const CreditNoteCustomizeDrawerBody = React.lazy(
() => import('./CreditNoteCustomizeContent'), () => import('./CreditNoteCustomizeDrawerBody'),
); );
/** /**
@@ -16,12 +16,12 @@ function CreditNoteCustomizeDrawerRoot({
name, name,
// #withDrawer // #withDrawer
isOpen, isOpen,
payload: {}, payload,
}) { }) {
return ( return (
<Drawer isOpen={isOpen} name={name} size={'100%'}> <Drawer isOpen={isOpen} name={name} payload={payload} size={'100%'}>
<DrawerSuspense> <DrawerSuspense>
<CreditNoteCustomizeContent /> <CreditNoteCustomizeDrawerBody />
</DrawerSuspense> </DrawerSuspense>
</Drawer> </Drawer>
); );

View File

@@ -6,7 +6,7 @@ import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
export default function CreditNoteCustomizeDrawerBody() { export default function CreditNoteCustomizeDrawerBody() {
const { payload } = useDrawerContext(); const { payload } = useDrawerContext();
const templateId = payload.templateId; const templateId = payload?.templateId || null;
return ( return (
<Box className={Classes.DRAWER_BODY}> <Box className={Classes.DRAWER_BODY}>

View File

@@ -1,9 +1,19 @@
// @ts-nocheck // @ts-nocheck
import { Classes } from '@blueprintjs/core'; import { Classes } from '@blueprintjs/core';
import { FFormGroup, FSwitch, Stack } from '@/components'; import {
FFormGroup,
FieldRequiredHint,
FInputGroup,
FSwitch,
Stack,
} from '@/components';
import { FColorInput } from '@/components/Forms/FColorInput'; import { FColorInput } from '@/components/Forms/FColorInput';
import { Overlay } from '../../Invoices/InvoiceCustomize/Overlay';
import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
export function CreditNoteCustomizeGeneralField() { export function CreditNoteCustomizeGeneralField() {
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}>
@@ -14,6 +24,17 @@ export function CreditNoteCustomizeGeneralField() {
</p> </p>
</Stack> </Stack>
<FFormGroup
name={'templateName'}
label={'Template Name'}
labelInfo={<FieldRequiredHint />}
fastField
style={{ marginBottom: 10 }}
>
<FInputGroup name={'templateName'} fastField />
</FFormGroup>
<Overlay visible={!isTemplateNameFilled}>
<Stack spacing={0}> <Stack spacing={0}>
<FFormGroup <FFormGroup
name={'primaryColor'} name={'primaryColor'}
@@ -53,6 +74,7 @@ export function CreditNoteCustomizeGeneralField() {
/> />
</FFormGroup> </FFormGroup>
</Stack> </Stack>
</Overlay>
</Stack> </Stack>
); );
} }

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 CreditNoteCustomizeValues { import { BrandingTemplateValues } from '@/containers/BrandingTemplates/types';
export interface CreditNoteCustomizeValues extends BrandingTemplateValues {
// Colors // Colors
primaryColor?: string; primaryColor?: string;
secondaryColor?: string; secondaryColor?: string;

View File

@@ -17,10 +17,10 @@ function EstimateCustomizeDrawerRoot({
// #withDrawer // #withDrawer
isOpen, isOpen,
payload: {}, payload,
}) { }) {
return ( return (
<Drawer isOpen={isOpen} name={name} size={'100%'}> <Drawer isOpen={isOpen} name={name} payload={payload} size={'100%'}>
<DrawerSuspense> <DrawerSuspense>
<EstimateCustomizeDrawerBody /> <EstimateCustomizeDrawerBody />
</DrawerSuspense> </DrawerSuspense>

View File

@@ -9,9 +9,12 @@ import {
Stack, Stack,
} from '@/components'; } from '@/components';
import { FColorInput } from '@/components/Forms/FColorInput'; import { FColorInput } from '@/components/Forms/FColorInput';
// import styles from './InvoiceCustomizeFields.module.scss'; import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
import { Overlay } from '../../Invoices/InvoiceCustomize/Overlay';
export function EstimateCustomizeGeneralField() { export function EstimateCustomizeGeneralField() {
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}>
@@ -32,6 +35,7 @@ export function EstimateCustomizeGeneralField() {
<FInputGroup name={'templateName'} fastField /> <FInputGroup name={'templateName'} fastField />
</FFormGroup> </FFormGroup>
<Overlay visible={!isTemplateNameFilled}>
<Stack spacing={0}> <Stack spacing={0}>
<FFormGroup <FFormGroup
name={'primaryColor'} name={'primaryColor'}
@@ -71,6 +75,7 @@ export function EstimateCustomizeGeneralField() {
/> />
</FFormGroup> </FFormGroup>
</Stack> </Stack>
</Overlay>
</Stack> </Stack>
); );
} }

View File

@@ -11,7 +11,7 @@ import {
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 { Overlay } from './Overlay';
import { useIsTemplateNamedFilled } from './utils'; import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
export function InvoiceCustomizeGeneralField() { export function InvoiceCustomizeGeneralField() {
const isTemplateNameFilled = useIsTemplateNamedFilled(); const isTemplateNameFilled = useIsTemplateNamedFilled();

View File

@@ -28,12 +28,6 @@ export const transformToNewRequest = (
}; };
}; };
export const useIsTemplateNamedFilled = () => {
const { values } = useFormikContext<InvoiceCustomizeValues>();
return values.templateName && values.templateName?.length >= 4;
};
export const useInvoiceCustomizeInitialValues = (): InvoiceCustomizeValues => { export const useInvoiceCustomizeInitialValues = (): InvoiceCustomizeValues => {
const { pdfTemplate } = useBrandingTemplateBoot(); const { pdfTemplate } = useBrandingTemplateBoot();

View File

@@ -1,10 +1,19 @@
// @ts-nocheck // @ts-nocheck
import { Classes } from '@blueprintjs/core'; import { Classes } from '@blueprintjs/core';
import { FFormGroup, FSwitch, Stack } from '@/components'; import {
FFormGroup,
FieldRequiredHint,
FInputGroup,
FSwitch,
Stack,
} from '@/components';
import { FColorInput } from '@/components/Forms/FColorInput'; import { FColorInput } from '@/components/Forms/FColorInput';
// import styles from './InvoiceCustomizeFields.module.scss'; import { Overlay } from '../../Invoices/InvoiceCustomize/Overlay';
import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
export function PaymentReceivedCustomizeGeneralField() { export function PaymentReceivedCustomizeGeneralField() {
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}>
@@ -15,6 +24,17 @@ export function PaymentReceivedCustomizeGeneralField() {
</p> </p>
</Stack> </Stack>
<FFormGroup
name={'templateName'}
label={'Template Name'}
labelInfo={<FieldRequiredHint />}
style={{ marginBottom: 10 }}
fastField
>
<FInputGroup name={'templateName'} fastField />
</FFormGroup>
<Overlay visible={!isTemplateNameFilled}>
<Stack spacing={0}> <Stack spacing={0}>
<FFormGroup <FFormGroup
name={'primaryColor'} name={'primaryColor'}
@@ -54,6 +74,7 @@ export function PaymentReceivedCustomizeGeneralField() {
/> />
</FFormGroup> </FFormGroup>
</Stack> </Stack>
</Overlay>
</Stack> </Stack>
); );
} }

View File

@@ -1,9 +1,19 @@
// @ts-nocheck // @ts-nocheck
import { Classes } from '@blueprintjs/core'; import { Classes } from '@blueprintjs/core';
import { FFormGroup, FSwitch, Stack } from '@/components'; import {
FFormGroup,
FieldRequiredHint,
FInputGroup,
FSwitch,
Stack,
} from '@/components';
import { FColorInput } from '@/components/Forms/FColorInput'; import { FColorInput } from '@/components/Forms/FColorInput';
import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
import { Overlay } from '../../Invoices/InvoiceCustomize/Overlay';
export function ReceiptCustomizeGeneralField() { export function ReceiptCustomizeGeneralField() {
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}>
@@ -14,6 +24,17 @@ export function ReceiptCustomizeGeneralField() {
</p> </p>
</Stack> </Stack>
<FFormGroup
name={'templateName'}
label={'Template Name'}
labelInfo={<FieldRequiredHint />}
fastField
style={{ marginBottom: 10 }}
>
<FInputGroup name={'templateName'} fastField />
</FFormGroup>
<Overlay visible={!isTemplateNameFilled}>
<Stack spacing={0}> <Stack spacing={0}>
<FFormGroup <FFormGroup
name={'primaryColor'} name={'primaryColor'}
@@ -53,6 +74,7 @@ export function ReceiptCustomizeGeneralField() {
/> />
</FFormGroup> </FFormGroup>
</Stack> </Stack>
</Overlay>
</Stack> </Stack>
); );
} }