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

@@ -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}} />
</>
);
}