From 3a08f59f9b52463c5e360d9d2cdc78e153472ff6 Mon Sep 17 00:00:00 2001 From: xrendan Date: Mon, 4 May 2026 16:43:59 -0600 Subject: [PATCH 1/2] images: split banner vs SEO image roles for memos and posts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit york_factory now exposes banner_image_url and seo_image_url on both memos and posts. SEO images drive social previews (OG, Twitter, JSON-LD); banner images drive featured displays only (homepage featured memo card, post slot in the homepage feed). The banner hero on the memo detail page is removed — banner is reserved for the featured spot. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/app/memos/[slug]/page.tsx | 17 +---------------- src/app/memos/types.ts | 2 +- src/app/toronto/memos/[slug]/page.tsx | 17 +---------------- src/components/FeaturedMemos.tsx | 2 +- src/components/FeedPreview.tsx | 2 +- src/components/ui/memo-card.tsx | 6 +++--- src/lib/api/memos.ts | 10 +++++----- src/lib/api/posts.ts | 4 +++- src/lib/api/types.ts | 3 ++- src/lib/schemas/generators/article.ts | 3 +-- 10 files changed, 19 insertions(+), 47 deletions(-) diff --git a/src/app/memos/[slug]/page.tsx b/src/app/memos/[slug]/page.tsx index de905a7..c3f9e8d 100644 --- a/src/app/memos/[slug]/page.tsx +++ b/src/app/memos/[slug]/page.tsx @@ -1,6 +1,5 @@ import { notFound } from "next/navigation"; import type { Metadata } from "next"; -import Image from "next/image"; import { fetchMemo, fetchMemos, getSiteConfig } from "@/lib/api"; import { extractHeadings } from "@/lib/extract-headings"; import { TwitterEmbed, MemoSubscribe, RelatedMemos } from "./MemoClientParts"; @@ -37,7 +36,7 @@ export async function generateMetadata({ const title = `${memo.title} | Build Canada`; const description = memo.keyMessage1; - const image = memo.seoImage || memo.splashImage || undefined; + const image = memo.seoImage || undefined; return { title, @@ -100,7 +99,6 @@ export default async function MemoDetailPage({ slug: memo.slug, keyMessage1: memo.keyMessage1, seoImage: memo.seoImage, - splashImage: memo.splashImage, publishedAt: memo.publishedAt ? new Date(memo.publishedAt) : null, createdAt: new Date(memo.createdAt), updatedAt: new Date(memo.updatedAt), @@ -187,19 +185,6 @@ export default async function MemoDetailPage({ - {memo.splashImage && ( -
- -
- )} - - {memo.splashImage && ( -
- -
- )} - { slug: p.slug, summary: p.summary, body: p.body, + bannerImage: p.banner_image_url, seoImage: p.seo_image_url, publishedAt: p.published_at, createdAt: p.published_at ?? new Date().toISOString(), diff --git a/src/lib/api/types.ts b/src/lib/api/types.ts index db4dcdf..c8dcc5e 100644 --- a/src/lib/api/types.ts +++ b/src/lib/api/types.ts @@ -29,6 +29,7 @@ export interface YFMemo { featured: boolean; published_at: string | null; seo_image_url: string | null; + banner_image_url: string | null; author: YFAuthor; } @@ -41,7 +42,6 @@ export interface YFMemoDetail extends YFMemo { author_name: string | null; author_title: string | null; co_author: YFAuthor | null; - splash_image_url: string | null; } export interface YFTeamMember { @@ -95,6 +95,7 @@ export interface YFPost { summary: string | null; published_at: string | null; seo_image_url: string | null; + banner_image_url: string | null; } export interface YFPostDetail extends YFPost { diff --git a/src/lib/schemas/generators/article.ts b/src/lib/schemas/generators/article.ts index 2f84f0e..4e6589e 100644 --- a/src/lib/schemas/generators/article.ts +++ b/src/lib/schemas/generators/article.ts @@ -12,7 +12,6 @@ interface MemoData { slug: string; keyMessage1?: string | null; seoImage?: string | null; - splashImage?: string | null; publishedAt?: Date | string | null; createdAt: Date | string; updatedAt: Date | string; @@ -27,7 +26,7 @@ export function generateArticleSchema( "@type": "Article" as const, headline: memo.title, description: memo.keyMessage1, - image: memo.seoImage || memo.splashImage, + image: memo.seoImage, datePublished: toISO8601(memo.publishedAt ?? memo.createdAt), dateModified: toISO8601(memo.updatedAt), author: generatePersonSchema(author), From 66b2a2d7da93bff4be6a1f4f4f01f9b27c72c86e Mon Sep 17 00:00:00 2001 From: xrendan Date: Mon, 4 May 2026 16:51:57 -0600 Subject: [PATCH 2/2] lint: clear pre-existing eslint warnings Drop unused imports/vars, suppress one intentional useEffect dep warning, and migrate tracker tags to next/image. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/app/builders/[slug]/page.tsx | 2 +- src/app/content/ContentFeedClient.tsx | 2 +- src/app/memos/MemosListClient.tsx | 1 + src/app/memos/[slug]/MemoHero.tsx | 2 -- src/app/memos/[slug]/page.tsx | 2 -- src/app/page.tsx | 35 ------------------- src/app/toronto/memos/[slug]/page.tsx | 1 - src/components/CyclingWord.tsx | 2 +- src/components/ProjectsGrid.tsx | 1 - .../custom/signpost/desktop-nav.tsx | 2 +- src/components/tracker/ChartLine.tsx | 1 - src/components/tracker/MinistryGrid.tsx | 6 +++- src/components/tracker/Sidebar.tsx | 28 ++++++++++----- 13 files changed, 30 insertions(+), 55 deletions(-) diff --git a/src/app/builders/[slug]/page.tsx b/src/app/builders/[slug]/page.tsx index 97edea5..7bb992b 100644 --- a/src/app/builders/[slug]/page.tsx +++ b/src/app/builders/[slug]/page.tsx @@ -2,7 +2,7 @@ import { notFound } from "next/navigation"; import type { Metadata } from "next"; import Image from "next/image"; import Link from "next/link"; -import { fetchBuilder, fetchBuilders } from "@/lib/api/builders"; +import { fetchBuilder } from "@/lib/api/builders"; export async function generateMetadata({ params, diff --git a/src/app/content/ContentFeedClient.tsx b/src/app/content/ContentFeedClient.tsx index 227c83f..c1d583b 100644 --- a/src/app/content/ContentFeedClient.tsx +++ b/src/app/content/ContentFeedClient.tsx @@ -1,6 +1,6 @@ "use client"; -import { useEffect, useState } from "react"; +import { useState } from "react"; import { useSearchParams } from "next/navigation"; import SectionLabel from "@/components/SectionLabel"; import { diff --git a/src/app/memos/MemosListClient.tsx b/src/app/memos/MemosListClient.tsx index 1583acc..983797d 100644 --- a/src/app/memos/MemosListClient.tsx +++ b/src/app/memos/MemosListClient.tsx @@ -20,6 +20,7 @@ function CategoryFromSearchParams({ categories }: { categories: string[] }) { if (category && categories.includes(category)) { setActiveCategory(category); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return null; diff --git a/src/app/memos/[slug]/MemoHero.tsx b/src/app/memos/[slug]/MemoHero.tsx index e491a6d..5d863e4 100644 --- a/src/app/memos/[slug]/MemoHero.tsx +++ b/src/app/memos/[slug]/MemoHero.tsx @@ -2,7 +2,6 @@ import Image from "next/image"; import Link from "next/link"; interface MemoHeroProps { - category: string | null; title: string; authorName: string; authorImage: string | null; @@ -13,7 +12,6 @@ interface MemoHeroProps { } export function MemoHero({ - category, title, authorName, authorImage, diff --git a/src/app/memos/[slug]/page.tsx b/src/app/memos/[slug]/page.tsx index c3f9e8d..1304995 100644 --- a/src/app/memos/[slug]/page.tsx +++ b/src/app/memos/[slug]/page.tsx @@ -3,7 +3,6 @@ import type { Metadata } from "next"; import { fetchMemo, fetchMemos, getSiteConfig } from "@/lib/api"; import { extractHeadings } from "@/lib/extract-headings"; import { TwitterEmbed, MemoSubscribe, RelatedMemos } from "./MemoClientParts"; -import { AuthorCard } from "./AuthorCard"; import { ShareSection } from "@/components/share"; import { MemoHero } from "./MemoHero"; import { Signpost } from "@/components/custom/signpost"; @@ -186,7 +185,6 @@ export default async function MemoDetailPage({ -
- {SOCIALS.map(({ label, href, iconFile }) => ( - - {label} - - ))} - {/* /content is being phased out — hide entry point until decision is finalized. -
- - Full Archive - - */} -
-
- ); -} - - function FeedAndEvents() { return (
diff --git a/src/app/toronto/memos/[slug]/page.tsx b/src/app/toronto/memos/[slug]/page.tsx index 54e825f..eebfa75 100644 --- a/src/app/toronto/memos/[slug]/page.tsx +++ b/src/app/toronto/memos/[slug]/page.tsx @@ -157,7 +157,6 @@ export default async function TorontoMemoDetailPage({ /> projects.find((p) => p.slug === slug)) .filter((p): p is ProjectData => p != null); diff --git a/src/components/custom/signpost/desktop-nav.tsx b/src/components/custom/signpost/desktop-nav.tsx index da7ee17..d109ed2 100644 --- a/src/components/custom/signpost/desktop-nav.tsx +++ b/src/components/custom/signpost/desktop-nav.tsx @@ -9,7 +9,7 @@ import { TocTree } from "./toc-tree"; import { ShareButtons } from "@/components/share/ui/ShareButtons"; export function DesktopNav() { - const { tree, activeId, activeParentId, navigateTo, shareTitle, shareUrl } = useSignpost(); + const { activeId, activeParentId, navigateTo, shareTitle, shareUrl } = useSignpost(); const containerRef = useRef(null); useEffect(() => { diff --git a/src/components/tracker/ChartLine.tsx b/src/components/tracker/ChartLine.tsx index 683f849..1a6c150 100644 --- a/src/components/tracker/ChartLine.tsx +++ b/src/components/tracker/ChartLine.tsx @@ -59,7 +59,6 @@ import { // The override signature in chart.js types is stricter than what date-fns // returns (Date vs number), but the runtime behavior is correct — date-fns // Date instances are coerced to numbers by chart.js internals. -// eslint-disable-next-line @typescript-eslint/no-explicit-any _adapters._date.override({ _id: "date-fns", formats: () => ({ diff --git a/src/components/tracker/MinistryGrid.tsx b/src/components/tracker/MinistryGrid.tsx index 8e553ff..0675e33 100644 --- a/src/components/tracker/MinistryGrid.tsx +++ b/src/components/tracker/MinistryGrid.tsx @@ -1,6 +1,7 @@ "use client"; import { useState, useRef } from "react"; +import Image from "next/image"; import Link from "next/link"; import type { CommitmentListing, MinistryGroup } from "@/lib/commitment-types"; @@ -47,9 +48,12 @@ function MinistryCard({ ministry }: { ministry: MinistryGroup }) { {minister && (
{minister.avatar_url ? ( - {`${minister.first_name} ) : ( diff --git a/src/components/tracker/Sidebar.tsx b/src/components/tracker/Sidebar.tsx index 6f7e15c..425d024 100644 --- a/src/components/tracker/Sidebar.tsx +++ b/src/components/tracker/Sidebar.tsx @@ -1,6 +1,7 @@ "use client"; import { usePathname } from "next/navigation"; import useSWR from "swr"; +import Image from "next/image"; import Link from "next/link"; import type { DashboardResponse, @@ -45,9 +46,11 @@ function SidebarLogo() { return (
- Build Canada @@ -118,9 +121,12 @@ function DefaultSidebar({ pageTitle }: { pageTitle: string }) {
{pmDept?.minister?.avatar_url && (
- Mark Carney
@@ -385,12 +391,15 @@ function SupportingMinisterCard({ minister }: { minister: MinisterInfo }) { return (
-
+
{minister.avatar_url ? ( - {fullName} ) : (
@@ -447,12 +456,15 @@ function MinisterCard({

{departmentName}

-
+
{minister.avatar_url ? ( - {fullName} ) : (