From ddffe630ff6bef8bcad993f0c5ae2b7ef26c23cc Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Tue, 19 Nov 2024 14:00:25 +0200 Subject: [PATCH] feat: email templates --- .../lib/CreditNoteEmailTemplate.stories.tsx | 12 ++ .../src/lib/CreditNoteEmailTemplate.tsx | 202 +++++++++++++++++- .../src/lib/EmailTemplate.tsx | 48 +++++ .../src/lib/EstimatePaymentEmail.tsx | 43 +--- .../src/lib/InvoicePaymentEmail.tsx | 38 +--- .../PaymentReceivedEmailTemplate.stories.tsx | 15 +- .../src/lib/PaymentReceivedEmailTemplate.tsx | 157 +++++++++++++- .../src/lib/ReceiptPaymentEmail.stories.tsx | 13 +- .../src/lib/ReceiptPaymentEmail.tsx | 200 ++++++++++++++++- 9 files changed, 629 insertions(+), 99 deletions(-) create mode 100644 shared/email-components/src/lib/EmailTemplate.tsx diff --git a/shared/email-components/src/lib/CreditNoteEmailTemplate.stories.tsx b/shared/email-components/src/lib/CreditNoteEmailTemplate.stories.tsx index 9ee681c5c..04e55d40e 100644 --- a/shared/email-components/src/lib/CreditNoteEmailTemplate.stories.tsx +++ b/shared/email-components/src/lib/CreditNoteEmailTemplate.stories.tsx @@ -18,4 +18,16 @@ export const Default: StoryFn = 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`, }; diff --git a/shared/email-components/src/lib/CreditNoteEmailTemplate.tsx b/shared/email-components/src/lib/CreditNoteEmailTemplate.tsx index 52024b21c..898b5c1c3 100644 --- a/shared/email-components/src/lib/CreditNoteEmailTemplate.tsx +++ b/shared/email-components/src/lib/CreditNoteEmailTemplate.tsx @@ -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

asdasd

; + return ( + + +
+ {companyLogoUri && } + +
+ + {companyName} + + + {total} + + + + {creditNoteNumberLabel?.replace( + '{creditNoteNumber}', + creditNoteNumber + )} + + +
+ + {message} + + +
+ {items.map((item, index) => ( + + + {item.label} + + + + + {item.quantity} x {item.rate} + + + + ))} + + + + {totalLabel} + + + + {total} + + +
+
+
+
+ ); }; + +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', +}; diff --git a/shared/email-components/src/lib/EmailTemplate.tsx b/shared/email-components/src/lib/EmailTemplate.tsx new file mode 100644 index 000000000..f12a7442e --- /dev/null +++ b/shared/email-components/src/lib/EmailTemplate.tsx @@ -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 {children}; +} + +EmailTemplate.CompanyLogo = ({ src }: { src: string }) => { + return ( +
+
+
+ ); +}; + +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', +}; diff --git a/shared/email-components/src/lib/EstimatePaymentEmail.tsx b/shared/email-components/src/lib/EstimatePaymentEmail.tsx index 50572bf3e..99ef54186 100644 --- a/shared/email-components/src/lib/EstimatePaymentEmail.tsx +++ b/shared/email-components/src/lib/EstimatePaymentEmail.tsx @@ -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 ( - - {companyLogoUri && ( -
-
-
- )} + + {companyLogoUri && }
@@ -146,21 +138,11 @@ export const EstimatePaymentEmail: React.FC<
- -
+
); }; -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', -}; diff --git a/shared/email-components/src/lib/InvoicePaymentEmail.tsx b/shared/email-components/src/lib/InvoicePaymentEmail.tsx index e46e57949..1e871dcc1 100644 --- a/shared/email-components/src/lib/InvoicePaymentEmail.tsx +++ b/shared/email-components/src/lib/InvoicePaymentEmail.tsx @@ -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 ( - +
- {companyLogoUri && ( -
-
-
- )} + {companyLogoUri && }
@@ -179,7 +166,7 @@ export const InvoicePaymentEmail: React.FC<
-
+
); }; @@ -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', -}; diff --git a/shared/email-components/src/lib/PaymentReceivedEmailTemplate.stories.tsx b/shared/email-components/src/lib/PaymentReceivedEmailTemplate.stories.tsx index 3b2cddc7b..a763c2862 100644 --- a/shared/email-components/src/lib/PaymentReceivedEmailTemplate.stories.tsx +++ b/shared/email-components/src/lib/PaymentReceivedEmailTemplate.stories.tsx @@ -14,6 +14,19 @@ const Template: StoryFn = (args) => ( ); export const Default: StoryFn = - 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 = {}; diff --git a/shared/email-components/src/lib/PaymentReceivedEmailTemplate.tsx b/shared/email-components/src/lib/PaymentReceivedEmailTemplate.tsx index d2c6c7daf..0c4eb6217 100644 --- a/shared/email-components/src/lib/PaymentReceivedEmailTemplate.tsx +++ b/shared/email-components/src/lib/PaymentReceivedEmailTemplate.tsx @@ -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

asdasd

; + return ( + + + {companyLogoUri && ( +
+
+
+ )} +
+ + {companyName} + + + {total} + + + + {paymentNumberLabel?.replace('{paymentNumber}', paymentNumber)} + + +
+ + {message} + +
+
+ ); }; + +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', +}; diff --git a/shared/email-components/src/lib/ReceiptPaymentEmail.stories.tsx b/shared/email-components/src/lib/ReceiptPaymentEmail.stories.tsx index d1ae99df5..b68d4ab3b 100644 --- a/shared/email-components/src/lib/ReceiptPaymentEmail.stories.tsx +++ b/shared/email-components/src/lib/ReceiptPaymentEmail.stories.tsx @@ -16,5 +16,16 @@ const Template: StoryFn = (args) => ( export const Default: StoryFn = 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`, }; diff --git a/shared/email-components/src/lib/ReceiptPaymentEmail.tsx b/shared/email-components/src/lib/ReceiptPaymentEmail.tsx index 0b633af9f..a97e34799 100644 --- a/shared/email-components/src/lib/ReceiptPaymentEmail.tsx +++ b/shared/email-components/src/lib/ReceiptPaymentEmail.tsx @@ -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 ( -

asdasd

- ) + + + {companyLogoUri && } + +
+ + {companyName} + + + {total} + + + + {receiptNumberLabel?.replace('{receiptNumber}', receiptNumber)} + + +
+ + {message} + + +
+ {items.map((item, index) => ( + + + {item.label} + + + + + {item.quantity} x {item.rate} + + + + ))} + + + + {subtotalLabel} + + + + {subtotal} + + + + + + {totalLabel} + + + + {total} + + +
+
+
+ ); }; + +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', +};