239 lines
8.6 KiB
TypeScript
239 lines
8.6 KiB
TypeScript
import InputMask from 'react-input-mask';
|
||
import { ChangeEvent, FormEvent, useState } from 'react';
|
||
import { api } from '../../api/contactsFormInstance';
|
||
import { AsteriskIcon } from '../icons/AstreskIcon';
|
||
import { LoaderIcon } from '../icons/LoaderIcon';
|
||
import { SendIcon } from '../icons/SendIcon';
|
||
import { Button } from '../../ui/Button';
|
||
import { CheckGradientIcon } from '../icons/CheckGradientIcon';
|
||
|
||
export function ContactsForm({ inModal = false }: { inModal?: boolean }) {
|
||
const [name, setName] = useState('');
|
||
const [phone, setPhone] = useState('');
|
||
const [email, setEmail] = useState('');
|
||
const [description, setDescription] = useState('');
|
||
const [isLoading, setIsLoading] = useState(false);
|
||
const [isSend, setIsSend] = useState(false);
|
||
|
||
function handleSubmit(e: FormEvent<HTMLFormElement>) {
|
||
e.preventDefault();
|
||
|
||
sendMail();
|
||
}
|
||
|
||
async function sendMail() {
|
||
setIsLoading(true);
|
||
|
||
try {
|
||
await api
|
||
.post('mail', {
|
||
json: {
|
||
fullname: name,
|
||
phone,
|
||
email,
|
||
request: description,
|
||
},
|
||
})
|
||
.json();
|
||
|
||
setIsSend(true);
|
||
setIsLoading(false);
|
||
} catch (error) {
|
||
setIsLoading(false);
|
||
if (error instanceof Error) {
|
||
alert(error.message);
|
||
}
|
||
}
|
||
}
|
||
|
||
return (
|
||
<>
|
||
{!isSend ? (
|
||
<form
|
||
onSubmit={handleSubmit}
|
||
className={
|
||
'grid' +
|
||
(!inModal
|
||
? ' lg:grid-cols-3 sm:grid-cols-2 lg:col-span-9 sm:col-span-full sm:mb-20 mb-14'
|
||
: '')
|
||
}
|
||
>
|
||
<div className="relative">
|
||
<input
|
||
required
|
||
type="text"
|
||
value={name}
|
||
onChange={e => setName(e.target.value)}
|
||
className="feedback-field bg-transparent border border-[#3D425C] rounded-none sm:pt-12 sm:pb-4 sm:px-4 pt-8 pb-3 px-3 outline-none outline-1 -outline-offset-1 focus:outline-[#D375FF] transition-all w-full"
|
||
/>
|
||
<p className="feedback-placeholder lg:text-base text-sm absolute sm:pt-4 sm:pb-4 sm:px-4 sm:top-4 pt-3 pb-3 px-3 top-3 w-full opacity-50 transition-all pointer-events-none flex justify-between items-center">
|
||
<span>Имя</span>
|
||
<AsteriskIcon />
|
||
</p>
|
||
</div>
|
||
|
||
<div className="relative">
|
||
<InputMask
|
||
required
|
||
type="tel"
|
||
mask={'+999999999999999'}
|
||
maskChar={null}
|
||
value={phone}
|
||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
||
setPhone(e.target.value)
|
||
}
|
||
className={
|
||
(!inModal
|
||
? 'sm:border-l-0 lg:border-x-0 border max-sm:border-t-0 '
|
||
: 'border-x ') +
|
||
'feedback-field bg-transparent rounded-none border-[#3D425C] sm:pt-12 sm:pb-4 sm:px-4 pt-8 pb-3 px-3 outline-none outline-1 -outline-offset-1 focus:outline-[#D375FF] transition-all w-full'
|
||
}
|
||
/>
|
||
<p className="feedback-placeholder lg:text-base text-sm absolute sm:pt-4 sm:pb-4 sm:px-4 sm:top-4 pt-3 pb-3 px-3 top-3 w-full opacity-50 transition-all pointer-events-none flex justify-between items-center">
|
||
<span>Телефон</span>
|
||
<AsteriskIcon />
|
||
</p>
|
||
</div>
|
||
|
||
<div
|
||
className={'relative' + (!inModal ? ' sm:max-lg:col-span-2' : '')}
|
||
>
|
||
<input
|
||
required
|
||
type="text"
|
||
value={email}
|
||
onChange={e => setEmail(e.target.value)}
|
||
className={
|
||
(!inModal ? 'max-lg:border-t-0' : '') +
|
||
' feedback-field bg-transparent border rounded-none border-[#3D425C] sm:pt-12 sm:pb-4 sm:px-4 pt-8 pb-3 px-3 outline-none outline-1 -outline-offset-1 focus:outline-[#D375FF] transition-all w-full'
|
||
}
|
||
/>
|
||
<p className="feedback-placeholder lg:text-base text-sm absolute sm:pt-4 sm:pb-4 sm:px-4 sm:top-4 pt-3 pb-3 px-3 top-3 w-full opacity-50 transition-all pointer-events-none flex justify-between items-center">
|
||
<span>Email</span>
|
||
<AsteriskIcon />
|
||
</p>
|
||
</div>
|
||
|
||
<div
|
||
className={
|
||
'relative' + (!inModal ? ' lg:col-span-3 sm:col-span-2' : '')
|
||
}
|
||
>
|
||
<textarea
|
||
placeholder="Опишите вашу задачу"
|
||
value={description}
|
||
onChange={e => setDescription(e.target.value)}
|
||
className="feedback-field bg-transparent resize-none border rounded-none border-t-0 border-[#3D425C] p-4 sm:min-h-[192px] min-h-[128px] h-full outline-none outline-1 -outline-offset-1 focus:outline-[#D375FF] transition-all w-full"
|
||
></textarea>
|
||
</div>
|
||
|
||
<div
|
||
className={
|
||
!inModal ? 'col-start-1 max-lg:hidden pt-4 pr-4' : 'hidden'
|
||
}
|
||
>
|
||
<Button
|
||
width="full"
|
||
disabled={isLoading}
|
||
icon={
|
||
isLoading ? (
|
||
<LoaderIcon className="relative 2xl:w-8 2xl:h-8 w-6 h-6 animate-spin" />
|
||
) : (
|
||
<SendIcon className="relative 2xl:w-8 2xl:h-8 w-6 h-6" />
|
||
)
|
||
}
|
||
className="py-4"
|
||
>
|
||
Отправить
|
||
</Button>
|
||
</div>
|
||
|
||
<div
|
||
className={
|
||
(!inModal
|
||
? 'border sm:border-r-0 max-sm:border-b-0 sm:border-t-0 lg:col-start-2 max-sm:mt-6'
|
||
: 'mt-6 border-x') +
|
||
' border-[#3D425C] 2xl:p-6 p-4 sm:mt-0 flex items-center'
|
||
}
|
||
>
|
||
<div className="text-xs desktop-figma:text-base leading-tight">
|
||
Нажимая кнопку отправить, вы принимаете{' '}
|
||
<a
|
||
href="https://graff.tech/privacypolicy"
|
||
target="_blank"
|
||
className="text-[#798FFF] cursor-pointer opacity-95 lg:hover:opacity-100 transition-all"
|
||
>
|
||
условия использования
|
||
</a>{' '}
|
||
и{' '}
|
||
<a
|
||
href="https://graff.tech/privacypolicy"
|
||
target="_blank"
|
||
className="text-[#798FFF] cursor-pointer opacity-95 lg:hover:opacity-100 transition-all"
|
||
>
|
||
политику конфиденциальности
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div
|
||
className={
|
||
(!inModal ? 'sm:border-t-0 border-t lg:col-start-3' : '') +
|
||
' border border-[#3D425C] 2xl:p-6 p-4 sm:mt-0 text-xs desktop-figma:text-base flex items-center gap-2 '
|
||
}
|
||
>
|
||
<div className="flex gap-2">
|
||
<div className="">
|
||
<AsteriskIcon />
|
||
</div>
|
||
<p>—</p>
|
||
<p>
|
||
Звездочкой отмечены обязательные
|
||
<br />
|
||
для заполнения поля
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div className={(!inModal ? 'lg:hidden sm:pr-4' : '') + ' pt-4'}>
|
||
<Button
|
||
width="full"
|
||
disabled={isLoading}
|
||
icon={
|
||
isLoading ? (
|
||
<LoaderIcon className="relative 2xl:w-8 2xl:h-8 w-6 h-6 animate-spin" />
|
||
) : (
|
||
<SendIcon className="relative 2xl:w-8 2xl:h-8 w-6 h-6" />
|
||
)
|
||
}
|
||
className="py-4"
|
||
>
|
||
Отправить
|
||
</Button>
|
||
</div>
|
||
</form>
|
||
) : (
|
||
<div className="flex flex-col gap-4">
|
||
<div className="flex justify-between items-center">
|
||
<p className="text-gradient sm:text-2xl text-xl w-fit font-semibold flex items-center gap-2">
|
||
<span>Заявка отправлена</span>
|
||
<CheckGradientIcon className="lg:w-8 lg:h-8 w-6 h-6" />
|
||
</p>
|
||
</div>
|
||
|
||
<div className="flex flex-col gap-2">
|
||
<p className="leading-snug lg:text-2xl text-xl font-semibold">
|
||
Спасибо за подачу заявки!
|
||
</p>
|
||
|
||
<p className="lg:text-base text-sm">
|
||
Мы ценим ваш интерес к нашей компании и в ближайшее время свяжемся
|
||
с вами для уточнения деталей проекта.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</>
|
||
);
|
||
}
|