From ebbc8f053cae13999dbb3966e8e265acf5634c73 Mon Sep 17 00:00:00 2001 From: Chloe Date: Sun, 7 Jul 2024 15:05:15 +0700 Subject: [PATCH] support dynamic content on PLP Signed-off-by: Chloe --- app/search/[collection]/page.tsx | 4 ++ components/page/accordion-block.tsx | 2 +- components/page/mini-icon-block.tsx | 38 ++++++++++++++++ components/page/page-content.tsx | 16 ++++--- components/page/rich-text-display.tsx | 12 ++++- components/plp/content.tsx | 31 +++++++++++++ components/plp/default-content.tsx | 28 ++++++++++++ components/plp/dynamic-content.tsx | 20 +++++++++ components/plp/tab-components.tsx | 4 ++ components/plp/tabs.tsx | 60 +++++++++++++++++++++++++ components/transmission-codes/index.tsx | 2 +- lib/types.ts | 1 + lib/utils.ts | 2 +- 13 files changed, 211 insertions(+), 9 deletions(-) create mode 100644 components/page/mini-icon-block.tsx create mode 100644 components/plp/content.tsx create mode 100644 components/plp/default-content.tsx create mode 100644 components/plp/dynamic-content.tsx create mode 100644 components/plp/tab-components.tsx create mode 100644 components/plp/tabs.tsx create mode 100644 lib/types.ts diff --git a/app/search/[collection]/page.tsx b/app/search/[collection]/page.tsx index 3d74389f8..67c668ea6 100644 --- a/app/search/[collection]/page.tsx +++ b/app/search/[collection]/page.tsx @@ -21,6 +21,7 @@ import HelpfulLinks from 'components/layout/search/helpful-links'; import ProductsGridPlaceholder from 'components/layout/search/placeholder'; import SortingMenu from 'components/layout/search/sorting-menu'; import Models from 'components/models'; +import Content from 'components/plp/content'; import TransmissionCode from 'components/transmission-codes'; import { Suspense } from 'react'; @@ -135,6 +136,9 @@ export default async function CategorySearchPage(props: { + + + {collectionHandle.startsWith('transmissions') && ( diff --git a/components/page/accordion-block.tsx b/components/page/accordion-block.tsx index 00e0629f2..19678e003 100644 --- a/components/page/accordion-block.tsx +++ b/components/page/accordion-block.tsx @@ -29,7 +29,7 @@ const AccordionBlock = ({ const accordionItemIds = JSON.parse(block.accordion || '[]') as string[]; return ( -
+
{block.title && (

{block.title}

)} diff --git a/components/page/mini-icon-block.tsx b/components/page/mini-icon-block.tsx new file mode 100644 index 000000000..6e1cdfaab --- /dev/null +++ b/components/page/mini-icon-block.tsx @@ -0,0 +1,38 @@ +import DynamicHeroIcon from 'components/hero-icon'; +import { getMetaobjectsByIds } from 'lib/shopify'; +import { Metaobject } from 'lib/shopify/types'; + +const MiniIconBlock = async ({ block }: { block: Metaobject }) => { + const contentIds = block.content ? JSON.parse(block.content) : []; + const contentBlocks = await getMetaobjectsByIds(contentIds); + + if (!contentBlocks || contentBlocks.length === 0) { + return null; + } + + return ( +
+ {block.title ? ( +

{block.title}

+ ) : null} + {contentBlocks.map((content) => ( +
+ {content.icon_name && ( + + )} + {content.title && content.content && ( +
+ {content.title && ( +
{content.title}
+ )} + {content.content &&

{content.content}

} +
+ )} + {content.title &&
{content.title}
} +
+ ))} +
+ ); +}; + +export default MiniIconBlock; diff --git a/components/page/page-content.tsx b/components/page/page-content.tsx index 928a14bc1..44916acdc 100644 --- a/components/page/page-content.tsx +++ b/components/page/page-content.tsx @@ -4,16 +4,22 @@ import AccordionBlock from './accordion-block'; import CategoryPreview, { CategoryPreviewPlaceholder } from './category-preview'; import IconWithTextBlock, { IconBlockPlaceholder } from './icon-with-text-block'; import ImageWithTextBlock from './image-with-text-block'; +import MiniIconBlock from './mini-icon-block'; import TextBlock from './text-block'; const PageContent = ({ block }: { block: Metaobject }) => { // eslint-disable-next-line no-unused-vars const contentMap: Record JSX.Element> = { - icon_content_section: (block) => ( - }> - - - ), + icon_content_section: (block) => + block.mini ? ( + + + + ) : ( + }> + + + ), image: (block) => , page_section: (block) => , accordion: (block) => , diff --git a/components/page/rich-text-display.tsx b/components/page/rich-text-display.tsx index a66b2e407..a666be22e 100644 --- a/components/page/rich-text-display.tsx +++ b/components/page/rich-text-display.tsx @@ -1,4 +1,5 @@ import clsx from 'clsx'; +import Link from 'next/link'; type Text = { type: 'text'; @@ -14,7 +15,8 @@ type Content = listType: 'bullet' | 'ordered' | 'unordered'; children: Array<{ type: 'listItem'; children: Text[] }>; } - | { type: 'listItem'; children: Text[] }; + | { type: 'listItem'; children: Text[] } + | { type: 'link'; children: Text[]; target: string; title: string; url: string }; const RichTextBlock = ({ block }: { block: Content }) => { if (block.type === 'text') { @@ -25,6 +27,14 @@ const RichTextBlock = ({ block }: { block: Content }) => { ); } + if (block.type === 'link') { + return ( + + {block.children[0]?.value || block.title} + + ); + } + if (block.type === 'listItem') { return block.children.map((child, index) => ); } diff --git a/components/plp/content.tsx b/components/plp/content.tsx new file mode 100644 index 000000000..75af208ce --- /dev/null +++ b/components/plp/content.tsx @@ -0,0 +1,31 @@ +import { getMetaobject } from 'lib/shopify'; +import DefaultContent from './default-content'; +import DynamicContent from './dynamic-content'; + +const Content = async ({ collection }: { collection: string }) => { + const [lastSegment] = collection.split('_').slice(-1); + + if (!lastSegment) { + return ; + } + + let content = null; + + if (collection.startsWith('transmissions')) { + content = await getMetaobject({ + handle: { handle: `transmission_code_${lastSegment}`, type: 'plp_content' } + }); + } else if (collection.startsWith('engines')) { + content = await getMetaobject({ + handle: { handle: `engine_size_${lastSegment}`, type: 'plp_content' } + }); + } + + if (!content) { + return ; + } + + return ; +}; + +export default Content; diff --git a/components/plp/default-content.tsx b/components/plp/default-content.tsx new file mode 100644 index 000000000..4884f1d22 --- /dev/null +++ b/components/plp/default-content.tsx @@ -0,0 +1,28 @@ +import Tag from 'components/tag'; +import { getMetaobject } from 'lib/shopify'; +import { Suspense } from 'react'; +import Tabs, { TabsPlaceholder } from './tabs'; + +const DefaultContent = async () => { + const defaultPLPContent = await getMetaobject({ + handle: { handle: 'default-plp-content', type: 'plp_content' } + }); + + if (!defaultPLPContent) return null; + + const sectionIds = defaultPLPContent.sections ? JSON.parse(defaultPLPContent.sections) : []; + + return ( +
+ +

+ {defaultPLPContent.title} +

+ }> + + +
+ ); +}; + +export default DefaultContent; diff --git a/components/plp/dynamic-content.tsx b/components/plp/dynamic-content.tsx new file mode 100644 index 000000000..a589adfa3 --- /dev/null +++ b/components/plp/dynamic-content.tsx @@ -0,0 +1,20 @@ +import Tag from 'components/tag'; +import { Metaobject } from 'lib/shopify/types'; +import { Suspense } from 'react'; +import Tabs, { TabsPlaceholder } from './tabs'; + +const DynamicContent = async ({ content }: { content: Metaobject }) => { + const sectionIds = content.sections ? JSON.parse(content.sections) : []; + + return ( +
+ +

{content.title}

+ }> + + +
+ ); +}; + +export default DynamicContent; diff --git a/components/plp/tab-components.tsx b/components/plp/tab-components.tsx new file mode 100644 index 000000000..b170492e1 --- /dev/null +++ b/components/plp/tab-components.tsx @@ -0,0 +1,4 @@ +'use client'; +import { Tab, TabGroup, TabList, TabPanel, TabPanels } from '@headlessui/react'; + +export { Tab, TabGroup, TabList, TabPanel, TabPanels }; diff --git a/components/plp/tabs.tsx b/components/plp/tabs.tsx new file mode 100644 index 000000000..5be0546ed --- /dev/null +++ b/components/plp/tabs.tsx @@ -0,0 +1,60 @@ +import { ChevronRightIcon } from '@heroicons/react/24/solid'; +import PageContent from 'components/page/page-content'; +import { getMetaobjectsByIds } from 'lib/shopify'; +import { Tab, TabGroup, TabList, TabPanel, TabPanels } from './tab-components'; + +const TabPanelContent = async ({ ids }: { ids: string[] }) => { + const content = await getMetaobjectsByIds(ids); + + return ( + + {content.map((block) => ( + + ))} + + ); +}; + +const Tabs = async ({ tabItemIds }: { tabItemIds: string[] }) => { + const tabItems = await getMetaobjectsByIds(tabItemIds); + if (!tabItems || tabItems.length === 0) return null; + + return ( + +
+ + {tabItems.map((item) => ( + + {item.title} + + + ))} + + + {tabItems.map((item) => ( + + ))} + +
+
+ ); +}; + +export const TabsPlaceholder = () => { + return ( +
+
+
+
+
+
+
+
+
+
+ ); +}; +export default Tabs; diff --git a/components/transmission-codes/index.tsx b/components/transmission-codes/index.tsx index 53218e20f..2a5289bb8 100644 --- a/components/transmission-codes/index.tsx +++ b/components/transmission-codes/index.tsx @@ -28,7 +28,7 @@ const TransmissionCode = async ({ collectionHandle }: { collectionHandle: string
{transmissionCodes.values.map((transmissionCode) => (
diff --git a/lib/types.ts b/lib/types.ts new file mode 100644 index 000000000..c112205d6 --- /dev/null +++ b/lib/types.ts @@ -0,0 +1 @@ +export type SearchParams = { [key: string]: string | string[] | undefined }; diff --git a/lib/utils.ts b/lib/utils.ts index 5e2d7ffdb..586bf338b 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -145,7 +145,7 @@ export const isBeforeToday = (date?: string | null) => { }; export const getCollectionUrl = (handle: string, includeSlashPrefix = true) => { - const rewriteUrl = handle.split('_').filter(Boolean).join('/'); + const rewriteUrl = handle.split('_').filter(Boolean).join('/').toLowerCase(); return includeSlashPrefix ? `/${rewriteUrl}` : rewriteUrl; };