Activity Types
Every lesson ends with one or more activities. The quiz is always last. Additional practice activities unlock sequentially — students must complete each before the next unlocks.
Quiz
The primary assessed activity. Students must score ≥80% to pass.
- Multiple choice, randomized question draw from a pool
- Immediate wrong-answer feedback via Miles AI
- Retake lockout: 10 minutes between attempts
- Route:
/activity/:activityId?context=lesson-native&lessonId=LESSON_SLUG
Quiz activities are stored in the lesson’s activities JSONB array. They use the unified route via the lesson-native context like all other lesson-native activities.
Activity Format Types
The type column on the activities table is the canonical format identifier used for routing, filtering, and documentation.
Unified Route
All activity types use a single unified route, except the Toughest Questions Drill which has its own dedicated route with no activity ID:
/activity/:activityId?context=CONTEXT&lessonId=LESSON_SLUGFor the Toughest Questions Drill: /drill/toughest (no activity ID — data is loaded dynamically from the student’s quiz history)
Context values:
| Context | When to use | Data source |
|---|---|---|
recommended | Personalized weak-topic questions from the study API. Used for flashcard and matching. | /api/study/recommended-activity/:id |
checkpoint | Questions missed on a unit checkpoint. | /api/study/missed-from-checkpoint/:id |
missed-quiz | All questions the student has answered incorrectly across all quiz attempts for a lesson, deduplicated. No progress tracking. | /api/study/missed-from-quiz/:lessonSlug |
lesson-native | Full lesson data — tracks progress, shows completion state. Used for classifier, true_false, and quiz. | Lesson row + lessons.quiz_questions |
toughest-drill | Set automatically by the /drill/toughest pathname — no ?context= param needed. | /api/users/me/toughest-questions/drill |
Note: The checkpoint context is partially implemented. The
checkpointcontext value is handled by ActivityRenderer, but entry-point navigation from CheckpointResult currently targets routes that are not registered. Do not rely on checkpoint retry navigation until this is completed.
Routing per type:
| Type | Context | Example URL |
|---|---|---|
flashcard | recommended | /activity/1?context=recommended |
matching | recommended | /activity/2?context=recommended |
classifier | lesson-native | /activity/6?context=lesson-native&lessonId=1-5 |
true_false | lesson-native | /activity/8?context=lesson-native&lessonId=1-5 |
| Any type | missed-quiz | /study/missed-quiz?lessonId=1-5&type=flashcard |
Backward compatibility: The old /lesson/:lessonId/activity/:activityId route (ActivityPage) remains registered for backward compatibility. Existing bookmarks still work, but all new navigation uses the unified ActivityRenderer route.
Practice mode: Append &practice=true to force a fresh mount and ignore prior completion state. Used when launching from the quiz fail screen.
flashcard
Flip-card term and definition review. Students flip cards to reveal definitions.
- Context:
recommended - Content: API-driven via
questionsJSONB. No hardcoded content in the component. - Best for: vocabulary, definitions, regulatory codes
- Minimum recommended items: 8
matching
Students match terms to their definitions by pairing cards.
- Context:
recommended - Content: API-driven via
questionsJSONB. No hardcoded content in the component. - Best for: paired concepts (term ↔ definition, code ↔ meaning)
- Minimum recommended items: 6 pairs
classifier
Students read a scenario and select the correct outcome from labeled choices. Use when the learning objective is distinguishing between two or more regulatory outcomes (e.g., 24-hour OOS vs. CDL disqualification vs. no violation).
- Context:
lesson-native - Content:
itemsJSONB (array of scenario objects) +choicesJSONB (array of answer options) - Components:
ConsequenceClassifier,TestingClockActivity - Minimum recommended items: 5
true_false
Students read a statement and classify it as true or false. Use when the learning objective is correcting common misconceptions or testing knowledge of specific regulatory facts.
- Context:
lesson-native - Content:
itemsJSONB (array of statement objects) +choicesJSONB (true/false options) - Components:
ClearinghouseMythBusters - Minimum recommended items: 5
Engine Compartment Diagram
Interactive labeled diagram of a truck engine compartment. Students identify and label components.
- Context:
lesson-native - Component:
EngineCompartmentActivity(src/app/components/EngineCompartmentActivity.jsx) - Has start screen: Yes
- Best for: Pre-trip inspection preparation
Learner Profile
Onboarding form that collects CDL class goal, prior experience, and study preferences. Data is stored to the student’s profile.
- Context:
lesson-native - Component:
LearnerProfileActivity(src/app/components/LearnerProfileActivity.jsx) - Has start screen: Yes
- Best for: Lesson 1-1 onboarding — runs once
missed-quiz
Practice all quiz questions you have ever answered incorrectly in a lesson.
- Route:
/study/missed-quiz?lessonId=SLUG&type=flashcard|matching - Renderer: Flashcard or Matching (controlled by
?type=param) - Data source:
GET /api/study/missed-from-quiz/:lessonSlug - Aggregation: All quiz attempts, deduplicated by question text (newest occurrence preserved)
- Progress: None — unlimited practice, not assessed
- Entry point: Dashboard “Questions I’ve Missed” zone
Classification
Students assign each item to the correct category from a fixed set of choices. One item at a time, immediate feedback with explanation.
- Component:
MajorOffensesActivity(src/app/components/lesson/interactive/MajorOffensesActivity.jsx) - Best for: penalty tiers, regulatory categories, class/endorsement assignments
- Example: lesson 1-4 Major Offense Classification Challenge
Case File Review
Items are presented as redacted documents. Students unredact fields one at a time and watch the outcome update in real time.
- Component:
CaseFileReveal(src/app/components/lesson/interactive/CaseFileReveal.jsx) - Best for: scenario-based regulatory analysis, investigation simulations
Cost Calculator
Interactive calculator where students input variables (salary, violation type) and see computed financial impact.
- Component:
DisqualificationCostCalculator(src/app/components/lesson/interactive/DisqualificationCostCalculator.jsx) - Best for: financial impact illustration, real-world consequence modeling
Drill (Toughest Questions)
Personalized quiz of questions the student has personally missed 2+ times, ranked by miss rate.
- Route:
/drill/toughest - Automatically populated from quiz attempt history — no authoring required
Adding a New Activity Format
- Insert a row into
activitieswithtype,component,items,choices,topics,lesson_slug. - Register the React component in
src/app/components/lesson/interactive/index.js. - If introducing a new activity type: update
resolveRenderer()inActivityRenderer.jsx. If introducing a new interactive component (subtype ofinteractive): register it ininteractiveRegistry(src/app/components/lesson/interactive/index.js) — no changes toresolveRenderer()needed. - Update this page.
Sequential Unlock Rules
- Activity 1 always unlocks immediately
- Completing Activity N unlocks Activity N+1
- Quiz is always the final activity
- Interactive activities (non-quiz): completion is triggered explicitly by the component — either via a “Complete Activity” button or upon reaching the final step. Completion behavior is defined per component, not automatic.
- Quiz: requires ≥80% pass score to complete