259 lines
8.8 KiB
TypeScript
259 lines
8.8 KiB
TypeScript
import {
|
||
FormEvent,
|
||
useCallback,
|
||
useEffect,
|
||
useMemo,
|
||
useRef,
|
||
useState,
|
||
} from 'react';
|
||
import ReactInputMask from 'react-input-mask';
|
||
import { ArrowRightIcon } from '../icons/ArrowRightIcon';
|
||
import { CloseIcon } from '../icons/CloseIcon';
|
||
import { LoaderIcon } from '../icons/LoaderIcon';
|
||
import { api } from '../../api/contactsFormInstance';
|
||
import { Button } from '../../ui/Button';
|
||
import { useModalStore } from '../../store/modalStore';
|
||
import { SelectPhoneCode } from './SelectPhoneCode';
|
||
import { Country } from 'react-phone-number-input';
|
||
import { getExampleNumber } from 'libphonenumber-js';
|
||
import examples from 'libphonenumber-js/mobile/examples';
|
||
import { ClassNameWrapper } from '../../hocs/ClassNameWrapper';
|
||
import { NavLink } from 'react-router-dom';
|
||
|
||
export function ModalWithForm() {
|
||
const { setModal } = useModalStore();
|
||
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 [isSend, setIsSend] = useState(false);
|
||
|
||
const placeholder = useMemo(
|
||
() =>
|
||
getExampleNumber(country, examples)
|
||
?.formatInternational()
|
||
.split(' ')
|
||
.slice(1)
|
||
.join(' '),
|
||
[country],
|
||
);
|
||
|
||
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();
|
||
|
||
setIsSend(true);
|
||
setIsLoading(false);
|
||
} catch (error) {
|
||
setIsLoading(false);
|
||
if (error instanceof Error) {
|
||
alert(error.message);
|
||
}
|
||
}
|
||
}
|
||
|
||
const listener = useCallback(
|
||
(e: KeyboardEvent) => {
|
||
if (e.key === 'Escape') {
|
||
setModal(false);
|
||
}
|
||
},
|
||
[setModal],
|
||
);
|
||
|
||
useEffect(() => {
|
||
document.addEventListener('keydown', listener);
|
||
return () => document.removeEventListener('keydown', listener);
|
||
}, [listener, setModal]);
|
||
|
||
return (
|
||
<div className="fixed flex flex-col gap-4 top-0 right-0 h-full sm:w-[408px] w-full bg-[#14161F] overflow-y-auto sm:p-8 p-6">
|
||
{!isSend ? (
|
||
<div className="space-y-8">
|
||
<div className="flex items-center justify-between">
|
||
<p className="font-medium accent">Оставьте заявку</p>
|
||
<button
|
||
onClick={() => setModal(null)}
|
||
className="p-2 transition-colors rounded-full lg:hover:bg-white lg:hover:bg-opacity-10"
|
||
>
|
||
<CloseIcon />
|
||
</button>
|
||
</div>
|
||
<form onSubmit={handleSubmit}>
|
||
<div className="space-y-6">
|
||
<hr className="border-[#3D425C]" />
|
||
|
||
<div>
|
||
<label
|
||
className="m-text text-[#9299BD] select-none"
|
||
htmlFor="name"
|
||
>
|
||
Имя
|
||
</label>
|
||
<input
|
||
required
|
||
id="name"
|
||
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>
|
||
<label
|
||
className="m-text text-[#9299BD] select-none"
|
||
htmlFor="tel"
|
||
>
|
||
Телефон
|
||
</label>
|
||
<div className="flex gap-x-3 py-2 border-[#3D425C] relative">
|
||
<SelectPhoneCode
|
||
currentPhoneCodeAndCountry={[phoneCode, country]}
|
||
onClick={setPhoneCodeAndCountry}
|
||
/>
|
||
<div className="border-l border-[#3D425C]" />
|
||
<ReactInputMask
|
||
required
|
||
type="tel"
|
||
id={'tel'}
|
||
mask={placeholder?.replace(/\d/g, '9') ?? ''}
|
||
maskChar={null}
|
||
value={phone}
|
||
placeholder={placeholder}
|
||
onChange={e => setPhone(e.target.value.replace(/ /g, ''))}
|
||
className="w-full 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-2" />
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label
|
||
className="m-text text-[#9299BD] select-none"
|
||
htmlFor="email"
|
||
>
|
||
Email*
|
||
</label>
|
||
<input
|
||
required
|
||
id="email"
|
||
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>
|
||
<label
|
||
className="m-text text-[#9299BD] select-none"
|
||
htmlFor="description"
|
||
>
|
||
Задача
|
||
</label>
|
||
<textarea
|
||
ref={textAreaRef}
|
||
id="description"
|
||
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>
|
||
|
||
<Button
|
||
width="full"
|
||
disabled={isLoading}
|
||
className="px-6 py-3 mt-12"
|
||
onClick={() => `ym(98587267, 'reachGoal', 'submit_form');`}
|
||
icon={
|
||
isLoading ? (
|
||
<LoaderIcon className="relative w-6 h-6 animate-spin" />
|
||
) : (
|
||
<div className="p-2 bg-white rounded-full">
|
||
<ClassNameWrapper
|
||
element={<ArrowRightIcon />}
|
||
className="text-black"
|
||
/>
|
||
</div>
|
||
)
|
||
}
|
||
>
|
||
Отправить
|
||
</Button>
|
||
<p className="m-caption mt-6 text-center 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>
|
||
</form>
|
||
</div>
|
||
) : (
|
||
<div className="">
|
||
<div className="space-y-8">
|
||
<h2 className="font-medium h2">Спасибо за отправку заявки!</h2>
|
||
<p className="m-text">
|
||
Мы ценим ваш интерес к нашей компании и в ближайшее время свяжемся
|
||
с вами для уточнения деталей проекта.
|
||
</p>
|
||
</div>
|
||
<Button
|
||
width="full"
|
||
className="py-5 px-6 absolute top-[50vh]"
|
||
icon={<ArrowRightIcon className="w-6 h-6" />}
|
||
onClick={() => setModal(false)}
|
||
>
|
||
На главную
|
||
</Button>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|