feat: rendering pdf templates on the server-side

This commit is contained in:
Ahmed Bouhuolia
2024-09-17 13:53:57 +02:00
parent 4f59b27d70
commit 2c790427fa
44 changed files with 1833 additions and 363 deletions

View File

@@ -12,10 +12,15 @@ import {
Menu,
MenuItem,
} from '@blueprintjs/core';
import { If, Icon, FormattedMessage as T, Group } from '@/components';
import { If, Icon, FormattedMessage as T, Group, FSelect } from '@/components';
import { CLASSES } from '@/constants/classes';
import classNames from 'classnames';
import { useCreditNoteFormContext } from './CreditNoteFormProvider';
import {
BrandingThemeFormGroup,
BrandingThemeSelectButton,
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
import { useCreditNoteFormBrandingTemplatesOptions } from './utils';
/**
* Credit note floating actions.
@@ -74,6 +79,8 @@ export default function CreditNoteFloatingActions() {
resetForm();
};
const brandingTemplatesOptions = useCreditNoteFormBrandingTemplatesOptions();
return (
<Group
spacing={10}
@@ -190,6 +197,25 @@ export default function CreditNoteFloatingActions() {
onClick={handleCancelBtnClick}
text={<T id={'cancel'} />}
/>
{/* ----------- Branding Template Select ----------- */}
<BrandingThemeFormGroup
name={'pdf_template_id'}
label={'Branding'}
inline
fastField
style={{ marginLeft: 20 }}
>
<FSelect
name={'pdf_template_id'}
items={brandingTemplatesOptions}
input={({ activeItem, text, label, value }) => (
<BrandingThemeSelectButton text={text || 'Brand Theme'} minimal />
)}
filterable={false}
popoverProps={{ minimal: true }}
/>
</BrandingThemeFormGroup>
</Group>
);
}

View File

@@ -18,6 +18,7 @@ import {
useSettingsCreditNotes,
useInvoice,
} from '@/hooks/query';
import { useGetPdfTemplates } from '@/hooks/query/pdf-templates';
const CreditNoteFormContext = React.createContext();
@@ -73,6 +74,10 @@ function CreditNoteFormProvider({ creditNoteId, ...props }) {
isSuccess: isBranchesSuccess,
} = useBranches({}, { enabled: isBranchFeatureCan });
// Fetches branding templates of invoice.
const { data: brandingTemplates, isLoading: isBrandingTemplatesLoading } =
useGetPdfTemplates({ resource: 'PaymentReceive' });
// Handle fetching settings.
useSettingsCreditNotes();
@@ -115,13 +120,18 @@ function CreditNoteFormProvider({ creditNoteId, ...props }) {
createCreditNoteMutate,
editCreditNoteMutate,
setSubmitPayload,
// Branding templates.
brandingTemplates,
isBrandingTemplatesLoading,
};
const isLoading =
isItemsLoading ||
isCustomersLoading ||
isCreditNoteLoading ||
isInvoiceLoading;
isInvoiceLoading ||
isBrandingTemplatesLoading;
return (
<DashboardInsider loading={isLoading} name={'credit-note-form'}>

View File

@@ -24,6 +24,7 @@ import {
transformAttachmentsToForm,
transformAttachmentsToRequest,
} from '@/containers/Attachments/utils';
import { convertBrandingTemplatesToOptions } from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
export const MIN_LINES_NUMBER = 1;
@@ -54,7 +55,8 @@ export const defaultCreditNote = {
exchange_rate: 1,
currency_code: '',
entries: [...repeatValue(defaultCreditNoteEntry, MIN_LINES_NUMBER)],
attachments: []
attachments: [],
pdf_template_id: '',
};
/**
@@ -214,3 +216,13 @@ export const useCreditNoteIsForeignCustomer = () => {
);
return isForeignCustomer;
};
export const useCreditNoteFormBrandingTemplatesOptions = () => {
const { brandingTemplates } = useCreditNoteFormContext();
return React.useMemo(
() => convertBrandingTemplatesToOptions(brandingTemplates),
[brandingTemplates],
);
};

View File

@@ -11,11 +11,16 @@ import {
Menu,
MenuItem,
} from '@blueprintjs/core';
import { If, Icon, FormattedMessage as T, Group } from '@/components';
import { If, Icon, FormattedMessage as T, Group, FSelect } from '@/components';
import { CLASSES } from '@/constants/classes';
import { useHistory } from 'react-router-dom';
import { useFormikContext } from 'formik';
import { useEstimateFormContext } from './EstimateFormProvider';
import { useEstimateFormBrandingTemplatesOptions } from './utils';
import {
BrandingThemeFormGroup,
BrandingThemeSelectButton,
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
/**
* Estimate floating actions bar.
@@ -73,6 +78,8 @@ export default function EstimateFloatingActions() {
resetForm();
};
const brandingTemplatesOptions = useEstimateFormBrandingTemplatesOptions();
return (
<Group
spacing={10}
@@ -193,6 +200,25 @@ export default function EstimateFloatingActions() {
onClick={handleCancelBtnClick}
text={<T id={'cancel'} />}
/>
{/* ----------- Branding Template Select ----------- */}
<BrandingThemeFormGroup
name={'pdf_template_id'}
label={'Branding'}
inline
fastField
style={{ marginLeft: 20 }}
>
<FSelect
name={'pdf_template_id'}
items={brandingTemplatesOptions}
input={({ activeItem, text, label, value }) => (
<BrandingThemeSelectButton text={text || 'Brand Theme'} minimal />
)}
filterable={false}
popoverProps={{ minimal: true }}
/>
</BrandingThemeFormGroup>
</Group>
);
}

