Skip to content

Commit e72a16e

Browse files
committed
feat(algolia): add custom product_field metafield for faq in pdp
1 parent 69ee38a commit e72a16e

13 files changed

Lines changed: 779 additions & 893 deletions

File tree

starters/shopify-algolia/app/(ai-browse)/ai/product/[slug]/page.tsx

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ import { VariantDropdowns } from "components/product/variant-dropdowns"
2727
import { ProductTitle } from "components/product/product-title"
2828
import { ProductImages } from "components/product/product-images"
2929
import { RightSection } from "components/product/right-section"
30-
import { FaqSection } from "components/product/faq-section"
30+
import { FaqAccordionItem, FaqSectionClient } from "components/product/faq-section/faq-section-client"
31+
import { ShopifyRichText } from "components/product/faq-section/shopify-rich-text"
3132
import { AddToCartButton } from "components/product/add-to-cart-button"
3233
import { ReviewsSection } from "components/product/reviews-section"
3334

@@ -142,7 +143,36 @@ export default async function Product(props: ProductProps) {
142143
<AddToCartButton className="mt-4" product={product} combination={combination} />
143144
<FavoriteMarker handle={slug} />
144145
</div>
145-
<FaqSection />
146+
147+
<FaqSectionClient defaultOpenSections={product.productDetailsMetafield?.value ?? getDefaultFaqAccordionItemValue()}>
148+
<FaqAccordionItem title={getDefaultFaqAccordionItemValue()[0]}>
149+
<ShopifyRichText data={product.productDetailsMetafield?.value || getDefaultFaqAccordionItemRichText()} className="prose prose-sm max-w-none" />
150+
</FaqAccordionItem>
151+
<FaqAccordionItem title="Size and Fit">
152+
<p>
153+
Est veniam qui aute nisi occaecat ad non velit anim commodo sit proident. Labore sint officia nostrud eu est fugiat nulla velit sint commodo. Excepteur sit ut
154+
anim pariatur minim adipisicing dolore sit dolore cupidatat. Amet reprehenderit ipsum aute minim incididunt adipisicing est.
155+
</p>
156+
</FaqAccordionItem>
157+
<FaqAccordionItem title="Free Delivery and Returns">
158+
<p>
159+
Aliqua Lorem ullamco officia cupidatat cupidatat. Nostrud occaecat ex in Lorem. Et occaecat adipisicing do aliquip duis aliquip enim culpa nulla. Nulla quis aute
160+
ex eu est ullamco enim incididunt fugiat proident laboris. Laboris sint ad et nostrud velit fugiat fugiat proident enim sit irure elit. Ut amet elit labore
161+
cupidatat id consectetur sint fugiat esse excepteur pariatur. Tempor pariatur dolor eiusmod proident ad incididunt officia labore fugiat consectetur. Sunt veniam
162+
officia officia eiusmod minim incididunt est sit esse excepteur non cupidatat voluptate ea. Do excepteur sunt nostrud eu do id nisi dolore laboris ea ullamco
163+
magna eu. Eiusmod irure dolore amet velit laboris excepteur cupidatat est cupidatat minim ut anim id. Deserunt velit ex exercitation consequat quis magna pariatur
164+
laboris elit minim eiusmod anim.
165+
</p>
166+
</FaqAccordionItem>
167+
<FaqAccordionItem title="Supplier Information">
168+
<p>
169+
Aliqua ut ex irure eu officia dolore velit et occaecat pariatur excepteur nostrud ad. Ea reprehenderit sint culpa excepteur adipisicing ipsum esse excepteur
170+
officia culpa adipisicing nostrud. Nulla Lorem voluptate tempor officia id mollit do est amet dolor nulla. Sint sunt consequat non in reprehenderit Lorem velit
171+
enim cillum enim. Consequat occaecat exercitation consequat nisi veniam. Ipsum est reprehenderit cupidatat nulla minim anim deserunt consequat ipsum anim ea
172+
tempor.
173+
</p>
174+
</FaqAccordionItem>
175+
</FaqSectionClient>
146176
</RightSection>
147177
</div>
148178
<Suspense>
@@ -165,3 +195,11 @@ function makeBreadcrumbs(product: CommerceProduct) {
165195
[product.title]: "",
166196
}
167197
}
198+
199+
function getDefaultFaqAccordionItemRichText() {
200+
return "{\"type\":\"root\",\"children\":[{\"listType\":\"unordered\",\"type\":\"list\",\"children\":[{\"type\":\"list-item\",\"children\":[{\"type\":\"text\",\"value\":\"Super for the muscles\"}]},{\"type\":\"list-item\",\"children\":[{\"type\":\"text\",\"value\":\"Various types and color variants\"}]},{\"type\":\"list-item\",\"children\":[{\"type\":\"text\",\"value\":\"Outdoor, or indoor - you define the place where you want to exercise\"}]},{\"type\":\"list-item\",\"children\":[{\"type\":\"text\",\"value\":\"100% Plastic from \"},{\"type\":\"text\",\"value\":\"recycling the materials\",\"bold\":true}]}]}]}"
201+
}
202+
203+
function getDefaultFaqAccordionItemValue() {
204+
return ["Product Details"]
205+
}

