fix: form

This commit is contained in:
Val 2025-06-09 00:57:47 +07:00
parent c6507ad99e
commit f47ec1a333
14 changed files with 539 additions and 375 deletions

View File

@ -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 });
}
}

View 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 });
// }
// }

View File

@ -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 });
}
}

View File

@ -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>
</>
);
}

View File

@ -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>
);
}

View File

@ -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>
</>
);
}

View File

@ -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>
);
}

View File

@ -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"
);
}
};

View File

@ -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} />
)}
</>
);
}

View 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",
},
},
}}
/>
);
};

View File

@ -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
View 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
View File

@ -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",

View File

@ -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",