From e5ebf60e8377299beeec577fb845e1340d320d5e Mon Sep 17 00:00:00 2001 From: Daniele Pancottini Date: Tue, 20 Dec 2022 19:15:40 +0100 Subject: [PATCH] Continue Migration, almost done --- packages/commerce/src/types/product.ts | 27 +++- packages/shopify/src/utils/normalize.ts | 19 +++ .../src/utils/queries/get-product-query.ts | 1 + pnpm-lock.yaml | 43 ++++--- .../ProductCardRoom/ProductCardRoom.tsx | 115 +++++++++-------- .../product/ProductSidebar/ProductSidebar.tsx | 86 +++++-------- .../product/ProductView/ProductView.tsx | 37 +++++- site/package.json | 3 +- site/pages/[region]/[decade]/[start].tsx | 12 +- site/pages/product/[slug].tsx | 23 ++-- .../static_data/productDetailsMetafields.json | 120 ++++++++++-------- 11 files changed, 290 insertions(+), 196 deletions(-) diff --git a/packages/commerce/src/types/product.ts b/packages/commerce/src/types/product.ts index 76942216c..8b89f5b05 100644 --- a/packages/commerce/src/types/product.ts +++ b/packages/commerce/src/types/product.ts @@ -160,7 +160,6 @@ 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. @@ -170,6 +169,10 @@ export interface Product { * The product’s vendor name. */ vendor?: string + /** + * List of product's media + */ + media: ProductMedia[] } export interface SearchProductsBody { @@ -266,3 +269,25 @@ export type GetProductOperation = { 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[] +} diff --git a/packages/shopify/src/utils/normalize.ts b/packages/shopify/src/utils/normalize.ts index 710ba7423..8249e9722 100644 --- a/packages/shopify/src/utils/normalize.ts +++ b/packages/shopify/src/utils/normalize.ts @@ -19,6 +19,8 @@ import type { Collection, Maybe, Metafield as ShopifyMetafield, + MediaConnection, + Model3d, } from '../../schema' import { colorMap } from './colors' @@ -112,6 +114,7 @@ export function normalizeProduct( priceRange, options, metafields, + media, ...rest }: ShopifyProduct, locale?: string @@ -131,6 +134,7 @@ export function normalizeProduct( .map((o) => normalizeProductOption(o)) : [], metafields: normalizeMetafields(metafields, locale), + media: media ? normalizeProductMedia(media) : [], description: description || '', ...(descriptionHtml && { descriptionHtml }), ...rest, @@ -256,3 +260,18 @@ export const normalizeMetafieldValue = ( } 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, + } + }), + } + }) +} diff --git a/packages/shopify/src/utils/queries/get-product-query.ts b/packages/shopify/src/utils/queries/get-product-query.ts index 169846ee8..66e45e47b 100644 --- a/packages/shopify/src/utils/queries/get-product-query.ts +++ b/packages/shopify/src/utils/queries/get-product-query.ts @@ -83,6 +83,7 @@ const getProductQuery = /* GraphQL */ ` } } } + } metafields(identifiers: $withMetafields) { key value diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2297bbe7c..16d99d9dd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -657,6 +657,7 @@ importers: tabbable: ^5.2.1 tailwindcss: ^3.0.13 typescript: 4.7.4 + yet-another-react-lightbox: ^2.2.3 dependencies: '@chakra-ui/icons': 2.0.14_react@18.2.0 '@chakra-ui/react': 2.4.4_4krdlvlqq3ittuocexwl336v2q @@ -702,6 +703,7 @@ importers: screenfull: 6.0.2 tabbable: 5.3.3 tailwindcss: 3.1.8_postcss@8.4.16 + yet-another-react-lightbox: 2.2.3_biqbaboplfbrettd7655fr4n2y devDependencies: '@next/bundle-analyzer': 12.3.0 '@types/body-scroll-lock': 3.1.0 @@ -3497,7 +3499,18 @@ packages: - supports-color 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: resolution: {integrity: sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==} dependencies: @@ -3544,19 +3557,6 @@ packages: hey-listen: 1.0.8 tslib: 2.4.0 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: resolution: {integrity: sha512-hzRLHIrtwOiGEku9rmG7qZk+OQhnqQOL+ycl2XrjBaztBN/xaqnjoG4+HEf9L7ELN943BR+K/ZlaF2OEgbGm+Q==} @@ -7117,7 +7117,6 @@ packages: tslib: 2.4.0 dev: true -<<<<<<< HEAD /hey-listen/1.0.8: resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==} dev: false @@ -7127,11 +7126,10 @@ packages: dependencies: react-is: 16.13.1 dev: false -======= + /hosted-git-info/2.8.9: resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} dev: true ->>>>>>> 1ba9d3bd6e79da1f0b05df2f1c0c1ca3632b6c16 /http-cache-semantics/4.1.0: resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==} @@ -11415,6 +11413,17 @@ packages: yargs-parser: 21.1.1 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: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} diff --git a/site/components/product/ProductCardRoom/ProductCardRoom.tsx b/site/components/product/ProductCardRoom/ProductCardRoom.tsx index 181e8c4a8..58f5beb58 100644 --- a/site/components/product/ProductCardRoom/ProductCardRoom.tsx +++ b/site/components/product/ProductCardRoom/ProductCardRoom.tsx @@ -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 ( - + - + className={style.cardBody} + > {`Picture {`Picture - - - - - - + + + + + + - - - + + - - {props.product.name} - + + {props.product.name} + - {historicDescription.pop()?.split('\n').map((line, index) => ( - + {historicDescription.split('\n').map((line, index) => ( + {line} ))} @@ -93,8 +99,7 @@ export default function ProductCardRoom(props: { - - ); -} \ No newline at end of file + ) +} diff --git a/site/components/product/ProductSidebar/ProductSidebar.tsx b/site/components/product/ProductSidebar/ProductSidebar.tsx index 3165ce3a7..55c659c1d 100644 --- a/site/components/product/ProductSidebar/ProductSidebar.tsx +++ b/site/components/product/ProductSidebar/ProductSidebar.tsx @@ -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 = ({ product, className }) => { const addItem = useAddItem() - const { openSidebar, setSidebarView } = useUI() + const { openSidebar } = useUI() const [loading, setLoading] = useState(false) - const [error, setError] = useState(null) const [selectedOptions, setSelectedOptions] = useState({}) useEffect(() => { @@ -32,27 +31,20 @@ const ProductSidebar: FC = ({ 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 (
= ({ product, className }) => { selectedOptions={selectedOptions} setSelectedOptions={setSelectedOptions} /> - - {product.metafields?.reviews?.rating && ( -
- -
- {product.metafields.reviews.count?.value ?? 0} reviews -
-
- )} + {/* Product Description With Metafields */} -
- {error && } + + + {productDetailsMetafields.metafields[0].names.map((meta) => ( + + + {meta.name}:{' '} + + + {product.metafields.custom + .filter((o) => o.key == meta.key) + .map((o) => o.value)} + + + ))} + + + +
{process.env.COMMERCE_CART_ENABLED && ( )}
-
- - This is a limited edition production run. Printing starts when the - drop ends. - - - - 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. - - - {product.customFields && product.customFields?.length > 0 && ( - - - - )} - - {product.metafields?.my_fields && ( - - - - )} -
) } diff --git a/site/components/product/ProductView/ProductView.tsx b/site/components/product/ProductView/ProductView.tsx index 31cbcd577..af5501197 100644 --- a/site/components/product/ProductView/ProductView.tsx +++ b/site/components/product/ProductView/ProductView.tsx @@ -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 = ({ 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 ( <> @@ -37,6 +53,7 @@ const ProductView: FC = ({ product, relatedProducts }) => { {product.images.map((image, i) => (
{image.alt = ({ product, relatedProducts }) => { height={600} priority={i === 0} quality="85" + style={{ cursor: 'pointer' }} + onClick={() => setLightboxOpen(true)} />
))} + {model3dPath != undefined ? ( +
+ +
+ ) : ( + <> + )} + setLightboxOpen(false)} + slides={product.images.map((image) => { + return { + src: image.url, + } + })} + />
{process.env.COMMERCE_WISHLIST_ENABLED && ( { + 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, diff --git a/site/static_data/productDetailsMetafields.json b/site/static_data/productDetailsMetafields.json index 35b89378f..264e1b3d3 100644 --- a/site/static_data/productDetailsMetafields.json +++ b/site/static_data/productDetailsMetafields.json @@ -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" } - ] -} \ No newline at end of file + ] + } + ] +}