From 146be05ccdb911119eefb821a3a8ccd446ce84ad Mon Sep 17 00:00:00 2001 From: phoenixpereira Date: Sat, 8 Feb 2025 11:24:35 +1030 Subject: [PATCH 1/9] feat(contact): Implement contact form submission and email notification --- .env.local.example | 1 + src/app/api/contact/route.ts | 26 +++++ src/app/contact/Form.tsx | 32 +++++- src/emails/contact.tsx | 174 +++++++++++++++++++++++++++++++ src/env.mjs | 3 +- src/server/send-contact-email.ts | 20 ++++ 6 files changed, 251 insertions(+), 5 deletions(-) create mode 100644 src/app/api/contact/route.ts create mode 100644 src/emails/contact.tsx create mode 100644 src/server/send-contact-email.ts diff --git a/.env.local.example b/.env.local.example index 7ee5004e..4f60e5e9 100644 --- a/.env.local.example +++ b/.env.local.example @@ -30,3 +30,4 @@ SMTP_HOST= SMTP_USER= SMTP_PASS= SMTP_EMAIL_ADDRESS= +CONTACT_EMAIL_ADDRESS= diff --git a/src/app/api/contact/route.ts b/src/app/api/contact/route.ts new file mode 100644 index 00000000..417273c8 --- /dev/null +++ b/src/app/api/contact/route.ts @@ -0,0 +1,26 @@ +import { sendContactEmail } from '@/server/send-contact-email'; +import { z } from 'zod'; + +export async function POST(request: Request) { + try { + const req = await request.json(); + + const schema = z.object({ + fullName: z.string().min(1), + email: z.string().email().min(1), + message: z.string().min(1), + }); + + const reqBody = schema.safeParse(req); + + if (!reqBody.success) { + return new Response(JSON.stringify(reqBody.error.format()), { status: 400 }); + } + + await sendContactEmail(reqBody.data.fullName, reqBody.data.email, reqBody.data.message); + + return new Response(JSON.stringify({ success: true }), { status: 200 }); + } catch { + return new Response(JSON.stringify({ error: 'Internal Server Error' }), { status: 500 }); + } +} diff --git a/src/app/contact/Form.tsx b/src/app/contact/Form.tsx index 594fddf7..ab487d0c 100644 --- a/src/app/contact/Form.tsx +++ b/src/app/contact/Form.tsx @@ -3,14 +3,31 @@ import Button from '@/components/Button'; import Duck from '@/components/Duck'; import { createSubmit } from 'just-submit'; +import { useState } from 'react'; export default function Form({ className }: { className?: string }) { + const [submitInfo, setSubmitInfo] = useState(''); + const [isSuccess, setIsSuccess] = useState(false); const submit = createSubmit({ fullName: 'string', email: 'string', message: 'string' }); - const handleSubmit = submit((data) => { - // TODO(#31): Email integration - console.log(data); + const handleSubmit = submit(async (data) => { + const response = await fetch('/api/contact', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }); + if (response.ok) { + setSubmitInfo('Message sent successfully!'); + setIsSuccess(true); + } else { + setSubmitInfo('Failed to send message. Please try again.'); + setIsSuccess(false); + } + setTimeout(() => { + setSubmitInfo(''); + }, 5000); }); - return (
@@ -49,6 +66,13 @@ export default function Form({ className }: { className?: string }) { Submit
+ {submitInfo && ( +
+ {submitInfo} +
+ )}
); diff --git a/src/emails/contact.tsx b/src/emails/contact.tsx new file mode 100644 index 00000000..aa14087d --- /dev/null +++ b/src/emails/contact.tsx @@ -0,0 +1,174 @@ +import { + Head, + Html, + Img, + Body, + Container, + Tailwind, + Section, + Row, + Column, +} from '@react-email/components'; +import * as React from 'react'; + +export interface EmailProps { + fullname: string; + email: string; + message: string; +} + +export default function Email({ fullname, email, message }: EmailProps) { + const currentYear = new Date().getFullYear(); + + interface IconSrcParams { + type: 'light' | 'dark'; + name: string; + } + + const getIconSrc = ({ type, name }: IconSrcParams): string => { + const lowerCaseName = name.toLowerCase(); + return `https://csclub.org.au/images/email/${type}/${lowerCaseName}.png`; + }; + + return ( + + + + + + + + + CS Club + + + + {/* Header */} + +
+ + + {/* Default Light Mode Logo */} + CS Club + {/* Dark Mode Logo */} + CS Club + + +
+
+ + {/* Email Content */} +
+ + + + + + + + + + + + + + + + +
+

+ Contact Us Form Submission +

+
+ +

+ Name +

+

{fullname}

+
+ +

+ Email +

+

{email}

+
+ +

+ Message +

+

{message}

+
+
+
+ + {/* Footer */} +
+

+ © {currentYear} The University of Adelaide Computer Science Club. +

+
+
+ + +
+ ); +} diff --git a/src/env.mjs b/src/env.mjs index 8a839a41..11f4b45d 100644 --- a/src/env.mjs +++ b/src/env.mjs @@ -16,7 +16,8 @@ export const env = createEnv({ SMTP_HOST: z.string().min(1).optional(), SMTP_USER: z.string().min(1).optional(), SMTP_PASS: z.string().min(1).optional(), - SMTP_EMAIL_ADDRESS: z.string().min(1).optional(), + SMTP_EMAIL_ADDRESS: z.string().min(1).email().optional(), + CONTACT_EMAIL_ADDRESS: z.string().min(1).email().optional(), }, client: { NEXT_PUBLIC_KEYCLOAK_REDIRECT_URI: z.string().url().min(1).optional(), diff --git a/src/server/send-contact-email.ts b/src/server/send-contact-email.ts new file mode 100644 index 00000000..efa45d70 --- /dev/null +++ b/src/server/send-contact-email.ts @@ -0,0 +1,20 @@ +import Email from '@/emails/contact'; +import { env } from '@/env.mjs'; +import { transporter } from '@/lib/email'; +import { render } from '@react-email/components'; +import React from 'react'; + +export const sendContactEmail = async (fullname: string, email: string, message: string) => { + const emailHtml = await render(React.createElement(Email, { fullname, email, message })); + + const options = { + from: `CS Club <${env.SMTP_EMAIL_ADDRESS}>`, + to: env.CONTACT_EMAIL_ADDRESS, + subject: 'Contact Us Form Submission', + html: emailHtml, + }; + + await transporter.sendMail(options); + + return; +}; From b8047dbd0963703478d9e27abe1f88f20cb365c5 Mon Sep 17 00:00:00 2001 From: phoenixpereira Date: Sun, 9 Feb 2025 11:57:20 +1030 Subject: [PATCH 2/9] fix(email): Fix dark mode text styling --- src/emails/contact.tsx | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/emails/contact.tsx b/src/emails/contact.tsx index aa14087d..48aea7ae 100644 --- a/src/emails/contact.tsx +++ b/src/emails/contact.tsx @@ -64,25 +64,25 @@ export default function Email({ fullname, email, message }: EmailProps) { /* Media query for dark mode */ .light { - display: inline !important; /* Show light mode by default */ - } - .dark { - display: none !important; /* Hide dark mode by default */ - } - .dynamicBorder { - border-color: #252020; - } + display: inline !important; /* Ensure it's hidden in dark mode */ + } + .dark { + display: none !important; /* Ensure dark logo is shown in dark mode */ + } + .dynamicBorder{ + border-color: #252020; + } @media (prefers-color-scheme: dark) { .light { - display: none !important; /* Hide light mode */ + display: none !important; /* Ensure it's hidden in dark mode */ } .dark { - display: inline !important; /* Show dark mode */ + display: inline !important; /* Ensure dark logo is shown in dark mode */ } - .dynamicBorder { + .dynamicBorder{ border-color: #F3F3EB; - } + } } `} @@ -126,24 +126,24 @@ export default function Email({ fullname, email, message }: EmailProps) { - -

+ +

Contact Us Form Submission

- +

Name

{fullname}

- +

Email

{email}

- +

Message

From d1466e002832d7d17a373b7834392622e359fabd Mon Sep 17 00:00:00 2001 From: phoenixpereira Date: Sun, 9 Feb 2025 12:58:10 +1030 Subject: [PATCH 3/9] fix(email): Fix copyright symbol rendering --- src/emails/contact.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/emails/contact.tsx b/src/emails/contact.tsx index 48aea7ae..2b72986f 100644 --- a/src/emails/contact.tsx +++ b/src/emails/contact.tsx @@ -127,10 +127,15 @@ export default function Email({ fullname, email, message }: EmailProps) { -

- Contact Us Form Submission -

-
+
+ +

+ Contact Us Form Submission +

+ {/* Apostrophe is required for the copyright symbol to be displayed properly due to issue with React Email */} +

+
+

Name From 99e8cc8be2f505d956f23bd6ed4e7dc5820f9f60 Mon Sep 17 00:00:00 2001 From: phoenixpereira Date: Sat, 15 Feb 2025 11:30:55 +1030 Subject: [PATCH 4/9] feat(email): Add Outlook (classic) support for contact email --- src/emails/contact.tsx | 209 +++++++++++++++++++++---------- src/server/send-contact-email.ts | 12 +- 2 files changed, 155 insertions(+), 66 deletions(-) diff --git a/src/emails/contact.tsx b/src/emails/contact.tsx index 2b72986f..a4729f03 100644 --- a/src/emails/contact.tsx +++ b/src/emails/contact.tsx @@ -11,6 +11,29 @@ import { } from '@react-email/components'; import * as React from 'react'; +interface HtmlConditionalCommentProps { + children: React.ReactNode; + comment: string; + msoOnly?: boolean; +} + +// Helper component for Outlook conditional rendering +function HtmlConditionalComment({ children, comment, msoOnly }: HtmlConditionalCommentProps) { + return msoOnly ? ( + <> +

+ {children} +
+ + ) : ( + <> +
+ {children} +
+ + ); +} + export interface EmailProps { fullname: string; email: string; @@ -26,8 +49,7 @@ export default function Email({ fullname, email, message }: EmailProps) { } const getIconSrc = ({ type, name }: IconSrcParams): string => { - const lowerCaseName = name.toLowerCase(); - return `https://csclub.org.au/images/email/${type}/${lowerCaseName}.png`; + return `https://csclub.org.au/images/email/${type}/${name.toLowerCase()}.png`; }; return ( @@ -38,14 +60,8 @@ export default function Email({ fullname, email, message }: EmailProps) { colors: { grey: '#252020', white: '#F3F3EB', - trueWhite: '#FFFFFF', - orange: '#E1652B', - yellow: '#FCC018', purple: '#7E7FE7', }, - backgroundImage: { - bg: "url('https://csclub.org.au/images/email/bg.png')", - }, fontFamily: { sans: ['Arial', 'sans-serif'], }, @@ -99,18 +115,28 @@ export default function Email({ fullname, email, message }: EmailProps) {
- {/* Default Light Mode Logo */} - CS Club - {/* Dark Mode Logo */} - CS Club + {/* Modern Email Clients */} + + CS Club + CS Club + + {/* Outlook (classic) */} + + CS Club +
@@ -118,51 +144,104 @@ export default function Email({ fullname, email, message }: EmailProps) { {/* Email Content */}
- - - - - - - - - - - - - - - - -
-
- -

- Contact Us Form Submission -

- {/* Apostrophe is required for the copyright symbol to be displayed properly due to issue with React Email */} -

-
-
- -

- Name -

-

{fullname}

-
- -

- Email -

-

{email}

-
- -

- Message -

-

{message}

-
-
+ {/* Modern Email Clients */} + + + + + + + + + + + + + + + + + +
+
+ +

+ Contact Us Form Submission +

+ {/* Apostrophe is required for the copyright symbol to be displayed properly due to issue with React Email */} +

+ ’ +

+
+
+ +

+ Name +

+

{fullname}

+
+ +

+ Email +

+

{email}

+
+ +

+ Message +

+

{message}

+
+
+
+ {/* Outlook (classic) */} + + + + + + + + + + + + + + + + + + + + +
+ +

+ Contact Us Form Submission +

+
+
+ +

+ Name +

+

{fullname}

+
+ +

+ Email +

+

{email}

+
+ +

+ Message +

+

{message}

+
+
+
{/* Footer */} diff --git a/src/server/send-contact-email.ts b/src/server/send-contact-email.ts index efa45d70..0d496820 100644 --- a/src/server/send-contact-email.ts +++ b/src/server/send-contact-email.ts @@ -4,14 +4,24 @@ import { transporter } from '@/lib/email'; import { render } from '@react-email/components'; import React from 'react'; +function renderComments(htmlString: string): string { + return htmlString + .replace(/<\/div>/g, '') + .replace(/<\/div>/g, '') + .replace(/<\/div>/g, ''); +} + export const sendContactEmail = async (fullname: string, email: string, message: string) => { const emailHtml = await render(React.createElement(Email, { fullname, email, message })); + const finalHtml = renderComments(emailHtml); + const options = { from: `CS Club <${env.SMTP_EMAIL_ADDRESS}>`, to: env.CONTACT_EMAIL_ADDRESS, subject: 'Contact Us Form Submission', - html: emailHtml, + html: finalHtml, }; await transporter.sendMail(options); From 3a081f80e3fb4c792d15e737c606d53b22cbb65a Mon Sep 17 00:00:00 2001 From: phoenixpereira Date: Tue, 25 Feb 2025 20:44:20 +1030 Subject: [PATCH 5/9] feat(email): Add HtmlConditionalComment component for Outlook rendering --- src/emails/HtmlConditionalComment.tsx | 28 +++ src/emails/body.tsx | 43 ++-- src/emails/contact.tsx | 24 +-- src/emails/outlookBody.tsx | 249 +++++++++++++++++++++ src/emails/socialIcon.tsx | 160 ++++++++++---- src/emails/welcome.tsx | 300 ++++++++++++++++---------- src/server/send-welcome-email.ts | 12 +- 7 files changed, 620 insertions(+), 196 deletions(-) create mode 100644 src/emails/HtmlConditionalComment.tsx create mode 100644 src/emails/outlookBody.tsx diff --git a/src/emails/HtmlConditionalComment.tsx b/src/emails/HtmlConditionalComment.tsx new file mode 100644 index 00000000..5c700026 --- /dev/null +++ b/src/emails/HtmlConditionalComment.tsx @@ -0,0 +1,28 @@ +import * as React from 'react'; + +interface HtmlConditionalCommentProps { + children: React.ReactNode; + comment: string; + msoOnly?: boolean; +} + +// Helper component for Outlook conditional rendering +export function HtmlConditionalComment({ + children, + comment, + msoOnly, +}: HtmlConditionalCommentProps) { + return msoOnly ? ( + <> +
+ {children} +
+ + ) : ( + <> +
+ {children} +
+ + ); +} diff --git a/src/emails/body.tsx b/src/emails/body.tsx index 226d7bb9..e5e34178 100644 --- a/src/emails/body.tsx +++ b/src/emails/body.tsx @@ -1,4 +1,4 @@ -import { Img } from '@react-email/components'; +import { Img, Container } from '@react-email/components'; import * as React from 'react'; export interface BodyProps { @@ -7,7 +7,7 @@ export interface BodyProps { export default function EmailBody({ firstName }: BodyProps) { return ( -
+ {'>'} -

Welcome, {firstName}!

+

{`Welcome, ${firstName}!`}

@@ -46,19 +46,19 @@ export default function EmailBody({ firstName }: BodyProps) {

The best way to keep up to date on what’s happening in our club is through our - Discord server, social media accounts, and our{' '} + Discord server, social media accounts, and our  website - ! If you haven’t seen a{' '} + ! If you haven’t seen a  committee member - {' '} - about your sign-up yet, please pop in to the Duck Lounge (Engineering and Maths - EM110) to get your cool ducky sticker for your uni ID card & verify your + +  about your sign-up yet, please pop in to the Duck Lounge (Engineering and + Maths EM110) to get your cool ducky sticker for your uni ID card & verify your membership payment!.

And of course, we hope to see you at our events and meet you very soon!

@@ -89,10 +89,10 @@ export default function EmailBody({ firstName }: BodyProps) {

There’s socialising, gaming, and of course if you have any questions with CS-related subjects, you’re bound to find someone - who can help you!{' '} + who can help you! 

- If you’re having trouble finding the room,{' '} + If you’re having trouble finding the room, 

- We hold Weekly Games Nights every{' '} + We hold Weekly Games Nights every  Friday in the Duck lounge! Our other events are always posted on our social media and in our Discord server, so be sure to be on a lookout for those announcements! @@ -192,14 +192,14 @@ export default function EmailBody({ firstName }: BodyProps) {

Our Club OneDrive consists of past exams, notes and other materials with Computer Science related subjects to help you fly - through exams! Log into our{' '} + through exams! Log into our  website - {' '} - to access or{' '} + +  to access or  Our Club OneDrive consists of past exams, notes and other materials with Computer Science related subjects to help you fly - through exams! Log into our{' '} + through exams! Log into our  website - {' '} - to access or{' '} + +  to access or  +

-
+ ); } diff --git a/src/emails/contact.tsx b/src/emails/contact.tsx index a4729f03..98e58dbd 100644 --- a/src/emails/contact.tsx +++ b/src/emails/contact.tsx @@ -10,29 +10,7 @@ import { Column, } from '@react-email/components'; import * as React from 'react'; - -interface HtmlConditionalCommentProps { - children: React.ReactNode; - comment: string; - msoOnly?: boolean; -} - -// Helper component for Outlook conditional rendering -function HtmlConditionalComment({ children, comment, msoOnly }: HtmlConditionalCommentProps) { - return msoOnly ? ( - <> -
- {children} -
- - ) : ( - <> -
- {children} -
- - ); -} +import { HtmlConditionalComment } from './HtmlConditionalComment'; export interface EmailProps { fullname: string; diff --git a/src/emails/outlookBody.tsx b/src/emails/outlookBody.tsx new file mode 100644 index 00000000..45c5b610 --- /dev/null +++ b/src/emails/outlookBody.tsx @@ -0,0 +1,249 @@ +import { Img, Container } from '@react-email/components'; +import * as React from 'react'; + +export interface BodyProps { + firstName: string; +} + +export default function OutlookEmailBody({ firstName }: BodyProps) { + return ( + + Welcome to the CS Club! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

{'>'}

+
+

{`Welcome, ${firstName}!`}

+
 
 
+

+ We host events all year round where you can connect with other + like-minded students, develop your skills and network with industry + professionals. As a member you’ll enjoy  + free or discounted entry to most of our events. Some + larger events, such as Industry Night, may have additional costs. Bring + a friend along, but keep in mind the $5 entry for non-members for most + events! +

+

+ The best way to keep up to date on what’s happening in our club is + through our Discord server, social media accounts, and our  + + website + + ! If you haven’t seen a  + + committee member + +  about your sign-up yet, please pop in to the Duck Lounge + (Engineering and Maths EM110) to get your cool ducky sticker for your + uni ID card & verify your membership payment!. +

+

+ And of course, we hope to see you at our events and meet you very soon! +

+
 
+

{'>'}

+
+

Where is the Duck Lounge?

+
 
 
+

+ What is the Duck Lounge? Where is the Duck Lounge? Who is the Duck + Lounge? More truer questions have never been asked. Our CS Club has its + very own student lounge in the Engineering Building in + room 110 (EM110). +

+

+ There’s socialising, gaming, and of course if you have any questions + with CS-related subjects, you’re bound to find someone who can help + you!  +

+

+ If you’re having trouble finding the room,  + + here's a map! + +

+
 
+ + + + + + +
+ Duck Lounge +
+ + + + + + + + + + + + +
 
 
+ + + + + + + + + + +
+ + + + + + +

{'+ Upcoming Events +'}

+ + + + + + + + + + +
 
  
  +

+ We hold Weekly Games Nights every  + Friday in the Duck lounge! Our other events + are always posted on our social media and in our Discord + server, so be sure to be on a lookout for those + announcements! +

+
 
 
+
 
+ + + + + + +

{'+ Club OneDrive +'}

+ + + + + + + + + + +
 
  
  +

+ Our Club OneDrive consists of past exams, notes and other + materials with Computer Science related subjects to help you + fly through exams! Log into our  + + website + +   to access or  + + click this link! + +

+
 
 
+
+ +
+

Contacts

+ + + + + + +
+

+ If you need any help, hop on Discord and DM any of the  + + committee + +  members, or... +

+ Send an email! +
+
+ ); +} diff --git a/src/emails/socialIcon.tsx b/src/emails/socialIcon.tsx index b395db16..0e01290d 100644 --- a/src/emails/socialIcon.tsx +++ b/src/emails/socialIcon.tsx @@ -3,51 +3,133 @@ import { Img } from '@react-email/components'; type SocialIconProps = { href: string; alt: string; - lightSrc: string; + lightSrc?: string; darkSrc: string; ariaLabel: string; }; function SocialIcon({ href, alt, lightSrc, darkSrc, ariaLabel }: SocialIconProps) { return ( - - - - - - - - - - - - - - - - - -
- -
- + <> + {lightSrc && ( + + + + + + + + + + + + + + + + + +
+ +
+ + )} + {!lightSrc && ( + + + + + + + + + + + + + + + + + + +
+ + {alt} + +
+ + )} + ); } diff --git a/src/emails/welcome.tsx b/src/emails/welcome.tsx index 08e83976..c9ef3cff 100644 --- a/src/emails/welcome.tsx +++ b/src/emails/welcome.tsx @@ -1,17 +1,19 @@ import { LINKS } from '@/data/links'; import { + Img, + Section, + Row, + Column, Head, Html, - Img, Body, Container, Tailwind, - Section, - Row, - Column, } from '@react-email/components'; import * as React from 'react'; +import { HtmlConditionalComment } from './HtmlConditionalComment'; import EmailBody from './body'; +import OutlookEmailBody from './outlookBody'; import SocialIcon from './socialIcon'; export interface EmailProps { @@ -99,118 +101,192 @@ export default function Email({ firstName }: EmailProps) { - {/* Header */} - -
- - - CS Club - CS Club - - -
-
+ + {/* Header */} + +
+ + + {/* Modern Email Clients */} + + CS Club + CS Club + + {/* Outlook (classic) */} + + CS Club +

 

+
+
+
+
+
- {/* Email Content */} -
- - - - - - - - - - - - - - - - -
- -
-
+ {/* Email Content */} +
+ {/* Modern Email Clients */} + + + + + + + + + + + + + + + + + +
+ +
+
+ {/* Outlook (classic) */} + + + + + + + + + + - {/* Footer */} -
-
-
+ +
- - {LINKS.map((link, index) => ( - - ))} - -
- - - {LINKS.slice(0, 4).map((link, index) => ( - - ))} - - - + + + + + + + +
+
+
- - {LINKS.slice(4).map((link, index) => ( - - ))} - - + {/* Footer */} +
+
+ + + {/* Modern Email Clients */} + + {LINKS.map((link, index) => ( + + ))} + + {/* Outlook (classic) */} + + {LINKS.map((link, index) => ( + + ))} + + +
+
+

+ © {currentYear} The University of Adelaide Computer Science + Club. +

-

- © {currentYear} The University of Adelaide Computer Science Club. -

-
+ diff --git a/src/server/send-welcome-email.ts b/src/server/send-welcome-email.ts index f19f6908..fb59d037 100644 --- a/src/server/send-welcome-email.ts +++ b/src/server/send-welcome-email.ts @@ -7,6 +7,14 @@ import { render } from '@react-email/components'; import { eq } from 'drizzle-orm'; import React from 'react'; +function renderComments(htmlString: string): string { + return htmlString + .replace(/<\/div>/g, '') + .replace(/<\/div>/g, '') + .replace(/<\/div>/g, ''); +} + export const sendWelcomeEmail = async ( keycloakId: string, recipientEmail: string, @@ -22,11 +30,13 @@ export const sendWelcomeEmail = async ( const emailHtml = await render(React.createElement(Email, { firstName })); + const finalHtml = renderComments(emailHtml); + const options = { from: `CS Club <${env.SMTP_EMAIL_ADDRESS}>`, to: recipientEmail, subject: 'Welcome to the CS Club!', - html: emailHtml, + html: finalHtml, }; await transporter.sendMail(options); From 6c0fb5f5bb1b5d3b5b71c82dc9c89d58d26eea4c Mon Sep 17 00:00:00 2001 From: phoenixpereira Date: Tue, 25 Feb 2025 20:53:33 +1030 Subject: [PATCH 6/9] feat(email): Add border for contact email in Outlook --- src/emails/contact.tsx | 145 +++++++++++++++++++++++++++++++---------- 1 file changed, 109 insertions(+), 36 deletions(-) diff --git a/src/emails/contact.tsx b/src/emails/contact.tsx index 98e58dbd..06071047 100644 --- a/src/emails/contact.tsx +++ b/src/emails/contact.tsx @@ -114,6 +114,7 @@ export default function Email({ fullname, email, message }: EmailProps) { alt="CS Club" className="mx-auto block max-h-[60px]" /> +

 

@@ -174,49 +175,121 @@ export default function Email({ fullname, email, message }: EmailProps) { {/* Outlook (classic) */} - +
- + + + + + + - - - - - - - - - - + + +
- -

- Contact Us Form Submission -

-
+ + + + + + + + + + + + + + + + + + + +
+ +

+ Contact Us Form Submission +

+
+
+ +

+ Name +

+

{fullname}

+
+ +

+ Email +

+

{email}

+
+ +

+ Message +

+

{message}

+
+
- -

- Name -

-

{fullname}

-
- -

- Email -

-

{email}

-
- -

- Message -

-

{message}

-
-
From d11ef4aa4c9eeb0267ea899b8ed8cd6b088bed6a Mon Sep 17 00:00:00 2001 From: phoenixpereira Date: Tue, 25 Feb 2025 22:07:08 +1030 Subject: [PATCH 7/9] chore(contact): Replace fetch with ky for API requests in contact form --- src/app/contact/Form.tsx | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/app/contact/Form.tsx b/src/app/contact/Form.tsx index ab487d0c..0b743148 100644 --- a/src/app/contact/Form.tsx +++ b/src/app/contact/Form.tsx @@ -3,6 +3,7 @@ import Button from '@/components/Button'; import Duck from '@/components/Duck'; import { createSubmit } from 'just-submit'; +import ky from 'ky'; import { useState } from 'react'; export default function Form({ className }: { className?: string }) { @@ -10,12 +11,8 @@ export default function Form({ className }: { className?: string }) { const [isSuccess, setIsSuccess] = useState(false); const submit = createSubmit({ fullName: 'string', email: 'string', message: 'string' }); const handleSubmit = submit(async (data) => { - const response = await fetch('/api/contact', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(data), + const response = await ky.post('/api/contact', { + json: data, }); if (response.ok) { setSubmitInfo('Message sent successfully!'); From 70ccd08ba2c1b5fb8b5cedcc6b88053400c590a6 Mon Sep 17 00:00:00 2001 From: phoenixpereira Date: Tue, 25 Feb 2025 22:11:15 +1030 Subject: [PATCH 8/9] refactor(emails): Use pascal case for email components --- src/emails/welcome.tsx | 6 +++--- src/server/send-contact-email.ts | 2 +- src/server/send-welcome-email.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/emails/welcome.tsx b/src/emails/welcome.tsx index c9ef3cff..57d51fff 100644 --- a/src/emails/welcome.tsx +++ b/src/emails/welcome.tsx @@ -11,10 +11,10 @@ import { Tailwind, } from '@react-email/components'; import * as React from 'react'; +import EmailBody from './Body'; import { HtmlConditionalComment } from './HtmlConditionalComment'; -import EmailBody from './body'; -import OutlookEmailBody from './outlookBody'; -import SocialIcon from './socialIcon'; +import OutlookEmailBody from './OutlookBody'; +import SocialIcon from './SocialIcon'; export interface EmailProps { firstName: string; diff --git a/src/server/send-contact-email.ts b/src/server/send-contact-email.ts index 0d496820..3825ff0c 100644 --- a/src/server/send-contact-email.ts +++ b/src/server/send-contact-email.ts @@ -1,4 +1,4 @@ -import Email from '@/emails/contact'; +import Email from '@/emails/Contact'; import { env } from '@/env.mjs'; import { transporter } from '@/lib/email'; import { render } from '@react-email/components'; diff --git a/src/server/send-welcome-email.ts b/src/server/send-welcome-email.ts index fb59d037..34d65226 100644 --- a/src/server/send-welcome-email.ts +++ b/src/server/send-welcome-email.ts @@ -1,6 +1,6 @@ import { db } from '@/db'; import { memberTable } from '@/db/schema'; -import Email from '@/emails/welcome'; +import Email from '@/emails/Welcome'; import { env } from '@/env.mjs'; import { transporter } from '@/lib/email'; import { render } from '@react-email/components'; From 7b19888cf3f5efae11944e6a9f77a4e5aeb5c6b6 Mon Sep 17 00:00:00 2001 From: phoenixpereira Date: Tue, 25 Feb 2025 22:27:32 +1030 Subject: [PATCH 9/9] refactor(emails): Use pascal case for email components --- src/emails/{body.tsx => Body.tsx} | 0 src/emails/{contact.tsx => Contact.tsx} | 0 src/emails/{outlookBody.tsx => OutlookBody.tsx} | 0 src/emails/{socialIcon.tsx => SocialIcon.tsx} | 0 src/emails/{welcome.tsx => Welcome.tsx} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename src/emails/{body.tsx => Body.tsx} (100%) rename src/emails/{contact.tsx => Contact.tsx} (100%) rename src/emails/{outlookBody.tsx => OutlookBody.tsx} (100%) rename src/emails/{socialIcon.tsx => SocialIcon.tsx} (100%) rename src/emails/{welcome.tsx => Welcome.tsx} (100%) diff --git a/src/emails/body.tsx b/src/emails/Body.tsx similarity index 100% rename from src/emails/body.tsx rename to src/emails/Body.tsx diff --git a/src/emails/contact.tsx b/src/emails/Contact.tsx similarity index 100% rename from src/emails/contact.tsx rename to src/emails/Contact.tsx diff --git a/src/emails/outlookBody.tsx b/src/emails/OutlookBody.tsx similarity index 100% rename from src/emails/outlookBody.tsx rename to src/emails/OutlookBody.tsx diff --git a/src/emails/socialIcon.tsx b/src/emails/SocialIcon.tsx similarity index 100% rename from src/emails/socialIcon.tsx rename to src/emails/SocialIcon.tsx diff --git a/src/emails/welcome.tsx b/src/emails/Welcome.tsx similarity index 100% rename from src/emails/welcome.tsx rename to src/emails/Welcome.tsx