Skip to content

Allow users to choose the problem count when starting an exam #302

@brianlan

Description

@brianlan

Summary

Allow users to choose how many problems are selected when starting an exam. Clicking Start New Exam should open a configuration modal with synchronized numeric and slider controls, defaulting to 5 problems and constrained to 1–20.

Background / Context

The Exams page currently creates an exam immediately with a hard-coded request:

{ "maxProblemCount": 10 }

The existing create-exam API and frontend CreateExamRequest already support maxProblemCount. The frontend also has a shared Modal component that should be reused. Backend request validation currently permits values from 1 through 100, which does not match the new product limit.

Problem

Users cannot choose the size of a new exam. Every exam started from the Exams page requests 10 problems, and the API permits values above the intended maximum of 20.

Goal / Expected Behavior

  • Clicking Start New Exam opens a modal instead of immediately sending a create request.
  • The modal contains a numeric input and range slider representing the same problem count.
  • Both controls default to 5 and remain synchronized.
  • Valid values are whole numbers from 1 through 20, inclusive.
  • Empty, decimal, or out-of-range numeric input displays concise validation feedback and disables exam creation.
  • Confirming a valid value sends it as maxProblemCount.
  • Existing loading, navigation, query invalidation, and active-exam conflict behavior remains intact.
  • The backend rejects maxProblemCount values outside 1–20.

Scope

This issue should cover:

  • Add the problem-count configuration modal to the Exams page.
  • Add synchronized numeric and range inputs.
  • Add client-side whole-number and range validation.
  • Submit the selected count through the existing create-exam request.
  • Change backend create-exam request validation to 1–20.
  • Add focused frontend and backend automated tests.

Out of Scope

This issue should not cover:

  • Persisting the previously selected count between modal openings or sessions.
  • Changing problem-selection scoring or eligibility.
  • Changing behavior when fewer eligible problems exist than requested.
  • Redesigning exam history or the active exam experience.
  • Introducing a reusable slider/input abstraction or broader form framework.

Chosen Implementation Approach

Reuse frontend/src/components/Modal.tsx for a local configuration dialog in ExamsPage. Keep the typed numeric value in a form state that can represent temporary invalid input, derive the validated integer separately, and disable submission until it is a whole number within 1–20. Keep the slider bound to valid numeric values and synchronize both controls when either changes.

Reset the selector to 5 each time the modal is newly opened. Provide explicit Cancel and Create Exam actions. During creation, disable submission and retain the existing Creating... feedback. If the API returns ACTIVE_EXAM_EXISTS, close the configuration modal before showing the existing continuation prompt.

Update the Pydantic boundary on CreateExamRequest.maxProblemCount from 1–100 to 1–20 so all API callers follow the same product rule.

Implementation Plan

The implementor should:

  1. Add modal visibility and problem-count form state to ExamsPage.
  2. Change Start New Exam to reset the value to 5 and open the modal without issuing a POST request.
  3. Render labeled numeric and range inputs with min=1, max=20, and step=1; synchronize them and expose accessible validation feedback.
  4. Add Cancel and Create Exam actions, disabling creation for invalid input or a pending mutation.
  5. Submit the validated value through the existing { maxProblemCount } request and preserve existing success behavior.
  6. Close the modal before showing the existing active-exam conflict prompt.
  7. Change backend request validation to reject values below 1 or above 20.
  8. Add the required frontend interaction tests and backend API boundary tests.

Relevant Files / Areas

Likely relevant areas:

  • frontend/src/pages/ExamsPage.tsx
  • frontend/src/pages/ExamsPage.test.tsx
  • frontend/src/components/Modal.tsx (reuse only; modification is not expected)
  • backend/app/presentation/exam_serialization.py
  • backend/tests/api/test_exams.py

Tests Required

The implementor must add or update automated tests covering:

  • Clicking Start New Exam opens the modal without sending a POST request.
  • Numeric and slider controls initially show 5 and expose limits 1–20.
  • Changing either control updates the other.
  • Empty, decimal, zero, negative, and above-20 typed values show validation and disable creation.
  • Re-entering a valid whole number clears validation and enables creation.
  • Confirming a valid value sends the corresponding maxProblemCount.
  • Cancel closes the modal without creating an exam.
  • Reopening the modal resets the value to 5.
  • Pending creation prevents duplicate submission and displays loading feedback.
  • ACTIVE_EXAM_EXISTS closes the configuration modal and shows the existing continuation prompt.
  • Backend validation accepts boundary values 1 and 20.
  • Backend validation rejects 0 and 21.

At minimum, tests should verify:

  • The valid creation happy path.
  • Both directions of input synchronization.
  • Invalid typed-input behavior.
  • API boundary enforcement.
  • Existing active-exam conflict behavior.

If automated tests are genuinely not feasible, the implementor must explain why in the PR and provide stronger manual verification steps.

Manual Verification / Self-Check

Before claiming this issue is done, the implementor must:

  1. Open the Exams page and verify the modal, default value, synchronized controls, validation, Cancel action, and successful creation.
  2. Verify keyboard entry and slider operation at both boundaries.
  3. Verify that an existing active exam still produces the continuation prompt.
  4. Run the relevant automated tests and frontend build.
  5. Record the exact commands and results in the PR description, along with changes made, tests added, manual verification, known limitations, and follow-up work.

Suggested verification commands:

cd frontend
npm test -- --run src/pages/ExamsPage.test.tsx
npm run build

cd backend
uv run pytest tests/api/test_exams.py

Reviewer Acceptance Checklist

The reviewer should verify that:

  • Start New Exam opens a modal and does not create an exam immediately.
  • Both inputs default to 5, stay synchronized, and enforce whole numbers from 1 through 20.
  • Invalid typed values are clearly indicated and cannot be submitted.
  • The selected valid value is sent as maxProblemCount.
  • Backend validation consistently enforces the 1–20 contract.
  • Loading, navigation, query invalidation, and active-exam conflict behavior have not regressed.
  • The change reuses the existing modal and does not introduce unnecessary abstractions.
  • Required frontend and backend tests were added or updated.
  • The PR description contains exact automated and manual verification results.
  • No unrelated refactoring or cleanup is bundled into the change.

Dependencies

None.

Follow-Up Work

None currently identified.

Definition of Done

This issue is done when:

  • Users can configure and create an exam containing a requested maximum of 1–20 problems through synchronized controls.
  • Invalid values cannot be submitted and receive accessible feedback.
  • The backend rejects requests outside the same range.
  • Existing exam creation and active-exam behavior remains functional.
  • Required automated tests pass.
  • The PR documents exact test commands, results, and manual verification.

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions