Skip to content

Many things - #333

Merged
gregv merged 173 commits into
mainfrom
develop
Jul 23, 2026
Merged

Many things#333
gregv merged 173 commits into
mainfrom
develop

Conversation

@gregv

@gregv gregv commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

No description provided.

gregv and others added 30 commits April 20, 2026 21:35
Initial PageSpeed Insights mobile perf score was 10 / desktop 41. The
site was pulling the reCAPTCHA v3 script and full MUI imports on every
page, plus wasted preconnect hints.

- Scope GoogleReCaptchaProvider to pages that actually use reCAPTCHA.
  Previously wrapped the entire app in _app.js, forcing every visitor to
  download Google's reCAPTCHA v3 bundle (~200KB + a cross-origin script
  request) on the homepage, event pages, nonprofit pages, profile, etc.
  Now lives only around LeadForm, /contact, /nonprofits/apply, and the
  5 application pages.
- Add experimental.optimizePackageImports for MUI (material, icons, lab,
  x-data-grid, x-date-pickers), date-fns, lodash, react-icons. Next 16
  transitively tree-shakes named imports from these packages, which
  should trim the main chunk noticeably.
- Remove orphan Google Fonts preconnects from pages/index.js and
  hack/[event_id].js. The codebase declares Inter / Space Grotesk /
  Fira Code in CSS variables but never loads them via @font-face or
  @import, so the preconnects cost two DNS+TLS handshakes for no
  content.
- Drop react-beautiful-dnd (deprecated) and migrate the sole usage
  (NonprofitRanker.js) to @hello-pangea/dnd, which is already a
  dependency and has an identical API. Removes ~8 transitive packages.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nly-subject

Fix/admin contact readonly subject
…-name pages

- Add semantic <h1> to homepage, /sponsor, /volunteer, /office-hours, /hack,
  /projects (+ ProjectList component); promote existing variant on judge pages.
- Refresh title/description on /, /sponsor, /volunteer, /nonprofit-grants
  with keyword-targeted, intent-matching copy (synced og:/twitter: where present).
- noindex,follow on /nonprofit/[nonprofit_id] and /project/[project_id]:
  GSC shows ~2k impressions/0 clicks for third-party-name searches
  (e.g. "tumbling t ranch") which dilute domain CTR.
- Fix volunteer/index.js getStaticProps using stale title literal instead of
  the local const.

Refs: seo-git-correlation-summary.md (P0 quick wins)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…opportunities)

Six near-duplicate landing pages were competing for the same keyword cluster
with no canonical strategy (created together in c62c043, Jul 2025).
The Sonnet-driven content audit confirmed 4 of 6 are pure keyword swaps
of the same JSX; only 2 had unique content blocks worth preserving.

Consolidation strategy:
- /hackathon-judge-opportunities = CANONICAL (most descriptive long-tail; matches
  the highest-volume GSC query at pos 3.5).
- 301 redirects: /judge-a-hackathon, /judge-hackathon → canonical (files deleted).
- rel=canonical: /hackathon-judge, /hackathon-judging, /hackathon-judging-opportunities
  → canonical. Excluded from sitemap so the canonical accumulates link equity.

Content merged into canonical:
- "Demo Example: Risk Scoring Solution" YouTube section (from /hackathon-judging-
  opportunities) — credibility / dwell-time content.
- "Learn About Our Mission & Impact" gradient CTA section (from /hackathon-judging)
  — strong internal link to /about that none of the other pages had.
- Hero image upgraded to judge_4_2024.webp (newest 2024 asset).
- New visible FAQ section with 8 Q&A (matches FAQPage schema).

Schema upgrades on canonical:
- WebPage.about: VolunteerOpportunity → JobPosting (richest type, with
  hiringOrganization, employmentType=VOLUNTEER, workLocation).
- FAQPage with 8 questions added to @graph.
- All URLs switched to https://www.ohack.dev (canonical scheme).

Critical infrastructure fix in _app.js:
- structuredData prop was never rendered — 17 pages had carefully built JSON-LD
  that Google never saw. Added <script type="application/ld+json"> rendering
  alongside <title>. Same fix activates schema on /about, /office-hours,
  /community-champions, /volunteer, /about/judges, /about/mentors, /about/
  hackers, /about/locations/asu-tempe-arizona, and the judge pages.
