email working for order form

This commit is contained in:
Jacob Delgado 2024-08-19 13:26:10 -07:00
parent ab54e84130
commit 90fd8a0d73
19 changed files with 112 additions and 17 deletions

Binary file not shown.

View File

@ -10,13 +10,17 @@
}, },
"dependencies": { "dependencies": {
"@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-slot": "^1.1.0",
"@react-email/components": "^0.0.22",
"@react-email/tailwind": "^0.0.19",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"dotenv": "^16.4.5",
"lucide-react": "^0.427.0", "lucide-react": "^0.427.0",
"next": "14.1.4", "next": "14.1.4",
"nodemailer": "^6.9.14", "nodemailer": "^6.9.14",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"resend": "^3.5.0",
"sharp": "^0.33.4", "sharp": "^0.33.4",
"tailwind-merge": "^2.5.2", "tailwind-merge": "^2.5.2",
"tailwindcss-animate": "^1.0.7" "tailwindcss-animate": "^1.0.7"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 867 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 619 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 732 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 505 KiB

View File

@ -2,7 +2,7 @@
{ {
"id": 1, "id": 1,
"name": "design update", "name": "design update",
"photo": "batch1/design update.png", "photo": "batch1/t shirt mockup 3.jpg",
"price": 25, "price": 25,
"size": "Youth-S/M/L/2XL | Adult-S/M/L/2XL", "size": "Youth-S/M/L/2XL | Adult-S/M/L/2XL",
"inStock": "yes", "inStock": "yes",
@ -42,7 +42,7 @@
{ {
"id": 5, "id": 5,
"name": "design 6 update-01", "name": "design 6 update-01",
"photo": "batch1/design 6 update-01.png", "photo": "batch1/t shirt mockup 6.jpg",
"price": 25, "price": 25,
"size": "Youth-S/M/L/2XL | Adult-S/M/L/2XL", "size": "Youth-S/M/L/2XL | Adult-S/M/L/2XL",
"inStock": "yes", "inStock": "yes",
@ -72,7 +72,7 @@
{ {
"id": 9, "id": 9,
"name": "design 4-01", "name": "design 4-01",
"photo": "batch1/design 4-01.png", "photo": "batch1/t shirt mockup 4.jpg",
"price": 25, "price": 25,
"size": "Youth-S/M/L/2XL | Adult-S/M/L/2XL", "size": "Youth-S/M/L/2XL | Adult-S/M/L/2XL",
"inStock": "yes", "inStock": "yes",
@ -99,16 +99,6 @@
"favorite": "no", "favorite": "no",
"description": "It's not an option.." "description": "It's not an option.."
}, },
{
"id": 12,
"name": "75-01",
"photo": "batch1/75-01.png",
"price": 25,
"size": "Youth-S/M/L/2XL | Adult-S/M/L/2XL",
"inStock": "yes",
"favorite": "no",
"description": "It's not an option.."
},
{ {
"id": 13, "id": 13,
"name": "Mockup 119", "name": "Mockup 119",
@ -132,7 +122,7 @@
{ {
"id": 15, "id": 15,
"name": "10-01", "name": "10-01",
"photo": "batch1/10-01.png", "photo": "batch1/t shirt mockup 10.jpg",
"price": 25, "price": 25,
"size": "Youth-S/M/L/2XL | Adult-S/M/L/2XL", "size": "Youth-S/M/L/2XL | Adult-S/M/L/2XL",
"inStock": "yes", "inStock": "yes",
@ -142,7 +132,7 @@
{ {
"id": 16, "id": 16,
"name": "design 7 update-01", "name": "design 7 update-01",
"photo": "batch1/design 7 update-01.png", "photo": "batch1/t shirt mockup 7.jpg",
"price": 25, "price": 25,
"size": "Youth-S/M/L/2XL | Adult-S/M/L/2XL", "size": "Youth-S/M/L/2XL | Adult-S/M/L/2XL",
"inStock": "yes", "inStock": "yes",
@ -182,7 +172,7 @@
{ {
"id": 20, "id": 20,
"name": "design 8 update-01", "name": "design 8 update-01",
"photo": "batch1/design 8 update-01.png", "photo": "batch1/t shirt mockup 8.jpg",
"price": 25, "price": 25,
"size": "Youth-S/M/L/2XL | Adult-S/M/L/2XL", "size": "Youth-S/M/L/2XL | Adult-S/M/L/2XL",
"inStock": "yes", "inStock": "yes",

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 552 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 KiB

View File

@ -0,0 +1,44 @@
"use server"
import { Resend } from "resend"
import { validateString } from "@/lib/utils";
import OrderFormEmail from "../emailTemplates/order-form-email";
import React from "react";
import 'dotenv/config';
const resend = new Resend(process.env.RESEND_API_KEY);
export const sendEmail = async (formData: FormData) => {
//rate limit
//only allow authenticated user as well
const senderEmail = formData.get('senderEmail');
const message = formData.get('message');
if (!validateString(senderEmail, 500)){
return{ error: "Invalid sender Email",};
}
if (!validateString(message, 5000)){
return{ error: "Invalid message",};
}
// console.log("Running on sever");
// console.log(formData.get("senderEmail"));
// console.log(formData.get("message"));
try {
await resend.emails.send({
from:"Order Form <orders@kevosattire.com>",
to:"arcjcb11@gmail.com",
subject: "New Order",
reply_to: senderEmail as string,
// text: message as string,
react: React.createElement(OrderFormEmail, {message: message as string, senderEmail: senderEmail as string}),
});
} catch (error: unknown){
return console.log(error)
}
};

View File

@ -0,0 +1,39 @@
import React from "react";
import {
Html,
Body,
Head,
Heading,
Hr,
Container,
Preview,
Section,
Text,
} from "@react-email/components";
import { Tailwind } from "@react-email/tailwind";
type OrderFormEmailProps = {
message: string;
senderEmail: string;
};
export default function OrderFormEmail({ message, senderEmail }: OrderFormEmailProps) {
return (
<Html>
<Head />
<Preview>New order from kevo&apos;s attire.</Preview>
<Tailwind>
<Body className="bg-slate-700">
<Container>
<Section className="bg-white border-black my-10 px-10 py-4 rounded-md">
<Heading className="leading-tight">You received a new order from the order form.</Heading>
<Text>{message}</Text>
<Hr />
<Text>The sender&apos;s email is: {senderEmail}</Text>
</Section>
</Container>
</Body>
</Tailwind>
</Html>
);
}

View File

@ -8,6 +8,7 @@ import Modal from "../components/Modal/Modal";
import ClientPortal from "@/components/ClientPortal/ClientPortal"; import ClientPortal from "@/components/ClientPortal/ClientPortal";
import styles from "@/styles/Home.module.css"; import styles from "@/styles/Home.module.css";
import { Poppins } from "next/font/google"; import { Poppins } from "next/font/google";
import { sendEmail } from "./actions/sendEmail";
const Popp = Poppins({ weight: "400", subsets: ["latin"] }); const Popp = Poppins({ weight: "400", subsets: ["latin"] });
@ -40,6 +41,16 @@ export default function Home() {
</Modal> </Modal>
</div> */} </div> */}
<form className="mt-10 flex flex-col w-full items-center gap-4" action={async (formData) => {await sendEmail(formData)}}>
<h2 className="text-orange-600 text-2xl">Order Form</h2>
<input className="h-14 px-4 rounded-lg border-black bg-slate-600 lg:w-1/3 md:w-1/2 text-[#EEEEEE]" type="email" required maxLength={500} placeholder="Your email" name="senderEmail"/>
<textarea className="h-52 my-3 rounded-lg lg:w-1/3 md:w-1/2 bg-slate-600 p-4 text-[#EEEEEE]" name="message" placeholder="Your order" required maxLength={500}/>
<button className="w-[8rem] flex items-center justify-center gap-2 h-[3rem] bg-slate-600 rounded-full outline-none transition-all text-white hover:bg-slate-500 hover:scale-110 hover:focus:scale-110 active:scale-105" type="submit" value="Submit">Submit</button>
</form>
{/* Footer */} {/* Footer */}
<div className="flex w-full h-[250px] items-center text-gray-400"> <div className="flex w-full h-[250px] items-center text-gray-400">
<div className="font-bold "> <div className="font-bold ">
@ -47,7 +58,7 @@ export default function Home() {
<a href="mailto:kevosattire@gmail.com" className="text-emerald-500"> <a href="mailto:kevosattire@gmail.com" className="text-emerald-500">
kevosattire@gmail.com kevosattire@gmail.com
</a> </a>
&nbsp;to order! &nbsp;for any questions!
</div> </div>
<div></div> <div></div>
</div> </div>

View File

@ -4,3 +4,10 @@ import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)) return twMerge(clsx(inputs))
} }
export const validateString = (value: unknown, maxLength: number) => {
if(!value || typeof value !== "string" || value.length > maxLength){
return false;
}
return true;
}