View File

@@ -12,8 +12,9 @@ import {
useCreateEstimate,
useEditEstimate,
} from '@/hooks/query';
import { Features } from '@/constants';
import { useProjects } from '@/containers/Projects/hooks';
import { useGetPdfTemplates } from '@/hooks/query/pdf-templates';
import { Features } from '@/constants';
import { useFeatureCan } from '@/hooks/state';
import { ITEMS_FILTER_ROLES } from './utils';
@@ -71,6 +72,10 @@ function EstimateFormProvider({ query, estimateId, ...props }) {
isLoading: isProjectsLoading,
} = useProjects({}, { enabled: !!isProjectsFeatureCan });
// Fetches branding templates of invoice.
const { data: brandingTemplates, isLoading: isBrandingTemplatesLoading } =
useGetPdfTemplates({ resource: 'SaleEstimate' });
// Handle fetch settings.
useSettingsEstimates();
@@ -112,13 +117,19 @@ function EstimateFormProvider({ query, estimateId, ...props }) {
createEstimateMutate,
editEstimateMutate,
brandingTemplates,
isBrandingTemplatesLoading,
};
const isLoading =
isCustomersLoading ||
isItemsLoading ||
isEstimateLoading ||
isBrandingTemplatesLoading;
return (
<DashboardInsider
loading={isCustomersLoading || isItemsLoading || isEstimateLoading}
name={'estimate-form'}
>
<DashboardInsider loading={isLoading} name={'estimate-form'}>
<EstimateFormContext.Provider value={provider} {...props} />
</DashboardInsider>
);

View File

@@ -22,6 +22,7 @@ import {
transformAttachmentsToForm,
transformAttachmentsToRequest,
} from '@/containers/Attachments/utils';
import { convertBrandingTemplatesToOptions } from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
export const MIN_LINES_NUMBER = 1;
@@ -60,7 +61,8 @@ export const defaultEstimate = {
exchange_rate: 1,
currency_code: '',
entries: [...repeatValue(defaultEstimateEntry, MIN_LINES_NUMBER)],
attachments: []
attachments: [],
pdf_template_id: '',
};
const ERRORS = {
@@ -262,3 +264,12 @@ export const resetFormState = ({ initialValues, values, resetForm }) => {
},
});
};
export const useEstimateFormBrandingTemplatesOptions = () => {
const { brandingTemplates } = useEstimateFormContext();
return React.useMemo(
() => convertBrandingTemplatesToOptions(brandingTemplates),
[brandingTemplates],
);
};

View File