starters/shopify-algolia/app/(browse)/product/[slug]/draft/page.tsx

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ import { ProductImages } from "components/product/product-images"
2626
import { RightSection } from "components/product/right-section"
2727
import { AddToCartButton } from "components/product/add-to-cart-button"
2828
import { FavoriteMarker } from "components/product/favorite-marker"
29-
import { FaqSection } from "components/product/faq-section"
29+
import { FaqAccordionItem, FaqSectionClient } from "components/product/faq-section/faq-section-client"
30+
import { ShopifyRichText } from "components/product/faq-section/shopify-rich-text"
3031

3132
import { slugToName } from "utils/slug-name"
3233

@@ -126,7 +127,36 @@ export default async function DraftProduct(props: ProductProps) {
126127
<p>{adminProduct.description}</p>
127128
<AddToCartButton className="mt-4" product={adminProduct as CommerceProduct} combination={combination} />
128129
<FavoriteMarker handle={slug} />
129-
<FaqSection />
130+
131+
<FaqSectionClient defaultOpenSections={adminProduct.productDetailsMetafield?.value ?? getDefaultFaqAccordionItemValue()}>
132+
<FaqAccordionItem title={getDefaultFaqAccordionItemValue()[0]}>
133+
<ShopifyRichText data={adminProduct.productDetailsMetafield?.value || getDefaultFaqAccordionItemRichText()} className="prose prose-sm max-w-none" />
134+
</FaqAccordionItem>
135+
<FaqAccordionItem title="Size and Fit">
136+
<p>
137+
Est veniam qui aute nisi occaecat ad non velit anim commodo sit proident. Labore sint officia nostrud eu est fugiat nulla velit sint commodo. Excepteur sit ut
138+
anim pariatur minim adipisicing dolore sit dolore cupidatat. Amet reprehenderit ipsum aute minim incididunt adipisicing est.
139+
</p>
140+
</FaqAccordionItem>
141+
<FaqAccordionItem title="Free Delivery and Returns">
142+
<p>
143+
Aliqua Lorem ullamco officia cupidatat cupidatat. Nostrud occaecat ex in Lorem. Et occaecat adipisicing do aliquip duis aliquip enim culpa nulla. Nulla quis aute
144+
ex eu est ullamco enim incididunt fugiat proident laboris. Laboris sint ad et nostrud velit fugiat fugiat proident enim sit irure elit. Ut amet elit labore
145+
cupidatat id consectetur sint fugiat esse excepteur pariatur. Tempor pariatur dolor eiusmod proident ad incididunt officia labore fugiat consectetur. Sunt veniam
146+
officia officia eiusmod minim incididunt est sit esse excepteur non cupidatat voluptate ea. Do excepteur sunt nostrud eu do id nisi dolore laboris ea ullamco
147+
magna eu. Eiusmod irure dolore amet velit laboris excepteur cupidatat est cupidatat minim ut anim id. Deserunt velit ex exercitation consequat quis magna pariatur
148+
laboris elit minim eiusmod anim.
149+
</p>
150+
</FaqAccordionItem>
151+
<FaqAccordionItem title="Supplier Information">
152+
<p>
153+
Aliqua ut ex irure eu officia dolore velit et occaecat pariatur excepteur nostrud ad. Ea reprehenderit sint culpa excepteur adipisicing ipsum esse excepteur
154+
officia culpa adipisicing nostrud. Nulla Lorem voluptate tempor officia id mollit do est amet dolor nulla. Sint sunt consequat non in reprehenderit Lorem velit
155+
enim cillum enim. Consequat occaecat exercitation consequat nisi veniam. Ipsum est reprehenderit cupidatat nulla minim anim deserunt consequat ipsum anim ea
156+
tempor.
157+
</p>
158+
</FaqAccordionItem>
159+
</FaqSectionClient>
130160
</RightSection>
131161
</div>
132162
</main>
@@ -147,3 +177,12 @@ function makeBreadcrumbs(product: CommerceProduct) {
147177
[product.title]: "",
148178
}
149179
}
180+
181+
182+
function getDefaultFaqAccordionItemRichText() {
183+
return "{\"type\":\"root\",\"children\":[{\"listType\":\"unordered\",\"type\":\"list\",\"children\":[{\"type\":\"list-item\",\"children\":[{\"type\":\"text\",\"value\":\"Super for the muscles\"}]},{\"type\":\"list-item\",\"children\":[{\"type\":\"text\",\"value\":\"Various types and color variants\"}]},{\"type\":\"list-item\",\"children\":[{\"type\":\"text\",\"value\":\"Outdoor, or indoor - you define the place where you want to exercise\"}]},{\"type\":\"list-item\",\"children\":[{\"type\":\"text\",\"value\":\"100% Plastic from \"},{\"type\":\"text\",\"value\":\"recycling the materials\",\"bold\":true}]}]}]}"
184+
}
185+
186+
function getDefaultFaqAccordionItemValue() {
187+
return ["Product Details"]
188+
}

