223 lines
7.1 KiB
TypeScript
223 lines
7.1 KiB
TypeScript
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>
|
||
и принимаете условия
|
||
<span className="text-gradient">
|
||
<NavLink to="/policy" target="_blank">
|
||
политики
|
||
</NavLink>
|
||
</span>
|
||
</p>
|
||
</div>
|
||
</form>
|
||
);
|
||
}
|