@@ -25,6 +25,7 @@ import {
DashboardFilterButton,
DashboardRowsHeightButton,
DashboardActionsBar,
FSelect,
} from '@/components';
import withEstimates from './withEstimates';
@@ -42,6 +43,10 @@ import { compose } from '@/utils';
import { DialogsName } from '@/constants/dialogs';
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
import { DRAWERS } from '@/constants/drawers';
import {
BrandingThemeFormGroup,
BrandingThemeSelectButton,
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
/**
* Estimates list actions bar.

View File

@@ -12,10 +12,15 @@ import {
MenuItem,
} from '@blueprintjs/core';
import { useHistory } from 'react-router-dom';
import { Group, Icon, FormattedMessage as T } from '@/components';
import { FSelect, Group, Icon, FormattedMessage as T } from '@/components';
import { useFormikContext } from 'formik';
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
import { CLASSES } from '@/constants/classes';
import {
BrandingThemeFormGroup,
BrandingThemeSelectButton,
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
import { usePaymentReceivedFormBrandingTemplatesOptions } from './utils';
/**
* Payment receive floating actions bar.
@@ -53,6 +58,9 @@ export default function PaymentReceiveFormFloatingActions() {
submitForm();
};
const brandingTemplatesOpts =
usePaymentReceivedFormBrandingTemplatesOptions();
return (
<Group
spacing={10}
@@ -109,6 +117,25 @@ export default function PaymentReceiveFormFloatingActions() {
onClick={handleCancelBtnClick}
text={<T id={'cancel'} />}
/>
{/* ----------- Branding Template Select ----------- */}
<BrandingThemeFormGroup
name={'pdf_template_id'}
label={'Branding'}
inline
fastField
style={{ marginLeft: 20 }}
>
<FSelect
name={'pdf_template_id'}
items={brandingTemplatesOpts}
input={({ activeItem, text, label, value }) => (
<BrandingThemeSelectButton text={text || 'Brand Theme'} minimal />
)}
filterable={false}
popoverProps={{ minimal: true }}
/>
</BrandingThemeFormGroup>
</Group>
);
}

View File

@@ -13,6 +13,7 @@ import {
useCreatePaymentReceive,
useEditPaymentReceive,
} from '@/hooks/query';
import { useGetPdfTemplates } from '@/hooks/query/pdf-templates';
// Payment receive form context.
const PaymentReceiveFormContext = createContext();
@@ -65,6 +66,10 @@ function PaymentReceiveFormProvider({ query, paymentReceiveId, ...props }) {
isLoading: isProjectsLoading,
} = useProjects({}, { enabled: !!isProjectsFeatureCan });
// Fetches branding templates of payment received module.
const { data: brandingTemplates, isLoading: isBrandingTemplatesLoading } =
useGetPdfTemplates({ resource: 'PaymentReceive' });
// Detarmines whether the new mode.
const isNewMode = !paymentReceiveId;
@@ -102,13 +107,20 @@ function PaymentReceiveFormProvider({ query, paymentReceiveId, ...props }) {
isExcessConfirmed,
setIsExcessConfirmed,
// Branding templates
brandingTemplates,
isBrandingTemplatesLoading,
};
const isLoading =
isPaymentLoading ||
isAccountsLoading ||
isCustomersLoading ||
isBrandingTemplatesLoading;
return (
<DashboardInsider
loading={isPaymentLoading || isAccountsLoading || isCustomersLoading}
name={'payment-receive-form'}
>
<DashboardInsider loading={isLoading} name={'payment-receive-form'}>
<PaymentReceiveFormContext.Provider value={provider} {...props} />
</DashboardInsider>
);

View File

