Files
graff.event/src/components/Layout/FeedbackForm.tsx
T
2026-03-31 14:02:47 +05:00

223 lines
7.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import ReactInputMask from 'react-input-mask';
import { SelectPhoneCode } from '../SelectPhoneCode';
import { ClassNameWrapper } from '../../hocs/ClassNameWrapper';
import { LoaderIcon } from '../icons/LoaderIcon';
import { Button } from '../ui/Button';
import { ArrowRightIcon } from '../icons/ArrowRightIcon';
import { FormEvent, useEffect, useMemo, useRef, useState } from 'react';
import { Country } from 'react-phone-number-input';
import { api } from '../../api';
import { getExampleNumber } from 'libphonenumber-js';
import examples from 'libphonenumber-js/mobile/examples';
import { NavLink } from 'react-router-dom';
export function FeedbackForm({
inModal = false,
send = () => {},
}: {
inModal?: boolean;
send?: () => void;
}) {
const [name, setName] = useState('');
const [[phoneCode, country], setPhoneCodeAndCountry] = useState<
[string, Country]
>(['+7', 'RU']);
const [phone, setPhone] = useState('');
const [email, setEmail] = useState('');
const [description, setDescription] = useState('');
const [isLoading, setIsLoading] = useState(false);
const textAreaRef = useRef<HTMLTextAreaElement>(null);
useEffect(() => {
if (textAreaRef.current) {
textAreaRef.current.style.height = 'auto';
textAreaRef.current.style.height =
textAreaRef.current.scrollHeight + 'px';
}
}, [textAreaRef, description]);
function handleSubmit(e: FormEvent<HTMLFormElement>) {
e.preventDefault();
sendMail();
}
async function sendMail() {
setIsLoading(true);
try {
await api
.post('mail', {
json: {
fullname: name,
phone: phoneCode + phone,
email,
request: description,
},
})
.json();
setIsLoading(false);
send?.();
} catch (error) {
setIsLoading(false);
if (error instanceof Error) {
alert(error.message);
}
}
}
const placeholder = useMemo(
() =>
getExampleNumber(country, examples)
?.formatInternational()
.split(' ')
.slice(1)
.join(' '),
[country],
);
return (
<form
onSubmit={handleSubmit}
className={
inModal
? 'space-y-6'
: 'lg:space-y-12 sm:space-y-36 space-y-8 lg:max-w-[66vw] sm:max-w-[calc(369/720*100%)]'
}
>
<div className="space-y-6">
<div
className={
'grid gap-x-4 items-start ' +
(inModal ? 'gap-y-6' : 'lg:grid-cols-3 max-lg:gap-y-4')
}
>
<div className="w-full">
<label
className="m-text text-[#9299BD] select-none"
htmlFor={'name' + +inModal}
>
Имя
</label>
<input
required
id={'name' + +inModal}
type="text"
value={name}
onChange={e => setName(e.target.value)}
placeholder="Ваше имя"
className="bg-transparent border-b border-[#3D425C] focus:border-white py-4 rounded-none outline-none transition-all w-full placeholder:h4 placeholder:font-medium placeholder:select-none"
/>
</div>
<div className="w-full">
<label
className="m-text text-[#9299BD] select-none"
htmlFor={'email' + +inModal}
>
Email*
</label>
<input
required
id={'email' + +inModal}
type="text"
value={email}
onChange={e => setEmail(e.target.value)}
placeholder="Ваш email"
className="bg-transparent border-b border-[#3D425C] focus:border-white py-4 rounded-none outline-none transition-all w-full placeholder:h4 placeholder:font-medium placeholder:select-none"
/>
</div>
<div className="w-full">
<label
className="m-text text-[#9299BD] select-none"
htmlFor={'tel' + +inModal}
>
Телефон
</label>
<div className="flex gap-x-3 py-4 border-[#3D425C] relative">
<SelectPhoneCode
currentPhoneCodeAndCountry={[phoneCode, country]}
onClick={setPhoneCodeAndCountry}
/>
<div className="border-l border-[#3D425C]" />
<ReactInputMask
required
type="tel"
id={'tel' + +inModal}
mask={placeholder?.replace(/\d/g, '9') ?? ''}
maskChar={null}
value={phone}
placeholder={'XXX XXX XX XX'}
onChange={e => setPhone(e.target.value.replace(/ /g, ''))}
className="transition-all bg-transparent rounded-none outline-none h4 placeholder:h4 placeholder:font-medium placeholder:select-none peer"
/>
<div className="bottom-0 absolute w-full border-b border-[#3D425C] peer-focus:border-white -mb-px" />
</div>
</div>
</div>
<div>
<label
className="m-text text-[#9299BD] select-none"
htmlFor={'description' + +inModal}
>
Задача
</label>
<textarea
ref={textAreaRef}
id={'description' + +inModal}
placeholder="Опишите вашу задачу"
value={description}
rows={1}
onChange={e => setDescription(e.target.value)}
className="bg-transparent border-b py-4 focus:border-white max-h-[300px] h-auto rounded-none border-[#3D425C] resize-none outline-none transition-all w-full placeholder:h4 placeholder:font-medium placeholder:select-none placeholder-shown:overflow-hidden"
/>
</div>
</div>
<div className="space-y-4 lg:max-w-[25vw] sm:max-lg:mt-36">
<Button
width="full"
disabled={isLoading}
className="p-2 pl-8"
onClick={() => `ym(98611545, 'reachGoal', 'submit_form);`}
icon={
isLoading ? (
<ClassNameWrapper
element={<LoaderIcon />}
className="relative w-5 h-5 animate-spin"
/>
) : (
<div className="p-2 bg-white rounded-full">
<ClassNameWrapper
element={<ArrowRightIcon />}
className="w-5 h-5 text-black"
/>
</div>
)
}
>
Отправить
</Button>
<p className="m-text text-[#52587A]">
Нажимая кнопку отправить, вы даете
<span className="text-gradient">
{' '}
<NavLink to="/privacy-policy" target="_blank">
согласие на обработку персональных данных
</NavLink>
</span>
&nbsp;и принимаете условия&nbsp;
<span className="text-gradient">
<NavLink to="/policy" target="_blank">
политики
</NavLink>
</span>
</p>
</div>
</form>
);
}