Start migration

This commit is contained in:
Daniele Pancottini 2022-12-20 17:30:36 +01:00
parent 6d783eae35
commit b1fb0bc138
125 changed files with 3819 additions and 161 deletions

View File

@ -45,6 +45,15 @@ export interface ProductOptionValues {
hexColors?: string[]
}
export type ProductMetafield = {
description: string
id: string
key: string
namespace: string
value: string
valueType: string
}
export interface ProductVariant {
/**
* The unique identifier for the variant.
@ -124,6 +133,7 @@ export interface Product {
/**
* The product's base price. Could be the minimum value, or default variant price.
*/
metafields: ProductMetafield[]
price: ProductPrice
/**
* List of product's options.

View File

@ -15,6 +15,11 @@ import type {
Page as ShopifyPage,
PageEdge,
Collection,
MetafieldConnection,
MediaConnection,
Model3d,
Metafield,
Maybe,
} from '../../schema'
import { colorMap } from './colors'
@ -95,6 +100,21 @@ const normalizeProductVariants = ({ edges }: ProductVariantConnection) => {
)
}
const normalizeProductMedia = ({ edges }: MediaConnection) => {
return edges
.filter(({ node }) => Object.keys(node).length !== 0)
.map(({ node }) => {
return {
sources: (node as Model3d).sources.map(({ format, url }) => {
return {
format: format,
url: url,
}
}),
}
})
}
export function normalizeProduct({
id,
title: name,

View File

@ -24,6 +24,14 @@ const getProductQuery = /* GraphQL */ `
currencyCode
}
}
metafields(first: 30) {
edges {
node {
key
value
}
}
}
variants(first: 250) {
pageInfo {
hasNextPage
@ -65,6 +73,22 @@ const getProductQuery = /* GraphQL */ `
}
}
}
media(first: 250) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {
node {
... on Model3d {
sources {
url
format
}
}
}
}
}
}
}
`

1539
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,136 @@
import React from 'react';
import {
Box,
IconButton,
useBreakpointValue,
Stack,
Heading,
Text,
Container,
} from '@chakra-ui/react';
// Here we have used react-icons package for the icons
import { BiLeftArrowAlt, BiRightArrowAlt } from 'react-icons/bi';
// And react-slick as our Carousel Lib
import Slider from 'react-slick';
// Settings for the slider
const settings = {
dots: true,
arrows: false,
fade: true,
infinite: true,
autoplay: true,
speed: 500,
autoplaySpeed: 5000,
slidesToShow: 1,
slidesToScroll: 1,
};
export default function AboutSlider() {
// As we have used custom buttons, we need a reference variable to
// change the state
const [slider, setSlider] = React.useState<Slider | null>(null);
// These are the breakpoints which changes the position of the
// buttons as the screen size changes
const top = useBreakpointValue({ base: '90%', md: '50%' });
const side = useBreakpointValue({ base: '30%', md: '40px' });
// This list contains all the data for carousels
// This can be static or loaded from a server
const cards = [
{
title: 'Storia del Mercato',
text:
`Ciò che è sempre stato alla base del progresso della civiltà umana è lo scambio (di beni, di idee, di conoscenze). In età neolitica avveniva sotto forma di baratto. Nell'antichità e per tutto il Medioevo i principali canali di scambio erano il mercato, in genere allestito nelle piazze delle città e la fiera, imponente evento commerciale. Il mercato giornaliero, settimanale o mensile, vivacizzava la città e provvedeva alle esigenze della popolazione; era il luogo dello scambio tra la produzione dell'artigianato urbano e quella agricola. La fiera era un evento periodico, suddiviso in più circuiti predeterminati, dove tanti operatori economici convergevano per scambiarsi in natura, in moneta o tramite titoli di credito, beni in quantità consistenti; non mancavano le agevolazioni fiscali per i mercanti stranieri che vi convenivano. Ma la fiera era caratterizzata anche per il clima di confusione e festività; accadeva di tutto: circolazione di persone, di merci e cultura, spettacoli, giochi d'azzardo, reliquie di santi in bella mostra. Mentre l'attività produttiva (e a volte anche commerciale) del mercante, così come dell'artigiano si svolgeva nella bottega. Fino ai primi decenni del XVII secolo le fiere erano il principale canale di scambio commerciale e connotavano i territori entro le quali raggiungevano la loro fortuna. Con l'avvento dell'industrializzazione si afferma in Europa e nel mondo il sistema capitalistico di produzione: le botteghe artigiane diventano grandi fabbriche, le fiere tradizionali sono meno affollate e meno rinomate, divenendo sempre più sbocco di derrate agricole, e lasciando spazio a un nuovo tipo di fiera: la fiera campionaria, il cui massimo esempio è rappresentato dallesposizione nazionale e universale. Nella città nascono gli spazi rispondenti alle nuove produzioni industriali e ai nuovi consumi: i passages, le gallerie, i grandi magazzini, gli antenati dell'odierno centro commerciale, tempio dell'attuale società consumistica del XXI secolo. Oggi sia i centri commerciali che le fiere specializzate (miranti ai singoli comparti dell'industria) sono gli attori fondamentali nel sistema di distribuzione delle novità introdotte dal grande mercato, propongono nuove idee e nuovi modelli di comportamento e consumo e, facilitando anche un'omologazione degli stessi, rispondono alle nuove dinamiche della globalizzazione. Se essi proliferano e riempiono il tessuto urbano, centrale o periferico, nei borghi e nei paesi, si trovano ancora le manifestazioni tipiche della sagra, del mercatino, della piccola fiera, della mostra a tema. Questi piccoli eventi preservano una cultura e una tradizione locali, talvolta ancora espressione della vita artigianale, attenuando il fenomeno della globalizzazione. In particolare il mercatino (agenzia d'affari) può essere strutturato come manifestazione periodica o avente una propria sede fissa. Rimane il luogo in cui possono rinvenirsi oggetti particolari, testimonianza del passato (più o meno recente) e non delle novità industriali.`,
image:
'https://images.unsplash.com/photo-1516796181074-bf453fbfa3e6?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1yZWxhdGVkfDV8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=900&q=60',
},
{
title: 'Iniziativa',
text:
`Se è vero che oggi sia i grandi centri commerciali che questi piccoli eventi sono fenomeni culturali (i primi riguardanti la cultura attuale, globale, di massa, i secondi quella passata), sono luoghi d'intrattenimento e svago, hanno entrambi carattere di coinvolgimento emotivo e grande varietà e assortimento merceologico, è anche vero che gli ultimi sono organizzati esponendo le merci tuttalpiù per generi, settori. I titolari, attenti soprattutto al valore economico del bene usato, possono limitarsi a fornire ai visitatori informazioni basilari sugli stessi, non soddisfacendo mai completamente un ipotetico arricchimento culturale dei propri clienti. Ecco l'ipotesi di una nuova formula di agenzia d'affari che aspiri a essere un'esposizione a tema (come la fiera e la mostra a tema) e allo stesso tempo avere grande varietà e assortimento di culture e generi, il tutto ordinato in un disegno che susciti emozioni e fornisca al cliente la massima trasmissione culturale che può scaturire dall'osservazione di prodotti usati; ridia vita alle merci, ricordandosi delle culture e tradizioni locali (oggi più che mai in pericolo) e sia attenta alle urgenti istanze di ecologia, riuso e sostenibilità. Safara è uno spazio innovativo che espone prodotti datati come un normale mercatino, ma li reinserisce nella loro "cornice" originaria, affianco ad oggetti e simboli dello stesso periodo storico. È costituita da un percorso ramificato in più "fermate", ognuna delle quali rappresenti un decennio del 900. Perfino ogni singolo oggetto racconterà in breve la sua storia. Se ciò che è sempre stato alla base del progresso della civiltà umana è lo scambio, è ancora attraverso esso che Safara fornisce un elemento di diversificazione rispetto agli altri attori dellusato. Riutilizzando la visione ancestrale del commercio basata sul baratto, e adattandola in chiave moderna, assieme ad altri servizi unici, Safara è davvero unagenzia unica nel suo genere. Pensiamo inoltre che lidea possa costituire un supporto utile come completamento di altra manifestazione locale.`,
image:
'https://images.unsplash.com/photo-1438183972690-6d4658e3290e?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=2274&q=80',
},
{
title: 'Chi Siamo',
text:
`Safara è un'agenzia d'affari, più nota con il termine di mercatino dell'usato, che a differenza dei concorrenti, affianca all'offerta commerciale un'offerta culturale, tentando parallelamente di valorizzare la sua regione di provenienza e di fornirle un servizio utile, soprattutto in un periodo storico delicatissimo per il pianeta intero.`,
image:
'https://images.unsplash.com/photo-1507237998874-b4d52d1dd655?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1yZWxhdGVkfDR8fHxlbnwwfHx8fA%3D%3D&auto=format&fit=crop&w=900&q=60',
},
];
return (
<Box
position={'relative'}
height={'600px'}
width={'full'}
overflow={'hidden'}>
{/* CSS files for react-slick */}
<link
rel="stylesheet"
type="text/css"
charSet="UTF-8"
href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.css"
/>
<link
rel="stylesheet"
type="text/css"
href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick-theme.min.css"
/>
{/* Left Icon */}
<IconButton
aria-label="left-arrow"
variant="ghost"
position="absolute"
left={side}
top={top}
transform={'translate(0%, -50%)'}
zIndex={2}
onClick={() => slider?.slickPrev()}>
<BiLeftArrowAlt size="40px" />
</IconButton>
{/* Right Icon */}
<IconButton
aria-label="right-arrow"
variant="ghost"
position="absolute"
right={side}
top={top}
transform={'translate(0%, -50%)'}
zIndex={2}
onClick={() => slider?.slickNext()}>
<BiRightArrowAlt size="40px" />
</IconButton>
{/* Slider */}
<Slider {...settings} ref={(slider: any) => setSlider(slider)}>
{cards.map((card, index) => (
<Box
key={index}
height={'6xl'}
position="relative"
backgroundPosition="center"
backgroundRepeat="no-repeat"
backgroundSize="cover">
{/* This is the block you need to change, to customize the caption */}
<Container size="container.2xl" height="600px" position="relative">
<Stack
spacing={6}
w={'full'}
maxW={'lg'}>
<Heading fontSize={{ base: '3xl', md: '4xl', lg: '5xl' }}>
{card.title}
</Heading>
<Text fontSize={{ base: 'md', lg: 'lg' }} color="GrayText">
{card.text}
</Text>
</Stack>
</Container>
</Box>
))}
</Slider>
</Box>
);
}

