/* ============================================================================ WHAT WE DO - /en/what-we-do and /ar/what-we-do ---------------------------------------------------------------------------- The redesigned What We Do page ("What-We-Do", Claude Design draft), carried onto the project's own design system. Four bands: Hero eyebrow, headline and the one-line lead, on the sunken surface with the two drifting washes behind it. Areas the four connected areas of work as a seam grid, each cell led by its numeral. Guidance the five values as a centred chip row, then the four principles as bar-topped tiles. Activities the closing panel with the accent start-edge. WHAT THE DRAFT SHIPS THAT THIS PAGE DOES NOT -------------------------------------------- * The draft gives each principle an explanatory line ("Local organisations lead the design and delivery of activities", "Solutions are built to outlast the project's own timeline", "Institutional change is supported step by step, not imposed"). None of those sentences is in an approved document, so the tiles carry only the clause the approved "How we work" sentence states, which is what the draft's own titles carry too. * The draft's coloured hover glow is deliberately dropped. Overridden field by field by api::what-we-do-page.what-we-do-page; the draft copy below is what renders until an editor fills that in. Arabic is this same DOM under `dir="rtl"`, so every inline edge is logical. ========================================================================= */ import { Link } from "react-router"; import { ArrowIcon, Button } from "~/components/ui/Button"; import { createTranslator, formatNumber, sectionPath } from "~/i18n"; import { buildBreadcrumbJsonLd, buildMeta } from "~/lib/seo"; import type { Route } from "./+types/what-we-do"; import { isRichTextBlock, type CmsWhatWeDoPage } from "./_page-kit/cms-types"; import { list, loadSingle, toImage, toStringList, } from "./_page-kit/cms.server"; import { pageCopy } from "./_page-kit/copy"; import { routeLocale } from "./_page-kit/locale"; import { RichText } from "./_page-kit/rich-text"; import { PageHero, PageSection, Prose, SectionHeader, ValueChips, } from "./_page-kit/sections"; import "./_page-kit/motion.css"; export async function loader({ params }: Route.LoaderArgs) { const locale = routeLocale(params); const page = await loadSingle("what-we-do-page", { locale, populate: { hero: { populate: { media: true, ctas: true } }, workstreams: { populate: { icon: true } }, principles: true, blocks: { on: { "blocks.rich-text": { populate: "*" } } }, seo: { populate: { ogImage: true } }, }, }); return { locale, hero: { eyebrow: page?.hero?.eyebrow ?? null, headline: page?.hero?.headline ?? null, subheading: page?.hero?.subheading ?? null, media: toImage(page?.hero?.media, "large") ?? null, }, intro: page?.intro ?? null, richTextBlocks: list(page?.blocks) .filter(isRichTextBlock) .map((block) => ({ id: block.id, body: block.body })), workstreams: list(page?.workstreams).map((workstream) => ({ title: workstream.title, description: workstream.description ?? "", icon: toImage(workstream.icon, "thumbnail") ?? null, })), principles: list(page?.principles).map((principle) => ({ title: principle.title, description: principle.description ?? "", })), guidingValues: toStringList(page?.guidingValues), seo: { title: page?.seo?.metaTitle ?? null, description: page?.seo?.metaDescription ?? null, image: toImage(page?.seo?.ogImage, "large") ?? null, noIndex: page?.seo?.noIndex ?? false, }, }; } export function meta({ loaderData, matches }: Route.MetaArgs) { const root = matches[0].loaderData; const locale = loaderData?.locale ?? root.locale; const t = createTranslator(locale); const copy = pageCopy(locale).whatWeDo; return buildMeta({ locale, pathname: root.pathname, siteUrl: root.siteUrl, title: loaderData?.seo.title ?? t("nav.sections.whatWeDo"), description: loaderData?.seo.description ?? copy.lead, ...(loaderData?.seo.image ? { image: { url: loaderData.seo.image.src, alt: loaderData.seo.image.alt } } : {}), noIndex: loaderData?.seo.noIndex ?? false, jsonLd: buildBreadcrumbJsonLd(root.siteUrl, [ { name: t("nav.sections.home"), pathname: sectionPath("home", locale) }, { name: t("nav.sections.whatWeDo"), pathname: sectionPath("whatWeDo", locale), }, ]), }); } /** See the note on the same constant in about.tsx. */ const ACCENT_EDGE = "[border-inline-start-color:var(--border-accent)] [border-inline-start-width:4px]"; export default function WhatWeDo({ loaderData }: Route.ComponentProps) { const { locale, hero, intro, richTextBlocks } = loaderData; const t = createTranslator(locale); const copy = pageCopy(locale).whatWeDo; const workstreams = loaderData.workstreams.length > 0 ? loaderData.workstreams : copy.workstreams.map((workstream) => ({ ...workstream, icon: null })); const values = loaderData.guidingValues.length > 0 ? loaderData.guidingValues : copy.values; /* An editor's principles carry a line of their own; the bundled fallback is the four clauses of the approved sentence and nothing more. */ const principles = loaderData.principles.length > 0 ? loaderData.principles : copy.principleTitles.map((title) => ({ title, description: "" })); return ( <> {/* THE FOUR AREAS OF WORK - one seam grid, the cells raised out of the band, each led by its numeral (localised, so the Arabic page numbers its cells in its own conventions). */}
{/* The draft prints the heading alone - the approved intro sentence IS this heading - so no lead is drawn under it. */} {intro ? ( ) : null}
    {workstreams.map((workstream, index) => (
  • {/* The draft sets an area title a step above body - the same size it gives the consortium names on About. */}

    {workstream.title}

    {workstream.description}

  • ))}
{/* WHAT GUIDES US, AND HOW WE WORK - the one band the draft raises off the near-black ground, so it names its surface explicitly. */}
{/* The draft's chips rest as a plain outline and only take the accent fill under the pointer; the shared component rests filled, so the resting and hover paints are restated here until it carries the draft's own treatment. */}
{/* The draft draws the four principles straight under the rule, with no heading over them; the heading stays in the outline. */}

{copy.principlesHeading}

    {principles.map((principle) => (
  • {/* The short accent rule the draft draws over each tile, drawn on from the inline start. Decorative. */}
  • ))}
{richTextBlocks.map((block) => ( ))}
{/* THE WAY ON TO THE ACTIVITIES */}

{copy.activitiesCtaHeading}

); }