Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand All @@ -15,6 +16,8 @@ export default function CdcasestudiesUI(dynamicComponent: ViewComponentProps) {
switch (variant) {
case "xDemo":
return <DemoVariant matchingData={matchingData} {...dynamicComponent} />;
case "ourWork":
return <OurWorkVariant matchingData={matchingData} {...dynamicComponent} />;
default:
return <CdcasestudiesDefaultVariant matchingData={matchingData} {...dynamicComponent} />;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
"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";

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 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<string>('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;
}

const filtered = items.filter((item: any) => {
const itemTopics = item?.topics || [];
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 = (topic: string) => {
console.log('DEBUG: handleFilterClick called with topic:', topic);
setActiveFilter(topic);
console.log('DEBUG: activeFilter set to:', topic);
};

return (
<section className="bg-white px-6 md:px-12 lg:px-20 py-24">
<div className="max-w-7xl mx-auto">
<div className="flex flex-col md:flex-row md:items-center md:justify-between mb-16">
<h2 className="text-[#0C093F] font-extrabold text-2xl md:text-3xl mb-6 md:mb-0">
{title}
</h2>
<div className="max-w-xl">
<p className="text-[#0C093F] text-2xl">
{subtitle}
</p>
</div>
</div>

{availableTopics && availableTopics.length > 0 && (
<div className="mb-16">
<div className="flex flex-wrap gap-4 mb-4">
<button
onClick={() => handleFilterClick('all')}
className={`px-4 py-2 rounded-full text-sm font-medium transition-all duration-300 ${
activeFilter === 'all'
? 'bg-[#FF6B35] text-white shadow-lg'
: 'bg-[#fff6ec] text-[#0C093F] hover:bg-[#FF6B35]/10'
}`}
>
All ({items.length})
</button>
{availableTopics.map((topic: string, index: number) => {
// Count items that match this topic (case-insensitive)
const matchingCount = items.filter((item: any) =>
item?.topics?.some((itemTopic: string) =>
itemTopic?.toLowerCase().trim() === topic?.toLowerCase().trim()
)
).length;

return (
<button
key={index}
onClick={() => handleFilterClick(topic)}
className={`px-4 py-2 rounded-full text-sm font-medium transition-all duration-300 ${
activeFilter === topic
? 'bg-[#FF6B35] text-white shadow-lg'
: 'bg-[#fff6ec] text-[#0C093F] hover:bg-[#FF6B35]/10'
}`}
>
{topic} ({matchingCount})
</button>
);
})}
</div>
</div>
)}

{activeFilter !== 'all' && filteredItems.length === 0 && (
<div className="text-center py-12">
<p className="text-[#0C093F] text-lg mb-4">No case studies found for topic "{activeFilter}"</p>
<button
onClick={() => handleFilterClick('all')}
className="px-6 py-3 bg-[#FF6B35] text-white rounded-full hover:bg-[#FF6B35]/90 transition"
>
Show All Case Studies
</button>
</div>
)}

<div className="grid md:grid-cols-2 gap-16">
{filteredItems.map((item: any, index: number) => {
const { hasImage, imageLocation } = getCmsImage(item);
const finalImage = hasImage ? imageLocation : item?.imageUrl;

return (
<a
key={index}
href={item?.link || "#"}
className="group relative h-[400px] rounded-3xl overflow-hidden shadow-2xl"
>
<div className="absolute inset-0">
<img
src={finalImage || "/case-studies/befitfood.jpg"}
alt={item?.title || "Case Study"}
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/50 via-black/40 to-transparent " />
</div>

<div className="absolute bottom-0 left-0 right-0 p-8">
{item?.topics && item.topics.length > 0 && (
<div className="flex flex-wrap gap-2">
{item.topics.map((topic: string, topicIndex: number) => (
<span
key={topicIndex}
className="py-3 text-white text-xs font-medium"
>
{topic.toUpperCase()}
</span>
))}
</div>
)}
<h5 className="text-white font-extrabold text-2xl mb-4">{item?.title || "Be Fit Food"}</h5>
</div>
</a>
);
})}
</div>

{button?.text && button?.link && (
<div className="mt-12 text-center">
<a
href={button.link}
className="inline-flex items-center gap-2 px-6 py-3 bg-[#0C093F] text-white rounded-full hover:bg-opacity-90 transition"
>
{button.text}
</a>
</div>
)}
</div>
</section>
);
}
8 changes: 8 additions & 0 deletions src/theme/conversion/components/cdcasestudies/sanity-query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str
sortOrder
title
subtitle
topics
items {
title
description
link
topics
image {
asset {
url
Expand All @@ -33,10 +35,12 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str
selectableVariant
title
subtitle
topics
items {
title
description
link
topics
image {
asset {
url
Expand All @@ -58,10 +62,12 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str
sortOrder
title
subtitle
topics
items {
title
description
link
topics
image {
asset {
url
Expand All @@ -77,10 +83,12 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str
selectableVariant
title
subtitle
topics
items {
title
description
link
topics
image {
asset {
url
Expand Down
42 changes: 42 additions & 0 deletions src/theme/conversion/components/cdcasestudies/sanity-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
})
]
})
Expand All @@ -46,6 +56,7 @@ export default defineType({
options: {
list: [
{ title: 'Default', value: 'default' },
{ title: 'Our Work', value: 'ourWork' },
{ title: 'Demo', value: 'xDemo' },
],
},
Expand All @@ -68,6 +79,16 @@ export default defineType({
type: 'string',
description: 'Subtitle or tagline for the case studies section.'
}),
defineField({
name: 'topics',
title: 'Topics',
type: 'array',
of: [{ type: 'string' }],
description: 'Topics used for filtering case studies',
options: {
layout: 'tags'
}
}),
defineField({
name: 'items',
title: 'Case Studies',
Expand All @@ -92,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: {
Expand Down
2 changes: 0 additions & 2 deletions src/theme/conversion/components/cdfooter/sanity-mapping.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +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);

return matchingData
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ 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"
import IntroductionVariant from "./variants/IntroductionVariant"
import TitleOnlyVariant from "./variants/TitleOnlyVariant"


export const log = getLogger("default.components.heartcore.template.variants");

export default function TemplateUI(dynamicComponent: ViewComponentProps) {
Expand All @@ -17,6 +19,8 @@ export default function TemplateUI(dynamicComponent: ViewComponentProps) {
switch (variant) {
case "xDemo":
return <DemoVariant matchingData={matchingData} {...dynamicComponent} />;
case "caseStudy":
return <CaseStudyVariant matchingData={matchingData} {...dynamicComponent} />;
case "title-only":
return <TitleOnlyVariant matchingData={matchingData} {...dynamicComponent} />;
case "introduction":
Expand Down
Loading
Loading