Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🚀 Feature: Add email template for better email experience. #584 #585

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=maps-api-key
#
EMAIL_USER= # your email ex: [email protected]
EMAIL_PASSWORD=
EMAIL_USERNAME="JOB BOARD"
EMAIL_SERVICE=gmail
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
Expand Down
186 changes: 186 additions & 0 deletions src/components/email/BaseEmailHtml.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/* eslint-disable @next/next/no-head-element */
import BaseTable from './BaseTable';
import EmailBodyLogo from './EmailBodyLogo';
import EmailHead from './EmailHead';

import RawHtml from './RawHtml';
import Row from './Row';

const Html = (props: { children: React.ReactNode }) => (
<>
<RawHtml html="<!doctype html>" />
<html>{props.children}</html>
</>
);

export const BaseEmailHtml = (props: {
children: React.ReactNode;
subject: string;
}) => {
return (
<Html>
<EmailHead title={props.subject} />
<body style={{ wordSpacing: 'normal', backgroundColor: '#F3F4F6' }}>
<div style={{ backgroundColor: '#F3F4F6' }}>
<RawHtml
html={`<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->`}
/>

{/* Header with logo and platform name */}
<div
style={{
margin: '0px auto',
maxWidth: 600,
textAlign: 'center',
padding: '20px 0',
}}
>
<EmailBodyLogo />
</div>

{/* Main Content */}
<div
style={{
margin: '0px auto',
maxWidth: 600,
borderRadius: '8px',
border: '1px solid #E5E7EB',
padding: '2px',
backgroundColor: '#FFFFFF',
}}
>
<RawHtml
html={`<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" className="" style="width:600px;" width="600" bgcolor="#FFFFFF" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->`}
/>
<div
style={{
background: '#FFFFFF',
backgroundColor: '#FFFFFF',
margin: '0px auto',
maxWidth: 600,
}}
>
<Row
align="center"
border={0}
style={{
background: '#FFFFFF',
backgroundColor: '#FFFFFF',
width: '100%',
}}
>
<td
style={{
direction: 'ltr',
fontSize: 0,
padding: 0,
textAlign: 'center',
}}
>
<RawHtml
html={`<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td className="" style="vertical-align:top;width:598px;" ><![endif]-->`}
/>
<div
className="mj-column-per-100 mj-outlook-group-fix"
style={{
fontSize: 0,
textAlign: 'left',
direction: 'ltr',
display: 'inline-block',
verticalAlign: 'top',
width: '100%',
}}
>
<Row
border={0}
style={{ verticalAlign: 'top' }}
width="100%"
>
<td
align="center"
style={{
fontSize: 0,
padding: '10px 25px',
wordBreak: 'break-word',
}}
>
<div
style={{
fontFamily: 'Helvetica, Arial, sans-serif',
fontSize: 16,
fontWeight: 500,
lineHeight: 1.5,
textAlign: 'center',
color: '#101010',
backgroundColor: '#FFFFFF',
}}
>
{props.children}
</div>
</td>
</Row>
</div>
<RawHtml html="<!--[if mso | IE]></td></tr></table><![endif]-->" />
</td>
</Row>
</div>
</div>

{/* Footer with logo, address, social links, and privacy policy */}
<div
style={{
margin: '0px auto',
maxWidth: 600,
textAlign: 'center',
padding: '20px 0',
color: '#6B7280',
fontFamily: 'Helvetica, Arial, sans-serif',
}}
>
<EmailBodyLogo />
<BaseTable>
<p>1234 Example Street, Suite 100, City, State, Zip</p>
<div
style={{
display: 'flex',
justifyContent: 'center',
gap: '10px',
marginTop: '10px',
}}
>
<a
href="https://facebook.com"
style={{ color: '#6B7280', textDecoration: 'none' }}
>
Facebook
</a>
<a
href="https://twitter.com"
style={{ color: '#6B7280', textDecoration: 'none' }}
>
Twitter
</a>
<a
href="https://instagram.com"
style={{ color: '#6B7280', textDecoration: 'none' }}
>
Instagram
</a>
</div>
<p style={{ marginTop: '10px' }}>
<a
href="/privacy-policy"
style={{ color: '#6B7280', textDecoration: 'underline' }}
>
Privacy Policy
</a>
</p>
</BaseTable>
</div>

<RawHtml html="<!--[if mso | IE]></td></tr></table><![endif]-->" />
</div>
</body>
</Html>
);
};
29 changes: 29 additions & 0 deletions src/components/email/BaseTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
interface BaseTableProps extends Omit<React.DetailedHTMLProps<React.TableHTMLAttributes<HTMLTableElement>, HTMLTableElement>, "border"> {
align?: React.TableHTMLAttributes<HTMLTableElement>["align"];
border?: React.TableHTMLAttributes<HTMLTableElement>["border"];
}

