diff --git a/src/components/OnboardingGate.tsx b/src/components/OnboardingGate.tsx index 2939e02c..821d86fd 100644 --- a/src/components/OnboardingGate.tsx +++ b/src/components/OnboardingGate.tsx @@ -9,6 +9,7 @@ import { useTranslation } from 'react-i18next'; import { ArrowLeft, ArrowRight, + BadgeCheck, Bitcoin, Download, Eye, @@ -129,18 +130,22 @@ function CaptiveOverlay() { } }, [step, user, cancel, goTo]); - // Role pick is the final step. Picking a role both records the choice - // (used by the role-pick CTA labels) and navigates to the matching - // surface: creator → campaign-creation form, donor → full campaign grid - // (`/campaigns`, not `/`, so they land on the browse-everything view - // rather than the curated home with its own marketing hero). No separate - // outro / celebration screen. + // Role pick is the final step for creator/donor. Picking a role both + // records the choice (used by the role-pick CTA labels) and navigates to + // the matching surface: creator → campaign-creation form, donor → full + // campaign grid (`/campaigns`, not `/`, so they land on the + // browse-everything view rather than the curated home with its own + // marketing hero). The verifier role does not navigate away — it branches + // into the captive verifier sub-flow (wired up in a later step); for now + // it routes to the public /organizations onboarding tool. const handleRolePick = useCallback( - (next: 'creator' | 'donor') => { + (next: 'creator' | 'donor' | 'verifier') => { setContextRole(next); cancel(); if (next === 'creator') { navigate('/campaigns/new'); + } else if (next === 'verifier') { + navigate('/organizations'); } else { navigate('/campaigns'); } @@ -273,7 +278,7 @@ function CaptiveOverlay() { interface RoleStepProps { role: OnboardingRole; - onPick: (role: 'creator' | 'donor') => void; + onPick: (role: 'creator' | 'donor' | 'verifier') => void; } /** @@ -310,6 +315,14 @@ function RoleStep({ role, onPick }: RoleStepProps) { selected={role === 'donor'} onClick={() => onPick('donor')} /> + } + title={t('onboarding.role.verifier.title')} + description={t('onboarding.role.verifier.description')} + finderNote={t('onboarding.role.verifier.finderNote')} + selected={role === 'verifier'} + onClick={() => onPick('verifier')} + /> diff --git a/src/contexts/OnboardingProvider.tsx b/src/contexts/OnboardingProvider.tsx index c6060aa1..207dfcc7 100644 --- a/src/contexts/OnboardingProvider.tsx +++ b/src/contexts/OnboardingProvider.tsx @@ -32,7 +32,7 @@ export function OnboardingProvider({ children }: { children: ReactNode }) { // animation. We re-seed on the next startSignup(). }, []); - const setRole = useCallback((next: 'creator' | 'donor') => { + const setRole = useCallback((next: 'creator' | 'donor' | 'verifier') => { setRoleState(next); }, []); diff --git a/src/contexts/onboardingContextDef.ts b/src/contexts/onboardingContextDef.ts index 23d7c942..9c0e2d46 100644 --- a/src/contexts/onboardingContextDef.ts +++ b/src/contexts/onboardingContextDef.ts @@ -1,13 +1,17 @@ import { createContext, useContext } from 'react'; /** - * The two top-level roles a new user can pick during onboarding. Drives - * downstream copy (creator vs. donor framing) and the role-pick CTA target - * (creator → /campaigns/new, donor → /campaigns). + * The top-level roles a new user can pick during onboarding. Drives + * downstream copy (creator vs. donor vs. verifier framing) and the + * role-pick behavior: + * - `creator` → navigate to /campaigns/new + * - `donor` → navigate to /campaigns + * - `verifier`→ stay captive and branch into the verifier sub-flow + * (org identity → org bio → publish statement → how-to-verify) * * `null` before the user has answered the role-picker step. */ -export type OnboardingRole = 'creator' | 'donor' | null; +export type OnboardingRole = 'creator' | 'donor' | 'verifier' | null; /** Options to pre-seed when invoking the captive flow from a specific CTA. */ export interface StartSignupOptions { @@ -15,7 +19,7 @@ export interface StartSignupOptions { * Pre-fill the role picker. CTAs that semantically already imply a role * (e.g. "Start a campaign") can skip the role step by passing this. */ - role?: 'creator' | 'donor'; + role?: 'creator' | 'donor' | 'verifier'; } export interface OnboardingContextValue { @@ -29,7 +33,7 @@ export interface OnboardingContextValue { * finishes or explicitly bails out. */ cancel: () => void; /** Update the selected role from inside the flow (role-picker step). */ - setRole: (role: 'creator' | 'donor') => void; + setRole: (role: 'creator' | 'donor' | 'verifier') => void; } export const OnboardingContext = createContext(undefined); diff --git a/src/locales/en.json b/src/locales/en.json index bb289f4a..0201c716 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -104,6 +104,11 @@ "title": "Give to campaigns", "description": "Support causes with Bitcoin.", "finderNote": "Your donation goes straight to the organizer's wallet." + }, + "verifier": { + "title": "Verify campaigns", + "description": "Vouch for campaigns you've checked out, as an organization.", + "finderNote": "Donors see your badge on campaigns you trust." } }, "keygen": {