View File

@ -21,6 +21,18 @@ const links = [
name: 'Home',
url: '/',
},
{
name: 'About',
url: '/about'
},
{
name: 'News',
url: '/news'
},
{
name: 'Contact',
url: '/contact'
}
]
const Footer: FC<Props> = ({ className, pages }) => {

View File

@ -0,0 +1,71 @@
import {
Modal,
ModalOverlay,
ModalContent,
ModalBody,
Box,
} from "@chakra-ui/react"
import decadesManifest from '../../../../static_data/decadesManifest.json';
import { useRouter } from "next/router";
import { useEffect, useRef, useState } from "react";
import ImageMapper from "react-img-mapper"
export default function MarkerCardModal(props: {
isOpen: boolean,
onModalClose: () => void,
decade: string,
}) {
const {locale} = useRouter();
const containerRef = useRef<any>();
const decadeColor = decadesManifest[props.decade as keyof typeof decadesManifest].color;
const [width, setWidth] = useState(200);
const [isMapLoaded, setIsMapLoaded] = useState<boolean>(false)
const [mapDefinition, setMapDefinition] = useState<any>()
const getContainerSize = () => {
if(containerRef.current != undefined) {
setWidth(containerRef.current.clientWidth);
}
};
const getMapDefinition = async () => {
const tempMapDefinition = await import("../../../../static_data/regions/abruzzo/" + props.decade + "/plan/manifest.json")
tempMapDefinition.areas.forEach((area: any) => {
area.href = "/" + locale + area.href
})
setMapDefinition(tempMapDefinition)
}
useEffect(() => {
getContainerSize()
}, [isMapLoaded]);
useEffect(() => {
getMapDefinition()
return window.addEventListener("resize", getContainerSize);
}, []);
return (
<>
<Modal onClose={props.onModalClose} isOpen={props.isOpen} isCentered>
<ModalOverlay />
<ModalContent rounded={"lg"}>
<ModalBody rounded={"lg"} style={{background: 'linear-gradient(120deg, ' + decadeColor + ' 0%, #000000 130%)'}} p={5}>
<Box
w={'full'}
ref={containerRef}
>
<ImageMapper onLoad={() => setIsMapLoaded(true)} natural stayHighlighted responsive={true} parentWidth={width} map={mapDefinition} src={"/regions/abruzzo/" + props.decade + "/plan/plan.jpeg"}></ImageMapper>
</Box>
</ModalBody>
</ModalContent>
</Modal>
</>
)
}

View File

@ -17,6 +17,8 @@ import { MenuSidebarView } from '@components/common/UserNav'
import type { Page } from '@commerce/types/page'
import type { Category } from '@commerce/types/site'
import type { Link as LinkProps } from '../UserNav/MenuSidebarView'
import navBarLinks from '../../../static_data/navBarLinks.json';
import Script from 'next/script'
const Loading = () => (
<div className="w-80 h-80 flex items-center text-center justify-center p-3">
@ -108,11 +110,8 @@ const Layout: React.FC<Props> = ({
pageProps: { categories = [], ...pageProps },
}) => {
const { acceptedCookies, onAcceptCookies } = useAcceptCookies()
const { locale = 'en-US' } = useRouter()
const navBarlinks = categories.slice(0, 2).map((c) => ({
label: c.name,
href: `/search/${c.slug}`,
}))
const { locale = 'it' } = useRouter()
const navBarlinks = navBarLinks.links;
return (
<CommerceProvider locale={locale}>
@ -133,6 +132,22 @@ const Layout: React.FC<Props> = ({
</Button>
}
/>
{/** Sendinblue Chat Script to implement Widget */}
<Script id="show-banner" strategy="lazyOnload">
{`
<!-- Sendinblue Conversations {literal} -->
(function(d, w, c) {
w.SibConversationsID = '632dab316c8a1e0b083a91c2';
w[c] = w[c] || function() {
(w[c].q = w[c].q || []).push(arguments);
};
var s = d.createElement('script');
s.async = true;
s.src = 'https://conversations-widget.sendinblue.com/sib-conversations.js';
if (d.head) d.head.appendChild(s);
})(document, window, 'SibConversations');
`}
</Script>
</div>
</CommerceProvider>
)

View File

@ -0,0 +1,57 @@
import { Drawer, DrawerOverlay, DrawerContent, DrawerHeader, DrawerBody, Link, Box, Stack, Heading, Divider } from "@chakra-ui/react"
import React, { useEffect, useState } from "react"
import filtersData from '../../../static_data/navBarMenuData.json';
import NavBarFiltersItem from './NavBarFiltersItem';
export default function NavBarFiltersDrawer(props: {
onClose: () => void;
isOpen: boolean;
}) {
const [placement, setPlacement] = React.useState('left' as const)
const [regions, setRegions] = React.useState<NavItem[]>([]);
const [categories, setCategories] = React.useState<NavItem[]>([]);
useEffect(() => {
setRegions(filtersData.regions);
setCategories(filtersData.categories);
}, []);
return (
<>
<Drawer placement={placement} onClose={props.onClose} isOpen={props.isOpen}>
<DrawerOverlay />
<DrawerContent>
<DrawerBody>
<Stack mt={5} direction={"column"} spacing={"20"}>
<Box>
<Heading size={"lg"} mb={5}>Regions</Heading>
{
regions.map(region => (
<NavBarFiltersItem key={region.label} {...region} />
))
}
</Box>
<Box>
<Heading mb={5} size={"lg"}>Categories</Heading>
{
categories.map(category => (
<NavBarFiltersItem key={category.label} {...category} />
))
}
</Box>
</Stack>
</DrawerBody>
</DrawerContent>
</Drawer>
</>
)
}
interface NavItem {
label: string;
subLabel?: string;
children?: Array<NavItem>;
href?: string;
enabled: boolean;
};

View File

@ -0,0 +1,83 @@
import {
Box,
Flex,
Text,
IconButton,
Button,
Stack,
Collapse,
Icon,
Link,
Popover,
PopoverTrigger,
PopoverContent,
useColorModeValue,
useBreakpointValue,
useDisclosure,
} from '@chakra-ui/react';
import {
HamburgerIcon,
CloseIcon,
ChevronDownIcon,
ChevronRightIcon,
} from '@chakra-ui/icons';
const NavBarFiltersItem = ({ label, children, href, enabled }: NavItem) => {
const { isOpen, onToggle } = useDisclosure();
return (
<Stack spacing={4} onClick={children && onToggle}>
<Flex
py={2}
as={Link}
href={enabled ? href : '#'}
justify={'space-between'}
align={'center'}
_hover={{
textDecoration: 'none',
}}>
<Text
fontWeight={600}
color={useColorModeValue('gray.600', 'gray.200')}>
{label}
</Text>
{children && (
<Icon
as={ChevronDownIcon}
transition={'all .25s ease-in-out'}
transform={isOpen ? 'rotate(180deg)' : ''}
w={6}
h={6}
/>
)}
</Flex>
<Collapse in={isOpen} animateOpacity style={{ marginTop: '0!important' }}>
<Stack
mt={2}
pl={4}
borderLeft={1}
borderStyle={'solid'}
borderColor={useColorModeValue('gray.200', 'gray.700')}
align={'start'}>
{children &&
children.map((child) => (
<Link key={child.label} py={2} href={enabled ? child.href : '#'}>
{child.label}
</Link>
))}
</Stack>
</Collapse>
</Stack>
);
};
interface NavItem {
label: string;
subLabel?: string;
children?: Array<NavItem>;
href?: string;
enabled: boolean;
};
export default NavBarFiltersItem;

View File

@ -4,6 +4,8 @@ import s from './Navbar.module.css'
import NavbarRoot from './NavbarRoot'
import { Logo, Container } from '@components/ui'
import { Searchbar, UserNav } from '@components/common'
import { useDisclosure } from '@chakra-ui/react'
import NavBarFiltersDrawer from './NavBarFiltersDrawer'
interface Link {
href: string
@ -14,43 +16,54 @@ interface NavbarProps {
links?: Link[]
}
const Navbar: FC<NavbarProps> = ({ links }) => (
<NavbarRoot>
<Container clean className="mx-auto max-w-8xl px-6">
<div className={s.nav}>
<div className="flex items-center flex-1">
<Link href="/">
<a className={s.logo} aria-label="Logo">
<Logo />
</a>
</Link>
<nav className={s.navMenu}>
<Link href="/search">
<a className={s.link}>All</a>
</Link>
{links?.map((l) => (
<Link href={l.href} key={l.href}>
<a className={s.link}>{l.label}</a>
const Navbar: FC<NavbarProps> = ({ links }) => {
const { isOpen: isOpenDrawer, onOpen: onOpenDrawer, onClose: onCloseDrawer } = useDisclosure()
return (
<>
<NavBarFiltersDrawer onClose={onCloseDrawer} isOpen={isOpenDrawer} ></NavBarFiltersDrawer>
<NavbarRoot>
<Container clean className="mx-auto max-w-8xl px-6">
<div className={s.nav}>
<div className="flex items-center flex-1">
<Link href="/">
<a className={s.logo} aria-label="Logo">
<Logo />
</a>
</Link>
))}
</nav>
</div>
{process.env.COMMERCE_SEARCH_ENABLED && (
<div className="justify-center flex-1 hidden lg:flex">
<Searchbar />
<nav className={s.navMenu}>
<Link href="/search">
<a className={s.link}>All</a>
</Link>
{links?.map((l) => (
<Link href={l.href} key={l.href}>
{
l.label === 'Regions' ? (<a onClick={onOpenDrawer} className={s.link}>{l.label}</a>)
: <a className={s.link}>{l.label}</a>
}
</Link>
))}
</nav>
</div>
{process.env.COMMERCE_SEARCH_ENABLED && (
<div className="justify-center flex-1 hidden lg:flex">
<Searchbar />
</div>
)}
<div className="flex items-center justify-end flex-1 space-x-8">
<UserNav />
</div>
</div>
)}
<div className="flex items-center justify-end flex-1 space-x-8">
<UserNav />
</div>
</div>
{process.env.COMMERCE_SEARCH_ENABLED && (
<div className="flex pb-4 lg:px-6 lg:hidden">
<Searchbar id="mobile-search" />
</div>
)}
</Container>
</NavbarRoot>
)
{process.env.COMMERCE_SEARCH_ENABLED && (
<div className="flex pb-4 lg:px-6 lg:hidden">
<Searchbar id="mobile-search" />
</div>
)}
</Container>
</NavbarRoot>
</>
)
}
export default Navbar

View File

@ -0,0 +1,53 @@
import {
Modal,
ModalOverlay,
ModalContent,
ModalBody,
} from "@chakra-ui/react"
import { Product } from "@commerce/types"
import ProductCardRoom from "../../../product/ProductCardRoom/ProductCardRoom"
import decadesManifest from '../../../../static_data/decadesManifest.json';
import ResourceCardModal from "@components/common/Room/ResourceCardModal/ResourceCardModal";
import { MarkerData, MarkerResourcePayload } from "../RoomTypes/RoomTypes";
export default function MarkerCardModal(props: {
isOpen: boolean,
onModalClose: () => void,
marker: MarkerData,
decade: string,
onAudioPlayerPlay?: (player: HTMLAudioElement) => void,
onAudioPlayerPause?: () => void
}) {
const decadeColor = decadesManifest[props.decade as keyof typeof decadesManifest].color;
const getCardToRender = (markerType: string) => {
switch(markerType) {
case "product":
return <ProductCardRoom decade={props.decade} product={props.marker.markerPayload as Product.Product} />
case "image":
case "video":
return <ResourceCardModal decade={props.decade} resourcePayload={props.marker.markerPayload as MarkerResourcePayload} />
case "audio":
return <ResourceCardModal decade={props.decade} resourcePayload={props.marker.markerPayload as MarkerResourcePayload}
onAudioPlay={props.onAudioPlayerPlay!} onAudioPause={props.onAudioPlayerPause!} onModalClose={props.onModalClose} />
}
}
return (
<>
<Modal onClose={props.onModalClose} isOpen={props.isOpen} isCentered>
<ModalOverlay />
<ModalContent rounded={"lg"}>
<ModalBody rounded={"lg"} style={{background: 'linear-gradient(120deg, ' + decadeColor + ' 0%, #000000 130%)'}} p={5}>
{
getCardToRender(props.marker.markerType)
}
</ModalBody>
</ModalContent>
</Modal>
</>
)
}

View File

@ -0,0 +1,44 @@
import { Box, Stack, Button, Text } from "@chakra-ui/react";
import { createRef } from "react";
import H5AudioPlayer from "react-h5-audio-player";
export default function AudioCardContent(props: {
style: any
resourcePath: string,
resourceCaption: string,
onPlay: (player: HTMLAudioElement) => void,
onPause: () => void,
onClose: () => void
}) {
const player = createRef<H5AudioPlayer>()
return (
<>
<Box
className={props.style.imageContainer}
w={'full'}
>
<H5AudioPlayer
src={props.resourcePath}
ref={player}
onCanPlay={e => props.onPlay(player.current?.audio.current!)}
/>
</Box>
<Box
p={5}
className={props.style.captionContainer}>
<Stack align={'center'}>
<Text padding={0} color={'gray.500'} fontSize={'sm'} align={'center'}>
{props.resourceCaption}
</Text>
<Button mt={5} onClick={(e) => {props.onClose();props.onPause();}} colorScheme='teal' variant='solid'>
BACKGROUND AUDIO
</Button>
</Stack>
</Box>
</>
)
};

View File

@ -0,0 +1,40 @@
import { Box, Stack, Image, Text } from "@chakra-ui/react";
import screenfull from "screenfull";
export default function ImageCardContent(props: {
style: any
resourcePath: string
resourceCaption: string
}) {
return (
<>
<Box
className={props.style.imageContainer}
w={'full'}
height={'220px'}
>
<Image cursor={'pointer'} onClick={() => openFullScreen('resource-image')} id='resource-image' alt='Resource Image Not Found' src={props.resourcePath} />
</Box>
<Box
p={5}
className={props.style.captionContainer}>
<Stack mt={6} align={'center'}>
<Text padding={0} color={'gray.500'} fontSize={'sm'} align={'center'}>
{props.resourceCaption}
</Text>
</Stack>
</Box>
</>
)
};
const openFullScreen = (imageId: string) => {
if (screenfull.isEnabled) {
screenfull.request(document.getElementById(imageId)!);
}
}

View File

@ -0,0 +1,35 @@
import { Box, Stack, Image, Text } from "@chakra-ui/react";
export default function VideoCardContent(props: {
style: any
resourcePath: string
resourceCaption: string
}) {
return (
<>
<Box
className={props.style.imageContainer}
w={'full'}
height={'220px'}
>
<video controls >
<source src={props.resourcePath} type="video/mp4"/>
</video>
</Box>
<Box
p={5}
className={props.style.captionContainer}>
<Stack mt={6} align={'center'}>
<Text padding={0} color={'gray.500'} fontSize={'sm'} align={'center'}>
{props.resourceCaption}
</Text>
</Stack>
</Box>
</>
)
};

View File

@ -0,0 +1,64 @@
import { Box, Flex, propNames, Stack, Text } from "@chakra-ui/react"
import { MarkerResourcePayload } from "../RoomTypes/RoomTypes"
import { Image } from "@chakra-ui/react"
import 'react-h5-audio-player/lib/styles.css';
import style from './ResourceCardStyle.module.css';
import ImageCardContent from "../ResourceCardContent/ImageCardContent";
import AudioCardContent from "../ResourceCardContent/AudioCardContent";
import VideoCardContent from "../ResourceCardContent/VideoCardContent";
export default function ResourceCardModal(props: {
decade: string,
resourcePayload: MarkerResourcePayload,
onModalClose?: () => void,
onAudioPlay?: (player: HTMLAudioElement) => void,
onAudioPause?: () => void
}) {
const RES_PATH = '/regions/abruzzo/' + props.decade + '/resources/' + props.resourcePayload.resourceSource;
const getResourceContent = () => {
switch(props.resourcePayload.resourceType) {
case 'image':
return (
<ImageCardContent resourceCaption={props.resourcePayload.resourceCaption} resourcePath={RES_PATH} style={style} />
)
case 'audio':
return (
<AudioCardContent resourceCaption={props.resourcePayload.resourceCaption} style={style} resourcePath={RES_PATH} onPlay={props.onAudioPlay!} onPause={props.onAudioPause!} onClose={props.onModalClose!} />
)
case 'video':
return (
<VideoCardContent resourceCaption={props.resourcePayload.resourceCaption} style={style} resourcePath={RES_PATH} />
)
default:
return (<></>)
}
}
return (
<Flex w="full" alignItems="center" justifyContent="center" direction={'row'}>
<Box
maxW={'445px'}
w={'full'}
boxShadow={'2xl'}
rounded={'md'}
overflow={'hidden'}
className={style.cardBody}>
<Image
className={style.decadeIcon}
src={'/assets/polygons/' + props.decade + '.svg'}
alt={`Picture of Decade`}
/>
{
getResourceContent()
}
</Box>
</Flex>
)
};

View File

@ -0,0 +1,17 @@
.decadeIcon {
margin: 5px;
position: absolute;
top: 0;
left: 0;
width: 50px;
height: 50px;
}
.cardBody {
background-color: rgba(255, 255, 255, 0.70);
}
.imageContainer {
background-color: rgba(0, 0, 0, 0.7);
}

View File

@ -0,0 +1,22 @@
import { Product } from "@commerce/types"
export type MarkerResourcePayload = {
resourceType: string,
resourceSource: string,
resourceName: string,
resourceCaption: string
}
export type MarkerData = {
markerType: string,
markerPayload: Product.Product | MarkerResourcePayload
}
export type MarkerJson = {
markerType: string,
markerSource: string,
resourceName?: string,
resourceCaption?: string,
longitude: number,
latitude: number
}

View File

@ -0,0 +1,100 @@
import {
Flex,
Box,
Image,
Heading,
Divider,
Text,
Stack,
Link,
} from '@chakra-ui/react';
import NextLink from "next/link"
import { Product } from '@commerce/types';
import style from './ProductCardRoomStyle.module.css';
export default function ProductCardRoom(props: {
product: Product.Product,
decade: string
}) {
let historicDescription = props.product.metafields
.filter(meta => meta.key == 'descrizione_storica')
.map(meta => meta.value);
let technicalDescription = props.product.metafields
.filter(meta => meta.key == 'descrizione_tecnica')
.map(meta => meta.value);
let nationOrigin = props.product.metafields
.filter(meta => meta.key == 'nazionalit_')
.map(meta => meta.value);
return (
<Flex w="full" alignItems="center" justifyContent="center" direction={'row'}>
<Box
maxW={'445px'}
w={'full'}
boxShadow={'2xl'}
rounded={'md'}
overflow={'hidden'}
className={style.cardBody}>
<Image
className={style.flagIcon}
src={'http://purecatamphetamine.github.io/country-flag-icons/3x2/' + nationOrigin + '.svg'}
alt={`Picture of Flag`}
rounded={'lg'}
/>
<Image
className={style.decadeIcon}
src={'/assets/polygons/colorized/' + props.decade + '.svg'}
alt={`Picture of Decade`}
/>
<Box
className={style.imageContainer}
w={'full'}
height={'220px'}
>
<NextLink href={'/product/' + props.product.slug} passHref>
<Link style={{textDecoration: 'none', height: 'inherit'}}>
<Image
src={
props.product.images[0].url
}
objectFit={'cover'}
margin={'auto'}
height={'inherit'}
/>
</Link>
</NextLink>
</Box>
<Box
p={5}
className={style.captionContainer}>
<Stack align={'center'}>
<Heading fontSize={'2xl'} textAlign={'center'} fontFamily={'body'} fontWeight={500}>
{props.product.name}
</Heading>
</Stack>
<Stack mt={6} align={'center'}>
<Divider borderColor={'blackAlpha.600'} />
{historicDescription.pop()?.split('\n').map((line, index) => (
<Text key={index} padding={0} color={'gray.500'} fontSize={'sm'} align={'center'}>
{line}
</Text>
))}
<Divider borderColor={'blackAlpha.600'} />
<Text color={'gray.500'} fontSize={'sm'} align={'center'}>
{technicalDescription}
</Text>
</Stack>
</Box>
</Box>
</Flex>
);
}

View File

@ -0,0 +1,25 @@
.flagIcon {
margin: 5px;
position: absolute;
top: 0;
right: 0;
width: 40px;
height: 40px;
}
.decadeIcon {
margin: 5px;
position: absolute;
top: 0;
left: 0;
width: 50px;
height: 50px;
}
.cardBody {
background-color: rgba(255, 255, 255, 0.70);
}
.imageContainer {
background-color: rgba(0, 0, 0, 0.7);
}

View File

@ -0,0 +1,72 @@
import Script from 'next/script';
import { useEffect, useState } from 'react';
export default function ProductModel(props: {
modelPath: string
}) {
const [windowSize, setWindowSize] = useState({
width: 600,
height: 600,
});
useEffect(() => {
// only execute all the code below in client side
// Handler to call on window resize
function handleResize() {
// Set window width/height to state
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
// Add event listener
window.addEventListener("resize", handleResize);
// Call handler right away so state gets updated with initial window size
handleResize();
// Remove event listener on cleanup
return () => window.removeEventListener("resize", handleResize);
}, []); // Empty array ensures that effect is only run on mount
const modelViewerTag = `
<style>
model-viewer {
top: 0;
left: 0;
right: 0;
bottom: 0;
display: block;
min-width: 100%;
min-height: 100%;
max-width: 100%;
max-height: 100%;
padding: 0;
margin: auto;
height: 0;
width: 0;
position: absolute;
box-sizing: border-box
}
#productModelViewer {
height: ${windowSize.width < 600 ? windowSize.width : 600}px;
width: ${windowSize.width < 600 ? windowSize.width : 600}px;
}
</style>
<model-viewer src="${props.modelPath}" ar ar-modes="webxr scene-viewer quick-look" seamless-poster shadow-intensity="1" camera-controls enable-pan shadow-intensity="0" enviroment-image="neutral"></model-viewer>
`;
return (
<>
<Script type='module' src='https://unpkg.com/@google/model-viewer/dist/model-viewer.min.js'></Script>
<div id="productModelViewer" dangerouslySetInnerHTML={{__html: modelViewerTag}} />
</>
);
}

View File

@ -13,6 +13,10 @@
},
"sideEffects": false,
"dependencies": {
"@chakra-ui/icons": "^2.0.14",
"@chakra-ui/react": "^2.4.4",
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@radix-ui/react-dropdown-menu": "^1.0.0",
"@react-spring/web": "^9.4.1",
"@vercel/commerce": "workspace:*",
@ -22,28 +26,35 @@
"@vercel/commerce-local": "workspace:*",
"@vercel/commerce-ordercloud": "workspace:*",
"@vercel/commerce-saleor": "workspace:*",
"@vercel/commerce-sfcc": "workspace:*",
"@vercel/commerce-shopify": "workspace:*",
"@vercel/commerce-spree": "workspace:*",
"@vercel/commerce-swell": "workspace:*",
"@vercel/commerce-sfcc": "workspace:*",
"@vercel/commerce-vendure": "workspace:*",
"autoprefixer": "^10.4.2",
"body-scroll-lock": "^4.0.0-beta.0",
"clsx": "^1.1.1",
"email-validator": "^2.0.4",
"framer-motion": "^7.10.3",
"js-cookie": "^3.0.1",
"keen-slider": "^6.7.0",
"lodash.random": "^3.2.0",
"lodash.throttle": "^4.1.1",
"next": "^12.3.0",
"next-themes": "^0.2.0",
"photo-sphere-viewer": "^4.8.1",
"postcss": "^8.3.5",
"postcss-nesting": "^10.1.10",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-fast-marquee": "^1.3.1",
"react-h5-audio-player": "^3.8.6",
"react-icons": "^4.7.1",
"react-img-mapper": "^1.4.0",
"react-merge-refs": "^2.0.1",
"react-slick": "^0.29.0",
"react-use-measure": "^2.1.1",
"screenfull": "^6.0.2",
"tabbable": "^5.2.1",
"tailwindcss": "^3.0.13"
},
@ -56,6 +67,7 @@
"@types/node": "^18.0.3",
"@types/react": "^18.0.20",
"@types/react-dom": "^18.0.6",
"@types/react-slick": "^0.23.10",
"eslint": "^8.6.0",
"eslint-config-next": "^12.0.8",
"eslint-config-prettier": "^8.3.0",

View File

@ -0,0 +1,380 @@
import { Layout } from '@components/common'
import commerce from '@lib/api/commerce'
import {
GetStaticPathsContext,
GetStaticPropsContext,
InferGetStaticPropsType,
} from 'next'
import { Viewer } from 'photo-sphere-viewer'
import { GyroscopePlugin } from 'photo-sphere-viewer/dist/plugins/gyroscope'
import { MarkersPlugin } from 'photo-sphere-viewer/dist/plugins/markers'
import { GalleryPlugin } from 'photo-sphere-viewer/dist/plugins/gallery'
import 'photo-sphere-viewer/dist/plugins/markers.css'
import 'photo-sphere-viewer/dist/plugins/gallery.css'
import { StereoPlugin } from 'photo-sphere-viewer/dist/plugins/stereo'
import { VirtualTourPlugin } from 'photo-sphere-viewer/dist/plugins/virtual-tour'
import { VisibleRangePlugin } from 'photo-sphere-viewer/dist/plugins/visible-range'
import { RefObject, useEffect, useRef, useState } from 'react'
import { useRouter } from 'next/router'
import RegionsDataJson from '../../../static_data/navBarMenuData.json'
import MarkerCardModal from '@components/common/Room/MarkerCardModal/MarkerCardModal'
import { useDisclosure } from '@chakra-ui/react'
import decadesManifest from '../../../static_data/decadesManifest.json'
import {
MarkerData,
MarkerJson,
} from '@components/common/Room/RoomTypes/RoomTypes'
import { getNodes, getRecursiveMarkers } from 'workers/DecadeWorker'
import screenfull from 'screenfull'
import { Stream } from 'stream'
export async function getStaticPaths({ locales }: GetStaticPathsContext) {
const decadesPaths = RegionsDataJson.regions
.filter((region) => region.enabled)
.flatMap((region) => {
let regionDecadesPath = new Array<string>()
for (let key in decadesManifest) {
if (decadesManifest[key as keyof typeof decadesManifest].enabled) {
Array.from(Array(7).keys()).forEach((nodeId: number) =>
regionDecadesPath.push(region.href + '/' + key + '/' + nodeId)
)
}
}
return regionDecadesPath
})
return {
paths: locales
? locales.reduce<string[]>((arr, locale) => {
// Add a decade path for every locale
decadesPaths.forEach((path: any) => {
arr.push(`/${locale}${path}`)
})
return arr
}, [])
: decadesPaths,
fallback: 'blocking',
}
}
export async function getStaticProps({
params,
locale,
locales,
preview,
}: GetStaticPropsContext<{ region: string; decade: string; start: string }>) {
const config = { locale, locales }
let products = new Array()
let decadeManifest = (
await import(
`../../../static_data/regions/${params?.region}/${params?.decade}/manifest.json`
)
).rooms
let region = params?.region
let decade = params?.decade
let startNode = params?.start
//sunrise sunset api to retreive Rome rise/set times
const sunriseSunsetAPI =
'https://api.sunrise-sunset.org/json?lat=41.9027835&lng=12.4963655'
const [sunriseTime, sunsetTime] = await fetch(sunriseSunsetAPI)
.then((response) => {
if (!response.ok) {
throw Error(response.statusText)
}
return response.json()
})
.then((data) => data.results)
.then((data) => [
parseInt(data.sunrise.split(':')[0]),
parseInt(data.sunset.split(':')[0]),
])
.catch((error) => [7, 7])
const time =
new Date().getUTCHours() >= sunsetTime + 12 ||
new Date().getUTCHours() <= sunriseTime
? 'night'
: 'day'
for (const roomMeta of decadeManifest) {
let roomMarkers
try {
roomMarkers = await import(
`../../../static_data/regions/abruzzo/${decade}/${time}/${
roomMeta.filename.split('.')[0]
}.json`
)
} catch (e) {
continue
}
for (const productMarker of roomMarkers.markers.filter(
(marker: MarkerJson) => marker.markerType == 'product'
)) {
if (
products.find(({ product }) =>
product.slug?.includes(productMarker.markerSource)
)
)
continue
const productPromise = commerce.getProduct({
variables: { slug: productMarker.markerSource },
config,
preview,
})
products.push(await productPromise)
}
}
console.log(products)
if (!products) {
throw new Error(`Products associated with markers not found`)
}
return {
props: {
products,
decadeManifest,
region,
decade,
startNode,
time,
},
revalidate: 200,
}
}
export default function RoomPage({
products,
decadeManifest,
region,
decade,
startNode,
time,
}: InferGetStaticPropsType<typeof getStaticProps>) {
const {
isOpen: isOpenModal,
onOpen: onOpenModal,
onClose: onCloseModal,
} = useDisclosure()
const [currentMarkerInModal, setCurrentMarkerInModal] = useState<MarkerData>()
const [recursiveMarkers] = useState<any>(
getRecursiveMarkers(decade!, decadeManifest, time)
)
const [virtualTourNodes] = useState<Array<any>>(
getNodes(products, decadeManifest, region!, decade!, time)
)
const [audioPlayer, setAudioPlayer] = useState<HTMLAudioElement>()
const [isAudioInBackground, setIsAudioInBackground] = useState<boolean>(false)
const [isNodeChanged, setIsNodeChanged] = useState<boolean>(false)
const router = useRouter()
const audioPlayerRef = useRef<HTMLAudioElement | undefined>(audioPlayer)
let navbarArray = new Array(
'autorotate',
'zoom',
'move',
'markers',
'markersList',
'gallery',
'stereo',
'gyroscope',
'fullscreen'
)
navbarArray = navbarArray.concat(
recursiveMarkers.map((recursiveMarker: any) => {
return {
id: recursiveMarker.name,
content: recursiveMarker.name,
title: recursiveMarker.name,
className: 'custom-button',
visible: false,
onClick: (viewer: any) => {
changeToRecursiveRoom(recursiveMarker, viewer)
},
}
})
)
const changeToRecursiveRoom = (recursiveMarker: any, viewer: any) => {
const virtualTour = viewer.getPlugin(VirtualTourPlugin)
const destNode = virtualTourNodes.find(
(node: any) => node.name === recursiveMarker.name
)
virtualTour.setCurrentNode(destNode.id)
}
useEffect(() => {
const shperePlayerInstance = new Viewer({
container: 'roomViewer',
plugins: [
[
VisibleRangePlugin,
{
longitudeRange: [null],
latitudeRange: [-Math.PI / 2, Math.PI / 4], //Restrict range so you can't see the top of the pano
},
],
[GyroscopePlugin, StereoPlugin],
MarkersPlugin,
[
GalleryPlugin,
{
thumbnailSize: { width: 100, height: 100 },
},
],
[
VirtualTourPlugin,
{
positionMode: VirtualTourPlugin.MODE_3D,
renderMode: VirtualTourPlugin.MODE_MARKERS,
nodes: virtualTourNodes,
startNodeId: startNode,
},
],
],
touchmoveTwoFingers: true,
mousewheelCtrlKey: true,
navbar: navbarArray,
})
const markersPlugin = shperePlayerInstance.getPlugin(MarkersPlugin)
const virtualTourPlugin = shperePlayerInstance.getPlugin(VirtualTourPlugin)
markersPlugin?.on('select-marker', (e, marker) => {
if (marker.id.includes('tour-link')) {
return
}
if (screenfull.isEnabled) {
screenfull.exit()
}
if (marker.data.markerType === 'room') {
virtualTourPlugin?.setCurrentNode(
decadeManifest.find(
(room: any) =>
room.name === marker.data.markerPayload.resourceSource
).id
)
return
}
setCurrentMarkerInModal(marker.data)
onOpenModal()
})
virtualTourPlugin?.on('node-changed', (e, nodeId, data) => {
setIsNodeChanged(true)
recursiveMarkers.forEach((recursive: any) =>
shperePlayerInstance.navbar.getButton(recursive.name).hide()
)
const originNode = virtualTourNodes.find(
(node: any) => node.id === nodeId
)
const originRoom = recursiveMarkers.find((recursive: any) =>
originNode.panorama.includes(recursive.panorama)
)
if (originRoom != undefined) {
shperePlayerInstance.navbar.getButton(originRoom.name).show()
}
})
router.events.on('routeChangeStart', () => {
if (audioPlayerRef.current && !audioPlayerRef.current?.paused) {
audioPlayerRef.current!.pause()
audioPlayerRef.current!.src = ''
}
})
// unmount component instructions
return () => {
shperePlayerInstance.destroy()
router.events.off('routeChangeStart', () => {
if (audioPlayerRef.current && !audioPlayerRef.current?.paused) {
audioPlayerRef.current!.pause()
audioPlayerRef.current!.src = ''
}
})
}
}, [])
useEffect(() => {
if (isAudioInBackground === true) {
setTimeout(() => {
audioPlayer!.play()
}, 500)
setTimeout(() => {
audioPlayer?.pause()
setIsAudioInBackground(false)
}, audioPlayer!.duration - audioPlayer!.currentTime)
}
}, [isAudioInBackground])
useEffect(() => {
if (
currentMarkerInModal != undefined &&
currentMarkerInModal.markerType === 'video' &&
!audioPlayer?.paused
) {
audioPlayer?.pause()
}
}, [currentMarkerInModal])
const onAudioPlay = (player: HTMLAudioElement) => {
if (!audioPlayer?.paused) {
audioPlayer?.pause()
}
setAudioPlayer(player)
}
useEffect(() => {
audioPlayerRef.current = audioPlayer
}, [audioPlayer])
useEffect(() => {
if (isNodeChanged && !audioPlayer?.paused) {
audioPlayer?.pause()
setIsNodeChanged(false)
}
}, [isNodeChanged])
return (
<>
{currentMarkerInModal != undefined ? (
currentMarkerInModal.markerType === 'audio' ? (
<MarkerCardModal
decade={decade!}
marker={currentMarkerInModal}
onModalClose={onCloseModal}
isOpen={isOpenModal}
onAudioPlayerPause={() => setIsAudioInBackground(true)}
onAudioPlayerPlay={onAudioPlay}
/>
) : (
<MarkerCardModal
decade={decade!}
marker={currentMarkerInModal}
onModalClose={onCloseModal}
isOpen={isOpenModal}
/>
)
) : (
<></>
)}
<div style={{ height: '90vh' }} id="roomViewer" />
</>
)
}
RoomPage.Layout = Layout

View File

@ -6,6 +6,7 @@ import { FC, ReactNode, useEffect } from 'react'
import type { AppProps } from 'next/app'
import { Head } from '@components/common'
import { ManagedUIContext } from '@components/ui/context'
import { ChakraProvider } from '@chakra-ui/react'
const Noop: FC<{ children?: ReactNode }> = ({ children }) => <>{children}</>
@ -20,9 +21,11 @@ export default function MyApp({ Component, pageProps }: AppProps) {
<>
<Head />
<ManagedUIContext>
<Layout pageProps={pageProps}>
<Component {...pageProps} />
</Layout>
<ChakraProvider>
<Layout pageProps={pageProps}>
<Component {...pageProps} />
</Layout>
</ChakraProvider>
</ManagedUIContext>
</>
)

View File

@ -4,7 +4,12 @@ class MyDocument extends Document {
render() {
return (
<Html>
<Head />
<Head>
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/photo-sphere-viewer@4/dist/photo-sphere-viewer.min.css"
/>
</Head>
<body className="loading">
<Main />
<NextScript />

18
site/pages/about.tsx Normal file
View File

@ -0,0 +1,18 @@
import { Divider, Tab, TabList, TabPanel, TabPanels, Tabs } from '@chakra-ui/react'
import { Layout } from '@components/common'
import AboutSlider from '@components/common/About/AboutSlider'
import { Text } from '@components/ui'
export default function About() {
return (
<>
<div className="mx-8 sm:mx-auto py-20 flex flex-col items-center justify-center fit">
<AboutSlider></AboutSlider>
</div>
</>
)
}
About.Layout = Layout

View File

@ -1,89 +1,85 @@
import commerce from '@lib/api/commerce'
import { Layout } from '@components/common'
import { ProductCard } from '@components/product'
import { Grid, Marquee, Hero } from '@components/ui'
// import HomeAllProductsGrid from '@components/common/HomeAllProductsGrid'
import type { GetStaticPropsContext, InferGetStaticPropsType } from 'next'
import ImageMapper from 'react-img-mapper'
import { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import { useDisclosure } from '@chakra-ui/react'
import PolygonModal from '@components/common/HomePage/PolygonModal/PolygonModal'
export async function getStaticProps({
preview,
locale,
locales,
}: GetStaticPropsContext) {
const config = { locale, locales }
const productsPromise = commerce.getAllProducts({
variables: { first: 6 },
config,
preview,
// Saleor provider only
...({ featured: true } as any),
})
const pagesPromise = commerce.getAllPages({ config, preview })
const siteInfoPromise = commerce.getSiteInfo({ config, preview })
const { products } = await productsPromise
const { pages } = await pagesPromise
const { categories, brands } = await siteInfoPromise
export default function Home() {
return {
props: {
products,
categories,
brands,
pages,
},
revalidate: 60,
const imagePath = "homepageBackgroundImage.png";
const {locale} = useRouter();
const [mapContainerWidth, setMapContainerWidth] = useState<number | undefined>(600);
const [innerWidth, setInnerWidth] = useState<number | undefined>(600);
const [decadeClicked, setDecadeClicked] = useState<string>("12")
const { isOpen, onOpen, onClose } = useDisclosure()
const mapDefinition = {
name: "my-map",
areas: [
{
id: '12',
title: '2000',
name: '2000',
fillColor: '#eab54d4d',
strokeColor: 'black',
coords: [4653,1231,5039,1396,5204,1622,5218,2027,5039,2315,4709,2461,4276,2339,4068,2084,4040,1679,4177,1457,4337,1325],
shape: "poly",
//href: `/${locale}/abruzzo/12`,
href: "#",
},
{
id: '11',
title: '1990',
name: '1990',
fillColor: '#eab54d4d',
strokeColor: 'black',
coords: [3904,974,475],
shape: "circle",
//href: `/${locale}/abruzzo/12`,
href: "#",
},
{
id: '10',
title: '1980',
name: '1980',
fillColor: '#eab54d4d',
strokeColor: 'black',
coords: [3045,611,387],
shape: "circle",
//href: `/${locale}/abruzzo/12`,
href: "#",
}
]
}
}
export default function Home({
products,
}: InferGetStaticPropsType<typeof getStaticProps>) {
useEffect(() => {
// Handler to call on window resize
function handleResize() {
setInnerWidth(window.innerWidth);
}
window.addEventListener('resize', handleResize);
handleResize();
return () => window.removeEventListener('resize', handleResize);
}, []);
useEffect(() => {
setMapContainerWidth(document.getElementById('mapContainer')?.clientWidth);
}, [innerWidth]);
return (
<>
<Grid variant="filled">
{products.slice(0, 3).map((product: any, i: number) => (
<ProductCard
key={product.id}
product={product}
imgProps={{
width: i === 0 ? 1080 : 540,
height: i === 0 ? 1080 : 540,
priority: true,
}}
/>
))}
</Grid>
<Marquee variant="secondary">
{products.slice(0, 3).map((product: any, i: number) => (
<ProductCard key={product.id} product={product} variant="slim" />
))}
</Marquee>
<Hero
headline=" Dessert dragée halvah croissant."
description="Cupcake ipsum dolor sit amet lemon drops pastry cotton candy. Sweet carrot cake macaroon bonbon croissant fruitcake jujubes macaroon oat cake. Soufflé bonbon caramels jelly beans. Tiramisu sweet roll cheesecake pie carrot cake. "
/>
<Grid layout="B" variant="filled">
{products.slice(0, 3).map((product: any, i: number) => (
<ProductCard
key={product.id}
product={product}
imgProps={{
width: i === 0 ? 1080 : 540,
height: i === 0 ? 1080 : 540,
}}
/>
))}
</Grid>
<Marquee>
{products.slice(3).map((product: any, i: number) => (
<ProductCard key={product.id} product={product} variant="slim" />
))}
</Marquee>
{/* <HomeAllProductsGrid
newestProducts={products}
categories={categories}
brands={brands}
/> */}
<section id='mapContainer' className='w-full'>
<div>
<ImageMapper natural stayHighlighted onClick={area => { setDecadeClicked(area.id!); onOpen() }} parentWidth={mapContainerWidth} responsive={true} src={imagePath} map={mapDefinition}></ImageMapper>
<PolygonModal key={decadeClicked} decade={decadeClicked} onModalClose={onClose} isOpen={isOpen} />
</div>
</section>
</>
)
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 495 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?>
<svg width='150' height='150' id='svg' xmlns='http://www.w3.org/2000/svg'><polygon id="poly" points="45.61073738537634,34.54915028125263 27.447174185242325,59.54915028125263 27.447174185242318,90.45084971874736 45.61073738537634,115.45084971874736 75,125 104.38926261462365,115.45084971874738 122.55282581475768,90.45084971874738 122.55282581475768,59.549150281252636 104.38926261462368,34.549150281252636 75.00000000000001,25 "></polygon></svg>

After

Width:  |  Height:  |  Size: 483 B

View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?>
<svg width='150' height='150' id='svg' xmlns='http://www.w3.org/2000/svg'><polygon id="poly" points="47.96795912722013,32.93732335844094 29.518400232274082,54.22924934990568 25.508927905953357,82.11574191366425 37.21252128228708,107.74303669726424 60.91337215792852,122.97464868072487 89.08662784207147,122.97464868072487 112.7874787177129,107.74303669726426 124.49107209404664,82.11574191366427 120.48159976772592,54.2292493499057 102.03204087277987,32.93732335844094 75.00000000000006,25 "></polygon></svg>

After

Width:  |  Height:  |  Size: 546 B

View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?>
<svg width='150' height='150' id='svg' xmlns='http://www.w3.org/2000/svg'><polygon id="poly" points="50,31.69872981077806 31.69872981077807,49.99999999999999 25,75 31.69872981077806,99.99999999999999 50,118.30127018922194 75,125 99.99999999999999,118.30127018922194 118.30127018922192,100.00000000000003 125,75.00000000000001 118.30127018922192,49.99999999999999 100.00000000000003,31.698729810778083 75.00000000000001,25 "></polygon></svg>

After

Width:  |  Height:  |  Size: 478 B

View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?>
<svg width='150' height='150' id='svg' xmlns='http://www.w3.org/2000/svg'><circle cx="75" cy="75" r="50" /></svg>

After

Width:  |  Height:  |  Size: 151 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="150" id="svg"><polygon id="poly" points="45.61073738537634,34.54915028125263 27.447174185242325,59.54915028125263 27.447174185242318,90.45084971874736 45.61073738537634,115.45084971874736 75,125 104.38926261462365,115.45084971874738 122.55282581475768,90.45084971874738 122.55282581475768,59.549150281252636 104.38926261462368,34.549150281252636 75.00000000000001,25 " fill="#120eff"/></svg>

After

Width:  |  Height:  |  Size: 451 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="150" id="svg"><polygon id="poly" points="47.96795912722013,32.93732335844094 29.518400232274082,54.22924934990568 25.508927905953357,82.11574191366425 37.21252128228708,107.74303669726424 60.91337215792852,122.97464868072487 89.08662784207147,122.97464868072487 112.7874787177129,107.74303669726426 124.49107209404664,82.11574191366427 120.48159976772592,54.2292493499057 102.03204087277987,32.93732335844094 75.00000000000006,25 " fill="#8710ff"/></svg>

After

Width:  |  Height:  |  Size: 514 B

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="150"
height="150"
id="svg"
version="1.1"
sodipodi:docname="12.svg"
inkscape:export-filename="colorized\12.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs12" />
<sodipodi:namedview
id="namedview10"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false" />
<polygon
id="poly"
points="50,118.30127 75,125 100,118.30127 118.30127,100 125,75 118.30127,50 100,31.69873 75,25 50,31.69873 31.69873,50 25,75 31.69873,100 "
style="fill:#fa3975;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-dasharray:none;stroke-opacity:1"
inkscape:export-filename="colorized\12.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96" />
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?>
<svg width='150' height='150' id='svg' xmlns='http://www.w3.org/2000/svg'><polygon id="poly" points="42.86061951567304,36.697777844051096 25.7596123493896,66.31759111665347 31.69872981077806,99.99999999999999 57.898992833716555,121.98463103929541 92.10100716628344,121.98463103929542 118.30127018922192,100.00000000000003 124.24038765061042,66.3175911166535 107.13938048432698,36.69777784405111 75.00000000000001,25 "></polygon></svg>

After

Width:  |  Height:  |  Size: 472 B

View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?>
<svg width='150' height='150' id='svg' xmlns='http://www.w3.org/2000/svg'><circle cx="75" cy="0" r="50" /></svg>

After

Width:  |  Height:  |  Size: 150 B

View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?>
<svg width='150' height='150' id='svg' xmlns='http://www.w3.org/2000/svg'><polygon id="poly" points="35.90842587659851,43.82550990706332 26.25360439090882,86.12604669781572 53.30581304412209,120.04844339512096 96.6941869558779,120.04844339512096 123.74639560909118,86.12604669781572 114.0915741234015,43.825509907063335 75.00000000000001,25 "></polygon></svg>

After

Width:  |  Height:  |  Size: 397 B

View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?>
<svg width='150' height='150' id='svg' xmlns='http://www.w3.org/2000/svg'><polygon id="poly" points="31.69872981077807,49.99999999999999 31.69872981077806,99.99999999999999 75,125 118.30127018922192,100.00000000000003 118.30127018922192,49.99999999999999 75.00000000000001,25 "></polygon></svg>

After

Width:  |  Height:  |  Size: 332 B

View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?>
<svg width='150' height='150' id='svg' xmlns='http://www.w3.org/2000/svg'><polygon id="poly" points="39.64466094067263,39.64466094067262 25,75 39.64466094067262,110.35533905932738 75,125 110.35533905932738,110.35533905932738 125,75.00000000000001 110.35533905932738,39.64466094067263 75.00000000000001,25 "></polygon></svg>

After

Width:  |  Height:  |  Size: 361 B

View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?>
<svg width='150' height='150' id='svg' xmlns='http://www.w3.org/2000/svg'><polygon id="poly" points="27.447174185242325,59.54915028125263 45.61073738537634,115.45084971874736 104.38926261462365,115.45084971874738 122.55282581475768,59.549150281252636 75.00000000000001,25 "></polygon></svg>

After

Width:  |  Height:  |  Size: 328 B

View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?>
<svg width='150' height='150' id='svg' xmlns='http://www.w3.org/2000/svg'><polygon id="poly" points="25,75 75,125 125,75.00000000000001 75.00000000000001,25 "></polygon></svg>

After

Width:  |  Height:  |  Size: 213 B

View File

@ -0,0 +1,2 @@
<?xml version="1.0" standalone="no"?>
<svg width='150' height='150' id='svg' xmlns='http://www.w3.org/2000/svg'><polygon id="poly" points="31.69872981077806,99.99999999999999 118.30127018922192,100.00000000000003 75.00000000000001,25 "></polygon></svg>

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 330 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 420 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 651 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 356 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 850 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 MiB

Some files were not shown because too many files have changed in this diff Show More