- Added <link rel="canonical"> rendering when pageProps.canonical is set.

Bug fixes uncovered while editing:
- All 4 hackathon-judg* pages had the same getStaticProps bug as PR1's
  volunteer/index.js: a stale literal title in props masked the SEO-rich
  local const. _app.js renders pageProps.title — so the actual <title>
  was the stale one. Fixed across all 4.

Sitemap config:
- Excluded the 3 rel=canonical pages.
- Added "judge" to the priority-0.8 keyword list (was only "judging") so the
  canonical gets the right priority.

Internal-link sweep (Haiku): no internal links to the deprecated URLs were
found in src/. Existing links already pointed to the canonical.

Refs: seo-git-correlation-summary.md (P0 #1 — judge cannibalization)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ge schema)

Targets the "coding for nonprofits" keyword cluster (113 imp at pos 9.8 for the
primary, with secondaries: "coding nonprofits", "nonprofit software development",
"coding for charity", "coding for social good", "develop for good" — total ~660
weekly impressions sitting on positions 24-46 across the cluster).

Page structure:
- Single h1 "Coding for Nonprofits"; semantic h2/h3 hierarchy.
- ~2,200 words across 6 sections: model overview, how nonprofits get free
  software (3-step), what kinds of projects work (and don't), why we do it
  this way (capacity gap + talent gap + multiplier effect), how to participate
  (3 audience cards), FAQ (8 questions).
- Hero CTAs to /nonprofits/apply and /volunteer.
- Five distinct internal-link targets: /nonprofits, /about, /volunteer, /hack,
  /sponsor, /hackathon-judge-opportunities.

Schema:
- Organization (with sameAs LinkedIn/GitHub/Twitter)
- WebPage with about → Service ("Free Software Development for Nonprofits",
  provider Opportunity Hack, areaServed Worldwide)
- BreadcrumbList: Home → Coding for Nonprofits
- FAQPage with all 8 Q/A as mainEntity (matches the visible Accordion
  content; Google's FAQPage rich-result requires both schema and visible UI).

Internal links added on existing pages:
- src/pages/index.js: text Button "Learn how we code for nonprofits" after
  the HackathonList section.
- src/pages/about/index.js: inline Link woven into the founding paragraph.
- src/components/NonProfitList/NonProfitList.js: Alert callout above the
  Upcoming Events block — visible on /nonprofits.

Verified end-to-end via static HTML inspection: <title>, rel=canonical,
FAQPage JSON-LD, Service schema all rendered. Sitemap correctly includes
/coding-for-nonprofits.

CLAUDE.md updated to document the pillar-page pattern.

Refs: seo-git-correlation-summary.md (P0 + new content velocity)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
[SEO] PR1: H1 tags, meta refresh, noindex third-party-name pages
[SEO] PR3: Consolidate 6 judge pages into 1 canonical + activate dead JSON-LD site-wide
… in index.js

Co-authored-by: gregv <6913307+gregv@users.noreply.github.com>
[SEO] PR4: New /coding-for-nonprofits pillar page (~2.2k words, FAQPage)
Site-wide Organization JSON-LD in _app.js renders on every page (including
those without their own structuredData prop — /sponsor, /volunteer, etc.).
Single source of truth at @id https://www.ohack.dev/#organization with
sameAs LinkedIn / GitHub / Twitter / Instagram / YouTube — supports brand
SERP features (knowledge panel eligibility) and entity verification.

/about/judges:
- The page already had FAQ_DATA (8 Q&A in the visible Accordion at line 609).
  Wrapped that array as FAQPage JSON-LD via mainEntity.map. Visible UI ↔
  schema parity is preserved (Google's hard requirement for FAQ rich results).
- Switched all schema URLs from ohack.dev → www.ohack.dev for canonical
  consistency.
- Added canonical prop pointing to the www URL.
- Removed per-page Organization (subsumed by the global one in _app.js).
- Fixed the same stale-literal title bug (props.title was returning a
  literal that masked the SEO-rich local const).

/about/mentors:
- No FAQ content exists — schema would have to be fabricated to populate
  FAQPage, which violates the visible-UI-parity rule. Skipped FAQPage here.
  Adding mentor FAQ content is a future content-writing task.
- Same stale-literal title bug fixed; canonical added.

Verified via static HTML inspection:
- /about/judges: FAQPage with 8 Question/Answer pairs renders.
- Homepage / about / index / any page: global Organization JSON-LD with
  sameAs array renders.

Refs: seo-git-correlation-summary.md (P1 — FAQPage + Organization)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Targets 724 weekly unclaimed impressions across:
- "hackathon judging criteria" (256 imp at pos 28.5)
- "hackathon scorecard" (187 imp at pos 39.4)
- "hackathon criteria" (281 imp at pos 59.1)

Authoritative reference for OHack's 4-category framework — meant to be linked
to and re-shared by other hackathon organizers. Open-source rubric, no
attribution required.

Page structure (~2.5k words, single h1, semantic h2/h3/h4):
- Intro framing the problem (bad criteria reward demos, good ones identify
  ship-ready projects)
- 4-category framework (Scope, Documentation, Polish, Security) — each with
  body + "what to look for" bullets
- Scoring scale (1-5) with calibration guidance
- Worked example: scoring a real volunteer-scheduling project across all 4
  categories with score justifications
- Copy-pasteable scorecard template (Box component=pre, monospace) — anchor
  id="scorecard" for the "Copy the Scorecard" hero CTA
- 5 common judging pitfalls (Accordion)
- 8 FAQs (Accordion)
- 3-button final CTA back to /hackathon-judge-opportunities, /about/mentors,
  /coding-for-nonprofits

Schema (rendered via _app.js JSON-LD pipeline; no per-page Organization
since global one in _app.js handles it):
- WebPage with about → CreativeWork (the rubric IS a creative work)
- BreadcrumbList: Home → Hackathon Judging Criteria
- HowTo with 4 HowToSteps (one per scoring category) + estimatedCost $0,
  totalTime PT15M — eligible for HowTo rich results
- FAQPage with 8 Question/Answer pairs matching the visible Accordion

Internal link added on canonical judge page:
- src/pages/hackathon-judge-opportunities.js — Alert + Button below the
  Training Score Paper in the "Expert Evaluation Criteria & Training"
  section, linking to /hackathon-judging-criteria. Drives judge-page
  visitors to the deeper resource without disrupting the existing
  conversion flow.

Verified via static HTML inspection: canonical, title, and all 5 schema
types (WebPage, BreadcrumbList, HowTo, FAQPage, CreativeWork) render in
the <script type="application/ld+json"> output. Sitemap includes the URL.

CLAUDE.md updated with an "SEO Resource Pages" section documenting the
pattern for future resource-page additions.

Refs: seo-git-correlation-summary.md (P2 — judging-criteria opportunity)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ng pages

Two pillar pages timed to be indexed before the Fall 2026 hackathon
(Nov 14-15) — per the git-correlation analysis, new pages take 3-5 weeks
to rank, so an Aug 1 ship is the latest reasonable date.

/hackathon-for-social-good (~2.0k words):
- Pillar page positioning the OHack hackathon as the canonical "hackathon
  for social good". Distinct from /coding-for-nonprofits (which is about
  the SERVICE) — this page is about the EVENT.
- Targets: hackathon for social good (75 imp pos 24.6), hack for social
  impact (22 imp pos 39.5), develop for good (42 imp pos 38.8).
- 7 sections: what makes a hackathon for social good, how it works,
  past event highlights (3,000+ devs / 200+ nonprofits / 2013), what
  makes OHack different (mentor density, post-event continuation,
  open-source rubric), Fall 2026 event details, how to participate,
  8-question FAQ.
- Schema: WebPage(about Event), BreadcrumbList, Event for Fall 2026
  with Place(ASU)/PostalAddress/Offer, FAQPage with 8 Q/A.

/hackathons/arizona (~1.7k words):
- Local landing page targeting Arizona-based companies (philanthropy
  angle) and ASU students. Designed to drive sponsor inquiries to
  /hack/request and nonprofit applications.
- Targets: asu hackathon (942 imp pos 7.2), phoenix hackathon (151 imp
  pos 4.8), hack arizona (230 imp pos 11.6), hackathons near me
  (339 imp pos 21.5) — total 1,662 weekly impressions of local intent.
- DYNAMIC: pulls real events from useHackathonEvents("current") and
  ("previous"), filters by location string for Arizona / Phoenix /
  ASU / Tempe / Scottsdale / Mesa / Chandler / Gilbert / Glendale /
  Peoria / Polytechnic. Renders Upcoming and Past sections with the
  actual event archive — no hardcoded event list.
- 6 sections: upcoming AZ events (with helpful empty-state pointing to
  Fall 2026), past AZ events archive, why ASU is the anchor venue, AZ
  company sponsorship pitch (linking to /hack/request), AZ nonprofit
  hackathon-request 3-step, 8-question FAQ.
- Schema: WebPage(about Place), BreadcrumbList, Event(Fall 2026) with
  GeoCoordinates(33.4242, -111.9281) for ASU Tempe, FAQPage.

Internal-link surface added on existing pages:
- src/pages/index.js: 3 pillar text-buttons row (existing
  /coding-for-nonprofits + new /hackathon-for-social-good +
  /hackathons/arizona).
- src/pages/about/index.js: inline Link in founding paragraph.
- src/pages/coding-for-nonprofits/index.js: cross-link to
  /hackathon-for-social-good in final CTA.
- src/pages/hack/index.js: Alert above events list pointing AZ visitors
  to /hackathons/arizona.
- src/pages/sponsor/index.js: sentence in About section linking to
  /hackathons/arizona for local Arizona companies.

Sitemap config: added "hackathon" to the priority-0.8 keyword list so
both new pages and any future hackathon-related landing pages get
appropriate priority. /hackathon-for-social-good and
/hackathons/arizona now ship at priority 0.8.

CLAUDE.md updated.

Verified end-to-end via static HTML inspection: both pages render
title, canonical, full schema graph including Event with proper
location data and Offer with $0 price.

Refs: seo-git-correlation-summary.md (P1 — new content velocity)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PR5 skipped FAQPage on /about/mentors because the page had no FAQ content
to schematize (the visible-UI-parity rule for FAQ rich results means
inventing schema without matching UI is invalid).

This commit closes that gap: 8 mentor FAQs covering the questions we
actually field from prospective mentors — what mentors do, time
commitment, technical-expertise expectations, paid vs volunteer, remote
mentoring, first-time mentor onboarding, the Founding Engineer
continuation path, and the mentor-vs-judge role distinction.

Implementation:
- New FAQ_DATA constant at the top of the page file (mirrors the
  /about/judges pattern from PR5).
- Visible Accordion section rendered below <Mentorship />, matching the
  visual style of other landing pages (Container, Typography intro, mb,
  fontSize, lineHeight on answer body).
- Final CTA Paper after the FAQ pointing to /hack and
  /hackathon-judge-opportunities.
- FAQPage JSON-LD with mainEntity = FAQ_DATA.map(...) — visible UI ↔
  schema parity preserved (Google's hard requirement for FAQ rich
  results).
- Cleaned up the structuredData to match PR5's pattern: removed the
  per-page Organization (subsumed by global Organization in _app.js),
  switched all schema URLs to https://www.ohack.dev for canonical
  consistency.

Verified via static HTML inspection: 8 Question entries inside FAQPage
schema render correctly. WebPage and BreadcrumbList schemas preserved.

Refs: PR5 follow-up (mentor FAQ content) — closes the only outstanding
gap from the FAQPage rollout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adapts a standalone HTML field report into a full Next.js page targeting
the people we want to attract: corporate teams considering a
hackathon-for-good, university partners thinking about hosting, and
philanthropic Arizona companies looking for evidence Opportunity Hack
delivers. Companion to /hackathon-for-social-good — that page is the
elevator pitch, this page is the data dossier.

Page

- URL /12-years-of-social-good
- Editorial dark-mode design (Fraunces serif + JetBrains Mono +
  Inter Tight) scoped to the .report wrapper via styled-jsx so the
  light-theme NavBar/Footer don't clash. Deliberate "this is a report,
  not a marketing page" visual register.
- 8 sections: masthead + headline stats / Story (4-era timeline of all
  21 events) / Funnel (yearly registration vs submission) / Biggest
  Lever (team formation) / Acquisition (channel quality) / Who Shows Up
  (universities, cities, specialty, domain) / Behavior (timing, repeat
  hackers) / What Gets Built (tech stack) / 5 hackathon-host lessons.
- Final CTA section with three cards: host a hackathon (/hack/request),
  sponsor (/sponsor), read the companion page (/hackathon-for-social-good).
  A second prominent "Want to host a hackathon for social good?" CTA
  sits at the top of the masthead so the host intent is captured before
  scroll.
- The original report's §9 (methodology + "one lesson worth sharing")
  is intentionally omitted per instruction.

Charts

- 11 charts via chart.js + react-chartjs-2 (added as deps).
- Code-split into a single chunk via Next.js dynamic imports with
  ssr:false. None of chart.js loads on any other page; only on this
  page, only client-side.
- ChartSkeleton shimmer placeholder for the loading window.

SEO

- Title: "12 Years of Social Good Hackathons: A Field Report | OHack"
- Description targets the host/sponsor audience explicitly.
- Keywords meta covers: how to host a hackathon, hackathon impact
  report, hackathon completion rate, social good hackathon, run a
  hackathon, organize a hackathon, hackathon best practices, hackathon
  data, hackathon case study.
- og:type article + article:published_time + article:author
- Schema: Article (with publisher → global Organization @id from PR5),
  WebPage, BreadcrumbList, FAQPage with 5 entries (one per lesson —
  matches visible UI for FAQ rich-result eligibility).

Internal links

- /hackathon-for-social-good: prominent "Read the 12-Year Field Report"
  button paired with the existing "Browse Past Hackathons" CTA in the
  Past Event Highlights section.
- /sponsor: inline link in the Current Sponsorship Opportunities lede,
  positioned where prospective sponsors are evaluating "should I fund
  this".
- The page itself out-links to: /hack/request, /sponsor,
  /hackathon-for-social-good, /coding-for-nonprofits.

Sitemap

Added "social-good" and "nonprofits" to the priority-0.8 keyword list
so this page and any other pillar pages on those topics get the right
priority. /12-years-of-social-good ships at 0.8.

New deps: chart.js@^4.5.1, react-chartjs-2@^5.3.1. Both code-split.

Source: /Users/gregv/Downloads/opportunity_hack_field_report.html

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Register BarController and LineController explicitly. Required by chart.js v3+
when using the generic Chart component with mixed bar/line datasets, which
was throwing "line is not a registered controller" on production.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…social-good, /hackathons/arizona, judging-criteria, heart cert FAQ, chart.js fix
- Application flow including meal selection
- Public profile updates
- Show praises on public profile along with hackathon history
- Profile updates
- Add media for each hackathon
Full Trello-parity planning board for OHack hackathon organizers:

Frontend (Next.js):
- /hack/[event_id]/plan — ISR page (revalidate 60s), public read
- usePlanningBoard hook — adaptive polling (6s hot / 30s cold), ETag,
  optimistic mutations with If-Match, 412 conflict support
- PlanningBoard + PlanningList + PlanningCard — @dnd-kit drag/drop
- PlanningCardDialog — description, checklists, attachments (image-only),
  comments thread, card archive, live kind stat strips
- PlanningBudgetWidget — Goal/Raised/Earmarked/Spent stacked bars per bucket
- PlanningSponsorPipelineView — pipeline columns by outreach_status
- PlanningCardConflictDialog — mine/theirs/side-by-side with localStorage draft
- PlanningRunOfShowSyncDialog — diff preview + confirm before writing countdowns
- PlanningEditorsManager + PlanningSlackSettings — admin controls
- PlanningPublicNotice — sticky public-data warning on board, dialog, comment box
- 5 per-kind stat strips (judges/mentors/hackers/nonprofits/teams)
- LexoRank helper (src/lib/lexorank.js)
- Admin Planning Board accordion in Advanced Settings
- HackathonDuplicator: reset planning subobject on duplicate

Backend (Flask, separate commit on backend repo):
- api/planning/planning_views.py — 20 routes: lists, cards, comments,
  labels, editors, config, seed-template, RoS sync, rebalance, flush_digests
- services/hackathon_planning_service.py — require_plan_editor decorator,
  can_write_plan, is_admin (editor list never cached)
- services/planning_template_service.py — 8-list OHack default template
- services/planning_slack_notifier.py — Firestore-backed digest queue,
  lazy + read-driven flush (no cron required)
- services/planning_ros_service.py — RoS sync preserving source:manual entries
- model/planning.py — schema constants and defaults
- validators.py — validate_planning_subobject
- firestore.indexes.json — composite indexes for planning subcollections
- _flush_digests endpoint secured: token required or localhost-only fallback

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…lbart-87b2d3

Add hackathon planning board — /hack/[event_id]/plan
…to-edge horizontal scroll

The board was rendering inside a padded Box with overflowX:hidden on the
parent and the page footer competing for vertical space, so columns past
the viewport felt truncated and there was no obvious scroll affordance.

Trello's pattern, applied here:
- Board is a fixed-position workspace that fills the viewport below the
  global NavBar, so the page footer no longer pulls vertical attention.
- Single horizontal scroll container with internal padding (so the first
  and last lists have breathing room without truncating the scroll surface).
- Each list sizes to the parent (maxHeight: 100%) and scrolls vertically
  on its own — independent of viewport-relative calc().
- Visible custom scrollbar at the bottom (12px, white-on-blue) so the
  scroll affordance is unmistakable.
- Compact public-notice strip above the board (less vertical space than
  the full Alert with title) since the board is now the focal point.
- Trello-style accent background (#1e88e5) and lighter list bg (#ebecf0).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces the raw PropelAuth user_id text input with a debounced search
backed by /api/planning/_users/search. Admins type a name or email,
pick from a dropdown showing avatar + name + email, and the propel
user_id is added to editors[] without ever being human-visible.

Also resolves display profiles for existing editors so the list shows
real names + avatars instead of opaque IDs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…elper

orgHelper.getOrgs()[i] returns plain org info objects without
hasPermission() — that call silently threw, the catch returned false,
and admins were shown as Viewer with all editor controls hidden.

Switch to the established OHack pattern (see pages/admin/index.js:128):
  userClass.getOrgByName("Opportunity Hack Org").hasPermission(...)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
selectedCard was a stale snapshot captured at click time and never
refreshed when the board polled. Two consequences:

  1. After saving a description, reopening the card showed empty —
     the dialog was reading the original (empty) description from
     the captured selectedCard, not the freshly-saved value in board.cards.
  2. Subsequent PATCHes in the same dialog used the stale updated_at,
     guaranteeing 412 conflicts on edit #2.

Fix: derive liveCard = board.cards.find(c.id === selectedCard.id) ||
selectedCard so the dialog always sees the latest version (including
the fresh updated_at for If-Match).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The admin "Show budget widget on the public event page" toggle was a
no-op — the value was persisting to planning.budget_widget_on_event_page
but no surface read it. Adds:

- PlanningBudgetEventPageWidget: client-side wrapper that fetches the
  planning board (public read) and renders PlanningBudgetWidget inside
  a Paper card with a "See the full planning board →" link.
- /hack/[event_id]: mounts the widget above the donation thermometer,
  gated on event.planning?.enabled && event.planning?.budget_widget_on_event_page
  so events without planning don't pay any cost.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
gregv and others added 27 commits June 20, 2026 14:05
Branch on slackNotificationSent from the API response:
- True: existing 'announced in #ask-a-mentor' message
- False/absent: inline Alert explaining no Slack account was found and linking to
  the Slack join page so the mentor can fix it and check in again

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…l fields

- ApplicationReviewCard: header LinkedIn chip (blue, always visible) + 'No LinkedIn'
  indicator; OHack profile chip via user_db_id; avatar uses profile_image fallback;
  flexWrap header for narrow screens; CardActions stacks on mobile; availability
  timeline wrapped in overflowX:auto; 'All submitted fields' Collapse section shows
  any key not already rendered (skip-list for internal/audit fields)
- ApplicationReviewList: LinkedIn filter (All / Has / No); mobile Accordion collapse
  for filter bar (collapsed by default, 'Active' badge when filters set);
  useTheme/useMediaQuery added; linkedInFilter state + dep array
- VolunteerTable: profile link (OpenInNew icon) in table row actions + MobileCardView
  actions; avatar uses profile_image; NextLink import for /profile/{user_db_id}

Backend counterpart (user_db_id/profile_image enrichment) is in
feat/volunteer-admin-profile-link on backend-ohack.dev.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
[Mentor] Show actionable notice when check-in has no Slack announcement
…provements

[Volunteer Admin] Mobile, LinkedIn filter, OHack profile link, all submitted fields
…provements

Feat/volunteer admin improvements
Self-service feedback form for selected volunteers and nonprofit partners,
rendered at /hack/[event_id]/survey and /feedback (alias).

- surveyQuestions.js: pure question catalog + getSurveyQuestions(role, mode).
  Universal block + per-role blocks (hacker/mentor/judge/nonprofit/volunteer),
  each tagged live/post/both; stable answer IDs across modes.
- EventSurvey.js: reads mode + eligibility from /api/surveys/<id>/context,
  posts to /responses. Logged-in selected volunteers skip CAPTCHA;
  nonprofits/anonymous get an invisible reCAPTCHA v3 token. Backend owns the
  live/post/upcoming decision. noindex, refined design.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…on low scores

- Role picker is now limited to ctx.allowed_roles (the role[s] the volunteer is
  isSelected for). A single allowed role renders as a fixed chip; nonprofits and
  anonymous users are locked to Nonprofit. An effect keeps role within the
  allowed set (backend also enforces).
- hacker_onboarding now reveals a "What could be improved?" text area only when
  the score is 1-3 (scale_text + noteWhen); the note is cleared if the score
  rises above 3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New SurveyCTA component (renders only once an event is live or ended; hidden
for upcoming; mount-gated to avoid hydration mismatch). Wired into:
- /hack/[event_id] event page (after masthead — reaches nonprofits too)
- mentor-checkin, manageteam, team/[team_id]
- judge-application (gated on isSelected)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- /admin/feedback: 3-tab volunteer.admin page (Event Surveys, Peer Feedback, Onboarding) registered in both nav registries.
- EventSurveysPanel: Single-event (during-event pulse, needs-attention, themes, breakdowns) + Compare-events sub-view (CrossEventSurveys: calendar macro-trend + elapsed-time overlay) via GET /api/surveys/overview.
- OnboardingPanel: trends + action lists; PeerFeedbackPanel: name-resolved list.
- Pure helpers in surveyAnalytics.js / onboardingAnalytics.js.
- EventSurvey.js: hoist Shell to module scope so the form no longer remounts and drops textarea focus while typing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rebuild the volunteer time-tracking page on the civic-editorial "refined"
system (RefinedRoot/.ohx-*, Fraunces/Hanken, navy/terracotta, native form
controls) and fix the flows so people can accurately track committed and
actual time.

Fixes / UX:
- Wall-clock timer: elapsed is computed from a persisted startEpoch (capped at
  the commitment), so it's accurate across background tabs and survives refresh.
  Replaces setTimeout tick-counting (throttled in bg tabs) and the stale-closure
  save that lost a session on refresh right after starting.
- Manual time logging: new "Log time you already did" card (date + hours +
  reason) for time done away from the keyboard.
- Inclusive date range via native date inputs normalized to start/end-of-day
  (the old MUI DatePicker sent local-midnight -> UTC, excluding same-day rows).
- Graceful empty state instead of an error; single on-theme Toast (dropped the
  duplicate Alert + Snackbar).
- Restyle FunVolunteerTimer (navy ring showing elapsed) and VolunteerStatsTable
  (hairline day-grouped rows) with CSS-var fallbacks.

Notes:
- Fonts injected via useEffect (next/head drops the Google Fonts <link>s on this
  withAuthInfo-wrapped, client-rendered page).
- Backend lazy-create fix is in a separate PR on backend-ohack.dev.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…veys

Event feedback survey pages (/hack/[event_id]/survey + /feedback)
…ined

Refine /volunteer/track: accurate time tracking + fix load bug
Live projects don't need new volunteers, so the help toggle no longer
renders when status === "production". Users already marked as helping
still see it so they can toggle off. Maintenance-status projects keep
the toggle since they still need patching/enhancement help.

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

Hide "Want to help?" toggle on production-status projects

@github-advanced-security github-advanced-security AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

CodeQL found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

@vercel

vercel Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
frontend-ohack-dev Ready Ready Preview Jul 23, 2026 3:13pm

Request Review

@gregv
gregv merged commit 3e39fd1 into main Jul 23, 2026
4 of 5 checks passed
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.

4 participants