Skip to content

✨ feat: catalog navigation rework, wanted skills, onboarding + newsletter prefs#97

Merged
mathix420 merged 11 commits into
stagingfrom
feat/navigation-skills-onboarding
Jul 13, 2026
Merged

✨ feat: catalog navigation rework, wanted skills, onboarding + newsletter prefs#97
mathix420 merged 11 commits into
stagingfrom
feat/navigation-skills-onboarding

Conversation

@mathix420

Copy link
Copy Markdown
Owner

What

Three related workstreams, built from a content scan of the production catalog (130 geolocated case studies, 72 solutions, 64 issues, 122 tags — almost none of it reachable from the old single-list home page).

Navigation & discovery

  • Home rework: new hero, linked catalog stat line, catalog-wide full-text search (/api/search/quick, grouped issue/solution/case-study results), topic quick-nav chips, explore-card grid into every section
  • New browse pages: /issues (search + tag + SDG + sort), /solutions (status filter), /case-studies (outcome / scale / semantic query), /tags (usage-ranked index), /map (fullscreen map of all geolocated content with layer toggles)
  • New/extended endpoints: /api/stats, /api/map, /api/solutions, /api/search/quick; sdg filter on /api/issues; usage counts on /api/tags and /api/sdgs; case-study limit cap raised to 50
  • Case-study cards redesigned minimal; solution cards drop irrelevant sub-issue/solution chips; sitemap + SEO + JSON-LD for all new pages; mobile responsiveness pass across public pages

Wanted skills (demand side of skill matching)

  • wanted_skills table (case-insensitive unique per node, custom migration 0007) + CRUD endpoints under /api/issue/:id/wanted-skills
  • "Skills wanted" section on issue/solution pages with social share; contribute page rework with always-on ways to help

Onboarding + newsletter preferences

  • /onboarding flow shown once after signup: skills, free-text interests, newsletter opt-in (frequency + content flags: good news, skill matches, topic matches, help wanted)
  • Interests are semantic, not relational: free text embedded via the existing OpenAI pipeline into user_interests.embedding (HNSW index, custom migration 0008), with a live "targets these topics" preview via /api/tags/similar
  • newsletter_prefs, users.onboarded_at, settings sections for both; sending pipeline deliberately out of scope

Migrations

00190021 (drizzle) + custom 0006_help_labels_indexes, 0007_wanted_skills_unique, 0008_user_interests_indexes. Applied and verified locally.

Testing

  • 96/96 unit tests pass; new API tests for wanted-skills and onboarding prefs under tests/api/
  • All endpoints exercised against the dev server (public reads, 401s for anonymous writes, onboarding redirect)
  • Every page screenshot-verified at desktop and true mobile viewport (390px via Playwright)

Notes for review

  • Also includes the help-labels moderation work and guide restructure that was on the working branch
  • 3 pre-existing tests/api failures (issue-detail moderation fields, default sort, similar too_short) predate this branch

🤖 Generated with Claude Code

mathix420 and others added 2 commits July 13, 2026 11:07
…tter prefs

Navigation and discovery:
- Rework home page: new hero, linked catalog stat line, catalog-wide
  full-text search (/api/search/quick) with grouped results, topic
  quick-nav chips, explore-card grid into every section
- New browse pages: /issues (tag + SDG + sort filters), /solutions
  (status filter), /case-studies (outcome/scale/semantic filters),
  /tags (usage-ranked topic index), /map (fullscreen map of all
  geolocated content with layer toggles)
- New endpoints: /api/stats, /api/map, /api/solutions,
  /api/search/quick; sdg filter on /api/issues; usage counts on
  /api/tags and /api/sdgs
- Minimal redesign of case-study cards; solution cards drop
  irrelevant sub-issue/solution chips; sitemap + SEO for new pages
- Mobile responsiveness pass across public pages

Community features:
- Wanted skills on issues/solutions: schema + unique index, CRUD
  endpoints, node-page section with social share, contribute card
- Contribute page rework with always-on ways to help
- Onboarding flow (/onboarding): skills, free-text interests with
  embedding-based topic preview (/api/tags/similar), newsletter
  opt-in with frequency + content preferences
