From ac01e4109e86e957b6c2e85ca22fdff55e8f7d12 Mon Sep 17 00:00:00 2001 From: Steven Cao Date: Mon, 30 Jun 2025 21:59:10 +0700 Subject: [PATCH 1/6] HEAD-124: update sanity schema service page --- .../cdserviceintro/sanity-schema.ts | 26 +++-- .../cdserviceofferings/sanity-schema.ts | 110 ++++++++++-------- .../cdservicestats/sanity-schema.ts | 40 ++++--- .../components/sitemap/sanity-query.ts | 1 + 4 files changed, 99 insertions(+), 78 deletions(-) diff --git a/src/theme/conversion/components/cdserviceintro/sanity-schema.ts b/src/theme/conversion/components/cdserviceintro/sanity-schema.ts index 419c1a6..2966ee9 100644 --- a/src/theme/conversion/components/cdserviceintro/sanity-schema.ts +++ b/src/theme/conversion/components/cdserviceintro/sanity-schema.ts @@ -1,10 +1,11 @@ import { defineField, defineType } from 'sanity' import { DocumentTextIcon } from '@sanity/icons' +// Define a separate document type for cdserviceintro export default defineType({ name: 'cdserviceintro', title: 'Service Introduction (CD)', - type: 'object', + type: 'document', icon: DocumentTextIcon, fields: [ defineField({ @@ -61,8 +62,14 @@ export default defineType({ fields: [ { name: 'href', - type: 'url', + type: 'string', title: 'URL' + }, + { + name: 'blank', + type: 'boolean', + title: 'Open in new tab', + initialValue: false } ] } @@ -76,14 +83,15 @@ export default defineType({ name: 'sortOrder', title: 'Sort Order', type: 'number' - }), - defineField({ - name: 'globalComponentSource', - title: 'Global Component Source', - type: 'reference', - to: [{ type: 'cdserviceintro' }], - description: 'Select a global re-usable service introduction.' }) + // Temporarily removing the globalComponentSource field as it may be causing circular references + // defineField({ + // name: 'globalComponentSource', + // title: 'Global Component Source', + // type: 'reference', + // to: [{ type: 'cdserviceintro' }], + // description: 'Select a global re-usable service introduction.' + // }) ], preview: { select: { diff --git a/src/theme/conversion/components/cdserviceofferings/sanity-schema.ts b/src/theme/conversion/components/cdserviceofferings/sanity-schema.ts index a94a148..79b8416 100644 --- a/src/theme/conversion/components/cdserviceofferings/sanity-schema.ts +++ b/src/theme/conversion/components/cdserviceofferings/sanity-schema.ts @@ -1,11 +1,66 @@ import { defineField, defineType } from 'sanity' -import { TagIcon } from '@sanity/icons' +import { EyeOpenIcon } from '@sanity/icons' + +// Define a separate type for the offering object +export const cdserviceOfferingItem = defineType({ + name: 'cdserviceOfferingItem', + title: 'Service Offering Item', + type: 'object', + fields: [ + defineField({ + name: 'title', + title: 'Title', + type: 'string', + description: 'The name of the service offering' + }), + defineField({ + name: 'icon', + title: 'Icon', + type: 'string', + description: 'The icon name to display for this offering' + }), + defineField({ + name: 'id', + title: 'ID', + type: 'slug', + description: 'A unique identifier for this service offering', + }) + ] +}) + +// Define a separate type for the service detail object +export const cdserviceDetailItem = defineType({ + name: 'cdserviceDetailItem', + title: 'Service Detail Item', + type: 'object', + fields: [ + defineField({ + name: 'id', + title: 'ID', + type: 'string', + description: 'A unique identifier for this service detail (should match an offering ID)' + }), + defineField({ + name: 'title', + title: 'Title', + type: 'string', + description: 'The title of the service detail' + }), + defineField({ + name: 'content', + title: 'Content', + type: 'array', + of: [{ type: 'block' }], + description: 'The rich text content for this service detail' + }) + ] +}) export default defineType({ name: 'cdserviceofferings', title: 'Service Offerings (CD)', type: 'object', - icon: TagIcon, + icon: EyeOpenIcon, fields: [ defineField({ name: 'selectableVariant', @@ -34,30 +89,7 @@ export default defineType({ title: 'Service Offerings', type: 'array', of: [ - { - type: 'object', - title: 'Offering', - fields: [ - { - name: 'title', - title: 'Title', - type: 'string', - description: 'The name of the service offering' - }, - { - name: 'icon', - title: 'Icon', - type: 'string', - description: 'The icon name to display for this offering' - }, - { - name: 'id', - title: 'ID', - type: 'slug', - description: 'A unique identifier for this service offering', - } - ] - } + { type: 'cdserviceOfferingItem' } ], }), defineField({ @@ -65,31 +97,7 @@ export default defineType({ title: 'Service Details', type: 'array', of: [ - { - type: 'object', - title: 'Service Detail', - fields: [ - { - name: 'id', - title: 'ID', - type: 'string', - description: 'A unique identifier for this service detail (should match an offering ID)' - }, - { - name: 'title', - title: 'Title', - type: 'string', - description: 'The title of the service detail' - }, - { - name: 'content', - title: 'Content', - type: 'array', - of: [{ type: 'block' }], - description: 'The rich text content for this service detail' - } - ] - } + { type: 'cdserviceDetailItem' } ], description: 'The detailed content for each service offering' }), diff --git a/src/theme/conversion/components/cdservicestats/sanity-schema.ts b/src/theme/conversion/components/cdservicestats/sanity-schema.ts index 62ccd54..0751039 100644 --- a/src/theme/conversion/components/cdservicestats/sanity-schema.ts +++ b/src/theme/conversion/components/cdservicestats/sanity-schema.ts @@ -1,6 +1,27 @@ import { defineField, defineType } from 'sanity' import { BarChartIcon } from '@sanity/icons' +// Define a separate type for the statistic object +export const cdserviceStatItem = defineType({ + name: 'cdserviceStatItem', + title: 'Service Statistic Item', + type: 'object', + fields: [ + defineField({ + name: 'value', + title: 'Value', + type: 'string', + description: 'The statistical value to display (e.g., "93%", "5.14")' + }), + defineField({ + name: 'description', + title: 'Description', + type: 'string', + description: 'The description of what the statistic represents' + }) + ] +}) + export default defineType({ name: 'cdservicestats', title: 'Service Statistics (CD)', @@ -22,24 +43,7 @@ export default defineType({ title: 'Statistics', type: 'array', of: [ - { - type: 'object', - title: 'Statistic', - fields: [ - { - name: 'value', - title: 'Value', - type: 'string', - description: 'The statistical value to display (e.g., "93%", "5.14")' - }, - { - name: 'description', - title: 'Description', - type: 'string', - description: 'The description of what the statistic represents' - } - ] - } + { type: 'cdserviceStatItem' } ], }), defineField({ diff --git a/src/theme/default/components/sitemap/sanity-query.ts b/src/theme/default/components/sitemap/sanity-query.ts index dcaff75..aaba696 100644 --- a/src/theme/default/components/sitemap/sanity-query.ts +++ b/src/theme/default/components/sitemap/sanity-query.ts @@ -26,6 +26,7 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails) { } components { __typename + _type } } } From cf0c0c35ed7edc8ae22c6a88d187235c4d632ced Mon Sep 17 00:00:00 2001 From: Steven Cao Date: Tue, 1 Jul 2025 23:35:22 +0700 Subject: [PATCH 2/6] HEAD-124: update sanity schema service page --- src/theme/default/components/sitemap/sanity-query.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/theme/default/components/sitemap/sanity-query.ts b/src/theme/default/components/sitemap/sanity-query.ts index aaba696..dcaff75 100644 --- a/src/theme/default/components/sitemap/sanity-query.ts +++ b/src/theme/default/components/sitemap/sanity-query.ts @@ -26,7 +26,6 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails) { } components { __typename - _type } } } From e44611e123502ba65191a58310107ddaf0ba5816 Mon Sep 17 00:00:00 2001 From: Steven Cao Date: Thu, 3 Jul 2025 04:26:45 +0700 Subject: [PATCH 3/6] feat(HEAD-124): update schema and component for /services/search-engine-optimisation page --- package.json | 3 +- .../components/cdfaqs/classification.ts | 1 + .../components/index.tsx | 10 +- .../variants/CdFaqsDefaultVariant.tsx | 79 +++++++++++ .../{cdservicestats => cdfaqs}/index.ts | 4 +- .../sanity-mapping.ts | 4 +- .../sanity-query.ts | 64 +++------ .../components/cdfaqs/sanity-schema.ts | 127 ++++++++++++++++++ .../{cdservicestats => cdfaqs}/view.tsx | 4 +- .../cdintroduction/classification.ts | 1 + .../components/index.tsx | 10 +- .../variants/CdIntroductionDefaultVariant.tsx | 24 ++++ .../index.ts | 4 +- .../sanity-mapping.ts | 4 +- .../sanity-query.ts | 20 ++- .../sanity-schema.ts | 40 +++--- .../view.tsx | 4 +- .../components/cdnav/sanity-schema.ts | 21 ++- .../cdserviceintro/classification.ts | 1 - .../variants/cdserviceintroDefaultVariant.tsx | 27 ---- .../cdserviceofferings/classification.ts | 1 - .../cdserviceofferingsDefaultVariant.tsx | 102 -------------- .../cdserviceofferings/sanity-schema.ts | 122 ----------------- .../cdservicestats/classification.ts | 1 - .../components/cdstatistics/classification.ts | 1 + .../components/index.tsx | 10 +- .../variants/CdStatisticsDefaultVariant.tsx} | 10 +- .../index.ts | 4 +- .../sanity-mapping.ts | 6 +- .../sanity-query.ts | 8 +- .../sanity-schema.ts | 33 +++-- .../view.tsx | 4 +- src/theme/conversion/index.ts | 6 +- .../structures/servicepage/sanity-mapping.ts | 97 ------------- .../structures/servicepage/sanity-query.ts | 117 ---------------- src/theme/conversion/styles/globals.css | 23 ++-- src/theme/conversion/tailwind.config.js | 3 +- 37 files changed, 382 insertions(+), 618 deletions(-) create mode 100644 src/theme/conversion/components/cdfaqs/classification.ts rename src/theme/conversion/components/{cdserviceintro => cdfaqs}/components/index.tsx (53%) create mode 100644 src/theme/conversion/components/cdfaqs/components/variants/CdFaqsDefaultVariant.tsx rename src/theme/conversion/components/{cdservicestats => cdfaqs}/index.ts (76%) rename src/theme/conversion/components/{cdserviceofferings => cdfaqs}/sanity-mapping.ts (86%) rename src/theme/conversion/components/{cdserviceofferings => cdfaqs}/sanity-query.ts (50%) create mode 100644 src/theme/conversion/components/cdfaqs/sanity-schema.ts rename src/theme/conversion/components/{cdservicestats => cdfaqs}/view.tsx (67%) create mode 100644 src/theme/conversion/components/cdintroduction/classification.ts rename src/theme/conversion/components/{cdservicestats => cdintroduction}/components/index.tsx (62%) create mode 100644 src/theme/conversion/components/cdintroduction/components/variants/CdIntroductionDefaultVariant.tsx rename src/theme/conversion/components/{cdserviceintro => cdintroduction}/index.ts (76%) rename src/theme/conversion/components/{cdserviceintro => cdintroduction}/sanity-mapping.ts (87%) rename src/theme/conversion/components/{cdserviceintro => cdintroduction}/sanity-query.ts (75%) rename src/theme/conversion/components/{cdserviceintro => cdintroduction}/sanity-schema.ts (68%) rename src/theme/conversion/components/{cdserviceintro => cdintroduction}/view.tsx (67%) delete mode 100644 src/theme/conversion/components/cdserviceintro/classification.ts delete mode 100644 src/theme/conversion/components/cdserviceintro/components/variants/cdserviceintroDefaultVariant.tsx delete mode 100644 src/theme/conversion/components/cdserviceofferings/classification.ts delete mode 100644 src/theme/conversion/components/cdserviceofferings/components/variants/cdserviceofferingsDefaultVariant.tsx delete mode 100644 src/theme/conversion/components/cdserviceofferings/sanity-schema.ts delete mode 100644 src/theme/conversion/components/cdservicestats/classification.ts create mode 100644 src/theme/conversion/components/cdstatistics/classification.ts rename src/theme/conversion/components/{cdserviceofferings => cdstatistics}/components/index.tsx (52%) rename src/theme/conversion/components/{cdservicestats/components/variants/cdservicestatsDefaultVariant.tsx => cdstatistics/components/variants/CdStatisticsDefaultVariant.tsx} (72%) rename src/theme/conversion/components/{cdserviceofferings => cdstatistics}/index.ts (74%) rename src/theme/conversion/components/{cdservicestats => cdstatistics}/sanity-mapping.ts (78%) rename src/theme/conversion/components/{cdservicestats => cdstatistics}/sanity-query.ts (89%) rename src/theme/conversion/components/{cdservicestats => cdstatistics}/sanity-schema.ts (70%) rename src/theme/conversion/components/{cdserviceofferings => cdstatistics}/view.tsx (65%) delete mode 100644 src/theme/conversion/structures/servicepage/sanity-mapping.ts delete mode 100644 src/theme/conversion/structures/servicepage/sanity-query.ts diff --git a/package.json b/package.json index be0b5d0..a3d5b2b 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "test": "jest", "chromatic": "npx chromatic --project-token=chpt_219e0bf9f15c669", "postbuild": "find dist -name '*.js' -exec sed -i 's/\\.css\\.js/\\.css/g' {} +" - }, + }, "dependencies": { "@babel/cli": "7.23.9", "@babel/core": "7.24.0", @@ -48,6 +48,7 @@ "@fortawesome/free-solid-svg-icons": "^6.7.2", "@fortawesome/react-fontawesome": "^0.2.2", "@portabletext/react": "^3.0.0", + "@portabletext/to-html": "^2.0.14", "@radix-ui/colors": "^0.1.8", "@radix-ui/react-accessible-icon": "^1.1.0", "@radix-ui/react-accordion": "^1.2.0", diff --git a/src/theme/conversion/components/cdfaqs/classification.ts b/src/theme/conversion/components/cdfaqs/classification.ts new file mode 100644 index 0000000..7ae186a --- /dev/null +++ b/src/theme/conversion/components/cdfaqs/classification.ts @@ -0,0 +1 @@ +export default ["cdfaqs", "faqs", "cdfaqs"] diff --git a/src/theme/conversion/components/cdserviceintro/components/index.tsx b/src/theme/conversion/components/cdfaqs/components/index.tsx similarity index 53% rename from src/theme/conversion/components/cdserviceintro/components/index.tsx rename to src/theme/conversion/components/cdfaqs/components/index.tsx index 7223819..bd1c770 100644 --- a/src/theme/conversion/components/cdserviceintro/components/index.tsx +++ b/src/theme/conversion/components/cdfaqs/components/index.tsx @@ -2,19 +2,19 @@ import React from 'react'; import { componentBoilerPlate } from "@conversiondigital/headless-basics-data/src/component-tools/componentBoilerPlate"; import { ViewComponentProps } from "@conversiondigital/headless-basics-data/src/interfaces"; import { getLogger, logPrefix } from "@conversiondigital/headless-basics-data/src"; -import CdserviceintroDefaultVariant from './variants/cdserviceintroDefaultVariant'; +import CdFaqsDefaultVariant from './variants/CdFaqsDefaultVariant'; -export const log = getLogger("conversion.components.cdserviceintro"); +export const log = getLogger("conversion.components.cdfaqs"); -export default function CdserviceintroUI(dynamicComponent: ViewComponentProps) { +export default function CdFaqsUI(dynamicComponent: ViewComponentProps) { const { variant, matchingData } = componentBoilerPlate(dynamicComponent); if (!matchingData) return null; - log.trace(`${logPrefix()} cdserviceintroUI started, matchingData: ${JSON.stringify(matchingData)}`); + log.trace(`${logPrefix()} CdFaqsUI started, matchingData: ${JSON.stringify(matchingData)}`); switch (variant) { case 'default': default: - return ; + return ; } } \ No newline at end of file diff --git a/src/theme/conversion/components/cdfaqs/components/variants/CdFaqsDefaultVariant.tsx b/src/theme/conversion/components/cdfaqs/components/variants/CdFaqsDefaultVariant.tsx new file mode 100644 index 0000000..e10732a --- /dev/null +++ b/src/theme/conversion/components/cdfaqs/components/variants/CdFaqsDefaultVariant.tsx @@ -0,0 +1,79 @@ +"use client"; +import { StandardComponentProps } from "@conversiondigital/headless-basics-components/src/interfaces/standardComponentProps"; +import { toHTML } from "@portabletext/to-html"; +import React, { useState } from "react"; + +interface FaqItem { + title: string; + richtextRaw: any[]; +} + +const CdFaqsDefaultVariant: React.FC = ({ matchingData }) => { + const [indexActive, setIndexActive] = useState(0); + + const title = matchingData?.title || ""; + const description = matchingData?.description || ""; + const items = matchingData?.items || []; + const uniqueId = matchingData?._key || ""; + + return ( +
+
+
+
+

