From ac8f654334cc71167e752801566bb7e1d78e7124 Mon Sep 17 00:00:00 2001 From: Anh Pham Date: Thu, 19 Jun 2025 01:41:39 -0500 Subject: [PATCH 1/4] Created variants --- .../cdcasestudies/components/index.tsx | 3 + .../components/variants/ourWorkVariant.tsx | 83 +++++++++++++++++++ .../components/cdcasestudies/sanity-schema.ts | 1 + 3 files changed, 87 insertions(+) create mode 100644 src/theme/conversion/components/cdcasestudies/components/variants/ourWorkVariant.tsx diff --git a/src/theme/conversion/components/cdcasestudies/components/index.tsx b/src/theme/conversion/components/cdcasestudies/components/index.tsx index e9b622b..4b2f488 100644 --- a/src/theme/conversion/components/cdcasestudies/components/index.tsx +++ b/src/theme/conversion/components/cdcasestudies/components/index.tsx @@ -4,6 +4,7 @@ import type { ViewComponentProps } from "@conversiondigital/headless-basics-data import { getLogger, logPrefix } from "@conversiondigital/headless-basics-data/src"; import DemoVariant from "./variants/demoVariant"; import CdcasestudiesDefaultVariant from "./variants/cdcasestudiesDefaultVariant"; +import OurWorkVariant from "./variants/ourWorkVariant"; export const log = getLogger("conversion.components.cdcasestudies"); @@ -15,6 +16,8 @@ export default function CdcasestudiesUI(dynamicComponent: ViewComponentProps) { switch (variant) { case "xDemo": return ; + case "ourWork": + return ; default: return ; } diff --git a/src/theme/conversion/components/cdcasestudies/components/variants/ourWorkVariant.tsx b/src/theme/conversion/components/cdcasestudies/components/variants/ourWorkVariant.tsx new file mode 100644 index 0000000..b0f73d8 --- /dev/null +++ b/src/theme/conversion/components/cdcasestudies/components/variants/ourWorkVariant.tsx @@ -0,0 +1,83 @@ +import React from "react"; +import { StandardComponentProps } from "@conversiondigital/headless-basics-components/src/interfaces/standardComponentProps"; +import { getCmsImage } from "@conversiondigital/headless-basics-data/src/cms/tools/multiCmsImageTools"; + +export default function OurWorkVariant(props: StandardComponentProps) { + const { matchingData } = props; + const title = matchingData?.title?.toUpperCase(); + const subtitle = matchingData?.subtitle; + const items = matchingData?.items || []; + const button = matchingData?.button || {}; + + return ( +
+
+
+

+ {title} +

+
+

+ {subtitle} +