- user_interests (vector embeddings + HNSW index), newsletter_prefs,
  users.onboarded_at; settings sections for interests + newsletter

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 13, 2026

Copy link
Copy Markdown

🧹 Code quality

Check Summary
vize lint 0 errors · 342 warnings
vize format all files formatted
fallow audit pass · 0 dead-code · 116 complexity · 0 dupes (151 files)

updated for 60c4625 · run #273

mathix420 and others added 9 commits July 13, 2026 11:44
Typecheck (vue-tsc):
- Widen useUmami track() data type to accept boolean | undefined so
  event props like { enabled, on, newsletter } and conditional
  { tag: x || undefined } type-check (NewsletterPrefs, map, onboarding,
  issues pages).
- contribute.vue: type help-label counts as Record<string, number> and
  guard LABEL_META[activeLabel]?.hint under noUncheckedIndexedAccess.

Formatting (vize fmt): reformat files flagged by format:check
(admin logs handler wrap + drizzle migration meta trailing newlines).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Escape user-derived values in revision notification emails (shared
  escapeHtml util, also reused by the OAuth consent page)
- Guard the revision approve compensation and make post-apply
  snapshot/audit writes loud-but-best-effort (never revert an applied
  revision to pending — that would allow a double apply)
- Exclude rejected case studies from the me/overview counter
- Add missing DB CHECK constraints for revisions.ai_verdict,
  revisions.decided_by_role, and newsletter_prefs.frequency
- /api/case-studies now returns { items, degraded } so clients can tell
  when a semantic query fell back to recency; surface it on the page
- Remove forbidden data-umami-event attributes; use useUmami() instead
  of the bare umami global in appeal handlers
- Clear debounce timers on unmount; skip empty-query quick search on
  homepage first paint
- Fix tests: assert the intended most_voted default sort, make
  similar.test.ts actually exercise the endpoint (summary, not
  description), and turn the stale issue-detail moderation-fields test
  into a privacy regression test

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Clickable stepper with sharp progress bars and live count chips,
personalized greeting, icon tiles + ghost step numerals on cards,
tap-to-fill skill examples, back navigation with direction-aware
transitions, ?step= URL sync, auto-add of typed-but-unsubmitted
skills on continue, and a completion panel with next-step CTAs
instead of an instant redirect.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Deduplicate the issue/solution list queries (issue-list.ts), route-param
and node-lookup guards (route-guards.ts), and parent counter updates
(issue-counters.ts, shared with the moderation worker), and spread a
common timestamps pair across the schema tables.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
….css

The three map components carried identical :deep(.leaflet-control-zoom)
overrides; hoist them into one global stylesheet.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Weekly/monthly digest emails for opted-in users, sent by cron tasks
(Mondays / 1st of month, 8am UTC):

- newsletter-digest.ts assembles per-run sections (good news, help
  wanted, product updates) once and per-user sections (interest matches
  via pgvector, skill matches vs recent wanted skills) per recipient;
  empty digests are never sent
- newsletter-send.ts mirrors compute:trust-scores (chunked, per-user
  failures isolated) with a lastSentAt column guarding cron retries
- dashboard-bento email template (rounded cards on gray, Oswald caps,
  chips, blue pill button) with a plaintext twin; all user content
  escaped
- HMAC-token unsubscribe endpoint that works logged-out, linked from
  every footer
- sendEmail now works without an h3 event: the cloudflare:scheduled
  hook stashes the EMAIL binding for tasks

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Eliminate all 6 fallow clone groups:
- add requireEventContext/loadIssueOr404 to route-guards, shared by the
  5 issue/case-study moderation handlers
- extract formatRelative into app/utils/relative-time.ts (4 call sites)
- extract cleanLinkRows into app/utils/link-rows.ts (6 call sites)

Also reformat the two drizzle-generated migration meta files that were
failing vize fmt --check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drizzle rewrites the snapshot/journal JSON in its own format on every
db:generate, so scanning them just makes a new migration fail fmt --check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mathix420
mathix420 marked this pull request as ready for review July 13, 2026 12:41
@mathix420
mathix420 merged commit f676c7b into staging Jul 13, 2026
11 of 12 checks passed
@mathix420
mathix420 deleted the feat/navigation-skills-onboarding branch July 13, 2026 12:41
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.

1 participant