feat: email templates

This commit is contained in:
Ahmed Bouhuolia
2024-11-19 14:00:25 +02:00
parent 2c54092591
commit ddffe630ff
9 changed files with 629 additions and 99 deletions

View File

@@ -18,4 +18,16 @@ export const Default: StoryFn<CreditNoteEmailProps> = Template.bind({});
Default.args = {
total: '$1,000.00',
items: [{ label: 'Swaniawski Muller', quantity: '1', rate: '$1,000.00' }],
message: `Hi Ahmed Bouhuolia,
Here's invoice # INV-00005 for $1,000.00
The amount outstanding of $1,000.00 is due on 10 Oct 2024.
From your online payment page you can print a PDF or view your outstanding bills.
If you have any questions, please let us know.
Thanks,
Bigcapital`,
};

View File

@@ -1,19 +1,43 @@
export interface CreditNoteEmailProps {
preview?: string;
import {
Button,
Column,
Container,
Heading,
Row,
Section,
Text,
} from '@react-email/components';
import { EmailTemplateLayout } from './EmailTemplateLayout';
import { CSSProperties } from 'react';
import { EmailTemplate } from './EmailTemplate';
export interface CreditNoteEmailProps {
preview: string;
// # Company
companyName?: string;
companyLogoUri: string;
// # Color
primaryColor?: string;
// # Total
total: string;
totalLabel?: string;
// # Items
items: Array<{ label: string; quantity: string; rate: string }>;
viewEstimateButtonLabel?: string;
viewEstimateButtonUrl?: string;
// # View button
viewButtonLabel?: string;
viewButtonUrl?: string;
// # Credit Note #
creditNoteNumber?: string;
creditNoteNumberLabel?: string;
// # Message
message?: string;
}
export const CreditNoteEmailTemplate: React.FC<
@@ -22,19 +46,179 @@ export const CreditNoteEmailTemplate: React.FC<
preview,
// # Company
companyName,
companyName = 'Bigcapital, Inc.',
companyLogoUri,
// # Colors
primaryColor = 'rgb(0, 82, 204)',
// # invoice total
// # Invoice total
total,
totalLabel = 'Total',
// # Credit Note #
creditNoteNumberLabel = 'Credit Note # {creditNoteNumber}',
creditNoteNumber = 'CN-00001',
// # View invoice button
viewEstimateButtonLabel = 'View Estimate',
viewEstimateButtonUrl,
viewButtonLabel = 'View Credit Note',
viewButtonUrl,
// # Message
message = '',
// # Items
items = [],
}) => {
return <h1>asdasd</h1>;
return (
<EmailTemplateLayout preview={preview}>
<EmailTemplate>
<Section style={mainSectionStyle}>
{companyLogoUri && <EmailTemplate.CompanyLogo src={companyLogoUri} />}
<Section style={headerInfoStyle}>
<Row>
<Heading style={companyNameStyle}>{companyName}</Heading>
</Row>
<Row>
<Text style={invoiceAmountStyle}>{total}</Text>
</Row>
<Row>
<Text style={creditNumberStyle}>
{creditNoteNumberLabel?.replace(
'{creditNoteNumber}',
creditNoteNumber
)}
</Text>
</Row>
</Section>
<Text style={messageStyle}>{message}</Text>
<Button
href={viewButtonUrl}
style={{
...viewInvoiceButtonStyle,
backgroundColor: primaryColor,
}}
>
{viewButtonLabel}
</Button>
<Section style={totalsSectionStyle}>
{items.map((item, index) => (
<Row key={index} style={itemLineRowStyle}>
<Column width={'50%'}>
<Text style={listItemLabelStyle}>{item.label}</Text>
</Column>
<Column width={'50%'}>
<Text style={listItemAmountStyle}>
{item.quantity} x {item.rate}
</Text>
</Column>
</Row>
))}
<Row style={totalLineRowStyle}>
<Column width={'50%'}>
<Text style={totalLineItemLabelStyle}>{totalLabel}</Text>
</Column>
<Column width={'50%'}>
<Text style={totalLineItemAmountStyle}>{total}</Text>
</Column>
</Row>
</Section>
</Section>
</EmailTemplate>
</EmailTemplateLayout>
);
};
const containerStyle: CSSProperties = {
backgroundColor: '#fff',
width: '100%',
maxWidth: '500px',
padding: '35px 25px',
color: '#000',
borderRadius: '5px',
};
const headerInfoStyle: CSSProperties = {
textAlign: 'center',
marginBottom: 20,
};
const mainSectionStyle: CSSProperties = {};
const invoiceAmountStyle: CSSProperties = {
margin: 0,
color: '#383E47',
fontWeight: 500,
};
const creditNumberStyle: CSSProperties = {
margin: 0,
fontSize: '13px',
color: '#404854',
};
const companyNameStyle: CSSProperties = {
margin: 0,
fontSize: '18px',
fontWeight: 500,
color: '#404854',
};
const viewInvoiceButtonStyle: CSSProperties = {
display: 'block',
cursor: 'pointer',
textAlign: 'center',
fontSize: 16,
padding: '10px 15px',
lineHeight: '1',
backgroundColor: 'rgb(0, 82, 204)',
color: '#fff',
borderRadius: '5px',
};
const listItemLabelStyle: CSSProperties = {
margin: 0,
};
const listItemAmountStyle: CSSProperties = {
margin: 0,
textAlign: 'right',
};
const messageStyle: CSSProperties = {
whiteSpace: 'pre-line',
color: '#252A31',
margin: '0 0 20px 0',
lineHeight: '20px',
};
const totalLineRowStyle: CSSProperties = {
borderBottom: '1px solid #000',
height: 40,
};
const totalLineItemLabelStyle: CSSProperties = {
...listItemLabelStyle,
fontWeight: 500,
};
const totalLineItemAmountStyle: CSSProperties = {
...listItemAmountStyle,
fontWeight: 600,
};
const itemLineRowStyle: CSSProperties = {
borderBottom: '1px solid #D9D9D9',
height: 40,
};
const totalsSectionStyle = {
marginTop: '20px',
borderTop: '1px solid #D9D9D9',
};

