Start migration
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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
136
site/components/common/About/AboutSlider.tsx
Normal 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 dall’esposizione 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 dell’usato. Riutilizzando la visione ancestrale del commercio basata sul baratto, e adattandola in chiave moderna, assieme ad altri servizi unici, Safara è davvero un’agenzia unica nel suo genere. Pensiamo inoltre che l’idea 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>
|
||||
);
|
||||
}
|
@ -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 }) => {
|
||||
|
@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -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>
|
||||
)
|
||||
|
57
site/components/common/Navbar/NavBarFiltersDrawer.tsx
Normal 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;
|
||||
};
|
83
site/components/common/Navbar/NavBarFiltersItem.tsx
Normal 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;
|
@ -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
|
||||
|
@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -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>
|
||||
</>
|
||||
)
|
||||
};
|
@ -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)!);
|
||||
}
|
||||
}
|
@ -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>
|
||||
</>
|
||||
)
|
||||
};
|
@ -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>
|
||||
)
|
||||
};
|
@ -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);
|
||||
}
|
22
site/components/common/Room/RoomTypes/RoomTypes.tsx
Normal 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
|
||||
}
|
100
site/components/product/ProductCardRoom/ProductCardRoom.tsx
Normal 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>
|
||||
);
|
||||
}
|
@ -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);
|
||||
}
|
72
site/components/product/ProductModel/ProductModel.tsx
Normal 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}} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -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",
|
||||
|
380
site/pages/[region]/[decade]/[start].tsx
Normal 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
|
@ -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>
|
||||
</>
|
||||
)
|
||||
|
@ -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
@ -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
|
@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
Before Width: | Height: | Size: 154 KiB |
Before Width: | Height: | Size: 254 KiB |
Before Width: | Height: | Size: 239 KiB |
Before Width: | Height: | Size: 154 KiB |
Before Width: | Height: | Size: 495 KiB |
Before Width: | Height: | Size: 466 KiB |
Before Width: | Height: | Size: 345 KiB |
2
site/public/assets/polygons/10.svg
Normal 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 |
2
site/public/assets/polygons/11.svg
Normal 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 |
2
site/public/assets/polygons/12.svg
Normal 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 |
2
site/public/assets/polygons/circle.svg
Normal 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 |
1
site/public/assets/polygons/colorized/10.svg
Normal 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 |
1
site/public/assets/polygons/colorized/11.svg
Normal 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 |
34
site/public/assets/polygons/colorized/12.svg
Normal 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 |
2
site/public/assets/polygons/enneagon.svg
Normal 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 |
2
site/public/assets/polygons/halfcircle.svg
Normal 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 |
2
site/public/assets/polygons/heptagon.svg
Normal 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 |
2
site/public/assets/polygons/hexagon.svg
Normal 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 |
2
site/public/assets/polygons/octagon.svg
Normal 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 |
2
site/public/assets/polygons/pentagon.svg
Normal 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 |
2
site/public/assets/polygons/quadrilateral.svg
Normal 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 |
2
site/public/assets/polygons/triangle.svg
Normal 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 |
Before Width: | Height: | Size: 406 KiB |
Before Width: | Height: | Size: 330 KiB |
Before Width: | Height: | Size: 420 KiB |
Before Width: | Height: | Size: 651 KiB |
Before Width: | Height: | Size: 356 KiB |
BIN
site/public/homepageBackgroundImage.png
Normal file
After Width: | Height: | Size: 197 KiB |
BIN
site/public/regions/abruzzo/10/day/bagno80.jpg
Normal file
After Width: | Height: | Size: 2.9 MiB |
BIN
site/public/regions/abruzzo/10/day/cameraadu80.jpg
Normal file
After Width: | Height: | Size: 5.0 MiB |
BIN
site/public/regions/abruzzo/10/day/camerarag80.jpg
Normal file
After Width: | Height: | Size: 4.7 MiB |
BIN
site/public/regions/abruzzo/10/day/cucina80.jpg
Normal file
After Width: | Height: | Size: 4.7 MiB |
BIN
site/public/regions/abruzzo/10/day/garage80.jpg
Normal file
After Width: | Height: | Size: 850 KiB |
BIN
site/public/regions/abruzzo/10/day/giardino80.jpg
Normal file
After Width: | Height: | Size: 147 KiB |
BIN
site/public/regions/abruzzo/10/day/sala80.jpg
Normal file
After Width: | Height: | Size: 5.1 MiB |
BIN
site/public/regions/abruzzo/10/night/bagno80.jpg
Normal file
After Width: | Height: | Size: 3.0 MiB |
BIN
site/public/regions/abruzzo/10/night/cameraadu80.jpg
Normal file
After Width: | Height: | Size: 5.0 MiB |
BIN
site/public/regions/abruzzo/10/night/camerarag80.jpg
Normal file
After Width: | Height: | Size: 5.4 MiB |
BIN
site/public/regions/abruzzo/10/night/cucina80.jpg
Normal file
After Width: | Height: | Size: 4.6 MiB |
BIN
site/public/regions/abruzzo/10/night/garage80.jpg
Normal file
After Width: | Height: | Size: 848 KiB |
BIN
site/public/regions/abruzzo/10/night/giardino80.jpg
Normal file
After Width: | Height: | Size: 782 KiB |
BIN
site/public/regions/abruzzo/10/night/sala80.jpg
Normal file
After Width: | Height: | Size: 5.1 MiB |
BIN
site/public/regions/abruzzo/10/plan/plan.jpeg
Normal file
After Width: | Height: | Size: 202 KiB |
BIN
site/public/regions/abruzzo/10/resources/canzone.mp3
Normal file
BIN
site/public/regions/abruzzo/10/resources/canzone2.mp3
Normal file
BIN
site/public/regions/abruzzo/10/resources/immagineTV.png
Normal file
After Width: | Height: | Size: 4.7 MiB |
BIN
site/public/regions/abruzzo/10/resources/video.mp4
Normal file
BIN
site/public/regions/abruzzo/11/day/bagno11.jpg
Normal file
After Width: | Height: | Size: 3.6 MiB |
BIN
site/public/regions/abruzzo/11/day/cameraadu11.jpg
Normal file
After Width: | Height: | Size: 4.9 MiB |
BIN
site/public/regions/abruzzo/11/day/camerarag11.jpg
Normal file
After Width: | Height: | Size: 4.8 MiB |
BIN
site/public/regions/abruzzo/11/day/cucina11.jpg
Normal file
After Width: | Height: | Size: 7.0 MiB |
BIN
site/public/regions/abruzzo/11/day/garage11.jpg
Normal file
After Width: | Height: | Size: 3.2 MiB |
BIN
site/public/regions/abruzzo/11/day/giardino11.jpg
Normal file
After Width: | Height: | Size: 5.7 MiB |
BIN
site/public/regions/abruzzo/11/day/sala11.jpg
Normal file
After Width: | Height: | Size: 5.1 MiB |
BIN
site/public/regions/abruzzo/11/night/bagno11.jpg
Normal file
After Width: | Height: | Size: 3.6 MiB |
BIN
site/public/regions/abruzzo/11/night/cameraadu11.jpg
Normal file
After Width: | Height: | Size: 4.9 MiB |
BIN
site/public/regions/abruzzo/11/night/camerarag11.jpg
Normal file
After Width: | Height: | Size: 4.8 MiB |
BIN
site/public/regions/abruzzo/11/night/cucina11.jpg
Normal file
After Width: | Height: | Size: 7.0 MiB |
BIN
site/public/regions/abruzzo/11/night/garage11.jpg
Normal file
After Width: | Height: | Size: 3.0 MiB |
BIN
site/public/regions/abruzzo/11/night/giardino11.jpg
Normal file
After Width: | Height: | Size: 3.3 MiB |
BIN
site/public/regions/abruzzo/11/night/sala11.jpg
Normal file
After Width: | Height: | Size: 5.0 MiB |
BIN
site/public/regions/abruzzo/11/plan/plan.jpeg
Normal file
After Width: | Height: | Size: 273 KiB |
BIN
site/public/regions/abruzzo/12/day/bagno12.jpg
Normal file
After Width: | Height: | Size: 4.7 MiB |
BIN
site/public/regions/abruzzo/12/day/cameraadu12.jpg
Normal file
After Width: | Height: | Size: 4.6 MiB |
BIN
site/public/regions/abruzzo/12/day/camerarag12.jpg
Normal file
After Width: | Height: | Size: 5.0 MiB |
BIN
site/public/regions/abruzzo/12/day/corridoio12.jpg
Normal file
After Width: | Height: | Size: 3.2 MiB |
BIN
site/public/regions/abruzzo/12/day/cucina12.jpg
Normal file
After Width: | Height: | Size: 4.8 MiB |
BIN
site/public/regions/abruzzo/12/day/garage12.jpg
Normal file
After Width: | Height: | Size: 3.2 MiB |
BIN
site/public/regions/abruzzo/12/day/giardino12.jpg
Normal file
After Width: | Height: | Size: 3.6 MiB |
BIN
site/public/regions/abruzzo/12/day/sala12.jpg
Normal file
After Width: | Height: | Size: 5.0 MiB |
BIN
site/public/regions/abruzzo/12/night/bagno12.jpg
Normal file
After Width: | Height: | Size: 4.6 MiB |
BIN
site/public/regions/abruzzo/12/night/cameraadu12.jpg
Normal file
After Width: | Height: | Size: 4.5 MiB |
BIN
site/public/regions/abruzzo/12/night/camerarag12.jpg
Normal file
After Width: | Height: | Size: 4.9 MiB |