@@ -19,6 +19,7 @@ import {
transformAttachmentsToForm,
transformAttachmentsToRequest,
} from '@/containers/Attachments/utils';
import { convertBrandingTemplatesToOptions } from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
// Default payment receive entry.
export const defaultPaymentReceiveEntry = {
@@ -44,10 +45,11 @@ export const defaultPaymentReceive = {
statement: '',
amount: '',
currency_code: '',
branch_id: '',
exchange_rate: 1,
entries: [],
attachments: [],
branch_id: '',
pdf_template_id: '',
};
export const defaultRequestPaymentEntry = {
@@ -303,3 +305,12 @@ export const getExceededAmountFromValues = (values) => {
return totalAmount - totalApplied;
};
export const usePaymentReceivedFormBrandingTemplatesOptions = () => {
const { brandingTemplates } = usePaymentReceiveFormContext();
return React.useMemo(
() => convertBrandingTemplatesToOptions(brandingTemplates),
[brandingTemplates],
);
};

View File

@@ -11,12 +11,17 @@ import {
Menu,
MenuItem,
} from '@blueprintjs/core';
import { Group, FormattedMessage as T } from '@/components';
import { FSelect, Group, FormattedMessage as T } from '@/components';
import { useFormikContext } from 'formik';
import { useHistory } from 'react-router-dom';
import { CLASSES } from '@/constants/classes';
import { If, Icon } from '@/components';
import { useReceiptFormContext } from './ReceiptFormProvider';
import { useReceiptFormBrandingTemplatesOptions } from './utils';
import {
BrandingThemeFormGroup,
BrandingThemeSelectButton,
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
/**
* Receipt floating actions bar.
@@ -76,6 +81,8 @@ export default function ReceiptFormFloatingActions() {
resetForm();
};
const brandingTemplatesOptions = useReceiptFormBrandingTemplatesOptions();
return (
<Group
spacing={10}
@@ -191,6 +198,25 @@ export default function ReceiptFormFloatingActions() {
onClick={handleCancelBtnClick}
text={<T id={'cancel'} />}
/>
{/* ----------- Branding Template Select ----------- */}
<BrandingThemeFormGroup
name={'pdf_template_id'}
label={'Branding'}
inline
fastField
style={{ marginLeft: 20 }}
>
<FSelect
name={'pdf_template_id'}
items={brandingTemplatesOptions}
input={({ activeItem, text, label, value }) => (
<BrandingThemeSelectButton text={text || 'Brand Theme'} minimal />
)}
filterable={false}
popoverProps={{ minimal: true }}
/>
</BrandingThemeFormGroup>
</Group>
);
}

View File

@@ -15,6 +15,7 @@ import {
useEditReceipt,
} from '@/hooks/query';
import { useProjects } from '@/containers/Projects/hooks';
import { useGetPdfTemplates } from '@/hooks/query/pdf-templates';
const ReceiptFormContext = createContext();
@@ -77,7 +78,7 @@ function ReceiptFormProvider({ receiptId, ...props }) {
[],
);
// Handle fetch Items data table or list
// Handle fetch Items data table or list.
const {
data: { items },
isLoading: isItemsLoading,
@@ -85,13 +86,16 @@ function ReceiptFormProvider({ receiptId, ...props }) {
page_size: 10000,
stringified_filter_roles: stringifiedFilterRoles,
});
// Fetch project list.
const {
data: { projects },
isLoading: isProjectsLoading,
} = useProjects({}, { enabled: !!isProjectsFeatureCan });
// Fetches branding templates of receipt.
const { data: brandingTemplates, isLoading: isBrandingTemplatesLoading } =
useGetPdfTemplates({ resource: 'SaleReceipt' });
// Fetch receipt settings.
const { isLoading: isSettingLoading } = useSettingsReceipts();
@@ -101,7 +105,6 @@ function ReceiptFormProvider({ receiptId, ...props }) {
const [submitPayload, setSubmitPayload] = useState({});
const isNewMode = !receiptId;
const isFeatureLoading = isWarehouesLoading || isBranchesLoading;
const provider = {
@@ -130,6 +133,10 @@ function ReceiptFormProvider({ receiptId, ...props }) {
createReceiptMutate,
editReceiptMutate,
setSubmitPayload,
// Branding templates
brandingTemplates,
isBrandingTemplatesLoading
};
return (
<DashboardInsider

View File

@@ -22,6 +22,7 @@ import {
transformAttachmentsToForm,
transformAttachmentsToRequest,
} from '@/containers/Attachments/utils';
import { convertBrandingTemplatesToOptions } from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
export const MIN_LINES_NUMBER = 1;
@@ -61,6 +62,7 @@ export const defaultReceipt = {
currency_code: '',
entries: [...repeatValue(defaultReceiptEntry, MIN_LINES_NUMBER)],
attachments: [],
pdf_template_id: '',
};
const ERRORS = {
@@ -272,3 +274,12 @@ export const resetFormState = ({ initialValues, values, resetForm }) => {
},
});
};
export const useReceiptFormBrandingTemplatesOptions = () => {
const { brandingTemplates } = useReceiptFormContext();
return React.useMemo(
() => convertBrandingTemplatesToOptions(brandingTemplates),
[brandingTemplates],
);
};