V pre0.3_8

main
Саске Учиха 3 years ago
parent f6a6c5d329
commit 87f49f8276

@ -25,6 +25,12 @@ Talent Team - разработка корпоративных мероприят
- Отзывы заменены на благодарственные письма. - Отзывы заменены на благодарственные письма.
- Пункты меню в Header и Footer теперь меняют цвет при наведении. - Пункты меню в Header и Footer теперь меняют цвет при наведении.
- Исправлен баг из-за которого при нажатии на пункты в бургер меню, само меню не закрывалось. (баг с pre0.3_4) - Исправлен баг из-за которого при нажатии на пункты в бургер меню, само меню не закрывалось. (баг с pre0.3_4)
- Изменено положение логотипа и добавлены контакты WhatsApp и Telegram в header и footer.
- Создан компонент для модальных форм.
- Добавлен функционал для формы связи.
- В разделе бизнес игры кнопка "Заказать игру", теперь открывает модальное окно с формой связи.
- Теперь при нажатии на благодарственные письма они открываются в модальном окне.
- Оптимизированы картинки в баннере на главной странице.
#### V 0.2 #### V 0.2
- Добавлен блоки "Связаться с нами" и Футер сайта - Добавлен блоки "Связаться с нами" и Футер сайта

5617
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -17,7 +17,9 @@
"gatsby-transformer-sharp": "^5.7.0", "gatsby-transformer-sharp": "^5.7.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-hook-form": "^7.45.0",
"react-icons": "^4.7.1", "react-icons": "^4.7.1",
"react-input-mask": "^2.0.4",
"react-scroll": "^1.8.9", "react-scroll": "^1.8.9",
"react-type-animation": "^3.0.1", "react-type-animation": "^3.0.1",
"swiper": "^9.3.2", "swiper": "^9.3.2",

@ -0,0 +1,25 @@
<?php
//Временно, в проде CORS headers удалить
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, DELETE, PUT, PATCH, OPTIONS');
header('Access-Control-Allow-Headers: token, Content-Type');
header('Access-Control-Max-Age: 1728000');
header('Content-Length: 0');
header('Content-Type: text/plain');
die();
}
header('Access-Control-Allow-Origin: *');
header('Content-Type: application/json');
$postData = file_get_contents('php://input');
$data = json_decode($postData, true);
if($data) {
$message = "Фио: ". $data['fio'] ."\r\nE-mail: ". $data['email'] ."\r\nНомер телефона: ". $data['phone'] ."\r\nОбращение: ". $data['message'] ."\r\n";
mail('mrfrumpusslive@gmail.com', 'Заявка', $message);
echo json_encode('{"status": "true"}');
}
?>

@ -0,0 +1,22 @@
const submitForm = async (data) => {
const response = await fetch("http://localhost:8080/form.php", {
method: "POST",
mode: "cors",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data)
})
.then((response) => {
if (!response.ok) {
throw new Error('Error!');
}
return response.status;
})
.catch(() => {
return 500;
})
return response;
}
export default submitForm;

@ -0,0 +1,10 @@
const EmailValidation = {
required: "Поле должно быть заполнено",
pattern: {
value: /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<,>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, //eslint-disable-line
message: "Не валидный email"
},
}
export { EmailValidation }

