forked from UKSOURCE/hailearning.edu.vn
238 lines
7.8 KiB
TypeScript
238 lines
7.8 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect, FormEvent } from "react";
|
|
import { ContactData } from "./types";
|
|
import Breadcrumb from "../components/Breadcrumb";
|
|
|
|
export default function ContactPage() {
|
|
const [contactData, setContactData] = useState<ContactData | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [formData, setFormData] = useState({
|
|
name: "",
|
|
email: "",
|
|
phone: "",
|
|
address: "",
|
|
date: "",
|
|
message: "",
|
|
});
|
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
const [submitStatus, setSubmitStatus] = useState<{
|
|
type: "success" | "error" | null;
|
|
message: string;
|
|
}>({ type: null, message: "" });
|
|
|
|
const apiUrl = process.env.NEXT_PUBLIC_API_URL || "http://localhost:3001";
|
|
|
|
// Fetch contact data from CMS
|
|
useEffect(() => {
|
|
const fetchContactData = async () => {
|
|
try {
|
|
const response = await fetch(`${apiUrl}/api/contact`, { cache: 'no-store' });
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
setContactData(data);
|
|
}
|
|
} catch (error) {
|
|
console.error("Error fetching contact data:", error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
fetchContactData();
|
|
}, [apiUrl]);
|
|
|
|
const handleChange = (
|
|
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
|
) => {
|
|
const { name, value } = e.target;
|
|
setFormData((prev) => ({ ...prev, [name]: value }));
|
|
};
|
|
|
|
const handleSubmit = async (e: FormEvent) => {
|
|
e.preventDefault();
|
|
setIsSubmitting(true);
|
|
setSubmitStatus({ type: null, message: "" });
|
|
|
|
try {
|
|
const response = await fetch(`${apiUrl}/api/contact/submit`, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(formData),
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (response.ok && data.success) {
|
|
setSubmitStatus({
|
|
type: "success",
|
|
message: data.message || "Thank you! We will contact you soon.",
|
|
});
|
|
setFormData({
|
|
name: "",
|
|
email: "",
|
|
phone: "",
|
|
address: "",
|
|
date: "",
|
|
message: "",
|
|
});
|
|
} else {
|
|
setSubmitStatus({
|
|
type: "error",
|
|
message: data.error || "Something went wrong. Please try again.",
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error("Submit error:", error);
|
|
setSubmitStatus({
|
|
type: "error",
|
|
message: "Network error. Please check your connection and try again.",
|
|
});
|
|
} finally {
|
|
setIsSubmitting(false);
|
|
}
|
|
};
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="loading-wrapper" style={{ minHeight: "100vh", display: "flex", alignItems: "center", justifyContent: "center" }}>
|
|
<p>Loading...</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Default values if API fails
|
|
const hero = contactData?.hero || { title: "Contact Us", backgroundImage: "/assets/img/inner-page/breadcrumb.jpg" };
|
|
const contactCards = contactData?.contactCards || [];
|
|
const map = contactData?.map || { embedUrl: "" };
|
|
const form = contactData?.form || {
|
|
heading: "Send Us Message",
|
|
description: "Have questions? Send us a message today.",
|
|
fields: [],
|
|
submitButton: { text: "SEND MESSAGE", icon: "fa-solid fa-arrow-right", buttonClass: "theme-btn style-2" }
|
|
};
|
|
|
|
return (
|
|
<>
|
|
{/* Breadcrumb-Wrapper Section Start */}
|
|
<Breadcrumb title={hero.title} current={hero.title} />
|
|
|
|
{/* Contact Icon Section Start */}
|
|
<section className="contact-us-section-3 section-padding fix">
|
|
<div className="container">
|
|
<div className="row g-4">
|
|
{contactCards.map((card, index) => (
|
|
<div className="col-xl-4 col-lg-6 col-md-6" key={index}>
|
|
<div className="contact-icon-item">
|
|
<div className="icon">
|
|
<i className={card.iconType}></i>
|
|
</div>
|
|
<div className="content">
|
|
<p>{card.title}</p>
|
|
<h6>
|
|
{card.content.map((line, i) => (
|
|
<span key={i}>
|
|
{card.type === "email" ? (
|
|
<a href={`mailto:${line}`}>{line}</a>
|
|
) : card.type === "phone" ? (
|
|
<a href={`tel:${line.replace(/\s/g, "")}`}>{line}</a>
|
|
) : (
|
|
line
|
|
)}
|
|
{i < card.content.length - 1 && <br />}
|
|
</span>
|
|
))}
|
|
</h6>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Contact Section Start */}
|
|
<section className="contact-section-3 section-padding fix pt-0">
|
|
<div className="container">
|
|
<div className="contact-from-wrapper">
|
|
<h5 className="text-center">{form.heading}</h5>
|
|
<p className="text-center mt-3 mb-5">"{form.description}"</p>
|
|
|
|
{/* Status Message */}
|
|
{submitStatus.type && (
|
|
<div
|
|
className={`alert ${submitStatus.type === "success" ? "alert-success" : "alert-danger"
|
|
} text-center mb-4`}
|
|
>
|
|
{submitStatus.message}
|
|
</div>
|
|
)}
|
|
|
|
<div className="row g-4">
|
|
<div className="col-xl-12">
|
|
<form onSubmit={handleSubmit} className="contact-form-items">
|
|
<div className="row g-4">
|
|
{form.fields.map((field, index) => (
|
|
<div className={field.colClass || "col-lg-12"} key={index}>
|
|
<div className="form-clt">
|
|
<span>{field.label}{field.required && " *"}</span>
|
|
{field.type === "textarea" ? (
|
|
<textarea
|
|
name={field.name}
|
|
value={formData[field.name as keyof typeof formData] || ""}
|
|
onChange={handleChange}
|
|
placeholder={field.placeholder}
|
|
required={field.required}
|
|
></textarea>
|
|
) : (
|
|
<input
|
|
type={field.type}
|
|
name={field.name}
|
|
value={formData[field.name as keyof typeof formData] || ""}
|
|
onChange={handleChange}
|
|
placeholder={field.placeholder}
|
|
required={field.required}
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
))}
|
|
<div className="col-lg-4">
|
|
<button
|
|
type="submit"
|
|
className={form.submitButton.buttonClass}
|
|
disabled={isSubmitting}
|
|
>
|
|
{isSubmitting ? "SENDING..." : form.submitButton.text}
|
|
<i className={form.submitButton.icon}></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* Map Section Start */}
|
|
{map.embedUrl && (
|
|
<div className="map-section section-padding pt-0">
|
|
<div className="map-items">
|
|
<div className="googpemap">
|
|
<iframe
|
|
src={map.embedUrl}
|
|
style={{ border: 0 }}
|
|
allowFullScreen
|
|
loading="lazy"
|
|
></iframe>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
}
|