starters/shopify-algolia/app/(browse)/product/[slug]/page.tsx

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ import { VariantDropdowns } from "components/product/variant-dropdowns"
2727
import { ProductTitle } from "components/product/product-title"
2828
import { ProductImages } from "components/product/product-images"
2929
import { RightSection } from "components/product/right-section"
30-
import { FaqSection } from "components/product/faq-section"
30+
import { FaqAccordionItem, FaqSectionClient } from "components/product/faq-section/faq-section-client"
31+
import { ShopifyRichText } from "components/product/faq-section/shopify-rich-text"
3132
import { AddToCartButton } from "components/product/add-to-cart-button"
3233
import { ReviewsSection } from "components/product/reviews-section"
3334

@@ -63,10 +64,8 @@ export default async function Product(props: ProductProps) {
6364
console.log(slug)
6465

6566
const multiOptions = getMultiOptionFromSlug(slug)
66-
const baseHandle = Object.keys(multiOptions).length > 0
67-
? removeMultiOptionFromSlug(slug)
68-
: removeVisualOptionFromSlug(slug)
69-
67+
const baseHandle = Object.keys(multiOptions).length > 0 ? removeMultiOptionFromSlug(slug) : removeVisualOptionFromSlug(slug)
68+
7069
const product = await getProduct(baseHandle || removeOptionsFromUrl(slug))
7170

7271
if (!product) {
@@ -95,7 +94,7 @@ export default async function Product(props: ProductProps) {
9594
let visualValue: string | null = null
9695
if (Object.keys(multiOptions).length > 0) {
9796
if (multiOptions.color) {
98-
visualValue = getOriginalOptionValue(product.variants, 'color', multiOptions.color)
97+
visualValue = getOriginalOptionValue(product.variants, "color", multiOptions.color)
9998
}
10099
if (!visualValue && Object.keys(multiOptions).length > 0) {
101100
const firstOption = Object.entries(multiOptions)[0]
@@ -104,7 +103,7 @@ export default async function Product(props: ProductProps) {
104103
} else {
105104
visualValue = getVisualOptionFromSlug(slug)
106105
}
107-
106+
108107
const { images: imagesToShow, activeIndex } = getImagesForCarousel(product.images, visualValue)
109108

110109
return (
@@ -123,11 +122,7 @@ export default async function Product(props: ProductProps) {
123122
price={combinationPrice}
124123
currency={combination?.price ? mapCurrencyToSign(combination.price?.currencyCode as CurrencyType) : "$"}
125124
/>
126-
<ProductImages
127-
key={slug}
128-
images={imagesToShow}
129-
initialActiveIndex={activeIndex}
130-
/>
125+
<ProductImages key={slug} images={imagesToShow} initialActiveIndex={activeIndex} />
131126
<RightSection className="md:col-span-6 md:col-start-8 md:mt-0">
132127
<ProductTitle
133128
className="hidden md:col-span-4 md:col-start-9 md:block"
@@ -139,7 +134,35 @@ export default async function Product(props: ProductProps) {
139134
<p>{product.description}</p>
140135
<AddToCartButton className="mt-4" product={product} combination={combination} />
141136
<FavoriteMarker handle={slug} />
142-
<FaqSection />
137+
<FaqSectionClient defaultOpenSections={product.productDetailsMetafield?.value ?? getDefaultFaqAccordionItemValue()}>
138+
<FaqAccordionItem title={getDefaultFaqAccordionItemValue()[0]}>
139+
<ShopifyRichText data={product.productDetailsMetafield?.value || getDefaultFaqAccordionItemRichText()} className="prose prose-sm max-w-none" />
140+
</FaqAccordionItem>
141+
<FaqAccordionItem title="Size and Fit">
142+
<p>
143+
Est veniam qui aute nisi occaecat ad non velit anim commodo sit proident. Labore sint officia nostrud eu est fugiat nulla velit sint commodo. Excepteur sit ut
144+
anim pariatur minim adipisicing dolore sit dolore cupidatat. Amet reprehenderit ipsum aute minim incididunt adipisicing est.
145+
</p>
146+
</FaqAccordionItem>
147+
<FaqAccordionItem title="Free Delivery and Returns">
148+
<p>
149+
Aliqua Lorem ullamco officia cupidatat cupidatat. Nostrud occaecat ex in Lorem. Et occaecat adipisicing do aliquip duis aliquip enim culpa nulla. Nulla quis aute
150+
ex eu est ullamco enim incididunt fugiat proident laboris. Laboris sint ad et nostrud velit fugiat fugiat proident enim sit irure elit. Ut amet elit labore
151+
cupidatat id consectetur sint fugiat esse excepteur pariatur. Tempor pariatur dolor eiusmod proident ad incididunt officia labore fugiat consectetur. Sunt veniam
152+
officia officia eiusmod minim incididunt est sit esse excepteur non cupidatat voluptate ea. Do excepteur sunt nostrud eu do id nisi dolore laboris ea ullamco
153+
magna eu. Eiusmod irure dolore amet velit laboris excepteur cupidatat est cupidatat minim ut anim id. Deserunt velit ex exercitation consequat quis magna pariatur
154+
laboris elit minim eiusmod anim.
155+
</p>
156+
</FaqAccordionItem>
157+
<FaqAccordionItem title="Supplier Information">
158+
<p>
159+
Aliqua ut ex irure eu officia dolore velit et occaecat pariatur excepteur nostrud ad. Ea reprehenderit sint culpa excepteur adipisicing ipsum esse excepteur
160+
officia culpa adipisicing nostrud. Nulla Lorem voluptate tempor officia id mollit do est amet dolor nulla. Sint sunt consequat non in reprehenderit Lorem velit
161+
enim cillum enim. Consequat occaecat exercitation consequat nisi veniam. Ipsum est reprehenderit cupidatat nulla minim anim deserunt consequat ipsum anim ea
162+
tempor.
163+
</p>
164+
</FaqAccordionItem>
165+
</FaqSectionClient>
143166
</RightSection>
144167
</div>
145168
<Suspense>
@@ -162,3 +185,12 @@ function makeBreadcrumbs(product: CommerceProduct) {
162185
[product.title]: "",
163186
}
164187
}
188+
189+
190+
function getDefaultFaqAccordionItemRichText() {
191+
return "{\"type\":\"root\",\"children\":[{\"listType\":\"unordered\",\"type\":\"list\",\"children\":[{\"type\":\"list-item\",\"children\":[{\"type\":\"text\",\"value\":\"Super for the muscles\"}]},{\"type\":\"list-item\",\"children\":[{\"type\":\"text\",\"value\":\"Various types and color variants\"}]},{\"type\":\"list-item\",\"children\":[{\"type\":\"text\",\"value\":\"Outdoor, or indoor - you define the place where you want to exercise\"}]},{\"type\":\"list-item\",\"children\":[{\"type\":\"text\",\"value\":\"100% Plastic from \"},{\"type\":\"text\",\"value\":\"recycling the materials\",\"bold\":true}]}]}]}"
192+
}
193+
194+
function getDefaultFaqAccordionItemValue() {
195+
return ["Product Details"]
196+
}

starters/shopify-algolia/components/product/faq-section.tsx

Lines changed: 0 additions & 51 deletions
This file was deleted.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
"use client"
2+
3+
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "components/ui/accordion"
4+
import { cn } from "utils/cn"
5+
import { nameToSlug } from "utils/slug-name"
6+
7+
interface FaqSectionClientProps {
8+
className?: string
9+
defaultOpenSections?: string[] | string
10+
children: React.ReactNode
11+
}
12+
13+
export function FaqSectionClient({ className, defaultOpenSections, children }: FaqSectionClientProps) {
14+
return (
15+
<Accordion type="multiple" className={cn("w-full", className)} defaultValue={Array.isArray(defaultOpenSections) ? defaultOpenSections.map(nameToSlug) : [nameToSlug(defaultOpenSections ?? "")]}>
16+
{children}
17+
</Accordion>
18+
)
19+
}
20+
21+
interface FaqAccordionItemProps {
22+
title: string
23+
children: React.ReactNode
24+
}
25+
26+
export function FaqAccordionItem({ title, children }: FaqAccordionItemProps) {
27+
console.log(title, nameToSlug(title))
28+
return (
29+
<AccordionItem value={nameToSlug(title)} key={nameToSlug(title)}>
30+
<AccordionTrigger className="py-4 text-base font-bold">{title}</AccordionTrigger>
31+
<AccordionContent>{children}</AccordionContent>
32+
</AccordionItem>
33+
)
34+
}

0 commit comments

Comments
 (0)