@ -15,7 +15,7 @@ const About = ({...props}) => {
<p className='mb-5 max-md:mb-4 max-md:text-sm'>Мы убеждены, что каждое проводимое корпоративное мероприятие должны быть максимально полезны, придерживаясь главной цели это развитие персонала. Ведь, развивая персонал, мы открываем для бизнеса новые возможности.</p> <p className='mb-5 max-md:mb-4 max-md:text-sm'>Мы убеждены, что каждое проводимое корпоративное мероприятие должны быть максимально полезны, придерживаясь главной цели это развитие персонала. Ведь, развивая персонал, мы открываем для бизнеса новые возможности.</p>
</div> </div>
<div className='basis-1/2 lg:ml-5 max-lg:flex max-lg:flex-col max-lg:items-center max-lg:w-full max-md:mt-0 max-lg:mt-5'> <div className='basis-1/2 lg:ml-5 max-lg:flex max-lg:flex-col max-lg:items-center max-lg:w-full max-md:mt-0 max-lg:mt-5'>
<iframe className='w-full h-[500px] max-sm:h-[250px] max-md:h-[350px] mb-7 rounded-lg' src="https://www.youtube-nocookie.com/embed/0zMLl9WbHVg" title="YouTube video player" allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowFullScreen></iframe> <iframe className='w-full h-[500px] max-sm:h-[250px] max-md:h-[350px] mb-7 rounded-lg' src="https://www.youtube-nocookie.com/embed/0zMLl9WbHVg" loading="lazy" title="YouTube video player" allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowFullScreen></iframe>
<Button className='max-sm:w-full w-52'>Наши услуги</Button> <Button className='max-sm:w-full w-52'>Наши услуги</Button>
</div> </div>
</div> </div>

@ -1,28 +1,46 @@
import React from "react"; import React, { useState } from "react";
import { useForm } from "react-hook-form"
import InputMask from 'react-input-mask';
import { EmailValidation } from "../Utils/ValidationRules";
import submitForm from "../Services/submitForm";
import Button from "./UI/Button"; import Button from "./UI/Button";
import Input from "./UI/Input"; import Input from "./UI/Input";
import Textarea from "./UI/Textarea"; import Textarea from "./UI/Textarea";
import SubmitFormModal from "./Modals/SubmitFormModal";
const Contact = ({white, ...props}) => { const Contact = ({white, ...props}) => {
const [modal, setModal] = useState(false);
const [error, setError] = useState(false);
const { register, handleSubmit, setValue, watch, formState: { isSubmitting, errors } } = useForm()
const onSubmit = async (data) => {
const status = await submitForm(data);
status === 200 ? setError(false) : setError(true);
setModal(true);
}
return ( return (
<section {...props} className={white ? 'py-32 max-md:py-16' : 'py-32 max-md:py-16 bg-[#0E0808]'}> <section {...props} className={white ? 'py-32 max-md:py-16' : 'py-32 max-md:py-16 bg-[#0E0808]'}>
<div className="container mx-auto px-24 max-md:px-3"> <form onSubmit={handleSubmit(onSubmit)} className="container mx-auto px-24 max-md:px-3">
<h2 className={white ? 'text-3xl max-md:text-2xl mb-16 max-md:mb-8 font-semibold text-slate-800 text-center uppercase' : 'text-3xl max-md:text-2xl mb-16 max-md:mb-8 font-semibold text-white text-center uppercase'}>Связаться с нами</h2> <h2 className={white ? 'text-3xl max-md:text-2xl mb-16 max-md:mb-8 font-semibold text-slate-800 text-center uppercase' : 'text-3xl max-md:text-2xl mb-16 max-md:mb-8 font-semibold text-white text-center uppercase'}>Связаться с нами</h2>
<div className="flex max-md:flex-col mb-10 max-md:mb-8"> <div className="flex max-md:flex-col mb-10 max-md:mb-8">
<div className="flex flex-col basis-[40%] mr-6 max-md:mr-0"> <div className="flex flex-col basis-[40%] mr-6 max-md:mr-0">
<Input labelClasses={white ? '!text-black' : null} className={white ? 'mb-5 border-2 !border-slate-700' : 'mb-5'} label='Ваше ФИО:' placeholder='Иванов Иван Иванович'></Input> <Input type="text" register={register} errors={errors} validation={{required: "Поле должно быть заполнено"}} name="fio" labelClasses={white ? '!text-black' : null} wrapperClasses='mb-5' className={white ? 'border-2 !border-slate-700' : null} label='Ваше ФИО:' placeholder='Иванов Иван Иванович'></Input>
<Input labelClasses={white ? '!text-black' : null} className={white ? 'mb-5 border-2 !border-slate-700' : 'mb-5'} label='Ваш Email:' placeholder='exampel@yandex.ru'></Input> <Input type="text" register={register} errors={errors} validation={EmailValidation} name="email" labelClasses={white ? '!text-black' : null} wrapperClasses='mb-5' className={white ? 'border-2 !border-slate-700' : null} label='Ваш Email:' placeholder='exampel@yandex.ru'></Input>
<Input labelClasses={white ? '!text-black' : null} className={white ? 'max-md:mb-5 border-2 !border-slate-700' : 'max-md:mb-5'} label='Ваш телефон:' placeholder='+7 (999) 999 99-99'></Input>
<InputMask mask="+7 (999) 999 99 99" value={watch("phone", false)} onChange={e=> setValue("phone", e.target.value)}>
{() => <Input type="phone" register={register} errors={errors} validation={{required: "Поле должно быть заполнено"}} name="phone" labelClasses={white ? '!text-black' : null} wrapperClasses='max-md:mb-5' className={white ? 'border-2 !border-slate-700' : null} label='Ваш телефон:' placeholder='+7 (999) 999 99-99'></Input>}
</InputMask>
</div> </div>
<div className="flex basis-[60%]"> <div className="flex basis-[60%]">
<Textarea labelClasses={white ? '!text-black' : null} className={white ? 'h-full resize-none border-2 !border-slate-700' : 'h-full resize-none'} label='Ваше обращение:' placeholder='Обращение...'></Textarea> <Textarea register={register} errors={errors} name="message" labelClasses={white ? '!text-black' : null} className={white ? 'h-full resize-none border-2 !border-slate-700' : 'h-full resize-none'} label='Ваше обращение:' placeholder='Обращение...'></Textarea>
</div> </div>
</div> </div>
<div className="flex justify-center"> <div className="flex justify-center">
<Button>Отправить форму</Button> <Button disabled={isSubmitting}>Отправить форму</Button>
</div>
</div> </div>
</form>
<SubmitFormModal modal={modal} setModal={setModal} error={error}/>
</section> </section>
) )
} }

@ -1,9 +1,11 @@
import React from 'react'; import React, { useState } from 'react';
import { StaticImage } from 'gatsby-plugin-image'; import { StaticImage } from 'gatsby-plugin-image';
import Button from '../UI/Button'; import Button from '../UI/Button';
import { Link } from 'gatsby'; import { Link } from 'gatsby';
import ContactFormModal from '../Modals/ContactFormModal';
const RecGame = () => { const RecGame = () => {
const [modal, setModal] = useState(false);
return ( return (
<div className="flex max-lg:flex-col px-20 max-lg:px-3 items-center lg:items-start xl:items-center"> <div className="flex max-lg:flex-col px-20 max-lg:px-3 items-center lg:items-start xl:items-center">
<StaticImage loading='lazy' src="../../images/games/ei.jfif" alt="logo" placeholder="none" className="max-lg:!hidden w-full basis-1/2 rounded-lg mr-5"/> <StaticImage loading='lazy' src="../../images/games/ei.jfif" alt="logo" placeholder="none" className="max-lg:!hidden w-full basis-1/2 rounded-lg mr-5"/>
@ -19,10 +21,11 @@ const RecGame = () => {
<li>управление эмоциями других людей.</li> <li>управление эмоциями других людей.</li>
</ul> </ul>
<div className="flex items-center"> <div className="flex items-center">
<Button className='mr-5'>Заказать игру</Button> <Button className='mr-5' onClick={()=> setModal(true)}>Заказать игру</Button>
<Link to="/games/emotional-intelligence" className='text-slate-800 hover:text-yellow-500 transition'>подробнее</Link> <Link to="/games/emotional-intelligence" className='text-slate-800 hover:text-yellow-500 transition'>подробнее</Link>
</div> </div>
</div> </div>
<ContactFormModal modal={modal} setModal={setModal}/>
</div> </div>
); );
} }

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { AiFillCheckCircle } from "react-icons/ai"; import { AiFillCheckCircle } from "react-icons/ai";
import { useStaticQuery, graphql } from "gatsby" import { useStaticQuery, graphql } from "gatsby"
import { getImage} from 'gatsby-plugin-image'; import { StaticImage} from 'gatsby-plugin-image';
import { TypeAnimation } from 'react-type-animation'; import { TypeAnimation } from 'react-type-animation';
import { motion } from "framer-motion" import { motion } from "framer-motion"
import { SlArrowDown } from "react-icons/sl"; import { SlArrowDown } from "react-icons/sl";
@ -12,29 +12,26 @@ const Hero = ({...props}) => {
query { query {
hero1: file(relativePath: {eq: "hero1.jpg"}) { hero1: file(relativePath: {eq: "hero1.jpg"}) {
childImageSharp { childImageSharp {
gatsbyImageData(formats: WEBP, quality: 90) fluid(maxWidth: 2500, toFormat: WEBP) {
...GatsbyImageSharpFluid,
} }
},
hero2: file(relativePath: {eq: "hero2.jpg"}) {
childImageSharp {
gatsbyImageData(formats: JPG, quality: 80)
} }
}, },
hero3: file(relativePath: {eq: "hero2.jpg"}) { hero2: file(relativePath: {eq: "hero2.jpg"}) {
childImageSharp { childImageSharp {
fluid { fluid(maxWidth: 2500, toFormat: WEBP) {
...GatsbyImageSharpFluid ...GatsbyImageSharpFluid,
} }
} }
}, },
}`); }`);
return ( return (
<> <>
<section {...props} className='pt-28 flex h-[calc(100vh-36px)] pb-16 relative mt-9 min-h-[650px]'> <section {...props} className='pt-28 flex h-[calc(100vh-36px)] pb-16 relative mt-9 min-h-[650px]'>
<div className="container max-sm:px-3 mx-auto items-center flex justify-center relative "> <div className="container max-sm:px-3 mx-auto items-center flex justify-center relative ">
<div className="flex flex-col items-center max-w-5xl pb-16"> <div className="flex flex-col items-center max-w-5xl pb-28">
<StaticImage src="../images/logo.svg" alt="logo" placeholder="none" className="w-40 max-md:w-28 mb-5"/>
<TypeAnimation <TypeAnimation
sequence={[ sequence={[
'Мы помогаем создавать команды', 'Мы помогаем создавать команды',
@ -57,8 +54,8 @@ const Hero = ({...props}) => {
<AnchorLink to="advantages" smooth={true} className="cursor-pointer absolute bottom-16 left-1/2 -translate-x-1/2 w-16 h-16 max-sm:w-12 max-sm:h-12 border-white border-2 rounded-full flex items-center justify-center"> <AnchorLink to="advantages" smooth={true} className="cursor-pointer absolute bottom-16 left-1/2 -translate-x-1/2 w-16 h-16 max-sm:w-12 max-sm:h-12 border-white border-2 rounded-full flex items-center justify-center">
<motion.div animate={{y: [0, 5, 0],}} transition={{ ease: "linear", duration: 2, repeat: Infinity }}><SlArrowDown className='text-white text-xl max-sm:text-base'></SlArrowDown></motion.div> <motion.div animate={{y: [0, 5, 0],}} transition={{ ease: "linear", duration: 2, repeat: Infinity }}><SlArrowDown className='text-white text-xl max-sm:text-base'></SlArrowDown></motion.div>
</AnchorLink> </AnchorLink>
<motion.div animate={{ opacity: 0 }} transition={{ delay: 5.5 }} style={{'--image-url': `url(${getImage(images.hero1).images.fallback.src})`}} className="-z-10 bg-fixed absolute w-full h-full top-0 bg-no-repeat bg-cover max-sm:bg-center left-0 bg-[linear-gradient(to_bottom,rgba(14,8,8,0.52),rgba(14,8,8,1)),var(--image-url)]"></motion.div> <motion.div animate={{ opacity: 0 }} transition={{ delay: 5.5 }} style={{'--image-url': `url(${images.hero1.childImageSharp.fluid.src})`}} className="-z-10 bg-fixed absolute w-full h-full top-0 bg-no-repeat bg-cover max-sm:bg-center left-0 bg-[linear-gradient(to_bottom,rgba(14,8,8,0.52),rgba(14,8,8,1)),var(--image-url)]"></motion.div>
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ delay: 5.6 }} style={{'--image-url': `url(${getImage(images.hero2).images.fallback.src})`}} className="-z-10 bg-fixed bg-no-repeat bg-cover max-sm:bg-center absolute w-full h-full top-0 left-0 bg-[linear-gradient(to_bottom,rgba(14,8,8,0.52),rgba(14,8,8,1)),var(--image-url)]"></motion.div> <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ delay: 5.6 }} style={{'--image-url': `url(${images.hero2.childImageSharp.fluid.src})`}} className="-z-10 bg-fixed bg-no-repeat bg-cover max-sm:bg-center absolute w-full h-full top-0 left-0 bg-[linear-gradient(to_bottom,rgba(14,8,8,0.52),rgba(14,8,8,1)),var(--image-url)]"></motion.div>
</section> </section>
</> </>
); );

@ -0,0 +1,55 @@
import React, { useState } from "react";
import { useForm } from "react-hook-form"
import InputMask from 'react-input-mask';
import { EmailValidation } from "../../Utils/ValidationRules";
import submitForm from "../../Services/submitForm";
import Modal from '../UI/Modal';
import Button from "../UI/Button";
import Input from "../UI/Input";
import Textarea from "../UI/Textarea";
import SubmitFormModal from "../Modals/SubmitFormModal";
const ContactFormModal = ({ modal, setModal }) => {
const [modal2, setModal2] = useState(false);
const [error, setError] = useState(false);
const { register, handleSubmit, setValue, watch, formState: { isSubmitting, errors } } = useForm()
const onSubmit = async (data) => {
const status = await submitForm(data);
status === 200 ? setError(false) : setError(true);
setModal(false);
setModal2(true);
}
return (
<>
<Modal active={modal} setActive={setModal} className='items-center max-w-[1000px]'>
<form onSubmit={handleSubmit(onSubmit)} className="container mx-auto px-24 max-md:px-3">
<h2 className='text-3xl max-md:text-2xl mb-16 max-md:mb-8 font-semibold text-slate-800 text-center uppercase'>Связаться с нами</h2>
<div className="flex max-md:flex-col mb-10 max-md:mb-8">
<div className="flex flex-col basis-[40%] mr-6 max-md:mr-0">
<Input type="text" register={register} errors={errors} validation={{required: "Поле должно быть заполнено"}} name="fio" labelClasses='!text-black' wrapperClasses='mb-5' className='border-2 !border-slate-700' label='Ваше ФИО:' placeholder='Иванов Иван Иванович'></Input>
<Input type="text" register={register} errors={errors} validation={EmailValidation} name="email" labelClasses='!text-black' wrapperClasses='mb-5' className='border-2 !border-slate-700' label='Ваш Email:' placeholder='exampel@yandex.ru'></Input>
<InputMask mask="+7 (999) 999 99 99" value={watch("phone", false)} onChange={e=> setValue("phone", e.target.value)}>
{() => <Input type="phone" register={register} errors={errors} validation={{required: "Поле должно быть заполнено"}} name="phone" labelClasses='!text-black' wrapperClasses='max-md:mb-5' className='border-2 !border-slate-700' label='Ваш телефон:' placeholder='+7 (999) 999 99-99'></Input>}
</InputMask>
</div>
<div className="flex basis-[60%]">
<Textarea register={register} errors={errors} name="message" labelClasses='!text-black' className='h-full resize-none border-2 !border-slate-700' label='Ваше обращение:' placeholder='Обращение...'></Textarea>
</div>
</div>
<div className="flex justify-center">
<Button disabled={isSubmitting}>Отправить форму</Button>
</div>
</form>
</Modal>
<SubmitFormModal modal={modal2} setModal={setModal2} error={error}/>
</>
)
}
export default ContactFormModal;

@ -0,0 +1,27 @@
import React from 'react'
import Modal from '../UI/Modal';
import { AiOutlineCheckCircle } from "react-icons/ai";
import { BiErrorCircle } from "react-icons/bi";
const SubmitFormModal = ({ modal, setModal, error }) => {
return (
<Modal active={modal} setActive={setModal} className='items-center'>
{error
?
<>
<BiErrorCircle className='text-red-500 text-8xl mb-3'/>
<h2 className='text-4xl font-medium mb-3'>Ошибка!</h2>
<p className='text-lg text-center'>При отправке заявки произошла ошибка. Повторите попытку через несколько минут.</p>
</>
:
<>
<AiOutlineCheckCircle className='text-green-400 text-8xl mb-3'/>
<h2 className='text-4xl font-medium mb-3'>Успех!</h2>
<p className='text-lg text-center'>Ваша заявка отправлена, мы свяжемся с вами в ближайшее время.</p>
</>
}
</Modal>
)
}
export default SubmitFormModal;

@ -2,9 +2,9 @@ import React from 'react'
import { GatsbyImage, getImage } from 'gatsby-plugin-image'; import { GatsbyImage, getImage } from 'gatsby-plugin-image';
const Review = ({ img }) => { const Review = ({ img, ...props }) => {
return ( return (
<div className="h-full border-2 rounded-md p-5 border-[#0E0808] relative flex justify-center"> <div className="h-full border-2 rounded-md p-5 border-[#0E0808] relative flex justify-center cursor-pointer" {...props}>
<GatsbyImage className='rounded-lg mb-2' loading='lazy' imgClassName="!object-contain" image={getImage(img)} alt="logo" placeholder="none"/> <GatsbyImage className='rounded-lg mb-2' loading='lazy' imgClassName="!object-contain" image={getImage(img)} alt="logo" placeholder="none"/>
</div> </div>
) )

@ -1,11 +1,13 @@
import React, { useState } from 'react' import React, { useState } from 'react'
import { Swiper, SwiperSlide } from 'swiper/react'; import { Swiper, SwiperSlide } from 'swiper/react';
import { useStaticQuery, graphql } from "gatsby" import { useStaticQuery, graphql } from "gatsby"
import { GatsbyImage, getImage } from 'gatsby-plugin-image';
import { Autoplay } from 'swiper'; import { Autoplay } from 'swiper';
import 'swiper/css'; import 'swiper/css';
import 'swiper/css/autoplay'; import 'swiper/css/autoplay';
import Review from './Review'; import Review from './Review';
import Modal from '../UI/Modal';
const Reviews = () => { const Reviews = () => {
const images = useStaticQuery(graphql` const images = useStaticQuery(graphql`
@ -38,6 +40,9 @@ const Reviews = () => {
{id: 3, img: images.img4}, {id: 3, img: images.img4},
]) ])
const [modal, setModal] = useState(false);
const [modalImg, setModalImg] = useState('');
return ( return (
<section className='py-24 max-md:py-16 max-md:px-3'> <section className='py-24 max-md:py-16 max-md:px-3'>
<div className="container mx-auto"> <div className="container mx-auto">
@ -53,11 +58,18 @@ const Reviews = () => {
}}> }}>
{reviews.map(review=> {reviews.map(review=>
<SwiperSlide key={review.id} className='!h-auto'> <SwiperSlide key={review.id} className='!h-auto'>
<Review key={review.id} img={review.img} ></Review> <Review key={review.id} img={review.img} onClick={()=>{setModalImg(review.img); setModal(true)}}></Review>
</SwiperSlide> </SwiperSlide>
)} )}
</Swiper> </Swiper>
</div> </div>
<Modal active={modal} setActive={setModal} className='max-w-[650px]'>
<div>
{modalImg &&
<GatsbyImage className='rounded-lg mb-2' loading='lazy' imgClassName="!object-contain" image={getImage(modalImg)} alt="logo" placeholder="none"/>
}
</div>
</Modal>
</section> </section>
) )
} }

@ -1,8 +1,9 @@
import React from 'react'; import React from 'react';
const Button = ({className, children, ...props}) => { const Button = ({className, disabled, children, ...props}) => {
let disableClasses = disabled ? 'bg-gray-400 hover:bg-gray-400' : '';
return ( return (
<button {...props} className={['bg-yellow-500 rounded-md py-2.5 px-5 text-lg text-white hover:bg-yellow-600 transition delay-50', className].join(' ')}>{children}</button> <button {...props} className={['bg-yellow-500 rounded-md py-2.5 px-5 text-lg text-white hover:bg-yellow-600 transition delay-50', disableClasses, className].join(' ')}>{children}</button>
); );
} }

@ -1,16 +1,19 @@
import React from 'react'; import React from 'react';
import uniqid from 'uniqid'; import uniqid from 'uniqid';
const Input = ({ className, label, labelClasses, ...props }) => { const Input = ({ className, label, labelClasses, wrapperClasses, name, errors, validation, register, ...props }) => {
const uid = uniqid(); const uid = uniqid();
return ( return (
<div className='flex flex-col'> <div className={['flex flex-col relative', wrapperClasses].join(' ')}>
{label && {label &&
<label className={'mb-3 text-white ' + labelClasses} htmlFor={uid}>{label}</label> <label className={['mb-3 text-white', labelClasses].join(' ')} htmlFor={uid}>{label}</label>
}
<input {...register && name ? {...register(name, validation)} : null} id={uid} className={['bg-white rounded-md py-3 px-5 text-base text-gray-800', className].join(' ')} {...props} />
{errors[name] &&
<p className="text-red-600 absolute -bottom-0.5 translate-y-full">{errors[name]?.message}</p>
} }
<input id={uid} className={['bg-white rounded-md py-3 px-5 text-base text-gray-800', className].join(' ')} {...props} type="text" />
</div> </div>
); );
} }

@ -0,0 +1,26 @@
import React, { useEffect } from 'react'
import { motion, AnimatePresence } from "framer-motion";
import { IoMdClose } from "react-icons/io";
import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock";
const Modal = ({className, children, active, setActive, ...props}) => {
useEffect(() => {
active ? disableBodyScroll(document.body) : enableBodyScroll(document.body);
}, [active])
return (
<AnimatePresence>
{active &&
<motion.div className='min-h-full overflow-y-auto fixed w-full h-full bg-black p-3 bg-opacity-80 left-0 top-0 flex justify-center items-center z-30' initial={{ opacity: 0 }} exit={{ opacity: 0 }} animate={{ opacity: 1 }} transition={{ duration: 0.3 }} onClick={() => setActive(false)}>
<motion.div className={['min-w-[320px] max-w-[500px] max-h-full overflow-y-auto w-full p-8 bg-white flex flex-col rounded-lg relative', className].join(' ')} initial={{ opacity: 0, scale: 0 }} exit={{ opacity: 0, scale: 0 }} animate={{ opacity: 1, scale: 1 }} transition={{ duration: 0.5 }} onClick={e => e.stopPropagation()} {...props}>
<IoMdClose className='absolute right-3 top-3 text-2xl cursor-pointer' onClick={() => setActive(false)}/>
{children}
</motion.div>
</motion.div>
}
</AnimatePresence>
)
}
export default Modal;

@ -1,16 +1,16 @@
import React from 'react'; import React from 'react';
import uniqid from 'uniqid'; import uniqid from 'uniqid';
const Textarea = ({ className, label, labelClasses, ...props }) => { const Textarea = ({ className, label, labelClasses, name, errors, validation, register, ...props }) => {
const uid = uniqid(); const uid = uniqid();
return ( return (
<div className='flex flex-col w-full'> <div className='flex flex-col w-full'>
{label && {label &&
<label className={'mb-3 text-white ' + labelClasses} htmlFor={uid}>{label}</label> <label className={['mb-3 text-white', labelClasses].join(' ')} htmlFor={uid}>{label}</label>
} }
<textarea id={uid} className={['bg-white rounded-md py-2.5 px-5 text-base text-gray-800', className].join(' ')} {...props}></textarea> <textarea {...register && name ? {...register(name)} : null} id={uid} className={['bg-white rounded-md py-2.5 px-5 text-base text-gray-800', className].join(' ')} {...props}></textarea>
</div> </div>
); );
} }

@ -3,6 +3,7 @@ import { Link as AnchorLink } from "react-scroll";
import { Link } from "gatsby" import { Link } from "gatsby"
import { StaticImage } from "gatsby-plugin-image" import { StaticImage } from "gatsby-plugin-image"
import { useLocation } from '@reach/router'; import { useLocation } from '@reach/router';
import { BsWhatsapp, BsTelegram } from "react-icons/bs";
const Footer = ({menu}) => { const Footer = ({menu}) => {
@ -31,11 +32,17 @@ const Footer = ({menu}) => {
</ul> </ul>
} }
</div> </div>
<div className="flex mt-8 justify-between"> <div className="flex mt-8 justify-between max-sm:flex-col max-sm:items-center">
<p className="text-white text-sm">© {new Date().getFullYear()} TalentTeam</p> <p className="text-white text-sm max-sm:order-2">© {new Date().getFullYear()} TalentTeam</p>
<div className="flex max-sm:order-1 max-sm:mb-5">
<div className="flex items-center">
<a href="https://wa.me/+79200745505" target="_blank" rel="noreferrer" aria-label="WhatsApp"><BsWhatsapp className="text-white text-lg mr-2"/></a>
<a href="https://t.me/+79200745505" target="_blank" rel="noreferrer" aria-label="Telegram"><BsTelegram className="text-white text-lg mr-2"/></a>
</div>
<a href="tel:+79200745505" className="text-white hover:text-gray-400 transition-colors">8 (920) 074 55-05</a> <a href="tel:+79200745505" className="text-white hover:text-gray-400 transition-colors">8 (920) 074 55-05</a>
</div> </div>
</div> </div>
</div>
</footer> </footer>
) )
} }

