AI Implementation feature(909): Challenges Page and Challenge Solve Modal 1.05 (#51)

This commit was merged in pull request #51.
This commit is contained in:
2026-07-23 04:06:49 +00:00
parent 5a6e832cca
commit 49d0930780
8 changed files with 116 additions and 131 deletions
+5 -4
View File
@@ -3,7 +3,7 @@ type: guide
title: Challenges Board
description: How a signed-in player navigates the `/challenges` page, opens a challenge modal, submits a flag, and watches live solve updates.
tags: [guide, challenges, board, flag, score, sse, tester]
timestamp: 2026-07-23T03:09:00Z
timestamp: 2026-07-23T04:05:00Z
---
# When this view is available
@@ -57,11 +57,11 @@ category.
|----------------------------------------|---------------------------------------------|-----------------------------------------------------------------------------------------------------------------|
| Category column | `[data-testid="category-column-CR"]` (or any abbreviation) | Header shows the category `icon`, uppercase `abbreviation`, and full `name`; below it is a vertical list of cards. |
| Category column with no challenges | Same | The header renders but the body shows `No challenges yet.` |
| Challenge card | `[data-testid="challenge-card-<uuid>"]` | Click anywhere on the card to open the modal. |
| Challenge card | `[data-testid="challenge-card-<uuid>"]` | Rendered as a real `<button type="button">`. Keyboard activatable (Tab + Enter/Space), exposes `aria-pressed` and an accessible name `Challenge <name>, solved` / `Challenge <name>, not solved`, and `data-solved="true"\|"false"`. Click anywhere (or press Enter/Space) to open the modal. |
| Difficulty pill | `.diff.diff-LOW` / `.diff-MEDIUM` / `.diff-HIGH` | Green / yellow / red pill matching the challenge difficulty. |
| Live points | `[data-testid="points-<uuid>"]` | `livePoints` from the board payload; updates live as `solve` frames arrive. |
| Solve count | text `N solve` / `N solves` | Number of distinct players who have solved this challenge. |
| Solved-by-me check | `.check` (the `` glyph) | Appears on the card once the player has solved it; the card border also flips to the success color. |
| Solved-by-me check | `[data-testid="challenge-check-<uuid>"]` (the `` glyph, `aria-hidden="true"`) | Appears on the card once the player has solved it; the card border also flips to the success color. The button's `aria-pressed` flips to `true` and the accessible name gains the trailing `, solved`. |
Columns are sorted alphabetically by `abbreviation`; cards within a
column are sorted `LOW → MEDIUM → HIGH` then by name (lowercased).
@@ -196,7 +196,8 @@ destruction of the page the SSE source is closed via
| `frontend/src/app/features/challenges/challenges.service.ts` | HTTP service: `getBoard`, `getDetail`, `getEventState`, `submit` (returns `ApiErrorEnvelope`). |
| `frontend/src/app/features/challenges/challenges.pure.ts` | Pure types (`BoardCard`, `SolverRow`, `SolveEventPayload`, etc.) + helpers (`sortColumnsByAbbrev`, `formatDdHhMm`, `parseSolveEvent`, `messageForSolveError`). |
| `frontend/src/app/features/challenges/category-column.component.ts` | Renders one category column (header + cards). |
| `frontend/src/app/features/challenges/challenge-card.component.ts` | Renders one challenge card on the board. |
| `frontend/src/app/features/challenges/challenge-card.component.ts` | Renders one challenge card on the board as a semantic `<button>` with `aria-pressed`, `aria-label`, `data-testid="challenge-card-<id>"`, `data-solved`, and an inner `data-testid="challenge-check-<id>"` for the solved glyph. |
| `tests/frontend/challenge-card-accessibility.spec.ts` | Keyboard activation (Enter/Space), `aria-pressed` toggling, accessible-name composition, and the `data-solved`/`challenge-check-*` selectors. |
| `frontend/src/app/features/challenges/challenge-modal.component.ts` | Renders the challenge detail modal, flag form, solvers list, and live-solve updates. |
| `frontend/src/app/core/services/event-status.store.ts` | Anchor-based clock + 4-state event store; reused here for the gate logic. |
| `frontend/src/app/core/services/authenticated-event-source.service.ts` | Fetch-based SSE transport used to open `/api/v1/events`. |