B2C Study Plan Freemium Gate¶
The Study/Lesson Plan sidebar entry is the marquee Pro-gated artifact for B2C users. This doc owns the entry point and the freemium gate around it: free users click it and hit PaywallModal; Pro users click it and land on /study-plan. The entry label is persona-aware ("Study Plan" for learner/parent, "Lesson Plan" for trainer) via artifactKeyFor(signup_intent). The Study Plan page itself is documented in b2c-personalized-learner-journey.md.
How it works¶
The behavior is a 2x2 of persona (label) × plan (action). Plan gates the action; persona only chooses the label.
Study/Lesson Plan sidebar entry (nav item with planUpsell: true, B2C only)
├── persona (signup_intent) → LABEL only, via artifactKeyFor()
│ learner → "Study Plan" parent → "Study Plan" trainer → "Lesson Plan"
└── plan → ACTION
free → lock icon, click → PaywallModal (upgrade)
pro → normal NavLink → /study-plan
Invariants:
- Plan gates the action; persona never does. A free trainer and a free learner both hit the paywall; only the label differs.
- The Study Plan page re-derives the same label independently via
artifactKeyFor(user.signup_intent), so the page title matches the nav entry. A null/unknown persona falls back to "Study Plan". /study-planis reachable only by B2C users (RoleGuard b2c_user). Free users are steered to the paywall at the nav before they can navigate there; a free user who deep-links still hits the role guard, and plan-level gating is primarily enforced at the nav.
DashboardLayout computes the Pro condition for the planUpsell entry: for Pro it rewrites the entry href to ROUTES.studyPlan and renders a plain NavLink; for free it keeps the lock rendering and wires onPlanUpsellClick to setPaywallOpen(true).
Reference¶
| Route | Component | Purpose |
|---|---|---|
/study-plan (ROUTES.studyPlan) |
StudyPlanPage |
Pro-only Study/Lesson Plan (the gated artifact) |
| i18n key | Value |
|---|---|
b2c.pricing.artifact.studyPlan |
"Study Plan" (learner/parent label + page title) |
b2c.pricing.artifact.lessonPlan |
"Lesson Plan" (trainer label + page title) |
Where it lives¶
apps/web/src/pages/b2c/StudyPlan/index.tsx:StudyPlanPage, title viat(artifactKeyFor(signup_intent)).apps/web/src/pages/b2c/constants.ts:artifactKeyFor+ARTIFACT_KEY_BY_INTENT.apps/web/src/components/layouts/DashboardLayout.tsx: Pro condition,hrefrewrite,onPlanUpsellClickfor free.apps/web/src/components/layouts/dashboard/NavItem.tsx:NavLinkfor Pro, lock + paywall button for free.apps/web/src/constants/navigation.ts:planUpsellnav item;apps/web/src/constants/routes.tshasstudyPlan: '/study-plan'.apps/web/src/routes/ProtectedRoutes/StudentRoutes.tsx: route underRoleGuard b2c_user.apps/web/src/pages/b2c/components/PaywallModal.tsx: the upgrade prompt free users see.
Related: b2c-two-tier-pricing.md (the Free/Pro model), b2c-persona-vocabulary.md (the learner/trainer/parent → Study/Lesson Plan naming).