Lays the groundwork for the post-signup welcome wizard (Phase 2, Task 38). Authed users hitting /welcome are routed to the next incomplete step based on users.onboarding_step_completed + users.onboarding_dismissed; refresh resumes correctly because every navigation persists state server-side first. Backend: - Expose onboarding_step_completed (Optional[int]) and onboarding_dismissed (bool) on UserResponse so /auth/me drives client-side routing without a separate fetch. Frontend: - WelcomeRouter handles the /welcome decision table (dismissed → /, completed >=3 → /, else next step). - WelcomeStep1 renders the "Your shop" form (company name pre-filled from accounts.name, team size 1-2/3-5/6-10/11-25/26+, role Owner/Lead Tech/Tech/Other). Continue PATCHes /users/me/onboarding-step with action=complete; Skip-this-step PATCHes action=skip; Skip-the-rest POSTs /users/me/onboarding-dismiss-rest. Each action refreshes the auth store before navigating so the router resumes correctly on the next visit. - onboardingApi.updateStep + dismissRest (typed against backend OnboardingStepRequest/Response schemas). - Routes mounted inside AppLayout so EmailVerificationBanner persists above each step per spec. - 11 vitest cases covering the routing decision table + Continue / Skip / Skip-the-rest / persist-failure paths. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
32 lines
1.2 KiB
TypeScript
32 lines
1.2 KiB
TypeScript
import { Navigate } from 'react-router-dom'
|
|
import { useAuthStore } from '@/store/authStore'
|
|
import { PageLoader } from '@/components/common/PageLoader'
|
|
|
|
/**
|
|
* `/welcome` index — redirect to the next incomplete step (or `/` if done /
|
|
* dismissed). Decision table:
|
|
*
|
|
* onboarding_dismissed === true → /
|
|
* onboarding_step_completed >= 3 → /
|
|
* onboarding_step_completed === null/0 → /welcome/step-1
|
|
* onboarding_step_completed === 1 → /welcome/step-2
|
|
* onboarding_step_completed === 2 → /welcome/step-3
|
|
*/
|
|
export function WelcomeRouter() {
|
|
const user = useAuthStore((s) => s.user)
|
|
|
|
// Auth gate sits above us — but if the user object is still loading, render
|
|
// the page loader rather than racing past the redirect.
|
|
if (!user) return <PageLoader />
|
|
|
|
if (user.onboarding_dismissed) return <Navigate to="/" replace />
|
|
|
|
const completed = user.onboarding_step_completed ?? 0
|
|
if (completed >= 3) return <Navigate to="/" replace />
|
|
if (completed === 2) return <Navigate to="/welcome/step-3" replace />
|
|
if (completed === 1) return <Navigate to="/welcome/step-2" replace />
|
|
return <Navigate to="/welcome/step-1" replace />
|
|
}
|
|
|
|
export default WelcomeRouter
|