From 06c7eab0c21a3b5d4ad1df3505b4374a5a375943 Mon Sep 17 00:00:00 2001 From: gmolki Date: Thu, 14 Nov 2024 20:07:35 +0100 Subject: [PATCH] feat: add article publishing information component --- src/builder-registry.ts | 11 +-- .../cmp.builder.tsx | 20 +++++ .../BlogArticlePublishingInformation/cmp.tsx | 81 +++++++++++++++++++ .../index.tsx | 1 + .../BlogArticlePublishingInformation/types.ts | 3 + src/utils/blog/buildArticleSchemaMarkup.tsx | 6 +- 6 files changed, 115 insertions(+), 7 deletions(-) create mode 100644 src/components/Blog/BlogArticlePublishingInformation/cmp.builder.tsx create mode 100644 src/components/Blog/BlogArticlePublishingInformation/cmp.tsx create mode 100644 src/components/Blog/BlogArticlePublishingInformation/index.tsx create mode 100644 src/components/Blog/BlogArticlePublishingInformation/types.ts diff --git a/src/builder-registry.ts b/src/builder-registry.ts index 2ac706c..ff340a7 100644 --- a/src/builder-registry.ts +++ b/src/builder-registry.ts @@ -1,17 +1,18 @@ import { builder } from "@builder.io/react"; import "./components/BlockTag/cmp.builder"; +import "./components/Blog/BlogArticleBreadcrumb/cmp.builder"; +import "./components/Blog/BlogArticlePublishingInformation/cmp.builder"; import "./components/Blog/BlogArticlesGrid/cmp.builder"; -import "./components/Blog/LatestBlogArticles/cmp.builder"; -import "./components/Blog/HighlightedBlogArticle/cmp.builder"; -import "./components/Blog/PinnedBlogArticles/cmp.builder"; +import "./components/Blog/BlogBreadcrumb/cmp.builder"; import "./components/Blog/BlogCategories/cmp.builder"; import "./components/Blog/BlogCategoriesFilter/cmp.builder"; import "./components/Blog/BlogTags/cmp.builder"; import "./components/Blog/BlogTagsFilter/cmp.builder"; import "./components/Blog/CategorizationDisplayName/cmp.builder"; -import "./components/Blog/BlogBreadcrumb/cmp.builder"; -import "./components/Blog/BlogArticleBreadcrumb/cmp.builder"; +import "./components/Blog/HighlightedBlogArticle/cmp.builder"; +import "./components/Blog/LatestBlogArticles/cmp.builder"; +import "./components/Blog/PinnedBlogArticles/cmp.builder"; import "./components/Box/cmp.builder"; import "./components/Breadcrumb/cmp.builder"; import "./components/BulletList/cmp.builder"; diff --git a/src/components/Blog/BlogArticlePublishingInformation/cmp.builder.tsx b/src/components/Blog/BlogArticlePublishingInformation/cmp.builder.tsx new file mode 100644 index 0000000..a6cdab9 --- /dev/null +++ b/src/components/Blog/BlogArticlePublishingInformation/cmp.builder.tsx @@ -0,0 +1,20 @@ +import { Builder } from "@builder.io/react"; +import { DEFAULT_PROPS } from "@/constants/builderProps"; +import { CSS_EDITABLE_INPUTS_ADVANCED } from "@/constants/builderInputs"; +import BlogArticlePublishingInformation from "./cmp"; + +Builder.registerComponent(BlogArticlePublishingInformation, { + ...DEFAULT_PROPS, + name: "Blog - Article Publishing Information", + image: + "https://cdn.builder.io/api/v1/image/assets%2Fd72545ec06f647d993e1349bf57ebd7f%2Ffd9c99abfd534bbf9ebc839eed5acfd1", + inputs: [ + ...DEFAULT_PROPS.inputs, + { + name: "variant", + type: "string", + defaultValue: "readTime", + enum: ["readTime", "writtenBy"], + }, + ], +}); diff --git a/src/components/Blog/BlogArticlePublishingInformation/cmp.tsx b/src/components/Blog/BlogArticlePublishingInformation/cmp.tsx new file mode 100644 index 0000000..8ba00d4 --- /dev/null +++ b/src/components/Blog/BlogArticlePublishingInformation/cmp.tsx @@ -0,0 +1,81 @@ +import Image from "next/image"; +import { BlogArticlePublishingInformationProps } from "./types"; +import { useEffect, useMemo, useState } from "react"; +import { useRouter } from "next/router"; +import { fetchBuilderData } from "@/utils/fetchBuilderData"; +import { buildArticleSchemaMarkup } from "@/utils/blog/buildArticleSchemaMarkup"; + +export const BlogArticlePublishingInformation = ({ + variant, +}: BlogArticlePublishingInformationProps) => { + const router = useRouter(); + + const [currentPage, setCurrentPage] = useState(); + + const articleSchemaMarkup = useMemo( + () => buildArticleSchemaMarkup(currentPage), + [currentPage] + ); + + useEffect(() => { + async function getCurrentBlogArticlePage() { + const page = await fetchBuilderData("get", [ + "blog-article", + { + userAttributes: { + urlPath: router.asPath, + }, + includeRefs: true, + }, + ]); + + setCurrentPage(page); + } + + getCurrentBlogArticlePage(); + }, [router.asPath]); + + if (!articleSchemaMarkup) return null; + + const { author, datePublished, timeRequired } = articleSchemaMarkup; + + // timeRequired is in ISO 8601 duration format (but only minutes) + const readableTimeRequired = timeRequired.match(/PT(\d+)M/)?.[1]; + + let title; + let subtitle; + + switch (variant) { + case "readTime": + title = author.name; + subtitle = ( + <> + {timeRequired ? `${readableTimeRequired} min. read` : ""}{" "} + {datePublished ? `- ${datePublished}` : ""} + + ); + break; + case "writtenBy": + title = `Written by ${author.name}`; + subtitle = author.title; + break; + } + + return ( +
+ Publisher +
+

{title}

+

{subtitle}

+
+
+ ); +}; + +export default BlogArticlePublishingInformation; diff --git a/src/components/Blog/BlogArticlePublishingInformation/index.tsx b/src/components/Blog/BlogArticlePublishingInformation/index.tsx new file mode 100644 index 0000000..a05ebc5 --- /dev/null +++ b/src/components/Blog/BlogArticlePublishingInformation/index.tsx @@ -0,0 +1 @@ +export { default } from "./cmp"; diff --git a/src/components/Blog/BlogArticlePublishingInformation/types.ts b/src/components/Blog/BlogArticlePublishingInformation/types.ts new file mode 100644 index 0000000..a8dd2c8 --- /dev/null +++ b/src/components/Blog/BlogArticlePublishingInformation/types.ts @@ -0,0 +1,3 @@ +export type BlogArticlePublishingInformationProps = { + variant: "readTime" | "writtenBy"; +}; diff --git a/src/utils/blog/buildArticleSchemaMarkup.tsx b/src/utils/blog/buildArticleSchemaMarkup.tsx index 395ce27..5b1e22f 100644 --- a/src/utils/blog/buildArticleSchemaMarkup.tsx +++ b/src/utils/blog/buildArticleSchemaMarkup.tsx @@ -22,6 +22,7 @@ export const buildArticleSchemaMarkup = (page: any) => { const dateModified = formatDate(getField(["lastUpdated"])); const datePublished = formatDate(getField(["firstPublished"])); + const timeRequired = `PT${getField(["data", "metadata", "timeRequired"])}M`; return { "@context": "https://schema.org", @@ -34,8 +35,9 @@ export const buildArticleSchemaMarkup = (page: any) => { image: getField(["data", "featureImage"]), thumbnailUrl: getField(["data", "thumbnailImage"]), dateCreated: datePublished, - datePublished: datePublished, - dateModified: dateModified, + datePublished, + dateModified, + timeRequired, author: { "@type": typeFromModel(page.data?.metadata?.author?.model), ...getField(["data", "metadata", "author", "value", "data"]),