View File

@@ -0,0 +1,48 @@
import { Container, Section } from '@react-email/components';
import { CSSProperties } from 'react';
interface EmailTemplateProps {
children: React.ReactNode;
}
export function EmailTemplate({ children }: EmailTemplateProps) {
return <Container style={containerStyle}>{children}</Container>;
}
EmailTemplate.CompanyLogo = ({ src }: { src: string }) => {
return (
<Section style={logoSectionStyle}>
<div
style={{
...companyLogoStyle,
backgroundImage: `url("${src}")`,
}}
></div>
</Section>
);
};
const containerStyle: CSSProperties = {
backgroundColor: '#fff',
width: '100%',
maxWidth: '500px',
padding: '35px 25px',
color: '#000',
borderRadius: '5px',
};
const companyLogoStyle = {
height: 90,
width: 90,
borderRadius: '3px',
marginLeft: 'auto',
marginRight: 'auto',
textIndent: '-999999px',
overflow: 'hidden',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center center',
backgroundSize: 'contain',
};
const logoSectionStyle = {
marginBottom: '15px',
};

View File

@@ -9,6 +9,7 @@ import {
Text,
} from '@react-email/components';
import { EmailTemplateLayout } from './EmailTemplateLayout';
import { EmailTemplate } from './EmailTemplate';
export interface EstimatePaymentEmailProps {
preview: string;
@@ -79,17 +80,8 @@ export const EstimatePaymentEmail: React.FC<
}) => {
return (
<EmailTemplateLayout preview={preview}>
<Container style={containerStyle}>
{companyLogoUri && (
<Section style={logoSectionStyle}>
<div
style={{
...companyLogoStyle,
backgroundImage: `url("${companyLogoUri}")`,
}}
></div>
</Section>
)}
<EmailTemplate>
{companyLogoUri && <EmailTemplate.CompanyLogo src={companyLogoUri} />}
<Section style={headerInfoStyle}>
<Row>
@@ -146,21 +138,11 @@ export const EstimatePaymentEmail: React.FC<
</Column>
</Row>
</Section>
</Container>
</EmailTemplate>
</EmailTemplateLayout>
);
};
const containerStyle: CSSProperties = {
backgroundColor: '#fff',
width: '100%',
maxWidth: '500px',
padding: '35px 25px',
color: '#000',
borderRadius: '5px',
};
const headerInfoStyle: CSSProperties = {
textAlign: 'center',
marginBottom: 20,
@@ -240,20 +222,3 @@ const totalsSectionStyle = {
marginTop: '20px',
borderTop: '1px solid #D9D9D9',
};
const logoSectionStyle = {
marginBottom: '15px',
};
const companyLogoStyle = {
height: 90,
width: 90,
borderRadius: '3px',
marginLeft: 'auto',
marginRight: 'auto',
textIndent: '-999999px',
overflow: 'hidden',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center center',
backgroundSize: 'contain',
};

View File

@@ -1,20 +1,16 @@
import {
Html,
Button,
Head,
Body,
Container,
Section,
Heading,
Text,
Preview,
Tailwind,
Row,
Column,
render,
} from '@react-email/components';
import { CSSProperties } from 'react';
import { EmailTemplateLayout } from './EmailTemplateLayout';
import { EmailTemplate } from './EmailTemplate';
export interface InvoicePaymentEmailProps {
preview: string;
@@ -98,18 +94,9 @@ export const InvoicePaymentEmail: React.FC<
}) => {
return (
<EmailTemplateLayout preview={preview}>
<Container style={containerStyle}>
<EmailTemplate>
<Section style={mainSectionStyle}>
{companyLogoUri && (
<Section style={logoSectionStyle}>
<div
style={{
...companyLogoStyle,
backgroundImage: `url("${companyLogoUri}")`,
}}
></div>
</Section>
)}
{companyLogoUri && <EmailTemplate.CompanyLogo src={companyLogoUri} />}
<Section style={headerInfoStyle}>
<Row>
@@ -179,7 +166,7 @@ export const InvoicePaymentEmail: React.FC<
</Row>
</Section>
</Section>
</Container>
</EmailTemplate>
</EmailTemplateLayout>
);
};
@@ -293,20 +280,3 @@ const totalsSectionStyle = {
marginTop: '20px',
borderTop: '1px solid #D9D9D9',
};
const logoSectionStyle = {
marginBottom: '15px',
};
const companyLogoStyle = {
height: 90,
width: 90,
borderRadius: '3px',
marginLeft: 'auto',
marginRight: 'auto',
textIndent: '-999999px',
overflow: 'hidden',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center center',
backgroundSize: 'contain',
};

