diff --git a/apps/charterafrica/src/components/Tool/DescriptionAndShare.js b/apps/charterafrica/src/components/Tool/DescriptionAndShare.js index cd49706b6..a10f5b7b6 100644 --- a/apps/charterafrica/src/components/Tool/DescriptionAndShare.js +++ b/apps/charterafrica/src/components/Tool/DescriptionAndShare.js @@ -8,6 +8,7 @@ import ShareThisPage from "@/charterafrica/components/ShareThisPage"; const DescriptionAndShare = React.forwardRef( function DescriptionAndShare(props, ref) { const { sx, description, lastActive, activeText } = props; + const lastActiveText = lastActive ? `${activeText} ${lastActive}` : null; return ( - {activeText} {lastActive} + {lastActiveText} diff --git a/apps/charterafrica/src/components/Tool/OrgThemeAndOperatingCountries.js b/apps/charterafrica/src/components/Tool/OrgThemeAndOperatingCountries.js index 93734db76..6aa038f1b 100644 --- a/apps/charterafrica/src/components/Tool/OrgThemeAndOperatingCountries.js +++ b/apps/charterafrica/src/components/Tool/OrgThemeAndOperatingCountries.js @@ -8,7 +8,7 @@ const OrgThemeAndOperatingCountries = React.forwardRef( function OrgThemeAndOperatingCountries(props, ref) { const { sx, organisation, theme, operatingCountries } = props; const countries = operatingCountries?.join(", "); - if (!organisation) { + if (!organisation && !theme) { return null; } return ( @@ -25,8 +25,8 @@ const OrgThemeAndOperatingCountries = React.forwardRef( >
- {organisation.name} + {organisation?.name} - + > + + {goToRepo?.label} + + ) : null} - - - {collectionText} - - - {classification} - - + {classification ? ( + + + {collectionText} + + + {classification} + + + ) : null} renders unchanged 1`] = ` />
-
- 0 -
-
+ />
renders unchanged 1`] = ` />
-
- 0 -
-
+ />
renders unchanged 1`] = `
- - 6 Months ago + undefined 6 Months ago
renders unchanged 1`] = ` >
- -
-
(!obj[field] ? field : null)) + .filter(Boolean); + if (emptyFields.length > 0) { + return `The following fields are missing ${emptyFields.join(", ")}`; + } + return null; +} + function getRepoLink(source = "github", slug = "") { if (source === "github" && slug) { return `https://github.com/${slug}`; @@ -70,8 +80,9 @@ export function processTool(item, config, { partnersData }) { } = config; const data = { ...item.fields, id: item.id }; const externalId = getValue(data, toolTableColumns.slug)?.trim(); - if (!externalId?.length) { - const message = `Missing externalId for Tool ${data.id}. Skipping`; + const name = getValue(data, toolTableColumns.name)?.trim(); + if (!name) { + const message = `Missing name for Tool ${data.id}. Skipping`; Sentry.captureMessage(message); return null; } @@ -115,12 +126,28 @@ export function processTool(item, config, { partnersData }) { data, ); const source = getSourceType(getValue(data, toolTableColumns.source.url)); + const avatarUrl = + getValue(data, toolTableColumns.avatarUrl)?.[0]?.url ?? null; + const fieldsToCheck = { + avatarUrl, + name, + description, + operatingCountries, + theme, + }; + const missingFields = checkFields(fieldsToCheck); + if (missingFields) { + const message = `Tool ${name}: ${missingFields}`; + Sentry.captureMessage(message); + return null; + } + return { airtableId: data.id, - avatarUrl: getValue(data, toolTableColumns.avatarUrl)?.[0]?.url ?? null, + avatarUrl, externalId, repoLink: getValue(data, toolTableColumns.source.url), - name: getValue(data, toolTableColumns.name), + name, link: getValue(data, toolTableColumns.url), operatingCountries, contributors: getValue(data, toolTableColumns.contributors), diff --git a/apps/charterafrica/src/lib/ecosystem/github/processData.js b/apps/charterafrica/src/lib/ecosystem/github/processData.js index 000299153..62fb75c3e 100644 --- a/apps/charterafrica/src/lib/ecosystem/github/processData.js +++ b/apps/charterafrica/src/lib/ecosystem/github/processData.js @@ -80,6 +80,9 @@ function fetchUserQuery(username) { } export async function fetchTool({ externalId }) { + if (!externalId) { + return null; + } let [repositoryOwner, repositoryName] = externalId .replace(/^https?:\/\/github\.com\//, "") .replace(/\/$/, "") diff --git a/apps/roboshield/package.json b/apps/roboshield/package.json index 4990b18a1..f8a5ff3d2 100644 --- a/apps/roboshield/package.json +++ b/apps/roboshield/package.json @@ -23,6 +23,7 @@ "@emotion/styled": "^11.11.5", "@mui/icons-material": "^5.15.20", "@mui/material": "^5.15.20", + "@mui/material-nextjs": "^5.15.11", "@mui/utils": "^5.15.20", "@mui/x-date-pickers": "^7.7.1", "@next/env": "^14.2.4", diff --git a/apps/roboshield/payload-types.ts b/apps/roboshield/payload-types.ts index db5b1dfbc..03c3da4e1 100644 --- a/apps/roboshield/payload-types.ts +++ b/apps/roboshield/payload-types.ts @@ -56,6 +56,57 @@ export interface Page { blockType: "page-header"; } | PageHero + | { + content?: + | ( + | { + 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; + blockType: "content"; + } + | { + title: string; + statistics?: + | { + name: string; + value: string; + description: { + [k: string]: unknown; + }[]; + icon?: string | Media | null; + id?: string | null; + }[] + | null; + id?: string | null; + blockName?: string | null; + blockType: "statistics"; + } | { toolTipText: string; steps?: diff --git a/apps/roboshield/src/components/BlockRenderer/BlockRenderer.tsx b/apps/roboshield/src/components/BlockRenderer/BlockRenderer.tsx index 9202f0e73..840e14003 100644 --- a/apps/roboshield/src/components/BlockRenderer/BlockRenderer.tsx +++ b/apps/roboshield/src/components/BlockRenderer/BlockRenderer.tsx @@ -1,4 +1,6 @@ -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"; import Hero from "@/roboshield/components/Hero"; import RoboForm from "@/roboshield/components/RoboForm"; @@ -9,17 +11,19 @@ interface BlockRendererProps extends Pick {} const components = { "page-header": PageHeader, "page-hero": Hero, + content: Content, + statistics: Statistics, "robo-form": RoboForm, }; export default function BlockRenderer({ blocks }: BlockRendererProps) { return ( <> - {blocks?.map((block, index) => { + {blocks?.map((block) => { const Component: FC = 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 new file mode 100644 index 000000000..66f914f00 --- /dev/null +++ b/apps/roboshield/src/components/Content/Content.tsx @@ -0,0 +1,65 @@ +import { Section } from "@commons-ui/core"; +import { Page } from "@/root/payload-types"; +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"; +import { FC } from "react"; + +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: React.FC; + mediaBlock: React.FC; + externalEmbedd: React.FC; +}; + +export default function Content({ content }: ContentProps) { + const COMPONENT_BY_CONTENT_TYPE: ComponentMap = { + richtext: LongFormRichText, + mediaBlock: LongFormMedia, + externalEmbedd: LongFormExternalEmbed, + }; + + return ( +
+ {content?.map((child) => { + const Component: FC = COMPONENT_BY_CONTENT_TYPE[child.blockType]; + + if (Component) { + return ; + } + + return null; + })} +
+ ); +} 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/Hero/Hero.tsx b/apps/roboshield/src/components/Hero/Hero.tsx index 36cf7cce6..43117fb7b 100644 --- a/apps/roboshield/src/components/Hero/Hero.tsx +++ b/apps/roboshield/src/components/Hero/Hero.tsx @@ -109,7 +109,12 @@ const Hero = (props: PageHero) => { />