feat(badges): add Phase 1 identity and milestone badges
Nobody has claimed this yet.
- Dominant language
- HTML
- Stars
- 33
- Forks
- 104
- Avg merge
- 12h 3m
- Merged PRs (30d)
- 10
Description
Summary
Extend `computeBadges` with ~12 new badges derived entirely from existing data — no schema changes, no new tables, no R2 artwork. Account-anniversary, profile-completeness, identity-link, and breadth-of-craft badges that surface immediately for any active member.
Requirements
- Anniversary 1-Year / 5-Year / 10-Year milestones from `users.createdAt`. Emit only the highest tier the user qualifies for.
- ORCID Linked / GitHub Linked / LinkedIn Linked identity badges from non-null `profile.{orcid,githubUrl,linkedinUrl}`.
- Profile Complete milestone — earned when displayName + headline + bio + jobTitle + institution + publicLocation are all non-empty.
- Polyglot milestone — 5+ entries in `user_languages`.
- Polymath milestone — 10+ entries in `user_skills`.
- Cross-Disciplinary milestone — 3+ entries in `user_disciplines`.
- Decade Roster milestone — 10+ event participations of any role.
- Speaker Streak milestones — Speaker Decade (10+ talks) and Speaker Quarter (25+ talks). Suppress the lower tier when the higher is earned.
- Globetrotter milestone — events attended in 3+ distinct countries.
- New glyphs in `HexStamp.tsx` for each kind, geometric primitives matching the existing visual system.
- `pickFeaturedBadges` priority updated so identity / streak / completion milestones surface in the hero strip ahead of plain attendance.
Context
`packages/api/src/lib/badges.ts` today only sees `event_attendances`, `leadership_terms`, `users.createdAt`, and `users.isLegacyImport`. This phase widens its inputs to include profile fields and discipline/skill/language counts (all already loaded by `loadMemberDossier`) without touching the schema. Phase 2 and Phase 3 follow in separate issues.
Implementation Notes
- Identity badges (ORCID/GitHub/LinkedIn) are verification marks, not contribution marks — give them a distinct visual sub-tier so they don't compete with event chips for prestige weight.
- Anniversary badges emit at-most-one — a 12-year member shouldn't carry 1, 5, and 10 simultaneously.
- For the Speaker Streak threshold (10 vs 25), count distinct `event_attendances` rows with role=speaker; ignore poster-only attendances.
- Globetrotter needs a country signal on `events.location` — if missing, use the event's joined country reference, or skip if neither is present (don't emit false positives).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in packages/api/src/lib/badges.ts and trace the existing computeBadges inputs from loadMemberDossier, then inspect HexStamp.tsx and pickFeaturedBadges for rendering and ordering. Done means all listed badge thresholds and suppression rules work without schema changes, the new glyphs match the existing visual system, and the priority order surfaces the specified milestones.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- full-stack
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 64/100