View File

@@ -14,6 +14,19 @@ const Template: StoryFn<PaymentReceivedEmailTemplateProps> = (args) => (
);
export const Default: StoryFn<PaymentReceivedEmailTemplateProps> =
Template.bind({});
Template.bind({
message: `Hi Ahmed Bouhuolia,
Here's invoice # INV-00005 for $1,000.00
The amount outstanding of $1,000.00 is due on 10 Oct 2024.
From your online payment page you can print a PDF or view your outstanding bills.
If you have any questions, please let us know.
Thanks,
Bigcapital`,
});
Default.args = {};

View File

@@ -1,19 +1,39 @@
export interface PaymentReceivedEmailTemplateProps {
preview?: string;
import {
Button,
Container,
Heading,
Row,
Section,
Text,
} from '@react-email/components';
import { EmailTemplateLayout } from './EmailTemplateLayout';
import { CSSProperties } from 'react';
export interface PaymentReceivedEmailTemplateProps {
preview: string;
// # Company
companyName?: string;
companyLogoUri: string;
// # Color
primaryColor?: string;
// # Total
total: string;
totalLabel?: string;
// # Items
items: Array<{ label: string; quantity: string; rate: string }>;
viewEstimateButtonLabel?: string;
viewEstimateButtonUrl?: string;
// # View payment button
viewPaymentButtonLabel?: string;
viewPaymentButtonUrl?: string;
paymentNumberLabel?: string;
paymentNumber?: string;
message?: string;
}
export const PaymentReceivedEmailTemplate: React.FC<
@@ -22,19 +42,136 @@ export const PaymentReceivedEmailTemplate: React.FC<
preview,
// # Company
companyName,
companyName = 'Bigcapital, Inc.',
companyLogoUri,
// # Colors
primaryColor = 'rgb(0, 82, 204)',
// # invoice total
total,
// # Payment #
paymentNumberLabel = 'Payment # {paymentNumber}',
paymentNumber = 'PAY-00001',
// # Total
total = '$1,000.00',
totalLabel = 'Total',
// # Items
items,
message = '',
// # View invoice button
viewEstimateButtonLabel = 'View Estimate',
viewEstimateButtonUrl,
viewPaymentButtonLabel = 'View Payment',
viewPaymentButtonUrl,
}) => {
return <h1>asdasd</h1>;
return (
<EmailTemplateLayout preview={preview}>
<Container style={containerStyle}>
{companyLogoUri && (
<Section style={logoSectionStyle}>
<div
style={{
...companyLogoStyle,
backgroundImage: `url("${companyLogoUri}")`,
}}
></div>
</Section>
)}
<Section style={headerInfoStyle}>
<Row>
<Heading style={invoiceCompanyNameStyle}>{companyName}</Heading>
</Row>
<Row>
<Text style={paymentAmountStyle}>{total}</Text>
</Row>
<Row>
<Text style={paymentNumberStyle}>
{paymentNumberLabel?.replace('{paymentNumber}', paymentNumber)}
</Text>
</Row>
</Section>
<Text style={invoiceMessageStyle}>{message}</Text>
<Button
href={viewPaymentButtonUrl}
style={{
...viewInvoiceButtonStyle,
backgroundColor: primaryColor,
}}
>
{viewPaymentButtonLabel}
</Button>
</Container>
</EmailTemplateLayout>
);
};
const containerStyle: CSSProperties = {
backgroundColor: '#fff',
width: '100%',
maxWidth: '500px',
padding: '35px 25px',
color: '#000',
borderRadius: '5px',
};
const headerInfoStyle: CSSProperties = {
textAlign: 'center',
marginBottom: 20,
};
const mainSectionStyle: CSSProperties = {};
const paymentAmountStyle: CSSProperties = {
margin: 0,
color: '#383E47',
fontWeight: 500,
};
const paymentNumberStyle: CSSProperties = {
margin: 0,
fontSize: '13px',
color: '#404854',
};
const invoiceCompanyNameStyle: CSSProperties = {
margin: 0,
fontSize: '18px',
fontWeight: 500,
color: '#404854',
};
const viewInvoiceButtonStyle: CSSProperties = {
display: 'block',
cursor: 'pointer',
textAlign: 'center',
fontSize: 16,
padding: '10px 15px',
lineHeight: '1',
backgroundColor: 'rgb(0, 82, 204)',
color: '#fff',
borderRadius: '5px',
};
const invoiceMessageStyle: CSSProperties = {
whiteSpace: 'pre-line',
color: '#252A31',
margin: '0 0 20px 0',
lineHeight: '20px',
};
const logoSectionStyle = {
marginBottom: '15px',
};
const companyLogoStyle = {
height: 90,
width: 90,
borderRadius: '3px',
marginLeft: 'auto',
marginRight: 'auto',
textIndent: '-999999px',
overflow: 'hidden',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center center',
backgroundSize: 'contain',
};

