From 604e526dc45570c5cf9aa67e122014dad5cfd924 Mon Sep 17 00:00:00 2001 From: Kipruto <43873157+kelvinkipruto@users.noreply.github.com> Date: Thu, 27 Jun 2024 15:09:23 +0300 Subject: [PATCH 1/7] Add CMS blocks Signed-off-by: Kipruto <43873157+kelvinkipruto@users.noreply.github.com> --- apps/roboshield/src/payload/blocks/Content.ts | 33 +++++++++++++++++++ .../src/payload/collections/Pages.ts | 3 +- 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 apps/roboshield/src/payload/blocks/Content.ts diff --git a/apps/roboshield/src/payload/blocks/Content.ts b/apps/roboshield/src/payload/blocks/Content.ts new file mode 100644 index 000000000..900b73e74 --- /dev/null +++ b/apps/roboshield/src/payload/blocks/Content.ts @@ -0,0 +1,33 @@ +import { Block } from "payload/types"; +import { slateEditor } from "@payloadcms/richtext-slate"; + +export const Content: Block = { + slug: "content", + labels: { + singular: "Content", + plural: "Content", + }, + fields: [ + { + name: "content", + type: "richText", + label: "Content", + editor: slateEditor({ + admin: { + elements: [ + "h1", + "h2", + "h3", + "h4", + "h5", + "h6", + "link", + "ol", + "ul", + "indent", + ], + }, + }), + }, + ], +}; diff --git a/apps/roboshield/src/payload/collections/Pages.ts b/apps/roboshield/src/payload/collections/Pages.ts index e9c31e747..e97b25aca 100644 --- a/apps/roboshield/src/payload/collections/Pages.ts +++ b/apps/roboshield/src/payload/collections/Pages.ts @@ -1,6 +1,7 @@ import { CollectionConfig } from "payload/types"; import fullTitle from "../fields/fullTitle"; import slug from "../fields/slug"; +import { Content } from "../blocks/Content"; const Pages: CollectionConfig = { slug: "pages", @@ -25,7 +26,7 @@ const Pages: CollectionConfig = { { name: "blocks", type: "blocks", - blocks: [], + blocks: [Content], localized: true, admin: { initCollapsed: true, From 5fe71d69bd07c6359f5920381d1ea4c876e12041 Mon Sep 17 00:00:00 2001 From: Kipruto <43873157+kelvinkipruto@users.noreply.github.com> Date: Mon, 1 Jul 2024 16:56:29 +0300 Subject: [PATCH 2/7] Migrate changes Signed-off-by: Kipruto <43873157+kelvinkipruto@users.noreply.github.com> --- .../src/components/Content/Content.tsx | 36 ++++++++++++ .../src/components/Content/index.ts | 3 + .../components/Statistics/StatisticCard.tsx | 11 ++-- .../src/components/Statistics/Statistics.tsx | 20 +++---- .../src/payload/blocks/Statistics.ts | 58 +++++++++++++++++++ .../src/payload/collections/Pages.ts | 3 +- 6 files changed, 112 insertions(+), 19 deletions(-) create mode 100644 apps/roboshield/src/components/Content/Content.tsx create mode 100644 apps/roboshield/src/components/Content/index.ts create mode 100644 apps/roboshield/src/payload/blocks/Statistics.ts diff --git a/apps/roboshield/src/components/Content/Content.tsx b/apps/roboshield/src/components/Content/Content.tsx new file mode 100644 index 000000000..3286657b5 --- /dev/null +++ b/apps/roboshield/src/components/Content/Content.tsx @@ -0,0 +1,36 @@ +import { Section } from "@commons-ui/core"; +import RichText, { Children } from "@/roboshield/components/RichText"; + +interface ContentProps { + content: Children; +} + +export default function Content({ content }: ContentProps) { + return ( +
+ ({ + mb: "30px", + "& h2": { + typography: { xs: "h4", md: "h2" }, + }, + "& p,& a, & li": { + typography: { xs: "body1", md: "subheading" }, + mb: 2, + }, + "& a": { + textDecorationColor: theme.palette.primary.main, + }, + })} + /> +
+ ); +} diff --git a/apps/roboshield/src/components/Content/index.ts b/apps/roboshield/src/components/Content/index.ts new file mode 100644 index 000000000..7238170d4 --- /dev/null +++ b/apps/roboshield/src/components/Content/index.ts @@ -0,0 +1,3 @@ +import Content from "./Content"; + +export default Content; diff --git a/apps/roboshield/src/components/Statistics/StatisticCard.tsx b/apps/roboshield/src/components/Statistics/StatisticCard.tsx index 5beb12285..7e1374b8b 100644 --- a/apps/roboshield/src/components/Statistics/StatisticCard.tsx +++ b/apps/roboshield/src/components/Statistics/StatisticCard.tsx @@ -4,6 +4,7 @@ import { styled } from "@mui/material/styles"; import React from "react"; import RichText from "@/roboshield/components/RichText"; +import { Statistics } from "./Statistics"; const StatisticCardRoot = styled(Card, { slot: "Root", @@ -18,11 +19,9 @@ const StatisticCardRoot = styled(Card, { width: "332px", }, })); -export default function StatisticCard(props: any) { - const { icon, label, value, description } = props; +export default function StatisticCard(props: Statistics) { + const { icon, name, value, description } = props; - const imageSrc = icon?.src || icon?.url || icon; - const imageAlt = icon?.alt || label; return (
- {label} + {name} diff --git a/apps/roboshield/src/components/Statistics/Statistics.tsx b/apps/roboshield/src/components/Statistics/Statistics.tsx index 31615abe3..bd7790a46 100644 --- a/apps/roboshield/src/components/Statistics/Statistics.tsx +++ b/apps/roboshield/src/components/Statistics/Statistics.tsx @@ -2,21 +2,17 @@ import { Box, Grid } from "@mui/material"; import { Section } from "@commons-ui/core"; import { RichTypography } from "@commons-ui/next"; import StatisticCard from "./StatisticCard"; +import type { Children } from "@/roboshield/components/RichText"; +export type Statistics = { + name: string; + value: string; + icon: string; + description: Children; +}; export interface StatiscticsProps { title: string; - statistics: Array<{ - name: string; - value: string; - label: string; - icon: string; - description: Array<{ - type: string; - children: Array<{ - text: string; - }>; - }>; - }>; + statistics: Statistics[]; } export default function Statistics({ title, statistics }: StatiscticsProps) { diff --git a/apps/roboshield/src/payload/blocks/Statistics.ts b/apps/roboshield/src/payload/blocks/Statistics.ts new file mode 100644 index 000000000..70f0fa37c --- /dev/null +++ b/apps/roboshield/src/payload/blocks/Statistics.ts @@ -0,0 +1,58 @@ +import { Block } from "payload/types"; +import image from "../fields/image"; + +export const Statistics: Block = { + slug: "statistics", + labels: { + singular: "Statistics", + plural: "Statistics", + }, + fields: [ + { + name: "title", + type: "text", + label: "Title", + required: true, + defaultValue: "Statistics", + }, + { + name: "statistics", + type: "array", + label: "Statistics", + labels: { + singular: "Statistic", + plural: "Statistics", + }, + fields: [ + { + name: "name", + type: "text", + label: "Name", + required: true, + }, + { + name: "value", + type: "number", + label: "Number", + required: true, + }, + { + name: "description", + type: "richText", + label: "Description", + required: true, + }, + image({ + overrides: { + name: "icon", + localized: true, + admin: { + description: + "An icon to represent this statistic. SVG format is recommended.", + }, + }, + }), + ], + }, + ], +}; diff --git a/apps/roboshield/src/payload/collections/Pages.ts b/apps/roboshield/src/payload/collections/Pages.ts index e97b25aca..d8ce079e1 100644 --- a/apps/roboshield/src/payload/collections/Pages.ts +++ b/apps/roboshield/src/payload/collections/Pages.ts @@ -2,6 +2,7 @@ import { CollectionConfig } from "payload/types"; import fullTitle from "../fields/fullTitle"; import slug from "../fields/slug"; import { Content } from "../blocks/Content"; +import { Statistics } from "../blocks/Statistics"; const Pages: CollectionConfig = { slug: "pages", @@ -26,7 +27,7 @@ const Pages: CollectionConfig = { { name: "blocks", type: "blocks", - blocks: [Content], + blocks: [Content, Statistics], localized: true, admin: { initCollapsed: true, From b70cc2c5b798ccd7591f5e7a22306a43a4a0902e Mon Sep 17 00:00:00 2001 From: Kipruto <43873157+kelvinkipruto@users.noreply.github.com> Date: Mon, 1 Jul 2024 17:36:56 +0300 Subject: [PATCH 3/7] FIx types Signed-off-by: Kipruto <43873157+kelvinkipruto@users.noreply.github.com> --- apps/roboshield/payload-types.ts | 43 ++++++++++++++++--- .../BlockRenderer/BlockRenderer.tsx | 10 +++-- .../src/components/Content/Content.tsx | 11 +++-- .../src/components/Statistics/Statistics.tsx | 21 ++++----- .../src/components/Statistics/index.ts | 3 ++ 5 files changed, 62 insertions(+), 26 deletions(-) create mode 100644 apps/roboshield/src/components/Statistics/index.ts diff --git a/apps/roboshield/payload-types.ts b/apps/roboshield/payload-types.ts index 0251bfb07..357b67364 100644 --- a/apps/roboshield/payload-types.ts +++ b/apps/roboshield/payload-types.ts @@ -47,13 +47,42 @@ export interface Page { fullTitle?: string | null; slug?: string | null; blocks?: - | { - title: string; - subtitle: string; - id?: string | null; - blockName?: string | null; - blockType: "page-header"; - }[] + | ( + | { + title: string; + subtitle: string; + id?: string | null; + blockName?: string | null; + blockType: "page-header"; + } + | { + content?: + | { + [k: string]: unknown; + }[] + | null; + id?: string | null; + blockName?: string | null; + blockType: "content"; + } + | { + title: string; + statistics?: + | { + name: string; + value: number; + description: { + [k: string]: unknown; + }[]; + icon?: string | Media | null; + id?: string | null; + }[] + | null; + id?: string | null; + blockName?: string | null; + blockType: "statistics"; + } + )[] | null; meta?: { title?: string | null; diff --git a/apps/roboshield/src/components/BlockRenderer/BlockRenderer.tsx b/apps/roboshield/src/components/BlockRenderer/BlockRenderer.tsx index 759cf72b4..547e9baca 100644 --- a/apps/roboshield/src/components/BlockRenderer/BlockRenderer.tsx +++ b/apps/roboshield/src/components/BlockRenderer/BlockRenderer.tsx @@ -1,20 +1,24 @@ -import PageHeader from "@/roboshield/components/PageHeader/PageHeader"; +import PageHeader from "@/roboshield/components/PageHeader"; +import Content from "@/roboshield/components/Content"; +import Statistics from "@/roboshield/components/Statistics"; import { Page } from "@/root/payload-types"; interface BlockRendererProps extends Pick {} const components = { "page-header": PageHeader, + content: Content, + statistics: Statistics, }; export default function BlockRenderer({ blocks }: BlockRendererProps) { return ( <> - {blocks?.map((block, index) => { + {blocks?.map((block) => { const Component = components[block.blockType]; if (Component) { - return ; + return ; } return null; diff --git a/apps/roboshield/src/components/Content/Content.tsx b/apps/roboshield/src/components/Content/Content.tsx index 3286657b5..529a2e06d 100644 --- a/apps/roboshield/src/components/Content/Content.tsx +++ b/apps/roboshield/src/components/Content/Content.tsx @@ -1,9 +1,12 @@ import { Section } from "@commons-ui/core"; import RichText, { Children } from "@/roboshield/components/RichText"; +import { Page } from "@/root/payload-types"; +import { ExtractBlockType } from "@/roboshield/utils/blocks"; -interface ContentProps { - content: Children; -} +type ContentProps = ExtractBlockType< + NonNullable[number], + "content" +>; export default function Content({ content }: ContentProps) { return ( @@ -16,7 +19,7 @@ export default function Content({ content }: ContentProps) { }} > ({ mb: "30px", "& h2": { diff --git a/apps/roboshield/src/components/Statistics/Statistics.tsx b/apps/roboshield/src/components/Statistics/Statistics.tsx index bd7790a46..12663fada 100644 --- a/apps/roboshield/src/components/Statistics/Statistics.tsx +++ b/apps/roboshield/src/components/Statistics/Statistics.tsx @@ -2,18 +2,15 @@ import { Box, Grid } from "@mui/material"; import { Section } from "@commons-ui/core"; import { RichTypography } from "@commons-ui/next"; import StatisticCard from "./StatisticCard"; -import type { Children } from "@/roboshield/components/RichText"; +import { Page } from "@/root/payload-types"; +import { ExtractBlockType } from "@/roboshield/utils/blocks"; -export type Statistics = { - name: string; - value: string; - icon: string; - description: Children; -}; -export interface StatiscticsProps { - title: string; - statistics: Statistics[]; -} +type StatiscticsProps = ExtractBlockType< + NonNullable[number], + "statistics" +>; + +export type Statistics = NonNullable[number]; export default function Statistics({ title, statistics }: StatiscticsProps) { return ( @@ -39,7 +36,7 @@ export default function Statistics({ title, statistics }: StatiscticsProps) { )} - {statistics.map((statistic) => ( + {statistics?.map((statistic) => ( diff --git a/apps/roboshield/src/components/Statistics/index.ts b/apps/roboshield/src/components/Statistics/index.ts new file mode 100644 index 000000000..c8bd68afc --- /dev/null +++ b/apps/roboshield/src/components/Statistics/index.ts @@ -0,0 +1,3 @@ +import Statistics from "./Statistics"; + +export default Statistics; From f3172b784f5a6e32ac1343141298e709ca83fa95 Mon Sep 17 00:00:00 2001 From: Kipruto <43873157+kelvinkipruto@users.noreply.github.com> Date: Tue, 2 Jul 2024 09:32:21 +0300 Subject: [PATCH 4/7] FIx value Signed-off-by: Kipruto <43873157+kelvinkipruto@users.noreply.github.com> --- apps/roboshield/src/payload/blocks/Statistics.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/roboshield/src/payload/blocks/Statistics.ts b/apps/roboshield/src/payload/blocks/Statistics.ts index 70f0fa37c..e4b7feb85 100644 --- a/apps/roboshield/src/payload/blocks/Statistics.ts +++ b/apps/roboshield/src/payload/blocks/Statistics.ts @@ -32,8 +32,8 @@ export const Statistics: Block = { }, { name: "value", - type: "number", - label: "Number", + type: "text", + label: "Value", required: true, }, { From 92fbd685e6dbd7dc775ad97c4d512bbf015d88ab Mon Sep 17 00:00:00 2001 From: Kipruto <43873157+kelvinkipruto@users.noreply.github.com> Date: Tue, 2 Jul 2024 11:50:06 +0300 Subject: [PATCH 5/7] Use CMS Content Signed-off-by: Kipruto <43873157+kelvinkipruto@users.noreply.github.com> --- apps/roboshield/payload-types.ts | 32 +++++++- .../src/components/Content/Content.tsx | 60 +++++++++----- .../LongFormExternalEmbed.tsx | 30 +++++++ .../components/LongFormExternalEmbed/index.ts | 3 + .../LongFormMedia/LongFormMedia.tsx | 43 ++++++++++ .../src/components/LongFormMedia/index.ts | 3 + .../LongFormRichText/LongFormRichText.tsx | 78 +++++++++++++++++++ .../src/components/LongFormRichText/index.ts | 3 + apps/roboshield/src/payload/blocks/Content.ts | 24 ++---- .../src/payload/blocks/ExternalEmbedd.ts | 62 +++++++++++++++ .../src/payload/blocks/MediaBlock.ts | 16 ++++ .../roboshield/src/payload/blocks/RichText.ts | 16 ++++ apps/roboshield/src/utils/blocks.ts | 4 + 13 files changed, 333 insertions(+), 41 deletions(-) create mode 100644 apps/roboshield/src/components/LongFormExternalEmbed/LongFormExternalEmbed.tsx create mode 100644 apps/roboshield/src/components/LongFormExternalEmbed/index.ts create mode 100644 apps/roboshield/src/components/LongFormMedia/LongFormMedia.tsx create mode 100644 apps/roboshield/src/components/LongFormMedia/index.ts create mode 100644 apps/roboshield/src/components/LongFormRichText/LongFormRichText.tsx create mode 100644 apps/roboshield/src/components/LongFormRichText/index.ts create mode 100644 apps/roboshield/src/payload/blocks/ExternalEmbedd.ts create mode 100644 apps/roboshield/src/payload/blocks/MediaBlock.ts create mode 100644 apps/roboshield/src/payload/blocks/RichText.ts diff --git a/apps/roboshield/payload-types.ts b/apps/roboshield/payload-types.ts index 357b67364..6d731008f 100644 --- a/apps/roboshield/payload-types.ts +++ b/apps/roboshield/payload-types.ts @@ -57,9 +57,33 @@ export interface Page { } | { content?: - | { - [k: string]: unknown; - }[] + | ( + | { + content: { + [k: string]: unknown; + }[]; + id?: string | null; + blockName?: string | null; + blockType: "richtext"; + } + | { + image: string | Media; + id?: string | null; + blockName?: string | null; + blockType: "mediaBlock"; + } + | { + externalEmbeddFields?: { + embedType?: ("url" | "code") | null; + url?: string | null; + caption?: string | null; + code?: string | null; + }; + id?: string | null; + blockName?: string | null; + blockType: "externalEmbedd"; + } + )[] | null; id?: string | null; blockName?: string | null; @@ -70,7 +94,7 @@ export interface Page { statistics?: | { name: string; - value: number; + value: string; description: { [k: string]: unknown; }[]; diff --git a/apps/roboshield/src/components/Content/Content.tsx b/apps/roboshield/src/components/Content/Content.tsx index 529a2e06d..d01bbb7ae 100644 --- a/apps/roboshield/src/components/Content/Content.tsx +++ b/apps/roboshield/src/components/Content/Content.tsx @@ -1,14 +1,45 @@ import { Section } from "@commons-ui/core"; -import RichText, { Children } from "@/roboshield/components/RichText"; import { Page } from "@/root/payload-types"; -import { ExtractBlockType } from "@/roboshield/utils/blocks"; +import { + ExtractBlockType, + ExtractNestedBlockType, +} from "@/roboshield/utils/blocks"; +import LongFormRichText from "@/roboshield/components/LongFormRichText"; +import LongFormMedia from "@/roboshield/components/LongFormMedia"; +import LongFormExternalEmbed from "@/roboshield/components/LongFormExternalEmbed"; type ContentProps = ExtractBlockType< NonNullable[number], "content" >; +export type ExternalEmbeddBlock = ExtractNestedBlockType< + NonNullable[number], + "externalEmbedd" +>; + +export type RichTextBlock = ExtractNestedBlockType< + NonNullable[number], + "richtext" +>; + +export type MediaBlock = ExtractNestedBlockType< + NonNullable[number], + "mediaBlock" +>; + +type ComponentMap = { + richtext: (props: RichTextBlock) => JSX.Element; + mediaBlock?: (props: MediaBlock) => JSX.Element; + externalEmbedd?: (props: ExternalEmbeddBlock) => JSX.Element; +}; export default function Content({ content }: ContentProps) { + const COMPONENT_BY_CONTENT_TYPE: ComponentMap = { + richtext: LongFormRichText, + mediaBlock: LongFormMedia, + externalEmbedd: LongFormExternalEmbed, + }; + return (
- ({ - mb: "30px", - "& h2": { - typography: { xs: "h4", md: "h2" }, - }, - "& p,& a, & li": { - typography: { xs: "body1", md: "subheading" }, - mb: 2, - }, - "& a": { - textDecorationColor: theme.palette.primary.main, - }, - })} - /> + {content?.map((child) => { + const Component = COMPONENT_BY_CONTENT_TYPE[child.blockType]; + + if (Component) { + return ; + } + + return null; + })}
); } diff --git a/apps/roboshield/src/components/LongFormExternalEmbed/LongFormExternalEmbed.tsx b/apps/roboshield/src/components/LongFormExternalEmbed/LongFormExternalEmbed.tsx new file mode 100644 index 000000000..7f75a2a79 --- /dev/null +++ b/apps/roboshield/src/components/LongFormExternalEmbed/LongFormExternalEmbed.tsx @@ -0,0 +1,30 @@ +import { RichTypography } from "@commons-ui/core"; +import { Box } from "@mui/material"; +import React from "react"; +import { ExternalEmbeddBlock } from "@/roboshield/components/Content/Content"; + +export default function LongFormExternalEmbed(props: ExternalEmbeddBlock) { + const { externalEmbeddFields: { code = "", caption = "", url = "" } = {} } = + props; + + return ( + + {code && {code}} + {url && ( +