Skip to content

[Feature]: Adjust phase knowledge checks to review feedback #263

Description

@DavidLeuter

User Story

As a new team member going through onboarding,
I want a knowledge check to feel rewarding when I pass it, and questions I got wrong to be re-asked in a dedicated place instead of inside the next phase's check,
so that every check only asks about the phase I am actually in, and I still get re-tested on everything I missed before I count as onboarded.

As a PM,
I want to see and edit a member's knowledge checks in the team member view,
so that I can track whether they understood a phase and tailor the questions to the project.

Context & Motivation

Follow-up to #128. Three points came out of the review of the knowledge-check feature:

  1. Passing a check gives almost no feedback. The result is a small static banner. Passing should feel like an achievement, not like a form submit.
  2. The PM view is blind to knowledge checks. In Team Management → member detail, a PM sees the onboarding path and can edit steps, but no check status, no results, and no way to edit the questions — even though the backend endpoints for this already exist and are unused.
  3. Carry-over of wrong answers is thematically wrong. Questions answered incorrectly are currently appended to the next phase's check, where they do not belong to the topic being tested. On top of that, a question missed in the last phase is silently dropped and never re-asked, which defeats the original "you get tested on everything in the end" goal.

For point 3 the fix is to decouple the repeats from the phases: collect them in a per-user review pool that can be answered at any time via a "Test your knowledge" entry point, and make an empty pool a second condition for finishing onboarding (next to passing the final phase check). Care is needed around onboarding detection, since the sidebar hides the onboarding tab as soon as the user counts as onboarded.

Acceptance Criteria

Positive feedback on passing

  • Passing a knowledge check shows a celebratory result (animated banner + confetti), not just a static "done" state
  • After submitting, the view scrolls back to the result so it is visible even when the user was at the last question
  • Failing a check still scrolls to the result and clearly shows score and required percentage
  • All animations respect prefers-reduced-motion

Review pool instead of next-phase carry-over

  • A phase check only contains that phase's own questions
  • Questions answered incorrectly land in a per-user review pool, independent of any phase
  • Questions missed in the path's final phase are collected as well (currently dropped)
  • A "Test your knowledge" entry point in the onboarding view opens the pool, badged with the number of open questions, and is only shown when something is open
  • Answering correctly removes a question from the pool permanently; answering wrong keeps it open
  • Answering only part of the pool is possible (no all-or-nothing submit)
  • A user counts as onboarded only when the final phase check is passed and the pool is empty
  • Completing the pool as the last open item finishes onboarding and triggers the completion celebration
  • Onboarding detection / sidebar visibility still behaves correctly with the new gate

PM view

  • The phase overview in the member detail view shows per phase whether a check exists and whether it was passed (icon + label, not color alone)
  • A PM can open a member's submitted attempts for a phase, incl. score and which questions were answered wrong
  • A PM can create and edit the check questions of a phase (multiple choice + short text, options, correct answers, explanation)
  • Validation errors (e.g. MC with fewer than 2 options) are surfaced before saving
  • The PM view shows the member's open review questions, so it is understandable why someone is not onboarded yet

General

  • Backend: ./gradlew clean check green, tests for the new behavior added
  • Frontend: npm run lint, npm run build, npm run test green

Technical Notes

Backend (sprintstart-backend, module onboarding)

  • Core logic in PhaseCheckService; the pool entity PhaseCheckReviewItem already exists from [Story]: Knowledge checks at onboarding milestones #128 and only needs to be decoupled from the phase.
  • New endpoints:
    • GET /api/v1/onboarding/me/review-check — the user's open pool (no correct answers)
    • POST /api/v1/onboarding/me/review-check/attempts — grade answers; no pass threshold, partial submissions allowed
    • GET /api/v1/onboarding/users/{userId}/review-check — PM/HR/Admin view of a member's pool
  • Onboarding completion (userApi.markOnboardingCompleted) has to be evaluated from both submit paths — passing the final check and clearing the last pool item — otherwise a user can get stuck.
  • Gotcha: a question must enter the pool at most once ever. The attempt history counts a question as "once wrong" forever, so deduplicating only against open items resurrects an already cleared question when the phase check is retaken — with the new gate that blocks onboarding permanently.
  • Gotcha: PhaseCheckReviewItem.targetPhaseId becomes meaningless but cannot simply be dropped. The schema is managed by Hibernate ddl-auto: update (no Flyway at runtime), which never drops columns nor relaxes NOT NULL — removing the field works on a fresh schema and breaks inserts on every existing one. Keep it (write sourcePhaseId into it) or add a real migration.
  • No AI-service changes needed; short-text grading via /grade-answers stays as is.

Frontend (sprintstart-frontend)

  • The PM-facing endpoints already existed unused in onboardingService.ts: fetchPhaseCheckForEditing, savePhaseCheck, fetchPhaseCheckAttempts. getUserOnboardingPath already returns phases[].checkSummary — the member view just never rendered it.
  • PUT /phases/{phaseId}/checks is implicitly per-user: onboarding paths are per-user instances, so a phase belongs to exactly one member. No extra plumbing needed for "edit this member's check".
  • Attempt answers only carry questionId; join the question text from fetchPhaseCheckForEditing. Questions replaced since an attempt are no longer resolvable — handle that case.
  • Completion must come from a backend flag (onboardingCompleted), not from deriving "was this the last phase?" in the frontend, since an open pool keeps the journey running past the final check.
  • Sidebar visibility hangs off hasCompletedOnboarding via auth/accessPolicy.ts (isOnboardingAccessible); the profile is deliberately not refreshed immediately so the celebration can play before the onboarding UI is gated away.
  • Question rendering should be shared between the phase check and the review check instead of duplicating the question card.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestteam:backendBackend team (sprintstart-backend, Kotlin/Spring Boot)team:frontendFrontend team (sprintstart-frontend, React)

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions