From 2f19ee91d2cd261f3ace26e6fd767141d46c79d3 Mon Sep 17 00:00:00 2001 From: RamJogal Date: Tue, 2 Apr 2024 10:05:10 +0530 Subject: [PATCH 1/2] Product Detail page UI Change --- app/routes/($locale)._index.tsx | 64 +- app/routes/($locale).collections.$handle.tsx | 52 +- app/routes/($locale).collections.all.tsx | 102 ++- app/routes/($locale).products.$handle.tsx | 841 ++++++++++++------- app/styles/app.css | 1 + app/styles/pages.css | 225 ++++- app/styles/reset.css | 2 +- package-lock.json | 60 +- public/breadcrumb.svg | 3 + public/cart.svg | 4 + 10 files changed, 899 insertions(+), 455 deletions(-) create mode 100644 public/breadcrumb.svg create mode 100644 public/cart.svg diff --git a/app/routes/($locale)._index.tsx b/app/routes/($locale)._index.tsx index 384b54b..adf83d4 100644 --- a/app/routes/($locale)._index.tsx +++ b/app/routes/($locale)._index.tsx @@ -60,9 +60,6 @@ export async function loader({context, request}: LoaderFunctionArgs) { export default function Homepage() { const data = useLoaderData(); - // const categoriesWithBestSellers = filterCategoriesWithBestSellers( - // data?.topcategories?.collections, - // ); return (
@@ -167,52 +164,23 @@ function RecommendedProducts({ '' )}
- {cmsData?.banner?.button.repo.cta_title.title ? ( - - {cmsData.banner?.button.repo.cta_title.title} - - ) : ( - '' - )} - {cmsData.banner?.button.products.cta_title.title ? ( - - {cmsData.banner?.button.products.cta_title.title} - { + return button.cta_title.title ? ( + - - - - ) : ( - '' - )} + {button.cta_title.title} + + ) : ( + '' + ); + })}
diff --git a/app/routes/($locale).collections.$handle.tsx b/app/routes/($locale).collections.$handle.tsx index 29e6e07..7d3d42b 100644 --- a/app/routes/($locale).collections.$handle.tsx +++ b/app/routes/($locale).collections.$handle.tsx @@ -39,24 +39,39 @@ export async function loader({request, params, context}: LoaderFunctionArgs) { export default function Collection() { const {collection} = useLoaderData(); return ( -
-

{collection.title}

-

{collection.description}

- - {({nodes, isLoading, PreviousLink, NextLink}) => ( - <> - - {isLoading ? 'Loading...' : ↑ Load previous} - - -
- - {isLoading ? 'Loading...' : Load more ↓} - - - )} -
-
+ <> +
+ +
+
+

{collection.title}

+

{collection.description}

+ + {({nodes, isLoading, PreviousLink, NextLink}) => ( + <> + + {isLoading ? 'Loading...' : ↑ Load previous} + + +
+ + {isLoading ? 'Loading...' : Load more ↓} + + + )} +
+
+ ); } @@ -91,6 +106,7 @@ function ProductItem({ key={product.id} prefetch="intent" to={variantUrl} + state={{previousTabUrl: '/variantUrl'}} > {product.featuredImage && ( - Loading...}> -
-
-

{cmsData.product_title}

+ <> +
+ +
+
+ Loading...
}> +
+
+

{cmsData.product_title}

+
+
+ {products?.map((product: any) => { + return ( + + + {product.images.nodes[0] ? ( + + ) : ( + // eslint-disable-next-line jsx-a11y/img-redundant-alt + No Image + )} +

{product.title}

+ + {product.title} + + + + + +
+ ); + })} +
-
- {products?.map((product: any) => { - return ( - - - {product.images.nodes[0] ? ( - - ) : ( - // eslint-disable-next-line jsx-a11y/img-redundant-alt - No Image - )} -

{product.title}

- {product.title} - - - - -
- ); - })} -
-
- + -
-
+
+ + ); } diff --git a/app/routes/($locale).products.$handle.tsx b/app/routes/($locale).products.$handle.tsx index 8847d47..626754f 100644 --- a/app/routes/($locale).products.$handle.tsx +++ b/app/routes/($locale).products.$handle.tsx @@ -1,6 +1,8 @@ -import {Fragment, Suspense} from 'react'; +import {Fragment, Suspense, useState} from 'react'; import {defer, redirect, type LoaderFunctionArgs} from '@shopify/remix-oxygen'; + import { + useLocation, Await, Link, useLoaderData, @@ -36,7 +38,6 @@ export const meta: MetaFunction = ({data}) => { export async function loader({params, request, context}: LoaderFunctionArgs) { const {handle} = params; const {storefront} = context; - const selectedOptions = getSelectedProductOptions(request).filter( (option) => // Filter out Shopify predictive search query params @@ -143,11 +144,47 @@ function redirectToFirstVariant({ } export default function Product() { + const location = useLocation(); + const {state} = location; const {product, variants, relatedProductQueryResults, fetchedData} = useLoaderData(); const {selectedVariant} = product; + const collectionName = product?.collections?.edges?.[0]?.node?.title; + const productName = product?.title; + console.info('collectionName', collectionName, 'productName', productName); + + const limitedProducts = + relatedProductQueryResults?.productRecommendations?.slice(0, 5); + return ( <> +
+ +
{fetchedData.heading}
- {relatedProductQueryResults?.productRecommendations.map( - (product: any) => { - return ( - - - {product.images.nodes[0] ? ( - - ) : ( - // eslint-disable-next-line jsx-a11y/img-redundant-alt - No Image - )} -

{product.title}

- - {product.title} - - <> - - - -
- ); - }, - )} + {limitedProducts?.length + ? limitedProducts?.map((product: any) => { + return ( + + + {product.images.nodes[0] ? ( + + ) : ( + // eslint-disable-next-line jsx-a11y/img-redundant-alt + No Image + )} +

{product.title}

+ + {product.title} + + <> + + + +
+ ); + }) + : ''}
@@ -238,7 +275,7 @@ function ProductMain({ }) { const {title, descriptionHtml, metafield} = product; const valueMap = new Map(); - metafield.reference.fields.forEach((field: any) => { + metafield?.reference?.fields?.forEach((field: any) => { valueMap.set(field.key, field.value); }); function generateStars(rating: any) { @@ -250,70 +287,145 @@ function ProductMain({ const rating = '4.4'; const stars = generateStars(rating); + const collectionName = product?.collections?.edges?.[0]?.node?.title; return ( -
-

{title}

-
-
- -
- - } - > - - {(data) => ( + <> +
+

{title}

+
+
+ +
+ - )} - - + } + > + + {(data) => ( + + )} + + -
-
-
-

- Reviews -

-
- {/* */} -
-
-

- Shipping and Return -

-
-
-
-
+
+
+
+

+ Reviews +

+
+ {/* */} +
+
+

+ Shipping and Return +

+
+
+
+
+ + + +
+
+
+ + + + + +
+ +
+ Free Delivery + Enter your postal code +
+ +
+ + + + +
+ +
+ Return Policy + + Free 30 Days Delivery Returns. Details + +
+
+
+ ); } @@ -322,27 +434,72 @@ function ProductPrice({ }: { selectedVariant: ProductFragment['selectedVariant']; }) { + const originalPrice = parseFloat(selectedVariant?.compareAtPrice?.amount); + const discountedPrice = parseFloat(selectedVariant.price?.amount); + let priceOff; + + if (originalPrice && discountedPrice && discountedPrice < originalPrice) { + priceOff = originalPrice - discountedPrice; + } return ( -
- {selectedVariant?.compareAtPrice ? ( - <> -
-
- {selectedVariant ? ( - - ) : null} - - - -
- - ) : ( - selectedVariant?.price && - )} -
+ <> +
+ {selectedVariant?.compareAtPrice ? ( + <> +
+
+ {selectedVariant ? ( + <> + + + ) : null} + + + + {priceOff ? ( +

({priceOff} OFF)

+ ) : ( + '' + )} +
+ + + +
+ + IN STOCK +
+ + ) : ( + selectedVariant?.price && + )} +
+
+ + + +
+ ); } @@ -392,33 +549,53 @@ function ProductForm({ function ProductOptions({option}: {option: VariantOption}) { return ( -
-
{option.name}
-
- {option.values.map(({value, isAvailable, isActive, to}) => { - return ( - - {value} - - ); - })} + <> +
+
{option.name}
+
+ {option.values.map(({value, isAvailable, isActive, to}) => { + return ( + + {value} + + ); + })} +
+
-
-
+
+ + + +
+ ); } - function AddToCartButton({ analytics, children, @@ -432,191 +609,257 @@ function AddToCartButton({ lines: CartLineInput[]; onClick?: () => void; }) { + const [quantity, setQuantity] = useState(1); + const handleIncrement = () => { + setQuantity(quantity + 1); + }; + const handleDecrement = () => { + if (quantity > 1) { + setQuantity(quantity - 1); + } + }; return ( - - {(fetcher: FetcherWithComponents) => ( - <> - - - {children} - - - - - - - )} - + <> +
+ + {(fetcher: FetcherWithComponents) => ( + <> + +
+ + {quantity} + +
+ + {children} + + + )} +
+
+
+ + + +
+ ); } const PRODUCT_VARIANT_FRAGMENT = `#graphql - fragment ProductVariant on ProductVariant { - availableForSale - compareAtPrice { - amount - currencyCode - } - id - image { - __typename + fragment ProductVariant on ProductVariant { + availableForSale + compareAtPrice { + amount + currencyCode + } id - url - altText - width - height - } - price { - amount - currencyCode - } - product { + image { + __typename + id + url + altText + width + height + } + price { + amount + currencyCode + } + product { + title + handle + } + selectedOptions { + name + value + } + sku title - handle - } - selectedOptions { - name - value - } - sku - title - unitPrice { - amount - currencyCode + unitPrice { + amount + currencyCode + } } - } -` as const; + ` as const; const PRODUCT_FRAGMENT = `#graphql - fragment Product on Product { - id - title - vendor - handle - descriptionHtml - description - options { - name - values - } - selectedVariant: variantBySelectedOptions(selectedOptions: $selectedOptions) { - ...ProductVariant - } - variants(first: 1) { - nodes { + fragment Product on Product { + id + title + vendor + handle + descriptionHtml + description + options { + name + values + } + selectedVariant: variantBySelectedOptions(selectedOptions: $selectedOptions) { ...ProductVariant } + variants(first: 1) { + nodes { + ...ProductVariant + } + } + seo { + description + title + } } - seo { - description - title - } - } - ${PRODUCT_VARIANT_FRAGMENT} -` as const; + ${PRODUCT_VARIANT_FRAGMENT} + ` as const; const PRODUCT_QUERY = `#graphql - query Product( - $country: CountryCode - $handle: String! - $language: LanguageCode - $selectedOptions: [SelectedOptionInput!]! - ) @inContext(country: $country, language: $language) { - product(handle: $handle) { - ...Product - metafield(namespace: "custom", key: "information") { - key - value - reference { - ... on Metaobject { - id - fields { - key - value - } - } + query Product( + $country: CountryCode + $handle: String! + $language: LanguageCode + $selectedOptions: [SelectedOptionInput!]! + ) @inContext(country: $country, language: $language) { + product(handle: $handle) { + ...Product + collections(first: 1) { + edges { + node { + id + title + } + } + } + metafield(namespace: "custom", key: "information") { + key + value + reference { + ... on Metaobject { + id + fields { + key + value } + } + } } + } } - } - ${PRODUCT_FRAGMENT} -` as const; + ${PRODUCT_FRAGMENT} + ` as const; const PRODUCT_VARIANTS_FRAGMENT = `#graphql - fragment ProductVariants on Product { - variants(first: 250) { - nodes { - ...ProductVariant + fragment ProductVariants on Product { + variants(first: 250) { + nodes { + ...ProductVariant + } } } - } - ${PRODUCT_VARIANT_FRAGMENT} -` as const; + ${PRODUCT_VARIANT_FRAGMENT} + ` as const; const VARIANTS_QUERY = `#graphql - ${PRODUCT_VARIANTS_FRAGMENT} - query ProductVariants( - $country: CountryCode - $language: LanguageCode - $handle: String! - ) @inContext(country: $country, language: $language) { - product(handle: $handle) { - ...ProductVariants + ${PRODUCT_VARIANTS_FRAGMENT} + query ProductVariants( + $country: CountryCode + $language: LanguageCode + $handle: String! + ) @inContext(country: $country, language: $language) { + product(handle: $handle) { + ...ProductVariants + } } - } -` as const; + ` as const; const RELATED_PRODUCT_QUERY = `#graphql - query productRecommendations( - $country: CountryCode - $language: LanguageCode - $productID: ID! - ) @inContext(country: $country, language: $language) { - productRecommendations(productId: $productID, intent: RELATED) { - id, - title - priceRange { - minVariantPrice { - amount - currencyCode - } - maxVariantPrice { - amount - currencyCode + query productRecommendations( + $country: CountryCode + $language: LanguageCode + $productID: ID! + ) @inContext(country: $country, language: $language) { + productRecommendations(productId: $productID, intent: RELATED) { + id, + title + priceRange { + minVariantPrice { + amount + currencyCode + } + maxVariantPrice { + amount + currencyCode + } } - } - images(first:1){ - nodes{ - id - url - altText - width - height + images(first:1){ + nodes{ + id + url + altText + width + height + } } } } - } -` as const; + ` as const; diff --git a/app/styles/app.css b/app/styles/app.css index 62b46e3..af208ad 100644 --- a/app/styles/app.css +++ b/app/styles/app.css @@ -491,6 +491,7 @@ aside li { font-size: 40px; font-weight: 400; line-height: 60px; + margin-bottom: 0px; } .product { .description { diff --git a/app/styles/pages.css b/app/styles/pages.css index cb9f541..8b554a6 100644 --- a/app/styles/pages.css +++ b/app/styles/pages.css @@ -34,7 +34,7 @@ img { margin-left: 32px; } -.pg-t{ +.pg-t { padding-top: 32px !important; } @@ -69,7 +69,7 @@ img { flex-direction: column; align-items: flex-start; height: 800px; - background-image: url(https://images.contentstack.io/v3/assets/blt8b7008208fac8f80/blt134799bb22224b27/660510c86c4a397e5ce445e0/banner.svg); + background-image: url(https://images.contentstack.io/v3/assets/blt8b7008208fac8f80/blt727d9ff3d30f4ec9/65fc065a039fdd149b3399e7/Limited_Offer.png); position: relative; background-repeat: no-repeat; background-size: cover; @@ -127,7 +127,7 @@ img { font: inherit; font-size: 16px; text-decoration: none !important; - /* padding: 16px; */ + padding: 16px; color: var(--Colors-Text_white, #fff); font-weight: 600; line-height: 150%; /* 24px */ @@ -138,9 +138,7 @@ img { -webkit-appearance: none; appearance: none; min-height: 56px; - min-width: 219px; background-color: #212121; - margin: 10px; &:hover { border: 2px solid white; @@ -392,8 +390,8 @@ img { .feature-product { margin: 10px; img { - /* height: auto; */ - height: 440px; + /* height: auto; */ + height: 440px; } } /* about page */ @@ -511,7 +509,7 @@ img { .top-cat-img { height: auto !important; - margin-bottom: 32px ; + margin-bottom: 32px; } .col-left-best-seller { @@ -530,11 +528,11 @@ img { .best-sell-img { position: relative; } -.best-sell-cta1{ +.best-sell-cta1 { background-color: white; text-decoration: none !important; } -.best-sell-cta-heading{ +.best-sell-cta-heading { color: white; font-weight: 700; font-size: 48px; @@ -544,8 +542,8 @@ img { .best-sell-cta { position: absolute; - bottom: 13%; - left: 95px; + bottom: 13%; + left: 95px; } .login { @@ -567,11 +565,208 @@ img { .login-email { padding: 16px 70px !important; border-radius: 4px; - border: 1px solid #E6E6E6; - color: #E6E6E6; + border: 1px solid #e6e6e6; + color: #e6e6e6; ::placeholder { color: red; /* opacity: 1; Firefox */ } -} \ No newline at end of file +} + +.breadcrumbs { + margin-bottom: 10px; + max-width: 1400px; + min-width: 1487px; + margin-right: auto; + margin-left: auto; + margin-top: 30px; +} + +.breadcrumbs ul { + list-style-type: none; + padding: 0; + margin: 0; +} + +.breadcrumbs li { + display: inline; +} + +.breadcrumbs li:not(:last-child):after { + content: url('../../public/breadcrumb.svg'); + margin: 0 5px; + vertical-align: middle; + line-height: 1em; +} + +.breadcrumbs li:not(:last-child) a:after { + margin: 0 5px; + vertical-align: middle; + line-height: 1em; +} + +.breadcrumbs li a { + text-decoration: none; + color: #333; + font-family: Inter; + font-size: 16px; + font-style: normal; + font-weight: 400; + + line-height: 150%; +} + +.breadcrumbs li a:hover { + text-decoration: underline; +} +.referencePrice { + color: #212121 !important; + text-align: center; + font-family: Inter; + font-size: 14px !important; + font-style: normal; + font-weight: 600; + line-height: 150%; + letter-spacing: 0.12px; + text-transform: capitalize; + margin-left: 10px; +} + +.product-price-on-saleRef { + color: #212121; + text-align: center; + font-family: Inter; + font-size: 24px; + font-style: normal; + font-weight: 600; + line-height: 150%; + text-transform: capitalize; +} +.percentageOffPrice { + color: var(--Colors-Text_Primary, #212121); + text-align: center; + + font-family: Inter; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 150%; /* 18px */ + letter-spacing: 0.12px; + text-transform: capitalize; + margin-left: px; +} + +.banner_repo_cta .svg-icon { + position: absolute; + left: 10px; + z-index: 1; +} +.add-to-cart-container form { + display: flex !important; +} + +.addtocartquantiy { + top: -1px; + left: -1px; + width: 152px; + height: 56px; + -ms-border-radius: 8px; + -o-border-radius: 8px; + -moz-border-radius: 8px; + -webkit-border-radius: 8px; + border-radius: 8px; + border: 1px solid #212121; + background: rgba(255, 255, 255, 0); + margin-right: 20px; +} + +.increment-button { + border: none; + background: transparent; + margin-left: 20px; + margin-top: 15px; +} +.decrement-button { + border: none; + background: transparent; + margin-left: 10px; + margin-top: 15px; +} + +.totalquantiy { + vertical-align: super; + display: inline-block; + margin-left: 10px; +} +.increment-button:hover { + cursor: pointer; +} +.decrement-button:hover { + cursor: pointer; +} + +.shipping-container { + display: flex; + align-items: center; + margin-top: 20px; +} + +.postalcode-container { + margin-right: 10px; +} + +.details-container { + flex-grow: 1; + display: flex; + flex-direction: column; +} + +.shipping-container p { + margin: 0; +} + +.Free-Delivery { + color: #212121; + font-family: Inter; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 150%; + letter-spacing: 0.16px; +} +.Postal-code { + color: #212121; + font-family: Inter; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 150%; /* 18px */ + letter-spacing: 0.12px; + text-decoration-line: underline; + text-transform: capitalize; + margin-top: 4px; +} + +.instock { + color: var(--Colors-Success, #42cc71); + text-align: center; + + font-family: Inter; + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 150%; + letter-spacing: 0.16px; + margin-left: 5px; +} + +.verticalseprator { + margin-left: 5px; +} + +.banner_repo_cta_icon::before { + content: url('../../public/cart.svg'); + margin-right: 5px; + margin-top: 2px; +} diff --git a/app/styles/reset.css b/app/styles/reset.css index 5c1c084..120b37d 100644 --- a/app/styles/reset.css +++ b/app/styles/reset.css @@ -126,4 +126,4 @@ code { border-radius: 4px; font-family: monospace; padding: 0.25rem; -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 93d60f0..a8bbe2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4681,6 +4681,36 @@ } } }, + "node_modules/@remix-run/react/node_modules/react-router": { + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.17.0.tgz", + "integrity": "sha512-YJR3OTJzi3zhqeJYADHANCGPUu9J+6fT5GLv82UWRGSxu6oJYCKVmxUcaBQuGm9udpWmPsvpme/CdHumqgsoaA==", + "dependencies": { + "@remix-run/router": "1.10.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/@remix-run/react/node_modules/react-router-dom": { + "version": "6.17.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.17.0.tgz", + "integrity": "sha512-qWHkkbXQX+6li0COUUPKAUkxjNNqPJuiBd27dVwQGDNsuFBdMbrS6UZ0CLYc4CsbdLYTckn4oB4tGDuPZpPhaQ==", + "dependencies": { + "@remix-run/router": "1.10.0", + "react-router": "6.17.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/@remix-run/router": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.10.0.tgz", @@ -19250,36 +19280,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-router": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.17.0.tgz", - "integrity": "sha512-YJR3OTJzi3zhqeJYADHANCGPUu9J+6fT5GLv82UWRGSxu6oJYCKVmxUcaBQuGm9udpWmPsvpme/CdHumqgsoaA==", - "dependencies": { - "@remix-run/router": "1.10.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/react-router-dom": { - "version": "6.17.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.17.0.tgz", - "integrity": "sha512-qWHkkbXQX+6li0COUUPKAUkxjNNqPJuiBd27dVwQGDNsuFBdMbrS6UZ0CLYc4CsbdLYTckn4oB4tGDuPZpPhaQ==", - "dependencies": { - "@remix-run/router": "1.10.0", - "react-router": "6.17.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", diff --git a/public/breadcrumb.svg b/public/breadcrumb.svg new file mode 100644 index 0000000..8ada504 --- /dev/null +++ b/public/breadcrumb.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/public/cart.svg b/public/cart.svg new file mode 100644 index 0000000..c72481c --- /dev/null +++ b/public/cart.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file From 0f35a5c56896942ff24e6e9e8afacf4c8e11188b Mon Sep 17 00:00:00 2001 From: RamJogal Date: Tue, 2 Apr 2024 17:26:54 +0530 Subject: [PATCH 2/2] price change --- app/routes/($locale)._index.tsx | 30 ++- app/routes/($locale).collections.$handle.tsx | 18 +- app/routes/($locale).collections.all.tsx | 22 +- app/routes/($locale).products.$handle.tsx | 231 ++++++++++++------- 4 files changed, 189 insertions(+), 112 deletions(-) diff --git a/app/routes/($locale)._index.tsx b/app/routes/($locale)._index.tsx index 4b720d8..8a99acc 100644 --- a/app/routes/($locale)._index.tsx +++ b/app/routes/($locale)._index.tsx @@ -170,7 +170,7 @@ function RecommendedProducts({ target={button?.open_in_new_tab ? '_blank' : '_self'} className="banner-repo-cta" style={{ - margin:'10px' + margin: '10px', }} > {button?.cta_title?.title} @@ -253,16 +253,24 @@ function RecommendedProducts({ } /> ) : null} - - - - {priceOff ? ( + {product?.priceRange?.minVariantPrice + ?.amount < + product?.compareAtPriceRange?.minVariantPrice + ?.amount ? ( + + + + ) : ( + '' + )} + + {priceOff > 0 ? (

(${priceOff.toFixed(2)} OFF)

diff --git a/app/routes/($locale).collections.$handle.tsx b/app/routes/($locale).collections.$handle.tsx index 4149dc6..f3ce48c 100644 --- a/app/routes/($locale).collections.$handle.tsx +++ b/app/routes/($locale).collections.$handle.tsx @@ -146,12 +146,18 @@ function ProductItem({ data={product?.priceRange?.minVariantPrice} /> ) : null} - - - + {product?.priceRange?.minVariantPrice?.amount < + product?.compareAtPriceRange?.minVariantPrice?.amount ? ( + + + + ) : ( + '' + )} + {priceOff ? (

(${priceOff.toFixed(2)} OFF)

) : ( diff --git a/app/routes/($locale).collections.all.tsx b/app/routes/($locale).collections.all.tsx index cc9c820..eb20742 100644 --- a/app/routes/($locale).collections.all.tsx +++ b/app/routes/($locale).collections.all.tsx @@ -144,13 +144,21 @@ function RecommendedProducts({ data={product?.priceRange?.minVariantPrice} /> ) : null} - - - - {priceOff ? ( + {product?.priceRange?.minVariantPrice?.amount < + product?.compareAtPriceRange?.minVariantPrice + ?.amount ? ( + + + + ) : ( + '' + )} + {priceOff >0? (

(${priceOff.toFixed(2)} OFF)

diff --git a/app/routes/($locale).products.$handle.tsx b/app/routes/($locale).products.$handle.tsx index 62e759b..29d6dcb 100644 --- a/app/routes/($locale).products.$handle.tsx +++ b/app/routes/($locale).products.$handle.tsx @@ -154,30 +154,29 @@ export default function Product() { const limitedProducts = relatedProductQueryResults?.productRecommendations?.slice(0, 5); - return ( <>
{limitedProducts?.length - ? limitedProducts?.map( - (product: any) => { - return ( - - - {product?.images?.nodes[0] ? ( - - ) : ( - // eslint-disable-next-line jsx-a11y/img-redundant-alt - No Image - )} -

{product?.title}

- - {product?.title} - - <> - - - -
- ); - }, - ):""} + ? limitedProducts?.map((product: any) => { + let priceOff; + // Check if the product is available for sale and all necessary price data is provided + if ( + product.availableForSale && + product.priceRange && + product.compareAtPriceRange + ) { + const price = parseFloat( + product.priceRange.minVariantPrice.amount, + ); + const compareAtPrice = parseFloat( + product.compareAtPriceRange.minVariantPrice.amount, + ); + + priceOff = compareAtPrice - price; + + } + + return ( + + + {product?.images?.nodes[0] ? ( + + ) : ( + // eslint-disable-next-line jsx-a11y/img-redundant-alt + No Image + )} +

{product?.title}

+ + {product?.title} + + { +
+ {product?.priceRange ? ( + + ) : null} + {product?.priceRange?.minVariantPrice?.amount < + product?.compareAtPriceRange?.minVariantPrice + ?.amount ? ( + + + + ) : ( + '' + )} + + {priceOff > 0 ? ( +

+ (${priceOff.toFixed(2)} OFF) +

+ ) : ( + '' + )} +
+ } + +
+ ); + }) + : ''}
@@ -321,48 +364,48 @@ function ProductMain({ -
- {stars && ( -
- )} - -
- {valueMap.get('product_review') && ( - <> -

- Reviews -

-
+
+ {stars && (
- - )} + )} -
- {valueMap.get('shipping_return_policy') && ( - <> -

- Shipping and Return -

-
-
- - )} +
+ {valueMap.get('product_review') && ( + <> +

+ Reviews +

+
+
+ + )} -
-
+
+ {valueMap.get('shipping_return_policy') && ( + <> +

+ Shipping and Return +

+
+
+ + )} + +
+
-
+
); } @@ -851,16 +894,28 @@ const RELATED_PRODUCT_QUERY = `#graphql productRecommendations(productId: $productID, intent: RELATED) { id, title + availableForSale priceRange { - minVariantPrice { - amount - currencyCode + minVariantPrice { + amount + currencyCode + } + maxVariantPrice { + amount + currencyCode + } } - maxVariantPrice { - amount - currencyCode + compareAtPriceRange { + minVariantPrice { + amount + currencyCode + } + maxVariantPrice { + amount + currencyCode + } + } - } images(first:1){ nodes{ id