209 lines
8.0 KiB
TypeScript
209 lines
8.0 KiB
TypeScript
import { ChangeEvent, FormEvent, useState } from "react";
|
|
import AsteriskIcon from "./icons/AsteriskIcon";
|
|
import InputMask from "react-input-mask";
|
|
import "./ContactsForm.css";
|
|
import Button from "./Button";
|
|
import SendIcon from "./icons/SendIcon";
|
|
import LoaderIcon from "./icons/LoaderIcon";
|
|
import api from "../utils/api";
|
|
import CheckGradientIcon from "./icons/CheckGradientIcon";
|
|
import useModalStore from "../stores/useModalStore";
|
|
import Close2Icon from "./icons/Close2Icon";
|
|
|
|
function ContactsForm() {
|
|
const [name, setName] = useState<string>("");
|
|
const [phone, setPhone] = useState<string>("");
|
|
const [email, setEmail] = useState<string>("");
|
|
const [description, setDescription] = useState<string>("");
|
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
|
const [isSend, setIsSend] = useState<boolean>(false);
|
|
const [setModal] = useModalStore((state) => [state.setModal]);
|
|
|
|
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 ? (
|
|
<div className="flex flex-col gap-5">
|
|
<div className="flex justify-between items-center">
|
|
<p className="font-gilroy text-gradient sm:text-2xl text-xl w-fit font-semibold">
|
|
Contact us
|
|
</p>
|
|
<button
|
|
onClick={() => setModal(null)}
|
|
className="p-2 hover:bg-white hover:bg-opacity-10 transition-colors rounded-full"
|
|
>
|
|
<Close2Icon />
|
|
</button>
|
|
</div>
|
|
<form onSubmit={handleSubmit} className="flex flex-col gap-6">
|
|
<div>
|
|
<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>Name</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={[
|
|
"feedback-field bg-transparent border rounded-none border-t-0 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",
|
|
].join(" ")}
|
|
/>
|
|
<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>Phone</span>
|
|
<AsteriskIcon />
|
|
</p>
|
|
</div>
|
|
|
|
<div className="relative">
|
|
<input
|
|
required
|
|
type="text"
|
|
value={email}
|
|
onChange={(e) => setEmail(e.target.value)}
|
|
className="feedback-field bg-transparent border rounded-none border-t-0 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">
|
|
<textarea
|
|
placeholder="Describe your task"
|
|
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] outline-none outline-1 -outline-offset-1 focus:outline-[#D375FF] transition-all w-full"
|
|
></textarea>
|
|
</div>
|
|
|
|
<div
|
|
className="border border-t-0 border-[#3D425C] 2xl:p-6 p-4 sm:mt-0 flex items-center"
|
|
style={{ marginTop: "-6px" }}
|
|
>
|
|
<div className="text-xs leading-tight">
|
|
By clicking on the "Send" button, you accept the{" "}
|
|
<a
|
|
href="https://graff.tech/privacypolicy"
|
|
target="_blank"
|
|
className="text-[#798FFF] cursor-pointer opacity-95 hover:opacity-100 transition-all"
|
|
>
|
|
terms of use
|
|
</a>{" "}
|
|
and{" "}
|
|
<a
|
|
href="https://graff.tech/privacypolicy"
|
|
target="_blank"
|
|
className="text-[#798FFF] cursor-pointer opacity-95 hover:opacity-100 transition-all"
|
|
>
|
|
privacy policy
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div className="border border-t-0 border-[#3D425C] 2xl:p-6 p-4 sm:mt-0 text-xs flex items-center gap-2">
|
|
<div className="flex gap-2">
|
|
<div className="">
|
|
<AsteriskIcon />
|
|
</div>
|
|
<p>—</p>
|
|
<p>The required fields are marked with an asterisk.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<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"
|
|
>
|
|
Send
|
|
</Button>
|
|
</form>
|
|
</div>
|
|
) : (
|
|
<div className="flex flex-col gap-4">
|
|
<div className="flex justify-between items-center">
|
|
<p className="font-gilroy text-gradient sm:text-2xl text-xl w-fit font-semibold flex items-center gap-2">
|
|
<span>The application has been sent</span>
|
|
<CheckGradientIcon className="lg:w-8 lg:h-8 w-6 h-6" />
|
|
</p>
|
|
<button
|
|
onClick={() => setModal(null)}
|
|
className="p-2 hover:bg-white hover:bg-opacity-10 transition-colors rounded-full"
|
|
>
|
|
<Close2Icon />
|
|
</button>
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-2">
|
|
<p className="font-gilroy leading-snug lg:text-2xl text-xl font-semibold">
|
|
Thank you for submitting the application!
|
|
</p>
|
|
|
|
<p className="lg:text-base text-sm">
|
|
We appreciate your interest in our company and will contact you
|
|
soon to clarify the details of the project.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</>
|
|
);
|
|
}
|
|
|
|
export default ContactsForm;
|