mirror of
https://github.com/vercel/commerce.git
synced 2025-07-23 04:36:49 +00:00
Continue Migration, almost done
This commit is contained in:
@@ -7,83 +7,89 @@ import {
|
||||
Text,
|
||||
Stack,
|
||||
Link,
|
||||
} from '@chakra-ui/react';
|
||||
import NextLink from "next/link"
|
||||
import { Product } from '@commerce/types';
|
||||
} from '@chakra-ui/react'
|
||||
import NextLink from 'next/link'
|
||||
import { Product } from '@commerce/types'
|
||||
|
||||
import style from './ProductCardRoomStyle.module.css';
|
||||
import style from './ProductCardRoomStyle.module.css'
|
||||
|
||||
export default function ProductCardRoom(props: {
|
||||
product: Product.Product,
|
||||
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);
|
||||
let historicDescription =
|
||||
props.product.metafields!.custom.descrizione_storica.value
|
||||
let technicalDescription =
|
||||
props.product.metafields!.custom.descrizione_tecnica.value
|
||||
let nationOrigin = props.product.metafields!.custom.nazionalit_.value
|
||||
|
||||
return (
|
||||
<Flex w="full" alignItems="center" justifyContent="center" direction={'row'}>
|
||||
<Flex
|
||||
w="full"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
direction={'row'}
|
||||
>
|
||||
<Box
|
||||
maxW={'445px'}
|
||||
w={'full'}
|
||||
boxShadow={'2xl'}
|
||||
rounded={'md'}
|
||||
overflow={'hidden'}
|
||||
className={style.cardBody}>
|
||||
|
||||
className={style.cardBody}
|
||||
>
|
||||
<Image
|
||||
className={style.flagIcon}
|
||||
src={'http://purecatamphetamine.github.io/country-flag-icons/3x2/' + nationOrigin + '.svg'}
|
||||
alt={`Picture of Flag`}
|
||||
rounded={'lg'}
|
||||
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`}
|
||||
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 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}>
|
||||
|
||||
|
||||
<Box p={5} className={style.captionContainer}>
|
||||
<Stack align={'center'}>
|
||||
<Heading fontSize={'2xl'} textAlign={'center'} fontFamily={'body'} fontWeight={500}>
|
||||
{props.product.name}
|
||||
</Heading>
|
||||
<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'}>
|
||||
{historicDescription.split('\n').map((line, index) => (
|
||||
<Text
|
||||
key={index}
|
||||
padding={0}
|
||||
color={'gray.500'}
|
||||
fontSize={'sm'}
|
||||
align={'center'}
|
||||
>
|
||||
{line}
|
||||
</Text>
|
||||
))}
|
||||
@@ -93,8 +99,7 @@ export default function ProductCardRoom(props: {
|
||||
</Text>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
</Box>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@@ -3,15 +3,15 @@ import { useAddItem } from '@framework/cart'
|
||||
import { FC, useEffect, useState } from 'react'
|
||||
import { ProductOptions } from '@components/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 {
|
||||
getProductVariant,
|
||||
selectDefaultOptionFromProduct,
|
||||
SelectedOptions,
|
||||
} from '../helpers'
|
||||
import ErrorMessage from '@components/ui/ErrorMessage'
|
||||
import { ProductCustomFields } from '../ProductCustomFields'
|
||||
import { ProductMetafields } from '../ProductMetafields'
|
||||
import { Box, Stack, Text as ChakraText } from '@chakra-ui/react'
|
||||
|
||||
import productDetailsMetafields from '../../../static_data/productDetailsMetafields.json'
|
||||
|
||||
interface ProductSidebarProps {
|
||||
product: Product
|
||||
@@ -20,9 +20,8 @@ interface ProductSidebarProps {
|
||||
|
||||
const ProductSidebar: FC<ProductSidebarProps> = ({ product, className }) => {
|
||||
const addItem = useAddItem()
|
||||
const { openSidebar, setSidebarView } = useUI()
|
||||
const { openSidebar } = useUI()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [error, setError] = useState<null | Error>(null)
|
||||
const [selectedOptions, setSelectedOptions] = useState<SelectedOptions>({})
|
||||
|
||||
useEffect(() => {
|
||||
@@ -32,27 +31,20 @@ const ProductSidebar: FC<ProductSidebarProps> = ({ product, className }) => {
|
||||
const variant = getProductVariant(product, selectedOptions)
|
||||
const addToCart = async () => {
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
try {
|
||||
await addItem({
|
||||
productId: String(product.id),
|
||||
variantId: String(variant ? variant.id : product.variants[0]?.id),
|
||||
})
|
||||
setSidebarView('CART_VIEW')
|
||||
openSidebar()
|
||||
setLoading(false)
|
||||
} catch (err) {
|
||||
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 (
|
||||
<div className={className}>
|
||||
<ProductOptions
|
||||
@@ -60,22 +52,31 @@ const ProductSidebar: FC<ProductSidebarProps> = ({ product, className }) => {
|
||||
selectedOptions={selectedOptions}
|
||||
setSelectedOptions={setSelectedOptions}
|
||||
/>
|
||||
<Text
|
||||
className="pb-4 break-words w-full max-w-xl"
|
||||
html={product.descriptionHtml || product.description}
|
||||
/>
|
||||
|
||||
{product.metafields?.reviews?.rating && (
|
||||
<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>
|
||||
)}
|
||||
{/* Product Description With Metafields */}
|
||||
|
||||
<div>
|
||||
{error && <ErrorMessage error={error} className="my-5" />}
|
||||
<Box>
|
||||
<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 && (
|
||||
<Button
|
||||
aria-label="Add to Cart"
|
||||
@@ -91,33 +92,6 @@ const ProductSidebar: FC<ProductSidebarProps> = ({ product, className }) => {
|
||||
</Button>
|
||||
)}
|
||||
</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>
|
||||
)
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import cn from 'clsx'
|
||||
import Image from 'next/image'
|
||||
import s from './ProductView.module.css'
|
||||
import { FC } from 'react'
|
||||
import { FC, useState } from 'react'
|
||||
import type { Product } from '@commerce/types/product'
|
||||
import usePrice from '@framework/product/use-price'
|
||||
import { WishlistButton } from '@components/wishlist'
|
||||
@@ -10,6 +10,10 @@ import { Container, Text } from '@components/ui'
|
||||
import { SEO } from '@components/common'
|
||||
import ProductSidebar from '../ProductSidebar'
|
||||
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 {
|
||||
product: Product
|
||||
relatedProducts: Product[]
|
||||
@@ -22,6 +26,18 @@ const ProductView: FC<ProductViewProps> = ({ product, relatedProducts }) => {
|
||||
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 (
|
||||
<>
|
||||
<Container className="max-w-none w-full" clean>
|
||||
@@ -37,6 +53,7 @@ const ProductView: FC<ProductViewProps> = ({ product, relatedProducts }) => {
|
||||
{product.images.map((image, i) => (
|
||||
<div key={image.url} className={s.imageContainer}>
|
||||
<Image
|
||||
id={'product-image-' + i}
|
||||
className={s.img}
|
||||
src={image.url!}
|
||||
alt={image.alt || 'Product Image'}
|
||||
@@ -44,10 +61,28 @@ const ProductView: FC<ProductViewProps> = ({ product, relatedProducts }) => {
|
||||
height={600}
|
||||
priority={i === 0}
|
||||
quality="85"
|
||||
style={{ cursor: 'pointer' }}
|
||||
onClick={() => setLightboxOpen(true)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
{model3dPath != undefined ? (
|
||||
<div key={'model3d'} className={s.imageContainer}>
|
||||
<ProductModel modelPath={model3dPath}></ProductModel>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</ProductSlider>
|
||||
<Lightbox
|
||||
open={isLightboxOpen}
|
||||
close={() => setLightboxOpen(false)}
|
||||
slides={product.images.map((image) => {
|
||||
return {
|
||||
src: image.url,
|
||||
}
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
{process.env.COMMERCE_WISHLIST_ENABLED && (
|
||||
<WishlistButton
|
||||
|
@@ -56,7 +56,8 @@
|
||||
"react-use-measure": "^2.1.1",
|
||||
"screenfull": "^6.0.2",
|
||||
"tabbable": "^5.2.1",
|
||||
"tailwindcss": "^3.0.13"
|
||||
"tailwindcss": "^3.0.13",
|
||||
"yet-another-react-lightbox": "^2.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@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 decadesManifest from '../../../static_data/decadesManifest.json'
|
||||
import productDetailsMetafields from '../../../static_data/productDetailsMetafields.json'
|
||||
import {
|
||||
MarkerData,
|
||||
MarkerJson,
|
||||
@@ -123,7 +124,14 @@ export async function getStaticProps({
|
||||
continue
|
||||
|
||||
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,
|
||||
preview,
|
||||
})
|
||||
@@ -132,8 +140,6 @@ export async function getStaticProps({
|
||||
}
|
||||
}
|
||||
|
||||
console.log(products)
|
||||
|
||||
if (!products) {
|
||||
throw new Error(`Products associated with markers not found`)
|
||||
}
|
||||
|
@@ -10,14 +10,16 @@ import { useRouter } from 'next/router'
|
||||
import { Layout } from '@components/common'
|
||||
import { ProductView } from '@components/product'
|
||||
|
||||
// Used by the Shopify Example
|
||||
const withMetafields = [
|
||||
{ namespace: 'reviews', key: 'rating' },
|
||||
{ namespace: 'reviews', key: 'count' },
|
||||
{ namespace: 'my_fields', key: 'width' },
|
||||
{ namespace: 'my_fields', key: 'weight' },
|
||||
{ namespace: 'my_fields', key: 'length' },
|
||||
]
|
||||
import productDetailsMetafields from '../../static_data/productDetailsMetafields.json'
|
||||
|
||||
const withMetafields = productDetailsMetafields.metafields[0].names.map(
|
||||
(metafield: any) => {
|
||||
return {
|
||||
namespace: metafield.namespace,
|
||||
key: metafield.key,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
export async function getStaticProps({
|
||||
params,
|
||||
@@ -28,6 +30,9 @@ export async function getStaticProps({
|
||||
const config = { locale, locales }
|
||||
const pagesPromise = commerce.getAllPages({ config, preview })
|
||||
const siteInfoPromise = commerce.getSiteInfo({ config, preview })
|
||||
|
||||
console.log(withMetafields)
|
||||
|
||||
const productPromise = commerce.getProduct({
|
||||
variables: {
|
||||
slug: params!.slug,
|
||||
@@ -47,6 +52,8 @@ export async function getStaticProps({
|
||||
const { product } = await productPromise
|
||||
const { products: relatedProducts } = await allProductsPromise
|
||||
|
||||
console.log(product)
|
||||
|
||||
if (!product) {
|
||||
return {
|
||||
notFound: true,
|
||||
|
@@ -1,57 +1,69 @@
|
||||
{
|
||||
"metafields": [
|
||||
"metafields": [
|
||||
{
|
||||
"locale": "it",
|
||||
"names": [
|
||||
{
|
||||
"locale": "it",
|
||||
"names": [
|
||||
{
|
||||
"name": "Tipo",
|
||||
"key": "tipo"
|
||||
},
|
||||
{
|
||||
"name": "Materiale",
|
||||
"key": "materiale"
|
||||
},
|
||||
{
|
||||
"name": "Colore",
|
||||
"key": "colore"
|
||||
},
|
||||
{
|
||||
"name": "Condizioni Estetiche",
|
||||
"key": "condizioni_estetiche"
|
||||
},
|
||||
{
|
||||
"name": "Altezza",
|
||||
"key": "altezza"
|
||||
},
|
||||
{
|
||||
"name": "Spessore",
|
||||
"key": "spessore"
|
||||
},
|
||||
{
|
||||
"name": "Larghezza",
|
||||
"key": "larghezza"
|
||||
},
|
||||
{
|
||||
"name": "Peso",
|
||||
"key": "peso"
|
||||
},
|
||||
{
|
||||
"name": "Confezione e Accessori Originali",
|
||||
"key": "confezione_accessori"
|
||||
},
|
||||
{
|
||||
"name": "Descrizione",
|
||||
"key": "descrizione"
|
||||
},
|
||||
{
|
||||
"name": "Vintage",
|
||||
"key": "vintage"
|
||||
},
|
||||
{
|
||||
"name": "Made In",
|
||||
"key": "made_in"
|
||||
}
|
||||
]
|
||||
"name": "Tipo",
|
||||
"key": "tipo",
|
||||
"namespace": "custom"
|
||||
},
|
||||
{
|
||||
"name": "Materiale",
|
||||
"key": "materiale",
|
||||
"namespace": "custom"
|
||||
},
|
||||
{
|
||||
"name": "Colore",
|
||||
"key": "colore",
|
||||
"namespace": "custom"
|
||||
},
|
||||
{
|
||||
"name": "Condizioni Estetiche",
|
||||
"key": "condizioni_estetiche",
|
||||
"namespace": "custom"
|
||||
},
|
||||
{
|
||||
"name": "Altezza",
|
||||
"key": "altezza",
|
||||
"namespace": "custom"
|
||||
},
|
||||
{
|
||||
"name": "Spessore",
|
||||
"key": "spessore",
|
||||
"namespace": "custom"
|
||||
},
|
||||
{
|
||||
"name": "Larghezza",
|
||||
"key": "larghezza",
|
||||
"namespace": "custom"
|
||||
},
|
||||
{
|
||||
"name": "Peso",
|
||||
"key": "peso",
|
||||
"namespace": "custom"
|
||||
},
|
||||
{
|
||||
"name": "Confezione e Accessori Originali",
|
||||
"key": "confezione_accessori",
|
||||
"namespace": "custom"
|
||||
},
|
||||
{
|
||||
"name": "Descrizione",
|
||||
"key": "descrizione",
|
||||
"namespace": "custom"
|
||||
},
|
||||
{
|
||||
"name": "Vintage",
|
||||
"key": "vintage",
|
||||
"namespace": "custom"
|
||||
},
|
||||
{
|
||||
"name": "Made In",
|
||||
"key": "made_in",
|
||||
"namespace": "custom"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Reference in New Issue
Block a user