mirror of
https://github.com/vercel/commerce.git
synced 2025-05-17 06:56:59 +00:00
Continue Migration, almost done
This commit is contained in:
parent
15faacc7de
commit
e5ebf60e83
@ -160,7 +160,6 @@ export interface Product {
|
|||||||
/**
|
/**
|
||||||
* The product's base price. Could be the minimum value, or default variant price.
|
* The product's base price. Could be the minimum value, or default variant price.
|
||||||
*/
|
*/
|
||||||
metafields: ProductMetafield[]
|
|
||||||
price: ProductPrice
|
price: ProductPrice
|
||||||
/**
|
/**
|
||||||
* List of product's options.
|
* List of product's options.
|
||||||
@ -170,6 +169,10 @@ export interface Product {
|
|||||||
* The product’s vendor name.
|
* The product’s vendor name.
|
||||||
*/
|
*/
|
||||||
vendor?: string
|
vendor?: string
|
||||||
|
/**
|
||||||
|
* List of product's media
|
||||||
|
*/
|
||||||
|
media: ProductMedia[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SearchProductsBody {
|
export interface SearchProductsBody {
|
||||||
@ -266,3 +269,25 @@ export type GetProductOperation = {
|
|||||||
withMetafields?: MetafieldsIdentifiers
|
withMetafields?: MetafieldsIdentifiers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ProductPreviewMediaImage = {
|
||||||
|
altText: string
|
||||||
|
height: string
|
||||||
|
id: string
|
||||||
|
width: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProductMediaSource = {
|
||||||
|
filesize: number
|
||||||
|
format: string
|
||||||
|
mimeType: string
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ProductMedia = {
|
||||||
|
alt: string
|
||||||
|
id: string
|
||||||
|
mediaContentType: string
|
||||||
|
previewImage: ProductPreviewMediaImage
|
||||||
|
sources: ProductMediaSource[]
|
||||||
|
}
|
||||||
|
@ -19,6 +19,8 @@ import type {
|
|||||||
Collection,
|
Collection,
|
||||||
Maybe,
|
Maybe,
|
||||||
Metafield as ShopifyMetafield,
|
Metafield as ShopifyMetafield,
|
||||||
|
MediaConnection,
|
||||||
|
Model3d,
|
||||||
} from '../../schema'
|
} from '../../schema'
|
||||||
|
|
||||||
import { colorMap } from './colors'
|
import { colorMap } from './colors'
|
||||||
@ -112,6 +114,7 @@ export function normalizeProduct(
|
|||||||
priceRange,
|
priceRange,
|
||||||
options,
|
options,
|
||||||
metafields,
|
metafields,
|
||||||
|
media,
|
||||||
...rest
|
...rest
|
||||||
}: ShopifyProduct,
|
}: ShopifyProduct,
|
||||||
locale?: string
|
locale?: string
|
||||||
@ -131,6 +134,7 @@ export function normalizeProduct(
|
|||||||
.map((o) => normalizeProductOption(o))
|
.map((o) => normalizeProductOption(o))
|
||||||
: [],
|
: [],
|
||||||
metafields: normalizeMetafields(metafields, locale),
|
metafields: normalizeMetafields(metafields, locale),
|
||||||
|
media: media ? normalizeProductMedia(media) : [],
|
||||||
description: description || '',
|
description: description || '',
|
||||||
...(descriptionHtml && { descriptionHtml }),
|
...(descriptionHtml && { descriptionHtml }),
|
||||||
...rest,
|
...rest,
|
||||||
@ -256,3 +260,18 @@ export const normalizeMetafieldValue = (
|
|||||||
}
|
}
|
||||||
return getMetafieldValue(type, value, locale)
|
return getMetafieldValue(type, value, locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -83,6 +83,7 @@ const getProductQuery = /* GraphQL */ `
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
metafields(identifiers: $withMetafields) {
|
metafields(identifiers: $withMetafields) {
|
||||||
key
|
key
|
||||||
value
|
value
|
||||||
|
43
pnpm-lock.yaml
generated
43
pnpm-lock.yaml
generated
@ -657,6 +657,7 @@ importers:
|
|||||||
tabbable: ^5.2.1
|
tabbable: ^5.2.1
|
||||||
tailwindcss: ^3.0.13
|
tailwindcss: ^3.0.13
|
||||||
typescript: 4.7.4
|
typescript: 4.7.4
|
||||||
|
yet-another-react-lightbox: ^2.2.3
|
||||||
dependencies:
|
dependencies:
|
||||||
'@chakra-ui/icons': 2.0.14_react@18.2.0
|
'@chakra-ui/icons': 2.0.14_react@18.2.0
|
||||||
'@chakra-ui/react': 2.4.4_4krdlvlqq3ittuocexwl336v2q
|
'@chakra-ui/react': 2.4.4_4krdlvlqq3ittuocexwl336v2q
|
||||||
@ -702,6 +703,7 @@ importers:
|
|||||||
screenfull: 6.0.2
|
screenfull: 6.0.2
|
||||||
tabbable: 5.3.3
|
tabbable: 5.3.3
|
||||||
tailwindcss: 3.1.8_postcss@8.4.16
|
tailwindcss: 3.1.8_postcss@8.4.16
|
||||||
|
yet-another-react-lightbox: 2.2.3_biqbaboplfbrettd7655fr4n2y
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@next/bundle-analyzer': 12.3.0
|
'@next/bundle-analyzer': 12.3.0
|
||||||
'@types/body-scroll-lock': 3.1.0
|
'@types/body-scroll-lock': 3.1.0
|
||||||
@ -3497,7 +3499,18 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
<<<<<<< HEAD
|
/@manifoldco/swagger-to-ts/2.1.0:
|
||||||
|
resolution: {integrity: sha512-IH0FAHhwWHR3Gs3rnVHNEscZujGn+K6/2Zu5cWfZre3Vz2tx1SvvJKEbSM89MztfDDRjOpb+6pQD/vqdEoTBVg==}
|
||||||
|
engines: {node: '>= 10.0.0'}
|
||||||
|
deprecated: This package has changed to openapi-typescript
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
chalk: 4.1.2
|
||||||
|
js-yaml: 3.14.1
|
||||||
|
meow: 7.1.1
|
||||||
|
prettier: 2.7.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@motionone/animation/10.15.1:
|
/@motionone/animation/10.15.1:
|
||||||
resolution: {integrity: sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==}
|
resolution: {integrity: sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -3544,19 +3557,6 @@ packages:
|
|||||||
hey-listen: 1.0.8
|
hey-listen: 1.0.8
|
||||||
tslib: 2.4.0
|
tslib: 2.4.0
|
||||||
dev: false
|
dev: false
|
||||||
=======
|
|
||||||
/@manifoldco/swagger-to-ts/2.1.0:
|
|
||||||
resolution: {integrity: sha512-IH0FAHhwWHR3Gs3rnVHNEscZujGn+K6/2Zu5cWfZre3Vz2tx1SvvJKEbSM89MztfDDRjOpb+6pQD/vqdEoTBVg==}
|
|
||||||
engines: {node: '>= 10.0.0'}
|
|
||||||
deprecated: This package has changed to openapi-typescript
|
|
||||||
hasBin: true
|
|
||||||
dependencies:
|
|
||||||
chalk: 4.1.2
|
|
||||||
js-yaml: 3.14.1
|
|
||||||
meow: 7.1.1
|
|
||||||
prettier: 2.7.1
|
|
||||||
dev: true
|
|
||||||
>>>>>>> 1ba9d3bd6e79da1f0b05df2f1c0c1ca3632b6c16
|
|
||||||
|
|
||||||
/@next/bundle-analyzer/12.3.0:
|
/@next/bundle-analyzer/12.3.0:
|
||||||
resolution: {integrity: sha512-hzRLHIrtwOiGEku9rmG7qZk+OQhnqQOL+ycl2XrjBaztBN/xaqnjoG4+HEf9L7ELN943BR+K/ZlaF2OEgbGm+Q==}
|
resolution: {integrity: sha512-hzRLHIrtwOiGEku9rmG7qZk+OQhnqQOL+ycl2XrjBaztBN/xaqnjoG4+HEf9L7ELN943BR+K/ZlaF2OEgbGm+Q==}
|
||||||
@ -7117,7 +7117,6 @@ packages:
|
|||||||
tslib: 2.4.0
|
tslib: 2.4.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
/hey-listen/1.0.8:
|
/hey-listen/1.0.8:
|
||||||
resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==}
|
resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -7127,11 +7126,10 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
react-is: 16.13.1
|
react-is: 16.13.1
|
||||||
dev: false
|
dev: false
|
||||||
=======
|
|
||||||
/hosted-git-info/2.8.9:
|
/hosted-git-info/2.8.9:
|
||||||
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
|
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
|
||||||
dev: true
|
dev: true
|
||||||
>>>>>>> 1ba9d3bd6e79da1f0b05df2f1c0c1ca3632b6c16
|
|
||||||
|
|
||||||
/http-cache-semantics/4.1.0:
|
/http-cache-semantics/4.1.0:
|
||||||
resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==}
|
resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==}
|
||||||
@ -11415,6 +11413,17 @@ packages:
|
|||||||
yargs-parser: 21.1.1
|
yargs-parser: 21.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/yet-another-react-lightbox/2.2.3_biqbaboplfbrettd7655fr4n2y:
|
||||||
|
resolution: {integrity: sha512-wTjXSqcKZfmudXQqZxe24SqzBAaK5x686CMNMAfNOrbmtyUACCrXTM78899DwtkEYMCBAGvgZc/2GOVu+EAtYA==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.8.0'
|
||||||
|
react-dom: '>=16.8.0'
|
||||||
|
dependencies:
|
||||||
|
react: 18.2.0
|
||||||
|
react-dom: 18.2.0_react@18.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/yn/3.1.1:
|
/yn/3.1.1:
|
||||||
resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
|
resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
@ -7,40 +7,44 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
Stack,
|
Stack,
|
||||||
Link,
|
Link,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react'
|
||||||
import NextLink from "next/link"
|
import NextLink from 'next/link'
|
||||||
import { Product } from '@commerce/types';
|
import { Product } from '@commerce/types'
|
||||||
|
|
||||||
import style from './ProductCardRoomStyle.module.css';
|
import style from './ProductCardRoomStyle.module.css'
|
||||||
|
|
||||||
export default function ProductCardRoom(props: {
|
export default function ProductCardRoom(props: {
|
||||||
product: Product.Product,
|
product: Product.Product
|
||||||
decade: string
|
decade: string
|
||||||
}) {
|
}) {
|
||||||
|
let historicDescription =
|
||||||
let historicDescription = props.product.metafields
|
props.product.metafields!.custom.descrizione_storica.value
|
||||||
.filter(meta => meta.key == 'descrizione_storica')
|
let technicalDescription =
|
||||||
.map(meta => meta.value);
|
props.product.metafields!.custom.descrizione_tecnica.value
|
||||||
let technicalDescription = props.product.metafields
|
let nationOrigin = props.product.metafields!.custom.nazionalit_.value
|
||||||
.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 (
|
return (
|
||||||
<Flex w="full" alignItems="center" justifyContent="center" direction={'row'}>
|
<Flex
|
||||||
|
w="full"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
direction={'row'}
|
||||||
|
>
|
||||||
<Box
|
<Box
|
||||||
maxW={'445px'}
|
maxW={'445px'}
|
||||||
w={'full'}
|
w={'full'}
|
||||||
boxShadow={'2xl'}
|
boxShadow={'2xl'}
|
||||||
rounded={'md'}
|
rounded={'md'}
|
||||||
overflow={'hidden'}
|
overflow={'hidden'}
|
||||||
className={style.cardBody}>
|
className={style.cardBody}
|
||||||
|
>
|
||||||
<Image
|
<Image
|
||||||
className={style.flagIcon}
|
className={style.flagIcon}
|
||||||
src={'http://purecatamphetamine.github.io/country-flag-icons/3x2/' + nationOrigin + '.svg'}
|
src={
|
||||||
|
'http://purecatamphetamine.github.io/country-flag-icons/3x2/' +
|
||||||
|
nationOrigin +
|
||||||
|
'.svg'
|
||||||
|
}
|
||||||
alt={`Picture of Flag`}
|
alt={`Picture of Flag`}
|
||||||
rounded={'lg'}
|
rounded={'lg'}
|
||||||
/>
|
/>
|
||||||
@ -51,17 +55,11 @@ export default function ProductCardRoom(props: {
|
|||||||
alt={`Picture of Decade`}
|
alt={`Picture of Decade`}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Box
|
<Box className={style.imageContainer} w={'full'} height={'220px'}>
|
||||||
className={style.imageContainer}
|
|
||||||
w={'full'}
|
|
||||||
height={'220px'}
|
|
||||||
>
|
|
||||||
<NextLink href={'/product/' + props.product.slug} passHref>
|
<NextLink href={'/product/' + props.product.slug} passHref>
|
||||||
<Link style={{textDecoration: 'none', height: 'inherit'}}>
|
<Link style={{ textDecoration: 'none', height: 'inherit' }}>
|
||||||
<Image
|
<Image
|
||||||
src={
|
src={props.product.images[0].url}
|
||||||
props.product.images[0].url
|
|
||||||
}
|
|
||||||
objectFit={'cover'}
|
objectFit={'cover'}
|
||||||
margin={'auto'}
|
margin={'auto'}
|
||||||
height={'inherit'}
|
height={'inherit'}
|
||||||
@ -70,20 +68,28 @@ export default function ProductCardRoom(props: {
|
|||||||
</NextLink>
|
</NextLink>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box
|
<Box p={5} className={style.captionContainer}>
|
||||||
p={5}
|
|
||||||
className={style.captionContainer}>
|
|
||||||
|
|
||||||
<Stack align={'center'}>
|
<Stack align={'center'}>
|
||||||
<Heading fontSize={'2xl'} textAlign={'center'} fontFamily={'body'} fontWeight={500}>
|
<Heading
|
||||||
|
fontSize={'2xl'}
|
||||||
|
textAlign={'center'}
|
||||||
|
fontFamily={'body'}
|
||||||
|
fontWeight={500}
|
||||||
|
>
|
||||||
{props.product.name}
|
{props.product.name}
|
||||||
</Heading>
|
</Heading>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<Stack mt={6} align={'center'}>
|
<Stack mt={6} align={'center'}>
|
||||||
<Divider borderColor={'blackAlpha.600'} />
|
<Divider borderColor={'blackAlpha.600'} />
|
||||||
{historicDescription.pop()?.split('\n').map((line, index) => (
|
{historicDescription.split('\n').map((line, index) => (
|
||||||
<Text key={index} padding={0} color={'gray.500'} fontSize={'sm'} align={'center'}>
|
<Text
|
||||||
|
key={index}
|
||||||
|
padding={0}
|
||||||
|
color={'gray.500'}
|
||||||
|
fontSize={'sm'}
|
||||||
|
align={'center'}
|
||||||
|
>
|
||||||
{line}
|
{line}
|
||||||
</Text>
|
</Text>
|
||||||
))}
|
))}
|
||||||
@ -93,8 +99,7 @@ export default function ProductCardRoom(props: {
|
|||||||
</Text>
|
</Text>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
)
|
||||||
}
|
}
|
@ -3,15 +3,15 @@ import { useAddItem } from '@framework/cart'
|
|||||||
import { FC, useEffect, useState } from 'react'
|
import { FC, useEffect, useState } from 'react'
|
||||||
import { ProductOptions } from '@components/product'
|
import { ProductOptions } from '@components/product'
|
||||||
import type { Product } from '@commerce/types/product'
|
import type { Product } from '@commerce/types/product'
|
||||||
import { Button, Text, Rating, Collapse, useUI } from '@components/ui'
|
import { Button, Rating, Collapse, Text, useUI } from '@components/ui'
|
||||||
import {
|
import {
|
||||||
getProductVariant,
|
getProductVariant,
|
||||||
selectDefaultOptionFromProduct,
|
selectDefaultOptionFromProduct,
|
||||||
SelectedOptions,
|
SelectedOptions,
|
||||||
} from '../helpers'
|
} from '../helpers'
|
||||||
import ErrorMessage from '@components/ui/ErrorMessage'
|
import { Box, Stack, Text as ChakraText } from '@chakra-ui/react'
|
||||||
import { ProductCustomFields } from '../ProductCustomFields'
|
|
||||||
import { ProductMetafields } from '../ProductMetafields'
|
import productDetailsMetafields from '../../../static_data/productDetailsMetafields.json'
|
||||||
|
|
||||||
interface ProductSidebarProps {
|
interface ProductSidebarProps {
|
||||||
product: Product
|
product: Product
|
||||||
@ -20,9 +20,8 @@ interface ProductSidebarProps {
|
|||||||
|
|
||||||
const ProductSidebar: FC<ProductSidebarProps> = ({ product, className }) => {
|
const ProductSidebar: FC<ProductSidebarProps> = ({ product, className }) => {
|
||||||
const addItem = useAddItem()
|
const addItem = useAddItem()
|
||||||
const { openSidebar, setSidebarView } = useUI()
|
const { openSidebar } = useUI()
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [error, setError] = useState<null | Error>(null)
|
|
||||||
const [selectedOptions, setSelectedOptions] = useState<SelectedOptions>({})
|
const [selectedOptions, setSelectedOptions] = useState<SelectedOptions>({})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -32,27 +31,20 @@ const ProductSidebar: FC<ProductSidebarProps> = ({ product, className }) => {
|
|||||||
const variant = getProductVariant(product, selectedOptions)
|
const variant = getProductVariant(product, selectedOptions)
|
||||||
const addToCart = async () => {
|
const addToCart = async () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
setError(null)
|
|
||||||
try {
|
try {
|
||||||
await addItem({
|
await addItem({
|
||||||
productId: String(product.id),
|
productId: String(product.id),
|
||||||
variantId: String(variant ? variant.id : product.variants[0]?.id),
|
variantId: String(variant ? variant.id : product.variants[0]?.id),
|
||||||
})
|
})
|
||||||
setSidebarView('CART_VIEW')
|
|
||||||
openSidebar()
|
openSidebar()
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
if (err instanceof Error) {
|
|
||||||
console.error(err)
|
|
||||||
setError({
|
|
||||||
...err,
|
|
||||||
message: 'Could not add item to cart. Please try again.',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(product.metafields!.custom)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<ProductOptions
|
<ProductOptions
|
||||||
@ -60,22 +52,31 @@ const ProductSidebar: FC<ProductSidebarProps> = ({ product, className }) => {
|
|||||||
selectedOptions={selectedOptions}
|
selectedOptions={selectedOptions}
|
||||||
setSelectedOptions={setSelectedOptions}
|
setSelectedOptions={setSelectedOptions}
|
||||||
/>
|
/>
|
||||||
<Text
|
|
||||||
className="pb-4 break-words w-full max-w-xl"
|
|
||||||
html={product.descriptionHtml || product.description}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{product.metafields?.reviews?.rating && (
|
{/* Product Description With Metafields */}
|
||||||
<div className="flex flex-row justify-between items-center">
|
|
||||||
<Rating value={product.metafields.reviews.rating.value} />
|
|
||||||
<div className="text-accent-6 pr-1 font-medium text-sm">
|
|
||||||
{product.metafields.reviews.count?.value ?? 0} reviews
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div>
|
<Box>
|
||||||
{error && <ErrorMessage error={error} className="my-5" />}
|
<Stack>
|
||||||
|
{productDetailsMetafields.metafields[0].names.map((meta) => (
|
||||||
|
<Box key={meta.key}>
|
||||||
|
<ChakraText
|
||||||
|
as={'span'}
|
||||||
|
textTransform={'uppercase'}
|
||||||
|
fontWeight={'bold'}
|
||||||
|
>
|
||||||
|
{meta.name}:{' '}
|
||||||
|
</ChakraText>
|
||||||
|
<ChakraText as={'span'}>
|
||||||
|
{product.metafields.custom
|
||||||
|
.filter((o) => o.key == meta.key)
|
||||||
|
.map((o) => o.value)}
|
||||||
|
</ChakraText>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<div style={{ marginTop: 20 }}>
|
||||||
{process.env.COMMERCE_CART_ENABLED && (
|
{process.env.COMMERCE_CART_ENABLED && (
|
||||||
<Button
|
<Button
|
||||||
aria-label="Add to Cart"
|
aria-label="Add to Cart"
|
||||||
@ -91,33 +92,6 @@ const ProductSidebar: FC<ProductSidebarProps> = ({ product, className }) => {
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6">
|
|
||||||
<Collapse title="Care">
|
|
||||||
This is a limited edition production run. Printing starts when the
|
|
||||||
drop ends.
|
|
||||||
</Collapse>
|
|
||||||
|
|
||||||
<Collapse title="Details">
|
|
||||||
This is a limited edition production run. Printing starts when the
|
|
||||||
drop ends. Reminder: Bad Boys For Life. Shipping may take 10+ days due
|
|
||||||
to COVID-19.
|
|
||||||
</Collapse>
|
|
||||||
|
|
||||||
{product.customFields && product.customFields?.length > 0 && (
|
|
||||||
<Collapse title="Specifications">
|
|
||||||
<ProductCustomFields customFields={product.customFields} />
|
|
||||||
</Collapse>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{product.metafields?.my_fields && (
|
|
||||||
<Collapse title="Specifications">
|
|
||||||
<ProductMetafields
|
|
||||||
metafields={product.metafields}
|
|
||||||
namespace="my_fields"
|
|
||||||
/>
|
|
||||||
</Collapse>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import cn from 'clsx'
|
import cn from 'clsx'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
import s from './ProductView.module.css'
|
import s from './ProductView.module.css'
|
||||||
import { FC } from 'react'
|
import { FC, useState } from 'react'
|
||||||
import type { Product } from '@commerce/types/product'
|
import type { Product } from '@commerce/types/product'
|
||||||
import usePrice from '@framework/product/use-price'
|
import usePrice from '@framework/product/use-price'
|
||||||
import { WishlistButton } from '@components/wishlist'
|
import { WishlistButton } from '@components/wishlist'
|
||||||
@ -10,6 +10,10 @@ import { Container, Text } from '@components/ui'
|
|||||||
import { SEO } from '@components/common'
|
import { SEO } from '@components/common'
|
||||||
import ProductSidebar from '../ProductSidebar'
|
import ProductSidebar from '../ProductSidebar'
|
||||||
import ProductTag from '../ProductTag'
|
import ProductTag from '../ProductTag'
|
||||||
|
import ProductModel from '../ProductModel/ProductModel'
|
||||||
|
import Lightbox from 'yet-another-react-lightbox'
|
||||||
|
import 'yet-another-react-lightbox/styles.css'
|
||||||
|
|
||||||
interface ProductViewProps {
|
interface ProductViewProps {
|
||||||
product: Product
|
product: Product
|
||||||
relatedProducts: Product[]
|
relatedProducts: Product[]
|
||||||
@ -22,6 +26,18 @@ const ProductView: FC<ProductViewProps> = ({ product, relatedProducts }) => {
|
|||||||
currencyCode: product.price.currencyCode!,
|
currencyCode: product.price.currencyCode!,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const model3dPath = product.media
|
||||||
|
.map((media) => {
|
||||||
|
return media.sources
|
||||||
|
.filter((source) => source.format == 'glb')
|
||||||
|
.map((source) => source.url)
|
||||||
|
.slice(0)
|
||||||
|
})
|
||||||
|
.pop()
|
||||||
|
?.pop()
|
||||||
|
|
||||||
|
const [isLightboxOpen, setLightboxOpen] = useState(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Container className="max-w-none w-full" clean>
|
<Container className="max-w-none w-full" clean>
|
||||||
@ -37,6 +53,7 @@ const ProductView: FC<ProductViewProps> = ({ product, relatedProducts }) => {
|
|||||||
{product.images.map((image, i) => (
|
{product.images.map((image, i) => (
|
||||||
<div key={image.url} className={s.imageContainer}>
|
<div key={image.url} className={s.imageContainer}>
|
||||||
<Image
|
<Image
|
||||||
|
id={'product-image-' + i}
|
||||||
className={s.img}
|
className={s.img}
|
||||||
src={image.url!}
|
src={image.url!}
|
||||||
alt={image.alt || 'Product Image'}
|
alt={image.alt || 'Product Image'}
|
||||||
@ -44,10 +61,28 @@ const ProductView: FC<ProductViewProps> = ({ product, relatedProducts }) => {
|
|||||||
height={600}
|
height={600}
|
||||||
priority={i === 0}
|
priority={i === 0}
|
||||||
quality="85"
|
quality="85"
|
||||||
|
style={{ cursor: 'pointer' }}
|
||||||
|
onClick={() => setLightboxOpen(true)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
{model3dPath != undefined ? (
|
||||||
|
<div key={'model3d'} className={s.imageContainer}>
|
||||||
|
<ProductModel modelPath={model3dPath}></ProductModel>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
</ProductSlider>
|
</ProductSlider>
|
||||||
|
<Lightbox
|
||||||
|
open={isLightboxOpen}
|
||||||
|
close={() => setLightboxOpen(false)}
|
||||||
|
slides={product.images.map((image) => {
|
||||||
|
return {
|
||||||
|
src: image.url,
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
{process.env.COMMERCE_WISHLIST_ENABLED && (
|
{process.env.COMMERCE_WISHLIST_ENABLED && (
|
||||||
<WishlistButton
|
<WishlistButton
|
||||||
|
@ -56,7 +56,8 @@
|
|||||||
"react-use-measure": "^2.1.1",
|
"react-use-measure": "^2.1.1",
|
||||||
"screenfull": "^6.0.2",
|
"screenfull": "^6.0.2",
|
||||||
"tabbable": "^5.2.1",
|
"tabbable": "^5.2.1",
|
||||||
"tailwindcss": "^3.0.13"
|
"tailwindcss": "^3.0.13",
|
||||||
|
"yet-another-react-lightbox": "^2.2.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@next/bundle-analyzer": "^12.0.8",
|
"@next/bundle-analyzer": "^12.0.8",
|
||||||
|
@ -22,6 +22,7 @@ import MarkerCardModal from '@components/common/Room/MarkerCardModal/MarkerCardM
|
|||||||
import { useDisclosure } from '@chakra-ui/react'
|
import { useDisclosure } from '@chakra-ui/react'
|
||||||
|
|
||||||
import decadesManifest from '../../../static_data/decadesManifest.json'
|
import decadesManifest from '../../../static_data/decadesManifest.json'
|
||||||
|
import productDetailsMetafields from '../../../static_data/productDetailsMetafields.json'
|
||||||
import {
|
import {
|
||||||
MarkerData,
|
MarkerData,
|
||||||
MarkerJson,
|
MarkerJson,
|
||||||
@ -123,7 +124,14 @@ export async function getStaticProps({
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
const productPromise = commerce.getProduct({
|
const productPromise = commerce.getProduct({
|
||||||
variables: { slug: productMarker.markerSource },
|
variables: {
|
||||||
|
slug: productMarker.markerSource,
|
||||||
|
withMetafields: [
|
||||||
|
{ namespace: 'custom', key: 'nazionalit_' },
|
||||||
|
{ namespace: 'custom', key: 'descrizione_tecnica' },
|
||||||
|
{ namespace: 'custom', key: 'descrizione_storica' },
|
||||||
|
],
|
||||||
|
},
|
||||||
config,
|
config,
|
||||||
preview,
|
preview,
|
||||||
})
|
})
|
||||||
@ -132,8 +140,6 @@ export async function getStaticProps({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(products)
|
|
||||||
|
|
||||||
if (!products) {
|
if (!products) {
|
||||||
throw new Error(`Products associated with markers not found`)
|
throw new Error(`Products associated with markers not found`)
|
||||||
}
|
}
|
||||||
|
@ -10,14 +10,16 @@ import { useRouter } from 'next/router'
|
|||||||
import { Layout } from '@components/common'
|
import { Layout } from '@components/common'
|
||||||
import { ProductView } from '@components/product'
|
import { ProductView } from '@components/product'
|
||||||
|
|
||||||
// Used by the Shopify Example
|
import productDetailsMetafields from '../../static_data/productDetailsMetafields.json'
|
||||||
const withMetafields = [
|
|
||||||
{ namespace: 'reviews', key: 'rating' },
|
const withMetafields = productDetailsMetafields.metafields[0].names.map(
|
||||||
{ namespace: 'reviews', key: 'count' },
|
(metafield: any) => {
|
||||||
{ namespace: 'my_fields', key: 'width' },
|
return {
|
||||||
{ namespace: 'my_fields', key: 'weight' },
|
namespace: metafield.namespace,
|
||||||
{ namespace: 'my_fields', key: 'length' },
|
key: metafield.key,
|
||||||
]
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
export async function getStaticProps({
|
export async function getStaticProps({
|
||||||
params,
|
params,
|
||||||
@ -28,6 +30,9 @@ export async function getStaticProps({
|
|||||||
const config = { locale, locales }
|
const config = { locale, locales }
|
||||||
const pagesPromise = commerce.getAllPages({ config, preview })
|
const pagesPromise = commerce.getAllPages({ config, preview })
|
||||||
const siteInfoPromise = commerce.getSiteInfo({ config, preview })
|
const siteInfoPromise = commerce.getSiteInfo({ config, preview })
|
||||||
|
|
||||||
|
console.log(withMetafields)
|
||||||
|
|
||||||
const productPromise = commerce.getProduct({
|
const productPromise = commerce.getProduct({
|
||||||
variables: {
|
variables: {
|
||||||
slug: params!.slug,
|
slug: params!.slug,
|
||||||
@ -47,6 +52,8 @@ export async function getStaticProps({
|
|||||||
const { product } = await productPromise
|
const { product } = await productPromise
|
||||||
const { products: relatedProducts } = await allProductsPromise
|
const { products: relatedProducts } = await allProductsPromise
|
||||||
|
|
||||||
|
console.log(product)
|
||||||
|
|
||||||
if (!product) {
|
if (!product) {
|
||||||
return {
|
return {
|
||||||
notFound: true,
|
notFound: true,
|
||||||
|
@ -5,51 +5,63 @@
|
|||||||
"names": [
|
"names": [
|
||||||
{
|
{
|
||||||
"name": "Tipo",
|
"name": "Tipo",
|
||||||
"key": "tipo"
|
"key": "tipo",
|
||||||
|
"namespace": "custom"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Materiale",
|
"name": "Materiale",
|
||||||
"key": "materiale"
|
"key": "materiale",
|
||||||
|
"namespace": "custom"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Colore",
|
"name": "Colore",
|
||||||
"key": "colore"
|
"key": "colore",
|
||||||
|
"namespace": "custom"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Condizioni Estetiche",
|
"name": "Condizioni Estetiche",
|
||||||
"key": "condizioni_estetiche"
|
"key": "condizioni_estetiche",
|
||||||
|
"namespace": "custom"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Altezza",
|
"name": "Altezza",
|
||||||
"key": "altezza"
|
"key": "altezza",
|
||||||
|
"namespace": "custom"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Spessore",
|
"name": "Spessore",
|
||||||
"key": "spessore"
|
"key": "spessore",
|
||||||
|
"namespace": "custom"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Larghezza",
|
"name": "Larghezza",
|
||||||
"key": "larghezza"
|
"key": "larghezza",
|
||||||
|
"namespace": "custom"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Peso",
|
"name": "Peso",
|
||||||
"key": "peso"
|
"key": "peso",
|
||||||
|
"namespace": "custom"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Confezione e Accessori Originali",
|
"name": "Confezione e Accessori Originali",
|
||||||
"key": "confezione_accessori"
|
"key": "confezione_accessori",
|
||||||
|
"namespace": "custom"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Descrizione",
|
"name": "Descrizione",
|
||||||
"key": "descrizione"
|
"key": "descrizione",
|
||||||
|
"namespace": "custom"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Vintage",
|
"name": "Vintage",
|
||||||
"key": "vintage"
|
"key": "vintage",
|
||||||
|
"namespace": "custom"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Made In",
|
"name": "Made In",
|
||||||
"key": "made_in"
|
"key": "made_in",
|
||||||
|
"namespace": "custom"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user