Skip to content

feat(learning): roadmap player side panel#61

Merged
OthmaneZ05 merged 3 commits into
mainfrom
feat/learning-roadmap-player
Jul 15, 2026
Merged

feat(learning): roadmap player side panel#61
OthmaneZ05 merged 3 commits into
mainfrom
feat/learning-roadmap-player

Conversation

@OthmaneZ05

@OthmaneZ05 OthmaneZ05 commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary of Changes

Adds the roadmap player: a sidebar next to the canvas that opens a roadmap, lists its steps with the current one highlighted, shows the instruction, and validates the current step against the real containers via POST /api/learning/validate. This closes the full loop: frontend → learning API → Docker → frontend, and gives new users a guided path instead of an empty canvas.

Deliberately deferred to follow-ups: rich ✓/✗ result presentation (this PR renders the raw validation report), a hints UI, and progression persistence (player state is in-memory).

How it's built:

  • New frontend/src/features/learning/useRoadmaps (catalogue) + useLearningPlayer (open/navigate/validate; per-step results kept when navigating; ref-based guard so double-clicking Validate sends one request; latest-open-wins guard) and 4 components (LearningPanel, RoadmapCatalog, RoadmapPlayer, StepValidationResults).
  • Minimal CanvasPage surface: one showLearning state, one topbar button, one conditional sibling in the body row. Closed panel = nothing mounted, no learning endpoint touched (tested).
  • Error handling per the learning API contract (docs/learning-api.md): non-2xx is a request/server problem — shown inline with retry, never as a learner failure.
  • Backend preamble (first commit):
    • POST /validate without a JSON body now returns 400 instead of 500.
    • Roadmap selection key is now (id, language): GET /roadmaps/:id?language= (exact match or 404), deterministic sorted pick without the param, /validate stays language-neutral. Documented in docs/learning-api.md.
  • i18n (en/fr), styling on the shared CSS variables.

Also fixes the example roadmap's first hint (the node library is on the right side of the canvas, not the left).

Types of Changes

  • New feature / node type addition
  • Bug fix (non-breaking change resolving an issue)
  • Refactoring / structural cleanup
  • Documentation update

Verification & Testing

Automated Checks

  • Run npm run lint successfully with no errors
  • Run npm run build successfully with no compilation errors
  • Run npm test successfully (all tests pass)

Backend: 211 passing (new: 400-not-500 on non-JSON body, language forwarding, deterministic/exact/missing-translation selection, fr fixture). Frontend: 195 passing (new: 22 — both hooks, LearningPanel behavior incl. network-down + retry, CanvasPage mount/unmount with zero learning fetches while closed).

Manual Verification

Verified against real Docker with curl: step 1 of the example roadmap fails on an empty project, passes after creating and starting the web node; ?language=de → 404; garbage body → 400; the disposable test project was deleted with no container residue.

Checklist

  • My code follows the repository's code style and lint standards
  • I have updated the documentation or instructions if necessary
  • All unit and integration tests are passing

…ge) roadmap selection

Two API fixes ahead of the roadmap player (its first real consumer):

- POST /api/learning/validate without a JSON Content-Type left req.body
  undefined, so the destructuring threw and the endpoint answered 500;
  it now falls through the existing per-field checks and returns 400.
- Translations share a roadmap id and differ by language, so
  getRoadmap(id) returned whichever file readdir listed first and the
  selection was nondeterministic. The key is now (id, language):
  GET /roadmaps/:id accepts ?language= (exact match or 404), the
  language-less form picks deterministically (sorted), and /validate
  stays language-neutral since steps and validators are shared across
  translations. Contract documented in docs/learning-api.md.

Also fixes the example roadmap's first hint: the node library sits on
the right side of the canvas, not the left.
New frontend/src/features/learning: a sidebar that turns the empty
canvas into a guided session. It lists the roadmap catalogue
(GET /api/learning/roadmaps), opens a roadmap by (id, language), shows
every step with the current one highlighted, and a Validate button that
runs POST /api/learning/validate against the real containers and
renders the per-validator report (raw rendering for now — P-2 owns the
rich pass/fail/error presentation).

- useLearningPlayer holds the play-through state: clamped navigation,
  per-step results kept in memory when navigating (persistence is P-4),
  a ref-based guard so double-clicking Validate sends one request.
- Network failures render inline with a retry action; per the learning
  API contract, non-2xx is a request/server problem and is never shown
  as a learner failure.
- Minimal CanvasPage surface: one showLearning state, one topbar
  button, one conditional sibling in the body row. Closed panel means
  nothing is mounted and no learning endpoint is touched (tested).
- Labels in en/fr via i18next; styling on the shared CSS variables.
- Render RoadmapCatalog at a single JSX position so a failed roadmap
  load no longer remounts it (which re-fired its catalogue fetch).
- Lift readErrorMessage to shared/utils and reuse it from
  useContainers and useLearningPlayer instead of duplicating it.
- Guard openRoadmap against out-of-order resolutions (latest request
  wins) and against a malformed 200 body (steps must be a non-empty
  array) so a bad payload surfaces as a load error, not a render crash.
- estimatedMinutes uses a null check: a value of 0 rendered a stray
  "0" text node in the catalogue card.
- Pluralize the step-count label (steps_one/steps_other) so a
  single-step roadmap doesn't read "1 steps".
- Name the requested language in the 404 when a roadmap id exists but
  the translation doesn't, instead of claiming the id is unknown.
- Derive Previous/Next bounds once (atFirstStep/atLastStep).
@OthmaneZ05 OthmaneZ05 requested a review from Derssa as a code owner July 15, 2026 06:30
@OthmaneZ05 OthmaneZ05 merged commit fc1b6ca into main Jul 15, 2026
3 checks passed
@OthmaneZ05 OthmaneZ05 deleted the feat/learning-roadmap-player branch July 15, 2026 15:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants