Skip to content

feat(ui): add description and identity claims to governance cards#2198

Open
howwohmm wants to merge 3 commits intonpmx-dev:mainfrom
howwohmm:feat/governance-card-identity
Open

feat(ui): add description and identity claims to governance cards#2198
howwohmm wants to merge 3 commits intonpmx-dev:mainfrom
howwohmm:feat/governance-card-identity

Conversation

@howwohmm
Copy link
Contributor

@howwohmm howwohmm commented Mar 22, 2026

Closes #1564

Adds bio and social links to governance member cards on the about page.

The fetchSponsorable GraphQL query is replaced with fetchGovernanceProfiles which fetches bio, twitterUsername, and socialAccounts in one request. Twitter is normalised into the socialAccounts array server-side so the client just maps over a single unified array.

Falls back gracefully when no GitHub token is available — cards still show login, role, and sponsor link.

@vercel
Copy link

vercel bot commented Mar 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment Mar 26, 2026 5:18am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview Mar 26, 2026 5:18am
npmx-lunaria Ignored Ignored Mar 26, 2026 5:18am

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 22, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The pull request adds support for displaying governance member profiles with biographical information and social media links on the about page. It introduces a new SocialAccount type in the API layer and extends the GitHubContributor interface to include bio, twitterUsername, and socialAccounts fields. The API layer replaces the sponsor-focused query with a new fetchGovernanceProfiles function that retrieves governance-related metadata including social accounts. The about page component updates the member card UI to display the bio text and render social media icon links alongside existing governance information.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The pull request successfully addresses all primary coding objectives from issue #1564: bio/description line with truncation support, social identity claim icons linking to external profiles, and graceful fallback when no GitHub token is available.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the requirements in issue #1564; no unrelated modifications to other features or components are present in the changeset.
Description check ✅ Passed The pull request description clearly explains the changes: adding bio and social links to governance cards, replacing fetchSponsorable with fetchGovernanceProfiles, and maintaining graceful fallback when no GitHub token is available.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
server/api/contributors.get.ts (1)

222-233: Return a fresh object instead of mutating c and casting it.

Object.assign(c, ...) changes a GitHubAPIContributor into a wider shape and then hides the mismatch with a cast. Returning a new object keeps this mapper genuinely type-safe and easier to extend.

♻️ Proposed refactor
     return filtered
