fixes, added form, some animatoins

This commit is contained in:
2024-07-10 18:49:17 +05:00
parent 9186673043
commit c8ed924690
25 changed files with 387 additions and 385 deletions
+1 -1
View File
@@ -1 +1 @@
PUBLIC_API_URL=https://graff.estate/api
VITE_PUBLIC_API_URL=https://graff.estate/api
+2
View File
@@ -2,6 +2,7 @@ import { Outlet } from 'react-router-dom';
import { Header } from './components/Layout/Header';
import { Navbar } from './components/Layout/Navbar';
import { Footer } from './components/Layout/Footer';
import ModalContainer from './components/Main/ModalContainer';
export default function Layout() {
return (
@@ -12,6 +13,7 @@ export default function Layout() {
<Outlet />
</main>
<Footer />
<ModalContainer />
</div>
);
}
+1 -1
View File
@@ -1,7 +1,7 @@
import ky from 'ky';
const api = ky.extend({
prefixUrl: process.env.PUBLIC_API_URL,
prefixUrl: import.meta.env.VITE_PUBLIC_API_URL,
});
export default api;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 410 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 224 KiB

+14 -2
View File
@@ -4,10 +4,13 @@ import { Link } from 'react-router-dom';
import { Lang, useLang } from '../../store/language';
import { HashLink } from 'react-router-hash-link';
import { useOnClickOutside } from 'usehooks-ts';
import useModalStore from '../../store/modal';
import ContactsForm from '../Main/ContactsForm';
export function Navbar() {
const [menuOpen, setMenuOpen] = useState(false);
const { value: lang } = useLang();
const setModal = useModalStore(state => state.setModal);
const menuRef = useRef<HTMLDivElement>(null);
const menuBtnRef = useRef<HTMLButtonElement>(null);
@@ -32,7 +35,10 @@ export function Navbar() {
<NavLink route="#trainings">Варианты комплектации</NavLink>
<NavLink route="#projects">Проекты</NavLink>
<NavLink route="#events">События</NavLink>
<button className="btn-text font-semibold text-[#ffffff] bg-gradient-to-r from-[#798FFF] to-[#D375FF] border-x border-[#3D425C] tablet:block mobile:hidden px-10">
<button
onClick={() => setModal(<ContactsForm />)}
className="btn-text font-semibold text-[#ffffff] bg-gradient-to-r from-[#798FFF] to-[#D375FF] border-x border-[#3D425C] tablet:block mobile:hidden px-10"
>
Оставить заявку
</button>
<LangToggler lang={lang} />
@@ -62,7 +68,13 @@ export function Navbar() {
<BurgerLink route="#projects">Проекты</BurgerLink>
<BurgerLink route="#events">События</BurgerLink>
<div className="grid mobile:max-tablet:grid-cols-[216px_1fr_1fr] tablet:grid-cols-2">
<button className="text-[#ffffff] tablet:hidden font-semibold btn-text bg-gradient-to-r from-[#798FFF] to-[#D375FF] py-[30px] px-10">
<button
onClick={() => {
setMenuOpen(false);
setModal(<ContactsForm />);
}}
className="text-[#ffffff] tablet:hidden font-semibold btn-text bg-gradient-to-r from-[#798FFF] to-[#D375FF] py-[30px] px-10"
>
Оставить заявку
</button>
<ChooseLang lang="RU" />
+5 -2
View File
@@ -1,3 +1,4 @@
import AppearanceHr from '../../ui/AppearanceHr';
import { AppearanceText } from '../../ui/AppearanceText';
import { MiniTitle } from '../../ui/MiniTitle';
import { Title } from '../../ui/Title';
@@ -19,10 +20,11 @@ export function Availables() {
<br />
режим обучения
</Title>
<div className="flex mobile:max-desktop:flex-col items-start desktop:w-fit tablet:pt-5 tablet:border-t border-[#3D425C]">
<AppearanceHr className="mobile:max-tablet:hidden" />
<div className="flex mobile:max-desktop:flex-col items-start desktop:w-fit tablet:pt-5">
<MiniTitle text="возможности" />
<div className="tablet:max-desktop-figma:mt-4 mobile:max-tablet:mt-[9px]">
<div className="flex mobile:max-tablet:flex-col mobile:max-tablet:gap-y-2 desktop:gap-x-4 tablet:max-desktop:gap-x-3 border-b border-[#3D425C] tablet:pb-5 mobile:pb-2 mb-6 mobile:max-tablet:border-t mobile:max-tablet:pt-2">
<div className="flex mobile:max-tablet:flex-col mobile:max-tablet:gap-y-2 desktop:gap-x-4 tablet:max-desktop:gap-x-3 tablet:pb-5 mobile:pb-2 mobile:max-tablet:border-t mobile:max-tablet:pt-2">
<MultiUserFeature
img="src/assets/processes.svg"
text="отработка производственных процессов, в которых участвует группа людей"
@@ -36,6 +38,7 @@ export function Availables() {
text="координация действий между несколькими сотрудниками"
/>
</div>
<AppearanceHr className=" mb-6" />
<AppearanceText
className="tablet-figma:max-w-[668px] desktop-figma:max-w-[39.25vw]"
splits={[
+7 -1
View File
@@ -1,6 +1,9 @@
import useModalStore from '../../store/modal';
import { Title } from '../../ui/Title';
import ContactsForm from './ContactsForm';
export function Contacts() {
const setModal = useModalStore(state => state.setModal);
return (
<div className="desktop:py-[70px] desktop:px-10 mobile:py-14 tablet:px-6 mobile:px-4 desktop:flex justify-between gap-x-4">
<div className="tablet:max-desktop:mb-20 mobile:mb-14">
@@ -18,7 +21,10 @@ export function Contacts() {
Давайте обсудим детали.
</span>
</Title>
<button className="bg-gradient-to-r from-[#798FFF] to-[#D375FF] rounded-[104px] py-4 px-6 flex font-semibold text-[#ffffff] justify-between desktop-figma:min-w-[23vw] desktop:max-desktop-figma:min-w-[368px] tablet:max-desktop:min-w-[332px] mobile:max-tablet:w-full items-center btn-text">
<button
onClick={() => setModal(<ContactsForm />)}
className="bg-gradient-to-r from-[#798FFF] to-[#D375FF] rounded-[104px] py-4 px-6 flex font-semibold text-[#ffffff] justify-between desktop-figma:min-w-[23vw] desktop:max-desktop-figma:min-w-[368px] tablet:max-desktop:min-w-[332px] mobile:max-tablet:w-full items-center btn-text"
>
Оставить заявку <img src="src/assets/send.svg" alt="" />
</button>
</div>
+139 -125
View File
@@ -7,14 +7,16 @@ import Button from '../../ui/Button';
import LoaderIcon from '../icons/LoaderIcon';
import SendIcon from '../icons/SendIcon';
import CheckGradientIcon from '../icons/CheckGradientIcon';
import useModalStore from '../../store/modal';
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 [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);
const setModal = useModalStore(state => state.setModal);
function handleSubmit(e: FormEvent<HTMLFormElement>) {
e.preventDefault();
@@ -50,132 +52,144 @@ function ContactsForm() {
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">
Свяжитесь с нами
</p>
<button 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>Имя</span>
<AsteriskIcon />
<div className="fixed top-0 right-0 h-full sm:w-[408px] w-full bg-[#14161F] overflow-y-auto sm:p-8 p-6">
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-5">
<div className="flex justify-between items-center">
<p
className="w-fit font-semibold bg-text-gradient bg-gradient-to-r from-[#798FFF] to-[#D375FF] sm:text-2xl text-xl"
style={{
backgroundClip: 'text',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
}}
>
Свяжитесь с нами
</p>
<button
onClick={() => setModal(null)}
className="p-2 hover:bg-white hover:bg-opacity-10 transition-colors rounded-full"
>
<Close2Icon className="text-white" />
</button>
</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>Телефон</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="Опишите вашу задачу"
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">
Нажимая кнопку отправить, вы принимаете{' '}
<a
href="https://graff.tech/privacypolicy"
target="_blank"
className="text-[#798FFF] cursor-pointer opacity-95 hover:opacity-100 transition-all"
>
условия использования
</a>{' '}
и{' '}
<a
href="https://graff.tech/privacypolicy"
target="_blank"
className="text-[#798FFF] cursor-pointer opacity-95 hover:opacity-100 transition-all"
>
политику конфиденциальности
</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 />
<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>Имя</span>
<AsteriskIcon />
</p>
</div>
<p></p>
<p>
Звездочкой отмечены обязательные
<br />
для заполнения поля
</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"
>
Отправить
</Button>
</form>
<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"
/>
<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">
<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="Опишите вашу задачу"
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">
Нажимая кнопку отправить, вы принимаете{' '}
<a
href="https://graff.tech/privacypolicy"
target="_blank"
className="text-[#798FFF] cursor-pointer opacity-95 hover:opacity-100 transition-all"
>
условия использования
</a>{' '}
и{' '}
<a
href="https://graff.tech/privacypolicy"
target="_blank"
className="text-[#798FFF] cursor-pointer opacity-95 hover:opacity-100 transition-all"
>
политику конфиденциальности
</a>
</div>
</div>
<div className="border border-t-0 text-white border-[#3D425C] 2xl:p-6 p-4 sm:mt-0 text-xs flex items-center gap-2">
<div className="flex gap-2">
<div>
<AsteriskIcon />
</div>
<p></p>
<p>
Звездочкой отмечены обязательные
<br />
для заполнения поля
</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"
>
Отправить
</Button>
</form>
</div>
</div>
</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">
<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>
@@ -185,7 +199,7 @@ function ContactsForm() {
</div>
<div className="flex flex-col gap-2">
<p className="font-gilroy leading-snug lg:text-2xl text-xl font-semibold">
<p className="leading-snug lg:text-2xl text-xl font-semibold">
Спасибо за подачу заявки!
</p>
+11 -5
View File
@@ -1,3 +1,4 @@
import AppearanceHr from '../../ui/AppearanceHr';
import { Title } from '../../ui/Title';
export function Decreasing() {
@@ -32,23 +33,28 @@ export function Decreasing() {
</Title>
<div className="flex tablet-figma:flex-row mobile:max-tablet-figma:flex-col justify-normal desktop-figma:gap-x-[10vw] tablet-figma:max-desktop-figma:gap-x-[clamp(16px,16px+(100vw-1024px)/576*142,160px)] desktop-figma:max-w-[calc(70.8vw+256px)] desktop-figma:justify-between tablet:items-end xl:pl-64 desktop:mt-14 mobile:mt-6">
<ul className="xl:max-w-[47vw] w-full tablet:min-w-[318px] mobile:mb-6">
<li className="flex justify-between py-5 border-t border-[#3D425C] gap-x-4">
<AppearanceHr />
<li className="flex justify-between py-5 gap-x-4">
<Plus text="снижение количества несчастных случаев" />
<Number text="[01]" />
</li>
<li className="flex justify-between py-5 border-t border-[#3D425C] gap-x-4">
<AppearanceHr />
<li className="flex justify-between py-5 gap-x-4">
<Plus text="уменьшение количества ошибок при ТО и ППР" />
<Number text="[02]" />
</li>
<li className="flex justify-between py-5 border-t border-[#3D425C] gap-x-4">
<AppearanceHr />
<li className="flex justify-between py-5 gap-x-4">
<Plus text="меньше случаев внеплановой остановки оборудования" />
<Number text="[03]" />
</li>
<li className="flex justify-between py-5 border-t border-[#3D425C] gap-x-4">
<AppearanceHr />
<li className="flex justify-between py-5 gap-x-4">
<Plus text="снижение расходов на закупку реальной техники и оборудования для обучения" />
<Number text="[04]" />
</li>
<li className="flex justify-between py-5 border-t border-[#3D425C] gap-x-4">
<AppearanceHr />
<li className="flex justify-between py-5 gap-x-4">
<Plus text="сокращение сроков обучения" />
<Number text="[05]" />
</li>
+5 -2
View File
@@ -1,13 +1,15 @@
import { MiniTitle } from '../../ui/MiniTitle';
import { AppearanceText } from '../../ui/AppearanceText';
import AppearanceHr from '../../ui/AppearanceHr';
export function Effeciency() {
return (
<div className="desktop:py-[70px] mobile:py-14 desktop:px-10 tablet:px-6 mobile:px-4">
<div className="flex mobile:max-desktop:flex-col tablet:border-t border-[#3D425C] pt-5 tablet:max-desktop:gap-4">
<AppearanceHr className="mobile:max-tablet:hidden" />
<div className="flex mobile:max-desktop:flex-col pt-5 tablet:max-desktop:gap-4">
<MiniTitle text={'экономическая эффективность'} />
<div className="mobile:max-tablet:mt-4">
<div className="flex mobile:max-tablet:flex-col desktop:max-desktop-figma:w-[clamp(728px,728px+(100vw-1024px)/576*405,1133px)] justify-stretch gap-x-4 gap-y-2 mobile:max-tablet:py-5 tablet:pb-5 border-b border-[#3D425C] mb-9 tablet:border-t-0 mobile:border-t">
<div className="flex mobile:max-tablet:flex-col desktop:max-desktop-figma:w-[clamp(728px,728px+(100vw-1024px)/576*405,1133px)] justify-stretch gap-x-4 gap-y-2 mobile:max-tablet:py-5 tablet:pb-5">
<Figure
variance={'left'}
percents={50}
@@ -24,6 +26,7 @@ export function Effeciency() {
title={'готовность к опасным ситуациямние выше на'}
/>
</div>
<AppearanceHr className="mb-9" />
<AppearanceText
className="max-w-[752px] desktop-figma:max-w-[47vw]"
splits={[
+10 -4
View File
@@ -1,6 +1,7 @@
import { Link } from 'react-router-dom';
import { MiniTitle } from '../../ui/MiniTitle';
import { PropsWithChildren } from 'react';
import AppearanceHr from '../../ui/AppearanceHr';
export function Events() {
return (
@@ -8,10 +9,12 @@ export function Events() {
className="desktop:py-[70px] desktop:px-10 tablet:py-14 tablet:px-6 mobile:px-4"
id="events"
>
<div className="flex desktop:border-t mobile:max-desktop:flex-col border-[#3D425C] pt-5 gap-x-4 w-full">
<AppearanceHr className="mobile:max-desktop:hidden" />
<div className="flex mobile:max-desktop:flex-col pt-5 gap-x-4 w-full">
<MiniTitle text="события" className="mobile:max-tablet:mb-2" />
<div className="desktop:max-desktop-figma:min-w-[clamp(688px,688px+(100vw-1024px)/576*425,1133px)] desktop-figma:min-w-[70.9vw]">
<div className="py-7 tablet:border-b mobile:max-tablet:border-t border-[#3D425C] flex justify-between mobile:max-tablet:flex-col items-start gap-x-4">
<AppearanceHr className="tablet:hidden" />
<div className="desktop:py-7 tablet:max-desktop:py-6 mobile:max-tablet:py-5 flex justify-between mobile:max-tablet:flex-col items-start gap-x-4">
<div className="w-fit">
<EventTitle className="tablet:mb-8 w-fit">
Макет кабины машиниста «Иволга» на выставке ВДНХ
@@ -31,16 +34,19 @@ export function Events() {
</div>
<LinkButton href="/" />
</div>
<div className="py-7 tablet:border-b mobile:max-tablet:border-t border-[#3D425C] flex mobile:max-tablet:flex-col tablet:items-center justify-between gap-x-4">
<AppearanceHr />
<div className="py-7 flex mobile:max-tablet:flex-col tablet:items-center justify-between gap-x-4">
<EventTitle>Победа на BuildUP 2023 в номинации IT</EventTitle>
<LinkButton href="/" />
</div>
<div className="py-7 tablet:border-b mobile:max-tablet:border-t border-[#3D425C] flex mobile:max-tablet:flex-col tablet:items-center justify-between gap-x-4">
<AppearanceHr />
<div className="py-7 flex mobile:max-tablet:flex-col tablet:items-center justify-between gap-x-4">
<EventTitle>
Транспортное и специальное тренажеростроение 2023
</EventTitle>
<LinkButton href="/" />
</div>
<AppearanceHr />
</div>
</div>
</div>
+35 -190
View File
@@ -5,196 +5,41 @@ export function Marquee() {
<div className="w-0 h-0 desktop:border-b-[100px] desktop:border-l-[100px] tablet:border-b-[82px] tablet:border-l-[82px] border-[transparent_transparent_#D0D6DF_transparent]" />
<div className="desktop:w-[260px] tablet:w-[216px] bg-[#D0D6DF]" />
</div>
<div className="flex items-stretch overflow-clip animate-infinite-scroll">
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C] py-[30px]">
<img
src="src/assets/partners_logos/rosatom.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/rzhd.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/npoa.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/dubai_police.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/ugmk.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/moscowgov.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/mintransrf.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/uztm.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/uralhimmash.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/elem.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/electrohimpribor.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/uralvagonzavod.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/croc.svg"
className="m-auto"
alt=""
/>
</div>
</div>
<div
className="flex items-stretch overflow-clip animate-infinite-scroll"
aria-hidden
>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/rosatom.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/rzhd.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/npoa.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/dubai_police.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/ugmk.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/moscowgov.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/mintransrf.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/uztm.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/uralhimmash.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/elem.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/electrohimpribor.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/uralvagonzavod.svg"
className="m-auto"
alt=""
/>
</div>
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border border-[#3D425C]">
<img
src="src/assets/partners_logos/croc.svg"
className="m-auto"
alt=""
/>
</div>
</div>
<MarqueeHalf />
<MarqueeHalf />
</div>
);
}
function MarqueeHalf() {
const srcs = [
'src/assets/partners_logos/rosatom.svg',
'src/assets/partners_logos/rzhd.svg',
'src/assets/partners_logos/npoa.svg',
'src/assets/partners_logos/dubai_police.svg',
'src/assets/partners_logos/ugmk.svg',
'src/assets/partners_logos/moscowgov.svg',
'src/assets/partners_logos/mintransrf.svg',
'src/assets/partners_logos/uztm.svg',
'src/assets/partners_logos/uralhimmash.svg',
'src/assets/partners_logos/elem.svg',
'src/assets/partners_logos/electrohimpribor.svg',
'src/assets/partners_logos/uralvagonzavod.svg',
'src/assets/partners_logos/croc.svg',
];
return (
<div className="flex items-stretch overflow-clip animate-infinite-scroll">
{srcs.map(src => (
<MarqueeItem src={src} key={src} />
))}
</div>
);
}
function MarqueeItem({ src }: { src: string }) {
return (
<div className="desktop:min-w-[212px] tablet:min-w-[180px] mobile:min-w-[170px] flex border-l border-y border-[#3D425C] first:py-[30px]">
<img src={src} className="m-auto" alt="" />
</div>
);
}
+15
View File
@@ -0,0 +1,15 @@
import useModalStore from '../../store/modal';
export default function ModalContainer() {
const modal = useModalStore(state => state.modal);
return (
modal && (
<div className="fixed top-0 left-0 z-50 w-full h-full flex justify-center items-center bg-black bg-opacity-40 transition-opacity">
<div onClick={e => e.stopPropagation()} className="cursor-default">
{modal}
</div>
</div>
)
);
}
+37 -32
View File
@@ -1,6 +1,7 @@
import { useHover } from 'usehooks-ts';
import { Title } from '../../ui/Title';
import { useRef } from 'react';
import AppearanceHr from '../../ui/AppearanceHr';
export function Trainings() {
return (
@@ -22,25 +23,26 @@ export function Trainings() {
</span>
, основываясь на специфике вашего тренировочного процесса
</Title>
<div className="desktop:max-desktop-figma:pl-[16vw] desktop-figma:pl-[256px]">
<div className="xl:max-desktop-figma:pl-[16vw] desktop-figma:pl-[256px] w-fit">
<TrainingsFeature
order="[01]"
src="src/assets/vr_1.png"
src="src/assets/vr_1.svg"
title="VR - тренажеры"
text="Обучение навыкам работы с инструментами и оборудованием. Пешее хождение по территории или между оборудованием"
/>
<TrainingsFeature
order="[02]"
src="src/assets/vr_2.png"
src="src/assets/vr_2.svg"
title="Cтенды"
text="Отработки навыков вождение и управления техникой. Работа с панелями управления"
/>
<TrainingsFeature
order="[03]"
src="src/assets/vr_3.png"
src="src/assets/vr_3.svg"
title="Учебные классы"
text="Оснащение учебных классов и центров всем необходимым для современного обучения и профессиональной подготовки кадров"
/>
<AppearanceHr />
</div>
</div>
);
@@ -61,43 +63,46 @@ function TrainingsFeature({
const hovered = useHover(ref);
return (
<div
ref={ref}
className="desktop:first:h-[200px] desktop:last:h-[200px] desktop:h-[176px] tablet:flex items-stretch justify-between tablet:py-10 desktop:max-desktop-figma:max-w-[clamp(557px,557px+(100vw-1024px)/576*576,1133px)] desktop-figma:w-[70.8vw] border-b border-[#3D425C] first:border-t mobile:max-tablet:pt-5"
>
<div className="tablet:flex flex-col gap-y-4 mobile:max-tablet:mb-[42px] tablet-figma:max-w-[43.7%]">
<h3 className="font-medium text-[#ffffff] mobile:max-tablet:mb-2 h3">
{title}
</h3>
<p className="text-[#ffffff] opacity-60 l-text">{text}</p>
</div>
<div className="mobile:max-tablet:flex tablet:hidden justify-between items-end">
<p className="text-[#52587A] m-text mb-5">{order}</p>
<img src={src} alt="" className="w-[50vw]" />
</div>
<div className="tablet-figma:flex mobile:max-tablet-figma:hidden">
{hovered && (
<div className="xl:max-desktop-figma:max-w-[clamp(557px,557px+(100vw-1024px)/576*576,1133px)] desktop-figma:w-[70.8vw]">
<AppearanceHr />
<div
ref={ref}
className="desktop:first:h-[200px] desktop:last:h-[200px] desktop:h-[176px] tablet:flex items-stretch justify-between tablet:py-10 mobile:max-tablet:pt-5 "
>
<div className="tablet:flex flex-col gap-y-4 mobile:max-tablet:mb-[42px] tablet-figma:max-w-[43.7%]">
<h3 className="font-medium text-[#ffffff] mobile:max-tablet:mb-2 h3">
{title}
</h3>
<p className="text-[#ffffff] opacity-60 l-text">{text}</p>
</div>
<div className="mobile:max-tablet:flex tablet:hidden justify-between items-end">
<p className="text-[#52587A] m-text mb-5">{order}</p>
<img src={src} alt="" className="w-[50vw]" />
</div>
<div className="tablet-figma:flex mobile:max-tablet-figma:hidden">
{hovered && (
<img
src={src}
alt=""
className="relative self-center w-[27vw/1120*843] -my-10 h-[calc(27vw*0.6)] mobile:max-desktop:hidden"
/>
)}
<img
src={src}
alt=""
className="relative self-center w-[27vw] -my-10 h-[calc(27vw*0.6)] mobile:max-desktop:hidden"
className="self-center w-[27vw] h-[calc(27vw*0.6)] desktop:hidden"
/>
)}
<p className="text-[#52587A] desktop:font-medium m-text">{order}</p>
</div>
<img
src={src}
className="mobile:max-tablet:hidden tablet-figma:hidden w-[calc(280/708*100%)]"
alt=""
className="self-center w-[27vw] h-[calc(27vw*0.6)] desktop:hidden"
/>
<p className="text-[#52587A] desktop:font-medium m-text">{order}</p>
<p className="text-[#52587A] desktop:font-medium m-text mobile:max-tablet:hidden tablet-figma:hidden">
{order}
</p>
</div>
<img
src={src}
className="mobile:max-tablet:hidden tablet-figma:hidden w-[calc(280/708*100%)]"
alt=""
/>
<p className="text-[#52587A] desktop:font-medium m-text mobile:max-tablet:hidden tablet-figma:hidden">
{order}
</p>
</div>
);
}
+3 -1
View File
@@ -1,4 +1,4 @@
export function AsteriskIcon() {
export function AsteriskIcon({ className = '' }: { className?: string }) {
return (
<svg
width="12"
@@ -6,6 +6,8 @@ export function AsteriskIcon() {
viewBox="0 0 12 13"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={className}
color="currentColor"
>
<path
d="M4.81534 12.2727L4.9858 7.58523L1.02273 10.0994L0 8.30966L4.17614 6.13636L0 3.96307L1.02273 2.1733L4.9858 4.6875L4.81534 0H6.8608L6.69034 4.6875L10.6534 2.1733L11.6761 3.96307L7.5 6.13636L11.6761 8.30966L10.6534 10.0994L6.69034 7.58523L6.8608 12.2727H4.81534Z"
+21
View File
@@ -6,6 +6,7 @@
body {
font-family: 'TTHovesPro';
color: #fff;
}
@layer components {
@@ -44,3 +45,23 @@ body {
@apply tracking-[.02em] leading-[120%] desktop-figma:text-[clamp(14px,0.875vw,16px)] desktop:max-desktop-figma:text-[clamp(12px,12px+(100vw-1024px)/576*2,14px)] tablet-figma:max-desktop:text-[clamp(12px,12px+(100vw-640px)/256*2,14px)] tablet:max-tablet-figma:text-xs mobile:max-tablet:text-[clamp(12px,12px+(100vw-360px)/280*2,14px)];
}
}
.feedback-field:focus ~ .feedback-placeholder {
top: 0;
}
.feedback-field:focus ~ .feedback-placeholder-2 {
opacity: 0;
}
.feedback-field:valid ~ .feedback-placeholder {
top: 0;
}
.feedback-field:valid ~ .feedback-placeholder-2 {
opacity: 0;
}
.feedback-field::placeholder {
@apply lg:text-base text-sm font-semibold text-[#77787d];
}
+14 -17
View File
@@ -1,4 +1,3 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import { RouterProvider, createBrowserRouter } from 'react-router-dom';
@@ -6,20 +5,18 @@ import { MainPage } from './pages/MainPage';
import Layout from './Layout';
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<RouterProvider
router={createBrowserRouter([
{
path: '/',
Component: Layout,
children: [
{
index: true,
Component: MainPage,
},
],
},
])}
/>
</React.StrictMode>,
<RouterProvider
router={createBrowserRouter([
{
path: '/',
Component: Layout,
children: [
{
index: true,
Component: MainPage,
},
],
},
])}
/>,
);
+14
View File
@@ -0,0 +1,14 @@
import { ReactNode } from 'react';
import { create } from 'zustand';
interface IModalState {
modal: ReactNode | null;
setModal: (modal: ReactNode) => void;
}
const useModalStore = create<IModalState>(set => ({
modal: null,
setModal: modal => set({ modal }),
}));
export default useModalStore;
+22
View File
@@ -0,0 +1,22 @@
import { useInView } from 'framer-motion';
import { useRef } from 'react';
export default function AppearanceHr({
className = '',
}: {
className?: string;
}) {
const ref = useRef<HTMLHRElement>(null);
const isInView = useInView(ref);
return (
<hr
ref={ref}
className={
className +
' border-[#3D425C] h-px duration-500 origin-left delay-200 ' +
(isInView ? 'scale-x-1' : 'scale-x-0')
}
/>
);
}
+4 -2
View File
@@ -24,14 +24,16 @@ function Button({
disabled={disabled}
onClick={onClick}
className={`group relative px-6 py-2 rounded-full min-w-fit ${
(color === 'primary' ? 'bg-gradient' : '') ||
(color === 'primary'
? 'bg-gradient-to-r from-[#798FFF] to-[#D375FF]'
: '') ||
(color === 'secondary' ? 'outline outline-1 outline-[#3D425C]' : '')
} ${
icon ? 'pr-4' : ''
} flex justify-between gap-1 items-center overflow-hidden w-${width} ${className}`}
>
<span className="group-hover:opacity-10 opacity-0 bg-black transition-opacity absolute top-0 left-0 w-full h-full"></span>
<span className="relative font-gilroy font-medium">{children}</span>
<span className="relative font-medium">{children}</span>
<span className="relative ">{icon}</span>
</button>
);