const BaseTable: React.FC<BaseTableProps> = ({ children, align, border, ...props }) => (
<table
align={align || "center"}
cellSpacing="0"
border={border||0}
style={{
borderCollapse: "collapse",
borderSpacing: "0",
boxSizing: "border-box",
fontFamily: "helvetica, arial, sans-serif !important",
margin: "0",
padding: "0",
position: "relative",
textAlign: "left",
verticalAlign: "top",
width: "100%",
}}
{...props}
>
{children}
</table>
);

export default BaseTable;
51 changes: 51 additions & 0 deletions src/components/email/CallToAction.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from "react";

type Props = {
buttonText?: string;
buttonLink?: string;
};

export function CallToAction({
buttonText = "Call to Action",
buttonLink = "#",
}: Props) {
return (
<table
role="presentation"
border={0}
cellPadding="0"
cellSpacing="0"
style={{
margin: "20px auto",
textAlign: "center",
width: "100%",
}}
>
<tbody>
<tr>
<td align="center">
<a
href={buttonLink}
style={{
backgroundColor: "#0073e6",
color: "#ffffff",
fontFamily: "Helvetica, Arial, sans-serif",
fontSize: "16px",
fontWeight: "bold",
lineHeight: "1.5",
padding: "12px 24px",
textDecoration: "none",
borderRadius: "5px",
display: "inline-block",
width: "100%",
maxWidth: "200px",
}}
>
{buttonText}
</a>
</td>
</tr>
</tbody>
</table>
);
}
93 changes: 93 additions & 0 deletions src/components/email/EmailBodyLogo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import RawHtml from "./RawHtml";
import Row from "./Row";

const CommentIE = ({ html = "" }) => <RawHtml html={`<!--[if mso | IE]>${html}<![endif]-->`} />;

const EmailBodyLogo = () => {
const imageUrl = `${process.env.NEXT_PUBLIC_SITE_URL}/main.png`;

return (
<div style={{ margin: "0 auto", maxWidth: 600, width: "100%" }}>
<Row align="center" style={{ width: "100%" }}>
<td
style={{
textAlign: "center",
padding: "0",
direction: "ltr",
fontSize: "0px",
}}
>
{/* Conditional Comment for Outlook */}
<CommentIE html={`<table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td style="vertical-align:top;width:600px;">`} />

<div
style={{
fontSize: "0px",
textAlign: "center",
display: "inline-block",
width: "100%",
verticalAlign: "middle", // ensures horizontal centering
}}
>
<Row style={{ verticalAlign: "top", width: "100%" }}>
<td
align="center"
style={{
padding: "20px 0 32px", // Padding to balance layout visually
wordBreak: "break-word",
textAlign: "center", // Center content horizontally
}}
>
<table
role="presentation"
style={{
borderCollapse: "collapse",
borderSpacing: "0",
width: "100%",
textAlign: "center",
}}
>
<tr>
<td style={{ textAlign: "center" }}>
<a href="https://job.vineet.tech" style={{ textDecoration: "none", color: "#000000", display: "inline-flex", alignItems: "center", justifyContent: "center" }}>
<img
src={imageUrl}
alt="Job Board Logo"
width="30"
height="30"
style={{
display: "inline-block",
maxWidth: "100%",
border: "0",
outline: "none",
marginRight: "8px", // Spacing between logo and name
}}
/>
<span
style={{
fontSize: "22px",
fontWeight: "bold",
color: "#000000",
lineHeight: "1.2",
fontFamily: "Helvetica, Arial, sans-serif",
}}
>
Job Board
</span>
</a>
</td>
</tr>
</table>
</td>
</Row>
</div>

{/* Close Conditional Comment */}
<CommentIE html="</td></tr></table>" />
</td>
</Row>
</div>
);
};

export default EmailBodyLogo;
Loading