-      .map(c => {
+      .map((c): GitHubContributor & { order: number } => {
         const { role, order } = getRoleInfo(c.login, teams)
         const profile = governanceProfiles.get(c.login)
         const sponsors_url = profile?.hasSponsorsListing
           ? `https://github.com/sponsors/${c.login}`
           : null
         const bio = profile?.bio ?? null
         const twitterUsername = profile?.twitterUsername ?? null
         const socialAccounts = profile?.socialAccounts ?? []
-        Object.assign(c, { role, order, sponsors_url, bio, twitterUsername, socialAccounts })
-        return c as GitHubContributor & { order: number }
+        return {
+          ...c,
+          role,
+          order,
+          sponsors_url,
+          bio,
+          twitterUsername,
+          socialAccounts,
+        }
       })

As per coding guidelines, "Ensure you write strictly type-safe code".


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1b543fcc-a4bf-42e0-a636-4f3779dfe564

📥 Commits

Reviewing files that changed from the base of the PR and between 7f2fc1a and e7100cc.

📒 Files selected for processing (2)
  • app/pages/about.vue
  • server/api/contributors.get.ts

@codecov
Copy link

codecov bot commented Mar 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

Comment on lines +17 to +18
twitterUsername: string | null
socialAccounts: SocialAccount[]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

twitter/x should just be in this social accounts array too

@howwohmm
Copy link
Contributor Author

addressed your feedback:

  • unified twitterUsername into the socialAccounts array (removed the separate field entirely)
  • replaced Object.assign with spread return
  • added i18n for social link aria-labels
  • added error logging for GraphQL responses

the sponsors_url fallback is correct as-is — when no token is available, profile is undefined so null is returned gracefully.

@github-actions
Copy link

github-actions bot commented Mar 24, 2026

Lunaria Status Overview

🌕 This pull request will trigger status changes.

Learn more

By default, every PR changing files present in the Lunaria configuration's files property will be considered and trigger status changes accordingly.

You can change this by adding one of the keywords present in the ignoreKeywords property in your Lunaria configuration file in the PR's title (ignoring all files) or by including a tracker directive in the merged commit's description.

Tracked Files

File Note
i18n/locales/ar.json Localization changed, will be marked as complete. 🔄️
i18n/locales/az-AZ.json Localization changed, will be marked as complete. 🔄️
i18n/locales/bg-BG.json Localization changed, will be marked as complete. 🔄️
i18n/locales/bn-IN.json Localization changed, will be marked as complete. 🔄️
i18n/locales/cs-CZ.json Localization changed, will be marked as complete. 🔄️
i18n/locales/de-AT.json Localization removed, will be marked as missing. 🔄️
i18n/locales/de-DE.json Localization changed, will be marked as complete. 🔄️
i18n/locales/de.json Localization removed, will be marked as missing. 🔄️
i18n/locales/en.json Source changed, localizations will be marked as outdated.
i18n/locales/es.json Localization changed, will be marked as complete. 🔄️
i18n/locales/fr-FR.json Localization changed, will be marked as complete. 🔄️
i18n/locales/hi-IN.json Localization changed, will be marked as complete. 🔄️
i18n/locales/hu-HU.json Localization changed, will be marked as complete. 🔄️
i18n/locales/id-ID.json Localization changed, will be marked as complete. 🔄️
i18n/locales/it-IT.json Localization changed, will be marked as complete. 🔄️
i18n/locales/ja-JP.json Localization changed, will be marked as complete. 🔄️
i18n/locales/kn-IN.json Localization changed, will be marked as complete. 🔄️
i18n/locales/mr-IN.json Localization changed, will be marked as complete. 🔄️
i18n/locales/nb-NO.json Localization changed, will be marked as complete. 🔄️
i18n/locales/ne-NP.json Localization changed, will be marked as complete. 🔄️
i18n/locales/pl-PL.json Localization changed, will be marked as complete. 🔄️
i18n/locales/pt-BR.json Localization changed, will be marked as complete. 🔄️
i18n/locales/ru-RU.json Localization changed, will be marked as complete. 🔄️
i18n/locales/ta-IN.json Localization changed, will be marked as complete. 🔄️
i18n/locales/te-IN.json Localization changed, will be marked as complete. 🔄️
i18n/locales/tr-TR.json Localization changed, will be marked as complete. 🔄️
i18n/locales/uk-UA.json Localization changed, will be marked as complete. 🔄️
i18n/locales/zh-CN.json Localization changed, will be marked as complete. 🔄️
i18n/locales/zh-TW.json Localization changed, will be marked as complete. 🔄️
Warnings reference
Icon Description
🔄️ The source for this localization has been updated since the creation of this pull request, make sure all changes in the source have been applied.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
app/pages/about.vue (1)

269-290: Avoid calling getSocialLinks(person) twice per card.

The function is invoked both for the v-if check (line 270) and the v-for loop (line 274), creating redundant iterations for each governance member. Consider extracting the result to avoid duplicate computation.

♻️ Suggested refactor using a computed or inline variable

One option is to use a small helper component or restructure to compute once. Alternatively, if you want to keep it inline, you could use a v-for with a length check:

-                    <div
-                      v-if="getSocialLinks(person).length"
-                      class="relative z-10 flex items-center gap-1"
-                    >
-                      <a
-                        v-for="link in getSocialLinks(person)"
+                    <template
+                      v-for="(links, index) in [getSocialLinks(person)]"
+                      :key="index"
+                    >
+                      <div
+                        v-if="links.length"
+                        class="relative z-10 flex items-center gap-1"
+                      >
+                        <a
+                          v-for="link in links"

Or extract to a computed property that maps all governance members to their social links.

server/api/contributors.get.ts (1)

225-244: Consider restructuring to avoid object spread in map.

The static analysis tool flags spreading to modify object properties in map calls as inefficient. While the impact is minimal for this array size, you could restructure to directly construct the object.

♻️ Suggested refactor
     return filtered
-      .map((c): GitHubContributor & { order: number } => {
+      .map((c): GitHubContributor & { order: number } => ({
         const { role, order } = getRoleInfo(c.login, teams)
         const profile = governanceProfiles.get(c.login)
-        // When profile data is unavailable (e.g., no token), sponsors_url is null.
-        const sponsors_url = profile
+        login: c.login,
+        id: c.id,
+        avatar_url: c.avatar_url,
+        html_url: c.html_url,
+        contributions: c.contributions,
+        role,
+        order,
+        sponsors_url: profile
           ? profile.hasSponsorsListing
             ? `https://github.com/sponsors/${c.login}`
             : null
-          : null
-        const bio = profile?.bio ?? null
-        const socialAccounts = profile?.socialAccounts ?? []
-        return {
-          ...c,
-          role,
-          order,
-          sponsors_url,
-          bio,
-          socialAccounts,
-        }
-      })
+          : null,
+        bio: profile?.bio ?? null,
+        socialAccounts: profile?.socialAccounts ?? [],
+      }))

Alternatively, keep the current structure if readability is preferred over the minor performance gain.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c58344d3-fb93-47bc-97be-2ce178a6c9ef

📥 Commits

Reviewing files that changed from the base of the PR and between e7100cc and 35a73b7.

📒 Files selected for processing (3)
  • app/pages/about.vue
  • i18n/locales/en.json
  • server/api/contributors.get.ts
✅ Files skipped from review due to trivial changes (1)
  • i18n/locales/en.json

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 20

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
i18n/locales/te-IN.json (1)

16-171: ⚠️ Potential issue | 🟠 Major

Please do not ship EN TEXT TO REPLACE literals in this locale.

These keys will render verbatim for Telugu users instead of falling back, including the new governance/social copy added in this PR. If the translations are not ready yet, it is safer to omit the unfinished entries from this locale file for now.

Based on learnings, "new or changed entries in i18n translation files (locale JSON files) may be omitted from non-English languages."

Also applies to: 181-246, 260-388, 393-559, 582-676, 798-843, 879-941, 998-1010, 1032-1034, 1068-1069, 1090-1434

♻️ Duplicate comments (1)
server/api/contributors.get.ts (1)

220-234: ⚠️ Potential issue | 🟠 Major

Tokenless fallback still drops the sponsor action.

When githubToken is absent, governanceProfiles is always empty, so profile is undefined and sponsors_url stays null for every governance member. That means preview/tokenless deployments cannot show the sponsor link on governance cards, and the inline comment is misleading because there is no REST-derived fallback in this branch. If the UI still needs that CTA in tokenless environments, this branch needs a deterministic fallback instead of null.

🧹 Nitpick comments (1)
app/pages/about.vue (1)

250-254: Only set the bio title when truncation actually happens.

At the moment every bio gets a browser tooltip, even when the full text is already visible. If the intended behaviour is “tooltip on overflow”, bind title only after an overflow check.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c0f936ac-22a3-4979-8191-8a34cd6f6fe1

📥 Commits

Reviewing files that changed from the base of the PR and between 125f9e9 and c83970c.

📒 Files selected for processing (31)
  • app/pages/about.vue
  • i18n/locales/ar.json
  • i18n/locales/az-AZ.json
  • i18n/locales/bg-BG.json
  • i18n/locales/bn-IN.json
  • i18n/locales/cs-CZ.json
  • i18n/locales/de-DE.json
  • i18n/locales/en.json
  • i18n/locales/es.json
  • i18n/locales/fr-FR.json
  • i18n/locales/hi-IN.json
  • i18n/locales/hu-HU.json
  • i18n/locales/id-ID.json
  • i18n/locales/it-IT.json
  • i18n/locales/ja-JP.json
  • i18n/locales/kn-IN.json
  • i18n/locales/mr-IN.json
  • i18n/locales/nb-NO.json
  • i18n/locales/ne-NP.json
  • i18n/locales/pl-PL.json
  • i18n/locales/pt-BR.json
  • i18n/locales/ru-RU.json
  • i18n/locales/ta-IN.json
  • i18n/locales/te-IN.json
  • i18n/locales/tr-TR.json
  • i18n/locales/uk-UA.json
  • i18n/locales/zh-CN.json
  • i18n/locales/zh-TW.json
  • i18n/schema.json
  • server/api/contributors.get.ts
  • uno.config.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • uno.config.ts

Comment on lines +40 to +41
"open_main": "EN TEXT TO REPLACE: Open main information",
"open_diff": "EN TEXT TO REPLACE: Open version differences"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Please avoid committing EN TEXT TO REPLACE strings in this locale.

If the copy is intentionally deferred, omitting these keys is better than shipping placeholder text. As it stands, localised users — and screen readers reading about.team.social_link_aria — will see the placeholder verbatim.

Based on learnings, new or changed entries in i18n translation files may be omitted from non-English languages; translations are not completed in-band in the same PR and are tracked elsewhere.

Also applies to: 99-99, 142-168, 217-229, 309-336, 397-445, 457-457, 482-482, 514-514, 536-537, 559-559, 635-637, 842-843, 905-906, 941-941, 1010-1010, 1090-1090, 1108-1109, 1221-1266, 1407-1433

Comment on lines +1009 to +1010
"sponsor_aria": "Спонсорувати {name} на GitHub",
"social_link_aria": "EN TEXT TO REPLACE: {name} on {platform}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't ship placeholder copy for the new social-link ARIA label.

about.team.social_link_aria will render the literal EN TEXT TO REPLACE... string for Ukrainian users, and because the key exists they will not fall back to the default locale. Please either provide the final translation here or omit the key until localisation lands.

Based on learnings, "It is acceptable for non-English locale files to be missing keys that exist in English locale files."

@ghostdevv
Copy link
Contributor

Looks like your rebase/merge got a little messed up? @howwohmm

Expand governance member cards on the about page to show:
- Bio/description line from GitHub profile
- Social identity links (X/Twitter, Bluesky, Mastodon, LinkedIn, etc.)

The existing `fetchSponsorable` GraphQL query is replaced with a broader
`fetchGovernanceProfiles` that fetches bio, twitterUsername, and
socialAccounts in the same request. Social icons use the existing
UnoCSS icon sets (simple-icons + lucide). Data gracefully degrades
when the GitHub token is unavailable.

Closes npmx-dev#1564

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@howwohmm howwohmm force-pushed the feat/governance-card-identity branch from c83970c to bf0f030 Compare March 26, 2026 05:00
@howwohmm
Copy link
Contributor Author

rebased onto latest main — should be clean now. let me know if you spot anything else.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (3)
app/pages/about.vue (1)

276-285: ⚠️ Potential issue | 🟡 Minor

Localise the social-link labels.

These aria-labels are still hard-coded English and derive the provider name straight from the enum value, so screen readers will announce odd strings such as “hometown”. Please route this through $t(...) and a provider display map instead of toLowerCase().

server/api/contributors.get.ts (2)

118-147: ⚠️ Potential issue | 🟠 Major

Handle GitHub GraphQL errors explicitly.

GitHub can return HTTP 200 with an errors array and partial or missing data. This parser ignores that field entirely, so broken profile fetches get cached as if they succeeded and the cards quietly lose their extra metadata for an hour. Please parse and log json.errors before accepting the payload.

GitHub GraphQL API response format: can a successful HTTP 200 response still include an `errors` array alongside `data`, and how should clients handle partial `data` plus `errors`?

As per coding guidelines, "Use error handling patterns consistently".


218-228: ⚠️ Potential issue | 🟠 Major

Tokenless mode still removes the sponsor CTA.

When githubToken is absent, governanceProfiles is always empty, so sponsors_url becomes null for every governance member. That is the root cause of the sponsor link disappearing in preview/tokenless environments, which does not match the PR’s graceful-degradation goal. Please preserve a non-GraphQL fallback here.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8ca3314b-3e38-424f-bb9a-00d5045db992

📥 Commits

Reviewing files that changed from the base of the PR and between c83970c and 7e191a0.

📒 Files selected for processing (2)
  • app/pages/about.vue
  • server/api/contributors.get.ts

- Merge twitterUsername into socialAccounts array (per ghostdevv feedback)
- Simplify getSocialLinks — single unified array, no special-casing

Closes npmx-dev#1564
@howwohmm howwohmm force-pushed the feat/governance-card-identity branch from 7e191a0 to d79af98 Compare March 26, 2026 05:15
@howwohmm
Copy link
Contributor Author

good call — moved twitterUsername into the socialAccounts array on the server side. GitHub returns it as a separate field because it predates socialAccounts, so I normalise it in fetchGovernanceProfiles before it ever reaches the client. getSocialLinks in about.vue is now just a plain .map() over the array.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: core team in /about, with description and identity claims

3 participants