upd
This commit is contained in:
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 26 KiB |
@@ -4,7 +4,7 @@ import { PhoneIcon } from './icons/PhoneIcon';
|
|||||||
import { SendIcon } from './icons/SendIcon';
|
import { SendIcon } from './icons/SendIcon';
|
||||||
import { TelegramIcon } from './icons/TelegramIcon';
|
import { TelegramIcon } from './icons/TelegramIcon';
|
||||||
import { VKIcon } from './icons/VKIcon';
|
import { VKIcon } from './icons/VKIcon';
|
||||||
import { YoutubeIcon } from './icons/YoutubeIcon';
|
import { RutubeIcon } from './icons/RutubeIcon';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { useModalStore } from '../stores/modalStore';
|
import { useModalStore } from '../stores/modalStore';
|
||||||
import { ModalWithForm } from './Layout/ModalWithForm';
|
import { ModalWithForm } from './Layout/ModalWithForm';
|
||||||
@@ -38,9 +38,7 @@ export function Contacts() {
|
|||||||
className="py-4"
|
className="py-4"
|
||||||
width="full"
|
width="full"
|
||||||
icon={<MailIcon />}
|
icon={<MailIcon />}
|
||||||
onClick={() => {
|
href="mailto:info@graff.tech"
|
||||||
window.location.href = 'mailto:info@graff.tech';
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<span className="btn-text opacity-80">Написать</span>
|
<span className="btn-text opacity-80">Написать</span>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -49,9 +47,7 @@ export function Contacts() {
|
|||||||
className="py-4"
|
className="py-4"
|
||||||
width="full"
|
width="full"
|
||||||
icon={<PhoneIcon />}
|
icon={<PhoneIcon />}
|
||||||
onClick={() => {
|
href="tel:88007700067"
|
||||||
window.location.href = 'tel:88007700067';
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<span className="btn-text opacity-80">Позвонить</span>
|
<span className="btn-text opacity-80">Позвонить</span>
|
||||||
</Button>
|
</Button>
|
||||||
@@ -60,10 +56,10 @@ export function Contacts() {
|
|||||||
<h4 className="h4 font-medium">Социальные сети</h4>
|
<h4 className="h4 font-medium">Социальные сети</h4>
|
||||||
<div className="gap-x-2 flex">
|
<div className="gap-x-2 flex">
|
||||||
<Link
|
<Link
|
||||||
to={'https://www.youtube.com/@GRAFFtech'}
|
to={'https://rutube.ru/channel/25505040/'}
|
||||||
className="p-4 rounded-full opacity-80 border-[#3D425C] border hover:border-[#52587A] transition-all"
|
className="p-4 flex items-center justify-center rounded-full opacity-80 border-[#3D425C] border hover:border-[#52587A] transition-all"
|
||||||
>
|
>
|
||||||
<YoutubeIcon />
|
<RutubeIcon />
|
||||||
</Link>
|
</Link>
|
||||||
<Link
|
<Link
|
||||||
to={'https://vk.com/graffinteractive?from=groups'}
|
to={'https://vk.com/graffinteractive?from=groups'}
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ export function FeedbackForm({
|
|||||||
mask={placeholder?.replace(/\d/g, '9') ?? ''}
|
mask={placeholder?.replace(/\d/g, '9') ?? ''}
|
||||||
maskChar={null}
|
maskChar={null}
|
||||||
value={phone}
|
value={phone}
|
||||||
placeholder={placeholder}
|
placeholder={'XXX XXX XX XX'}
|
||||||
onChange={e => setPhone(e.target.value.replace(/ /g, ''))}
|
onChange={e => setPhone(e.target.value.replace(/ /g, ''))}
|
||||||
className="transition-all bg-transparent rounded-none outline-none h4 placeholder:h4 placeholder:font-medium placeholder:select-none peer"
|
className="transition-all bg-transparent rounded-none outline-none h4 placeholder:h4 placeholder:font-medium placeholder:select-none peer"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
import { LogoIcon } from '../icons/LogoIcon';
|
|
||||||
import { Link, NavLink } from 'react-router-dom';
|
import { Link, NavLink } from 'react-router-dom';
|
||||||
|
import { LogoWithoutText } from '../icons/LogoWithoutTextIcon';
|
||||||
|
|
||||||
export function Footer() {
|
export function Footer() {
|
||||||
return (
|
return (
|
||||||
<footer className="sm:grid xl:grid-cols-[2fr_1fr_1fr] sm:grid-cols-2 sm:max-xl:grid-rows-2 bg-[#14161F]">
|
<footer className="sm:grid xl:grid-cols-[2fr_1fr_1fr] sm:grid-cols-2 sm:max-xl:grid-rows-2 bg-[#14161F]">
|
||||||
<div className="flex sm:items-center max-sm:flex-col sm:px-6 px-4 sm:py-9 py-4 border-t border-[#3D425C] gap-6 sm:max-xl:row-start-1 sm:max-xl:col-span-2">
|
<div className="flex sm:items-center max-sm:flex-col sm:px-6 px-4 sm:py-9 py-4 border-t border-[#3D425C] gap-6 sm:max-xl:row-start-1 sm:max-xl:col-span-2">
|
||||||
<Link to={'/'}>
|
<Link to={'/'}>
|
||||||
<LogoIcon />
|
<img
|
||||||
|
src="images/GRAFFinteractive.svg"
|
||||||
|
alt="GRAFFinteractive"
|
||||||
|
className="w-[118px] max-md:hidden"
|
||||||
|
/>
|
||||||
|
<div className="md:hidden">
|
||||||
|
<LogoWithoutText />
|
||||||
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
<div className="flex flex-col gap-y-1">
|
<div className="flex flex-col gap-y-1">
|
||||||
<div className="flex gap-x-4 m-text">
|
<div className="flex gap-x-4 m-text">
|
||||||
@@ -20,7 +27,7 @@ export function Footer() {
|
|||||||
<Link to="https://graff.tech">graff.tech</Link>
|
<Link to="https://graff.tech">graff.tech</Link>
|
||||||
</div>
|
</div>
|
||||||
<p className="opacity-40 sm:font-medium m-text">
|
<p className="opacity-40 sm:font-medium m-text">
|
||||||
© 2024 GRAFF interactive. Все права защищены
|
© 2026 GRAFF interactive. Все права защищены
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { LogoIcon } from '../icons/LogoIcon';
|
|
||||||
import { Button } from '../ui/Button';
|
import { Button } from '../ui/Button';
|
||||||
import { ClassNameWrapper } from '../../hocs/ClassNameWrapper';
|
import { ClassNameWrapper } from '../../hocs/ClassNameWrapper';
|
||||||
import { useModalStore } from '../../stores/modalStore';
|
import { useModalStore } from '../../stores/modalStore';
|
||||||
@@ -26,9 +25,10 @@ export function Header() {
|
|||||||
return (
|
return (
|
||||||
<header className="sticky top-0 lg:px-6 px-4 flex max-lg:justify-between items-center lg:h-16 h-12 border-b border-[#3D425C] bg-[#14161F] z-50">
|
<header className="sticky top-0 lg:px-6 px-4 flex max-lg:justify-between items-center lg:h-16 h-12 border-b border-[#3D425C] bg-[#14161F] z-50">
|
||||||
<Link to={'/'} className="max-sm:hidden">
|
<Link to={'/'} className="max-sm:hidden">
|
||||||
<ClassNameWrapper
|
<img
|
||||||
element={<LogoIcon />}
|
src="images/GRAFFinteractive.svg"
|
||||||
className="h-8 lg:h-10 lg:w-[113px] w-[93px]"
|
alt="GRAFFinteractive"
|
||||||
|
className="lg:w-[118px] sm:w-[80px] max-sm:hidden"
|
||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
<Link to={'/'} className="sm:hidden">
|
<Link to={'/'} className="sm:hidden">
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { ClassNameWrapper } from '../../hocs/ClassNameWrapper';
|
import { ClassNameWrapper } from '../../hocs/ClassNameWrapper';
|
||||||
import { useModalStore } from '../../stores/modalStore';
|
import { useModalStore } from '../../stores/modalStore';
|
||||||
import { Button } from '../ui/Button';
|
import { Button } from '../ui/Button';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { ArrowRightIcon } from '../icons/ArrowRightIcon';
|
import { ArrowRightIcon } from '../icons/ArrowRightIcon';
|
||||||
import { CloseIcon } from '../icons/CloseIcon';
|
import { CloseIcon } from '../icons/CloseIcon';
|
||||||
import { FeedbackForm } from './FeedbackForm';
|
import { FeedbackForm } from './FeedbackForm';
|
||||||
|
import { useOnClickOutside } from 'usehooks-ts';
|
||||||
|
|
||||||
export function ModalWithForm() {
|
export function ModalWithForm() {
|
||||||
const { setModal } = useModalStore();
|
const { setModal } = useModalStore();
|
||||||
|
|
||||||
const [sent, setSent] = useState(false);
|
const [sent, setSent] = useState(false);
|
||||||
|
const modalRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const listener = (e: KeyboardEvent) => {
|
const listener = (e: KeyboardEvent) => {
|
||||||
@@ -22,8 +23,13 @@ export function ModalWithForm() {
|
|||||||
return () => document.removeEventListener('keydown', listener);
|
return () => document.removeEventListener('keydown', listener);
|
||||||
}, [setModal]);
|
}, [setModal]);
|
||||||
|
|
||||||
|
useOnClickOutside(modalRef, () => setModal(false));
|
||||||
|
|
||||||
return (
|
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">
|
<div
|
||||||
|
ref={modalRef}
|
||||||
|
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"
|
||||||
|
>
|
||||||
{!sent ? (
|
{!sent ? (
|
||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export function LogoIcon() {
|
export function LogoIcon() {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width={128}
|
width={138}
|
||||||
height={50}
|
height={50}
|
||||||
viewBox="0 0 128 50"
|
viewBox="0 0 128 50"
|
||||||
fill="none"
|
fill="none"
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
export function RutubeIcon() {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<g opacity="0.8" transform="translate(2, 3)">
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M8.793 4.543H2.62V2.205h6.174c.36 0 .612.06.738.165.125.106.203.3.203.585v.84c0 .3-.078.494-.203.6-.126.104-.377.15-.738.15zM9.217.001H0V10h2.619V6.747h4.825L9.734 10h2.933l-2.525-3.268c.93-.132 1.349-.405 1.693-.855s.518-1.17.518-2.13v-.749c0-.57-.063-1.019-.173-1.364a2.26 2.26 0 0 0-.564-.914 2.6 2.6 0 0 0-.973-.555C10.268.06 9.797 0 9.218 0"
|
||||||
|
fill="#fff"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -8,6 +8,8 @@ interface ButtonProps {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
|
/** Native link (mailto:, tel:, https://). More reliable than JS navigation on some mobile WebViews. */
|
||||||
|
href?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Button({
|
export function Button({
|
||||||
@@ -18,25 +20,47 @@ export function Button({
|
|||||||
disabled = false,
|
disabled = false,
|
||||||
className,
|
className,
|
||||||
onClick,
|
onClick,
|
||||||
|
href,
|
||||||
}: ButtonProps) {
|
}: ButtonProps) {
|
||||||
return (
|
const sharedClassName = `group relative px-6 py-2 rounded-full min-w-fit ${
|
||||||
<button
|
(color === 'primary'
|
||||||
disabled={disabled}
|
? 'bg-gradient-to-r from-[#798FFF] to-[#D375FF]'
|
||||||
onClick={onClick}
|
: '') ||
|
||||||
className={`group relative px-6 py-2 rounded-full min-w-fit ${
|
(color === 'secondary' ? 'outline outline-1 outline-[#3D425C]' : '')
|
||||||
(color === 'primary'
|
} ${
|
||||||
? 'bg-gradient-to-r from-[#798FFF] to-[#D375FF]'
|
icon ? 'pr-4' : ''
|
||||||
: '') ||
|
} flex gap-1 items-center overflow-hidden w-${width} ${className} justify-between`;
|
||||||
(color === 'secondary' ? 'outline outline-1 outline-[#3D425C]' : '')
|
|
||||||
} ${
|
const inner = (
|
||||||
icon ? 'pr-4' : ''
|
<>
|
||||||
} flex gap-1 items-center overflow-hidden w-${width} ${className} justify-between`}
|
|
||||||
>
|
|
||||||
<span className="group-hover:opacity-10 opacity-0 bg-black transition-opacity absolute top-0 left-0 w-full h-full"></span>
|
<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-medium' + (icon ? '' : ' m-auto')}>
|
<span className={'relative font-medium' + (icon ? '' : ' m-auto')}>
|
||||||
{children}
|
{children}
|
||||||
</span>
|
</span>
|
||||||
<span className="relative">{icon}</span>
|
<span className="relative">{icon}</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (href) {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href={disabled ? undefined : href}
|
||||||
|
className={sharedClassName + (disabled ? ' pointer-events-none opacity-50' : '')}
|
||||||
|
aria-disabled={disabled || undefined}
|
||||||
|
onClick={disabled ? (e) => e.preventDefault() : onClick}
|
||||||
|
>
|
||||||
|
{inner}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={onClick}
|
||||||
|
className={sharedClassName}
|
||||||
|
>
|
||||||
|
{inner}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,13 +27,13 @@ export const stands: IStand[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
img: 'images/stands/interactive_floor.jpg',
|
img: 'images/stands/interactive_floor.jpg',
|
||||||
title: 'Interactive floor',
|
title: 'Интерактивный этаж',
|
||||||
annotation: 'Выставка «Россия ВДНХ»',
|
annotation: 'Выставка «Россия ВДНХ»',
|
||||||
year: '2022',
|
year: '2022',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
img: 'images/stands/graff_estate_stream.jpg',
|
img: 'images/stands/graff_estate_stream.jpg',
|
||||||
title: 'Graff.estate stream',
|
title: 'Модуль удаленной демонстрации недвижимости GRAFF.estate',
|
||||||
annotation: 'WOW FEST',
|
annotation: 'WOW FEST',
|
||||||
year: '2023',
|
year: '2023',
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user