todo: mailer config
This commit is contained in:
@@ -1,40 +1,41 @@
|
||||
'use client';
|
||||
import { useState } from "react";
|
||||
import { contactFormSubmit, testMailerSDK } from "@/server/actions/mailer.actions"
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
export default function ContactPage() {
|
||||
const MESSAGE_LIMIT = 600;
|
||||
|
||||
const [name, setName] = useState('');
|
||||
const [email, setEmail] = useState('');
|
||||
const [message, setMessage] = useState('');
|
||||
|
||||
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||
e.preventDefault();
|
||||
console.log(name, email, message);
|
||||
}
|
||||
const characterCount = useMemo(() => message.length.toString(), [message]);
|
||||
|
||||
return (
|
||||
<div className="min-w-screen min-h-screen bg-gradient-to-b from-black to-darkPlum">
|
||||
<div className="flex flex-col mx-24 items-center">
|
||||
<h1 className="text-3xl my-8 place-self-start">Thanks for your interest! I'm looking forward to hearing from you.</h1>
|
||||
|
||||
<form onSubmit={handleSubmit} className="w-full">
|
||||
<form action={testMailerSDK} className="w-full">
|
||||
<div className="flex w-full">
|
||||
<div className="flex flex-col w-1/2 mr-2">
|
||||
<label htmlFor="name">Name</label>
|
||||
<input className="bg-neutral-100 px-1.5 py-1 text-black" value={name} onChange={(e) => setName(e.target.value)} type="text" name="name" id="name" />
|
||||
<input required className="bg-neutral-100 px-1.5 py-1 text-black" value={name} onChange={(e) => setName(e.target.value)} type="text" name="name" id="name" />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col w-1/2 ml-2">
|
||||
<label htmlFor="email">Email</label>
|
||||
<input className="bg-neutral-100 px-1.5 py-1 text-black" value={email} onChange={(e) => setEmail(e.target.value)} type="email" name="email" id="email" />
|
||||
<input required className="bg-neutral-100 px-1.5 py-1 text-black" value={email} onChange={(e) => setEmail(e.target.value)} type="email" name="email" id="email" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col w-full mt-4">
|
||||
<label htmlFor="message">Message</label>
|
||||
<textarea className="bg-neutral-100 px-1.5 py-1 text-black" value={message} onChange={(e) => setMessage(e.target.value)} name="message" id="message" cols={30} rows={5}></textarea>
|
||||
<textarea required className="bg-neutral-100 px-1.5 py-1 text-black" value={message} onChange={(e) => setMessage(e.target.value)} name="message" id="message" cols={30} rows={5}></textarea>
|
||||
<p className={"text-sm " + (parseInt(characterCount) > MESSAGE_LIMIT ? "text-red-500" : "text-white")}>{characterCount}/{MESSAGE_LIMIT}</p>
|
||||
</div>
|
||||
|
||||
<button className="p-2 px-8 mt-8 rounded-lg bg-rose-300 hover:bg-rose-400 text-black" type="submit">Send!</button>
|
||||
<button disabled={parseInt(characterCount) > MESSAGE_LIMIT} className="p-2 px-8 mt-8 rounded-lg bg-rose-300 hover:bg-rose-400 text-black" type="submit">Send!</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@ const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
experimental: {
|
||||
// mdxRs: true,
|
||||
// serverActions: true,
|
||||
serverActions: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
"@mdx-js/loader": "^2.3.0",
|
||||
"@mdx-js/react": "^2.3.0",
|
||||
"@next/mdx": "^13.4.4",
|
||||
"@sendgrid/mail": "^7.7.0",
|
||||
"@supabase/supabase-js": "^2.26.0",
|
||||
"autoprefixer": "10.4.14",
|
||||
"eslint": "^8.46.0",
|
||||
|
||||
34
server/actions/mailer.actions.ts
Normal file
34
server/actions/mailer.actions.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
'use server';
|
||||
import { Mailer } from "../services/sendgrid";
|
||||
|
||||
export async function contactFormSubmit(e: FormData) {
|
||||
const data = [e.get('email'), e.get('message'), e.get('name')].map(each => each?.valueOf());
|
||||
const mailer = new Mailer();
|
||||
|
||||
data.forEach(item => {
|
||||
if (typeof item !== 'string') throw new Error('Invalid form data')
|
||||
})
|
||||
|
||||
const result = await mailer.send(...data as [string, string, string]);
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function testMailerSDK(e: FormData) {
|
||||
const sgMail = require('@sendgrid/mail')
|
||||
sgMail.setApiKey(process.env.SENDGRID_API_KEY)
|
||||
const msg = {
|
||||
to: 'mikaylaherself@gmail.com', // Change to your recipient
|
||||
from: 'me@mikayla.dev', // Change to your verified sender
|
||||
subject: 'Sending with SendGrid is Fun',
|
||||
text: 'and easy to do anywhere, even with Node.js',
|
||||
html: '<strong>and easy to do anywhere, even with Node.js</strong>',
|
||||
}
|
||||
sgMail
|
||||
.send(msg)
|
||||
.then(() => {
|
||||
console.log('Email sent')
|
||||
})
|
||||
.catch((error: unknown) => {
|
||||
console.error(error)
|
||||
})
|
||||
}
|
||||
28
server/services/sendgrid.ts
Normal file
28
server/services/sendgrid.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import mailer, { ClientResponse, MailDataRequired, MailService } from "@sendgrid/mail";
|
||||
|
||||
export class Mailer {
|
||||
private mailer: MailService;
|
||||
|
||||
constructor() {
|
||||
const service = mailer;
|
||||
const key = process.env.SENDGRID_API_KEY;
|
||||
|
||||
if (!key) throw new Error("No SendGrid API key provided");
|
||||
|
||||
service.setApiKey(key);
|
||||
|
||||
this.mailer = service;
|
||||
}
|
||||
|
||||
public async send(from: string, text: string, name: string) {
|
||||
const data: MailDataRequired = {
|
||||
text, from,
|
||||
cc: from,
|
||||
to: 'hello@mikayla.dev',
|
||||
subject: `Contact form submission from ${name}`
|
||||
}
|
||||
const result = await this.mailer.send(data);
|
||||
return result[0] as ClientResponse;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user