fix: form
This commit is contained in:
parent
c6507ad99e
commit
f47ec1a333
@ -1,47 +1,35 @@
|
||||
import { EmailTemplate } from "@/components/custom/ContactSender";
|
||||
import { Resend } from "resend";
|
||||
|
||||
const resend = new Resend(process.env.RESEND_API_KEY);
|
||||
import { supabase } from "@/lib/supabase";
|
||||
|
||||
export async function POST(request) {
|
||||
try {
|
||||
const formData = await request.json();
|
||||
try {
|
||||
const formData = await request.json();
|
||||
|
||||
// Send email using Resend
|
||||
const { data: resendData, error: resendError } = await resend.emails.send({
|
||||
from: "support@rankrunners.net",
|
||||
to: ["sales@rankrunners.net"],
|
||||
subject: "New Quotes From /contact",
|
||||
react: EmailTemplate({
|
||||
name: formData.name,
|
||||
emailAddress: formData.emailAddress,
|
||||
phoneNumber: formData.phoneNumber,
|
||||
subject: formData.subject,
|
||||
message: formData.message,
|
||||
}),
|
||||
});
|
||||
// Store form data in Supabase
|
||||
const { data: supabaseData, error: supabaseError } = await supabase
|
||||
.from("rankrunners-submission")
|
||||
.insert([
|
||||
{
|
||||
name: formData.name,
|
||||
email: formData.emailAddress,
|
||||
phone: formData.phoneNumber,
|
||||
subject: formData.subject,
|
||||
message: formData.message,
|
||||
},
|
||||
])
|
||||
.select();
|
||||
|
||||
if (resendError) {
|
||||
return Response.json({ error: resendError }, { status: 500 });
|
||||
}
|
||||
|
||||
const response = await fetch("https://cryagent.pythonanywhere.com/submitrankrunnerscontact", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ email: formData.emailAddress }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to submit to cryagent.pythonanywhere.com");
|
||||
}
|
||||
|
||||
const pythonAnywhereData = await response.json();
|
||||
|
||||
return Response.json({ resendData, pythonAnywhereData });
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
return Response.json({ error: error.message }, { status: 500 });
|
||||
if (supabaseError) {
|
||||
console.log("err", supabaseError);
|
||||
return Response.json({ error: supabaseError }, { status: 500 });
|
||||
}
|
||||
|
||||
return Response.json({
|
||||
supabaseData,
|
||||
message: "Form submitted successfully",
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Error:", error);
|
||||
return Response.json({ error: error.message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
53
app/api/schedule-send/(backup).js
Normal file
53
app/api/schedule-send/(backup).js
Normal file
@ -0,0 +1,53 @@
|
||||
// import { EmailTemplate } from "@/components/custom/ScheduleSender";
|
||||
// import { Resend } from "resend";
|
||||
|
||||
// const resend = new Resend(process.env.RESEND_API_KEY);
|
||||
|
||||
// export async function POST(request) {
|
||||
// try {
|
||||
// const formData = await request.json();
|
||||
|
||||
// const { data: resendData, error: resendError } = await resend.emails.send({
|
||||
// from: "support@rankrunners.net",
|
||||
// to: ["sales@rankrunners.net"],
|
||||
// subject: "New Quotes From /schedule",
|
||||
// react: EmailTemplate({
|
||||
// firstName: formData.firstName,
|
||||
// lastName: formData.lastName,
|
||||
// emailAddress: formData.emailAddress,
|
||||
// phoneNumber: formData.phoneNumber,
|
||||
// companyName: formData.companyName,
|
||||
// websiteUrl: formData.websiteUrl,
|
||||
// annualRevenue: formData.annualRevenue,
|
||||
// learnFrom: formData.learnFrom,
|
||||
// additionalInfo: formData.additionalInfo,
|
||||
// }),
|
||||
// });
|
||||
|
||||
// if (resendError) {
|
||||
// return Response.json({ error: resendError }, { status: 500 });
|
||||
// }
|
||||
|
||||
// const response = await fetch(
|
||||
// "https://cryagent.pythonanywhere.com/submitrankrunnersschedule",
|
||||
// {
|
||||
// method: "POST",
|
||||
// headers: {
|
||||
// "Content-Type": "application/json",
|
||||
// },
|
||||
// body: JSON.stringify({ email: formData.emailAddress }),
|
||||
// }
|
||||
// );
|
||||
|
||||
// if (!response.ok) {
|
||||
// throw new Error("Failed to submit to cryagent.pythonanywhere.com");
|
||||
// }
|
||||
|
||||
// const pythonAnywhereData = await response.json();
|
||||
|
||||
// return Response.json({ resendData, pythonAnywhereData });
|
||||
// } catch (error) {
|
||||
// console.error("Error:", error);
|
||||
// return Response.json({ error: error.message }, { status: 500 });
|
||||
// }
|
||||
// }
|
@ -1,50 +1,38 @@
|
||||
import { EmailTemplate } from "@/components/custom/ScheduleSender";
|
||||
import { Resend } from "resend";
|
||||
|
||||
const resend = new Resend(process.env.RESEND_API_KEY);
|
||||
import { supabase } from "@/lib/supabase";
|
||||
|
||||
export async function POST(request) {
|
||||
try {
|
||||
const formData = await request.json();
|
||||
try {
|
||||
const formData = await request.json();
|
||||
|
||||
const { data: resendData, error: resendError } = await resend.emails.send({
|
||||
from: "support@rankrunners.net",
|
||||
to: ["sales@rankrunners.net"],
|
||||
subject: "New Quotes From /schedule",
|
||||
react: EmailTemplate({
|
||||
firstName: formData.firstName,
|
||||
lastName: formData.lastName,
|
||||
emailAddress: formData.emailAddress,
|
||||
phoneNumber: formData.phoneNumber,
|
||||
companyName: formData.companyName,
|
||||
websiteUrl: formData.websiteUrl,
|
||||
annualRevenue: formData.annualRevenue,
|
||||
learnFrom: formData.learnFrom,
|
||||
additionalInfo: formData.additionalInfo,
|
||||
}),
|
||||
});
|
||||
// Store form data in Supabase
|
||||
const { data: supabaseData, error: supabaseError } = await supabase
|
||||
.from("rankrunners-schedules")
|
||||
.insert([
|
||||
{
|
||||
first_name: formData.firstName,
|
||||
last_name: formData.lastName,
|
||||
email: formData.emailAddress,
|
||||
phone: formData.phoneNumber,
|
||||
company_name: formData.companyName,
|
||||
web_url: formData.websiteUrl,
|
||||
revenue: formData.annualRevenue,
|
||||
learn_option: formData.learnFrom,
|
||||
additional_info: formData.additionalInfo,
|
||||
},
|
||||
])
|
||||
.select();
|
||||
|
||||
if (resendError) {
|
||||
return Response.json({ error: resendError }, { status: 500 });
|
||||
}
|
||||
|
||||
const response = await fetch("https://cryagent.pythonanywhere.com/submitrankrunnersschedule", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ email: formData.emailAddress }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to submit to cryagent.pythonanywhere.com");
|
||||
}
|
||||
|
||||
const pythonAnywhereData = await response.json();
|
||||
|
||||
return Response.json({ resendData, pythonAnywhereData });
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
return Response.json({ error: error.message }, { status: 500 });
|
||||
if (supabaseError) {
|
||||
console.log("err", supabaseError);
|
||||
return Response.json({ error: supabaseError }, { status: 500 });
|
||||
}
|
||||
|
||||
return Response.json({
|
||||
supabaseData,
|
||||
message: "Appointment Scheduled Successfully!",
|
||||
});
|
||||
} catch (error) {
|
||||
console.log("Error:", error);
|
||||
return Response.json({ error: error.message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
@ -3,73 +3,74 @@ import Link from "next/link";
|
||||
import Contacting from "@/components/custom/Contact";
|
||||
|
||||
export const metadata = {
|
||||
title: "Contact | RankRunners - SEO, Web Design & Digital Marketing Agency",
|
||||
description:
|
||||
"RankRunners provides enterprise level SEO, Web Design/Development, Digital Marketing and IT Management services for companies across all industries, regardless of scope or size. Contact support@rankrunners.net to get started today!",
|
||||
title: "Contact | RankRunners - SEO, Web Design & Digital Marketing Agency",
|
||||
description:
|
||||
"RankRunners provides enterprise level SEO, Web Design/Development, Digital Marketing and IT Management services for companies across all industries, regardless of scope or size. Contact support@rankrunners.net to get started today!",
|
||||
};
|
||||
|
||||
export default function Contact() {
|
||||
return (
|
||||
<>
|
||||
<Layout headerStyle={4} footerStyle={3} breadcrumbTitle="Contact Us">
|
||||
<div>
|
||||
<section className="contact__area">
|
||||
<div className="container">
|
||||
<div className="row mb-70">
|
||||
<div className="col-lg-5">
|
||||
<div className="contact__content">
|
||||
<div className="section-title mb-30">
|
||||
<span className="sub-title">
|
||||
GET <span className="title-color">IN TOUCH</span>
|
||||
</span>
|
||||
<h2 className="title">Let's Talk</h2>
|
||||
<p>
|
||||
Have a question about a service, pricing, or case study? Send us a
|
||||
message and we'll provide all the answers you need!
|
||||
</p>
|
||||
</div>
|
||||
<div className="contact__info">
|
||||
<ul className="list-wrap">
|
||||
<li>
|
||||
<div className="icon">
|
||||
<i className="flaticon-pin" />
|
||||
</div>
|
||||
<div className="content">
|
||||
<h4 className="title">Address</h4>
|
||||
<p>
|
||||
<Link
|
||||
href="https://maps.app.goo.gl/3vpv8Y9pSTtpcNGB6"
|
||||
target="_blank"
|
||||
>
|
||||
Suite B, 1934 N. Druid Hills Rd, Brookhaven, GA 30319,
|
||||
USA
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div className="icon">
|
||||
<i className="flaticon-phone-call" />
|
||||
</div>
|
||||
<div className="content">
|
||||
<h4 className="title">Phone</h4>
|
||||
<Link href="tel:+14702604117">(470) 260-4117</Link>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div className="icon">
|
||||
<i className="flaticon-mail" />
|
||||
</div>
|
||||
<div className="content">
|
||||
<h4 className="title">Email</h4>
|
||||
<Link href="mailto:support@rankrunners.net">
|
||||
support@rankrunners.net
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{/* <div className="contact__left mb-30">
|
||||
return (
|
||||
<>
|
||||
<Layout headerStyle={4} footerStyle={3} breadcrumbTitle="Contact Us">
|
||||
<div>
|
||||
<section className="contact__area">
|
||||
<div className="container">
|
||||
<div className="row mb-70">
|
||||
<div className="col-lg-5">
|
||||
<div className="contact__content">
|
||||
<div className="section-title mb-30">
|
||||
<span className="sub-title">
|
||||
GET <span className="title-color">IN TOUCH</span>
|
||||
</span>
|
||||
<h2 className="title">Let's Talk</h2>
|
||||
<p>
|
||||
Have a question about a service, pricing, or case study?
|
||||
Send us a message and we'll provide all the answers you
|
||||
need!
|
||||
</p>
|
||||
</div>
|
||||
<div className="contact__info">
|
||||
<ul className="list-wrap">
|
||||
<li>
|
||||
<div className="icon">
|
||||
<i className="flaticon-pin" />
|
||||
</div>
|
||||
<div className="content">
|
||||
<h4 className="title">Address</h4>
|
||||
<p>
|
||||
<Link
|
||||
href="https://maps.app.goo.gl/3vpv8Y9pSTtpcNGB6"
|
||||
target="_blank"
|
||||
>
|
||||
Suite B, 1934 N. Druid Hills Rd, Brookhaven, GA
|
||||
30319, USA
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div className="icon">
|
||||
<i className="flaticon-phone-call" />
|
||||
</div>
|
||||
<div className="content">
|
||||
<h4 className="title">Phone</h4>
|
||||
<Link href="tel:+14702604117">(470) 260-4117</Link>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div className="icon">
|
||||
<i className="flaticon-mail" />
|
||||
</div>
|
||||
<div className="content">
|
||||
<h4 className="title">Email</h4>
|
||||
<Link href="mailto:support@rankrunners.net">
|
||||
support@rankrunners.net
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{/* <div className="contact__left mb-30">
|
||||
<ul>
|
||||
<li>
|
||||
We'll increase your company website's organic search ranking and
|
||||
@ -88,27 +89,27 @@ export default function Contact() {
|
||||
<div className="section-title">
|
||||
<p>Are you ready to outrun your competitors?</p>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-lg-7">
|
||||
<Contacting />
|
||||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-lg-12">
|
||||
<div className="contact-map">
|
||||
<iframe
|
||||
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3105.090533713237!2d-84.34295258493827!3d33.83509628065662!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x88f507b69aa2e0e3:0x51ce91157771e43d!2sRankRunners!5e0!3m2!1sen!2sus!4v1685560274134!5m2!1sen!2sus"
|
||||
allowFullScreen
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
<div className="col-lg-7">
|
||||
<Contacting />
|
||||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-lg-12">
|
||||
<div className="contact-map">
|
||||
<iframe
|
||||
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d3105.090533713237!2d-84.34295258493827!3d33.83509628065662!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x88f507b69aa2e0e3:0x51ce91157771e43d!2sRankRunners!5e0!3m2!1sen!2sus!4v1685560274134!5m2!1sen!2sus"
|
||||
allowFullScreen
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -12,28 +12,30 @@ import "/public/assets/css/main.css";
|
||||
import "/public/assets/css/custom.css";
|
||||
import ScrollHandler from "@/components/custom/ScrollHandler";
|
||||
import PopUp from "@/components/custom/PopUp";
|
||||
import { ToastProvider } from "@/components/toast/Toast";
|
||||
|
||||
const inter = Inter({
|
||||
weight: ["300", "400", "500", "600", "700"],
|
||||
subsets: ["latin"],
|
||||
variable: "--tg-body-font-family",
|
||||
display: "swap",
|
||||
weight: ["300", "400", "500", "600", "700"],
|
||||
subsets: ["latin"],
|
||||
variable: "--tg-body-font-family",
|
||||
display: "swap",
|
||||
});
|
||||
const outfit = Outfit({
|
||||
weight: ["400", "500", "600", "700", "800", "900"],
|
||||
subsets: ["latin"],
|
||||
variable: "--tg-heading-font-family",
|
||||
display: "swap",
|
||||
weight: ["400", "500", "600", "700", "800", "900"],
|
||||
subsets: ["latin"],
|
||||
variable: "--tg-heading-font-family",
|
||||
display: "swap",
|
||||
});
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={`${inter.variable} ${outfit.variable}`}>
|
||||
{children}
|
||||
<PopUp />
|
||||
<ScrollHandler />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={`${inter.variable} ${outfit.variable}`}>
|
||||
{children}
|
||||
<ToastProvider />
|
||||
<PopUp />
|
||||
<ScrollHandler />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
@ -3,58 +3,63 @@ import Testimonials from "@/components/sections/Testimonials";
|
||||
import ScheduleChooser from "@/components/custom/ScheduleChooser";
|
||||
|
||||
export const metadata = {
|
||||
title: "Schedule a Call | RankRunners - SEO, Web Design & Digital Marketing Agency",
|
||||
description:
|
||||
"RankRunners provides enterprise level SEO, Web Design/Development, Digital Marketing and IT Management services for companies across all industries, regardless of scope or size. Contact support@rankrunners.net to get started today!",
|
||||
title:
|
||||
"Schedule a Call | RankRunners - SEO, Web Design & Digital Marketing Agency",
|
||||
description:
|
||||
"RankRunners provides enterprise level SEO, Web Design/Development, Digital Marketing and IT Management services for companies across all industries, regardless of scope or size. Contact support@rankrunners.net to get started today!",
|
||||
};
|
||||
|
||||
export default function Contact() {
|
||||
return (
|
||||
<>
|
||||
<Layout headerStyle={4} footerStyle={3} breadcrumbTitle="Schedule a Call">
|
||||
<div>
|
||||
<section className="contact__area-schedule">
|
||||
<div className="container">
|
||||
<div className="row mb-30">
|
||||
<div className="col-lg-5 schedule-content">
|
||||
<div className="contact__content">
|
||||
<div className="section-title mb-30">
|
||||
<h2 className="title">Schedule your 100% Free IT/SEO Strategy Call</h2>
|
||||
<p>
|
||||
Schedule a FREE consultation with our team of experts today and let us
|
||||
take your company's online presence to the next level!
|
||||
</p>
|
||||
</div>
|
||||
<div className="contact__left mb-30">
|
||||
<ul>
|
||||
<li>
|
||||
We'll increase your company website's organic search ranking and
|
||||
visibility in Google search
|
||||
</li>
|
||||
<li>
|
||||
This increased brand exposure will help attract and reel in
|
||||
thousands of brand new customers
|
||||
</li>
|
||||
<li>
|
||||
You will experience a higher bottom line, increased ROI and better
|
||||
position yourself for sustainable, organic growth
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="section-title">
|
||||
<p>Are you ready to outrun your competitors?</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-lg-7">
|
||||
<ScheduleChooser />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
return (
|
||||
<>
|
||||
<Layout headerStyle={4} footerStyle={3} breadcrumbTitle="Schedule a Call">
|
||||
<div>
|
||||
<section className="contact__area-schedule">
|
||||
<div className="container">
|
||||
<div className="row mb-30">
|
||||
<div className="col-lg-5 schedule-content">
|
||||
<div className="contact__content">
|
||||
<div className="section-title mb-30">
|
||||
<h2 className="title">
|
||||
Schedule your 100% Free IT/SEO Strategy Call
|
||||
</h2>
|
||||
<p>
|
||||
Schedule a FREE consultation with our team of experts
|
||||
today and let us take your company's online presence to
|
||||
the next level!
|
||||
</p>
|
||||
</div>
|
||||
<div className="contact__left mb-30">
|
||||
<ul>
|
||||
<li>
|
||||
We'll increase your company website's organic search
|
||||
ranking and visibility in Google search
|
||||
</li>
|
||||
<li>
|
||||
This increased brand exposure will help attract and
|
||||
reel in thousands of brand new customers
|
||||
</li>
|
||||
<li>
|
||||
You will experience a higher bottom line, increased
|
||||
ROI and better position yourself for sustainable,
|
||||
organic growth
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="section-title">
|
||||
<p>Are you ready to outrun your competitors?</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Testimonials />
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
<div className="col-lg-7">
|
||||
<ScheduleChooser />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<Testimonials />
|
||||
</Layout>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -2,131 +2,144 @@
|
||||
import React, { useState } from "react";
|
||||
import "react-phone-number-input/style.css";
|
||||
import PhoneInput from "react-phone-number-input";
|
||||
import { useToast } from "@/lib/toast-hook";
|
||||
|
||||
export default function ReportContent() {
|
||||
const [formData, setFormData] = useState({
|
||||
name: "",
|
||||
emailAddress: "",
|
||||
phoneNumber: "",
|
||||
subject: "",
|
||||
message: "",
|
||||
checkbox: false,
|
||||
});
|
||||
const { showLoading, dismissToast, showSuccess, showError } = useToast();
|
||||
const [formData, setFormData] = useState({
|
||||
name: "",
|
||||
emailAddress: "",
|
||||
phoneNumber: "",
|
||||
subject: "",
|
||||
message: "",
|
||||
checkbox: false,
|
||||
});
|
||||
|
||||
const handleChange = (e) => {
|
||||
const { name, value, type, checked } = e.target;
|
||||
setFormData((prevState) => ({
|
||||
...prevState,
|
||||
[name]: type === "checkbox" ? checked : value,
|
||||
}));
|
||||
};
|
||||
const handleChange = (e) => {
|
||||
const { name, value, type, checked } = e.target;
|
||||
setFormData((prevState) => ({
|
||||
...prevState,
|
||||
[name]: type === "checkbox" ? checked : value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handlePhoneChange = (value) => {
|
||||
setFormData((prevState) => ({
|
||||
...prevState,
|
||||
phoneNumber: value,
|
||||
}));
|
||||
};
|
||||
const handlePhoneChange = (value) => {
|
||||
setFormData((prevState) => ({
|
||||
...prevState,
|
||||
phoneNumber: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
const response = await fetch("/api/contact-send", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(formData),
|
||||
});
|
||||
const handleSubmit = async (e) => {
|
||||
const loadingToastId = showLoading("Submitting form...");
|
||||
e.preventDefault();
|
||||
try {
|
||||
const response = await fetch("/api/contact-send", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(formData),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
alert("Form submitted successfully!");
|
||||
document.cookie = `prospectClient=true; path=/; max-age=31536000`;
|
||||
} else {
|
||||
throw new Error("Form submission failed");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
alert("An error occurred while submitting the form.");
|
||||
}
|
||||
};
|
||||
if (response.ok) {
|
||||
dismissToast(loadingToastId);
|
||||
showSuccess("Your form has been successfully submitted.");
|
||||
document.cookie = `prospectClient=true; path=/; max-age=31536000`;
|
||||
} else {
|
||||
dismissToast(loadingToastId);
|
||||
showError(
|
||||
error instanceof Error ? error.message : "Something went wrong"
|
||||
);
|
||||
throw new Error("Form submission failed");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
dismissToast(loadingToastId);
|
||||
showError(
|
||||
error instanceof Error ? error.message : "Something went wrong"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="contact__form-wrap">
|
||||
<h2 className="title">Get In Touch</h2>
|
||||
<form id="contact-form" onSubmit={handleSubmit}>
|
||||
<div className="row">
|
||||
<div className="col-md-6">
|
||||
<div className="form-grp">
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
placeholder="Your Name"
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<div className="form-grp">
|
||||
<input
|
||||
type="text"
|
||||
name="emailAddress"
|
||||
placeholder="Email Address"
|
||||
value={formData.emailAddress}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<div className="form-grp">
|
||||
<PhoneInput
|
||||
international
|
||||
defaultCountry="US"
|
||||
value={formData.phoneNumber}
|
||||
onChange={handlePhoneChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<div className="form-grp">
|
||||
<input
|
||||
type="text"
|
||||
name="subject"
|
||||
placeholder="Subject"
|
||||
value={formData.subject}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-grp">
|
||||
<textarea
|
||||
name="message"
|
||||
placeholder="Write Message"
|
||||
value={formData.message}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="form-grp checkbox-grp">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="checkbox"
|
||||
id="checkbox"
|
||||
checked={formData.checkbox}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<label htmlFor="checkbox">Save my name, email and phone number for future messages</label>
|
||||
</div>
|
||||
<button type="submit" className="btn">
|
||||
Submit Request
|
||||
</button>
|
||||
</form>
|
||||
<p className="ajax-response mb-0" />
|
||||
return (
|
||||
<div className="contact__form-wrap">
|
||||
<h2 className="title">Get In Touch</h2>
|
||||
<form id="contact-form" onSubmit={handleSubmit}>
|
||||
<div className="row">
|
||||
<div className="col-md-6">
|
||||
<div className="form-grp">
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
placeholder="Your Name"
|
||||
value={formData.name}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<div className="form-grp">
|
||||
<input
|
||||
type="text"
|
||||
name="emailAddress"
|
||||
placeholder="Email Address"
|
||||
value={formData.emailAddress}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<div className="form-grp">
|
||||
<PhoneInput
|
||||
international
|
||||
defaultCountry="US"
|
||||
value={formData.phoneNumber}
|
||||
onChange={handlePhoneChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<div className="form-grp">
|
||||
<input
|
||||
type="text"
|
||||
name="subject"
|
||||
placeholder="Subject"
|
||||
value={formData.subject}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
<div className="form-grp">
|
||||
<textarea
|
||||
name="message"
|
||||
placeholder="Write Message"
|
||||
value={formData.message}
|
||||
onChange={handleChange}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="form-grp checkbox-grp">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="checkbox"
|
||||
id="checkbox"
|
||||
checked={formData.checkbox}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<label htmlFor="checkbox">
|
||||
Save my name, email and phone number for future messages
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" className="btn">
|
||||
Submit Request
|
||||
</button>
|
||||
</form>
|
||||
<p className="ajax-response mb-0" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -2,8 +2,10 @@
|
||||
import React, { useState } from "react";
|
||||
import "react-phone-number-input/style.css";
|
||||
import PhoneInput from "react-phone-number-input";
|
||||
import { useToast } from "@/lib/toast-hook";
|
||||
|
||||
export default function Schedule({ onFormSubmit }) {
|
||||
const { showLoading, dismissToast, showSuccess, showError } = useToast();
|
||||
const [formData, setFormData] = useState({
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
@ -33,6 +35,7 @@ export default function Schedule({ onFormSubmit }) {
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
const loadingToastId = showLoading("Submitting schedule request...");
|
||||
e.preventDefault();
|
||||
try {
|
||||
const response = await fetch("/api/schedule-send", {
|
||||
@ -43,15 +46,19 @@ export default function Schedule({ onFormSubmit }) {
|
||||
body: JSON.stringify(formData),
|
||||
});
|
||||
|
||||
dismissToast(loadingToastId);
|
||||
if (response.ok) {
|
||||
showSuccess("Appointment Scheduled Successfully!");
|
||||
document.cookie = `prospectClient=true; path=/; max-age=31536000`;
|
||||
onFormSubmit();
|
||||
} else {
|
||||
throw new Error("Form submission failed");
|
||||
showError("Something went wrong");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
alert("An error occurred while submitting the form.");
|
||||
dismissToast(loadingToastId);
|
||||
showError(
|
||||
error instanceof Error ? error.message : "Something went wrong"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -5,19 +5,27 @@ import ScheduleAfter from "@/components/custom/ScheduleAfter";
|
||||
import { getCookie, setCookie } from "cookies-next";
|
||||
|
||||
export default function ScheduleChooser() {
|
||||
const [showScheduleAfter, setShowScheduleAfter] = useState(false);
|
||||
const [showScheduleAfter, setShowScheduleAfter] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const scheduleAfterCookie = getCookie("scheduleAfter");
|
||||
if (scheduleAfterCookie) {
|
||||
setShowScheduleAfter(true);
|
||||
}
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
const scheduleAfterCookie = getCookie("scheduleAfter");
|
||||
if (scheduleAfterCookie) {
|
||||
setShowScheduleAfter(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleFormSubmit = () => {
|
||||
setShowScheduleAfter(true);
|
||||
setCookie("scheduleAfter", "true", { maxAge: 3600, path: "/schedule" });
|
||||
};
|
||||
const handleFormSubmit = () => {
|
||||
setShowScheduleAfter(true);
|
||||
setCookie("scheduleAfter", "true", { maxAge: 3600, path: "/schedule" });
|
||||
};
|
||||
|
||||
return <>{showScheduleAfter ? <ScheduleAfter /> : <Schedule onFormSubmit={handleFormSubmit} />}</>;
|
||||
return (
|
||||
<>
|
||||
{showScheduleAfter ? (
|
||||
<ScheduleAfter />
|
||||
) : (
|
||||
<Schedule onFormSubmit={handleFormSubmit} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
42
components/toast/Toast.jsx
Normal file
42
components/toast/Toast.jsx
Normal file
@ -0,0 +1,42 @@
|
||||
"use client";
|
||||
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
export const ToastProvider = () => {
|
||||
return (
|
||||
<Toaster
|
||||
position="top-center"
|
||||
gutter={12}
|
||||
containerStyle={{ margin: "8px" }}
|
||||
toastOptions={{
|
||||
success: {
|
||||
duration: 3000,
|
||||
style: {
|
||||
background: "#fff",
|
||||
color: "black",
|
||||
boxShadow:
|
||||
"0 3px 10px rgba(0, 0, 0, 0.1), 0 3px 3px rgba(0, 0, 0, 0.05)",
|
||||
},
|
||||
iconTheme: {
|
||||
primary: "#fff",
|
||||
secondary: "#4BB543",
|
||||
},
|
||||
},
|
||||
error: {
|
||||
duration: 4000,
|
||||
style: {
|
||||
background: "#FF3333",
|
||||
color: "#fff",
|
||||
},
|
||||
},
|
||||
loading: {
|
||||
duration: Infinity,
|
||||
style: {
|
||||
background: "#333",
|
||||
color: "#fff",
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
// import { createClient } from "@supabase/supabase-js";
|
||||
import { createClient } from "@supabase/supabase-js";
|
||||
|
||||
// const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
||||
// const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
|
||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
||||
const supabaseServiceKey = process.env.NEXT_SUPABASE_SERVICE_KEY;
|
||||
|
||||
// export const supabase = createClient(supabaseUrl, supabaseAnonKey);
|
||||
export const supabase = createClient(supabaseUrl, supabaseServiceKey);
|
||||
|
29
lib/toast-hook.js
Normal file
29
lib/toast-hook.js
Normal file
@ -0,0 +1,29 @@
|
||||
// lib/hooks/use-toast.ts
|
||||
"use client";
|
||||
|
||||
import { toast } from "react-hot-toast";
|
||||
|
||||
export const useToast = () => {
|
||||
const showSuccess = (message) => {
|
||||
toast.success(message);
|
||||
};
|
||||
|
||||
const showError = (message) => {
|
||||
toast.error(message);
|
||||
};
|
||||
|
||||
const showLoading = (message) => {
|
||||
return toast.loading(message);
|
||||
};
|
||||
|
||||
const dismissToast = (id) => {
|
||||
toast.dismiss(id);
|
||||
};
|
||||
|
||||
return {
|
||||
showSuccess,
|
||||
showError,
|
||||
showLoading,
|
||||
dismissToast,
|
||||
};
|
||||
};
|
27
package-lock.json
generated
27
package-lock.json
generated
@ -27,6 +27,7 @@
|
||||
"react-curved-text": "^3.0.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-google-recaptcha": "^3.1.0",
|
||||
"react-hot-toast": "^2.5.2",
|
||||
"react-modal-video": "^2.0.1",
|
||||
"react-phone-number-input": "^3.4.4",
|
||||
"resend": "^3.5.0",
|
||||
@ -2038,6 +2039,15 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/goober": {
|
||||
"version": "2.1.16",
|
||||
"resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz",
|
||||
"integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"csstype": "^3.0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||
@ -3715,6 +3725,23 @@
|
||||
"react": ">=16.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-hot-toast": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.5.2.tgz",
|
||||
"integrity": "sha512-Tun3BbCxzmXXM7C+NI4qiv6lT0uwGh4oAfeJyNOjYUejTsm35mK9iCaYLGv8cBz9L5YxZLx/2ii7zsIwPtPUdw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"csstype": "^3.1.3",
|
||||
"goober": "^2.1.16"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16",
|
||||
"react-dom": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
|
@ -30,6 +30,7 @@
|
||||
"react-curved-text": "^3.0.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-google-recaptcha": "^3.1.0",
|
||||
"react-hot-toast": "^2.5.2",
|
||||
"react-modal-video": "^2.0.1",
|
||||
"react-phone-number-input": "^3.4.4",
|
||||
"resend": "^3.5.0",
|
||||
|
Loading…
x
Reference in New Issue
Block a user