@ -1,9 +1,10 @@
import React, { useState, useRef, useEffect } from "react"; import React, { useState, useRef, useEffect } from "react";
import { Link } from "gatsby" import { Link } from "gatsby"
import { StaticImage } from "gatsby-plugin-image"
import { Link as AnchorLink } from "react-scroll"; import { Link as AnchorLink } from "react-scroll";
import { useLocation } from '@reach/router'; import { useLocation } from '@reach/router';
import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock"; import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock";
import { BsWhatsapp, BsTelegram } from "react-icons/bs";
const Header = ({ menu }) => { const Header = ({ menu }) => {
@ -46,9 +47,12 @@ const Header = ({ menu }) => {
return ( return (
<header className="bg-black h-9 flex w-full fixed top-0 z-30 max-md:justify-between max-md:px-3 max-md:items-center"> <header className="bg-black h-9 flex w-full fixed top-0 z-30 max-md:justify-between max-md:px-3 max-md:items-center">
<nav className={burgerMenu ? menuGeneralClasses + " max-md:h-screen max-md:translate-y-0" : menuGeneralClasses + " max-md:h-0 max-md:-translate-y-full"}> <nav className={burgerMenu ? menuGeneralClasses + " max-md:h-screen max-md:translate-y-0" : menuGeneralClasses + " max-md:h-0 max-md:-translate-y-full"}>
<div className="flex items-center"> <div className="flex items-center max-md:mb-12 max-md:flex-col">
<Link to="/" className="max-md:hidden"><StaticImage src="../images/logo.svg" alt="logo" placeholder="none" className="w-8 mr-5"/></Link> <div className="flex items-center max-md:mb-3">
<a href="tel:+79200745505" className="text-white hover:text-gray-400 transition-colors text-xs max-md:text-xl max-md:mb-12">8 (920) 074 55-05</a> <a href="https://wa.me/+79200745505" target="_blank" rel="noreferrer" aria-label="WhatsApp"><BsWhatsapp className="text-white text-lg max-md:text-xl mr-2"/></a>
<a href="https://t.me/+79200745505" target="_blank" rel="noreferrer" aria-label="Telegram"><BsTelegram className="text-white text-lg max-md:text-xl mr-5 max-md:mr-0"/></a>
</div>
<a href="tel:+79200745505" className="text-white hover:text-gray-400 transition-colors text-xs max-md:text-xl">8 (920) 074 55-05</a>
</div> </div>
{location.pathname === '/' && !menu {location.pathname === '/' && !menu
? ?
@ -75,7 +79,11 @@ const Header = ({ menu }) => {
</ul> </ul>
} }
</nav> </nav>
<div className="md:hidden"> <div className="md:hidden flex">
<div className="flex items-center">
<a href="https://wa.me/+79200745505" target="_blank" rel="noreferrer" aria-label="WhatsApp"><BsWhatsapp className="text-white text-lg mr-2"/></a>
<a href="https://t.me/+79200745505" target="_blank" rel="noreferrer" aria-label="Telegram"><BsTelegram className="text-white text-lg mr-3"/></a>
</div>
<a href="tel:+79200745505" className="text-white hover:text-gray-400 transition-colors text-md">8 (920) 074 55-05</a> <a href="tel:+79200745505" className="text-white hover:text-gray-400 transition-colors text-md">8 (920) 074 55-05</a>
</div> </div>
<div className="md:hidden cursor-pointer" role="presentation" onClick={e=> burgerActivation()} onKeyDown={e=> this.handleKeyDown}> <div className="md:hidden cursor-pointer" role="presentation" onClick={e=> burgerActivation()} onKeyDown={e=> this.handleKeyDown}>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 365 KiB

Loading…
Cancel
Save