Add send appeal form
This commit is contained in:
parent
9dda476c16
commit
9c3657190a
@ -118,5 +118,19 @@
|
|||||||
"serverError": "Error on the server side",
|
"serverError": "Error on the server side",
|
||||||
"noPassword": "Weak password, please avoid obvious passwords",
|
"noPassword": "Weak password, please avoid obvious passwords",
|
||||||
"errorOccured": "An unexpected error occurred"
|
"errorOccured": "An unexpected error occurred"
|
||||||
|
},
|
||||||
|
"contacts": {
|
||||||
|
"serverError": "Error on the server side",
|
||||||
|
"addressTitle": "Address",
|
||||||
|
"address": "Bishkek, Turusbekov str. 109/1, office 108",
|
||||||
|
"phoneTitle": "Phone",
|
||||||
|
"title": "We're in touch",
|
||||||
|
"name": "Name",
|
||||||
|
"surname": "Last name",
|
||||||
|
"phone": "Phone number",
|
||||||
|
"message": "Message",
|
||||||
|
"checkbox": "I agree to the processing of my personal data",
|
||||||
|
"send": "Send a message",
|
||||||
|
"successMessage": "Your message has been sent"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,5 +118,19 @@
|
|||||||
"serverError": "Сервер тараптагы ката",
|
"serverError": "Сервер тараптагы ката",
|
||||||
"noPassword": "Начар сырсөз, ачык сырсөздөрдү колдонбоңуз",
|
"noPassword": "Начар сырсөз, ачык сырсөздөрдү колдонбоңуз",
|
||||||
"errorOccured": "Күтүлбөгөн ката кетти"
|
"errorOccured": "Күтүлбөгөн ката кетти"
|
||||||
|
},
|
||||||
|
"contacts": {
|
||||||
|
"serverError": "Сервер тараптагы ката",
|
||||||
|
"addressTitle": "Дарек",
|
||||||
|
"address": "Бишкек ш., Турусбеков көч. 109/1, офис 108",
|
||||||
|
"phoneTitle": "Телефон",
|
||||||
|
"title": "Биз байланыштабыз",
|
||||||
|
"name": "Аты-жөнү",
|
||||||
|
"familia": "фамилиясы",
|
||||||
|
"phone": "Телефон номери",
|
||||||
|
"message": "Кабар",
|
||||||
|
"checkbox": "Мен жеке маалыматтарымды иштетүүгө макулмун",
|
||||||
|
"send": "Билдирүү жөнөтүү",
|
||||||
|
"successMessage": "Сиздин билдирүү жөнөтүлдү"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,5 +109,19 @@
|
|||||||
"serverError": "Ошибка на стороне сервера",
|
"serverError": "Ошибка на стороне сервера",
|
||||||
"noPassword": "Слабый пароль, прошу избегайте очевидных паролей",
|
"noPassword": "Слабый пароль, прошу избегайте очевидных паролей",
|
||||||
"errorOccured": "Произошла непредвиденная ошибка"
|
"errorOccured": "Произошла непредвиденная ошибка"
|
||||||
|
},
|
||||||
|
"contacts": {
|
||||||
|
"serverError": "Ошибка на стороне сервера",
|
||||||
|
"addressTitle": "Адрес",
|
||||||
|
"address": "г. Бишкек, ул. Турусбекова 109/1, офис 108",
|
||||||
|
"phoneTitle": "Телефон",
|
||||||
|
"title": "Мы на связи",
|
||||||
|
"name": "Имя",
|
||||||
|
"surname": "Фамилия",
|
||||||
|
"phone": "Номер телефона",
|
||||||
|
"message": "Сообщение",
|
||||||
|
"checkbox": "Я согласен (-на) на обработку моих личных данных",
|
||||||
|
"send": "Отправить сообщение",
|
||||||
|
"successMessage": "Ваше сообщение отправлено"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
package-lock.json
generated
14
package-lock.json
generated
@ -18,6 +18,7 @@
|
|||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"react-hook-form": "^7.52.2",
|
"react-hook-form": "^7.52.2",
|
||||||
|
"react-toastify": "^10.0.5",
|
||||||
"sass": "^1.77.8",
|
"sass": "^1.77.8",
|
||||||
"tailwind-merge": "^2.5.2",
|
"tailwind-merge": "^2.5.2",
|
||||||
"zod": "^3.23.8",
|
"zod": "^3.23.8",
|
||||||
@ -4224,6 +4225,19 @@
|
|||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/react-toastify": {
|
||||||
|
"version": "10.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.5.tgz",
|
||||||
|
"integrity": "sha512-mNKt2jBXJg4O7pSdbNUfDdTsK9FIdikfsIE/yUCxbAEXl4HMyJaivrVFcn3Elvt5xvCQYhUZm+hqTIu1UXM3Pw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"clsx": "^2.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=18",
|
||||||
|
"react-dom": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/read-cache": {
|
"node_modules/read-cache": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
"react": "^18",
|
"react": "^18",
|
||||||
"react-dom": "^18",
|
"react-dom": "^18",
|
||||||
"react-hook-form": "^7.52.2",
|
"react-hook-form": "^7.52.2",
|
||||||
|
"react-toastify": "^10.0.5",
|
||||||
"sass": "^1.77.8",
|
"sass": "^1.77.8",
|
||||||
"tailwind-merge": "^2.5.2",
|
"tailwind-merge": "^2.5.2",
|
||||||
"zod": "^3.23.8",
|
"zod": "^3.23.8",
|
||||||
|
@ -1,8 +1,58 @@
|
|||||||
|
"use client";
|
||||||
|
import { apiInstance } from "@/shared/config/apiConfig";
|
||||||
import { Container, Title } from "@/shared/ui";
|
import { Container, Title } from "@/shared/ui";
|
||||||
|
import Loader from "@/shared/ui/Loader/Loader";
|
||||||
// import ContactForm from "@/widgets/forms/ContactForm";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
|
import { AxiosError } from "axios";
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import { useState } from "react";
|
||||||
|
import { useForm } from "react-hook-form";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
export default function Contacts() {
|
export default function Contacts() {
|
||||||
|
const t = useTranslations("contacts");
|
||||||
|
const [error, setError] = useState<string>("");
|
||||||
|
const [loader, setLoader] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const appealFormScheme = z.object({
|
||||||
|
first_name: z.string(),
|
||||||
|
last_name: z.string(),
|
||||||
|
email: z.string(),
|
||||||
|
phone_number: z.string(),
|
||||||
|
message: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
type FormFields = z.infer<typeof appealFormScheme>;
|
||||||
|
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors, isSubmitting },
|
||||||
|
} = useForm<FormFields>({
|
||||||
|
resolver: zodResolver(appealFormScheme),
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = async (data: FormFields) => {
|
||||||
|
try {
|
||||||
|
setError("");
|
||||||
|
setLoader(true);
|
||||||
|
|
||||||
|
const response = await apiInstance.post("/appeal_response/", data);
|
||||||
|
console.log(response);
|
||||||
|
toast.success(t("successMessage"));
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AxiosError) {
|
||||||
|
if (
|
||||||
|
[500, 501, 502, 503, 504].includes(error.response?.status as number)
|
||||||
|
) {
|
||||||
|
toast.error(t("serverError"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setLoader(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<section className="bg-[#FAFCFF]">
|
<section className="bg-[#FAFCFF]">
|
||||||
<Container className="py-[100px] ">
|
<Container className="py-[100px] ">
|
||||||
@ -12,13 +62,11 @@ export default function Contacts() {
|
|||||||
></iframe>
|
></iframe>
|
||||||
<section className="flex items-center justify-between mb-[152px] mt-[64px]">
|
<section className="flex items-center justify-between mb-[152px] mt-[64px]">
|
||||||
<div className="max-w-[384px] max-h-[106px] flex flex-col items-center px-7">
|
<div className="max-w-[384px] max-h-[106px] flex flex-col items-center px-7">
|
||||||
<Title text="Адрес" className="font-extrabold mb-2" />
|
<Title text={t("addressTitle")} className="font-extrabold mb-2" />
|
||||||
<p className="text-grey-text">
|
<p className="text-grey-text">{t("address")}</p>
|
||||||
г. Бишкек, ул. Турусбекова 109/1, офис 108
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="max-w-[384px] max-h-[106px] flex flex-col items-center px-7">
|
<div className="max-w-[384px] max-h-[106px] flex flex-col items-center px-7">
|
||||||
<Title text="Телефон" className="font-extrabold mb-2" />
|
<Title text={t("phoneTitle")} className="font-extrabold mb-2" />
|
||||||
<p className="text-grey-text">(0312) 39 40 38</p>
|
<p className="text-grey-text">(0312) 39 40 38</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="max-w-[384px] max-h-[106px] flex flex-col items-center px-7">
|
<div className="max-w-[384px] max-h-[106px] flex flex-col items-center px-7">
|
||||||
@ -26,36 +74,38 @@ export default function Contacts() {
|
|||||||
<p className="text-grey-text">kyrgyzstan@transparency.org</p>
|
<p className="text-grey-text">kyrgyzstan@transparency.org</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
{/* <ContactForm /> */}
|
<form
|
||||||
<form className="w-[480px] my-0 mx-auto">
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
className="w-[480px] my-0 mx-auto"
|
||||||
|
>
|
||||||
<div className="flex items-center flex-col mt-[96px] mb-[96px]">
|
<div className="flex items-center flex-col mt-[96px] mb-[96px]">
|
||||||
<h1 className="text-gray-900 text-[36px]">Мы на связи</h1>
|
<h1 className="text-gray-900 text-[36px]">{t("title")}</h1>
|
||||||
<div className="flex flex-col items-center gap-6 mt-[64px]">
|
<div className="flex flex-col items-center gap-6 mt-[64px]">
|
||||||
<div className="flex gap-8 w-full">
|
<div className="flex gap-8 w-full">
|
||||||
<div className="w-[224px] flex flex-col">
|
<div className="w-[224px] flex flex-col">
|
||||||
<label htmlFor="firstName" className="mb-[6px]">
|
<label htmlFor="firstName" className="mb-[6px]">
|
||||||
Имя
|
{t("name")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="firstName"
|
|
||||||
className="w-full h-[48px] bg-white py-3 px-4 rounded-md shadow-sm border border-gray"
|
className="w-full h-[48px] bg-white py-3 px-4 rounded-md shadow-sm border border-gray"
|
||||||
placeholder="Имя"
|
placeholder={t("name")}
|
||||||
id="firstName"
|
id="firstName"
|
||||||
required
|
required
|
||||||
|
{...register("first_name", { required: true })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-[224px] flex flex-col">
|
<div className="w-[224px] flex flex-col">
|
||||||
<label htmlFor="lastName" className="mb-[6px]">
|
<label htmlFor="lastName" className="mb-[6px]">
|
||||||
Фамилия
|
{t("surname")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="lastName"
|
|
||||||
className="w-full h-[48px] bg-white py-3 px-4 rounded-md shadow-sm border border-gray"
|
className="w-full h-[48px] bg-white py-3 px-4 rounded-md shadow-sm border border-gray"
|
||||||
placeholder="Фамилия"
|
placeholder={t("surname")}
|
||||||
id="lastName"
|
id="lastName"
|
||||||
required
|
required
|
||||||
|
{...register("last_name", { required: true })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -65,36 +115,36 @@ export default function Contacts() {
|
|||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="email"
|
|
||||||
placeholder="user@gmail.com"
|
placeholder="user@gmail.com"
|
||||||
className="w-full h-[48px] bg-white py-3 px-4 rounded-md shadow-sm border border-gray"
|
className="w-full h-[48px] bg-white py-3 px-4 rounded-md shadow-sm border border-gray"
|
||||||
id="email"
|
id="email"
|
||||||
|
{...register("email", { required: true })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<label htmlFor="phoneNumber" className="mb-[6px]">
|
<label htmlFor="phoneNumber" className="mb-[6px]">
|
||||||
Номер телефона
|
{t("phone")}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="phoneNumber"
|
|
||||||
className="w-full h-[48px] bg-white py-3 px-4 rounded-md shadow-sm border border-gray"
|
className="w-full h-[48px] bg-white py-3 px-4 rounded-md shadow-sm border border-gray"
|
||||||
placeholder="+996"
|
placeholder="+996"
|
||||||
id="phoneNumber"
|
id="phoneNumber"
|
||||||
required
|
required
|
||||||
|
{...register("phone_number", { required: true })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<label htmlFor="message" className="mb-[6px]">
|
<label htmlFor="message" className="mb-[6px]">
|
||||||
Сообщение
|
{t("message")}
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
className="h-[120px] resize-none w-full bg-white py-3 px-4 rounded-md shadow-sm border border-gray"
|
className="h-[120px] resize-none w-full bg-white py-3 px-4 rounded-md shadow-sm border border-gray"
|
||||||
id="message"
|
id="message"
|
||||||
name="message"
|
|
||||||
required
|
required
|
||||||
|
{...register("message", { required: true })}
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -110,16 +160,24 @@ export default function Contacts() {
|
|||||||
htmlFor="consentCheckbox"
|
htmlFor="consentCheckbox"
|
||||||
className="text-[#667085] ml-[10px]"
|
className="text-[#667085] ml-[10px]"
|
||||||
>
|
>
|
||||||
Я согласен (-на) на обработку моих личных данных
|
{t("checkbox")}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
{errors.root && (
|
||||||
|
<p className="text-sm leading-5 text-red-500">
|
||||||
|
{errors.root.message}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
{error ? (
|
||||||
|
<p className="text-red-500 leading-5 text-sm">{error}</p>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="bg-blue text-white w-full h-[48px] rounded-md "
|
className="bg-blue text-white w-full h-[48px] rounded-md "
|
||||||
>
|
>
|
||||||
Отправить сообщение
|
{loader ? <Loader /> : t("send")}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,6 +5,8 @@ import { NextIntlClientProvider, useMessages } from "next-intl";
|
|||||||
import Footer from "../../widgets/Footer/Footer";
|
import Footer from "../../widgets/Footer/Footer";
|
||||||
import Navbar from "@/widgets/Navbar/Navbar";
|
import Navbar from "@/widgets/Navbar/Navbar";
|
||||||
import { Providers } from "./Providers";
|
import { Providers } from "./Providers";
|
||||||
|
import "react-toastify/dist/ReactToastify.css";
|
||||||
|
import { ToastContainer } from "react-toastify";
|
||||||
|
|
||||||
const inter = Inter({ subsets: ["latin"] });
|
const inter = Inter({ subsets: ["latin"] });
|
||||||
|
|
||||||
@ -29,6 +31,7 @@ export default function RootLayout({
|
|||||||
<Providers>
|
<Providers>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<div>{children}</div>
|
<div>{children}</div>
|
||||||
|
<ToastContainer />
|
||||||
<Footer />
|
<Footer />
|
||||||
</Providers>
|
</Providers>
|
||||||
</NextIntlClientProvider>
|
</NextIntlClientProvider>
|
||||||
|
Loading…
Reference in New Issue
Block a user