From 4b153a09a026f770aea514e18311fae997253f90 Mon Sep 17 00:00:00 2001 From: Greg V <6913307+gregv@users.noreply.github.com> Date: Thu, 23 Jul 2026 07:49:12 -0700 Subject: [PATCH 1/3] Overhaul /onboarding: accurate content, full lifecycle, unified refined design Content: - New 9-step flow: Welcome, Mission, How It Works, Get Involved, Using the Site, Slack Tutorial, Introduce Yourself, FAQs, Feedback - New HowItWorksSection: nonprofit problem -> projects -> hackathon -> judging -> winners continue ~3 months -> Definition of Done -> production/maintenance, with the project-status ladder - New RolesSection: hacker/mentor/judge/volunteer/nonprofit role cards with time commitments and how to apply; keeps mentor + judge videos - New WebsiteTourSection: first-steps guide, site map, and a public- portfolio band (GitHub Issues = public credit; no issues in a repo -> pull the code and write tickets like a PM; Slack/demos/profile/praise round out what recruiters can review) - FAQ corrections: no project leads, no commenting on projects, no buddy system; joining a project = "Want to help?" toggle + project Slack channel + GitHub repo + next hackathon; answers now match /about/process and /about/completion - Slack step reorganized: two-account gotcha first (ohack.dev login does not create a Slack account -> /signup guide), real channel deep links (#introductions, #ask-a-mentor, #random, #npo-* per project) - Praise board called out for social proof on Welcome + portfolio band - Removed unused BuddySystem component and all buddy-system claims Design: - Scoped onboardingTheme ThemeProvider: Fraunces headings, Hanken body, navy/terracotta palette, flat hairline surfaces (no elevation shadows) - Shared StepHeader across all steps (renders h2; masthead owns the h1) - Calmed hover-lift cards and rainbow chips per the refined system Feedback plumbing: - topicOptions mirror the new steps; admin USEFUL_TOPICS keeps legacy "Buddy System" so old responses still chart Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 4 +- src/components/Onboarding/BuddySystem.js | 474 ------------------ src/components/Onboarding/FeedbackSection.js | 30 +- .../Onboarding/HowItWorksSection.js | 218 ++++++++ .../Onboarding/IntroductionPrompt.js | 15 +- src/components/Onboarding/MissionOverview.js | 31 +- src/components/Onboarding/OnboardingDialog.js | 2 +- src/components/Onboarding/OnboardingFAQ.js | 152 +++--- src/components/Onboarding/RolesSection.js | 206 ++++++++ src/components/Onboarding/SlackTutorial.js | 161 ++++-- src/components/Onboarding/StepHeader.js | 29 ++ .../Onboarding/WebsiteTourSection.js | 283 +++++++++++ src/components/Onboarding/WelcomeSection.js | 72 ++- .../admin/feedback/onboardingAnalytics.js | 9 +- src/pages/onboarding/index.js | 76 ++- 15 files changed, 1094 insertions(+), 668 deletions(-) delete mode 100644 src/components/Onboarding/BuddySystem.js create mode 100644 src/components/Onboarding/HowItWorksSection.js create mode 100644 src/components/Onboarding/RolesSection.js create mode 100644 src/components/Onboarding/StepHeader.js create mode 100644 src/components/Onboarding/WebsiteTourSection.js diff --git a/CLAUDE.md b/CLAUDE.md index bd033779..5ce35eff 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,7 +2,7 @@ ## "Refined" design scope (civic-editorial facelift) -Full spec + rollout checklist: **`docs/refined-design-system.md`** (read this before extending the look to more pages). Migrated so far: `/` (`src/pages/index.js`), `/projects` (`src/components/ProjectList/*`), the global NavBar (incl. a refined mobile layout — centered logo + refined dropdown), `/about`, `/about/judges`, `/about/mentors` (`Mentorship` rewritten as refined sections), `/about/success-stories`, `/about/completion`, `/sponsor`, `/nonprofits` (`NonProfitList` + new calm card `NonProfitListTileRefined`; original `NonProfitListTile` stays for event pages), `/blog` (`BlogPage`; reused `News` list kept), `/onboarding` (refined chrome; wizard logic intact), `/signup` (full rewrite: editorial hero, "What you get" benefit cards, numbered join steps beside the framed `join_slack_1.png`, navy CTA band; primary CTA is now a real `` — dropped the JS `router.push`/`window.open` branch; `handleSignupClick` only fires the `CompleteRegistration` GA event), `/volunteer`, `/about/hackers`, `/about/process` (refined chrome; Mermaid flow + Gantt diagrams kept in calm card frames), `/praise` (refined chrome; `PraiseBoard` feed kept), `/hack/code-of-conduct` (full rewrite: editorial hero, core-values cards, numbered accessibility list, expected-vs-unacceptable two-col w/ +/× markers, navy CTA band), `/profile/[userid]` public (`PublicProfile`), and a full refined pass on the own `/profile` editor (`Profile.js` — editorial masthead, hairline sticky tab strip, scoped navy/terracotta MUI theme, `PanelHeader` on every tab, privacy legend on Basic Info, hairline rules in Volunteer History, `.ohx-card` Giveaway Entries; form logic untouched), `/myfeedback` (full rewrite: editorial hero, navy CircularProgress score ring w/ Fraunces number, hairline skill accordions w/ navy LinearProgress bars, `.ohx-card` entries + `.ohx-tag` chips) and `/feedback/[userid]` (`GiveFeedback`, `ssr:false`: hero + `Section`-framed form, navy-themed MUI Select/Radio/Slider/Checkbox, native `.ohx-btn` submit; also fixed a pre-existing hooks-order bug where `if (!user)` returned before `useEffect`), `/hack/[event_id]/manageteam` (full rewrite: `RefinedRoot`+`RefinedFonts`, editorial masthead, `.ohx-card` gating panels, calm `--surface-2` panels for auth/apply gates; split `error` → `teamsError`+`formError`; lazy Slack/nonprofit fetches gated by `activeStep`/`showNewTeamForm` + `slackFetchedRef`/`nonprofitFetchedRef`; `onTeamUpdated` callback propagated to `TeamStatusPanel` so DevPost/demo label updates without reload; `fetchMyTeams()` after submit + scroll to `#team-hub`; `noindex` meta; no PII logs; `active_days=365` for Slack fetch), and `/hack/[event_id]/team/[team_id]` (full rewrite: editorial masthead + hairline `.ohx-card` sections + a **deep-linkable sticky Table of Contents** — sections are `
` with hover `#` copy-link anchors (keyboard accessible via `&:focus-visible`), the TOC lists only present sections (`aria-current="location"`) and tracks the active one via `IntersectionObserver`; embedded `MentorTeamPanel`/`TeamCompletionChecklist` keep their own styling in anchored `headed={false}` wrappers). **Key invariants on this page:** (1) Status labels come from `TEAM_STATUS_OPTIONS.find()` via `statusLabel()` — never render raw enum strings like `NONPROFIT_SELECTED`; winning statuses get a 🏆 prefix from `getWinningStatus()`; `INACTIVE` skips the status tag since the dot already conveys it. (2) `SectionBlock` is **module-scope** (not inside the component) — defining it inside caused a remount storm on every IntersectionObserver `setActiveId` tick, wiping in-progress mentor note drafts and reloading the demo iframe; it receives `copiedId`/`onCopyLink` as props. (3) `parseLocalDate` from `src/lib/dateUtils.js` is used for all event window checks (`eventHasStarted`, `eventEnded`) so date-only strings aren't parsed as UTC midnight. (4) Membership check uses `useTeamMembership(eventId, teamId)` from `src/hooks/use-team-membership.js` — shared by the page and passed as `isOnTeam`/`membershipChecked` props to `TeamCompletionChecklist` (no duplicate fetch). Page uses result to show: "Manage your team" primary CTA for members, a combined nudge card for missing DevPost/demo, and a ghost "Want to join this team?" affordance for non-members when status is joinable. (5) Nonprofit: never render `selected_nonprofit_id` text — only show when `nonprofitData.name` is available; `getStaticProps` fetches `{ name, description }` into `nonprofitData`; a "Nonprofit partner" section card links to `/nonprofit/`. (6) `awards[]` rendered as `ohx-tag--accent` chips after the status tag. (7) All GitHub repos in `github_links[]` rendered (handles both string and `{link,name}` shapes). (8) `getStaticProps` rethrows network errors (ISR keeps the last good version) and returns `notFound` only on genuine 404. **Harmonized (not full rewrite):** `/hack` index (keeps its bespoke finder + `HackPageNav`; hero/CTAs/Support band recolored via inline `RX` tokens). The upcoming/current event cards were refined too: `HackathonList` full-mode heading → Fraunces eyebrow; `EventFeature` full card rewritten to a refined hairline card (eyebrow date + quiet type tag, Fraunces title, navy donation rings, navy-primary/ghost event-link buttons, "View event →"; fixed the old nested-anchor bug; var-fallback colors since `/hack` isn't a `RefinedRoot`). `ImpactMetrics` (shared with the archive) → warm `--surface-2` tiles with Fraunces navy numbers + "Impact at a glance" overline (was rainbow `color`-prop numbers). `/hack/[event_id]` got a **masthead-led refined pass**: body wrapped in `` (it's now the `
`; all section IDs + `TableOfContents` + `FloatingNavigation` preserved), `HackathonHeader` rebuilt into a scope-independent editorial masthead (inline styles w/ `var(--x, fallback)` so `/agenda` + `/census` also benefit), refined "Build a team" buttons + recap teaser. Phase-2 refined the **event-only** sub-components: `NonprofitList` (calm clickable cards + navy view-toggle), `EventConstraints`, `DonationProgress` (navy rings), `EventCountdown` (navy countdown card replacing the gradient + flat timeline), `TeamList`/`TeamCard` (flat hairline cards, Fraunces team names, refined "Team members" label + navy Join button — `TeamList` is event-page-only despite earlier substring matches in admin/event-teams; **profile avatars load lazily per-card via IntersectionObserver with `rootMargin: "200px"`, tracked by `fetchedTeamProfilesRef` Set** — do NOT restore the eager `fetchTeamMemberProfiles(teams)` call on mount that fired N parallel requests for all 30+ cards at once). **Backend C7 enrichment:** `get_single_hackathon_event` (`services/hackathons_service.py`) now calls `_enrich_teams_users_batch` after converting team DocumentReferences — one `db.get_all` across all members of all teams, deduped, so the frontend receives `users[]` already as `{id, user_id, name, nickname, profile_image}` objects. `HackathonResults.js`/`TeamList.js` handle both the old id-string and new object shapes (backwards-compatible). The list endpoint `get_hackathon_list` is NOT enriched — only the single-event getter., and `HackathonLeaderboard` (flat cards, warm stat tiles w/ terracotta icons + Fraunces navy numbers, squared navy Org button). **Phase 3 (done) finished the event page**: `EventLinks` (6 rainbow app buttons → one calm uniform card set + calm social-proof strip), `InteractiveFAQ` (flat hairline accordions), `HackathonResults` (calm `--surface-2` frame, Fraunces navy stat numbers; gold/silver/bronze winner medals kept), `VolunteerList` (`PersonCard` → flat hairline). The **shared** ones (`InteractiveFAQ`, `HackathonResults`) use **inline CSS-var fallbacks** (`var(--ink,#16181D)`) so they render refined on their non-`RefinedRoot` routes too (verified on `/hack/[event_id]/results` + `OnboardingFAQ`) — no per-route gating. A final chrome pass refined `TableOfContents` (navy active pill, was magenta), `FloatingNavigation` (navy FAB), `VolunteerList` (navy "Learn more" + navy availability chips; in-person stays green), and `MentorAvailability` (navy banner/Slack button). The event page is now fully refined end-to-end. Also refined: `/hack/request` (host-an-event page + `HackathonRequestForm`) and `/office-hours`. **`/hack/request` is student-first** — org-type selectable cards default to **university** (was corporate); the **$5k budget minimum was removed** (slider min `$0`, never blocks submit, reframed as optional funding); the donation-% ask is **corporate-only** (forced to 0 at submit otherwise); phone is optional; rainbow Papers → calm `--surface-2` cards; everything navy/terracotta via a scoped MUI `ThemeProvider` (`createTheme(base, …)`). **`formData` keys are preserved** — `HackathonRequestDetailDialog`, the edit page `/hack/request/[request_id]`, and the backend all depend on them. **Scroll gotcha:** step nav now `scrollIntoView`s a `formTopRef` on the form's Paper (`scrollMarginTop:88`), NOT `window.scrollTo(0,0)` (which overshot to page-top now that the form sits below the hero). `/office-hours` now renders its JSON-LD, uses the idempotent `initFacebookPixel` (was a raw `ReactPixel.init` — CWV rule), and dropped the artificial 1s loading delay + dead ICS code. Also refined: `/nonprofit/[nonprofit_id]` (`NonProfit.js` — editorial masthead, meta row as `.ohx-tag`/`.ohx-link`s, projects list, canonical www fix) and `/project/[project_id]` (`Project.js` — `RefinedRoot` chrome, soft breadcrumb, canonical). Both share `ProblemStatement.js` which was fully restyled (purple gradient hero → calm `--surface-2` band; gradient chips → `.ohx-tag`; gradient metric tiles → Fraunces navy tiles; gradient section headers → hairline `.ohx-card`; gradient CTAs → `.ohx-btn`). **Key invariant:** `ProblemStatement` is ONLY ever inside a `RefinedRoot`, so it may use scoped `.ohx-*` classes directly (no inline `var(--x, fallback)` needed). Pass `headingLevel="h2"` from the nonprofit page (N cards, nonprofit name is the `

`) and `headingLevel="h1"` from the project page (project title is the `

`). The "Want to help?" toggle is hidden when `status === "production"` (live projects don't need volunteers) but stays visible for users already helping so they can toggle off; `maintenance` status keeps the toggle. `styles/nonprofit/styles.js` is now orphaned — leave in place. +Full spec + rollout checklist: **`docs/refined-design-system.md`** (read this before extending the look to more pages). Migrated so far: `/` (`src/pages/index.js`), `/projects` (`src/components/ProjectList/*`), the global NavBar (incl. a refined mobile layout — centered logo + refined dropdown), `/about`, `/about/judges`, `/about/mentors` (`Mentorship` rewritten as refined sections), `/about/success-stories`, `/about/completion`, `/sponsor`, `/nonprofits` (`NonProfitList` + new calm card `NonProfitListTileRefined`; original `NonProfitListTile` stays for event pages), `/blog` (`BlogPage`; reused `News` list kept), `/onboarding` (refined chrome; wizard logic intact; **July 2026 content overhaul** — 9 steps: Welcome, Mission, How It Works [full lifecycle incl. post-hackathon Definition of Done + project-status ladder], Get Involved [hacker/mentor/judge/volunteer/nonprofit role cards], Using the Site [site map + first-steps guide + "public portfolio" band: GitHub/Slack/demo-videos/public-profile are what recruiters can review; work is documented as GitHub Issues for public credit — no issues in a repo → pull the code and write them like a PM], Slack, Introduce Yourself, FAQs, Feedback. New sections: `HowItWorksSection`/`RolesSection`/`WebsiteTourSection` in `src/components/Onboarding/`; `JudgingOverview`/`MentoringOverview`/`BuddySystem` are now orphaned — don't re-add them or their claims. FAQ answers must reflect real flows: there are NO project leads, NO commenting on projects, NO buddy system — joining a project = "Want to help?" toggle / its Slack channel / GitHub repo (write Issues like a PM if none exist) / next hackathon. **Design contract:** step content renders inside a scoped `onboardingTheme` ThemeProvider (in `pages/onboarding/index.js` — Fraunces headings, Hanken body, navy/terracotta palette, flat hairline Papers/Cards; don't reintroduce elevation shadows, hover-lifts, or rainbow chips) and every step opens with the shared `StepHeader` (renders `

`; the page masthead owns the `

`). Slack step leads with the two-account gotcha (an ohack.dev login [usually Google] does NOT create a Slack account — join via `/signup`) and real channel deep links: #introductions C01EY49JV8U, #ask-a-mentor C01E5CGDQ74, #random C06BRHRS5BQ, plus per-project #npo-* channels; never reference #help/#team-formation/#buddy-matching/#project-matching — they don't exist), `/signup` (full rewrite: editorial hero, "What you get" benefit cards, numbered join steps beside the framed `join_slack_1.png`, navy CTA band; primary CTA is now a real `` — dropped the JS `router.push`/`window.open` branch; `handleSignupClick` only fires the `CompleteRegistration` GA event), `/volunteer`, `/about/hackers`, `/about/process` (refined chrome; Mermaid flow + Gantt diagrams kept in calm card frames), `/praise` (refined chrome; `PraiseBoard` feed kept), `/hack/code-of-conduct` (full rewrite: editorial hero, core-values cards, numbered accessibility list, expected-vs-unacceptable two-col w/ +/× markers, navy CTA band), `/profile/[userid]` public (`PublicProfile`), and a full refined pass on the own `/profile` editor (`Profile.js` — editorial masthead, hairline sticky tab strip, scoped navy/terracotta MUI theme, `PanelHeader` on every tab, privacy legend on Basic Info, hairline rules in Volunteer History, `.ohx-card` Giveaway Entries; form logic untouched), `/myfeedback` (full rewrite: editorial hero, navy CircularProgress score ring w/ Fraunces number, hairline skill accordions w/ navy LinearProgress bars, `.ohx-card` entries + `.ohx-tag` chips) and `/feedback/[userid]` (`GiveFeedback`, `ssr:false`: hero + `Section`-framed form, navy-themed MUI Select/Radio/Slider/Checkbox, native `.ohx-btn` submit; also fixed a pre-existing hooks-order bug where `if (!user)` returned before `useEffect`), `/hack/[event_id]/manageteam` (full rewrite: `RefinedRoot`+`RefinedFonts`, editorial masthead, `.ohx-card` gating panels, calm `--surface-2` panels for auth/apply gates; split `error` → `teamsError`+`formError`; lazy Slack/nonprofit fetches gated by `activeStep`/`showNewTeamForm` + `slackFetchedRef`/`nonprofitFetchedRef`; `onTeamUpdated` callback propagated to `TeamStatusPanel` so DevPost/demo label updates without reload; `fetchMyTeams()` after submit + scroll to `#team-hub`; `noindex` meta; no PII logs; `active_days=365` for Slack fetch), and `/hack/[event_id]/team/[team_id]` (full rewrite: editorial masthead + hairline `.ohx-card` sections + a **deep-linkable sticky Table of Contents** — sections are `
` with hover `#` copy-link anchors (keyboard accessible via `&:focus-visible`), the TOC lists only present sections (`aria-current="location"`) and tracks the active one via `IntersectionObserver`; embedded `MentorTeamPanel`/`TeamCompletionChecklist` keep their own styling in anchored `headed={false}` wrappers). **Key invariants on this page:** (1) Status labels come from `TEAM_STATUS_OPTIONS.find()` via `statusLabel()` — never render raw enum strings like `NONPROFIT_SELECTED`; winning statuses get a 🏆 prefix from `getWinningStatus()`; `INACTIVE` skips the status tag since the dot already conveys it. (2) `SectionBlock` is **module-scope** (not inside the component) — defining it inside caused a remount storm on every IntersectionObserver `setActiveId` tick, wiping in-progress mentor note drafts and reloading the demo iframe; it receives `copiedId`/`onCopyLink` as props. (3) `parseLocalDate` from `src/lib/dateUtils.js` is used for all event window checks (`eventHasStarted`, `eventEnded`) so date-only strings aren't parsed as UTC midnight. (4) Membership check uses `useTeamMembership(eventId, teamId)` from `src/hooks/use-team-membership.js` — shared by the page and passed as `isOnTeam`/`membershipChecked` props to `TeamCompletionChecklist` (no duplicate fetch). Page uses result to show: "Manage your team" primary CTA for members, a combined nudge card for missing DevPost/demo, and a ghost "Want to join this team?" affordance for non-members when status is joinable. (5) Nonprofit: never render `selected_nonprofit_id` text — only show when `nonprofitData.name` is available; `getStaticProps` fetches `{ name, description }` into `nonprofitData`; a "Nonprofit partner" section card links to `/nonprofit/`. (6) `awards[]` rendered as `ohx-tag--accent` chips after the status tag. (7) All GitHub repos in `github_links[]` rendered (handles both string and `{link,name}` shapes). (8) `getStaticProps` rethrows network errors (ISR keeps the last good version) and returns `notFound` only on genuine 404. **Harmonized (not full rewrite):** `/hack` index (keeps its bespoke finder + `HackPageNav`; hero/CTAs/Support band recolored via inline `RX` tokens). The upcoming/current event cards were refined too: `HackathonList` full-mode heading → Fraunces eyebrow; `EventFeature` full card rewritten to a refined hairline card (eyebrow date + quiet type tag, Fraunces title, navy donation rings, navy-primary/ghost event-link buttons, "View event →"; fixed the old nested-anchor bug; var-fallback colors since `/hack` isn't a `RefinedRoot`). `ImpactMetrics` (shared with the archive) → warm `--surface-2` tiles with Fraunces navy numbers + "Impact at a glance" overline (was rainbow `color`-prop numbers). `/hack/[event_id]` got a **masthead-led refined pass**: body wrapped in `` (it's now the `
`; all section IDs + `TableOfContents` + `FloatingNavigation` preserved), `HackathonHeader` rebuilt into a scope-independent editorial masthead (inline styles w/ `var(--x, fallback)` so `/agenda` + `/census` also benefit), refined "Build a team" buttons + recap teaser. Phase-2 refined the **event-only** sub-components: `NonprofitList` (calm clickable cards + navy view-toggle), `EventConstraints`, `DonationProgress` (navy rings), `EventCountdown` (navy countdown card replacing the gradient + flat timeline), `TeamList`/`TeamCard` (flat hairline cards, Fraunces team names, refined "Team members" label + navy Join button — `TeamList` is event-page-only despite earlier substring matches in admin/event-teams; **profile avatars load lazily per-card via IntersectionObserver with `rootMargin: "200px"`, tracked by `fetchedTeamProfilesRef` Set** — do NOT restore the eager `fetchTeamMemberProfiles(teams)` call on mount that fired N parallel requests for all 30+ cards at once). **Backend C7 enrichment:** `get_single_hackathon_event` (`services/hackathons_service.py`) now calls `_enrich_teams_users_batch` after converting team DocumentReferences — one `db.get_all` across all members of all teams, deduped, so the frontend receives `users[]` already as `{id, user_id, name, nickname, profile_image}` objects. `HackathonResults.js`/`TeamList.js` handle both the old id-string and new object shapes (backwards-compatible). The list endpoint `get_hackathon_list` is NOT enriched — only the single-event getter., and `HackathonLeaderboard` (flat cards, warm stat tiles w/ terracotta icons + Fraunces navy numbers, squared navy Org button). **Phase 3 (done) finished the event page**: `EventLinks` (6 rainbow app buttons → one calm uniform card set + calm social-proof strip), `InteractiveFAQ` (flat hairline accordions), `HackathonResults` (calm `--surface-2` frame, Fraunces navy stat numbers; gold/silver/bronze winner medals kept), `VolunteerList` (`PersonCard` → flat hairline). The **shared** ones (`InteractiveFAQ`, `HackathonResults`) use **inline CSS-var fallbacks** (`var(--ink,#16181D)`) so they render refined on their non-`RefinedRoot` routes too (verified on `/hack/[event_id]/results` + `OnboardingFAQ`) — no per-route gating. A final chrome pass refined `TableOfContents` (navy active pill, was magenta), `FloatingNavigation` (navy FAB), `VolunteerList` (navy "Learn more" + navy availability chips; in-person stays green), and `MentorAvailability` (navy banner/Slack button). The event page is now fully refined end-to-end. Also refined: `/hack/request` (host-an-event page + `HackathonRequestForm`) and `/office-hours`. **`/hack/request` is student-first** — org-type selectable cards default to **university** (was corporate); the **$5k budget minimum was removed** (slider min `$0`, never blocks submit, reframed as optional funding); the donation-% ask is **corporate-only** (forced to 0 at submit otherwise); phone is optional; rainbow Papers → calm `--surface-2` cards; everything navy/terracotta via a scoped MUI `ThemeProvider` (`createTheme(base, …)`). **`formData` keys are preserved** — `HackathonRequestDetailDialog`, the edit page `/hack/request/[request_id]`, and the backend all depend on them. **Scroll gotcha:** step nav now `scrollIntoView`s a `formTopRef` on the form's Paper (`scrollMarginTop:88`), NOT `window.scrollTo(0,0)` (which overshot to page-top now that the form sits below the hero). `/office-hours` now renders its JSON-LD, uses the idempotent `initFacebookPixel` (was a raw `ReactPixel.init` — CWV rule), and dropped the artificial 1s loading delay + dead ICS code. Also refined: `/nonprofit/[nonprofit_id]` (`NonProfit.js` — editorial masthead, meta row as `.ohx-tag`/`.ohx-link`s, projects list, canonical www fix) and `/project/[project_id]` (`Project.js` — `RefinedRoot` chrome, soft breadcrumb, canonical). Both share `ProblemStatement.js` which was fully restyled (purple gradient hero → calm `--surface-2` band; gradient chips → `.ohx-tag`; gradient metric tiles → Fraunces navy tiles; gradient section headers → hairline `.ohx-card`; gradient CTAs → `.ohx-btn`). **Key invariant:** `ProblemStatement` is ONLY ever inside a `RefinedRoot`, so it may use scoped `.ohx-*` classes directly (no inline `var(--x, fallback)` needed). Pass `headingLevel="h2"` from the nonprofit page (N cards, nonprofit name is the `

`) and `headingLevel="h1"` from the project page (project title is the `

`). The "Want to help?" toggle is hidden when `status === "production"` (live projects don't need volunteers) but stays visible for users already helping so they can toggle off; `maintenance` status keeps the toggle. `styles/nonprofit/styles.js` is now orphaned — leave in place. See `docs/refined-design-system.md`. Pattern for new pages: keep `Head`/`getStaticProps`/schema verbatim, add ``, wrap body in ``, rebuild sections with `.ohx-*` classes, one `

`, preserve GA. @@ -757,7 +757,7 @@ One `volunteer.admin`-gated page (`src/pages/admin/feedback/index.js`) with 3 MU - **CrossEventSurveys** (the "Compare events" sub-view) — `GET /api/surveys/overview` (one server-side scan, aggregates only). KPI band (total responses, latest-event rating + ▲/▼ vs prior event, best/worst) → **calendar macro-trend** (`ComposedChart`: events ordered by date, avg overall_rating [solid] + would_return [dashed] lines on 0–5, volume bars [grey when n<`LOW_N`=5]) → **elapsed-time overlay** (multi-select events, defaults to ~3 most recent with live responses; fetches each selected event's `/responses?mode=live` on demand and overlays `elapsedHourSeries` rating curves on a shared hours-since-kickoff axis) → per-event summary table. - Pure survey helpers: `src/components/admin/feedback/surveyAnalytics.js` (`aggregate`/`formatAnswer` lifted from the panel; `elapsedHourSeries` [date-only start anchored at LOCAL midnight so hour 0 ≈ kickoff for same-tz admins], `eventDurationHours`, `collectFreeText`, `redFlags`, `gist`; re-exports `parseTs`/`extractThemes` from `onboardingAnalytics`). - **PeerFeedbackPanel** (global, all-time) — `GET /api/admin/feedback/peer`. In the `feedback` map, numeric values = 0-100 skill scores, strings = free-text; `role` is metadata. Giver hidden when `is_anonymous`. -- **OnboardingPanel** (global, all-time) — `GET /api/admin/feedback/onboarding`. A trends-+-actions dashboard, not a flat list. A single **window** toggle (90d / 12mo / all) drives everything. Sections: KPI band (responses, avg rating + ▲/▼ vs prior period, **% "clear"** = Very easy + Mostly clear, % willing to follow up) → **Feedback over time** (recharts `ComposedChart`: volume bars [grey when n<3] + avg-rating line, with `ReferenceLine` markers at hackathon start dates; plus a 100%-stacked `stackOffset="expand"` clarity-mix of `easeOfUnderstanding`) → snapshot distributions (rating / ease / useful-topics) → action lists (**follow-up queue** = `contact.willing && email`, copy-emails button; **needs attention** = rating ≤2 or confusing ease) → **keyword themes** from missingTopics+improvements → response accordions (device chip). Pure helpers (bucketing, themes, device, `parseTs`) live in `src/components/admin/feedback/onboardingAnalytics.js`. **Data shape gotcha**: `contactForFollowup` is `{willing}` or `{willing, firstName, email}` (NOT `name`); `easeOfUnderstanding` is a 4-point ordinal (Very easy▸Mostly clear▸Somewhat confusing▸Very difficult); `usefulTopics` is the 5 fixed onboarding topics; `overallRating` 0 = unrated (excluded from averages). The backend strips a `__Timestamp__` export-sentinel prefix off timestamps. +- **OnboardingPanel** (global, all-time) — `GET /api/admin/feedback/onboarding`. A trends-+-actions dashboard, not a flat list. A single **window** toggle (90d / 12mo / all) drives everything. Sections: KPI band (responses, avg rating + ▲/▼ vs prior period, **% "clear"** = Very easy + Mostly clear, % willing to follow up) → **Feedback over time** (recharts `ComposedChart`: volume bars [grey when n<3] + avg-rating line, with `ReferenceLine` markers at hackathon start dates; plus a 100%-stacked `stackOffset="expand"` clarity-mix of `easeOfUnderstanding`) → snapshot distributions (rating / ease / useful-topics) → action lists (**follow-up queue** = `contact.willing && email`, copy-emails button; **needs attention** = rating ≤2 or confusing ease) → **keyword themes** from missingTopics+improvements → response accordions (device chip). Pure helpers (bucketing, themes, device, `parseTs`) live in `src/components/admin/feedback/onboardingAnalytics.js`. **Data shape gotcha**: `contactForFollowup` is `{willing}` or `{willing, firstName, email}` (NOT `name`); `easeOfUnderstanding` is a 4-point ordinal (Very easy▸Mostly clear▸Somewhat confusing▸Very difficult); `usefulTopics` values come from `USEFUL_TOPICS` in `onboardingAnalytics.js` (current onboarding-step names + legacy "Buddy System" kept for pre-July-2026 responses; keep in sync with `FeedbackSection` `topicOptions`); `overallRating` 0 = unrated (excluded from averages). The backend strips a `__Timestamp__` export-sentinel prefix off timestamps. ## Praise Bot admin (`/admin/praise-bot`) diff --git a/src/components/Onboarding/BuddySystem.js b/src/components/Onboarding/BuddySystem.js deleted file mode 100644 index 28e92fc5..00000000 --- a/src/components/Onboarding/BuddySystem.js +++ /dev/null @@ -1,474 +0,0 @@ -import React, { useState } from 'react'; -import { - Box, - Typography, - Paper, - Grid, - Card, - CardContent, - CardActions, - Avatar, - Button, - Chip, - Divider, - TextField, - FormControl, - InputLabel, - Select, - MenuItem, - Alert, - Accordion, - AccordionSummary, - AccordionDetails, - CircularProgress -} from '@mui/material'; -import { styled } from '@mui/material/styles'; -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import PersonIcon from '@mui/icons-material/Person'; -import LinkedInIcon from '@mui/icons-material/LinkedIn'; -import GitHubIcon from '@mui/icons-material/GitHub'; -import TwitterIcon from '@mui/icons-material/Twitter'; -import SearchIcon from '@mui/icons-material/Search'; -import FilterListIcon from '@mui/icons-material/FilterList'; -import { useEnv } from '../../context/env.context'; - -// Styled components -const BuddyCard = styled(Card)(({ theme }) => ({ - height: '100%', - display: 'flex', - flexDirection: 'column', - transition: 'transform 0.3s ease', - cursor: 'pointer', - '&:hover': { - transform: 'translateY(-5px)', - boxShadow: theme.shadows[5] - } -})); - -const BuddyAvatar = styled(Avatar)(({ theme }) => ({ - width: 80, - height: 80, - margin: '0 auto', - border: `3px solid ${theme.palette.primary.main}`, - boxShadow: theme.shadows[3] -})); - -/** - * BuddySystem component - * Matches new members with experienced buddies to provide guidance and support - */ -const BuddySystem = () => { - const { slackSignupUrl } = useEnv(); - const [filters, setFilters] = useState({ - skills: '', - interests: '', - availability: '' - }); - const [searchTerm, setSearchTerm] = useState(''); - const [selectedBuddy, setSelectedBuddy] = useState(null); - const [showFilters, setShowFilters] = useState(false); - const [requestSent, setRequestSent] = useState(false); - const [loading, setLoading] = useState(false); - const [requestMessage, setRequestMessage] = useState(''); - - // Handle filter changes - const handleFilterChange = (event) => { - const { name, value } = event.target; - setFilters({ - ...filters, - [name]: value - }); - }; - - // Handle search input - const handleSearchChange = (event) => { - setSearchTerm(event.target.value); - }; - - // Select a buddy - const handleSelectBuddy = (buddy) => { - setSelectedBuddy(buddy); - setRequestSent(false); - }; - - // Reset selection - const handleBackToList = () => { - setSelectedBuddy(null); - }; - - // Handle sending buddy request - const handleSendRequest = () => { - setLoading(true); - - // Simulate API call delay - setTimeout(() => { - setLoading(false); - setRequestSent(true); - }, 1500); - }; - - return ( - - {/* Header */} - - - Find a Buddy - - - Get paired with an experienced member who can guide you - - - - - {/* Buddy system explanation */} - - - About Our Buddy System - - - Our buddy system pairs new members with experienced volunteers who can answer questions, - provide guidance, and help you navigate our community. Your buddy can: - - - - - - - - Answer questions about our community and projects - - - - - - Help you find projects that match your skills and interests - - - - - - Introduce you to other community members - - - - - - - - - - Provide technical guidance on your first contributions - - - - - - Share resources to help you develop your skills - - - - - - Offer encouragement and support as you get started - - - - - - - - {/* Selected buddy detail view */} - {selectedBuddy ? ( - - - - - {/* Buddy profile */} - - - - {selectedBuddy.name.charAt(0)} - - - {selectedBuddy.name} - - - {selectedBuddy.title} - - - - {selectedBuddy.linkedin && ( - - )} - {selectedBuddy.github && ( - - )} - {selectedBuddy.twitter && ( - - )} - - - - - - - "{selectedBuddy.bio}" - - - - - Availability: - - - - - - {/* Buddy details and request form */} - - - - Skills - - - {selectedBuddy.skills.map((skill, index) => ( - - ))} - - - - - - Interests - - - {selectedBuddy.interests.map((interest, index) => ( - - ))} - - - - - - {/* Request form */} - {requestSent ? ( - - - Your buddy request has been sent! {selectedBuddy.name} will reach out to you soon. - - - ) : ( - <> - - Request {selectedBuddy.name} as Your Buddy - - - Tell {selectedBuddy.name.split(' ')[0]} a bit about yourself and what kind of support you're looking for: - - - setRequestMessage(e.target.value)} - sx={{ mb: 2 }} - /> - - - - )} - - - - ) : ( - <> - {/* Search and filter section */} - - - - Search Buddies} - placeholder="Search by name, skills, or interests..." - value={searchTerm} - onChange={handleSearchChange} - InputProps={{ - startAdornment: , - sx: { fontSize: '1.25rem' } - }} - InputLabelProps={{ sx: { fontSize: '1.4rem', fontWeight: 'bold' } }} - sx={{ - '& .MuiInputBase-input': { fontSize: '1.25rem' }, - '& .MuiInputLabel-root': { fontSize: '1.4rem', fontWeight: 'bold' } - }} - /> - - - - - - - {/* Expandable filters */} - setShowFilters(!showFilters)} - sx={{ mt: 2 }} - > - }> - Filter Options - - - - - - Skills - - - - - - Interests - - - - - - Availability - - - - - - - - - {/* Buddy Finder Section - NEED TO REPLACE PLACEHOLDER WITH JSON OF REAL BUDDIES*/} - - - Available Mentors: Coming Soon! - - - We are actively working on building our mentor network. - Please check back soon for a list of available mentors. - - - - )} - - {/* Additional info */} - - - How the Buddy System Works - - - - - - For New Members - - -
  • Browse and select a buddy based on your interests and needs
  • -
  • Send a request with information about what you're looking for
  • -
  • Your buddy will reach out to you via Slack to introduce themselves
  • -
  • Schedule an initial meeting to discuss your goals and questions
  • -
  • Maintain regular communication for the first month
  • -
    -
    -
    - - - - Buddy Expectations - - -
  • Buddies commit to at least one month of regular support
  • -
  • The relationship is meant to be casual and supportive
  • -
  • No specific time commitment, but responsiveness is expected
  • -
  • Focus is on community integration and basic guidance
  • -
  • For deeper technical mentoring, we have separate programs
  • -
    -
    -
    -
    -
    - - {/* Slack channel info */} - - - You can also find buddies in our - #buddy-matching channel on{' '} -
    - Slack - . Post an introduction and what kind of buddy you're looking for! - - - - ); -}; - -export default BuddySystem; \ No newline at end of file diff --git a/src/components/Onboarding/FeedbackSection.js b/src/components/Onboarding/FeedbackSection.js index 9f9635d5..9a57fd6c 100644 --- a/src/components/Onboarding/FeedbackSection.js +++ b/src/components/Onboarding/FeedbackSection.js @@ -35,6 +35,7 @@ import InsightsIcon from '@mui/icons-material/Insights'; import { trackEvent } from '../../lib/ga'; import axios from 'axios'; import { useAuthInfo } from '@propelauth/react'; +import StepHeader from './StepHeader'; // Styled components const RatingContainer = styled(Box)(({ theme }) => ({ @@ -129,12 +130,15 @@ const FeedbackSection = () => { }; }; - // Available topics for feedback + // Available topics for feedback — mirrors the onboarding steps. + // Keep in sync with USEFUL_TOPICS in admin/feedback/onboardingAnalytics.js. const topicOptions = [ - 'Mission Overview', - 'Introduction Guide', - 'Slack Tutorial', - 'Buddy System', + 'Mission Overview', + 'How It Works', + 'Ways to Get Involved', + 'Website Tour', + 'Slack Tutorial', + 'Introduction Guide', 'FAQ Section' ]; @@ -318,16 +322,10 @@ const FeedbackSection = () => { return ( - {/* Header */} - - - Your Feedback Matters - - - Help us improve the onboarding experience for future members - - - + {/* Why feedback matters */} @@ -697,7 +695,7 @@ const FeedbackSection = () => {
  • Added more detailed Slack channel descriptions based on new member confusion
  • Created step-by-step guides for common technical setup questions
  • -
  • Improved the buddy matching system to connect new members faster
  • +
  • Rebuilt the onboarding around the full project lifecycle, roles, and a website tour
  • Expanded FAQ section with questions frequently asked by new members
  • Added more visual examples and screenshots to improve clarity
  • diff --git a/src/components/Onboarding/HowItWorksSection.js b/src/components/Onboarding/HowItWorksSection.js new file mode 100644 index 00000000..0524591c --- /dev/null +++ b/src/components/Onboarding/HowItWorksSection.js @@ -0,0 +1,218 @@ +import React from 'react'; +import Link from 'next/link'; +import { + Box, + Typography, + Paper, + Grid, + Divider, + Chip, + Button +} from '@mui/material'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; +import TimelineIcon from '@mui/icons-material/Timeline'; +import FactCheckIcon from '@mui/icons-material/FactCheck'; +import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; +import StepHeader from './StepHeader'; + +const NAVY = '#1B3A6B'; + +// Mirrors the six stages on /about/process — keep the two in sync. +const stages = [ + { + title: 'Nonprofits share their challenges', + description: + 'Nonprofits apply with real problems technology could solve — no tech expertise required. Our team reviews each application and works with the nonprofit to clarify scope, goals, and success criteria.' + }, + { + title: 'Problems become projects', + description: + 'Each selected challenge is published as a project on ohack.dev/projects with a description, the skills needed, a GitHub repository — where the work to be done is documented as public GitHub Issues — and its own Slack channel where the work is coordinated.' + }, + { + title: 'The hackathon weekend', + description: + 'Kickoff starts with nonprofit pitches. Hackers form teams around the projects that interest them, then spend the weekend building a working prototype. Mentors circulate in person and on Slack to help teams scope, architect, and get unstuck.' + }, + { + title: 'Demos and judging', + description: + 'Teams demo what they built. Judges review every project, then deliberate to rank the top ones — evaluating scope, documentation, polish, and security. Winners are announced at the closing ceremony.' + }, + { + title: 'Winning teams keep building', + description: + 'This is what makes us different from most hackathons: winning teams are invited to continue for roughly three months after the event, turning their prototype into production software with regular check-ins along the way.' + }, + { + title: 'Completion and handoff', + description: + 'A project is finished when it meets our Definition of Done and the nonprofit signs off. The software goes live, the nonprofit gets documentation and training, and we check in quarterly to keep it healthy.' + } +]; + +// Condensed from the Definition of Done on /about/completion. +const definitionOfDone = [ + 'Deployed to a production environment real users can reach', + 'Nonprofit has signed off that it meets their needs', + 'All code, README, and docs live in the project GitHub repo', + 'Test login details shared securely (never in the repo)', + 'No secrets or sensitive data committed to GitHub', + 'Documentation covers how to use, deploy, and update it', + 'Open-sourced under the MIT license' +]; + +// Matches the status ladder shown on every project page (ProjectProgress). +const projectStatuses = [ + { label: 'Concept', description: 'An idea no one has built yet — needs a team' }, + { label: 'Hackathon', description: 'Worked on at one or more hackathons' }, + { label: 'Post-Hackathon', description: 'Development continuing after the event' }, + { label: 'Production', description: 'Live and being used by the nonprofit' }, + { label: 'Maintenance', description: 'Live, receiving patches and enhancements' } +]; + +/** + * HowItWorksSection + * The end-to-end Opportunity Hack lifecycle: from a nonprofit's problem, + * through the hackathon, to completed software running in production. + */ +const HowItWorksSection = () => { + return ( + + + + + Opportunity Hack is more than a weekend event. Our hackathons kick off projects, but the + goal is always working software a nonprofit actually uses — which means the work continues + after the closing ceremony until the project is deployed, documented, and handed off. + Here's the full arc: + + + {/* Six stages */} + + {stages.map((stage, index) => ( + + + {index + 1} + + + + {stage.title} + + + {stage.description} + + + + ))} + + + {/* Definition of Done */} + + + + + What “done” means here + + + + Every project works toward the same completion checklist, so teams and nonprofits stay + aligned on expectations: + + + {definitionOfDone.map((item, index) => ( + + + + + {item} + + + + ))} + + + + + + + {/* Project status ladder */} + + + + + Project statuses you'll see on the site + + + + Every project page shows where it is in this journey — it's the quickest way to tell + which projects need help and which are already live: + + + {projectStatuses.map((status, index) => ( + + + = 3 ? NAVY : 'transparent', + color: index >= 3 ? '#fff' : 'text.primary', + border: `1px solid ${NAVY}` + }} + /> + + {status.description} + + + {index < projectStatuses.length - 1 && ( + + )} + + ))} + + + + + + + + ); +}; + +export default HowItWorksSection; diff --git a/src/components/Onboarding/IntroductionPrompt.js b/src/components/Onboarding/IntroductionPrompt.js index 7dfe5c8b..ebae2c4a 100644 --- a/src/components/Onboarding/IntroductionPrompt.js +++ b/src/components/Onboarding/IntroductionPrompt.js @@ -21,6 +21,7 @@ import ContentCopyIcon from '@mui/icons-material/ContentCopy'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; import { useAuthInfo } from '@propelauth/react'; import { useEnv } from '../../context/env.context'; +import StepHeader from './StepHeader'; const IntroCard = styled(Card)(({ theme }) => ({ height: '100%', @@ -171,16 +172,10 @@ const IntroductionPrompt = () => { return ( - {/* Header */} - - - Introduce Yourself - - - Share your background and interests with the community - - - + {/* Introduction guidance */} diff --git a/src/components/Onboarding/MissionOverview.js b/src/components/Onboarding/MissionOverview.js index 17c9f3f4..7fd5e916 100644 --- a/src/components/Onboarding/MissionOverview.js +++ b/src/components/Onboarding/MissionOverview.js @@ -11,15 +11,15 @@ import { Chip } from '@mui/material'; import { styled } from '@mui/material/styles'; +import StepHeader from './StepHeader'; const ValueCard = styled(Card)(({ theme }) => ({ height: '100%', display: 'flex', flexDirection: 'column', - transition: 'all 0.3s ease', + transition: 'border-color 0.2s', '&:hover': { - transform: 'translateY(-5px)', - boxShadow: theme.shadows[10] + borderColor: '#1B3A6B' } })); @@ -72,11 +72,8 @@ const MissionOverview = () => { return ( {/* Mission statement */} + - - Our Mission - - To empower nonprofits through sustainable technology solutions by connecting them with passionate volunteer developers. @@ -218,17 +215,17 @@ const MissionOverview = () => { 'Environmental Conservation', 'Mental Health', 'Youth Services', 'Elderly Care', 'Disaster Relief', 'Arts & Culture', 'Human Rights' ].map((cause, index) => ( - ))} diff --git a/src/components/Onboarding/OnboardingDialog.js b/src/components/Onboarding/OnboardingDialog.js index 3d0a6894..8e5c786d 100644 --- a/src/components/Onboarding/OnboardingDialog.js +++ b/src/components/Onboarding/OnboardingDialog.js @@ -152,7 +152,7 @@ const OnboardingDialog = () => {
  • - Finding a buddy to guide you + Ways to get involved — hacker, mentor, judge, or volunteer
  • diff --git a/src/components/Onboarding/OnboardingFAQ.js b/src/components/Onboarding/OnboardingFAQ.js index 93f98e2f..c82e9720 100644 --- a/src/components/Onboarding/OnboardingFAQ.js +++ b/src/components/Onboarding/OnboardingFAQ.js @@ -17,6 +17,7 @@ import LaptopIcon from '@mui/icons-material/Laptop'; import FeedIcon from '@mui/icons-material/Feed'; import { useEnv } from '../../context/env.context'; import InteractiveFAQ from '../Hackathon/InteractiveFAQ'; +import StepHeader from './StepHeader'; /** * OnboardingFAQ component @@ -58,17 +59,21 @@ const OnboardingFAQ = () => {
  • - Find a buddy - Request a buddy through this onboarding flow or in the #buddy-matching channel. + Log in on ohack.dev - Use the Log In button (most people sign in with Google) to get a profile, + submit applications, and track volunteer hours. Note this is separate from your Slack account — you'll + want both; see the signup guide if you're not on our Slack yet.
  • - Explore projects - Browse current projects in the #project-matching channel or on our website. + Join a hackathon - Find the next event at{' '} + ohack.dev/hack and apply as a hacker, mentor, judge, or volunteer right on the event page.
  • - Attend events - Join our next hackathon, workshop, or virtual meetup announced in #events. + Explore projects - Browse all projects at{' '} + ohack.dev/projects — each project page links to its own Slack channel where the work happens.
  • @@ -117,22 +122,18 @@ const OnboardingFAQ = () => {
  • - Virtual events - Attend our regular community calls, workshops, and social events. + Office hours - Drop into our weekly{' '} + office hours to meet organizers and other members live.
  • - Project teams - Join a project team to collaborate closely with other members. + Project teams - Join a project's Slack channel to collaborate closely with other members.
  • - Buddy system - Get paired with an experienced member who can introduce you around. - -
  • -
  • - - In-person hackathons - When available, our in-person events are a great way to network. + In-person hackathons - Our in-person events are the best way to meet the community face-to-face.
  • @@ -144,11 +145,11 @@ const OnboardingFAQ = () => { question: "Is there a code of conduct?", answer: ( - Yes, we have a comprehensive Code of Conduct that all members are required to follow. It emphasizes respect, - inclusivity, and professional behavior. We're committed to providing a harassment-free, welcoming environment - for everyone regardless of gender, sexual orientation, disability, physical appearance, body size, race, or religion. - You can find the full Code of Conduct on our website or in the #rules channel on Slack. If you experience any issues, - please contact a community moderator immediately. + Yes, we have a comprehensive Code of Conduct that all members are required to follow. It emphasizes respect, + inclusivity, and professional behavior. We're committed to providing a harassment-free, welcoming environment + for everyone regardless of gender, sexual orientation, disability, physical appearance, body size, race, or religion. + You can read the full Code of Conduct here. If you experience any issues, + please contact an organizer immediately. ), icon: "📜" @@ -179,27 +180,49 @@ const OnboardingFAQ = () => {
    1. - Browse current projects - Check the #project-matching channel on Slack or the Projects section on our website. + Browse projects - Go to ohack.dev/projects and look for projects + tagged "Needs Help" — those are actively looking for contributors. Projects tagged "Live" are already in + production and generally don't need new volunteers. + +
    2. +
    3. + + Flip the "Want to help?" switch - On the project page, sign in and slide the + "Want to help?" toggle to raise your hand as a helper — it tells us and the community you're on board. + +
    4. +
    5. + + Join the project's Slack channel - Every project page has a "Join #channel" button. + Say hello in the channel and ask where you can help — that's where the people working on it coordinate.
    6. - Express interest - Comment on the project post or message the project lead. + Check the GitHub repo - Every project page has a "Code & Tasks" section with a + Code and an Issues button for each repository. Reading the README and open issues is the fastest way + to find something concrete to pick up.
    7. - Attend an onboarding meeting - Most teams have a process to bring new members up to speed. + No issues yet? Create them - Pull the code down, run it, and think like a product + manager: write GitHub Issues for the work you find. We document all work as Issues because closed + tickets give you public credit for your contributions — and writing good tickets is itself a + contribution.
    8. - Start small - Begin with smaller tasks or issues labeled "good for beginners." + Join it at a hackathon - Most project work happens with a team during an event, so the + biggest on-ramp is applying to the next hackathon at ohack.dev/hack and picking that + project with your team.
    - We encourage teams to be open to new members, but some projects may have specific skill requirements or be in a phase where onboarding is challenging. Don't be discouraged if one project isn't a good fit—there are always new opportunities emerging. + Activity varies between events — if a channel is quiet, don't be discouraged. Ask in #general or come to + office hours and we'll point you at a project that needs what you bring. ), @@ -209,11 +232,11 @@ const OnboardingFAQ = () => { question: "Can I propose a new project idea?", answer: ( - Absolutely! We encourage members to propose new project ideas, especially those addressing nonprofit needs. - To propose a new project, share your idea in the #project-ideas channel on Slack with details about the problem - it addresses, potential solution, and technologies involved. Community members will provide feedback, and if there's - sufficient interest, you can form a team to work on it. If you already have a nonprofit partner in mind, that's great! - If not, our nonprofit outreach team can potentially help connect you with organizations that might benefit from your solution. + Absolutely — with one important note: our projects start from a real nonprofit's need, not from a solution + looking for a user. If you know a nonprofit that could use technical help, point them to{' '} + ohack.dev/nonprofits to submit their challenge — that's how new projects are born. + If you have an idea but no nonprofit partner yet, share it in Slack or bring it to{' '} + office hours and we can help figure out whether there's an organization it would serve. ), icon: "💡" @@ -275,11 +298,11 @@ const OnboardingFAQ = () => { question: "When is your next hackathon?", answer: ( - We typically organize multiple hackathons throughout the year, both virtual and in-person (when possible). - Our flagship events usually occur in the spring and fall. For the most up-to-date information on upcoming - hackathons, check the #events channel on Slack, subscribe to our newsletter, or visit the Events page on our website. - Hackathons are announced at least 4-8 weeks in advance to give participants time to prepare. Between major hackathons, - we also organize smaller coding sprints and workshops that you're welcome to join! + The single source of truth for upcoming events is ohack.dev/hack — every upcoming and past + hackathon is listed there, and each event page has the dates, location, schedule, and application forms. + Our flagship in-person event happens in the fall in Arizona, with other events through the year. New events + are also announced in Slack and on the blog, typically several weeks in advance so you + have time to apply and form a team. ), icon: "📅" @@ -350,12 +373,12 @@ const OnboardingFAQ = () => { question: "How do you handle project hosting and deployment?", answer: ( - We typically host projects on GitHub under the Opportunity Hack organization, which provides version control and collaboration tools. - For deployment, we use a variety of platforms depending on the project's needs. Many web applications are deployed on Heroku, Vercel, - or Netlify, which offer free tiers for nonprofit projects. For more complex applications requiring custom infrastructure, we work with - cloud providers like AWS, GCP, or Azure, often utilizing their nonprofit credits programs. Our DevOps team helps set up deployment - pipelines and monitors application health. We prioritize sustainable, low-maintenance hosting solutions that nonprofits can manage - long-term with minimal technical expertise. + Projects are hosted on GitHub under the Opportunity Hack organization, which provides version control and + collaboration tools — each project page links to its repository. For deployment, teams use cloud platforms + like AWS, fly.io, and Google Cloud depending on the project's needs; Opportunity Hack covers initial hosting + costs (up to $15/month plus up to $250 in one-time costs), and nonprofits can take over as a project grows. + We prioritize sustainable, low-maintenance hosting that nonprofits can manage long-term with minimal + technical expertise. ), icon: "☁️" @@ -378,12 +401,14 @@ const OnboardingFAQ = () => { question: "What happens to projects after hackathons end?", answer: ( - Unlike traditional hackathons, we emphasize long-term project development and sustainability. After a hackathon ends, - projects typically enter our "continued development" phase. Teams often continue working on their solutions, refining - features, addressing bugs, and ensuring the project meets the nonprofit's needs. We have a dedicated project maintenance - team that helps ensure projects remain active and supported. For projects that have reached a stable state, we provide - documentation and training to nonprofits on how to use and maintain their solutions. Some projects may evolve over multiple - hackathons, with new teams building upon previous work. Our goal is to create lasting impact, not just weekend prototypes. + Unlike traditional hackathons, the event is the beginning, not the end. Winning teams are invited to keep + building for roughly three months after the event, turning their prototype into production software with + regular check-ins along the way. A project counts as finished when it meets our{' '} + Definition of Done: deployed to production, signed off by the nonprofit, + documented, secured, and open-sourced. After that, the project shows as "Live" on the website, we check in + with the nonprofit quarterly, and maintenance needs are picked up by volunteers from the community. Some + projects also evolve over multiple hackathons, with new teams building on previous work. Our goal is lasting + impact, not weekend prototypes. ), icon: "🔄" @@ -396,10 +421,12 @@ const OnboardingFAQ = () => { Absolutely! We encourage members to showcase their Opportunity Hack contributions on resumes, portfolios, and LinkedIn profiles. These projects demonstrate real-world experience, teamwork, and a commitment to social impact—qualities that many employers value. - When listing your contributions, include specific technical skills utilized, your role in the team, and the impact of the project on - the nonprofit. We can provide verification of your participation upon request, and many of our corporate partners recognize Opportunity - Hack experience favorably during hiring processes. Additionally, your GitHub contributions through our organization are public and can - serve as concrete examples of your coding abilities. + When listing your contributions, include specific technical skills utilized, your role in the team, and the impact of the project on + the nonprofit. Because all our work happens in the open, a recruiter can review your entire portfolio: your GitHub commits and the + issue tickets you wrote and closed, your communication in public Slack channels, your team's demo videos, and your public + ohack.dev profile page. Volunteers can also request a signed verification letter directly through the website (find the letters link + on the event page you participated in), and many of our corporate partners recognize Opportunity Hack experience favorably during + hiring processes. ), icon: "📄" @@ -414,37 +441,32 @@ const OnboardingFAQ = () => {
    • - Become a project lead - Coordinate a team working on a nonprofit solution + Mentor at a hackathon - Guide teams through scoping, architecture, and getting unstuck. Apply on any event page.
    • - Volunteer as a mentor - Help guide newer members and share your expertise + Judge an event - Evaluate final projects and help decide the winners. Apply on any event page.
    • - Join the organizing team - Help plan events and community initiatives + Help run events - Volunteer for check-in, logistics, photography, and more — no coding required.
    • - Lead workshops - Share your knowledge through educational sessions + Help with nonprofit outreach - Connect us with organizations that could use free software.
    • - Become a buddy - Help onboard and support new community members - -
    • -
    • - - Help with outreach - Connect with nonprofits and identify potential projects + Sponsor or recruit - Introduce your company to sponsorship opportunities.
    - Express your interest in taking on these roles in the #community-involvement channel on Slack, or reach out to one of our community managers. + Reach out in Slack or come to office hours and tell us what you'd like to take on. ), @@ -479,16 +501,10 @@ const OnboardingFAQ = () => { return ( - {/* Header */} - - - Frequently Asked Questions - - - Find answers to common questions about getting involved - - - + {/* FAQ List */} diff --git a/src/components/Onboarding/RolesSection.js b/src/components/Onboarding/RolesSection.js new file mode 100644 index 00000000..17b09b16 --- /dev/null +++ b/src/components/Onboarding/RolesSection.js @@ -0,0 +1,206 @@ +import React from 'react'; +import Link from 'next/link'; +import { + Box, + Typography, + Paper, + Grid, + Divider, + Chip, + Button, + Alert +} from '@mui/material'; +import ArrowForwardIcon from '@mui/icons-material/ArrowForward'; +import StepHeader from './StepHeader'; + +const NAVY = '#1B3A6B'; + +const roles = [ + { + icon: '💻', + title: 'Hackers', + tagline: 'Build the software', + what: + 'Join a team at a hackathon and build a working prototype for a nonprofit over the weekend. Developers, designers, product folks, and data people are all hackers here — every skill level is welcome, and teams balance experience so beginners can learn from seasoned engineers.', + time: 'The hackathon weekend, plus an optional ~3 months if your team wins and continues to production.', + apply: 'Apply on any upcoming event page — no experience screening, everyone can participate.', + link: { href: '/about/hackers', label: 'More about being a hacker' } + }, + { + icon: '🧭', + title: 'Mentors', + tagline: 'Guide the teams', + what: + 'Experienced engineers, designers, and domain experts who circulate during the event helping teams scope realistically, make architecture decisions, get unstuck, and prepare for judging. Mentors guide — they don\'t write the team\'s code.', + time: 'Flexible blocks of a few hours during the event, in person or remote via Slack.', + apply: 'Apply on the event page. Mentor applications are reviewed by our team — expect a decision within about a week.', + link: { href: '/about/mentors', label: 'More about mentoring' } + }, + { + icon: '⚖️', + title: 'Judges', + tagline: 'Evaluate the projects', + what: + 'Industry professionals who review team demos and decide the winners in two stages: first reviewing every submitted project, then deliberating to rank the top ones. Judges score scope, documentation, polish, and security.', + time: 'Mostly the final day of the event — demos plus deliberation.', + apply: 'Apply on the event page. Judge applications are reviewed by our team — expect a decision within about a week.', + link: { href: '/about/judges', label: 'More about judging' } + }, + { + icon: '🙌', + title: 'Volunteers', + tagline: 'Make the event run', + what: + 'Everything that keeps a hackathon running smoothly: check-in and registration, room logistics, meals, photography, social media, and cheering teams on. No coding required.', + time: 'Shifts during the event — as much or as little as you can give.', + apply: 'Apply on the event page as a volunteer.', + link: { href: '/volunteer', label: 'More ways to volunteer' } + }, + { + icon: '🤝', + title: 'Nonprofits', + tagline: 'Bring the problem', + what: + 'Organizations with a real challenge technology could solve. Nonprofits pitch their problem at kickoff, stay reachable during the weekend to answer team questions, and sign off when the finished software meets their needs. The software is free.', + time: 'An application beforehand, a short pitch at kickoff, and availability for questions.', + apply: 'Submit your challenge through our nonprofit application any time of year.', + link: { href: '/nonprofits', label: 'More for nonprofits' } + } +]; + +const judgingCriteria = ['Scope', 'Documentation', 'Polish', 'Security']; + +/** + * RolesSection + * The five groups that make every Opportunity Hack happen, what each one + * does, the time commitment, and how to sign up. + */ +const RolesSection = () => { + return ( + + + + + You don't have to be a developer to contribute. Every event depends on hackers, + mentors, judges, event volunteers, and nonprofit partners — and many community members + take on different roles at different events. + + + {/* Role cards */} + + {roles.map((role) => ( + + + + + {role.icon} + + + + {role.title} + + + {role.tagline} + + + + + {role.what} + + + Time commitment: {role.time} + + + How to join: {role.apply} + + {role.title === 'Judges' && ( + + {judgingCriteria.map((criterion) => ( + + ))} + + )} + + {role.link.label} → + + + + ))} + + + + All applications live on the event pages — pick an upcoming hackathon at{' '} + ohack.dev/hack and you'll find + the hacker, mentor, judge, and volunteer application forms right on the event's page. + + + {/* Videos */} + + Hear it firsthand + + + + + What mentoring looks like + + +