mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 05:40:31 +00:00
Merge pull request #735 from bigcapitalhq/add-pdf-templates-package
feat: add shared package to pdf templates to render in the server and…
This commit is contained in:
@@ -20,10 +20,11 @@
|
||||
"bigcapital": "./bin/bigcapital.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.576.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.583.0",
|
||||
"@bigcapital/utils": "*",
|
||||
"@bigcapital/email-components": "*",
|
||||
"@bigcapital/pdf-templates": "*",
|
||||
"@aws-sdk/client-s3": "^3.576.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.583.0",
|
||||
"@casl/ability": "^5.4.3",
|
||||
"@hapi/boom": "^7.4.3",
|
||||
"@lemonsqueezy/lemonsqueezy.js": "^2.2.0",
|
||||
|
||||
@@ -450,8 +450,8 @@ export default class SaleInvoicesController extends BaseController {
|
||||
ACCEPT_TYPE.APPLICATION_JSON,
|
||||
ACCEPT_TYPE.APPLICATION_PDF,
|
||||
]);
|
||||
// Retrieves invoice in pdf format.
|
||||
if (ACCEPT_TYPE.APPLICATION_PDF == acceptType) {
|
||||
// Retrieves invoice in PDF format.
|
||||
if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||
const [pdfContent, filename] =
|
||||
await this.saleInvoiceApplication.saleInvoicePdf(
|
||||
tenantId,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { renderInvoicePaperTemplateHtml } from '@bigcapital/pdf-templates';
|
||||
import { ChromiumlyTenancy } from '@/services/ChromiumlyTenancy/ChromiumlyTenancy';
|
||||
import { TemplateInjectable } from '@/services/TemplateInjectable/TemplateInjectable';
|
||||
import { GetSaleInvoice } from './GetSaleInvoice';
|
||||
@@ -8,6 +9,7 @@ import { InvoicePdfTemplateAttributes } from '@/interfaces';
|
||||
import { SaleInvoicePdfTemplate } from './SaleInvoicePdfTemplate';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import events from '@/subscribers/events';
|
||||
import { renderInvoicePaymentEmail } from '@bigcapital/email-components';
|
||||
|
||||
@Service()
|
||||
export class SaleInvoicePdf {
|
||||
@@ -45,11 +47,9 @@ export class SaleInvoicePdf {
|
||||
tenantId,
|
||||
invoiceId
|
||||
);
|
||||
const htmlContent = await this.templateInjectable.render(
|
||||
tenantId,
|
||||
'modules/invoice-standard',
|
||||
brandingAttributes
|
||||
);
|
||||
const htmlContent = renderInvoicePaperTemplateHtml({
|
||||
...brandingAttributes,
|
||||
});
|
||||
// Converts the given html content to pdf document.
|
||||
const buffer = await this.chromiumlyTenancy.convertHtmlContent(
|
||||
tenantId,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@bigcapital/utils": "*",
|
||||
"@bigcapital/pdf-templates": "*",
|
||||
"@blueprintjs-formik/core": "^0.3.7",
|
||||
"@blueprintjs-formik/datetime": "^0.3.7",
|
||||
"@blueprintjs-formik/select": "^0.3.5",
|
||||
|
||||
1077
pnpm-lock.yaml
generated
1077
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
24
shared/pdf-templates/.gitignore
vendored
Normal file
24
shared/pdf-templates/.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
23
shared/pdf-templates/.storybook/main.tsx
Normal file
23
shared/pdf-templates/.storybook/main.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { StorybookConfig } from '@storybook/react-vite';
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],
|
||||
addons: [
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-essentials',
|
||||
'@storybook/addon-interactions',
|
||||
'@storybook/addon-styling',
|
||||
],
|
||||
framework: {
|
||||
name: '@storybook/react-vite',
|
||||
options: {
|
||||
builder: {
|
||||
viteConfigPath: '.storybook/vite.config.ts',
|
||||
},
|
||||
},
|
||||
},
|
||||
docs: {
|
||||
autodocs: 'tag',
|
||||
},
|
||||
};
|
||||
export default config;
|
||||
15
shared/pdf-templates/.storybook/preview.tsx
Normal file
15
shared/pdf-templates/.storybook/preview.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import type { Preview } from '@storybook/react';
|
||||
|
||||
const preview: Preview = {
|
||||
parameters: {
|
||||
actions: { argTypesRegex: '^on[A-Z].*' },
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default preview;
|
||||
17
shared/pdf-templates/.storybook/vite.config.ts
Normal file
17
shared/pdf-templates/.storybook/vite.config.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
import tailwindcss from 'tailwindcss';
|
||||
import { UserConfigExport } from 'vite';
|
||||
|
||||
const app = async (): Promise<UserConfigExport> => {
|
||||
return defineConfig({
|
||||
plugins: [react()],
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: [tailwindcss],
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
// https://vitejs.dev/config/
|
||||
export default app;
|
||||
28
shared/pdf-templates/eslint.config.js
Normal file
28
shared/pdf-templates/eslint.config.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import reactHooks from 'eslint-plugin-react-hooks'
|
||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||
import tseslint from 'typescript-eslint'
|
||||
|
||||
export default tseslint.config(
|
||||
{ ignores: ['dist'] },
|
||||
{
|
||||
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
},
|
||||
plugins: {
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
rules: {
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
||||
62
shared/pdf-templates/package.json
Normal file
62
shared/pdf-templates/package.json
Normal file
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"name": "@bigcapital/pdf-templates",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"build": "webpack --config webpack.config.js",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview",
|
||||
"storybook:dev": "storybook dev -p 6006",
|
||||
"storybook:build": "storybook build"
|
||||
},
|
||||
"main": "./dist/components.umd.js",
|
||||
"module": "./dist/components.es.js",
|
||||
"types": "./dist/src/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/src/index.d.ts",
|
||||
"import": "./dist/components.es.js",
|
||||
"require": "./dist/components.umd.js"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/cache": "^11.13.1",
|
||||
"@emotion/css": "^11.13.4",
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/server": "^11.11.0",
|
||||
"@types/lodash": "^4.17.13",
|
||||
"@xstyled/emotion": "^3.8.1",
|
||||
"@xstyled/system": "^3.8.1",
|
||||
"classnames": "^2.3.2",
|
||||
"css-loader": "^6.x",
|
||||
"declaration-bundler-webpack-plugin": "^1.0.3",
|
||||
"fork-ts-checker-webpack-plugin": "^9.0.2",
|
||||
"lodash": "^4.17.15",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"style-loader": "^3.x",
|
||||
"tailwindcss": "^3.4.14",
|
||||
"ts-loader": "^9.x",
|
||||
"webpack": "^5.x",
|
||||
"webpack-cli": "^5.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.13.0",
|
||||
"@storybook/addon-essentials": "7.2.2",
|
||||
"@storybook/addon-interactions": "7.2.2",
|
||||
"@storybook/addon-links": "7.2.2",
|
||||
"@storybook/addon-styling": "1.3.6",
|
||||
"@storybook/blocks": "7.2.2",
|
||||
"@storybook/react": "7.2.2",
|
||||
"@storybook/testing-library": "0.2.0",
|
||||
"@types/react": "18.3.4",
|
||||
"@types/react-dom": "18.3.0",
|
||||
"eslint": "^9.13.0",
|
||||
"eslint-plugin-react-hooks": "^5.0.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.13",
|
||||
"eslint-plugin-storybook": "0.6.13",
|
||||
"globals": "^15.11.0",
|
||||
"storybook": "7.2.2",
|
||||
"typescript": "~5.6.2",
|
||||
"typescript-eslint": "^8.10.0"
|
||||
}
|
||||
}
|
||||
6
shared/pdf-templates/postman.config.js
Normal file
6
shared/pdf-templates/postman.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
326
shared/pdf-templates/src/components/InvoicePaperTemplate.tsx
Normal file
326
shared/pdf-templates/src/components/InvoicePaperTemplate.tsx
Normal file
@@ -0,0 +1,326 @@
|
||||
import {
|
||||
PaperTemplate,
|
||||
PaperTemplateProps,
|
||||
PaperTemplateTotalBorder,
|
||||
} from './PaperTemplate';
|
||||
import { Box } from '../lib/layout/Box';
|
||||
import { Text } from '../lib/text/Text';
|
||||
import { Stack } from '../lib/layout/Stack';
|
||||
import { Group } from '../lib/layout/Group';
|
||||
import {
|
||||
DefaultPdfTemplateTerms,
|
||||
DefaultPdfTemplateItemDescription,
|
||||
DefaultPdfTemplateStatement,
|
||||
DefaultPdfTemplateItemName,
|
||||
DefaultPdfTemplateAddressBilledTo,
|
||||
DefaultPdfTemplateAddressBilledFrom,
|
||||
} from './_constants';
|
||||
|
||||
interface PapaerLine {
|
||||
item?: string;
|
||||
description?: string;
|
||||
quantity?: string;
|
||||
rate?: string;
|
||||
total?: string;
|
||||
}
|
||||
|
||||
interface PaperTax {
|
||||
label: string;
|
||||
amount: string;
|
||||
}
|
||||
|
||||
export interface InvoicePaperTemplateProps extends PaperTemplateProps {
|
||||
primaryColor?: string;
|
||||
secondaryColor?: string;
|
||||
|
||||
showCompanyLogo?: boolean;
|
||||
companyLogoUri?: string;
|
||||
|
||||
showInvoiceNumber?: boolean;
|
||||
invoiceNumber?: string;
|
||||
invoiceNumberLabel?: string;
|
||||
|
||||
showDateIssue?: boolean;
|
||||
dateIssue?: string;
|
||||
dateIssueLabel?: string;
|
||||
|
||||
showDueDate?: boolean;
|
||||
dueDate?: string;
|
||||
dueDateLabel?: string;
|
||||
|
||||
companyName?: string;
|
||||
bigtitle?: string;
|
||||
|
||||
// Address
|
||||
showCustomerAddress?: boolean;
|
||||
customerAddress?: string;
|
||||
|
||||
showCompanyAddress?: boolean;
|
||||
companyAddress?: string;
|
||||
|
||||
billedToLabel?: string;
|
||||
|
||||
// Entries
|
||||
lineItemLabel?: string;
|
||||
lineQuantityLabel?: string;
|
||||
lineRateLabel?: string;
|
||||
lineTotalLabel?: string;
|
||||
|
||||
// Totals
|
||||
showTotal?: boolean;
|
||||
totalLabel?: string;
|
||||
total?: string;
|
||||
|
||||
showDiscount?: boolean;
|
||||
discountLabel?: string;
|
||||
discount?: string;
|
||||
|
||||
showSubtotal?: boolean;
|
||||
subtotalLabel?: string;
|
||||
subtotal?: string;
|
||||
|
||||
showPaymentMade?: boolean;
|
||||
paymentMadeLabel?: string;
|
||||
paymentMade?: string;
|
||||
|
||||
showTaxes?: boolean;
|
||||
|
||||
showDueAmount?: boolean;
|
||||
showBalanceDue?: boolean;
|
||||
balanceDueLabel?: string;
|
||||
balanceDue?: string;
|
||||
|
||||
// Footer
|
||||
termsConditionsLabel?: string;
|
||||
showTermsConditions?: boolean;
|
||||
termsConditions?: string;
|
||||
|
||||
statementLabel?: string;
|
||||
showStatement?: boolean;
|
||||
statement?: string;
|
||||
|
||||
lines?: Array<PapaerLine>;
|
||||
taxes?: Array<PaperTax>;
|
||||
}
|
||||
|
||||
export function InvoicePaperTemplate({
|
||||
primaryColor,
|
||||
secondaryColor,
|
||||
|
||||
companyName = 'Bigcapital Technology, Inc.',
|
||||
|
||||
showCompanyLogo = true,
|
||||
companyLogoUri = '',
|
||||
|
||||
dueDate = 'September 3, 2024',
|
||||
dueDateLabel = 'Date due',
|
||||
showDueDate = true,
|
||||
|
||||
dateIssue = 'September 3, 2024',
|
||||
dateIssueLabel = 'Date of issue',
|
||||
showDateIssue = true,
|
||||
|
||||
// dateIssue,
|
||||
invoiceNumberLabel = 'Invoice number',
|
||||
invoiceNumber = '346D3D40-0001',
|
||||
showInvoiceNumber = true,
|
||||
|
||||
// Address
|
||||
showCustomerAddress = true,
|
||||
customerAddress = DefaultPdfTemplateAddressBilledTo,
|
||||
|
||||
showCompanyAddress = true,
|
||||
companyAddress = DefaultPdfTemplateAddressBilledFrom,
|
||||
|
||||
billedToLabel = 'Billed To',
|
||||
|
||||
// Entries
|
||||
lineItemLabel = 'Item',
|
||||
lineQuantityLabel = 'Qty',
|
||||
lineRateLabel = 'Rate',
|
||||
lineTotalLabel = 'Total',
|
||||
|
||||
totalLabel = 'Total',
|
||||
subtotalLabel = 'Subtotal',
|
||||
discountLabel = 'Discount',
|
||||
paymentMadeLabel = 'Payment Made',
|
||||
balanceDueLabel = 'Balance Due',
|
||||
|
||||
// Totals
|
||||
showTotal = true,
|
||||
showSubtotal = true,
|
||||
showDiscount = true,
|
||||
showTaxes = true,
|
||||
showPaymentMade = true,
|
||||
showDueAmount = true,
|
||||
showBalanceDue = true,
|
||||
|
||||
total = '$662.75',
|
||||
subtotal = '630.00',
|
||||
discount = '0.00',
|
||||
paymentMade = '100.00',
|
||||
balanceDue = '$562.75',
|
||||
|
||||
// Footer paragraphs.
|
||||
termsConditionsLabel = 'Terms & Conditions',
|
||||
showTermsConditions = true,
|
||||
termsConditions = DefaultPdfTemplateTerms,
|
||||
|
||||
lines = [
|
||||
{
|
||||
item: DefaultPdfTemplateItemName,
|
||||
description: DefaultPdfTemplateItemDescription,
|
||||
rate: '1',
|
||||
quantity: '1000',
|
||||
total: '$1000.00',
|
||||
},
|
||||
],
|
||||
taxes = [
|
||||
{ label: 'Sample Tax1 (4.70%)', amount: '11.75' },
|
||||
{ label: 'Sample Tax2 (7.00%)', amount: '21.74' },
|
||||
],
|
||||
|
||||
statementLabel = 'Statement',
|
||||
showStatement = true,
|
||||
statement = DefaultPdfTemplateStatement,
|
||||
...props
|
||||
}: InvoicePaperTemplateProps) {
|
||||
return (
|
||||
<PaperTemplate
|
||||
primaryColor={primaryColor}
|
||||
secondaryColor={secondaryColor}
|
||||
{...props}
|
||||
>
|
||||
<Stack spacing={24}>
|
||||
<Group align="start" spacing={10}>
|
||||
<Stack flex={1}>
|
||||
<PaperTemplate.BigTitle title={'Invoice'} />
|
||||
|
||||
<PaperTemplate.TermsList>
|
||||
{showInvoiceNumber && (
|
||||
<PaperTemplate.TermsItem label={invoiceNumberLabel}>
|
||||
{invoiceNumber}
|
||||
</PaperTemplate.TermsItem>
|
||||
)}
|
||||
{showDateIssue && (
|
||||
<PaperTemplate.TermsItem label={dateIssueLabel}>
|
||||
{dateIssue}
|
||||
</PaperTemplate.TermsItem>
|
||||
)}
|
||||
{showDueDate && (
|
||||
<PaperTemplate.TermsItem label={dueDateLabel}>
|
||||
{dueDate}
|
||||
</PaperTemplate.TermsItem>
|
||||
)}
|
||||
</PaperTemplate.TermsList>
|
||||
</Stack>
|
||||
|
||||
{companyLogoUri && showCompanyLogo && (
|
||||
<PaperTemplate.Logo logoUri={companyLogoUri} />
|
||||
)}
|
||||
</Group>
|
||||
|
||||
<PaperTemplate.AddressesGroup>
|
||||
{showCompanyAddress && (
|
||||
<PaperTemplate.Address>
|
||||
<Box dangerouslySetInnerHTML={{ __html: companyAddress }} />
|
||||
</PaperTemplate.Address>
|
||||
)}
|
||||
{showCustomerAddress && (
|
||||
<PaperTemplate.Address>
|
||||
<strong>{billedToLabel}</strong>
|
||||
<Box dangerouslySetInnerHTML={{ __html: customerAddress }} />
|
||||
</PaperTemplate.Address>
|
||||
)}
|
||||
</PaperTemplate.AddressesGroup>
|
||||
|
||||
<Stack spacing={0}>
|
||||
<PaperTemplate.Table
|
||||
columns={[
|
||||
{
|
||||
label: lineItemLabel,
|
||||
accessor: (data) => (
|
||||
<Stack spacing={2}>
|
||||
<Text>{data.item}</Text>
|
||||
<Text
|
||||
color={'#5f6b7c'}
|
||||
fontSize={12}
|
||||
>
|
||||
{data.description}
|
||||
</Text>
|
||||
</Stack>
|
||||
),
|
||||
},
|
||||
{ label: lineQuantityLabel, accessor: 'quantity', align: 'right' },
|
||||
{ label: lineRateLabel, accessor: 'rate', align: 'right' },
|
||||
{ label: lineTotalLabel, accessor: 'total', align: 'right' },
|
||||
]}
|
||||
data={lines}
|
||||
/>
|
||||
<PaperTemplate.Totals>
|
||||
{showSubtotal && (
|
||||
<PaperTemplate.TotalLine
|
||||
label={subtotalLabel}
|
||||
amount={subtotal}
|
||||
border={PaperTemplateTotalBorder.Gray}
|
||||
/>
|
||||
)}
|
||||
{showDiscount && (
|
||||
<PaperTemplate.TotalLine
|
||||
label={discountLabel}
|
||||
amount={discount}
|
||||
/>
|
||||
)}
|
||||
{showTaxes && (
|
||||
<>
|
||||
{taxes.map((tax, index) => (
|
||||
<PaperTemplate.TotalLine
|
||||
key={index}
|
||||
label={tax.label}
|
||||
amount={tax.amount}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{showTotal && (
|
||||
<PaperTemplate.TotalLine
|
||||
label={totalLabel}
|
||||
amount={total}
|
||||
border={PaperTemplateTotalBorder.Dark}
|
||||
style={{ fontWeight: 500 }}
|
||||
/>
|
||||
)}
|
||||
{showPaymentMade && (
|
||||
<PaperTemplate.TotalLine
|
||||
label={paymentMadeLabel}
|
||||
amount={paymentMade}
|
||||
/>
|
||||
)}
|
||||
{showBalanceDue && (
|
||||
<PaperTemplate.TotalLine
|
||||
label={balanceDueLabel}
|
||||
amount={balanceDue}
|
||||
border={PaperTemplateTotalBorder.Dark}
|
||||
style={{ fontWeight: 500 }}
|
||||
/>
|
||||
)}
|
||||
</PaperTemplate.Totals>
|
||||
</Stack>
|
||||
|
||||
<Stack spacing={0}>
|
||||
{showTermsConditions && termsConditions && (
|
||||
<PaperTemplate.Statement label={termsConditionsLabel}>
|
||||
{termsConditions}
|
||||
</PaperTemplate.Statement>
|
||||
)}
|
||||
|
||||
{showStatement && statement && (
|
||||
<PaperTemplate.Statement label={statementLabel}>
|
||||
{statement}
|
||||
</PaperTemplate.Statement>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</PaperTemplate>
|
||||
);
|
||||
}
|
||||
279
shared/pdf-templates/src/components/PaperTemplate.tsx
Normal file
279
shared/pdf-templates/src/components/PaperTemplate.tsx
Normal file
@@ -0,0 +1,279 @@
|
||||
import React from 'react';
|
||||
import clsx from 'classnames';
|
||||
import { get, isFunction } from 'lodash';
|
||||
import { x } from '@xstyled/emotion';
|
||||
import { css } from '@emotion/css';
|
||||
import { Box, BoxProps } from '../lib/layout/Box';
|
||||
import { Group, GroupProps } from '../lib/layout/Group';
|
||||
|
||||
export interface PaperTemplateProps extends BoxProps {
|
||||
primaryColor?: string;
|
||||
secondaryColor?: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export function PaperTemplate({
|
||||
primaryColor,
|
||||
secondaryColor,
|
||||
children,
|
||||
...restProps
|
||||
}: PaperTemplateProps) {
|
||||
return (
|
||||
<Box
|
||||
backgroundColor="#fff"
|
||||
color="#111"
|
||||
boxShadow="inset 0 4px 0px 0 var(--invoice-primary-color)"
|
||||
padding="30px 30px"
|
||||
fontSize="12px"
|
||||
position="relative"
|
||||
m="0 auto"
|
||||
h="1123px"
|
||||
w="794px"
|
||||
{...restProps}
|
||||
className={clsx(
|
||||
restProps?.className,
|
||||
css`
|
||||
@media print {
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
}
|
||||
`
|
||||
)}
|
||||
>
|
||||
<style>{`:root { --invoice-primary-color: ${primaryColor}; --invoice-secondary-color: ${secondaryColor}; }`}</style>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
interface PaperTemplateBigTitleProps {
|
||||
title: string;
|
||||
}
|
||||
|
||||
PaperTemplate.BigTitle = ({ title }: PaperTemplateBigTitleProps) => {
|
||||
return (
|
||||
<x.h1
|
||||
fontSize={'30px'}
|
||||
margin={0}
|
||||
lineHeight={1}
|
||||
fontWeight={500}
|
||||
color={'#333'}
|
||||
>
|
||||
{title}
|
||||
</x.h1>
|
||||
);
|
||||
};
|
||||
|
||||
interface PaperTemplateLogoProps {
|
||||
logoUri: string;
|
||||
}
|
||||
|
||||
PaperTemplate.Logo = ({ logoUri }: PaperTemplateLogoProps) => {
|
||||
return (
|
||||
<x.div overflow={'hidden'}>
|
||||
<x.img
|
||||
width={'100%'}
|
||||
height={'100%'}
|
||||
maxWidth={'260px'}
|
||||
maxHeight={'100px'}
|
||||
alt=""
|
||||
src={logoUri}
|
||||
/>
|
||||
</x.div>
|
||||
);
|
||||
};
|
||||
|
||||
interface PaperTemplateTableProps {
|
||||
columns: Array<{
|
||||
accessor: string | ((data: Record<string, any>) => JSX.Element);
|
||||
label: string;
|
||||
value?: JSX.Element;
|
||||
align?: 'left' | 'center' | 'right';
|
||||
}>;
|
||||
data: Array<Record<string, any>>;
|
||||
}
|
||||
|
||||
PaperTemplate.Table = ({ columns, data }: PaperTemplateTableProps) => {
|
||||
return (
|
||||
<table
|
||||
className={css`
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
text-align: left;
|
||||
|
||||
thead th {
|
||||
font-weight: 400;
|
||||
border-bottom: 1px solid #000;
|
||||
padding: 2px 10px;
|
||||
color: #333;
|
||||
|
||||
&.rate,
|
||||
&.total {
|
||||
text-align: right;
|
||||
}
|
||||
&:first-of-type {
|
||||
padding-left: 0;
|
||||
}
|
||||
&:last-of-type {
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
tbody {
|
||||
td {
|
||||
border-bottom: 1px solid #f6f6f6;
|
||||
padding: 12px 10px;
|
||||
|
||||
&:first-of-type {
|
||||
padding-left: 0;
|
||||
}
|
||||
&:last-of-type {
|
||||
padding-right: 0;
|
||||
}
|
||||
&.rate,
|
||||
&.total {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
`}
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
{columns.map((col, index) => (
|
||||
<x.th key={index} textAlign={col.align}>
|
||||
{col.label}
|
||||
</x.th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{data.map((_data: any) => (
|
||||
<tr>
|
||||
{columns.map((column, index) => (
|
||||
<x.td textAlign={column.align} key={index}>
|
||||
{isFunction(column?.accessor)
|
||||
? column?.accessor(_data)
|
||||
: get(_data, column.accessor)}
|
||||
</x.td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
};
|
||||
|
||||
export enum PaperTemplateTotalBorder {
|
||||
Gray = 'gray',
|
||||
Dark = 'dark',
|
||||
}
|
||||
|
||||
PaperTemplate.Totals = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<x.div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
marginLeft: 'auto',
|
||||
width: '300px',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</x.div>
|
||||
);
|
||||
};
|
||||
|
||||
const totalBottomBordered = css`
|
||||
border-bottom: 1px solid #000;
|
||||
`;
|
||||
const totalBottomGrayBordered = css`
|
||||
border-bottom: 1px solid #dadada;
|
||||
`;
|
||||
|
||||
PaperTemplate.TotalLine = ({
|
||||
label,
|
||||
amount,
|
||||
border,
|
||||
style,
|
||||
}: {
|
||||
label: string;
|
||||
amount: string;
|
||||
border?: PaperTemplateTotalBorder;
|
||||
style?: any;
|
||||
}) => {
|
||||
return (
|
||||
<x.div
|
||||
display={'flex'}
|
||||
padding={'4px 0'}
|
||||
className={clsx({
|
||||
[totalBottomBordered]: border === PaperTemplateTotalBorder.Dark,
|
||||
[totalBottomGrayBordered]: border === PaperTemplateTotalBorder.Gray,
|
||||
})}
|
||||
>
|
||||
<x.div min-w="160px">{label}</x.div>
|
||||
<x.div flex={'1 1 auto'} textAlign={'right'}>
|
||||
{amount}
|
||||
</x.div>
|
||||
</x.div>
|
||||
);
|
||||
};
|
||||
|
||||
PaperTemplate.AddressesGroup = (props: GroupProps) => {
|
||||
return (
|
||||
<Group
|
||||
spacing={10}
|
||||
align={'flex-start'}
|
||||
{...props}
|
||||
className={css`
|
||||
> div {
|
||||
flex: 1;
|
||||
}
|
||||
`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
PaperTemplate.Address = ({ children }: { children: React.ReactNode }) => {
|
||||
return <Box>{children}</Box>;
|
||||
};
|
||||
|
||||
PaperTemplate.Statement = ({
|
||||
label,
|
||||
children,
|
||||
}: {
|
||||
label: string;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<x.div mb={'20px'}>
|
||||
{label && <x.div color={'#666'}>{label}</x.div>}
|
||||
<x.div>{children}</x.div>
|
||||
</x.div>
|
||||
);
|
||||
};
|
||||
|
||||
PaperTemplate.TermsList = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
<x.div display={'flex'} flexDirection={'column'} gap={'4px'}>
|
||||
{children}
|
||||
</x.div>
|
||||
);
|
||||
};
|
||||
|
||||
PaperTemplate.TermsItem = ({
|
||||
label,
|
||||
children,
|
||||
}: {
|
||||
label: string;
|
||||
children: React.ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<Group spacing={12}>
|
||||
<x.div minWidth={'120px'} color={'#333'}>
|
||||
{label}
|
||||
</x.div>
|
||||
<x.div>{children}</x.div>
|
||||
</Group>
|
||||
);
|
||||
};
|
||||
66
shared/pdf-templates/src/components/PaperTemplateLayout.tsx
Normal file
66
shared/pdf-templates/src/components/PaperTemplateLayout.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import { CacheProvider, ThemeProvider } from '@emotion/react';
|
||||
import { EmotionCache } from '@emotion/cache';
|
||||
import { defaultTheme } from '@xstyled/system';
|
||||
import { createGlobalStyle, Preflight } from '@xstyled/emotion';
|
||||
|
||||
const theme = {
|
||||
...defaultTheme,
|
||||
};
|
||||
export function PaperTemplateLayout({
|
||||
cache,
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
cache: EmotionCache;
|
||||
}) {
|
||||
return (
|
||||
<CacheProvider value={cache}>
|
||||
<ThemeProvider theme={theme}>
|
||||
<Preflight />
|
||||
<GlobalStyles />
|
||||
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</CacheProvider>
|
||||
);
|
||||
}
|
||||
|
||||
// Create global styles to set the body font
|
||||
const GlobalStyles = createGlobalStyle`
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
th {
|
||||
text-align: inherit;
|
||||
text-align: -webkit-match-parent;
|
||||
}
|
||||
thead,
|
||||
tbody,
|
||||
tfoot,
|
||||
tr,
|
||||
td,
|
||||
th {
|
||||
border-color: inherit;
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
body{
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #000;
|
||||
background-color: #fff;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
body, h1, h2, h3, h4, h5, h6{
|
||||
font-family: "Open Sans", sans-serif;
|
||||
font-optical-sizing: auto;
|
||||
font-style: normal;
|
||||
}
|
||||
`;
|
||||
25
shared/pdf-templates/src/components/_constants.ts
Normal file
25
shared/pdf-templates/src/components/_constants.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export const DefaultPdfTemplateTerms =
|
||||
'All services provided are non-refundable. For any disputes, please contact us within 7 days of receiving this invoice.';
|
||||
|
||||
export const DefaultPdfTemplateStatement =
|
||||
'Thank you for your business. We look forward to working with you again!';
|
||||
|
||||
export const DefaultPdfTemplateItemName = 'Web development';
|
||||
|
||||
export const DefaultPdfTemplateItemDescription =
|
||||
'Website development with content and SEO optimization';
|
||||
|
||||
export const DefaultPdfTemplateAddressBilledTo = `Bigcapital Technology, Inc.<br />
|
||||
131 Continental Dr, <br />
|
||||
Suite 305, <br />
|
||||
Newark, Delaware 19713, <br />
|
||||
United States,<br />
|
||||
+1 762-339-5634
|
||||
`;
|
||||
|
||||
export const DefaultPdfTemplateAddressBilledFrom = `131 Continental Dr Suite 305 Newark, <br />
|
||||
Delaware 19713,<br />
|
||||
United States, <br />
|
||||
+1 762-339-5634, <br />
|
||||
ahmed@bigcapital.app
|
||||
`;
|
||||
5
shared/pdf-templates/src/constants.ts
Normal file
5
shared/pdf-templates/src/constants.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const OpenSansFontLink = `
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
|
||||
`;
|
||||
3
shared/pdf-templates/src/index.ts
Normal file
3
shared/pdf-templates/src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './components/PaperTemplate';
|
||||
export * from './components/InvoicePaperTemplate';
|
||||
export * from './renders/render-invoice-paper-template';
|
||||
19
shared/pdf-templates/src/lib/layout/Box.tsx
Normal file
19
shared/pdf-templates/src/lib/layout/Box.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React, { forwardRef, Ref } from 'react';
|
||||
import { SystemProps, x } from '@xstyled/emotion';
|
||||
|
||||
interface IProps {
|
||||
className?: string;
|
||||
}
|
||||
export interface BoxProps
|
||||
extends SystemProps,
|
||||
IProps,
|
||||
Omit<React.HTMLProps<HTMLDivElement>, 'color' | 'as'> { }
|
||||
|
||||
export const Box = forwardRef(
|
||||
({ className, ...rest }: BoxProps, ref: Ref<HTMLDivElement>) => {
|
||||
const Element = x.div;
|
||||
|
||||
return <Element className={className} ref={ref} {...rest} />;
|
||||
},
|
||||
);
|
||||
Box.displayName = '@bigcapital/Box';
|
||||
58
shared/pdf-templates/src/lib/layout/Group.tsx
Normal file
58
shared/pdf-templates/src/lib/layout/Group.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
import { SystemProps } from '@xstyled/emotion';
|
||||
import { Box } from './Box';
|
||||
import { filterFalsyChildren } from './utils';
|
||||
|
||||
export type GroupPosition = 'right' | 'center' | 'left' | 'apart';
|
||||
|
||||
export const GROUP_POSITIONS = {
|
||||
left: 'flex-start',
|
||||
center: 'center',
|
||||
right: 'flex-end',
|
||||
apart: 'space-between',
|
||||
};
|
||||
|
||||
export interface GroupProps
|
||||
extends SystemProps,
|
||||
Omit<React.ComponentPropsWithoutRef<'div'>, 'color'> {
|
||||
/** Defines justify-content property */
|
||||
position?: GroupPosition;
|
||||
|
||||
/** Defined flex-wrap property */
|
||||
noWrap?: boolean;
|
||||
|
||||
/** Defines flex-grow property for each element, true -> 1, false -> 0 */
|
||||
grow?: boolean;
|
||||
|
||||
/** Space between elements */
|
||||
spacing?: number;
|
||||
|
||||
/** Defines align-items css property */
|
||||
align?: React.CSSProperties['alignItems'];
|
||||
}
|
||||
|
||||
export function Group({
|
||||
position = 'left',
|
||||
spacing = 20,
|
||||
align = 'center',
|
||||
noWrap,
|
||||
children,
|
||||
...props
|
||||
}: GroupProps) {
|
||||
const filteredChildren = filterFalsyChildren(children);
|
||||
|
||||
return (
|
||||
<Box
|
||||
boxSizing={'border-box'}
|
||||
display={'flex'}
|
||||
flexDirection={'row'}
|
||||
alignItems={align}
|
||||
flexWrap={noWrap ? 'nowrap' : 'wrap'}
|
||||
justifyContent={GROUP_POSITIONS[position]}
|
||||
gap={`${spacing}px`}
|
||||
{...props}
|
||||
>
|
||||
{filteredChildren}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
33
shared/pdf-templates/src/lib/layout/Stack.tsx
Normal file
33
shared/pdf-templates/src/lib/layout/Stack.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import React from 'react';
|
||||
import { x, SystemProps } from '@xstyled/emotion';
|
||||
|
||||
export interface StackProps
|
||||
extends SystemProps,
|
||||
Omit<React.ComponentPropsWithoutRef<'div'>, 'color'> {
|
||||
/** Key of theme.spacing or number to set gap in px */
|
||||
spacing?: number;
|
||||
|
||||
/** align-items CSS property */
|
||||
align?: React.CSSProperties['alignItems'];
|
||||
|
||||
/** justify-content CSS property */
|
||||
justify?: React.CSSProperties['justifyContent'];
|
||||
}
|
||||
|
||||
export function Stack({
|
||||
spacing = 20,
|
||||
align = 'stretch',
|
||||
justify = 'top',
|
||||
...restProps
|
||||
}: StackProps) {
|
||||
return (
|
||||
<x.div
|
||||
display={'flex'}
|
||||
flexDirection="column"
|
||||
justifyContent="justify"
|
||||
gap={`${spacing}px`}
|
||||
alignItems={align}
|
||||
{...restProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
5
shared/pdf-templates/src/lib/layout/utils.ts
Normal file
5
shared/pdf-templates/src/lib/layout/utils.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
export const filterFalsyChildren = (children: React.ReactNode) => {
|
||||
return React.Children.toArray(children).filter(Boolean);
|
||||
};
|
||||
4
shared/pdf-templates/src/lib/main.ts
Normal file
4
shared/pdf-templates/src/lib/main.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export * from './layout/Stack';
|
||||
export * from './layout/Group';
|
||||
export * from './layout/Box';
|
||||
export * from './text/Text';
|
||||
9
shared/pdf-templates/src/lib/text/Text.tsx
Normal file
9
shared/pdf-templates/src/lib/text/Text.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { SystemProps, x } from '@xstyled/emotion';
|
||||
|
||||
export interface TextProps extends SystemProps {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export const Text = ({ children, ...restProps }: TextProps) => {
|
||||
return <x.div {...restProps}>{children}</x.div>;
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import createCache from '@emotion/cache';
|
||||
import { css } from '@emotion/css';
|
||||
import {
|
||||
InvoicePaperTemplate,
|
||||
InvoicePaperTemplateProps,
|
||||
} from '../components/InvoicePaperTemplate';
|
||||
import { PaperTemplateLayout } from '../components/PaperTemplateLayout';
|
||||
import { extractCritical } from '@emotion/server';
|
||||
import { OpenSansFontLink } from '../constants';
|
||||
import { renderSSR } from './render-ssr';
|
||||
|
||||
export const renderInvoicePaperTemplateHtml = (
|
||||
props: InvoicePaperTemplateProps
|
||||
) => {
|
||||
return renderSSR(
|
||||
<InvoicePaperTemplate
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
31
shared/pdf-templates/src/renders/render-ssr.tsx
Normal file
31
shared/pdf-templates/src/renders/render-ssr.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import createCache from '@emotion/cache';
|
||||
import { extractCritical } from '@emotion/server';
|
||||
import { OpenSansFontLink } from '../constants';
|
||||
import { PaperTemplateLayout } from '../components/PaperTemplateLayout';
|
||||
|
||||
export const renderSSR = (children: React.ReactNode) => {
|
||||
const key = 'invoice-paper-template';
|
||||
const cache = createCache({ key });
|
||||
|
||||
const renderedHtml = renderToString(
|
||||
<PaperTemplateLayout cache={cache}>{children}</PaperTemplateLayout>
|
||||
);
|
||||
const extractedHtml = extractCritical(renderedHtml);
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Invoice</title>
|
||||
${OpenSansFontLink}
|
||||
<style data-emotion="${key} ${extractedHtml.ids.join(' ')}">${extractedHtml.css
|
||||
}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">${extractedHtml.html}</div>
|
||||
</body>
|
||||
</html>`;
|
||||
};
|
||||
8
shared/pdf-templates/tailwind.config.js
Normal file
8
shared/pdf-templates/tailwind.config.js
Normal file
@@ -0,0 +1,8 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['./src/**/*.{ts,tsx}'],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
28
shared/pdf-templates/tsconfig.json
Normal file
28
shared/pdf-templates/tsconfig.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// "noEmit": true,
|
||||
"composite": false,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"inlineSources": false,
|
||||
"isolatedModules": true,
|
||||
"moduleResolution": "node",
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"preserveWatchOutput": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"jsx": "react-jsx",
|
||||
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"target": "ESNext",
|
||||
"types": ["vitest/globals"],
|
||||
"resolveJsonModule": true,
|
||||
"outDir": "dist",
|
||||
},
|
||||
"include": ["."],
|
||||
"exclude": ["dist", "build", "node_modules"]
|
||||
}
|
||||
23
shared/pdf-templates/tsconfig.node.json
Normal file
23
shared/pdf-templates/tsconfig.node.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "Bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
61
shared/pdf-templates/vite.config.ts
Normal file
61
shared/pdf-templates/vite.config.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import react from '@vitejs/plugin-react';
|
||||
import path from 'node:path';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
import dts from 'vite-plugin-dts';
|
||||
import tailwindcss from 'tailwindcss';
|
||||
import { UserConfigExport } from 'vite';
|
||||
import { name } from './package.json';
|
||||
|
||||
const app = async (): Promise<UserConfigExport> => {
|
||||
/**
|
||||
* Removes everything before the last
|
||||
* @octocat/library-repo -> library-repo
|
||||
* vite-component-library-template -> vite-component-library-template
|
||||
*/
|
||||
const formattedName = name.match(/[^/]+$/)?.[0] ?? name;
|
||||
|
||||
return defineConfig({
|
||||
define: {
|
||||
isBrowser: 'false', // This will replace isBrowser with false in the bundled code
|
||||
},
|
||||
ssr: {
|
||||
noExternal: true,
|
||||
},
|
||||
plugins: [
|
||||
react(),
|
||||
dts({
|
||||
insertTypesEntry: true,
|
||||
}),
|
||||
],
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: [tailwindcss],
|
||||
},
|
||||
},
|
||||
build: {
|
||||
lib: {
|
||||
entry: path.resolve(__dirname, 'src/lib/main.ts'),
|
||||
name: formattedName,
|
||||
formats: ['es', 'umd'],
|
||||
fileName: (format: string) => `${formattedName}.${format}.js`,
|
||||
},
|
||||
rollupOptions: {
|
||||
// external: ['react', 'react/jsx-runtime', 'react-dom', 'tailwindcss'],
|
||||
// output: {
|
||||
// globals: {
|
||||
// react: 'React',
|
||||
// 'react/jsx-runtime': 'react/jsx-runtime',
|
||||
// 'react-dom': 'ReactDOM',
|
||||
// tailwindcss: 'tailwindcss',
|
||||
// },
|
||||
// },
|
||||
},
|
||||
},
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
},
|
||||
});
|
||||
};
|
||||
// https://vitejs.dev/config/
|
||||
export default app;
|
||||
36
shared/pdf-templates/webpack.config.js
Normal file
36
shared/pdf-templates/webpack.config.js
Normal file
@@ -0,0 +1,36 @@
|
||||
// webpack.config.js
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
mode: 'production',
|
||||
target: 'node',
|
||||
entry: './src/index.ts',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
filename: 'components.umd.js',
|
||||
library: {
|
||||
name: '@bigcapital/library-components',
|
||||
type: 'umd',
|
||||
},
|
||||
globalObject: 'this',
|
||||
clean: true,
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js', '.jsx'],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(ts|tsx)$/,
|
||||
use: {
|
||||
loader: 'ts-loader',
|
||||
},
|
||||
exclude: /node_modules/,
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ['style-loader', 'css-loader'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user