{title}

+

{description}

+
+
+
+ {items.map((item: FaqItem, index: number) => { + return ( +
setIndexActive(index)} + role="button" + tabIndex={0} + aria-pressed={index === indexActive} + > +

+ {item.title} +

+ + + +
+ ); + })} +
+
+ {items.map((item: FaqItem, index: number) => { + return ( +
+
+
+ ); + })} +
+
+
+
+
+ ); +}; + +export default CdFaqsDefaultVariant; diff --git a/src/theme/conversion/components/cdservicestats/index.ts b/src/theme/conversion/components/cdfaqs/index.ts similarity index 76% rename from src/theme/conversion/components/cdservicestats/index.ts rename to src/theme/conversion/components/cdfaqs/index.ts index e14148a..0d09c5d 100644 --- a/src/theme/conversion/components/cdservicestats/index.ts +++ b/src/theme/conversion/components/cdfaqs/index.ts @@ -2,10 +2,10 @@ import { getLogger, getThemeConfig } from "@conversiondigital/headless-basics-da import { View } from "./view"; import { ThemeConfig } from "@conversiondigital/headless-basics-data/src/interfaces" -getLogger("theme.components.cdservicestats") +getLogger("theme.components.cdfaqs") async function getConfig(): Promise { - const config = await getThemeConfig('cdservicestats'); + const config = await getThemeConfig('cdfaqs'); config.view = View; return config; } diff --git a/src/theme/conversion/components/cdserviceofferings/sanity-mapping.ts b/src/theme/conversion/components/cdfaqs/sanity-mapping.ts similarity index 86% rename from src/theme/conversion/components/cdserviceofferings/sanity-mapping.ts rename to src/theme/conversion/components/cdfaqs/sanity-mapping.ts index f32e369..75f5ebc 100644 --- a/src/theme/conversion/components/cdserviceofferings/sanity-mapping.ts +++ b/src/theme/conversion/components/cdfaqs/sanity-mapping.ts @@ -1,7 +1,7 @@ import { getLogger, logPrefix, PageAndSingleComponentDetails } from "@conversiondigital/headless-basics-data/src" import { extractComponentsFromSanityData } from "@conversiondigital/headless-basics-data/src/cms/sanity/sanityMappingUtils" -export const log = getLogger("conversion.components.sanity.cdserviceofferings.mapping") +export const log = getLogger("conversion.components.sanity.cdfaqs.mapping") export async function mapIdentifierData(pageAndComponentCombo: PageAndSingleComponentDetails) { log.trace( @@ -10,7 +10,7 @@ export async function mapIdentifierData(pageAndComponentCombo: PageAndSingleComp const content = pageAndComponentCombo?.component?.data - const matchingData = extractComponentsFromSanityData(content, "cdserviceofferings", log); + const matchingData = extractComponentsFromSanityData(content, "cdfaqs", log); return matchingData } diff --git a/src/theme/conversion/components/cdserviceofferings/sanity-query.ts b/src/theme/conversion/components/cdfaqs/sanity-query.ts similarity index 50% rename from src/theme/conversion/components/cdserviceofferings/sanity-query.ts rename to src/theme/conversion/components/cdfaqs/sanity-query.ts index e77ef55..efd2251 100644 --- a/src/theme/conversion/components/cdserviceofferings/sanity-query.ts +++ b/src/theme/conversion/components/cdfaqs/sanity-query.ts @@ -2,28 +2,20 @@ import { PageAndSingleComponentDetails } from "@conversiondigital/headless-basic export function query(pageAndComponentCombo: PageAndSingleComponentDetails): string { return ` - query GetCdserviceofferingsBySlug($slug: String!) { + query GetCdFaqsBySlug($slug: String!) { allPage(where: { slug: { current: { eq: $slug } } }) { components { __typename - ... on Cdserviceofferings { + ... on Cdfaqs { __typename _key _type selectableVariant title - intro - offerings { + description + items { title - icon - id { - current - } - } - services { - id - title - content + richtextRaw } sortOrder globalComponentSource { @@ -31,44 +23,28 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str _key _type title - intro - offerings { - title - icon - id { - current - } - } - services { - id + description + items { title - content + richtextRaw } } } } } - allServicePage { + allHomepage { components { __typename - ... on Cdserviceofferings { + ... on Cdfaqs { __typename _key _type selectableVariant title - intro - offerings { + description + items { title - icon - id { - current - } - } - services { - id - title - content + richtextRaw } sortOrder globalComponentSource { @@ -76,18 +52,10 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str _key _type title - intro - offerings { - title - icon - id { - current - } - } - services { - id + description + items { title - content + richtextRaw } } } diff --git a/src/theme/conversion/components/cdfaqs/sanity-schema.ts b/src/theme/conversion/components/cdfaqs/sanity-schema.ts new file mode 100644 index 0000000..158df17 --- /dev/null +++ b/src/theme/conversion/components/cdfaqs/sanity-schema.ts @@ -0,0 +1,127 @@ +import { defineField, defineType } from 'sanity' +import { EyeOpenIcon } from '@sanity/icons' + +// Define a separate type for the FAQ item object +export const cdFaqItem = defineType({ + name: 'cdFaqItem', + title: 'FAQ Item', + type: 'object', + fields: [ + defineField({ + name: 'title', + title: 'Title', + type: 'string', + description: 'The title of the FAQ item' + }), + defineField({ + name: 'richtext', + title: 'Description', + type: 'array', + of: [ + { + type: 'block', + styles: [ + {title: 'Normal', value: 'normal'}, + {title: 'H2', value: 'h2'}, + {title: 'H3', value: 'h3'}, + {title: 'H4', value: 'h4'}, + {title: 'Quote', value: 'blockquote'} + ], + lists: [ + {title: 'Bullet', value: 'bullet'}, + {title: 'Numbered', value: 'number'} + ], + marks: { + decorators: [ + {title: 'Strong', value: 'strong'}, + {title: 'Emphasis', value: 'em'}, + {title: 'Underline', value: 'underline'} + ], + annotations: [ + { + name: 'link', + type: 'object', + title: 'Link', + fields: [ + { + name: 'href', + type: 'string', + title: 'URL' + }, + { + name: 'blank', + type: 'boolean', + title: 'Open in new tab', + initialValue: false + } + ] + } + ] + } + } + ], + description: 'The rich text content for the FAQ item' + }) + ], + preview: { + select: { + title: 'title' + } + } +}) + +export default defineType({ + name: 'cdfaqs', + title: 'FAQs (CD)', + type: 'object', + icon: EyeOpenIcon, + fields: [ + defineField({ + name: 'selectableVariant', + title: 'Selectable Variant', + type: 'string', + options: { + list: [ + { title: 'Default', value: 'default' } + ] + } + }), + defineField({ + name: 'title', + title: 'Title', + type: 'string', + description: 'The main title for the FAQs section (e.g., "Frequently Asked Questions")' + }), + defineField({ + name: 'description', + title: 'Introduction Text', + type: 'text', + description: 'A brief description of the FAQs' + }), + defineField({ + name: 'items', + title: 'FAQ Items', + type: 'array', + of: [ + { type: 'cdFaqItem' } + ], + }), + defineField({ + name: 'sortOrder', + title: 'Sort Order', + type: 'number' + }), + defineField({ + name: 'globalComponentSource', + title: 'Global Component Source', + type: 'reference', + to: [{ type: 'cdfaqs' }], + description: 'Select a global re-usable FAQs component.' + }) + ], + preview: { + select: { + title: 'title' + } + } +}) \ No newline at end of file diff --git a/src/theme/conversion/components/cdservicestats/view.tsx b/src/theme/conversion/components/cdfaqs/view.tsx similarity index 67% rename from src/theme/conversion/components/cdservicestats/view.tsx rename to src/theme/conversion/components/cdfaqs/view.tsx index 39aaf49..54cae12 100644 --- a/src/theme/conversion/components/cdservicestats/view.tsx +++ b/src/theme/conversion/components/cdfaqs/view.tsx @@ -2,10 +2,10 @@ import { ViewComponentProps } from "@conversiondigital/headless-basics-data/src"; import dynamic from "next/dynamic" -const CdservicestatsUI = dynamic(() => import("./components"), { +const CdFaqsUI = dynamic(() => import("./components"), { loading: () => (

Loading...

) }); export async function View(dynamicComponent: ViewComponentProps) { - return + return } diff --git a/src/theme/conversion/components/cdintroduction/classification.ts b/src/theme/conversion/components/cdintroduction/classification.ts new file mode 100644 index 0000000..f479165 --- /dev/null +++ b/src/theme/conversion/components/cdintroduction/classification.ts @@ -0,0 +1 @@ +export default ["cdintroduction", "introduction", "introduction-cd"] \ No newline at end of file diff --git a/src/theme/conversion/components/cdservicestats/components/index.tsx b/src/theme/conversion/components/cdintroduction/components/index.tsx similarity index 62% rename from src/theme/conversion/components/cdservicestats/components/index.tsx rename to src/theme/conversion/components/cdintroduction/components/index.tsx index 4dd3aca..fb690d2 100644 --- a/src/theme/conversion/components/cdservicestats/components/index.tsx +++ b/src/theme/conversion/components/cdintroduction/components/index.tsx @@ -2,19 +2,19 @@ import React from 'react'; import { componentBoilerPlate } from "@conversiondigital/headless-basics-data/src/component-tools/componentBoilerPlate"; import { ViewComponentProps } from "@conversiondigital/headless-basics-data/src/interfaces"; import { getLogger, logPrefix } from "@conversiondigital/headless-basics-data/src"; -import CdservicestatsDefaultVariant from './variants/cdservicestatsDefaultVariant'; +import CdIntroductionDefaultVariant from './variants/CdIntroductionDefaultVariant'; -export const log = getLogger("conversion.components.cdservicestats"); +export const log = getLogger("conversion.components.cdintroduction"); -export default function CdservicestatsUI(dynamicComponent: ViewComponentProps) { +export default function CdIntroductionUI(dynamicComponent: ViewComponentProps) { const { variant, matchingData } = componentBoilerPlate(dynamicComponent); if (!matchingData) return null; - log.trace(`${logPrefix()} cdservicestatsUI started, matchingData: ${JSON.stringify(matchingData)}`); + log.trace(`${logPrefix()} CdIntroductionUI started, matchingData: ${JSON.stringify(matchingData)}`); switch (variant) { case 'default': default: - return ; + return ; } } \ No newline at end of file diff --git a/src/theme/conversion/components/cdintroduction/components/variants/CdIntroductionDefaultVariant.tsx b/src/theme/conversion/components/cdintroduction/components/variants/CdIntroductionDefaultVariant.tsx new file mode 100644 index 0000000..82d6c11 --- /dev/null +++ b/src/theme/conversion/components/cdintroduction/components/variants/CdIntroductionDefaultVariant.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { StandardComponentProps } from "@conversiondigital/headless-basics-components/src/interfaces/standardComponentProps"; +import {toHTML} from '@portabletext/to-html' + +const CdIntroductionDefaultVariant: React.FC = ({ matchingData }) => { + const title = matchingData?.title || ''; + const content = matchingData?.richtextRaw || ''; + + return ( +
+
+
+

{title}

+
+
+
+
+ ); +}; + +export default CdIntroductionDefaultVariant; \ No newline at end of file diff --git a/src/theme/conversion/components/cdserviceintro/index.ts b/src/theme/conversion/components/cdintroduction/index.ts similarity index 76% rename from src/theme/conversion/components/cdserviceintro/index.ts rename to src/theme/conversion/components/cdintroduction/index.ts index 8cf23bb..393a44c 100644 --- a/src/theme/conversion/components/cdserviceintro/index.ts +++ b/src/theme/conversion/components/cdintroduction/index.ts @@ -2,10 +2,10 @@ import { getLogger, getThemeConfig } from "@conversiondigital/headless-basics-da import { View } from "./view"; import { ThemeConfig } from "@conversiondigital/headless-basics-data/src/interfaces" -getLogger("theme.components.cdserviceintro") +getLogger("theme.components.cdintroduction") async function getConfig(): Promise { - const config = await getThemeConfig('cdserviceintro'); + const config = await getThemeConfig('cdintroduction'); config.view = View; return config; } diff --git a/src/theme/conversion/components/cdserviceintro/sanity-mapping.ts b/src/theme/conversion/components/cdintroduction/sanity-mapping.ts similarity index 87% rename from src/theme/conversion/components/cdserviceintro/sanity-mapping.ts rename to src/theme/conversion/components/cdintroduction/sanity-mapping.ts index e7b9f29..240dbb8 100644 --- a/src/theme/conversion/components/cdserviceintro/sanity-mapping.ts +++ b/src/theme/conversion/components/cdintroduction/sanity-mapping.ts @@ -1,14 +1,14 @@ import { getLogger, logPrefix, PageAndSingleComponentDetails } from "@conversiondigital/headless-basics-data/src" import { extractComponentsFromSanityData } from "@conversiondigital/headless-basics-data/src/cms/sanity/sanityMappingUtils" -export const log = getLogger("conversion.components.sanity.cdserviceintro.mapping") +export const log = getLogger("conversion.components.sanity.cdintroduction.mapping") export async function mapIdentifierData(pageAndComponentCombo: PageAndSingleComponentDetails) { log.trace( `${logPrefix()}[${pageAndComponentCombo.component.identifier}][${pageAndComponentCombo.page.source}][${pageAndComponentCombo.page.preliminarySlug}] mapIdentifierData started, ${JSON.stringify(pageAndComponentCombo?.component?.data)}` ); const content = pageAndComponentCombo?.component?.data - const matchingData = extractComponentsFromSanityData(content, "cdserviceintro", log); + const matchingData = extractComponentsFromSanityData(content, "cdintroduction", log); return matchingData } \ No newline at end of file diff --git a/src/theme/conversion/components/cdserviceintro/sanity-query.ts b/src/theme/conversion/components/cdintroduction/sanity-query.ts similarity index 75% rename from src/theme/conversion/components/cdserviceintro/sanity-query.ts rename to src/theme/conversion/components/cdintroduction/sanity-query.ts index 260b921..3f2a4bd 100644 --- a/src/theme/conversion/components/cdserviceintro/sanity-query.ts +++ b/src/theme/conversion/components/cdintroduction/sanity-query.ts @@ -2,49 +2,45 @@ import { PageAndSingleComponentDetails } from "@conversiondigital/headless-basic export function query(pageAndComponentCombo: PageAndSingleComponentDetails): string { return ` - query GetCdserviceintroBySlug($slug: String!) { + query GetCdIntroductionBySlug($slug: String!) { allPage(where: { slug: { current: { eq: $slug } } }) { components { __typename - ... on Cdserviceintro { + ... on Cdintroduction { __typename _key _type selectableVariant - heading title - content + richtextRaw sortOrder globalComponentSource { __typename _key _type - heading title - content + richtextRaw } } } } - allServicePage { + allHomepage { components { __typename - ... on Cdserviceintro { + ... on Cdintroduction { __typename _key _type selectableVariant - heading title - content + richtextRaw sortOrder globalComponentSource { __typename _key _type - heading title - content + richtextRaw } } } diff --git a/src/theme/conversion/components/cdserviceintro/sanity-schema.ts b/src/theme/conversion/components/cdintroduction/sanity-schema.ts similarity index 68% rename from src/theme/conversion/components/cdserviceintro/sanity-schema.ts rename to src/theme/conversion/components/cdintroduction/sanity-schema.ts index 2966ee9..6842609 100644 --- a/src/theme/conversion/components/cdserviceintro/sanity-schema.ts +++ b/src/theme/conversion/components/cdintroduction/sanity-schema.ts @@ -1,10 +1,10 @@ import { defineField, defineType } from 'sanity' import { DocumentTextIcon } from '@sanity/icons' -// Define a separate document type for cdserviceintro +// Define a separate document type for cdintroduction export default defineType({ - name: 'cdserviceintro', - title: 'Service Introduction (CD)', + name: 'cdintroduction', + title: 'Introduction (CD)', type: 'document', icon: DocumentTextIcon, fields: [ @@ -18,21 +18,15 @@ export default defineType({ ] } }), - defineField({ - name: 'heading', - title: 'Heading', - type: 'string', - description: 'The main heading for the service introduction section' - }), defineField({ name: 'title', title: 'Title', type: 'string', - description: 'The secondary title for the service introduction section' + description: 'The secondary title for the introduction section' }), defineField({ - name: 'content', - title: 'Content', + name: 'richtext', + title: 'Rich Text', type: 'array', of: [ { @@ -77,26 +71,24 @@ export default defineType({ } } ], - description: 'The rich text content for the service introduction' + description: 'The rich text content for the introduction' }), defineField({ name: 'sortOrder', title: 'Sort Order', type: 'number' - }) - // Temporarily removing the globalComponentSource field as it may be causing circular references - // defineField({ - // name: 'globalComponentSource', - // title: 'Global Component Source', - // type: 'reference', - // to: [{ type: 'cdserviceintro' }], - // description: 'Select a global re-usable service introduction.' - // }) + }), + defineField({ + name: 'globalComponentSource', + title: 'Global Component Source', + type: 'reference', + to: [{ type: 'cdintroduction' }], + description: 'Select a global re-usable introduction.' + }), ], preview: { select: { - title: 'heading', - subtitle: 'title' + title: 'title', } } }) \ No newline at end of file diff --git a/src/theme/conversion/components/cdserviceintro/view.tsx b/src/theme/conversion/components/cdintroduction/view.tsx similarity index 67% rename from src/theme/conversion/components/cdserviceintro/view.tsx rename to src/theme/conversion/components/cdintroduction/view.tsx index 5fc6404..13c3448 100644 --- a/src/theme/conversion/components/cdserviceintro/view.tsx +++ b/src/theme/conversion/components/cdintroduction/view.tsx @@ -2,10 +2,10 @@ import { ViewComponentProps } from "@conversiondigital/headless-basics-data/src"; import dynamic from "next/dynamic" -const CdserviceintroUI = dynamic(() => import("./components"), { +const CdIntroductionUI = dynamic(() => import("./components"), { loading: () => (

Loading...

) }); export async function View(dynamicComponent: ViewComponentProps) { - return + return } \ No newline at end of file diff --git a/src/theme/conversion/components/cdnav/sanity-schema.ts b/src/theme/conversion/components/cdnav/sanity-schema.ts index 38e554e..6381457 100644 --- a/src/theme/conversion/components/cdnav/sanity-schema.ts +++ b/src/theme/conversion/components/cdnav/sanity-schema.ts @@ -1,6 +1,5 @@ import { defineField, defineType } from 'sanity' import { EyeOpenIcon } from '@sanity/icons' -import { linkItem } from '@conversiondigital/headless-basics-data/src/cms/sanity/sanityCommonSchema' // Define the dropdown menu type export const dropdownMenu = defineType({ @@ -22,6 +21,26 @@ export const dropdownMenu = defineType({ ] }) +export const linkItem = defineType({ + name: 'linkItem', + title: 'Link Item', + type: 'object', + fields: [ + defineField({ + name: 'label', + title: 'Label', + type: 'string', + validation: Rule => Rule.required() + }), + defineField({ + name: 'url', + title: 'URL', + type: 'url', + validation: Rule => Rule.required() + }) + ] +}) + export default defineType({ name: 'cdnav', title: 'Navigation (CD)', diff --git a/src/theme/conversion/components/cdserviceintro/classification.ts b/src/theme/conversion/components/cdserviceintro/classification.ts deleted file mode 100644 index 2d7f277..0000000 --- a/src/theme/conversion/components/cdserviceintro/classification.ts +++ /dev/null @@ -1 +0,0 @@ -export default ["cdserviceintro", "serviceintro", "service-intro"] \ No newline at end of file diff --git a/src/theme/conversion/components/cdserviceintro/components/variants/cdserviceintroDefaultVariant.tsx b/src/theme/conversion/components/cdserviceintro/components/variants/cdserviceintroDefaultVariant.tsx deleted file mode 100644 index 8139c8c..0000000 --- a/src/theme/conversion/components/cdserviceintro/components/variants/cdserviceintroDefaultVariant.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import { StandardComponentProps } from "@conversiondigital/headless-basics-components/src/interfaces/standardComponentProps"; - -const CdserviceintroDefaultVariant: React.FC = ({ matchingData }) => { - const heading = matchingData?.heading || ''; - const title = matchingData?.title || ''; - const content = matchingData?.content || ''; - - return ( -
-
-
-

{heading}

-
-
-

{title}

-
-
-
-
- ); -}; - -export default CdserviceintroDefaultVariant; \ No newline at end of file diff --git a/src/theme/conversion/components/cdserviceofferings/classification.ts b/src/theme/conversion/components/cdserviceofferings/classification.ts deleted file mode 100644 index d1209af..0000000 --- a/src/theme/conversion/components/cdserviceofferings/classification.ts +++ /dev/null @@ -1 +0,0 @@ -export default ["cdserviceofferings", "serviceofferings", "cdserviceofferings"] diff --git a/src/theme/conversion/components/cdserviceofferings/components/variants/cdserviceofferingsDefaultVariant.tsx b/src/theme/conversion/components/cdserviceofferings/components/variants/cdserviceofferingsDefaultVariant.tsx deleted file mode 100644 index 5d70cac..0000000 --- a/src/theme/conversion/components/cdserviceofferings/components/variants/cdserviceofferingsDefaultVariant.tsx +++ /dev/null @@ -1,102 +0,0 @@ -"use client" -import React, { useState, useEffect } from 'react'; -import { StandardComponentProps } from "@conversiondigital/headless-basics-components/src/interfaces/standardComponentProps"; - -interface Offering { - title: string; - icon: string; - id: { - current: string; - }; -} - -interface Service { - id: string; - title: string; - content: string; -} - -const CdserviceofferingsDefaultVariant: React.FC = ({ matchingData }) => { - const title = matchingData?.title || ''; - const intro = matchingData?.intro || ''; - const offerings = matchingData?.offerings || []; - const services = matchingData?.services || []; - - const [activeServiceId, setActiveServiceId] = useState(undefined); - const [isVisible, setIsVisible] = useState(true); - - const getIdValue = (offering: Offering) => offering.id?.current || ''; - - const currentService = services.find((service: Service) => service.id === activeServiceId); - - useEffect(() => { - if (!activeServiceId && offerings.length > 0) { - setActiveServiceId(getIdValue(offerings[0]) || '1'); - } - }, [offerings, activeServiceId]); - - const handleServiceClick = (serviceId: string, index: number) => { - if (serviceId === activeServiceId) return; - - setIsVisible(false); - - setTimeout(() => { - setActiveServiceId(serviceId || index.toString()); - setIsVisible(true); - }, 300); - }; - - return ( -
-
-
-
-

{title}

-

{intro}

-
-
-
- {offerings.map((offering: Offering, index: number) => { - const id = getIdValue(offering); - return ( -
handleServiceClick(id, index + 1)} - role="button" - tabIndex={0} - aria-pressed={id === activeServiceId} - onKeyDown={(e) => { - if (e.key === 'Enter' || e.key === ' ') { - handleServiceClick(id, index + 1); - } - }} - > -

{offering.title}

- - - -
- ); - })} -
-
-
-
-
-
-
-
-
-
-
-
- ); -}; - -export default CdserviceofferingsDefaultVariant; \ No newline at end of file diff --git a/src/theme/conversion/components/cdserviceofferings/sanity-schema.ts b/src/theme/conversion/components/cdserviceofferings/sanity-schema.ts deleted file mode 100644 index 79b8416..0000000 --- a/src/theme/conversion/components/cdserviceofferings/sanity-schema.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { defineField, defineType } from 'sanity' -import { EyeOpenIcon } from '@sanity/icons' - -// Define a separate type for the offering object -export const cdserviceOfferingItem = defineType({ - name: 'cdserviceOfferingItem', - title: 'Service Offering Item', - type: 'object', - fields: [ - defineField({ - name: 'title', - title: 'Title', - type: 'string', - description: 'The name of the service offering' - }), - defineField({ - name: 'icon', - title: 'Icon', - type: 'string', - description: 'The icon name to display for this offering' - }), - defineField({ - name: 'id', - title: 'ID', - type: 'slug', - description: 'A unique identifier for this service offering', - }) - ] -}) - -// Define a separate type for the service detail object -export const cdserviceDetailItem = defineType({ - name: 'cdserviceDetailItem', - title: 'Service Detail Item', - type: 'object', - fields: [ - defineField({ - name: 'id', - title: 'ID', - type: 'string', - description: 'A unique identifier for this service detail (should match an offering ID)' - }), - defineField({ - name: 'title', - title: 'Title', - type: 'string', - description: 'The title of the service detail' - }), - defineField({ - name: 'content', - title: 'Content', - type: 'array', - of: [{ type: 'block' }], - description: 'The rich text content for this service detail' - }) - ] -}) - -export default defineType({ - name: 'cdserviceofferings', - title: 'Service Offerings (CD)', - type: 'object', - icon: EyeOpenIcon, - fields: [ - defineField({ - name: 'selectableVariant', - title: 'Selectable Variant', - type: 'string', - options: { - list: [ - { title: 'Default', value: 'default' } - ] - } - }), - defineField({ - name: 'title', - title: 'Title', - type: 'string', - description: 'The main title for the service offerings section (e.g., "What We Offer")' - }), - defineField({ - name: 'intro', - title: 'Introduction Text', - type: 'text', - description: 'A brief introduction to the service offerings' - }), - defineField({ - name: 'offerings', - title: 'Service Offerings', - type: 'array', - of: [ - { type: 'cdserviceOfferingItem' } - ], - }), - defineField({ - name: 'services', - title: 'Service Details', - type: 'array', - of: [ - { type: 'cdserviceDetailItem' } - ], - description: 'The detailed content for each service offering' - }), - defineField({ - name: 'sortOrder', - title: 'Sort Order', - type: 'number' - }), - defineField({ - name: 'globalComponentSource', - title: 'Global Component Source', - type: 'reference', - to: [{ type: 'cdserviceofferings' }], - description: 'Select a global re-usable service offerings component.' - }) - ], - preview: { - select: { - title: 'title' - } - } -}) \ No newline at end of file diff --git a/src/theme/conversion/components/cdservicestats/classification.ts b/src/theme/conversion/components/cdservicestats/classification.ts deleted file mode 100644 index f1f4872..0000000 --- a/src/theme/conversion/components/cdservicestats/classification.ts +++ /dev/null @@ -1 +0,0 @@ -export default ["cdservicestats", "servicestats", "cdservicestats"] diff --git a/src/theme/conversion/components/cdstatistics/classification.ts b/src/theme/conversion/components/cdstatistics/classification.ts new file mode 100644 index 0000000..e4e1dc7 --- /dev/null +++ b/src/theme/conversion/components/cdstatistics/classification.ts @@ -0,0 +1 @@ +export default ["cdstatistics", "statistics"] diff --git a/src/theme/conversion/components/cdserviceofferings/components/index.tsx b/src/theme/conversion/components/cdstatistics/components/index.tsx similarity index 52% rename from src/theme/conversion/components/cdserviceofferings/components/index.tsx rename to src/theme/conversion/components/cdstatistics/components/index.tsx index 71b0175..7a93e95 100644 --- a/src/theme/conversion/components/cdserviceofferings/components/index.tsx +++ b/src/theme/conversion/components/cdstatistics/components/index.tsx @@ -2,19 +2,19 @@ import React from 'react'; import { componentBoilerPlate } from "@conversiondigital/headless-basics-data/src/component-tools/componentBoilerPlate"; import { ViewComponentProps } from "@conversiondigital/headless-basics-data/src/interfaces"; import { getLogger, logPrefix } from "@conversiondigital/headless-basics-data/src"; -import CdserviceofferingsDefaultVariant from './variants/cdserviceofferingsDefaultVariant'; +import CdStatisticsDefaultVariant from './variants/CdStatisticsDefaultVariant'; -export const log = getLogger("conversion.components.cdserviceofferings"); +export const log = getLogger("conversion.components.cdstatistics"); -export default function CdserviceofferingsUI(dynamicComponent: ViewComponentProps) { +export default function CdStatisticsUI(dynamicComponent: ViewComponentProps) { const { variant, matchingData } = componentBoilerPlate(dynamicComponent); if (!matchingData) return null; - log.trace(`${logPrefix()} cdserviceofferingsUI started, matchingData: ${JSON.stringify(matchingData)}`); + log.trace(`${logPrefix()} CdStatisticsUI started, matchingData: ${JSON.stringify(matchingData)}`); switch (variant) { case 'default': default: - return ; + return ; } } \ No newline at end of file diff --git a/src/theme/conversion/components/cdservicestats/components/variants/cdservicestatsDefaultVariant.tsx b/src/theme/conversion/components/cdstatistics/components/variants/CdStatisticsDefaultVariant.tsx similarity index 72% rename from src/theme/conversion/components/cdservicestats/components/variants/cdservicestatsDefaultVariant.tsx rename to src/theme/conversion/components/cdstatistics/components/variants/CdStatisticsDefaultVariant.tsx index 47fa4a5..d041cd6 100644 --- a/src/theme/conversion/components/cdservicestats/components/variants/cdservicestatsDefaultVariant.tsx +++ b/src/theme/conversion/components/cdstatistics/components/variants/CdStatisticsDefaultVariant.tsx @@ -6,17 +6,17 @@ interface Stat { description: string; } -const CdservicestatsDefaultVariant: React.FC = ({ matchingData }) => { +const CdStatisticsDefaultVariant: React.FC = ({ matchingData }) => { const stats = matchingData?.stats || []; return ( -
+
{stats.map((stat: Stat, index: number) => (
-
{stat.value}
-
{stat.description}
+
{stat.value}
+
{stat.description}
))}
@@ -25,4 +25,4 @@ const CdservicestatsDefaultVariant: React.FC = ({ matchi ); }; -export default CdservicestatsDefaultVariant; \ No newline at end of file +export default CdStatisticsDefaultVariant; \ No newline at end of file diff --git a/src/theme/conversion/components/cdserviceofferings/index.ts b/src/theme/conversion/components/cdstatistics/index.ts similarity index 74% rename from src/theme/conversion/components/cdserviceofferings/index.ts rename to src/theme/conversion/components/cdstatistics/index.ts index 53eb50b..e116827 100644 --- a/src/theme/conversion/components/cdserviceofferings/index.ts +++ b/src/theme/conversion/components/cdstatistics/index.ts @@ -2,10 +2,10 @@ import { getLogger, getThemeConfig } from "@conversiondigital/headless-basics-da import { View } from "./view"; import { ThemeConfig } from "@conversiondigital/headless-basics-data/src/interfaces" -getLogger("theme.components.cdserviceofferings") +getLogger("theme.components.cdstatistics") async function getConfig(): Promise { - const config = await getThemeConfig('cdserviceofferings'); + const config = await getThemeConfig('cdstatistics'); config.view = View; return config; } diff --git a/src/theme/conversion/components/cdservicestats/sanity-mapping.ts b/src/theme/conversion/components/cdstatistics/sanity-mapping.ts similarity index 78% rename from src/theme/conversion/components/cdservicestats/sanity-mapping.ts rename to src/theme/conversion/components/cdstatistics/sanity-mapping.ts index 34a4002..4f87e81 100644 --- a/src/theme/conversion/components/cdservicestats/sanity-mapping.ts +++ b/src/theme/conversion/components/cdstatistics/sanity-mapping.ts @@ -1,7 +1,7 @@ import { getLogger, logPrefix, PageAndSingleComponentDetails } from "@conversiondigital/headless-basics-data/src" import { extractComponentsFromSanityData } from "@conversiondigital/headless-basics-data/src/cms/sanity/sanityMappingUtils" -export const log = getLogger("conversion.components.sanity.cdservicestats.mapping") +export const log = getLogger("conversion.components.sanity.cdstatistics.mapping") export async function mapIdentifierData(pageAndComponentCombo: PageAndSingleComponentDetails) { log.trace( @@ -9,8 +9,8 @@ export async function mapIdentifierData(pageAndComponentCombo: PageAndSingleComp ); const content = pageAndComponentCombo?.component?.data - // The second arg 'cdservicestats' must match the name in cdservicestats's sanity-schema - const matchingData = extractComponentsFromSanityData(content, "cdservicestats", log); + // The second arg 'cdstatistics' must match the name in cdstatistics's sanity-schema + const matchingData = extractComponentsFromSanityData(content, "cdstatistics", log); return matchingData } diff --git a/src/theme/conversion/components/cdservicestats/sanity-query.ts b/src/theme/conversion/components/cdstatistics/sanity-query.ts similarity index 89% rename from src/theme/conversion/components/cdservicestats/sanity-query.ts rename to src/theme/conversion/components/cdstatistics/sanity-query.ts index 8e2501c..2ecb493 100644 --- a/src/theme/conversion/components/cdservicestats/sanity-query.ts +++ b/src/theme/conversion/components/cdstatistics/sanity-query.ts @@ -2,11 +2,11 @@ import { PageAndSingleComponentDetails } from "@conversiondigital/headless-basic export function query(pageAndComponentCombo: PageAndSingleComponentDetails): string { return ` - query GetCdservicestatsBySlug($slug: String!) { + query GetCdStatisticsBySlug($slug: String!) { allPage(where: { slug: { current: { eq: $slug } } }) { components { __typename - ... on Cdservicestats { + ... on Cdstatistics { __typename _key _type @@ -28,10 +28,10 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str } } } - allServicePage { + allHomepage { components { __typename - ... on Cdservicestats { + ... on Cdstatistics { __typename _key _type diff --git a/src/theme/conversion/components/cdservicestats/sanity-schema.ts b/src/theme/conversion/components/cdstatistics/sanity-schema.ts similarity index 70% rename from src/theme/conversion/components/cdservicestats/sanity-schema.ts rename to src/theme/conversion/components/cdstatistics/sanity-schema.ts index 0751039..b20816e 100644 --- a/src/theme/conversion/components/cdservicestats/sanity-schema.ts +++ b/src/theme/conversion/components/cdstatistics/sanity-schema.ts @@ -2,9 +2,9 @@ import { defineField, defineType } from 'sanity' import { BarChartIcon } from '@sanity/icons' // Define a separate type for the statistic object -export const cdserviceStatItem = defineType({ - name: 'cdserviceStatItem', - title: 'Service Statistic Item', +export const cdStatisticItem = defineType({ + name: 'cdStatisticItem', + title: 'Statistic Item', type: 'object', fields: [ defineField({ @@ -19,12 +19,18 @@ export const cdserviceStatItem = defineType({ type: 'string', description: 'The description of what the statistic represents' }) - ] + ], + preview: { + select: { + title: 'value', + subtitle: 'description' + } + } }) export default defineType({ - name: 'cdservicestats', - title: 'Service Statistics (CD)', + name: 'cdstatistics', + title: 'Statistics (CD)', type: 'object', icon: BarChartIcon, fields: [ @@ -38,12 +44,18 @@ export default defineType({ ] } }), + defineField({ + name: 'title', + title: 'Title', + type: 'string', + description: 'The title of the statistics component' + }), defineField({ name: 'stats', title: 'Statistics', type: 'array', of: [ - { type: 'cdserviceStatItem' } + { type: 'cdStatisticItem' } ], }), defineField({ @@ -55,8 +67,13 @@ export default defineType({ name: 'globalComponentSource', title: 'Global Component Source', type: 'reference', - to: [{ type: 'cdservicestats' }], + to: [{ type: 'cdstatistics' }], description: 'Select a global re-usable service statistics component.' }) ], + preview: { + select: { + title: 'title', + } + } }) \ No newline at end of file diff --git a/src/theme/conversion/components/cdserviceofferings/view.tsx b/src/theme/conversion/components/cdstatistics/view.tsx similarity index 65% rename from src/theme/conversion/components/cdserviceofferings/view.tsx rename to src/theme/conversion/components/cdstatistics/view.tsx index 7fcc2c2..fa048e0 100644 --- a/src/theme/conversion/components/cdserviceofferings/view.tsx +++ b/src/theme/conversion/components/cdstatistics/view.tsx @@ -2,10 +2,10 @@ import { ViewComponentProps } from "@conversiondigital/headless-basics-data/src"; import dynamic from "next/dynamic" -const CdserviceofferingsUI = dynamic(() => import("./components"), { +const CdStatisticsUI = dynamic(() => import("./components"), { loading: () => (

Loading...

) }); export async function View(dynamicComponent: ViewComponentProps) { - return + return } diff --git a/src/theme/conversion/index.ts b/src/theme/conversion/index.ts index 98908ef..7ec25c3 100644 --- a/src/theme/conversion/index.ts +++ b/src/theme/conversion/index.ts @@ -14,6 +14,6 @@ export { default as cdclients } from "./components/cdclients/index"; // Insert cdcta export { default as cdcta } from "./components/cdcta/index"; // New service page components -export { default as cdserviceintro } from "./components/cdserviceintro/index"; -export { default as cdservicestats } from "./components/cdservicestats/index"; -export { default as cdserviceofferings } from "./components/cdserviceofferings/index"; \ No newline at end of file +export { default as cdintroduction } from "./components/cdintroduction/index"; +export { default as cdstatistics } from "./components/cdstatistics/index"; +export { default as cdfaqs } from "./components/cdfaqs/index"; \ No newline at end of file diff --git a/src/theme/conversion/structures/servicepage/sanity-mapping.ts b/src/theme/conversion/structures/servicepage/sanity-mapping.ts deleted file mode 100644 index ad50988..0000000 --- a/src/theme/conversion/structures/servicepage/sanity-mapping.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { getLogger, logPrefix, PageAndSingleComponentDetails } from "@conversiondigital/headless-basics-data/src"; -import { extractComponentsFromSanityData } from "@conversiondigital/headless-basics-data/src/cms/sanity/sanityMappingUtils"; - -export const log = getLogger("conversion.structures.sanity.servicepage.mapping"); - -export async function mapIdentifierData(pageAndComponentCombo: PageAndSingleComponentDetails) { - log.trace( - `${logPrefix()}[${pageAndComponentCombo.component.identifier}][${pageAndComponentCombo.page.source}][${pageAndComponentCombo.page.preliminarySlug}] mapIdentifierData started` - ); - - const content = pageAndComponentCombo?.component?.data; - const servicePage = content?.allServicePage && Array.isArray(content.allServicePage) ? content.allServicePage[0] : null; - - if (!servicePage) { - log.warn(`${logPrefix()} No ServicePage data found`); - return null; - } - - // Find the components by type - const serviceIntroComponent = servicePage.components?.find((comp: any) => comp.__typename === 'Cdserviceintro'); - const serviceStatsComponent = servicePage.components?.find((comp: any) => comp.__typename === 'Cdservicestats'); - const serviceOfferingsComponent = servicePage.components?.find((comp: any) => comp.__typename === 'Cdserviceofferings'); - const serviceDetailComponent = servicePage.components?.find((comp: any) => comp.__typename === 'Cdservicedetail'); - - // Process the service intro component - let serviceIntroData = null; - if (serviceIntroComponent) { - const hasGlobal = serviceIntroComponent.globalComponentSource; - serviceIntroData = { - title: serviceIntroComponent.title || (hasGlobal?.title || ''), - content: serviceIntroComponent.content || (hasGlobal?.content || ''), - selectableVariant: serviceIntroComponent.selectableVariant || (hasGlobal?.selectableVariant || 'default') - }; - } - - // Process the service stats component - let serviceStatsData = null; - if (serviceStatsComponent) { - const hasGlobal = serviceStatsComponent.globalComponentSource; - serviceStatsData = { - stats: serviceStatsComponent.stats && serviceStatsComponent.stats.length > 0 - ? serviceStatsComponent.stats - : (hasGlobal?.stats || []), - selectableVariant: serviceStatsComponent.selectableVariant || (hasGlobal?.selectableVariant || 'default') - }; - } - - // Process the service offerings component - let serviceOfferingsData = null; - if (serviceOfferingsComponent) { - const hasGlobal = serviceOfferingsComponent.globalComponentSource; - serviceOfferingsData = { - title: serviceOfferingsComponent.title || (hasGlobal?.title || ''), - intro: serviceOfferingsComponent.intro || (hasGlobal?.intro || ''), - offerings: serviceOfferingsComponent.offerings && serviceOfferingsComponent.offerings.length > 0 - ? serviceOfferingsComponent.offerings.map((offering: any) => ({ - title: offering.title, - icon: offering.icon, - id: offering.id?.current - })) - : (hasGlobal?.offerings?.map((offering: any) => ({ - title: offering.title, - icon: offering.icon, - id: offering.id?.current - })) || []), - selectableVariant: serviceOfferingsComponent.selectableVariant || (hasGlobal?.selectableVariant || 'default') - }; - } - - // Process the service detail component - let serviceDetailData = null; - if (serviceDetailComponent) { - const hasGlobal = serviceDetailComponent.globalComponentSource; - serviceDetailData = { - services: serviceDetailComponent.services && serviceDetailComponent.services.length > 0 - ? serviceDetailComponent.services.map((service: any) => ({ - title: service.title, - id: service.id?.current, - content: service.content - })) - : (hasGlobal?.services?.map((service: any) => ({ - title: service.title, - id: service.id?.current, - content: service.content - })) || []), - selectableVariant: serviceDetailComponent.selectableVariant || (hasGlobal?.selectableVariant || 'default') - }; - } - - return { - serviceIntroData, - serviceStatsData, - serviceOfferingsData, - serviceDetailData, - defaultActiveServiceId: serviceOfferingsData?.offerings?.[0]?.id - }; -} \ No newline at end of file diff --git a/src/theme/conversion/structures/servicepage/sanity-query.ts b/src/theme/conversion/structures/servicepage/sanity-query.ts deleted file mode 100644 index 8631c23..0000000 --- a/src/theme/conversion/structures/servicepage/sanity-query.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { getLogger, logPrefix, PageAndSingleComponentDetails } from "@conversiondigital/headless-basics-data/src"; - -export const log = getLogger("conversion.structures.sanity.servicepage.query"); - -export function query(pageAndComponentCombo: PageAndSingleComponentDetails) { - log.info(`${logPrefix()}[servicepage][sanity-query][query] called for slug: ${pageAndComponentCombo?.page?.preliminarySlug}`); - - return ` - query GetServicePageBySlug($slug: String!) { - allServicePage(where: { slug: { current: { eq: $slug } } }) { - title - slug { - current - } - components { - __typename - ... on Cdserviceintro { - __typename - _key - _type - selectableVariant - title - content - sortOrder - globalComponentSource { - __typename - _key - _type - title - content - } - } - ... on Cdservicestats { - __typename - _key - _type - selectableVariant - stats { - value - description - } - sortOrder - globalComponentSource { - __typename - _key - _type - stats { - value - description - } - } - } - ... on Cdserviceofferings { - __typename - _key - _type - selectableVariant - title - intro - offerings { - title - icon - id { - current - } - } - sortOrder - globalComponentSource { - __typename - _key - _type - title - intro - offerings { - title - icon - id { - current - } - } - } - } - ... on Cdservicedetail { - __typename - _key - _type - selectableVariant - services { - title - id { - current - } - content - } - sortOrder - globalComponentSource { - __typename - _key - _type - services { - title - id { - current - } - content - } - } - } - } - } - } - `; -} - -export function getQuery() { - return query; -} \ No newline at end of file diff --git a/src/theme/conversion/styles/globals.css b/src/theme/conversion/styles/globals.css index f957ed0..cbf4625 100644 --- a/src/theme/conversion/styles/globals.css +++ b/src/theme/conversion/styles/globals.css @@ -35,6 +35,10 @@ background-color: var(--off-white); } +@theme { + --color-primary-bg: #0d0e47; +} + :root { --color-primary-bg: #0D0E47; --color-primary-text: white; @@ -71,6 +75,7 @@ --border-gray-700: #374151; --br-white: #DEDEDE; --border-purple: #6C2BD9; + --body-color: #0d0e47; } body { @@ -329,20 +334,20 @@ nav { } -.contentServiceIntroWrapper { +.contentIntroductionWrapper { color: #0d0e47; } -.contentServiceIntroWrapper p{ +.contentIntroductionWrapper p{ margin-top: 30px; color: #0d0e47; font-family: var(--font-figtree); } -.contentServiceIntroWrapper p:first-child, .contentServiceIntroWrapper h3:first-child{ +.contentIntroductionWrapper p:first-child, .contentIntroductionWrapper h3:first-child{ margin-top: 0; } -.contentServiceIntroWrapper h3{ +.contentIntroductionWrapper h3{ margin-top: 53px; margin-bottom: 22px; font-size: 30px; @@ -351,7 +356,7 @@ nav { color: #0d0e47; } -.contentServiceOfferingsWrapper { +.contentFaqsWrapper { color: white; font-family: var(--font-figtree); font-size: 18px; @@ -359,7 +364,7 @@ nav { font-weight: 500; } -.contentServiceOfferingsWrapper p{ +.contentFaqsWrapper p{ margin-top: 30px; color: white; font-family: var(--font-figtree); @@ -368,7 +373,7 @@ nav { font-weight: 500; } -.contentServiceOfferingsWrapper ul { +.contentFaqsWrapper ul { list-style: initial; font-size: 18px; line-height: 36px; @@ -376,11 +381,11 @@ nav { margin-left: 30px; } -.contentServiceOfferingsWrapper p:first-child, .contentServiceOfferingsWrapper h3:first-child{ +.contentFaqsWrapper p:first-child, .contentFaqsWrapper h3:first-child{ margin-top: 0; } -.contentServiceOfferingsWrapper h3, .contentServiceOfferingsWrapper h2{ +.contentFaqsWrapper h3, .contentFaqsWrapper h2{ margin-top: 53px; margin-bottom: 22px; font-size: 30px; diff --git a/src/theme/conversion/tailwind.config.js b/src/theme/conversion/tailwind.config.js index f5efe47..108e942 100644 --- a/src/theme/conversion/tailwind.config.js +++ b/src/theme/conversion/tailwind.config.js @@ -71,7 +71,8 @@ module.exports = { DEFAULT: "#FACF41", light: "#FFE799", }, - "body-color": "#0d0e47", + "body-color": "var(--body-color)", + "primary-bg": "var(--color-primary-bg)", }, backgroundImage: { "gradient-dark": "linear-gradient(277deg, #171717 55.29%, #3D3D3D 98.19%)", From 06ec1f72272e206a781a44acdbc4a137c2584dd3 Mon Sep 17 00:00:00 2001 From: Steven Cao Date: Tue, 8 Jul 2025 01:34:57 +0700 Subject: [PATCH 4/6] HEAD-124: add variant featureBlocks for cdservices component --- .../cdservices/components/index.tsx | 3 + .../CdServiceFeatureBlocksVariant.tsx | 120 ++++++++++++++++++ .../components/cdservices/sanity-query.ts | 14 ++ .../components/cdservices/sanity-schema.ts | 48 ++++++- src/theme/conversion/styles/globals.css | 4 +- src/theme/conversion/tailwind.config.js | 11 +- 6 files changed, 192 insertions(+), 8 deletions(-) create mode 100644 src/theme/conversion/components/cdservices/components/variants/CdServiceFeatureBlocksVariant.tsx diff --git a/src/theme/conversion/components/cdservices/components/index.tsx b/src/theme/conversion/components/cdservices/components/index.tsx index 94047ad..972a2a4 100644 --- a/src/theme/conversion/components/cdservices/components/index.tsx +++ b/src/theme/conversion/components/cdservices/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/cdservicesDefaultVariant"; +import CdServiceFeatureBlocksVariant from "./variants/CdServiceFeatureBlocksVariant"; export const log = getLogger("conversion.components.cdservices"); @@ -16,6 +17,8 @@ export default function TemplateUI(dynamicComponent: ViewComponentProps) { switch (variant) { case "xDemo": return ; + case "featureBlocks": + return ; default: return ; } diff --git a/src/theme/conversion/components/cdservices/components/variants/CdServiceFeatureBlocksVariant.tsx b/src/theme/conversion/components/cdservices/components/variants/CdServiceFeatureBlocksVariant.tsx new file mode 100644 index 0000000..2917c0c --- /dev/null +++ b/src/theme/conversion/components/cdservices/components/variants/CdServiceFeatureBlocksVariant.tsx @@ -0,0 +1,120 @@ +import { StandardComponentProps } from "@conversiondigital/headless-basics-components/src/interfaces/standardComponentProps"; +import { cn } from "@conversiondigital/headless-basics-data"; +import { ChevronRight } from "lucide-react"; +import Image from "next/image"; +import Link from "next/link"; + +interface ServiceLink { + name: string; +} + +interface ServiceImage { + asset: { + url: string; + }; +} + +interface ServiceItem { + title: string; + description: string; + image: ServiceImage; + serviceLinks: ServiceLink[]; + category: string; + imageTitle: string; + buttonText: string; + buttonUrl: string; +} + +interface CdServicesData { + _key: string; + _type: string; + selectableVariant: string; + sortOrder: number; + title: string; + subtitle: string; + image?: ServiceImage; + servicesList: ServiceItem[]; + globalComponentSource?: CdServicesData; +} + +export default function CdServiceFeatureBlocksVariant(props: StandardComponentProps) { + const { matchingData } = props; + const data = matchingData as CdServicesData; + const services = data?.servicesList || []; + + const padStart = (number: number) => String(number).padStart(2, "0"); + + return ( +
+
+ {services.map((service: ServiceItem, idx: number) => ( +
+
+
+ + {padStart(idx + 1)} + +

+ {service.title} +

+

+ {service.description} +

+ {!!service.serviceLinks?.length && ( +
+
    + {service.serviceLinks.map((link, linkIdx) => ( +
  • 3 ? "md:w-1/2" : "md:w-full", + )} + > + {link.name} +
  • + ))} +
+
+ )} + + {service.buttonText ?? "Learn More"} + +
+
+ + {service.imageTitle +
+ {service.category} +

+ {service.imageTitle || service.title} +

+
+
+ +
+
+
+ ))} +
+
+ ); +} diff --git a/src/theme/conversion/components/cdservices/sanity-query.ts b/src/theme/conversion/components/cdservices/sanity-query.ts index 1924fae..d34753b 100644 --- a/src/theme/conversion/components/cdservices/sanity-query.ts +++ b/src/theme/conversion/components/cdservices/sanity-query.ts @@ -27,6 +27,13 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str url } } + serviceLinks { + name + } + category + imageTitle + buttonText + buttonUrl } globalComponentSource { __typename @@ -77,6 +84,13 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str url } } + serviceLinks { + name + } + category + imageTitle + buttonText + buttonUrl } globalComponentSource { __typename diff --git a/src/theme/conversion/components/cdservices/sanity-schema.ts b/src/theme/conversion/components/cdservices/sanity-schema.ts index 29a873c..19a7b1a 100644 --- a/src/theme/conversion/components/cdservices/sanity-schema.ts +++ b/src/theme/conversion/components/cdservices/sanity-schema.ts @@ -1,6 +1,19 @@ import { defineField, defineType } from 'sanity' import { EyeOpenIcon } from '@sanity/icons' +export const serviceLink = defineType({ + name: 'serviceLink', + title: 'Service Link', + type: 'object', + fields: [ + defineField({ + name: 'name', + title: 'Name', + type: 'string' + }) + ] +}) + export const serviceItem = defineType({ name: 'serviceItem', title: 'Service Item', @@ -25,9 +38,37 @@ export const serviceItem = defineType({ } }), defineField({ - name: 'link', - title: 'Link', - type: 'url' + name: 'serviceLinks', + title: 'Service Links', + type: 'array', + of: [ + { + type: 'serviceLink' + } + ] + }), + defineField({ + name: 'category', + title: 'Category', + type: 'string', + description: 'e.g: Featured case study' + }), + defineField({ + name: 'imageTitle', + title: 'Image Title', + type: 'string' + }), + defineField({ + name: 'buttonText', + title: 'Button Text', + type: 'string', + initialValue: 'Learn more' + }), + defineField({ + name: 'buttonUrl', + title: 'Button URL', + type: 'string', + description: 'URL for the button', }) ] }) @@ -46,6 +87,7 @@ export default defineType({ list: [ { title: 'Default', value: 'default' }, { title: 'Demo', value: 'xDemo' }, + { title: 'Feature Blocks', value: 'featureBlocks' }, ] } }), diff --git a/src/theme/conversion/styles/globals.css b/src/theme/conversion/styles/globals.css index cbf4625..06f7e53 100644 --- a/src/theme/conversion/styles/globals.css +++ b/src/theme/conversion/styles/globals.css @@ -37,6 +37,9 @@ @theme { --color-primary-bg: #0d0e47; + --color-accent: #800928; + --font-staatliches: var(--font-staatliches); + --font-figtree: var(--font-figtree); } :root { @@ -75,7 +78,6 @@ --border-gray-700: #374151; --br-white: #DEDEDE; --border-purple: #6C2BD9; - --body-color: #0d0e47; } body { diff --git a/src/theme/conversion/tailwind.config.js b/src/theme/conversion/tailwind.config.js index 108e942..89c17c8 100644 --- a/src/theme/conversion/tailwind.config.js +++ b/src/theme/conversion/tailwind.config.js @@ -6,22 +6,22 @@ module.exports = { "pages/**/*.{ts,tsx}", "theme/**/*.{ts,tsx}", "./app/**/*.{ts,tsx}", - + "./node_modules/@conversiondigital/headless-basics-components/src/components/**/*.{ts,tsx}", "./../headless-basics-components/src/components/**/*.{ts,tsx}", "../../node_modules/@conversiondigital/headless-basics-components/src/components/**/*.{ts,tsx}", "./../../../headless-basics-components/src/components/**/*.{ts,tsx}", - + "./node_modules/@conversiondigital/headless-basics-components/src/theme/default/**/*.{ts,tsx}", "./../headless-basics-components/src/theme/default/**/*.{ts,tsx}", "../../node_modules/@conversiondigital/headless-basics-components/src/theme/default/**/*.{ts,tsx}", "./../../../headless-basics-components/src/theme/default/**/*.{ts,tsx}", - + "./node_modules/@conversiondigital/headless-basics-components/src/theme/conversion/**/*.{ts,tsx}", "./../headless-basics-components/src/theme/conversion/**/*.{ts,tsx}", "../../node_modules/@conversiondigital/headless-basics-components/src/theme/conversion/**/*.{ts,tsx}", "./../../../headless-basics-components/src/theme/conversion/**/*.{ts,tsx}", - './src/**/*.{js,jsx,ts,tsx}' + "./src/**/*.{js,jsx,ts,tsx}", ], colors: { black45: "var(--black-45)", @@ -73,12 +73,15 @@ module.exports = { }, "body-color": "var(--body-color)", "primary-bg": "var(--color-primary-bg)", + accent: "var(--color-accent)", }, backgroundImage: { "gradient-dark": "linear-gradient(277deg, #171717 55.29%, #3D3D3D 98.19%)", }, fontFamily: { helvetica: ["var(--font-helvetica)", ...fontFamily.sans], + staatliches: ["var(--font-staatliches)", ...fontFamily.sans], + figtree: ["var(--font-figtree)", ...fontFamily.sans], }, fontSize: { h1: "60px", From 6c35df71bbb8dfb787950fa805a8ac0391d7c1b9 Mon Sep 17 00:00:00 2001 From: Steven Cao Date: Tue, 8 Jul 2025 11:43:50 +0700 Subject: [PATCH 5/6] HEAD-124: add variant introduction and title for cdservices component --- .../variants/CdServiceFeatureBlocksVariant.tsx | 2 +- .../components/herobanner/components/index.tsx | 7 +++++++ .../variants/IntroductionVariant.tsx | 18 ++++++++++++++++++ .../components/variants/TitleOnlyVariant.tsx | 15 +++++++++++++++ .../components/herobanner/sanity-mapping.ts | 2 +- .../components/herobanner/sanity-query.ts | 4 ++++ .../components/herobanner/sanity-schema.ts | 8 ++++++++ 7 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 src/theme/conversion/components/herobanner/components/variants/IntroductionVariant.tsx create mode 100644 src/theme/conversion/components/herobanner/components/variants/TitleOnlyVariant.tsx diff --git a/src/theme/conversion/components/cdservices/components/variants/CdServiceFeatureBlocksVariant.tsx b/src/theme/conversion/components/cdservices/components/variants/CdServiceFeatureBlocksVariant.tsx index 2917c0c..67b3c4b 100644 --- a/src/theme/conversion/components/cdservices/components/variants/CdServiceFeatureBlocksVariant.tsx +++ b/src/theme/conversion/components/cdservices/components/variants/CdServiceFeatureBlocksVariant.tsx @@ -62,7 +62,7 @@ export default function CdServiceFeatureBlocksVariant(props: StandardComponentPr {padStart(idx + 1)} -

+

{service.title}

diff --git a/src/theme/conversion/components/herobanner/components/index.tsx b/src/theme/conversion/components/herobanner/components/index.tsx index af66077..bb1701a 100644 --- a/src/theme/conversion/components/herobanner/components/index.tsx +++ b/src/theme/conversion/components/herobanner/components/index.tsx @@ -4,16 +4,23 @@ 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 IntroductionVariant from "./variants/IntroductionVariant" +import TitleOnlyVariant from "./variants/TitleOnlyVariant" export const log = getLogger("default.components.heartcore.template.variants"); export default function TemplateUI(dynamicComponent: ViewComponentProps) { const { variant, matchingData } = componentBoilerPlate(dynamicComponent); + if (!matchingData) return null; log.trace(`${logPrefix} TemplateUI started, matchingData: ${JSON.stringify(matchingData)}`); switch (variant) { case "xDemo": return ; + case "title-only": + return ; + case "introduction": + return ; default: return ; } diff --git a/src/theme/conversion/components/herobanner/components/variants/IntroductionVariant.tsx b/src/theme/conversion/components/herobanner/components/variants/IntroductionVariant.tsx new file mode 100644 index 0000000..a7e7d4e --- /dev/null +++ b/src/theme/conversion/components/herobanner/components/variants/IntroductionVariant.tsx @@ -0,0 +1,18 @@ +import { StandardComponentProps } from "@conversiondigital/headless-basics-components/src/interfaces/standardComponentProps"; + +export default function IntroductionVariant(props: StandardComponentProps) { + const { matchingData } = props; + const { title, subtitle, category } = matchingData; + + return ( +

+
+
+ {category} +

{title}

+

{subtitle}

+
+
+
+ ); +} diff --git a/src/theme/conversion/components/herobanner/components/variants/TitleOnlyVariant.tsx b/src/theme/conversion/components/herobanner/components/variants/TitleOnlyVariant.tsx new file mode 100644 index 0000000..438f241 --- /dev/null +++ b/src/theme/conversion/components/herobanner/components/variants/TitleOnlyVariant.tsx @@ -0,0 +1,15 @@ +import { StandardComponentProps } from "@conversiondigital/headless-basics-components/src/interfaces/standardComponentProps"; + +export default function TitleOnlyVariant(props: StandardComponentProps) { + const { matchingData } = props; + const title = matchingData?.title; + return ( +
+
+
+

{title}

+
+
+
+ ); +} 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..61ab47b 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 + category image { asset { url @@ -31,6 +32,7 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str _type title subtitle + category image { asset { url @@ -55,6 +57,7 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str selectableVariant title subtitle + category image { asset { url @@ -72,6 +75,7 @@ export function query(pageAndComponentCombo: PageAndSingleComponentDetails): str _type title subtitle + category 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..76211b8 100644 --- a/src/theme/conversion/components/herobanner/sanity-schema.ts +++ b/src/theme/conversion/components/herobanner/sanity-schema.ts @@ -13,6 +13,8 @@ export default defineType({ type: 'string', options: { list: [ + { title: 'Title Only', value: 'title-only' }, + { title: 'Introduction', value: 'introduction' }, { title: 'Default', value: 'default' }, { title: 'Demo', value: 'xDemo' }, ] @@ -28,6 +30,12 @@ export default defineType({ title: 'Subtitle', type: 'string', }), + defineField({ + name: 'category', + title: 'Category', + description: 'e.g: Services, About Us, etc.', + type: 'string', + }), defineField({ name: 'image', title: 'Image', From 68cc024626ccac0c0b56a5be385c99dbaf59e644 Mon Sep 17 00:00:00 2001 From: Steven Cao Date: Tue, 8 Jul 2025 12:03:48 +0700 Subject: [PATCH 6/6] HEAD-124: defined interface for components --- .../components/cdfaqs/components/index.tsx | 37 +++++++++++++++++-- .../variants/CdFaqsDefaultVariant.tsx | 8 ++-- .../cdintroduction/components/index.tsx | 31 +++++++++++++--- .../variants/CdIntroductionDefaultVariant.tsx | 7 +++- .../CdServiceFeatureBlocksVariant.tsx | 8 ++-- .../cdstatistics/components/index.tsx | 34 +++++++++++++++-- .../variants/CdStatisticsDefaultVariant.tsx | 12 +++--- .../variants/IntroductionVariant.tsx | 4 +- 8 files changed, 111 insertions(+), 30 deletions(-) diff --git a/src/theme/conversion/components/cdfaqs/components/index.tsx b/src/theme/conversion/components/cdfaqs/components/index.tsx index bd1c770..87a2fb0 100644 --- a/src/theme/conversion/components/cdfaqs/components/index.tsx +++ b/src/theme/conversion/components/cdfaqs/components/index.tsx @@ -1,19 +1,48 @@ -import React from 'react'; +import React from "react"; import { componentBoilerPlate } from "@conversiondigital/headless-basics-data/src/component-tools/componentBoilerPlate"; import { ViewComponentProps } from "@conversiondigital/headless-basics-data/src/interfaces"; import { getLogger, logPrefix } from "@conversiondigital/headless-basics-data/src"; -import CdFaqsDefaultVariant from './variants/CdFaqsDefaultVariant'; +import CdFaqsDefaultVariant from "./variants/CdFaqsDefaultVariant"; export const log = getLogger("conversion.components.cdfaqs"); +// Interface for individual FAQ item +export interface FaqItem { + title: string; + richtextRaw: any[]; +} + +// Interface for global component source +interface GlobalComponentSource { + __typename: string; + _key: string; + _type: string; + title: string; + description: string; + items: FaqItem[]; +} + +// Interface for CdFaqs component data +export interface CdFaqsData { + __typename: string; + _key: string; + _type: string; + selectableVariant: string; + title: string; + description: string; + items: FaqItem[]; + sortOrder: number; + globalComponentSource?: GlobalComponentSource; +} + export default function CdFaqsUI(dynamicComponent: ViewComponentProps) { - const { variant, matchingData } = componentBoilerPlate(dynamicComponent); + const { variant, matchingData } = componentBoilerPlate(dynamicComponent); if (!matchingData) return null; log.trace(`${logPrefix()} CdFaqsUI started, matchingData: ${JSON.stringify(matchingData)}`); switch (variant) { - case 'default': + case "default": default: return ; } diff --git a/src/theme/conversion/components/cdfaqs/components/variants/CdFaqsDefaultVariant.tsx b/src/theme/conversion/components/cdfaqs/components/variants/CdFaqsDefaultVariant.tsx index e10732a..f1eda5f 100644 --- a/src/theme/conversion/components/cdfaqs/components/variants/CdFaqsDefaultVariant.tsx +++ b/src/theme/conversion/components/cdfaqs/components/variants/CdFaqsDefaultVariant.tsx @@ -1,14 +1,14 @@ "use client"; import { StandardComponentProps } from "@conversiondigital/headless-basics-components/src/interfaces/standardComponentProps"; +import { CdFaqsData, FaqItem } from "../index"; import { toHTML } from "@portabletext/to-html"; import React, { useState } from "react"; -interface FaqItem { - title: string; - richtextRaw: any[]; +interface CdFaqsDefaultVariantProps extends StandardComponentProps { + matchingData: CdFaqsData; } -const CdFaqsDefaultVariant: React.FC = ({ matchingData }) => { +const CdFaqsDefaultVariant: React.FC = ({ matchingData }) => { const [indexActive, setIndexActive] = useState(0); const title = matchingData?.title || ""; diff --git a/src/theme/conversion/components/cdintroduction/components/index.tsx b/src/theme/conversion/components/cdintroduction/components/index.tsx index fb690d2..3b0f8d4 100644 --- a/src/theme/conversion/components/cdintroduction/components/index.tsx +++ b/src/theme/conversion/components/cdintroduction/components/index.tsx @@ -1,20 +1,41 @@ -import React from 'react'; +import React from "react"; import { componentBoilerPlate } from "@conversiondigital/headless-basics-data/src/component-tools/componentBoilerPlate"; import { ViewComponentProps } from "@conversiondigital/headless-basics-data/src/interfaces"; import { getLogger, logPrefix } from "@conversiondigital/headless-basics-data/src"; -import CdIntroductionDefaultVariant from './variants/CdIntroductionDefaultVariant'; +import CdIntroductionDefaultVariant from "./variants/CdIntroductionDefaultVariant"; export const log = getLogger("conversion.components.cdintroduction"); +// Interface for global component source +interface GlobalComponentSource { + __typename: string; + _key: string; + _type: string; + title: string; + richtextRaw: any; +} + +// Interface for CdIntroduction component data +export interface CdIntroductionData { + __typename: string; + _key: string; + _type: string; + selectableVariant: string; + title: string; + richtextRaw: any; + sortOrder: number; + globalComponentSource?: GlobalComponentSource; +} + export default function CdIntroductionUI(dynamicComponent: ViewComponentProps) { - const { variant, matchingData } = componentBoilerPlate(dynamicComponent); + const { variant, matchingData } = componentBoilerPlate(dynamicComponent); if (!matchingData) return null; log.trace(`${logPrefix()} CdIntroductionUI started, matchingData: ${JSON.stringify(matchingData)}`); switch (variant) { - case 'default': + case "default": default: return ; } -} \ No newline at end of file +} diff --git a/src/theme/conversion/components/cdintroduction/components/variants/CdIntroductionDefaultVariant.tsx b/src/theme/conversion/components/cdintroduction/components/variants/CdIntroductionDefaultVariant.tsx index 82d6c11..6a176db 100644 --- a/src/theme/conversion/components/cdintroduction/components/variants/CdIntroductionDefaultVariant.tsx +++ b/src/theme/conversion/components/cdintroduction/components/variants/CdIntroductionDefaultVariant.tsx @@ -1,8 +1,13 @@ import React from 'react'; import { StandardComponentProps } from "@conversiondigital/headless-basics-components/src/interfaces/standardComponentProps"; +import { CdIntroductionData } from '../index'; import {toHTML} from '@portabletext/to-html' -const CdIntroductionDefaultVariant: React.FC = ({ matchingData }) => { +interface CdIntroductionDefaultVariantProps extends StandardComponentProps { + matchingData: CdIntroductionData; +} + +const CdIntroductionDefaultVariant: React.FC = ({ matchingData }) => { const title = matchingData?.title || ''; const content = matchingData?.richtextRaw || ''; diff --git a/src/theme/conversion/components/cdservices/components/variants/CdServiceFeatureBlocksVariant.tsx b/src/theme/conversion/components/cdservices/components/variants/CdServiceFeatureBlocksVariant.tsx index 67b3c4b..0d37d16 100644 --- a/src/theme/conversion/components/cdservices/components/variants/CdServiceFeatureBlocksVariant.tsx +++ b/src/theme/conversion/components/cdservices/components/variants/CdServiceFeatureBlocksVariant.tsx @@ -45,8 +45,8 @@ export default function CdServiceFeatureBlocksVariant(props: StandardComponentPr const padStart = (number: number) => String(number).padStart(2, "0"); return ( -
-
+
+
{services.map((service: ServiceItem, idx: number) => (
))} -
-
+
+
); } diff --git a/src/theme/conversion/components/cdstatistics/components/index.tsx b/src/theme/conversion/components/cdstatistics/components/index.tsx index 7a93e95..93a160b 100644 --- a/src/theme/conversion/components/cdstatistics/components/index.tsx +++ b/src/theme/conversion/components/cdstatistics/components/index.tsx @@ -1,19 +1,45 @@ -import React from 'react'; +import React from "react"; import { componentBoilerPlate } from "@conversiondigital/headless-basics-data/src/component-tools/componentBoilerPlate"; import { ViewComponentProps } from "@conversiondigital/headless-basics-data/src/interfaces"; import { getLogger, logPrefix } from "@conversiondigital/headless-basics-data/src"; -import CdStatisticsDefaultVariant from './variants/CdStatisticsDefaultVariant'; +import CdStatisticsDefaultVariant from "./variants/CdStatisticsDefaultVariant"; export const log = getLogger("conversion.components.cdstatistics"); +// Interface for individual statistic item +export interface StatisticItem { + value: string; + description: string; +} + +// Interface for global component source +interface GlobalComponentSource { + __typename: string; + _key: string; + _type: string; + stats: StatisticItem[]; +} + +// Interface for CdStatistics component data +export interface CdStatisticsData { + __typename: string; + _key: string; + _type: string; + selectableVariant: string; + title?: string; + stats: StatisticItem[]; + sortOrder: number; + globalComponentSource?: GlobalComponentSource; +} + export default function CdStatisticsUI(dynamicComponent: ViewComponentProps) { - const { variant, matchingData } = componentBoilerPlate(dynamicComponent); + const { variant, matchingData } = componentBoilerPlate(dynamicComponent); if (!matchingData) return null; log.trace(`${logPrefix()} CdStatisticsUI started, matchingData: ${JSON.stringify(matchingData)}`); switch (variant) { - case 'default': + case "default": default: return ; } diff --git a/src/theme/conversion/components/cdstatistics/components/variants/CdStatisticsDefaultVariant.tsx b/src/theme/conversion/components/cdstatistics/components/variants/CdStatisticsDefaultVariant.tsx index d041cd6..5033835 100644 --- a/src/theme/conversion/components/cdstatistics/components/variants/CdStatisticsDefaultVariant.tsx +++ b/src/theme/conversion/components/cdstatistics/components/variants/CdStatisticsDefaultVariant.tsx @@ -1,19 +1,19 @@ -import React from 'react'; +import React from "react"; import { StandardComponentProps } from "@conversiondigital/headless-basics-components/src/interfaces/standardComponentProps"; +import { CdStatisticsData, StatisticItem } from "../index"; -interface Stat { - value: string; - description: string; +interface CdStatisticsDefaultVariantProps extends StandardComponentProps { + matchingData: CdStatisticsData; } -const CdStatisticsDefaultVariant: React.FC = ({ matchingData }) => { +const CdStatisticsDefaultVariant: React.FC = ({ matchingData }) => { const stats = matchingData?.stats || []; return (
- {stats.map((stat: Stat, index: number) => ( + {stats.map((stat: StatisticItem, index: number) => (
{stat.value}
{stat.description}
diff --git a/src/theme/conversion/components/herobanner/components/variants/IntroductionVariant.tsx b/src/theme/conversion/components/herobanner/components/variants/IntroductionVariant.tsx index a7e7d4e..49d7e3c 100644 --- a/src/theme/conversion/components/herobanner/components/variants/IntroductionVariant.tsx +++ b/src/theme/conversion/components/herobanner/components/variants/IntroductionVariant.tsx @@ -5,8 +5,8 @@ export default function IntroductionVariant(props: StandardComponentProps) { const { title, subtitle, category } = matchingData; return ( -
-
+
+
{category}

{title}