mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 06:10:31 +00:00
feat: add style to SSR invoice paper template
This commit is contained in:
@@ -450,15 +450,15 @@ export default class SaleInvoicesController extends BaseController {
|
|||||||
ACCEPT_TYPE.APPLICATION_JSON,
|
ACCEPT_TYPE.APPLICATION_JSON,
|
||||||
ACCEPT_TYPE.APPLICATION_PDF,
|
ACCEPT_TYPE.APPLICATION_PDF,
|
||||||
]);
|
]);
|
||||||
// Retrieves invoice in pdf format.
|
// Retrieves invoice in PDF format.
|
||||||
if (true) {
|
if (ACCEPT_TYPE.APPLICATION_PDF === acceptType) {
|
||||||
const [pdfContent, filename] =
|
const [pdfContent, filename] =
|
||||||
await this.saleInvoiceApplication.saleInvoicePdf(
|
await this.saleInvoiceApplication.saleInvoicePdf(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoiceId
|
saleInvoiceId
|
||||||
);
|
);
|
||||||
res.set({
|
res.set({
|
||||||
'Content-Type': 'text/html',
|
'Content-Type': 'application/pdf',
|
||||||
'Content-Length': pdfContent.length,
|
'Content-Length': pdfContent.length,
|
||||||
'Content-Disposition': `attachment; filename="${filename}"`,
|
'Content-Disposition': `attachment; filename="${filename}"`,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -47,16 +47,9 @@ export class SaleInvoicePdf {
|
|||||||
tenantId,
|
tenantId,
|
||||||
invoiceId
|
invoiceId
|
||||||
);
|
);
|
||||||
// const htmlContent = await this.templateInjectable.render(
|
const htmlContent = renderInvoicePaperTemplateHtml({
|
||||||
// tenantId,
|
...brandingAttributes,
|
||||||
// 'modules/invoice-standard',
|
});
|
||||||
// brandingAttributes
|
|
||||||
// );
|
|
||||||
|
|
||||||
const htmlContent = renderInvoicePaperTemplateHtml({});
|
|
||||||
|
|
||||||
console.log(htmlContent);
|
|
||||||
|
|
||||||
// Converts the given html content to pdf document.
|
// Converts the given html content to pdf document.
|
||||||
const buffer = await this.chromiumlyTenancy.convertHtmlContent(
|
const buffer = await this.chromiumlyTenancy.convertHtmlContent(
|
||||||
tenantId,
|
tenantId,
|
||||||
@@ -69,7 +62,7 @@ export class SaleInvoicePdf {
|
|||||||
events.saleInvoice.onPdfViewed,
|
events.saleInvoice.onPdfViewed,
|
||||||
eventPayload
|
eventPayload
|
||||||
);
|
);
|
||||||
return [htmlContent, filename];
|
return [buffer, filename];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -243,15 +243,15 @@ export function InvoicePaperTemplate({
|
|||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
<Text>{data.item}</Text>
|
<Text>{data.item}</Text>
|
||||||
<Text
|
<Text
|
||||||
// variant={'muted'}
|
color={'#5f6b7c'}
|
||||||
// style={{ fontSize: 12 }}
|
fontSize={12}
|
||||||
>
|
>
|
||||||
{data.description}
|
{data.description}
|
||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{ label: lineQuantityLabel, accessor: 'quantity' },
|
{ label: lineQuantityLabel, accessor: 'quantity', align: 'right' },
|
||||||
{ label: lineRateLabel, accessor: 'rate', align: 'right' },
|
{ label: lineRateLabel, accessor: 'rate', align: 'right' },
|
||||||
{ label: lineTotalLabel, accessor: 'total', align: 'right' },
|
{ label: lineTotalLabel, accessor: 'total', align: 'right' },
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -20,10 +20,9 @@ export function PaperTemplate({
|
|||||||
}: PaperTemplateProps) {
|
}: PaperTemplateProps) {
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
borderRadius="5px"
|
|
||||||
backgroundColor="#fff"
|
backgroundColor="#fff"
|
||||||
color="#111"
|
color="#111"
|
||||||
boxShadow="inset 0 4px 0px 0 var(--invoice-primary-color), 0 10px 15px rgba(0, 0, 0, 0.05)"
|
boxShadow="inset 0 4px 0px 0 var(--invoice-primary-color)"
|
||||||
padding="30px 30px"
|
padding="30px 30px"
|
||||||
fontSize="12px"
|
fontSize="12px"
|
||||||
position="relative"
|
position="relative"
|
||||||
@@ -31,7 +30,15 @@ export function PaperTemplate({
|
|||||||
h="1123px"
|
h="1123px"
|
||||||
w="794px"
|
w="794px"
|
||||||
{...restProps}
|
{...restProps}
|
||||||
className={restProps?.className}
|
className={clsx(
|
||||||
|
restProps?.className,
|
||||||
|
css`
|
||||||
|
@media print {
|
||||||
|
width: auto !important;
|
||||||
|
height: auto !important;
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<style>{`:root { --invoice-primary-color: ${primaryColor}; --invoice-secondary-color: ${secondaryColor}; }`}</style>
|
<style>{`:root { --invoice-primary-color: ${primaryColor}; --invoice-secondary-color: ${secondaryColor}; }`}</style>
|
||||||
{children}
|
{children}
|
||||||
@@ -39,16 +46,6 @@ export function PaperTemplate({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PaperTemplateBigTitleProps {
|
interface PaperTemplateBigTitleProps {
|
||||||
title: string;
|
title: string;
|
||||||
}
|
}
|
||||||
@@ -86,6 +83,16 @@ PaperTemplate.Logo = ({ logoUri }: PaperTemplateLogoProps) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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) => {
|
PaperTemplate.Table = ({ columns, data }: PaperTemplateTableProps) => {
|
||||||
return (
|
return (
|
||||||
<table
|
<table
|
||||||
@@ -133,9 +140,9 @@ PaperTemplate.Table = ({ columns, data }: PaperTemplateTableProps) => {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
{columns.map((col, index) => (
|
{columns.map((col, index) => (
|
||||||
<th key={index} align={col.align}>
|
<x.th key={index} textAlign={col.align}>
|
||||||
{col.label}
|
{col.label}
|
||||||
</th>
|
</x.th>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -144,11 +151,11 @@ PaperTemplate.Table = ({ columns, data }: PaperTemplateTableProps) => {
|
|||||||
{data.map((_data: any) => (
|
{data.map((_data: any) => (
|
||||||
<tr>
|
<tr>
|
||||||
{columns.map((column, index) => (
|
{columns.map((column, index) => (
|
||||||
<td align={column.align} key={index}>
|
<x.td textAlign={column.align} key={index}>
|
||||||
{isFunction(column?.accessor)
|
{isFunction(column?.accessor)
|
||||||
? column?.accessor(_data)
|
? column?.accessor(_data)
|
||||||
: get(_data, column.accessor)}
|
: get(_data, column.accessor)}
|
||||||
</td>
|
</x.td>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
@@ -183,6 +190,7 @@ const totalBottomBordered = css`
|
|||||||
const totalBottomGrayBordered = css`
|
const totalBottomGrayBordered = css`
|
||||||
border-bottom: 1px solid #dadada;
|
border-bottom: 1px solid #dadada;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
PaperTemplate.TotalLine = ({
|
PaperTemplate.TotalLine = ({
|
||||||
label,
|
label,
|
||||||
amount,
|
amount,
|
||||||
@@ -262,7 +270,9 @@ PaperTemplate.TermsItem = ({
|
|||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<Group spacing={12}>
|
<Group spacing={12}>
|
||||||
<x.div minWidth={'12px'} color={'#333'}>{label}</x.div>
|
<x.div minWidth={'120px'} color={'#333'}>
|
||||||
|
{label}
|
||||||
|
</x.div>
|
||||||
<x.div>{children}</x.div>
|
<x.div>{children}</x.div>
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { CacheProvider, ThemeProvider } from '@emotion/react';
|
import { CacheProvider, ThemeProvider } from '@emotion/react';
|
||||||
import { EmotionCache } from '@emotion/cache';
|
import { EmotionCache } from '@emotion/cache';
|
||||||
import { defaultTheme } from '@xstyled/system';
|
import { defaultTheme } from '@xstyled/system';
|
||||||
import { Preflight } from '@xstyled/emotion';
|
import { createGlobalStyle, Preflight } from '@xstyled/emotion';
|
||||||
|
|
||||||
const theme = {
|
const theme = {
|
||||||
...defaultTheme,
|
...defaultTheme,
|
||||||
@@ -17,8 +17,50 @@ export function PaperTemplateLayout({
|
|||||||
<CacheProvider value={cache}>
|
<CacheProvider value={cache}>
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<Preflight />
|
<Preflight />
|
||||||
|
<GlobalStyles />
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</CacheProvider>
|
</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;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|||||||
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">
|
||||||
|
`;
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
import { x } from '@xstyled/emotion';
|
import { SystemProps, x } from '@xstyled/emotion';
|
||||||
|
|
||||||
export const Text = ({ children }: { children: React.ReactNode }) => {
|
export interface TextProps extends SystemProps {
|
||||||
return <x.div>{children}</x.div>;
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Text = ({ children, ...restProps }: TextProps) => {
|
||||||
|
return <x.div {...restProps}>{children}</x.div>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,36 +1,21 @@
|
|||||||
import { renderToString } from 'react-dom/server';
|
import { renderToString } from 'react-dom/server';
|
||||||
import createCache from '@emotion/cache';
|
import createCache from '@emotion/cache';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
import {
|
import {
|
||||||
InvoicePaperTemplate,
|
InvoicePaperTemplate,
|
||||||
InvoicePaperTemplateProps,
|
InvoicePaperTemplateProps,
|
||||||
} from '../components/InvoicePaperTemplate';
|
} from '../components/InvoicePaperTemplate';
|
||||||
import { PaperTemplateLayout } from '../components/PaperTemplateLayout';
|
import { PaperTemplateLayout } from '../components/PaperTemplateLayout';
|
||||||
import { extractCritical } from '@emotion/server';
|
import { extractCritical } from '@emotion/server';
|
||||||
|
import { OpenSansFontLink } from '../constants';
|
||||||
|
import { renderSSR } from './render-ssr';
|
||||||
|
|
||||||
export const renderInvoicePaperTemplateHtml = (
|
export const renderInvoicePaperTemplateHtml = (
|
||||||
props: InvoicePaperTemplateProps
|
props: InvoicePaperTemplateProps
|
||||||
) => {
|
) => {
|
||||||
const key = 'invoice-paper-template';
|
return renderSSR(
|
||||||
const cache = createCache({ key });
|
<InvoicePaperTemplate
|
||||||
|
{...props}
|
||||||
const renderedHtml = renderToString(
|
/>
|
||||||
<PaperTemplateLayout cache={cache}>
|
|
||||||
<InvoicePaperTemplate {...props} />
|
|
||||||
</PaperTemplateLayout>
|
|
||||||
);
|
);
|
||||||
const { html, css, ids } = 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>
|
|
||||||
<style data-emotion="${key} ${ids.join(' ')}">${css}</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="root">${html}</div>
|
|
||||||
</body>
|
|
||||||
</html>`;
|
|
||||||
};
|
};
|
||||||
|
|||||||
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>`;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user