feat: add Fight and Exhibition registration forms with input validation and file upload functionality

This commit is contained in:
2025-07-31 19:53:19 +09:00
parent d07e778570
commit 4d0a767eac
11 changed files with 369 additions and 19 deletions
+7
View File
@@ -9,6 +9,7 @@
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"next": "15.3.5", "next": "15.3.5",
"pocketbase": "^0.26.2",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"swiper": "^11.2.10" "swiper": "^11.2.10"
@@ -4995,6 +4996,12 @@
"url": "https://github.com/sponsors/jonschlinkert" "url": "https://github.com/sponsors/jonschlinkert"
} }
}, },
"node_modules/pocketbase": {
"version": "0.26.2",
"resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.26.2.tgz",
"integrity": "sha512-WA8EOBc3QnSJh8rJ3iYoi9DmmPOMFIgVfAmIGux7wwruUEIzXgvrO4u0W2htfQjGIcyezJkdZOy5Xmh7SxAftw==",
"license": "MIT"
},
"node_modules/possible-typed-array-names": { "node_modules/possible-typed-array-names": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+1
View File
@@ -10,6 +10,7 @@
}, },
"dependencies": { "dependencies": {
"next": "15.3.5", "next": "15.3.5",
"pocketbase": "^0.26.2",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"swiper": "^11.2.10" "swiper": "^11.2.10"
+21 -2
View File
@@ -1,15 +1,25 @@
/* eslint-disable @next/next/no-img-element */ /* eslint-disable @next/next/no-img-element */
import { fluxgore, gothampro } from "@/utils/fonts"; import { fluxgore, gothampro } from "@/utils/fonts";
import Button from "./Button"; import Button from "./Button";
import { useRouter } from "next/router";
interface EventCardProps extends React.HTMLAttributes<HTMLDivElement> { interface EventCardProps extends React.HTMLAttributes<HTMLDivElement> {
image: string; image: string;
title: string; title: string;
description: string; description: string;
link: string; link: string;
disabled: boolean;
} }
function EventCard(props: EventCardProps) { function EventCard(props: EventCardProps) {
const router = useRouter();
const handleClick = () => {
if (!props.disabled) {
router.push(props.link);
}
};
return ( return (
<div <div
id={props.id} id={props.id}
@@ -35,7 +45,12 @@ function EventCard(props: EventCardProps) {
</p> </p>
</div> </div>
<div className="flex justify-center md:justify-end w-full md:w-1/3"> <div className="flex justify-center md:justify-end w-full md:w-1/3">
<Button variant="blue" shadowEnabled={false}> <Button
onClick={handleClick}
disabled={props.disabled}
variant="blue"
shadowEnabled={false}
>
регистрация регистрация
</Button> </Button>
</div> </div>
@@ -77,6 +92,7 @@ function Events() {
<div className="flex flex-col space-y-4 md:space-y-7 mt-16 md:mt-36"> <div className="flex flex-col space-y-4 md:space-y-7 mt-16 md:mt-36">
<EventCard <EventCard
disabled={true}
id="yuka" id="yuka"
image="/events/yuka.png" image="/events/yuka.png"
title="YUKA Drive Fest Джимхана" title="YUKA Drive Fest Джимхана"
@@ -84,13 +100,15 @@ function Events() {
link="#" link="#"
/> />
<EventCard <EventCard
disabled={false}
id="moscow_fight" id="moscow_fight"
image="/events/moscow_fight.png" image="/events/moscow_fight.png"
title="Дрифт«Битва за Москву»" title="Дрифт«Битва за Москву»"
description="Любительский турнир по дрифту, который вырос из проекта «Дорога в дрифт», созданного в 2021 году для поиска новых талантов. За три года он превратился в полноценные соревнования с привлекательным призовым фондом. Во второй день фестиваля, 8 сентября, пройдет дрифт-гонка, где главным призом станет электромобиль «Москвич». Соревнования проводятся по традиционной олимпийской системе. Чтобы принять участие, необходимо подать заявку на сайте и дождаться приглашения от организаторов." description="Любительский турнир по дрифту, который вырос из проекта «Дорога в дрифт», созданного в 2021 году для поиска новых талантов. За три года он превратился в полноценные соревнования с привлекательным призовым фондом. Во второй день фестиваля, 8 сентября, пройдет дрифт-гонка, где главным призом станет электромобиль «Москвич». Соревнования проводятся по традиционной олимпийской системе. Чтобы принять участие, необходимо подать заявку на сайте и дождаться приглашения от организаторов."
link="#" link="/forms/fight"
/> />
<EventCard <EventCard
disabled={true}
id="moto" id="moto"
image="/events/moto.png" image="/events/moto.png"
title="КуБок ШОС по Мотокроссу" title="КуБок ШОС по Мотокроссу"
@@ -98,6 +116,7 @@ function Events() {
link="#" link="#"
/> />
<EventCard <EventCard
disabled={true}
image="/events/cart.png" image="/events/cart.png"
title="Кубок по Фиджитал картингу" title="Кубок по Фиджитал картингу"
description="На Фестивале технических видов спорта 2025 впервые состоится Кубок по Фиджитал Картингу! Это уникальное состязание, где виртуальная реальность встречается с реальной трассой. Участники будут сражаться на симуляторах, а затем переносить свои навыки на настоящий картинг, демонстрируя невероятную адаптивность и мастерство." description="На Фестивале технических видов спорта 2025 впервые состоится Кубок по Фиджитал Картингу! Это уникальное состязание, где виртуальная реальность встречается с реальной трассой. Участники будут сражаться на симуляторах, а затем переносить свои навыки на настоящий картинг, демонстрируя невероятную адаптивность и мастерство."
+2 -2
View File
@@ -272,7 +272,7 @@ function Fileupload({
className="ml-2 p-1 text-gray-400 hover:text-gray-600" className="ml-2 p-1 text-gray-400 hover:text-gray-600"
> >
<svg <svg
className="w-3 h-3" className="w-4 h-4"
fill="none" fill="none"
stroke="currentColor" stroke="currentColor"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@@ -281,7 +281,7 @@ function Fileupload({
strokeLinecap="round" strokeLinecap="round"
strokeLinejoin="round" strokeLinejoin="round"
strokeWidth={2} strokeWidth={2}
d="M6 18L18 6M6 6l12 12" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/> />
</svg> </svg>
</button> </button>
+7 -1
View File
@@ -5,7 +5,7 @@ import { gothampro } from "@/utils/fonts";
interface InputProps { interface InputProps {
label?: string; label?: string;
placeholder?: string; placeholder?: string;
type?: "text" | "email" | "password" | "number" | "tel"; type?: "text" | "email" | "password" | "number" | "tel" | "date" | "datetime-local" | "time";
value?: string; value?: string;
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void; onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
onBlur?: (e: React.FocusEvent<HTMLInputElement>) => void; onBlur?: (e: React.FocusEvent<HTMLInputElement>) => void;
@@ -15,6 +15,8 @@ interface InputProps {
className?: string; className?: string;
id?: string; id?: string;
name?: string; name?: string;
min?: string;
max?: string;
} }
function Input({ function Input({
@@ -30,6 +32,8 @@ function Input({
className = "", className = "",
id, id,
name, name,
min,
max,
}: InputProps) { }: InputProps) {
return ( return (
<div className={`flex flex-col gap-2 w-full ${className}`}> <div className={`flex flex-col gap-2 w-full ${className}`}>
@@ -52,6 +56,8 @@ function Input({
placeholder={placeholder} placeholder={placeholder}
disabled={disabled} disabled={disabled}
required={required} required={required}
min={min}
max={max}
className={` className={`
w-full px-4 py-3 w-full px-4 py-3
border border-gray-300 border border-gray-300
+26
View File
@@ -0,0 +1,26 @@
import { useState, useCallback } from 'react';
export const usePhoneMask = (initialValue: string = '') => {
const [value, setValue] = useState(initialValue);
const formatPhoneNumber = useCallback((input: string) => {
// Remove all non-digit characters
const digits = input.replace(/\D/g, '');
// Format as +7 (XXX) XXX-XX-XX
if (digits.length === 0) return '';
if (digits.length <= 1) return '+7';
if (digits.length <= 4) return `+7 (${digits.slice(1)}`;
if (digits.length <= 7) return `+7 (${digits.slice(1, 4)}) ${digits.slice(4)}`;
if (digits.length <= 9) return `+7 (${digits.slice(1, 4)}) ${digits.slice(4, 7)}-${digits.slice(7)}`;
return `+7 (${digits.slice(1, 4)}) ${digits.slice(4, 7)}-${digits.slice(7, 9)}-${digits.slice(9, 11)}`;
}, []);
const handleChange = useCallback((input: string) => {
const formatted = formatPhoneNumber(input);
setValue(formatted);
return formatted;
}, [formatPhoneNumber]);
return { value, handleChange, setValue };
};
+112 -5
View File
@@ -2,25 +2,120 @@ import Checkbox from "@/components/form/Checkbox";
import Fileupload from "@/components/form/Fileupload"; import Fileupload from "@/components/form/Fileupload";
import Input from "@/components/form/Input"; import Input from "@/components/form/Input";
import Textarea from "@/components/form/Textarea"; import Textarea from "@/components/form/Textarea";
import { usePhoneMask } from "@/hooks/usePhoneMask";
import { fluxgore, gothampro } from "@/utils/fonts"; import { fluxgore, gothampro } from "@/utils/fonts";
import { useState } from "react"; import { useState } from "react";
import PocketBase from "pocketbase";
import Head from "next/head";
import { useRouter } from "next/router";
const pb = new PocketBase(
"http://pocketbase-nkg4scskc4okw4w0cw4w88gk.176.114.67.63.sslip.io"
);
function ExhibtionFormPage() { function ExhibtionFormPage() {
const [checkboxValues, setCheckboxValues] = useState<string[]>([]); const [checkboxValues, setCheckboxValues] = useState<string[]>([]);
const router = useRouter();
const [formData, setFormData] = useState({
name: "",
email: "",
carBrand: "",
carModel: "",
description: "",
});
const [carPhotos, setCarPhotos] = useState<File[]>([]);
const [isSubmitting, setIsSubmitting] = useState(false);
// Add a key to force re-render of Fileupload component
const [fileUploadKey, setFileUploadKey] = useState(0);
const { value: phoneValue, handleChange: handlePhoneChange } = usePhoneMask();
const handleInputChange = (field: string, value: string) => {
setFormData((prev) => ({
...prev,
[field]: value,
}));
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!checkboxValues.includes("terms")) {
alert("Необходимо согласие на обработку персональных данных");
return;
}
setIsSubmitting(true);
try {
const formDataObject = {
name: formData.name,
phone: phoneValue,
email: formData.email,
carBrand: formData.carBrand,
carModel: formData.carModel,
description: formData.description,
};
const data = new FormData();
// Store all form data as JSON in the data field
data.append("data", JSON.stringify(formDataObject));
data.append("type", "exhibition");
// Add car photos to the images field (PocketBase will handle multiple files)
carPhotos.forEach((file) => {
data.append("images", file);
});
const record = await pb.collection("forms").create(data);
console.log("Form submitted successfully:", record);
console.log("Uploaded images:", record.images);
alert("Форма успешно отправлена!");
// Reset form
setFormData({
name: "",
email: "",
carBrand: "",
carModel: "",
description: "",
});
setCheckboxValues([]);
setCarPhotos([]);
// Force re-render of Fileupload component
setFileUploadKey((prev) => prev + 1);
handlePhoneChange(""); // Reset phone value
router.push("/thankyou");
} catch (error) {
console.error("Error submitting form:", error);
alert("Ошибка при отправке формы. Попробуйте еще раз.");
} finally {
setIsSubmitting(false);
}
};
return ( return (
<div className="bg-[#ffffff] relative p-6 md:p-8 lg:p-12 flex flex-col items-center justify-center md:h-full"> <div className="relative p-6 md:p-8 lg:p-12 flex flex-col items-center">
<Head>
<title>Фестиваль технических видов спорта</title>
</Head>
<h1 <h1
className={`${fluxgore.className} text-4xl md:text-7xl text-[#060606] relative mb-8 md:mb-12`} className={`${fluxgore.className} text-4xl md:text-7xl text-[#060606] relative mb-8 md:mb-12`}
> >
Регистрация на выставку Регистрация на выставку
</h1> </h1>
<div className="space-y-6 max-w-2xl w-full"> <form onSubmit={handleSubmit} className="space-y-6 max-w-2xl w-full">
<Input <Input
label="Имя" label="Имя"
placeholder="Введите ваше имя" placeholder="Введите ваше имя"
type="text" type="text"
value={formData.name}
onChange={(e) => handleInputChange("name", e.target.value)}
required required
/> />
@@ -28,6 +123,8 @@ function ExhibtionFormPage() {
label="Телефон" label="Телефон"
placeholder="+7 (___) ___-__-__" placeholder="+7 (___) ___-__-__"
type="tel" type="tel"
value={phoneValue}
onChange={(e) => handlePhoneChange(e.target.value)}
required required
/> />
@@ -35,6 +132,8 @@ function ExhibtionFormPage() {
label="Почта" label="Почта"
placeholder="example@email.com" placeholder="example@email.com"
type="email" type="email"
value={formData.email}
onChange={(e) => handleInputChange("email", e.target.value)}
required required
/> />
@@ -42,6 +141,8 @@ function ExhibtionFormPage() {
label="Марка автомобиля" label="Марка автомобиля"
placeholder="Введите марку автомобиля" placeholder="Введите марку автомобиля"
type="text" type="text"
value={formData.carBrand}
onChange={(e) => handleInputChange("carBrand", e.target.value)}
required required
/> />
@@ -49,12 +150,15 @@ function ExhibtionFormPage() {
label="Модель" label="Модель"
placeholder="Введите модель автомобиля" placeholder="Введите модель автомобиля"
type="text" type="text"
value={formData.carModel}
onChange={(e) => handleInputChange("carModel", e.target.value)}
required required
/> />
<Fileupload <Fileupload
key={fileUploadKey} // Force component re-render on reset
label="Фото автомобиля (до 3 файлов)" label="Фото автомобиля (до 3 файлов)"
onFileSelect={(files) => console.log("Selected files:", files)} onFileSelect={(files) => setCarPhotos(files)}
acceptedTypes={["image/*"]} acceptedTypes={["image/*"]}
maxFileSize={5} maxFileSize={5}
multiple={true} multiple={true}
@@ -64,6 +168,8 @@ function ExhibtionFormPage() {
<Textarea <Textarea
label="Интересное об автомобиле" label="Интересное об автомобиле"
placeholder="Расскажите что-то интересное о вашем автомобиле" placeholder="Расскажите что-то интересное о вашем автомобиле"
value={formData.description}
onChange={(e) => handleInputChange("description", e.target.value)}
rows={4} rows={4}
cols={50} cols={50}
/> />
@@ -90,11 +196,12 @@ function ExhibtionFormPage() {
<button <button
type="submit" type="submit"
disabled={isSubmitting}
className={`${fluxgore.className} bg-[#1068B0] hover:bg-[#0d5a96] text-white px-9 py-4 text-base font-medium uppercase tracking-wide disabled:opacity-50 w-full`} className={`${fluxgore.className} bg-[#1068B0] hover:bg-[#0d5a96] text-white px-9 py-4 text-base font-medium uppercase tracking-wide disabled:opacity-50 w-full`}
> >
Отправить {isSubmitting ? "Отправка..." : "Отправить"}
</button> </button>
</div> </form>
</div> </div>
); );
} }
+142 -6
View File
@@ -2,25 +2,137 @@ import Checkbox from "@/components/form/Checkbox";
import Fileupload from "@/components/form/Fileupload"; import Fileupload from "@/components/form/Fileupload";
import Input from "@/components/form/Input"; import Input from "@/components/form/Input";
import Textarea from "@/components/form/Textarea"; import Textarea from "@/components/form/Textarea";
import { usePhoneMask } from "@/hooks/usePhoneMask";
import { fluxgore, gothampro } from "@/utils/fonts"; import { fluxgore, gothampro } from "@/utils/fonts";
import Head from "next/head";
import { useState } from "react"; import { useState } from "react";
import PocketBase from "pocketbase";
import { useRouter } from "next/router";
const pb = new PocketBase(
"http://pocketbase-nkg4scskc4okw4w0cw4w88gk.176.114.67.63.sslip.io"
);
function FightFormPage() { function FightFormPage() {
const router = useRouter();
const [checkboxValues, setCheckboxValues] = useState<string[]>([]); const [checkboxValues, setCheckboxValues] = useState<string[]>([]);
const [formData, setFormData] = useState({
lastName: "",
firstName: "",
middleName: "",
birthDate: "",
citizenship: "",
email: "",
carBrand: "",
carModel: "",
engine: "",
power: "",
additionalInfo: "",
});
const [carPhotos, setCarPhotos] = useState<File[]>([]);
const [isSubmitting, setIsSubmitting] = useState(false);
// Add a key to force re-render of Fileupload component
const [fileUploadKey, setFileUploadKey] = useState(0);
const { value: phoneValue, handleChange: handlePhoneChange } = usePhoneMask();
const handleInputChange = (field: string, value: string) => {
setFormData((prev) => ({
...prev,
[field]: value,
}));
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!checkboxValues.includes("terms")) {
alert("Необходимо согласие на обработку персональных данных");
return;
}
setIsSubmitting(true);
try {
const formDataObject = {
lastName: formData.lastName,
firstName: formData.firstName,
middleName: formData.middleName,
birthDate: formData.birthDate,
citizenship: formData.citizenship,
phone: phoneValue,
email: formData.email,
carBrand: formData.carBrand,
carModel: formData.carModel,
engine: formData.engine,
power: formData.power,
additionalInfo: formData.additionalInfo,
};
const data = new FormData();
// Store all form data as JSON in the data field
data.append("data", JSON.stringify(formDataObject));
data.append("type", "fight");
// Add car photos to the images field (PocketBase will handle multiple files)
carPhotos.forEach((file) => {
data.append("images", file);
});
const record = await pb.collection("forms").create(data);
console.log("Form submitted successfully:", record);
console.log("Uploaded images:", record.images);
alert("Форма успешно отправлена!");
// Reset form
setFormData({
lastName: "",
firstName: "",
middleName: "",
birthDate: "",
citizenship: "",
email: "",
carBrand: "",
carModel: "",
engine: "",
power: "",
additionalInfo: "",
});
setCheckboxValues([]);
setCarPhotos([]);
// Force re-render of Fileupload component
setFileUploadKey((prev) => prev + 1);
handlePhoneChange(""); // Reset phone value
router.push("/thankyou");
} catch (error) {
console.error("Error submitting form:", error);
alert("Ошибка при отправке формы. Попробуйте еще раз.");
} finally {
setIsSubmitting(false);
}
};
return ( return (
<div className="bg-[#ffffff] relative p-6 md:p-8 lg:p-12 flex flex-col items-center justify-center"> <div className="relative p-6 md:p-8 lg:p-12 flex flex-col items-center justify-center">
<Head>
<title>Фестиваль технических видов спорта</title>
</Head>
<h1 <h1
className={`${fluxgore.className} text-4xl md:text-7xl text-[#060606] relative mb-8 md:mb-12`} className={`${fluxgore.className} text-4xl md:text-7xl text-[#060606] relative mb-8 md:mb-12`}
> >
Регистрация на Битву за Москву Регистрация на Битву за Москву
</h1> </h1>
<div className="space-y-6 max-w-2xl w-full"> <form onSubmit={handleSubmit} className="space-y-6 max-w-2xl w-full">
<Input <Input
label="Фамилия" label="Фамилия"
placeholder="Введите вашу фамилию" placeholder="Введите вашу фамилию"
type="text" type="text"
value={formData.lastName}
onChange={(e) => handleInputChange("lastName", e.target.value)}
required required
/> />
@@ -28,6 +140,8 @@ function FightFormPage() {
label="Имя" label="Имя"
placeholder="Введите ваше имя" placeholder="Введите ваше имя"
type="text" type="text"
value={formData.firstName}
onChange={(e) => handleInputChange("firstName", e.target.value)}
required required
/> />
@@ -35,13 +149,17 @@ function FightFormPage() {
label="Отчество" label="Отчество"
placeholder="Введите ваше отчество" placeholder="Введите ваше отчество"
type="text" type="text"
value={formData.middleName}
onChange={(e) => handleInputChange("middleName", e.target.value)}
required required
/> />
<Input <Input
label="Дата рождения" label="Дата рождения"
placeholder="дд.мм.гггг" placeholder="дд.мм.гггг"
type="text" type="date"
value={formData.birthDate}
onChange={(e) => handleInputChange("birthDate", e.target.value)}
required required
/> />
@@ -49,6 +167,8 @@ function FightFormPage() {
label="Гражданство" label="Гражданство"
placeholder="Введите ваше гражданство" placeholder="Введите ваше гражданство"
type="text" type="text"
value={formData.citizenship}
onChange={(e) => handleInputChange("citizenship", e.target.value)}
required required
/> />
@@ -56,6 +176,8 @@ function FightFormPage() {
label="Телефон" label="Телефон"
placeholder="+7 (___) ___-__-__" placeholder="+7 (___) ___-__-__"
type="tel" type="tel"
value={phoneValue}
onChange={(e) => handlePhoneChange(e.target.value)}
required required
/> />
@@ -63,6 +185,8 @@ function FightFormPage() {
label="Почта" label="Почта"
placeholder="example@email.com" placeholder="example@email.com"
type="email" type="email"
value={formData.email}
onChange={(e) => handleInputChange("email", e.target.value)}
required required
/> />
@@ -70,6 +194,8 @@ function FightFormPage() {
label="Марка автомобиля" label="Марка автомобиля"
placeholder="Введите марку автомобиля" placeholder="Введите марку автомобиля"
type="text" type="text"
value={formData.carBrand}
onChange={(e) => handleInputChange("carBrand", e.target.value)}
required required
/> />
@@ -77,6 +203,8 @@ function FightFormPage() {
label="Модель" label="Модель"
placeholder="Введите модель автомобиля" placeholder="Введите модель автомобиля"
type="text" type="text"
value={formData.carModel}
onChange={(e) => handleInputChange("carModel", e.target.value)}
required required
/> />
@@ -84,6 +212,8 @@ function FightFormPage() {
label="Двигатель" label="Двигатель"
placeholder="Введите тип двигателя" placeholder="Введите тип двигателя"
type="text" type="text"
value={formData.engine}
onChange={(e) => handleInputChange("engine", e.target.value)}
required required
/> />
@@ -91,12 +221,15 @@ function FightFormPage() {
label="Мощность" label="Мощность"
placeholder="Введите мощность (л.с.)" placeholder="Введите мощность (л.с.)"
type="text" type="text"
value={formData.power}
onChange={(e) => handleInputChange("power", e.target.value)}
required required
/> />
<Fileupload <Fileupload
key={fileUploadKey} // Force component re-render on reset
label="Фото автомобиля (до 3 файлов)" label="Фото автомобиля (до 3 файлов)"
onFileSelect={(files) => console.log("Selected files:", files)} onFileSelect={(files) => setCarPhotos(files)}
acceptedTypes={["image/*"]} acceptedTypes={["image/*"]}
maxFileSize={5} maxFileSize={5}
multiple={true} multiple={true}
@@ -106,6 +239,8 @@ function FightFormPage() {
<Textarea <Textarea
label="Дополнительная информация" label="Дополнительная информация"
placeholder="Дополнительная информация об автомобиле" placeholder="Дополнительная информация об автомобиле"
value={formData.additionalInfo}
onChange={(e) => handleInputChange("additionalInfo", e.target.value)}
rows={4} rows={4}
cols={50} cols={50}
/> />
@@ -132,11 +267,12 @@ function FightFormPage() {
<button <button
type="submit" type="submit"
disabled={isSubmitting}
className={`${fluxgore.className} bg-[#1068B0] hover:bg-[#0d5a96] text-white px-9 py-4 text-base font-medium uppercase tracking-wide disabled:opacity-50 w-full`} className={`${fluxgore.className} bg-[#1068B0] hover:bg-[#0d5a96] text-white px-9 py-4 text-base font-medium uppercase tracking-wide disabled:opacity-50 w-full`}
> >
Отправить {isSubmitting ? "Отправка..." : "Отправить"}
</button> </button>
</div> </form>
</div> </div>
); );
} }
+24 -2
View File
@@ -1,4 +1,15 @@
import { CoverSoon } from "@/components/Cover"; /* eslint-disable @typescript-eslint/no-unused-vars */
import Activities from "@/components/Activities";
import Cover from "@/components/Cover";
import Events from "@/components/Events";
import Footer from "@/components/Footer";
import Info from "@/components/Info";
import Map from "@/components/Map";
import Navbar from "@/components/Navbar";
import Partners from "@/components/Partners";
import Scheme from "@/components/Scheme";
import Video from "@/components/Video";
import Head from "next/head"; import Head from "next/head";
export default function Home() { export default function Home() {
@@ -7,7 +18,18 @@ export default function Home() {
<Head> <Head>
<title>Фестиваль технических видов спорта</title> <title>Фестиваль технических видов спорта</title>
</Head> </Head>
<CoverSoon /> <Navbar />
<main className="flex-col min-h-full">
<Cover />
<Info />
<Video />
{/* <Scheme /> */}
<Events />
<Activities />
<Partners />
<Map />
</main>
<Footer />
</> </>
); );
} }
+27
View File
@@ -0,0 +1,27 @@
import React from "react";
import Head from "next/head";
import { fluxgore, gothampro } from "@/utils/fonts";
function ThankYouPage() {
return (
<div className="relative p-6 md:p-8 lg:p-12 flex flex-col items-center justify-center h-full">
<Head>
<title>Фестиваль технических видов спорта</title>
</Head>
<h1
className={`${fluxgore.className} text-4xl md:text-7xl text-[#060606] relative mb-8 md:mb-12`}
>
СПАСИБО!
</h1>
<p
className={`${gothampro.className} text-lg md:text-2xl text-center text-[#060606]`}
>
Ваша заявка отправлена. вам придет письмо на почту
</p>
</div>
);
}
export default ThankYouPage;
-1
View File
@@ -13,5 +13,4 @@
body { body {
height: 100%; height: 100%;
overflow-x: hidden; overflow-x: hidden;
background-color: #0d0d0d;
} }