From 230ef35d3acf2091847e998ff1a4fbd8e2c4a170 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 08:56:16 +0000 Subject: [PATCH 1/4] Support Chair/Vice Chair project leadership roles from Notion Adds "Chair" and "Vice Chair" as recognized Role for Subscription values in the Notion fetch pipeline, mapping to "Project Lead (Chair)" and "Vice Chair" labels so standards pages (e.g. SWI) can distinguish a project Chair from a Vice Chair instead of showing both as "Lead". --- docs/components/people.md | 2 +- docs/notion.md | 16 ++++++++++++++++ scripts/fetch-notion-data.cjs | 4 ++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/components/people.md b/docs/components/people.md index d844f5b3..2d4d6a5a 100644 --- a/docs/components/people.md +++ b/docs/components/people.md @@ -39,7 +39,7 @@ The `groups` array determines which page sections a person appears in: |-------------|----------------------|----------------| | `steeringCommittee` | Steering Committee / Board | Subscriptions with committee roles linked to "Steering Committee" PWCI | | `administrativeTeam` | Our Team (staff) | GSF Team DB, Status = "Active" | -| `chairsAndLeads` | Chairs & Project Leads | Subscriptions with roles: WG Chair, Project Lead/Co-Lead, Committee Chair/Vice-Chair | +| `chairsAndLeads` | Chairs & Project Leads | Subscriptions with roles: WG Chair, Project Lead/Co-Lead, Chair/Vice Chair, Committee Chair/Vice-Chair | | `organisationalLeads` | Organisation Leads | Subscriptions with Role = "Organization Lead" | A person can belong to multiple groups and appears in each corresponding section. diff --git a/docs/notion.md b/docs/notion.md index c5d26c0a..6b31f44b 100644 --- a/docs/notion.md +++ b/docs/notion.md @@ -95,6 +95,22 @@ Person data for subscription-based people (name, title, LinkedIn, member org) is - `Subscription Status` (select) — must be "Active" - Roll-up fields: `First Name`, `Surname`, `Title`, `LinkedIn`, `Member`, `Volunteer Status` +#### Distinguishing leadership on a standards page (e.g. Chair vs Vice Chair) + +`fetchChairsAndLeads()` in `scripts/fetch-notion-data.cjs` maps `Role for Subscription` values to the `roleLabel` shown next to a project lead's name in `projects.json` (consumed by `TeamGrid` on standards pages): + +| Role for Subscription | roleLabel shown on site | +|---|---| +| `Working Group Chair` | Chair | +| `Project Lead` | Lead | +| `Project Co-Lead` | Co-Lead | +| `Chair` | Project Lead (Chair) | +| `Vice Chair` | Vice Chair | +| `Committee Chair` | Chair | +| `Committee Vice-Chair` | Vice-Chair | + +When a project has multiple "Project Lead" subscriptions and needs to distinguish a Chair from a Vice Chair (as opposed to two equal leads), set one person's `Role for Subscription` to `Chair` and the other's to `Vice Chair` rather than leaving both as `Project Lead`. + ### Volunteers DB - `First Name`, `Last Name` (rich text) diff --git a/scripts/fetch-notion-data.cjs b/scripts/fetch-notion-data.cjs index 31d03a0d..1baaae4a 100644 --- a/scripts/fetch-notion-data.cjs +++ b/scripts/fetch-notion-data.cjs @@ -408,6 +408,8 @@ async function fetchChairsAndLeads(memberById) { "Working Group Chair", "Project Lead", "Project Co-Lead", + "Chair", + "Vice Chair", "Committee Chair", "Committee Vice-Chair", "Committee Member", @@ -447,6 +449,8 @@ async function fetchChairsAndLeads(memberById) { "Working Group Chair": "Chair", "Project Lead": "Lead", "Project Co-Lead": "Co-Lead", + "Chair": "Project Lead (Chair)", + "Vice Chair": "Vice Chair", "Committee Chair": "Chair", "Committee Vice-Chair": "Vice-Chair", }; From ea6bb9b01c241cb7110a0ac0cdac35ca70aa7475 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 09:18:28 +0000 Subject: [PATCH 2/4] Match actual Notion role values for SWI Chair/Vice Chair MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Role for Subscription values in Notion had drifted to "Project Chair" / "Project Vice Chair" (retyping a select value creates a new option rather than reusing one), which the previous "Chair"/"Vice Chair" mapping didn't match — causing the SWI Project Leadership section to disappear entirely. Recognize both variants and document the pitfall. --- docs/notion.md | 6 +++++- scripts/fetch-notion-data.cjs | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/notion.md b/docs/notion.md index 6b31f44b..13029c46 100644 --- a/docs/notion.md +++ b/docs/notion.md @@ -106,10 +106,14 @@ Person data for subscription-based people (name, title, LinkedIn, member org) is | `Project Co-Lead` | Co-Lead | | `Chair` | Project Lead (Chair) | | `Vice Chair` | Vice Chair | +| `Project Chair` | Project Lead (Chair) | +| `Project Vice Chair` | Vice Chair | | `Committee Chair` | Chair | | `Committee Vice-Chair` | Vice-Chair | -When a project has multiple "Project Lead" subscriptions and needs to distinguish a Chair from a Vice Chair (as opposed to two equal leads), set one person's `Role for Subscription` to `Chair` and the other's to `Vice Chair` rather than leaving both as `Project Lead`. +When a project has multiple "Project Lead" subscriptions and needs to distinguish a Chair from a Vice Chair (as opposed to two equal leads), set one person's `Role for Subscription` to `Project Chair` and the other's to `Project Vice Chair` rather than leaving both as `Project Lead`. + +**Careful when picking a value in Notion's select field:** typing a value that isn't an exact match for an existing option silently creates a *new* option instead of reusing one — this is how `Chair`/`Vice Chair`/`Project Chair`/`Project Vice Chair` all ended up coexisting as separate, easily-confused options. Always pick from the existing dropdown list rather than retyping the text. `Project Chair` and `Project Vice Chair` are the canonical values to use going forward; `Chair` and `Vice Chair` are kept mapped for compatibility but are unused stray options that could be deleted from the Notion schema. ### Volunteers DB diff --git a/scripts/fetch-notion-data.cjs b/scripts/fetch-notion-data.cjs index 1baaae4a..25ba365d 100644 --- a/scripts/fetch-notion-data.cjs +++ b/scripts/fetch-notion-data.cjs @@ -410,6 +410,8 @@ async function fetchChairsAndLeads(memberById) { "Project Co-Lead", "Chair", "Vice Chair", + "Project Chair", + "Project Vice Chair", "Committee Chair", "Committee Vice-Chair", "Committee Member", @@ -451,6 +453,8 @@ async function fetchChairsAndLeads(memberById) { "Project Co-Lead": "Co-Lead", "Chair": "Project Lead (Chair)", "Vice Chair": "Vice Chair", + "Project Chair": "Project Lead (Chair)", + "Project Vice Chair": "Vice Chair", "Committee Chair": "Chair", "Committee Vice-Chair": "Vice-Chair", }; From a44c51a589e02c73a413e955f9fa4b4f1e22f67f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 09:23:48 +0000 Subject: [PATCH 3/4] Sort SWI project leadership by role so Chair appears first Notion's subscription query order isn't guaranteed, so the leads array from projects.json could list the Vice Chair before the Chair depending on query results. Sort by role rank in the page itself so the Chair (Yi Ding) is always shown first/leftmost. --- docs/pages/standards.md | 2 +- src/pages/standards/swi/index.astro | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/docs/pages/standards.md b/docs/pages/standards.md index 8f5ae7a6..0772b046 100644 --- a/docs/pages/standards.md +++ b/docs/pages/standards.md @@ -15,7 +15,7 @@ Each standards page is a long-form landing page for a specific GSF standard/proj - **Project metadata** — imported from `src/data/projects.json`. Each page looks up its project by slug (e.g. `projects.find(p => p.slug === "sci")`) - **Lifecycle stage** — displayed as a badge in the hero, read from `projects.json` - **Parent working group** — resolved from `projects.json` to show "A [WG Name] Project" -- **Project leads** — the `leads` array from `projects.json` provides `fullName` and `roleLabel` +- **Project leads** — the `leads` array from `projects.json` provides `fullName` and `roleLabel`. Notion's query order isn't guaranteed, so pages that need a deterministic display order (e.g. Chair before Vice Chair) should sort the array by `roleLabel` in the page itself rather than relying on array order — see the `leadRoleRank()` helper in `src/pages/standards/swi/index.astro` See [Notion doc](../notion.md) for how `projects.json` is populated from the PWCIs database. diff --git a/src/pages/standards/swi/index.astro b/src/pages/standards/swi/index.astro index d7b400df..5c7fffe4 100644 --- a/src/pages/standards/swi/index.astro +++ b/src/pages/standards/swi/index.astro @@ -21,6 +21,13 @@ import { articleUrl } from "@/lib/utils"; const sweProject = projects.find(p => p.slug === "swe"); const parentWg = projects.find(p => p.name === sweProject?.parent); +// Chair before Vice Chair before other lead roles, regardless of Notion query order +function leadRoleRank(roleLabel: string): number { + if (/chair/i.test(roleLabel) && !/vice/i.test(roleLabel)) return 0; + if (/vice.?chair/i.test(roleLabel)) return 1; + return 2; +} + const carouselArticles = (await getCollection("articles", (a) => a.data.published !== false)) .filter(a => (a.data.tags || []).map((t: string) => t.toLowerCase()).includes("swe")) .sort((a, b) => new Date(b.data.date).getTime() - new Date(a.data.date).getTime()) @@ -184,10 +191,12 @@ const carouselArticles = (await getCollection("articles", (a) => a.data.publishe body={parentWg ? `Part of the ${parentWg.name}` : ""} columns={sweProject.leads.length === 2 ? 2 : 3} highlighted={[]} - members={sweProject.leads.map(lead => ({ - fullName: lead.fullName, - role: lead.roleLabel, - }))} + members={[...sweProject.leads] + .sort((a, b) => leadRoleRank(a.roleLabel) - leadRoleRank(b.roleLabel)) + .map(lead => ({ + fullName: lead.fullName, + role: lead.roleLabel, + }))} bgClass="bg-white" /> )} From d20a64de8ebe191556627aa3cb60dca054efa4da Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 12:59:09 +0000 Subject: [PATCH 4/4] Fix SEI project slug mismatch so it appears in Current Standards & Projects The standards index page and SEI standards page looked up the project by slug "see", but Notion's PWCIs database has the SEI project's slug field set to "sei". The mismatch meant projects.find() never matched, silently dropping the SEI card from /standards/ and leaving its hero badges and project leadership empty on /standards/sei/. --- src/pages/standards/index.astro | 2 +- src/pages/standards/sei/index.astro | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pages/standards/index.astro b/src/pages/standards/index.astro index afb7000e..ca5e0cec 100644 --- a/src/pages/standards/index.astro +++ b/src/pages/standards/index.astro @@ -38,7 +38,7 @@ const standardDefs = [ { slug: "silo" }, { slug: "wdpc" }, { slug: "open19" }, - { slug: "see", urlSlug: "sei" }, + { slug: "sei" }, { slug: "swe" }, ]; const standardProjects = standardDefs diff --git a/src/pages/standards/sei/index.astro b/src/pages/standards/sei/index.astro index 56ccf42e..5c49c834 100644 --- a/src/pages/standards/sei/index.astro +++ b/src/pages/standards/sei/index.astro @@ -18,11 +18,11 @@ import ArticleCarousel from "@/components/article-carousel.astro"; import projects from "@/data/projects.json"; import { getCollection } from "astro:content"; import { articleUrl } from "@/lib/utils"; -const seeProject = projects.find(p => p.slug === "see"); -const parentWg = projects.find(p => p.name === seeProject?.parent); +const seiProject = projects.find(p => p.slug === "sei"); +const parentWg = projects.find(p => p.name === seiProject?.parent); const carouselArticles = (await getCollection("articles", (a) => a.data.published !== false)) - .filter(a => (a.data.tags || []).map((t: string) => t.toLowerCase()).includes("see")) + .filter(a => (a.data.tags || []).map((t: string) => t.toLowerCase()).includes("sei")) .sort((a, b) => new Date(b.data.date).getTime() - new Date(a.data.date).getTime()) .map(a => ({ title: a.data.title, @@ -56,7 +56,7 @@ const carouselArticles = (await getCollection("articles", (a) => a.data.publishe a.data.publishe ({ + members={(seiProject?.leads ?? []).map(lead => ({ fullName: lead.fullName, role: lead.roleLabel, }))}