From adf2451e2fdc1e2837d6877b394d1b9526480b7d Mon Sep 17 00:00:00 2001 From: hrithikroboto Date: Mon, 17 Mar 2025 20:01:13 +0530 Subject: [PATCH 1/3] refactor: Update blog display logic with new fields --- apps/studio/schema.json | 68 +++++++++---------- .../schemaTypes/documents/blog-index.ts | 47 ++++++++----- apps/web/src/app/blog/page.tsx | 42 +++++++++--- apps/web/src/components/blog-card.tsx | 8 ++- apps/web/src/lib/sanity/query.ts | 2 + apps/web/src/lib/sanity/sanity.types.ts | 22 ++---- 6 files changed, 110 insertions(+), 79 deletions(-) diff --git a/apps/studio/schema.json b/apps/studio/schema.json index 2ec77a4..80a0943 100644 --- a/apps/studio/schema.json +++ b/apps/studio/schema.json @@ -3324,47 +3324,41 @@ }, "optional": true }, - "featured": { + "displayFeaturedBlogs": { "type": "objectAttribute", "value": { - "type": "array", - "of": { - "type": "object", - "attributes": { - "_ref": { - "type": "objectAttribute", - "value": { - "type": "string" - } - }, - "_type": { - "type": "objectAttribute", - "value": { - "type": "string", - "value": "reference" - } - }, - "_weak": { - "type": "objectAttribute", - "value": { - "type": "boolean" - }, - "optional": true - } + "type": "union", + "of": [ + { + "type": "string", + "value": "yes" }, - "dereferencesTo": "blog", - "rest": { - "type": "object", - "attributes": { - "_key": { - "type": "objectAttribute", - "value": { - "type": "string" - } - } - } + { + "type": "string", + "value": "no" } - } + ] + }, + "optional": true + }, + "featuredBlogsCount": { + "type": "objectAttribute", + "value": { + "type": "union", + "of": [ + { + "type": "string", + "value": "1" + }, + { + "type": "string", + "value": "2" + }, + { + "type": "string", + "value": "3" + } + ] }, "optional": true }, diff --git a/apps/studio/schemaTypes/documents/blog-index.ts b/apps/studio/schemaTypes/documents/blog-index.ts index 0ea13fc..a3bd1c0 100644 --- a/apps/studio/schemaTypes/documents/blog-index.ts +++ b/apps/studio/schemaTypes/documents/blog-index.ts @@ -42,24 +42,37 @@ export const blogIndex = defineType({ validation: (Rule) => Rule.required(), }), defineField({ - name: "featured", - title: "Featured Blogs", + name: "displayFeaturedBlogs", + title: "Display Featured Blogs", description: - "Choose one special blog post to highlight at the top of your blog page. This post will be displayed in a larger size to catch visitors' attention.", - type: "array", - of: [ - defineArrayMember({ - type: "reference", - to: [ - { - type: "blog", - options: { disableNew: true }, - }, - ], - validation: (rule) => [rule.required()], - }), - ], - validation: (rule) => [rule.max(1), rule.unique()], + "When enabled, this will take the top blogs from the ordered blog list and display them as featured at the top of the page", + type: "string", + options: { + list: [ + { title: "Yes", value: "yes" }, + { title: "No", value: "no" }, + ], + layout: "radio", + }, + initialValue: "yes", + group: GROUP.MAIN_CONTENT, + }), + defineField({ + name: "featuredBlogsCount", + title: "Number of Featured Blogs", + description: "Select the number of blogs to display as featured.", + type: "string", + options: { + list: [ + { title: "1", value: "1" }, + { title: "2", value: "2" }, + { title: "3", value: "3" }, + ], + layout: "radio", + direction: "horizontal", + }, + initialValue: "1", + hidden: ({ parent }) => parent?.displayFeaturedBlogs !== "yes", group: GROUP.MAIN_CONTENT, }), pageBuilderField, diff --git a/apps/web/src/app/blog/page.tsx b/apps/web/src/app/blog/page.tsx index 0132367..d3e13c5 100644 --- a/apps/web/src/app/blog/page.tsx +++ b/apps/web/src/app/blog/page.tsx @@ -26,7 +26,22 @@ export default async function BlogIndexPage() { const { data } = await fetchBlogPosts(); if (!data) notFound(); - const { blogs = [], title, description, pageBuilder = [], _id, _type } = data; + const { + blogs = [], + title, + description, + pageBuilder = [], + _id, + _type, + displayFeaturedBlogs, + featuredBlogsCount, + } = data; + console.log("🚀 ~ BlogIndexPage ~ data:", data); + + // Ensure featuredBlogsCount is a number and provide a default value + const validFeaturedBlogsCount = featuredBlogsCount + ? Number.parseInt(featuredBlogsCount) + : 0; // Handle empty blogs case if (!blogs.length) { @@ -45,24 +60,35 @@ export default async function BlogIndexPage() { ); } - // Extract featured blog and remaining blogs - const [featuredBlog, ...remainingBlogs] = blogs; + // Check if featured blogs should be displayed + const shouldDisplayFeaturedBlogs = + displayFeaturedBlogs && validFeaturedBlogsCount > 0; + + // Extract featured blogs and remaining blogs + const featuredBlogs = shouldDisplayFeaturedBlogs + ? blogs.slice(0, validFeaturedBlogsCount) + : []; + const remainingBlogs = shouldDisplayFeaturedBlogs + ? blogs.slice(validFeaturedBlogsCount) + : blogs; return (
- {/* Featured Blog */} - {featuredBlog && ( -
- + {/* Featured Blogs */} + {featuredBlogs.length > 0 && ( +
+ {featuredBlogs.map((blog) => ( + + ))}
)} {/* Blog Grid */} {remainingBlogs.length > 0 && ( -
+
{remainingBlogs.map((blog) => ( ))} diff --git a/apps/web/src/components/blog-card.tsx b/apps/web/src/components/blog-card.tsx index a9621e3..198d8d8 100644 --- a/apps/web/src/components/blog-card.tsx +++ b/apps/web/src/components/blog-card.tsx @@ -68,7 +68,13 @@ function BlogMeta({ publishedAt }: { publishedAt: string | null }) { return (
); diff --git a/apps/web/src/lib/sanity/query.ts b/apps/web/src/lib/sanity/query.ts index e0f107c..26160d8 100644 --- a/apps/web/src/lib/sanity/query.ts +++ b/apps/web/src/lib/sanity/query.ts @@ -188,6 +188,8 @@ export const queryBlogIndexPageData = defineQuery(/* groq */ ` _type, title, description, + "displayFeaturedBlogs" : displayFeaturedBlogs == "yes", + "featuredBlogsCount" : featuredBlogsCount, ${pageBuilderFragment}, "slug": slug.current, "blogs": *[_type == "blog" && (seoHideFromLists != true)] | order(orderRank asc){ diff --git a/apps/web/src/lib/sanity/sanity.types.ts b/apps/web/src/lib/sanity/sanity.types.ts index e5c9ca7..9f1b9e5 100644 --- a/apps/web/src/lib/sanity/sanity.types.ts +++ b/apps/web/src/lib/sanity/sanity.types.ts @@ -494,13 +494,8 @@ export type BlogIndex = { title?: string; description?: string; slug?: Slug; - featured?: Array<{ - _ref: string; - _type: "reference"; - _weak?: boolean; - _key: string; - [internalGroqTypeReferenceTo]?: "blog"; - }>; + displayFeaturedBlogs?: "yes" | "no"; + featuredBlogsCount?: "1" | "2" | "3"; pageBuilder?: PageBuilder; seoTitle?: string; seoDescription?: string; @@ -1782,7 +1777,7 @@ export type QuerySlugPageDataResult = { // Query: *[_type == "page" && defined(slug.current)].slug.current export type QuerySlugPagePathsResult = Array; // Variable: queryBlogIndexPageData -// Query: *[_type == "blogIndex"][0]{ ..., _id, _type, title, description, pageBuilder[]{ ..., _type, _type == "cta" => { ..., richText[]{ ..., markDefs[]{ ..., ...customLink{ openInNewTab, "href": select( type == "internal" => internal->slug.current, type == "external" => external, "#" ), } } }, buttons[]{ text, variant, _key, _type, "openInNewTab": url.openInNewTab, "href": select( url.type == "internal" => url.internal->slug.current, url.type == "external" => url.external, url.href ), }, }, _type == "hero" => { ..., image{ ..., "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"), "blurData": asset->metadata.lqip, "dominantColor": asset->metadata.palette.dominant.background, }, buttons[]{ text, variant, _key, _type, "openInNewTab": url.openInNewTab, "href": select( url.type == "internal" => url.internal->slug.current, url.type == "external" => url.external, url.href ), }, richText[]{ ..., markDefs[]{ ..., ...customLink{ openInNewTab, "href": select( type == "internal" => internal->slug.current, type == "external" => external, "#" ), } } } }, _type == "faqAccordion" => { ..., "faqs": array::compact(faqs[]->{ title, _id, _type, richText[]{ ..., markDefs[]{ ..., ...customLink{ openInNewTab, "href": select( type == "internal" => internal->slug.current, type == "external" => external, "#" ), } } } }), link{ ..., "openInNewTab": url.openInNewTab, "href": select( url.type == "internal" => url.internal->slug.current, url.type == "external" => url.external, url.href ) } }, _type == "subscribeNewsletter" => { ..., "subTitle": subTitle[]{ ..., markDefs[]{ ..., ...customLink{ openInNewTab, "href": select( type == "internal" => internal->slug.current, type == "external" => external, "#" ), } } }, "helperText": helperText[]{ ..., markDefs[]{ ..., ...customLink{ openInNewTab, "href": select( type == "internal" => internal->slug.current, type == "external" => external, "#" ), } } } }, _type == "imageLinkCards" => { ..., richText[]{ ..., markDefs[]{ ..., ...customLink{ openInNewTab, "href": select( type == "internal" => internal->slug.current, type == "external" => external, "#" ), } } }, buttons[]{ text, variant, _key, _type, "openInNewTab": url.openInNewTab, "href": select( url.type == "internal" => url.internal->slug.current, url.type == "external" => url.external, url.href ), }, "cards": array::compact(cards[]{ ..., "openInNewTab": url.openInNewTab, "href": select( url.type == "internal" => url.internal->slug.current, url.type == "external" => url.external, url.href ), image{ ..., "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"), "blurData": asset->metadata.lqip, "dominantColor": asset->metadata.palette.dominant.background, }, }) } }, "slug": slug.current, "blogs": *[_type == "blog" && (seoHideFromLists != true)] | order(orderRank asc){ _type, _id, title, description, "slug":slug.current, richText, orderRank, image{ ..., "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"), "blurData": asset->metadata.lqip, "dominantColor": asset->metadata.palette.dominant.background, }, publishedAt, authors[0]->{ _id, name, position, image{ ..., "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"), "blurData": asset->metadata.lqip, "dominantColor": asset->metadata.palette.dominant.background, } } } } +// Query: *[_type == "blogIndex"][0]{ ..., _id, _type, title, description, "displayFeaturedBlogs" : displayFeaturedBlogs == "yes", "featuredBlogsCount" : featuredBlogsCount, pageBuilder[]{ ..., _type, _type == "cta" => { ..., richText[]{ ..., markDefs[]{ ..., ...customLink{ openInNewTab, "href": select( type == "internal" => internal->slug.current, type == "external" => external, "#" ), } } }, buttons[]{ text, variant, _key, _type, "openInNewTab": url.openInNewTab, "href": select( url.type == "internal" => url.internal->slug.current, url.type == "external" => url.external, url.href ), }, }, _type == "hero" => { ..., image{ ..., "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"), "blurData": asset->metadata.lqip, "dominantColor": asset->metadata.palette.dominant.background, }, buttons[]{ text, variant, _key, _type, "openInNewTab": url.openInNewTab, "href": select( url.type == "internal" => url.internal->slug.current, url.type == "external" => url.external, url.href ), }, richText[]{ ..., markDefs[]{ ..., ...customLink{ openInNewTab, "href": select( type == "internal" => internal->slug.current, type == "external" => external, "#" ), } } } }, _type == "faqAccordion" => { ..., "faqs": array::compact(faqs[]->{ title, _id, _type, richText[]{ ..., markDefs[]{ ..., ...customLink{ openInNewTab, "href": select( type == "internal" => internal->slug.current, type == "external" => external, "#" ), } } } }), link{ ..., "openInNewTab": url.openInNewTab, "href": select( url.type == "internal" => url.internal->slug.current, url.type == "external" => url.external, url.href ) } }, _type == "subscribeNewsletter" => { ..., "subTitle": subTitle[]{ ..., markDefs[]{ ..., ...customLink{ openInNewTab, "href": select( type == "internal" => internal->slug.current, type == "external" => external, "#" ), } } }, "helperText": helperText[]{ ..., markDefs[]{ ..., ...customLink{ openInNewTab, "href": select( type == "internal" => internal->slug.current, type == "external" => external, "#" ), } } } }, _type == "imageLinkCards" => { ..., richText[]{ ..., markDefs[]{ ..., ...customLink{ openInNewTab, "href": select( type == "internal" => internal->slug.current, type == "external" => external, "#" ), } } }, buttons[]{ text, variant, _key, _type, "openInNewTab": url.openInNewTab, "href": select( url.type == "internal" => url.internal->slug.current, url.type == "external" => url.external, url.href ), }, "cards": array::compact(cards[]{ ..., "openInNewTab": url.openInNewTab, "href": select( url.type == "internal" => url.internal->slug.current, url.type == "external" => url.external, url.href ), image{ ..., "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"), "blurData": asset->metadata.lqip, "dominantColor": asset->metadata.palette.dominant.background, }, }) } }, "slug": slug.current, "blogs": *[_type == "blog" && (seoHideFromLists != true)] | order(orderRank asc){ _type, _id, title, description, "slug":slug.current, richText, orderRank, image{ ..., "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"), "blurData": asset->metadata.lqip, "dominantColor": asset->metadata.palette.dominant.background, }, publishedAt, authors[0]->{ _id, name, position, image{ ..., "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"), "blurData": asset->metadata.lqip, "dominantColor": asset->metadata.palette.dominant.background, } } } } export type QueryBlogIndexPageDataResult = { _id: string; _type: "blogIndex"; @@ -1792,13 +1787,8 @@ export type QueryBlogIndexPageDataResult = { title: string | null; description: string | null; slug: string | null; - featured?: Array<{ - _ref: string; - _type: "reference"; - _weak?: boolean; - _key: string; - [internalGroqTypeReferenceTo]?: "blog"; - }>; + displayFeaturedBlogs: false | true; + featuredBlogsCount: "1" | "2" | "3" | null; pageBuilder: Array< | { _key: string; @@ -2518,7 +2508,7 @@ declare module "@sanity/client" { '*[_type == "homePage" && _id == "homePage"][0]{\n ...,\n _id,\n _type,\n "slug": slug.current,\n title,\n description,\n \n pageBuilder[]{\n ...,\n _type,\n \n _type == "cta" => {\n ...,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n,\n \n buttons[]{\n text,\n variant,\n _key,\n _type,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n }\n,\n }\n,\n \n _type == "hero" => {\n ...,\n \n image{\n ...,\n "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"),\n "blurData": asset->metadata.lqip,\n "dominantColor": asset->metadata.palette.dominant.background,\n }\n,\n \n buttons[]{\n text,\n variant,\n _key,\n _type,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n }\n,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n\n }\n,\n \n _type == "faqAccordion" => {\n ...,\n \n "faqs": array::compact(faqs[]->{\n title,\n _id,\n _type,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n\n })\n,\n link{\n ...,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n )\n }\n }\n,\n \n _type == "subscribeNewsletter" => {\n ...,\n "subTitle": subTitle[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n },\n "helperText": helperText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n }\n,\n \n _type == "imageLinkCards" => {\n ...,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n,\n \n buttons[]{\n text,\n variant,\n _key,\n _type,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n }\n,\n "cards": array::compact(cards[]{\n ...,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n \n image{\n ...,\n "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"),\n "blurData": asset->metadata.lqip,\n "dominantColor": asset->metadata.palette.dominant.background,\n }\n,\n })\n }\n\n }\n\n }': QueryHomePageDataResult; '\n *[_type == "page" && slug.current == $slug][0]{\n ...,\n "slug": slug.current,\n \n pageBuilder[]{\n ...,\n _type,\n \n _type == "cta" => {\n ...,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n,\n \n buttons[]{\n text,\n variant,\n _key,\n _type,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n }\n,\n }\n,\n \n _type == "hero" => {\n ...,\n \n image{\n ...,\n "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"),\n "blurData": asset->metadata.lqip,\n "dominantColor": asset->metadata.palette.dominant.background,\n }\n,\n \n buttons[]{\n text,\n variant,\n _key,\n _type,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n }\n,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n\n }\n,\n \n _type == "faqAccordion" => {\n ...,\n \n "faqs": array::compact(faqs[]->{\n title,\n _id,\n _type,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n\n })\n,\n link{\n ...,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n )\n }\n }\n,\n \n _type == "subscribeNewsletter" => {\n ...,\n "subTitle": subTitle[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n },\n "helperText": helperText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n }\n,\n \n _type == "imageLinkCards" => {\n ...,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n,\n \n buttons[]{\n text,\n variant,\n _key,\n _type,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n }\n,\n "cards": array::compact(cards[]{\n ...,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n \n image{\n ...,\n "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"),\n "blurData": asset->metadata.lqip,\n "dominantColor": asset->metadata.palette.dominant.background,\n }\n,\n })\n }\n\n }\n\n }\n ': QuerySlugPageDataResult; '\n *[_type == "page" && defined(slug.current)].slug.current\n': QuerySlugPagePathsResult; - '\n *[_type == "blogIndex"][0]{\n ...,\n _id,\n _type,\n title,\n description,\n \n pageBuilder[]{\n ...,\n _type,\n \n _type == "cta" => {\n ...,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n,\n \n buttons[]{\n text,\n variant,\n _key,\n _type,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n }\n,\n }\n,\n \n _type == "hero" => {\n ...,\n \n image{\n ...,\n "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"),\n "blurData": asset->metadata.lqip,\n "dominantColor": asset->metadata.palette.dominant.background,\n }\n,\n \n buttons[]{\n text,\n variant,\n _key,\n _type,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n }\n,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n\n }\n,\n \n _type == "faqAccordion" => {\n ...,\n \n "faqs": array::compact(faqs[]->{\n title,\n _id,\n _type,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n\n })\n,\n link{\n ...,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n )\n }\n }\n,\n \n _type == "subscribeNewsletter" => {\n ...,\n "subTitle": subTitle[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n },\n "helperText": helperText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n }\n,\n \n _type == "imageLinkCards" => {\n ...,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n,\n \n buttons[]{\n text,\n variant,\n _key,\n _type,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n }\n,\n "cards": array::compact(cards[]{\n ...,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n \n image{\n ...,\n "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"),\n "blurData": asset->metadata.lqip,\n "dominantColor": asset->metadata.palette.dominant.background,\n }\n,\n })\n }\n\n }\n,\n "slug": slug.current,\n "blogs": *[_type == "blog" && (seoHideFromLists != true)] | order(orderRank asc){\n \n _type,\n _id,\n title,\n description,\n "slug":slug.current,\n richText,\n orderRank,\n \n image{\n ...,\n "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"),\n "blurData": asset->metadata.lqip,\n "dominantColor": asset->metadata.palette.dominant.background,\n }\n,\n publishedAt,\n \n authors[0]->{\n _id,\n name,\n position,\n \n image{\n ...,\n "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"),\n "blurData": asset->metadata.lqip,\n "dominantColor": asset->metadata.palette.dominant.background,\n }\n\n }\n\n\n }\n }\n': QueryBlogIndexPageDataResult; + '\n *[_type == "blogIndex"][0]{\n ...,\n _id,\n _type,\n title,\n description,\n "displayFeaturedBlogs" : displayFeaturedBlogs == "yes",\n "featuredBlogsCount" : featuredBlogsCount,\n \n pageBuilder[]{\n ...,\n _type,\n \n _type == "cta" => {\n ...,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n,\n \n buttons[]{\n text,\n variant,\n _key,\n _type,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n }\n,\n }\n,\n \n _type == "hero" => {\n ...,\n \n image{\n ...,\n "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"),\n "blurData": asset->metadata.lqip,\n "dominantColor": asset->metadata.palette.dominant.background,\n }\n,\n \n buttons[]{\n text,\n variant,\n _key,\n _type,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n }\n,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n\n }\n,\n \n _type == "faqAccordion" => {\n ...,\n \n "faqs": array::compact(faqs[]->{\n title,\n _id,\n _type,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n\n })\n,\n link{\n ...,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n )\n }\n }\n,\n \n _type == "subscribeNewsletter" => {\n ...,\n "subTitle": subTitle[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n },\n "helperText": helperText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n }\n,\n \n _type == "imageLinkCards" => {\n ...,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n,\n \n buttons[]{\n text,\n variant,\n _key,\n _type,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n }\n,\n "cards": array::compact(cards[]{\n ...,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n \n image{\n ...,\n "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"),\n "blurData": asset->metadata.lqip,\n "dominantColor": asset->metadata.palette.dominant.background,\n }\n,\n })\n }\n\n }\n,\n "slug": slug.current,\n "blogs": *[_type == "blog" && (seoHideFromLists != true)] | order(orderRank asc){\n \n _type,\n _id,\n title,\n description,\n "slug":slug.current,\n richText,\n orderRank,\n \n image{\n ...,\n "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"),\n "blurData": asset->metadata.lqip,\n "dominantColor": asset->metadata.palette.dominant.background,\n }\n,\n publishedAt,\n \n authors[0]->{\n _id,\n name,\n position,\n \n image{\n ...,\n "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"),\n "blurData": asset->metadata.lqip,\n "dominantColor": asset->metadata.palette.dominant.background,\n }\n\n }\n\n\n }\n }\n': QueryBlogIndexPageDataResult; '\n *[_type == "blog" && slug.current == $slug][0]{\n ...,\n "slug": slug.current,\n \n authors[0]->{\n _id,\n name,\n position,\n \n image{\n ...,\n "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"),\n "blurData": asset->metadata.lqip,\n "dominantColor": asset->metadata.palette.dominant.background,\n }\n\n }\n,\n \n image{\n ...,\n "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"),\n "blurData": asset->metadata.lqip,\n "dominantColor": asset->metadata.palette.dominant.background,\n }\n,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n,\n \n pageBuilder[]{\n ...,\n _type,\n \n _type == "cta" => {\n ...,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n,\n \n buttons[]{\n text,\n variant,\n _key,\n _type,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n }\n,\n }\n,\n \n _type == "hero" => {\n ...,\n \n image{\n ...,\n "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"),\n "blurData": asset->metadata.lqip,\n "dominantColor": asset->metadata.palette.dominant.background,\n }\n,\n \n buttons[]{\n text,\n variant,\n _key,\n _type,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n }\n,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n\n }\n,\n \n _type == "faqAccordion" => {\n ...,\n \n "faqs": array::compact(faqs[]->{\n title,\n _id,\n _type,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n\n })\n,\n link{\n ...,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n )\n }\n }\n,\n \n _type == "subscribeNewsletter" => {\n ...,\n "subTitle": subTitle[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n },\n "helperText": helperText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n }\n,\n \n _type == "imageLinkCards" => {\n ...,\n \n richText[]{\n ...,\n \n markDefs[]{\n ...,\n \n ...customLink{\n openInNewTab,\n "href": select(\n type == "internal" => internal->slug.current,\n type == "external" => external,\n "#"\n ),\n }\n\n }\n\n }\n,\n \n buttons[]{\n text,\n variant,\n _key,\n _type,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n }\n,\n "cards": array::compact(cards[]{\n ...,\n "openInNewTab": url.openInNewTab,\n "href": select(\n url.type == "internal" => url.internal->slug.current,\n url.type == "external" => url.external,\n url.href\n ),\n \n image{\n ...,\n "alt": coalesce(asset->altText, asset->originalFilename, "Image-Broken"),\n "blurData": asset->metadata.lqip,\n "dominantColor": asset->metadata.palette.dominant.background,\n }\n,\n })\n }\n\n }\n\n }\n': QueryBlogSlugPageDataResult; '\n *[_type == "blog" && defined(slug.current)].slug.current\n': QueryBlogPathsResult; '\n *[_type == "homePage" && _id == $id][0]{\n \n _id,\n _type,\n "title": select(\n defined(ogTitle) => ogTitle,\n defined(seoTitle) => seoTitle,\n title\n ),\n "description": select(\n defined(ogDescription) => ogDescription,\n defined(seoDescription) => seoDescription,\n description\n ),\n "image": image.asset->url + "?w=566&h=566&dpr=2&fit=max",\n "dominantColor": image.asset->metadata.palette.dominant.background,\n "seoImage": seoImage.asset->url + "?w=1200&h=630&dpr=2&fit=max", \n "logo": *[_type == "settings"][0].logo.asset->url + "?w=80&h=40&dpr=3&fit=max&q=100",\n "date": coalesce(date, _createdAt)\n\n }\n ': QueryHomePageOGDataResult; From c7095c1dac214849e7af735e0fe0185b661f109b Mon Sep 17 00:00:00 2001 From: hrithikroboto Date: Mon, 17 Mar 2025 20:08:20 +0530 Subject: [PATCH 2/3] refactor: Refactor fetchBlogPosts function with error handling --- apps/web/src/app/blog/page.tsx | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/apps/web/src/app/blog/page.tsx b/apps/web/src/app/blog/page.tsx index d3e13c5..9e27ddd 100644 --- a/apps/web/src/app/blog/page.tsx +++ b/apps/web/src/app/blog/page.tsx @@ -5,26 +5,21 @@ import { PageBuilder } from "@/components/pagebuilder"; import { sanityFetch } from "@/lib/sanity/live"; import { queryBlogIndexPageData } from "@/lib/sanity/query"; import { getMetaData } from "@/lib/seo"; +import { handleErrors } from "@/utils"; -/** - * Fetches blog posts data from Sanity CMS - */ async function fetchBlogPosts() { - return await sanityFetch({ query: queryBlogIndexPageData }); + return await handleErrors(sanityFetch({ query: queryBlogIndexPageData })); } export async function generateMetadata() { - try { - const { data } = await fetchBlogPosts(); - return getMetaData(data ?? {}); - } catch { - return getMetaData({}); - } + const [result, err] = await fetchBlogPosts(); + if (err || !result?.data) return getMetaData({}); + return getMetaData(result.data); } export default async function BlogIndexPage() { - const { data } = await fetchBlogPosts(); - if (!data) notFound(); + const [res, err] = await fetchBlogPosts(); + if (err || !res?.data) notFound(); const { blogs = [], @@ -35,15 +30,12 @@ export default async function BlogIndexPage() { _type, displayFeaturedBlogs, featuredBlogsCount, - } = data; - console.log("🚀 ~ BlogIndexPage ~ data:", data); + } = res.data; - // Ensure featuredBlogsCount is a number and provide a default value const validFeaturedBlogsCount = featuredBlogsCount ? Number.parseInt(featuredBlogsCount) : 0; - // Handle empty blogs case if (!blogs.length) { return (
@@ -60,11 +52,9 @@ export default async function BlogIndexPage() { ); } - // Check if featured blogs should be displayed const shouldDisplayFeaturedBlogs = displayFeaturedBlogs && validFeaturedBlogsCount > 0; - // Extract featured blogs and remaining blogs const featuredBlogs = shouldDisplayFeaturedBlogs ? blogs.slice(0, validFeaturedBlogsCount) : []; @@ -77,7 +67,6 @@ export default async function BlogIndexPage() {
- {/* Featured Blogs */} {featuredBlogs.length > 0 && (
{featuredBlogs.map((blog) => ( @@ -86,7 +75,6 @@ export default async function BlogIndexPage() {
)} - {/* Blog Grid */} {remainingBlogs.length > 0 && (
{remainingBlogs.map((blog) => ( From 57226906bce78e5e092a3837e66853328997f875 Mon Sep 17 00:00:00 2001 From: hrithikroboto Date: Mon, 17 Mar 2025 20:19:18 +0530 Subject: [PATCH 3/3] refactor: simplify blog index page generation function --- apps/studio/scripts/create-data.ts | 2 +- apps/studio/utils/mock-data.ts | 19 +++---------------- 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/apps/studio/scripts/create-data.ts b/apps/studio/scripts/create-data.ts index 5607eff..9e7d271 100644 --- a/apps/studio/scripts/create-data.ts +++ b/apps/studio/scripts/create-data.ts @@ -136,7 +136,7 @@ async function createData() { console.log("\n"); console.log("📚 Generating blog index page..."); - const blogIndexPage = generateBlogIndexPage(blogPages); + const blogIndexPage = generateBlogIndexPage(); transaction.createIfNotExists(blogIndexPage); console.log("✅ Created blog index page"); diff --git a/apps/studio/utils/mock-data.ts b/apps/studio/utils/mock-data.ts index fe4f11a..fdb5874 100644 --- a/apps/studio/utils/mock-data.ts +++ b/apps/studio/utils/mock-data.ts @@ -424,11 +424,7 @@ export function generateMockBlogPages({ }); } -type Blog = ReturnType[number]; - -export function generateBlogIndexPage(blogs: Blog[]) { - const featuredBlog = faker.helpers.arrayElement(blogs); - +export function generateBlogIndexPage() { return { _id: "blogIndex" as const, _type: "blogIndex" as const, @@ -439,16 +435,7 @@ export function generateBlogIndexPage(blogs: Blog[]) { type: "slug", current: "/blog", }, - ...(featuredBlog?._id - ? { - featured: [ - { - _type: "reference", - _key: faker.string.uuid(), - _ref: featuredBlog._id, - }, - ], - } - : {}), + displayFeaturedBlogs: "yes", + featuredBlogsCount: "1", }; }