+
+
+ +
+ {items.map((item: any, index: number) => { + const { hasImage, imageLocation } = getCmsImage(item); + const finalImage = hasImage ? imageLocation : item?.imageUrl; + + return ( +
+
+ {finalImage && ( + {item?.title} + )} +
+
+ +
+ {item?.title && ( +
+ {item.title} +
+ )} + {item?.description && ( +

+ {item.description} +

+ )} + + {item?.link && ( + + View case study + + )} +
+
+ ); + })} +
+ + {button?.text && button?.link && ( + + )} +
+
+ ); +} \ No newline at end of file diff --git a/src/theme/conversion/components/cdcasestudies/sanity-schema.ts b/src/theme/conversion/components/cdcasestudies/sanity-schema.ts index 9705f11..6700f04 100644 --- a/src/theme/conversion/components/cdcasestudies/sanity-schema.ts +++ b/src/theme/conversion/components/cdcasestudies/sanity-schema.ts @@ -46,6 +46,7 @@ export default defineType({ options: { list: [ { title: 'Default', value: 'default' }, + { title: 'Our Work', value: 'ourWork' }, { title: 'Demo', value: 'xDemo' }, ], }, From 2e59e99f9e82e8305d13f6906879ce63d6341c36 Mon Sep 17 00:00:00 2001 From: Anh Pham Date: Mon, 23 Jun 2025 22:38:18 -0500 Subject: [PATCH 2/4] Created variants --- .../components/cdfooter/sanity-mapping.ts | 5 +- .../herobanner/components/index.tsx | 3 ++ .../components/variants/caseStudyVariant.tsx | 49 +++++++++++++++++++ .../components/herobanner/sanity-mapping.ts | 2 +- .../components/herobanner/sanity-query.ts | 4 ++ .../components/herobanner/sanity-schema.ts | 48 +++++++++++++++++- 6 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 src/theme/conversion/components/herobanner/components/variants/caseStudyVariant.tsx diff --git a/src/theme/conversion/components/cdfooter/sanity-mapping.ts b/src/theme/conversion/components/cdfooter/sanity-mapping.ts index 522b1bd..1c4fd3b 100644 --- a/src/theme/conversion/components/cdfooter/sanity-mapping.ts +++ b/src/theme/conversion/components/cdfooter/sanity-mapping.ts @@ -9,12 +9,9 @@ export async function mapIdentifierData(pageAndComponentCombo: PageAndSingleComp ); const content = pageAndComponentCombo?.component?.data - const thisComponentsOrder = pageAndComponentCombo?.component?.sortOrder ?? 0; log.trace(`${logPrefix()} thisComponentsOrder: ${thisComponentsOrder}`); - - - const matchingData = extractComponentsFromSanityData(content, "cdfooter", log, true, '', thisComponentsOrder); + const matchingData = extractComponentsFromSanityData(content, "cdfooter", log); return matchingData } \ No newline at end of file diff --git a/src/theme/conversion/components/herobanner/components/index.tsx b/src/theme/conversion/components/herobanner/components/index.tsx index af66077..f904e8b 100644 --- a/src/theme/conversion/components/herobanner/components/index.tsx +++ b/src/theme/conversion/components/herobanner/components/index.tsx @@ -4,6 +4,7 @@ import type { ViewComponentProps } from "@conversiondigital/headless-basics-data import { getLogger, logPrefix } from "@conversiondigital/headless-basics-data/src" import DemoVariant from "./variants/demoVariant" import DefaultVariant from "./variants/defaultVariant" +import CaseStudyVariant from "./variants/caseStudyVariant" export const log = getLogger("default.components.heartcore.template.variants"); @@ -14,6 +15,8 @@ export default function TemplateUI(dynamicComponent: ViewComponentProps) { switch (variant) { case "xDemo": return ; + case "caseStudy": + return ; default: return ; } diff --git a/src/theme/conversion/components/herobanner/components/variants/caseStudyVariant.tsx b/src/theme/conversion/components/herobanner/components/variants/caseStudyVariant.tsx new file mode 100644 index 0000000..12aa6b6 --- /dev/null +++ b/src/theme/conversion/components/herobanner/components/variants/caseStudyVariant.tsx @@ -0,0 +1,49 @@ +import React from "react" +import { StandardComponentProps } from "@conversiondigital/headless-basics-components/src/interfaces/standardComponentProps" +import Link from "next/link" +import { buttonIcon as ButtonIcon } from "../../../../styles/icons/icons" + +export default function CaseStudyVariant(props: StandardComponentProps) { + const { matchingData } = props; + const imageUrl = matchingData?.image?.asset?.url + const altText = matchingData?.altText || matchingData?.image?.asset?.altText || "HeroBanner Image" + const description = matchingData?.description || "Case Study HeroBanner Description" + + const buttonLabel = matchingData?.button?.label + const buttonLink = matchingData?.button?.link + return ( +
+
+

+ {(matchingData?.title || "Case Study HeroBanner Title").toUpperCase()} +

+

+ {matchingData?.subtitle || "Case Study HeroBanner Subtitle"} +

+

+ {description} +

+ {buttonLabel && buttonLink && ( +
+ + {buttonLabel} + + +
+ )} +
+ {imageUrl && ( +
+ {altText} +
+ )} +
+ ) +} \ No newline at end of file diff --git a/src/theme/conversion/components/herobanner/sanity-mapping.ts b/src/theme/conversion/components/herobanner/sanity-mapping.ts index f3c1273..33380fd 100644 --- a/src/theme/conversion/components/herobanner/sanity-mapping.ts +++ b/src/theme/conversion/components/herobanner/sanity-mapping.ts @@ -11,6 +11,6 @@ export async function mapIdentifierData(pageAndComponentCombo: PageAndSingleComp const content = pageAndComponentCombo?.component?.data; const thisComponentsOrder = pageAndComponentCombo?.component?.sortOrder ?? 0; log.trace(`${logPrefix()} thisComponentsOrder: ${thisComponentsOrder}`); - const matchingData = extractComponentsFromSanityData(content, "herobanner", log, true, '', thisComponentsOrder); + const matchingData = extractComponentsFromSanityData(content, "herobanner", log); return matchingData; } \ No newline at end of file diff --git a/src/theme/conversion/components/herobanner/sanity-query.ts b/src/theme/conversion/components/herobanner/sanity-query.ts index 8f1a37c..a8d9166 100644 --- a/src/theme/conversion/components/herobanner/sanity-query.ts +++ b/src/theme/conversion/components/herobanner/sanity-query.ts @@ -14,6 +14,7 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str selectableVariant title subtitle + description image { asset { url @@ -31,6 +32,7 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str _type title subtitle + description image { asset { url @@ -55,6 +57,7 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str selectableVariant title subtitle + description image { asset { url @@ -72,6 +75,7 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str _type title subtitle + description image { asset { url diff --git a/src/theme/conversion/components/herobanner/sanity-schema.ts b/src/theme/conversion/components/herobanner/sanity-schema.ts index 0955911..e011586 100644 --- a/src/theme/conversion/components/herobanner/sanity-schema.ts +++ b/src/theme/conversion/components/herobanner/sanity-schema.ts @@ -1,6 +1,38 @@ -import { defineField, defineType } from 'sanity' +import { defineField, defineType, defineArrayMember } from 'sanity' import { EyeOpenIcon } from '@sanity/icons' +// Define the case study item schema for hero banner as a top-level type +export const heroCaseStudyItem = defineType({ + name: 'heroCaseStudyItem', + title: 'Hero Case Study Item', + type: 'object', + fields: [ + defineField({ + name: 'title', + title: 'Title', + type: 'string' + }), + defineField({ + name: 'description', + title: 'Description', + type: 'text' + }), + defineField({ + name: 'image', + title: 'Image', + type: 'image', + options: { + hotspot: true + } + }), + defineField({ + name: 'link', + title: 'Link', + type: 'url' + }) + ] +}) + export default defineType({ name: 'herobanner', title: 'HeroBanner', @@ -14,6 +46,7 @@ export default defineType({ options: { list: [ { title: 'Default', value: 'default' }, + { title: 'Case Study', value: 'caseStudy' }, { title: 'Demo', value: 'xDemo' }, ] } @@ -28,6 +61,12 @@ export default defineType({ title: 'Subtitle', type: 'string', }), + defineField({ + name: 'description', + title: 'Description', + type: 'text', + description: 'Additional descriptive text for the hero banner (especially useful for Case Study variant)' + }), defineField({ name: 'image', title: 'Image', @@ -46,6 +85,13 @@ export default defineType({ title: 'Button', type: 'heroButton' }), + defineField({ + name: 'items', + title: 'Case Studies', + type: 'array', + of: [{ type: 'heroCaseStudyItem' }], + description: 'Case studies to display in the hero banner (for Case Study variant)' + }), defineField({ name: 'sortOrder', title: 'Sort Order', From 1adb981be1a221fad2f73c85b6fa5b20bc44cebf Mon Sep 17 00:00:00 2001 From: Anh Pham Date: Mon, 30 Jun 2025 18:42:24 -0500 Subject: [PATCH 3/4] Created variants for case studies and herobanner --- .../components/variants/ourWorkVariant.tsx | 129 +++++++++++++----- .../components/cdcasestudies/sanity-query.ts | 8 ++ .../components/cdcasestudies/sanity-schema.ts | 41 ++++++ .../components/variants/caseStudyVariant.tsx | 8 +- 4 files changed, 146 insertions(+), 40 deletions(-) diff --git a/src/theme/conversion/components/cdcasestudies/components/variants/ourWorkVariant.tsx b/src/theme/conversion/components/cdcasestudies/components/variants/ourWorkVariant.tsx index b0f73d8..f0625a8 100644 --- a/src/theme/conversion/components/cdcasestudies/components/variants/ourWorkVariant.tsx +++ b/src/theme/conversion/components/cdcasestudies/components/variants/ourWorkVariant.tsx @@ -1,4 +1,5 @@ -import React from "react"; +"use client" +import React, { useState, useMemo } from "react"; import { StandardComponentProps } from "@conversiondigital/headless-basics-components/src/interfaces/standardComponentProps"; import { getCmsImage } from "@conversiondigital/headless-basics-data/src/cms/tools/multiCmsImageTools"; @@ -6,11 +7,34 @@ export default function OurWorkVariant(props: StandardComponentProps) { const { matchingData } = props; const title = matchingData?.title?.toUpperCase(); const subtitle = matchingData?.subtitle; + const keywords = matchingData?.keywords || []; const items = matchingData?.items || []; const button = matchingData?.button || {}; + // State for active filter + const [activeFilter, setActiveFilter] = useState('all'); + + // Filter case studies based on active filter + const filteredItems = useMemo(() => { + if (activeFilter === 'all') { + return items; + } + + return items.filter((item: any) => { + const itemTopics = item?.topics || []; + // Check if any of the item's topics match the selected keyword (case-insensitive) + return itemTopics.some((topic: string) => + topic?.toLowerCase() === activeFilter?.toLowerCase() + ); + }); + }, [items, activeFilter]); + + const handleFilterClick = (keyword: string) => { + setActiveFilter(activeFilter === keyword ? 'all' : keyword); + }; + return ( -
+

@@ -23,46 +47,79 @@ export default function OurWorkVariant(props: StandardComponentProps) {

-
- {items.map((item: any, index: number) => { + {keywords.length > 0 && ( +
+ + {keywords.map((keyword: string, index: number) => { + // Count items that match this keyword (case-insensitive) + const matchingCount = items.filter((item: any) => + item?.topics?.some((topic: string) => + topic?.toLowerCase() === keyword?.toLowerCase() + ) + ).length; + + return ( + + ); + })} +
+ )} + +
+ {filteredItems.map((item: any, index: number) => { const { hasImage, imageLocation } = getCmsImage(item); const finalImage = hasImage ? imageLocation : item?.imageUrl; return ( -
-
- {finalImage && ( - {item?.title} - )} -
+ +
+ {item?.title +
-
- {item?.title && ( -
- {item.title} -
- )} - {item?.description && ( -

- {item.description} -

- )} - - {item?.link && ( -
- View case study - +
+ {item?.topics && item.topics.length > 0 && ( +
+ {item.topics.map((topic: string, topicIndex: number) => ( + + {topic.toUpperCase()} + + ))} +
)} +
{item?.title || "Be Fit Food"}
-
+ ); })}
@@ -71,7 +128,7 @@ export default function OurWorkVariant(props: StandardComponentProps) {
{button.text} @@ -80,4 +137,4 @@ export default function OurWorkVariant(props: StandardComponentProps) {
); -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/theme/conversion/components/cdcasestudies/sanity-query.ts b/src/theme/conversion/components/cdcasestudies/sanity-query.ts index c48d4ab..294e6af 100644 --- a/src/theme/conversion/components/cdcasestudies/sanity-query.ts +++ b/src/theme/conversion/components/cdcasestudies/sanity-query.ts @@ -14,10 +14,12 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str sortOrder title subtitle + keywords items { title description link + topics image { asset { url @@ -33,10 +35,12 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str selectableVariant title subtitle + keywords items { title description link + topics image { asset { url @@ -58,10 +62,12 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str sortOrder title subtitle + keywords items { title description link + topics image { asset { url @@ -77,10 +83,12 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str selectableVariant title subtitle + keywords items { title description link + topics image { asset { url diff --git a/src/theme/conversion/components/cdcasestudies/sanity-schema.ts b/src/theme/conversion/components/cdcasestudies/sanity-schema.ts index 6700f04..16c73a9 100644 --- a/src/theme/conversion/components/cdcasestudies/sanity-schema.ts +++ b/src/theme/conversion/components/cdcasestudies/sanity-schema.ts @@ -29,6 +29,16 @@ export const caseStudyItem = defineType({ name: 'link', title: 'Link', type: 'url' + }), + defineField({ + name: 'topics', + title: 'Topics', + type: 'array', + of: [{ type: 'string' }], + description: 'Topics/categories for this case study', + options: { + layout: 'tags' + } }) ] }) @@ -69,6 +79,16 @@ export default defineType({ type: 'string', description: 'Subtitle or tagline for the case studies section.' }), + defineField({ + name: 'keywords', + title: 'Keywords', + type: 'array', + of: [{ type: 'string' }], + description: 'Keywords displayed at the top of the case studies section', + options: { + layout: 'tags' + } + }), defineField({ name: 'items', title: 'Case Studies', @@ -93,6 +113,27 @@ export default defineType({ title: 'Button URL', type: 'string', description: 'URL for the button link' + }), + defineField({ + name: 'showFilter', + title: 'Show Keyword Filter', + type: 'boolean', + description: 'Show the keyword filter section above the case studies', + initialValue: true + }), + defineField({ + name: 'filterTitle', + title: 'Filter Title', + type: 'string', + description: 'Title for the keyword filter section', + initialValue: 'Filter by Category' + }), + defineField({ + name: 'allKeyword', + title: 'All Keywords Text', + type: 'string', + description: 'Text for the "show all" filter option', + initialValue: 'All' }) ], preview: { diff --git a/src/theme/conversion/components/herobanner/components/variants/caseStudyVariant.tsx b/src/theme/conversion/components/herobanner/components/variants/caseStudyVariant.tsx index 12aa6b6..236e933 100644 --- a/src/theme/conversion/components/herobanner/components/variants/caseStudyVariant.tsx +++ b/src/theme/conversion/components/herobanner/components/variants/caseStudyVariant.tsx @@ -14,12 +14,12 @@ export default function CaseStudyVariant(props: StandardComponentProps) { return (
+

+ {matchingData?.title || "Case Study HeroBanner Title"} +

- {(matchingData?.title || "Case Study HeroBanner Title").toUpperCase()} + {(matchingData?.subtitle || "Case Study HeroBanner Subtitle").toUpperCase()}

-

- {matchingData?.subtitle || "Case Study HeroBanner Subtitle"} -

{description}

From 9b65cebcab48cc3612ccb9426c0d39c1af061401 Mon Sep 17 00:00:00 2001 From: Anh Pham Date: Mon, 7 Jul 2025 13:30:14 -0500 Subject: [PATCH 4/4] trouubleshooting animations and localhost --- .../components/variants/ourWorkVariant.tsx | 98 +++++++++++++------ .../components/cdcasestudies/sanity-query.ts | 8 +- .../components/cdcasestudies/sanity-schema.ts | 6 +- 3 files changed, 77 insertions(+), 35 deletions(-) diff --git a/src/theme/conversion/components/cdcasestudies/components/variants/ourWorkVariant.tsx b/src/theme/conversion/components/cdcasestudies/components/variants/ourWorkVariant.tsx index f0625a8..b997026 100644 --- a/src/theme/conversion/components/cdcasestudies/components/variants/ourWorkVariant.tsx +++ b/src/theme/conversion/components/cdcasestudies/components/variants/ourWorkVariant.tsx @@ -4,33 +4,61 @@ import { StandardComponentProps } from "@conversiondigital/headless-basics-compo import { getCmsImage } from "@conversiondigital/headless-basics-data/src/cms/tools/multiCmsImageTools"; export default function OurWorkVariant(props: StandardComponentProps) { + console.log('DEBUG: OurWorkVariant props:', props); + const { matchingData } = props; + console.log('DEBUG: matchingData:', matchingData); + const title = matchingData?.title?.toUpperCase(); const subtitle = matchingData?.subtitle; - const keywords = matchingData?.keywords || []; + const topics = matchingData?.topics || []; const items = matchingData?.items || []; const button = matchingData?.button || {}; + + console.log('DEBUG: Extracted data:'); + console.log('- title:', title); + console.log('- subtitle:', subtitle); + console.log('- topics:', topics); + console.log('- items:', items); + console.log('- button:', button); // State for active filter const [activeFilter, setActiveFilter] = useState('all'); + // Use topics from main schema + const availableTopics = topics; + // Filter case studies based on active filter const filteredItems = useMemo(() => { + console.log('DEBUG: Filtering - activeFilter:', activeFilter); + console.log('DEBUG: Filtering - items:', items); + if (activeFilter === 'all') { return items; } - return items.filter((item: any) => { + const filtered = items.filter((item: any) => { const itemTopics = item?.topics || []; - // Check if any of the item's topics match the selected keyword (case-insensitive) - return itemTopics.some((topic: string) => - topic?.toLowerCase() === activeFilter?.toLowerCase() - ); + console.log('DEBUG: Item topics:', itemTopics, 'for item:', item?.title); + + const matches = itemTopics.some((topic: string) => { + const match = topic?.toLowerCase().trim() === activeFilter?.toLowerCase().trim(); + console.log('DEBUG: Comparing:', topic, 'with', activeFilter, 'match:', match); + return match; + }); + + console.log('DEBUG: Item matches filter:', matches); + return matches; }); + + console.log('DEBUG: Filtered items:', filtered); + return filtered; }, [items, activeFilter]); - const handleFilterClick = (keyword: string) => { - setActiveFilter(activeFilter === keyword ? 'all' : keyword); + const handleFilterClick = (topic: string) => { + console.log('DEBUG: handleFilterClick called with topic:', topic); + setActiveFilter(topic); + console.log('DEBUG: activeFilter set to:', topic); }; return ( @@ -47,40 +75,54 @@ export default function OurWorkVariant(props: StandardComponentProps) {
- {keywords.length > 0 && ( -
- - {keywords.map((keyword: string, index: number) => { - // Count items that match this keyword (case-insensitive) + {availableTopics && availableTopics.length > 0 && ( +
+
+ + {availableTopics.map((topic: string, index: number) => { + // Count items that match this topic (case-insensitive) const matchingCount = items.filter((item: any) => - item?.topics?.some((topic: string) => - topic?.toLowerCase() === keyword?.toLowerCase() + item?.topics?.some((itemTopic: string) => + itemTopic?.toLowerCase().trim() === topic?.toLowerCase().trim() ) ).length; return ( ); })} +
+
+ )} + + {activeFilter !== 'all' && filteredItems.length === 0 && ( +
+

No case studies found for topic "{activeFilter}"

+
)} diff --git a/src/theme/conversion/components/cdcasestudies/sanity-query.ts b/src/theme/conversion/components/cdcasestudies/sanity-query.ts index 294e6af..c0bd0df 100644 --- a/src/theme/conversion/components/cdcasestudies/sanity-query.ts +++ b/src/theme/conversion/components/cdcasestudies/sanity-query.ts @@ -14,7 +14,7 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str sortOrder title subtitle - keywords + topics items { title description @@ -35,7 +35,7 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str selectableVariant title subtitle - keywords + topics items { title description @@ -62,7 +62,7 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str sortOrder title subtitle - keywords + topics items { title description @@ -83,7 +83,7 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str selectableVariant title subtitle - keywords + topics items { title description diff --git a/src/theme/conversion/components/cdcasestudies/sanity-schema.ts b/src/theme/conversion/components/cdcasestudies/sanity-schema.ts index 16c73a9..434894a 100644 --- a/src/theme/conversion/components/cdcasestudies/sanity-schema.ts +++ b/src/theme/conversion/components/cdcasestudies/sanity-schema.ts @@ -80,11 +80,11 @@ export default defineType({ description: 'Subtitle or tagline for the case studies section.' }), defineField({ - name: 'keywords', - title: 'Keywords', + name: 'topics', + title: 'Topics', type: 'array', of: [{ type: 'string' }], - description: 'Keywords displayed at the top of the case studies section', + description: 'Topics used for filtering case studies', options: { layout: 'tags' }