From 8ac8a3be631a70c4b7037adb3906757e606e3a1c Mon Sep 17 00:00:00 2001 From: jagadeesh m <91142244+jagadeeshm2002@users.noreply.github.com> Date: Tue, 12 Nov 2024 14:37:57 +0530 Subject: [PATCH 1/2] =?UTF-8?q?feature:=20Add=20email=20templates=20for=20?= =?UTF-8?q?better=20email=20sending.=20(=F0=9F=9A=80=20Feature:=20Add=20em?= =?UTF-8?q?ail=20template=20for=20better=20email=20experience=20.=20#584)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env | 43 ++++ .env.example | 1 + src/components/email/BaseEmailHtml.tsx | 186 ++++++++++++++++++ src/components/email/BaseTable.tsx | 29 +++ src/components/email/CallToAction.tsx | 51 +++++ src/components/email/EmailBodyLogo.tsx | 93 +++++++++ src/components/email/EmailHead.tsx | 92 +++++++++ src/components/email/RawHtml.tsx | 6 + src/components/email/Row.tsx | 15 ++ src/components/email/index.ts | 2 + src/lib/email/emailManager.ts | 39 ++++ src/lib/email/emailSender.ts | 158 +++++++++++++++ src/lib/email/index.ts | 8 + src/lib/email/renderEmail.ts | 19 ++ .../templates/EmailVerificationEmail.tsx | 39 ++++ .../email/templates/ResetPasswordEmail.tsx | 37 ++++ src/lib/email/templates/WelcomeEmail.tsx | 27 +++ src/lib/email/templates/index.ts | 4 + src/lib/email/utils/error.ts | 19 ++ src/lib/email/utils/format.ts | 56 ++++++ src/lib/validators/email.validator.ts | 86 ++++++++ src/types/email.types.ts | 38 ++++ 22 files changed, 1048 insertions(+) create mode 100644 .env create mode 100644 src/components/email/BaseEmailHtml.tsx create mode 100644 src/components/email/BaseTable.tsx create mode 100644 src/components/email/CallToAction.tsx create mode 100644 src/components/email/EmailBodyLogo.tsx create mode 100644 src/components/email/EmailHead.tsx create mode 100644 src/components/email/RawHtml.tsx create mode 100644 src/components/email/Row.tsx create mode 100644 src/components/email/index.ts create mode 100644 src/lib/email/emailManager.ts create mode 100644 src/lib/email/emailSender.ts create mode 100644 src/lib/email/index.ts create mode 100644 src/lib/email/renderEmail.ts create mode 100644 src/lib/email/templates/EmailVerificationEmail.tsx create mode 100644 src/lib/email/templates/ResetPasswordEmail.tsx create mode 100644 src/lib/email/templates/WelcomeEmail.tsx create mode 100644 src/lib/email/templates/index.ts create mode 100644 src/lib/email/utils/error.ts create mode 100644 src/lib/email/utils/format.ts create mode 100644 src/lib/validators/email.validator.ts create mode 100644 src/types/email.types.ts diff --git a/.env b/.env new file mode 100644 index 00000000..5b27d4d5 --- /dev/null +++ b/.env @@ -0,0 +1,43 @@ +# +# Database +# +DATABASE_URL="postgres://postgres:9876@localhost:5432/postgres" +# +# AUTH +# +NEXTAUTH_SECRET="koXrQGB5TFD4KALDX4kAvnQ5RHHvAOIzB" +NEXTAUTH_URL="http://localhost:3000" + +NEXT_PUBLIC_BASE_URL="http://localhost:3000" + +# +# Bunny CDN +# +CDN_API_KEY="api-key" +CDN_BASE_UPLOAD_URL="https://sg.storage.bunnycdn.com/job-board/assets" +CDN_BASE_ACCESS_URL="https://job-board.b-cdn.net/assets" + +NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=maps-api-key + +# +# Email SMTP credentials +# +EMAIL_USER="hopejaga01@gmail.com" +EMAIL_PASSWORD="syhc qfxx ikcd daeq" +EMAIL_USERNAME="jagadeesh" +EMAIL_SERVICE=gmail +EMAIL_HOST=smtp.gmail.com +EMAIL_PORT=587 + +# +# Google OAuth credentials +# +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= + +# go to https://lightcast.io/open-skills and signup to recieve your credentials +LIGHTCAST_CLIENT_ID= +LIGHTCAST_CLIENT_SECRET= + +# To run the application in production environment / check the envs +# SKIP_ENV_CHECK=true npm run [replace with your script name] diff --git a/.env.example b/.env.example index 03efbc11..05a544c0 100644 --- a/.env.example +++ b/.env.example @@ -21,6 +21,7 @@ NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=maps-api-key # EMAIL_USER= # your email ex: vineetagarwal@gmail.com EMAIL_PASSWORD= +EMAIL_USERNAME="JOB BOARD" EMAIL_SERVICE=gmail EMAIL_HOST=smtp.gmail.com EMAIL_PORT=587 diff --git a/src/components/email/BaseEmailHtml.tsx b/src/components/email/BaseEmailHtml.tsx new file mode 100644 index 00000000..3e9e7538 --- /dev/null +++ b/src/components/email/BaseEmailHtml.tsx @@ -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 }) => ( + <> + + {props.children} + +); + +export const BaseEmailHtml = (props: { + children: React.ReactNode; + subject: string; +}) => { + return ( + + + +
+
`} + /> + + {/* Header with logo and platform name */} +
+ +
+ + {/* Main Content */} +
+
+ + + + {/* Close Conditional Comment */} + + + + + ); +}; + +export default EmailBodyLogo; diff --git a/src/components/email/EmailHead.tsx b/src/components/email/EmailHead.tsx new file mode 100644 index 00000000..410ced02 --- /dev/null +++ b/src/components/email/EmailHead.tsx @@ -0,0 +1,92 @@ +/* eslint-disable @next/next/no-head-element */ +import RawHtml from "./RawHtml"; + +const EmailHead = ({ title = "" }) => { + return ( + + {title} + `} + /> + + + + + `} + /> + + `} + /> + + + + + ); +}; + +export default EmailHead; \ No newline at end of file diff --git a/src/components/email/RawHtml.tsx b/src/components/email/RawHtml.tsx new file mode 100644 index 00000000..c76cff96 --- /dev/null +++ b/src/components/email/RawHtml.tsx @@ -0,0 +1,6 @@ +/** @see https://gist.github.com/zomars/4c366a0118a5b7fb391529ab1f27527a */ +const RawHtml = ({ html = '' }) => ( +
`} + /> +
+ +
+ + + + + + + + + + {/* Footer with logo, address, social links, and privacy policy */} +
+ + +

1234 Example Street, Suite 100, City, State, Zip

+ +

+ + Privacy Policy + +

+
+
+ + + + + + ); +}; diff --git a/src/components/email/BaseTable.tsx b/src/components/email/BaseTable.tsx new file mode 100644 index 00000000..12b937ba --- /dev/null +++ b/src/components/email/BaseTable.tsx @@ -0,0 +1,29 @@ +interface BaseTableProps extends Omit, HTMLTableElement>, "border"> { + align?: React.TableHTMLAttributes["align"]; + border?: React.TableHTMLAttributes["border"]; +} + +const BaseTable: React.FC = ({ children, align, border, ...props }) => ( +
`} + /> +
+ +
+
+ {props.children} +
+
+ {children} +
+); + +export default BaseTable; diff --git a/src/components/email/CallToAction.tsx b/src/components/email/CallToAction.tsx new file mode 100644 index 00000000..8b8c4405 --- /dev/null +++ b/src/components/email/CallToAction.tsx @@ -0,0 +1,51 @@ +import React from "react"; + +type Props = { + buttonText?: string; + buttonLink?: string; +}; + +export function CallToAction({ + buttonText = "Call to Action", + buttonLink = "#", +}: Props) { + return ( + + + + + + +
+ + {buttonText} + +
+ ); +} diff --git a/src/components/email/EmailBodyLogo.tsx b/src/components/email/EmailBodyLogo.tsx new file mode 100644 index 00000000..dee21919 --- /dev/null +++ b/src/components/email/EmailBodyLogo.tsx @@ -0,0 +1,93 @@ +import RawHtml from "./RawHtml"; +import Row from "./Row"; + +const CommentIE = ({ html = "" }) => ${html}`} />; + +const EmailBodyLogo = () => { + const imageUrl = `${process.env.NEXT_PUBLIC_SITE_URL}/main.png`; + + return ( +
+ +
+ {/* Conditional Comment for Outlook */} +
`} /> + +
+ +
+ + + + +
+ + Job Board Logo + + Job Board + + +
+