connetcted to backend: form request

This commit is contained in:
nabijonovdavronbek619@gmail.com
2026-02-05 11:02:57 +05:00
parent ca3e28779e
commit 3c862ea104
13 changed files with 458 additions and 90 deletions

View File

@@ -3,21 +3,26 @@
import { Check } from "lucide-react";
import { useTranslations } from "next-intl";
import { useState } from "react";
import { useMutation } from "@tanstack/react-query";
import axios from "axios";
import { toast } from "react-toastify";
import httpClient from "@/request/api";
import { endPoints } from "@/request/links";
interface FormData {
firstName: string;
lastName: string;
email: string;
subject: string;
name: string;
surname: string;
address: string;
theme: string;
message: string;
agreeToPolicy: boolean;
}
interface FormErrors {
firstName?: string;
lastName?: string;
email?: string;
subject?: string;
name?: string;
surname?: string;
address?: string;
theme?: string;
message?: string;
agreeToPolicy?: string;
}
@@ -25,10 +30,10 @@ interface FormErrors {
export default function Form() {
const t = useTranslations();
const [formData, setFormData] = useState<FormData>({
firstName: "",
lastName: "",
email: "",
subject: "",
name: "",
surname: "",
address: "",
theme: "",
message: "",
agreeToPolicy: false,
});
@@ -38,22 +43,48 @@ export default function Form() {
"idle" | "success" | "error"
>("idle");
const formRequest = useMutation({
mutationKey: [],
mutationFn: (data: FormData) => httpClient.post(endPoints.post.contact, data),
onSuccess: () => {
setSubmitStatus("success");
setFormData({
name: "",
surname: "",
address: "",
theme: "",
message: "",
agreeToPolicy: false,
});
setIsSubmitting(false);
toast.success(t("succes"));
setTimeout(() => setSubmitStatus("idle"), 3000);
},
onError: (error) => {
console.log("error: ", error);
setIsSubmitting(false);
setSubmitStatus("error");
toast.error(t("error"));
setTimeout(() => setSubmitStatus("idle"), 3000);
},
});
const validateForm = (): boolean => {
const newErrors: FormErrors = {};
if (!formData.firstName.trim()) {
newErrors.firstName = "First name is required";
if (!formData.name.trim()) {
newErrors.name = "First name is required";
}
if (!formData.lastName.trim()) {
newErrors.lastName = "Last name is required";
if (!formData.surname.trim()) {
newErrors.surname = "Last name is required";
}
if (!formData.email.trim()) {
newErrors.email = "Email is required";
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
newErrors.email = "Please enter a valid email";
if (!formData.address.trim()) {
newErrors.address = "address is required";
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.address)) {
newErrors.address = "Please enter a valid address";
}
if (!formData.subject.trim()) {
newErrors.subject = "Subject is required";
if (!formData.theme.trim()) {
newErrors.theme = "theme is required";
}
if (!formData.message.trim()) {
newErrors.message = "Message is required";
@@ -90,32 +121,7 @@ export default function Form() {
setIsSubmitting(true);
setSubmitStatus("idle");
try {
const response = await fetch("/api/contact", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(formData),
});
if (response.ok) {
setSubmitStatus("success");
setFormData({
firstName: "",
lastName: "",
email: "",
subject: "",
message: "",
agreeToPolicy: false,
});
} else {
setSubmitStatus("error");
}
} catch {
setSubmitStatus("error");
} finally {
setIsSubmitting(false);
}
formRequest.mutate(formData);
};
return (
@@ -125,66 +131,74 @@ export default function Form() {
<div>
<input
type="text"
name="firstName"
name="name"
placeholder={t("contact.form.placeholders.firstName")}
value={formData.firstName}
value={formData.name}
onChange={handleChange}
className={`font-almarai w-full rounded-3xl border bg-white px-4 py-3 text-sm
text-gray-700 placeholder-gray-400 outline-none transition focus:ring-2 focus:ring-red-500 ${
errors.firstName ? "border-red-500" : "border-transparent"
}`}
errors.name ? "border-red-500" : "border-transparent"
}`}
/>
{errors.firstName && (
<p className="font-almarai mt-1 text-xs text-red-500">{errors.firstName}</p>
{errors.name && (
<p className="font-almarai mt-1 text-xs text-red-500">
{errors.name}
</p>
)}
</div>
<div>
<input
type="text"
name="lastName"
name="surname"
placeholder={t("contact.form.placeholders.lastName")}
value={formData.lastName}
value={formData.surname}
onChange={handleChange}
className={`font-almarai w-full rounded-3xl border bg-white px-4 py-3 text-sm text-gray-700 placeholder-gray-400 outline-none transition focus:ring-2 focus:ring-red-500 ${
errors.lastName ? "border-red-500" : "border-transparent"
errors.surname ? "border-red-500" : "border-transparent"
}`}
/>
{errors.lastName && (
<p className="font-almarai mt-1 text-xs text-red-500">{errors.lastName}</p>
{errors.surname && (
<p className="font-almarai mt-1 text-xs text-red-500">
{errors.surname}
</p>
)}
</div>
</div>
{/* Second Row - Email & Subject */}
{/* Second Row - address & theme */}
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
<div>
<input
type="email"
name="email"
type="address"
name="address"
placeholder={t("contact.form.placeholders.email")}
value={formData.email}
value={formData.address}
onChange={handleChange}
className={`font-almarai w-full rounded-3xl border bg-white px-4 py-3 text-sm text-gray-700 placeholder-gray-400 outline-none transition focus:ring-2 focus:ring-red-500 ${
errors.email ? "border-red-500" : "border-transparent"
errors.address ? "border-red-500" : "border-transparent"
}`}
/>
{errors.email && (
<p className="font-almarai mt-1 text-xs text-red-500">{errors.email}</p>
{errors.address && (
<p className="font-almarai mt-1 text-xs text-red-500">
{errors.address}
</p>
)}
</div>
<div>
<input
type="text"
name="subject"
name="theme"
placeholder={t("contact.form.placeholders.subject")}
value={formData.subject}
value={formData.theme}
onChange={handleChange}
className={`font-almarai w-full rounded-3xl border bg-white px-4 py-3 text-sm text-gray-700 placeholder-gray-400 outline-none transition focus:ring-2 focus:ring-red-500 ${
errors.subject ? "border-red-500" : "border-transparent"
errors.theme ? "border-red-500" : "border-transparent"
}`}
/>
{errors.subject && (
<p className="font-almarai mt-1 text-xs text-red-500">{errors.subject}</p>
{errors.theme && (
<p className="font-almarai mt-1 text-xs text-red-500">
{errors.theme}
</p>
)}
</div>
</div>
@@ -202,7 +216,9 @@ export default function Form() {
}`}
/>
{errors.message && (
<p className="font-almarai mt-1 text-xs text-red-500">{errors.message}</p>
<p className="font-almarai mt-1 text-xs text-red-500">
{errors.message}
</p>
)}
</div>
@@ -236,7 +252,9 @@ export default function Form() {
</button>
</div>
{errors.agreeToPolicy && (
<p className="font-almarai text-xs text-red-500">{errors.agreeToPolicy}</p>
<p className="font-almarai text-xs text-red-500">
{errors.agreeToPolicy}
</p>
)}
{/* Status Messages */}

View File

@@ -1,5 +1,5 @@
import Image from "next/image";
import { Mail, MapPin, Phone, Check } from "lucide-react";
import { Mail, MapPin, Phone } from "lucide-react";
import ContactHeader from "./contactHeader";
import Form from "./form";
import { useTranslations } from "next-intl";
@@ -25,7 +25,7 @@ export function Contact() {
];
return (
<section className="relative min-h-175 w-full py-16 md:py-40">
<section className="relative min-h-175 w-full py-30 md:py-40">
{/* Background Image */}
<div className="absolute inset-0 z-0">
<Image