View File

@@ -16,5 +16,16 @@ const Template: StoryFn<ReceiptEmailTemplateProps> = (args) => (
export const Default: StoryFn<ReceiptEmailTemplateProps> = Template.bind({});
Default.args = {
// Add default props here
message: `Hi Ahmed Bouhuolia,
Here's invoice # INV-00005 for $1,000.00
The amount outstanding of $1,000.00 is due on 10 Oct 2024.
From your online payment page you can print a PDF or view your outstanding bills.
If you have any questions, please let us know.
Thanks,
Bigcapital`,
};

View File

@@ -1,5 +1,18 @@
import {
Button,
Column,
Container,
Heading,
Row,
Section,
Text,
} from '@react-email/components';
import { CSSProperties } from 'react';
import { EmailTemplateLayout } from './EmailTemplateLayout';
import { EmailTemplate } from './EmailTemplate';
export interface ReceiptEmailTemplateProps {
preview?: string;
preview: string;
// # Company
companyName?: string;
@@ -12,11 +25,23 @@ export interface ReceiptEmailTemplateProps {
total: string;
totalLabel?: string;
// # Receipt #
receiptNumber?: string;
receiptNumberLabel?: string;
// # Items
items: Array<{ label: string; quantity: string; rate: string }>;
// # Subtotal
subtotal?: string;
subtotalLabel?: string;
// # View receipt button.
viewReceiptButtonLabel?: string;
viewReceiptButtonUrl?: string;
// # Message
message?: string;
}
export const ReceiptEmailTemplate: React.FC<
@@ -25,21 +50,186 @@ export const ReceiptEmailTemplate: React.FC<
preview,
// # Company
companyName,
companyName = 'Bigcapital, Inc.',
companyLogoUri,
// # Colors
primaryColor = 'rgb(0, 82, 204)',
// # Invoice total
total,
total = '$1,000.00',
totalLabel = 'Total',
// # Subtotal
subtotal = '$1,000.00',
subtotalLabel = 'Subtotal',
// # Receipt #
receiptNumberLabel = 'Receipt # {receiptNumber}',
receiptNumber = 'REC-00001',
// # View invoice button
viewReceiptButtonLabel = 'View Estimate',
viewReceiptButtonUrl,
// # Message
message = '',
// # Items
items = [{ label: 'Swaniawski Muller', quantity: '1', rate: '$1,000.00' }],
}) => {
return (
<h1>asdasd</h1>
)
<EmailTemplateLayout preview={preview}>
<EmailTemplate>
{companyLogoUri && <EmailTemplate.CompanyLogo src={companyLogoUri} />}
<Section style={headerInfoStyle}>
<Row>
<Heading style={invoiceCompanyNameStyle}>{companyName}</Heading>
</Row>
<Row>
<Text style={amountStyle}>{total}</Text>
</Row>
<Row>
<Text style={receiptNumberStyle}>
{receiptNumberLabel?.replace('{receiptNumber}', receiptNumber)}
</Text>
</Row>
</Section>
<Text style={messageStyle}>{message}</Text>
<Button
href={viewReceiptButtonUrl}
style={{
...viewInvoiceButtonStyle,
backgroundColor: primaryColor,
}}
>
{viewReceiptButtonLabel}
</Button>
<Section style={totalsSectionStyle}>
{items.map((item, index) => (
<Row key={index} style={itemLineRowStyle}>
<Column width={'50%'}>
<Text style={listItemLabelStyle}>{item.label}</Text>
</Column>
<Column width={'50%'}>
<Text style={listItemAmountStyle}>
{item.quantity} x {item.rate}
</Text>
</Column>
</Row>
))}
<Row style={totalLineRowStyle}>
<Column width={'50%'}>
<Text style={dueAmountLineItemLabelStyle}>{subtotalLabel}</Text>
</Column>
<Column width={'50%'}>
<Text style={dueAmountLineItemAmountStyle}>{subtotal}</Text>
</Column>
</Row>
<Row style={totalLineRowStyle}>
<Column width={'50%'}>
<Text style={totalLineItemLabelStyle}>{totalLabel}</Text>
</Column>
<Column width={'50%'}>
<Text style={totalLineItemAmountStyle}>{total}</Text>
</Column>
</Row>
</Section>
</EmailTemplate>
</EmailTemplateLayout>
);
};
const headerInfoStyle: CSSProperties = {
textAlign: 'center',
marginBottom: 20,
};
const amountStyle: CSSProperties = {
margin: 0,
color: '#383E47',
fontWeight: 500,
};
const receiptNumberStyle: CSSProperties = {
margin: 0,
fontSize: '13px',
color: '#404854',
};
const invoiceCompanyNameStyle: CSSProperties = {
margin: 0,
fontSize: '18px',
fontWeight: 500,
color: '#404854',
};
const viewInvoiceButtonStyle: CSSProperties = {
display: 'block',
cursor: 'pointer',
textAlign: 'center',
fontSize: 16,
padding: '10px 15px',
lineHeight: '1',
backgroundColor: 'rgb(0, 82, 204)',
color: '#fff',
borderRadius: '5px',
};
const listItemLabelStyle: CSSProperties = {
margin: 0,
};
const listItemAmountStyle: CSSProperties = {
margin: 0,
textAlign: 'right',
};
const messageStyle: CSSProperties = {
whiteSpace: 'pre-line',
color: '#000000',
margin: '0 0 20px 0',
lineHeight: '20px',
};
const totalLineRowStyle: CSSProperties = {
borderBottom: '1px solid #000',
height: 40,
};
const totalLineItemLabelStyle: CSSProperties = {
...listItemLabelStyle,
fontWeight: 500,
};
const totalLineItemAmountStyle: CSSProperties = {
...listItemAmountStyle,
fontWeight: 600,
};
const dueAmountLineItemLabelStyle: CSSProperties = {
...listItemLabelStyle,
fontWeight: 500,
};
const dueAmountLineItemAmountStyle: CSSProperties = {
...listItemAmountStyle,
fontWeight: 600,
};
const itemLineRowStyle: CSSProperties = {
borderBottom: '1px solid #D9D9D9',
height: 40,
};
const totalsSectionStyle = {
marginTop: '20px',
borderTop: '1px solid #D9D9D9',
};