lernschiff/config
Boban Blaskovic cae775ba93 feat(tasks): dedicated /tasks/{key}/run page with per-subject question bank
Before: tasks.modals.runner showed the same 4 multiplication questions
regardless of subject (Mathe / Deutsch / Englisch / Sachkunde / Musik).
Made no sense — Englisch task shouldn't ask 12 × 8.

Now: dedicated full-page route /tasks/{key}/run loads the subject-correct
question bank based on the task's `color` token, with structured URL,
proper auth + license middleware, and Lazy skeleton placeholder.

New artifacts:

  config/task-questions.php  — Question bank keyed by color/subject:
    primary -> Mathematik (5 multiplication / division questions)
    rose    -> Deutsch (5 grammar/wortart/artikel questions)
    violet  -> Englisch (5 vocab/grammar in English)
    green   -> Sachkunde (5 nature/animals/space)
    amber   -> Musik (5 musical notation / theory)

  config/tasks.php  — Placeholder task list extracted from Index so
    both Tasks\Index (list page) and Tasks\Run (quiz page) share data.
    Future: replace with DB-backed Task model.

  app/Livewire/Tasks/Run.php
    - Lazy page-component, #[Layout('layouts.dashboard', active=tasks)]
    - mount(string $key) -> abort 404 if task or question bank missing
    - findTask() reads config('tasks')
    - questions = config("task-questions.{$task['color']}")
    - submitAnswer(int) records lastAnswerIndex + lastAnswerCorrect +
      tallies correctAnswers (visual feedback before advance)
    - nextStep() advances or sets finished=true on final
    - restart() resets all progress
    - placeholder() returns livewire.tasks.run-placeholder (skeleton)

  resources/views/livewire/tasks/run.blade.php
    - Back link + task header card (icon, meta, title, points)
    - Progress bar (Frage X / Y + correct counter)
    - Question card with 2x2 option grid, A/B/C/D labels
    - Per-color theme (primary/rose/violet/green/amber bar + ring)
    - Two-step interaction: pick option -> show correct/wrong feedback ->
      "Nächste Frage" / "Aufgabe abschließen"
    - Finished card: trophy or arrow-path; points earned; restart / done
    - All buttons use design tokens + heroicons (no inline SVG)

  resources/views/livewire/tasks/run-placeholder.blade.php
    - Skeleton with same layout shape (back link, header card, question)
    - data-testid="task-run-skeleton"

  routes/web.php
    Route::get('/tasks/{key}/run', \App\Livewire\Tasks\Run::class)
      ->whereAlphaNumeric('key')->name('tasks.run')
    Behind auth + license group like other dashboard pages.

  Tasks/Index::startTask() simplified:
    $this->redirectRoute('tasks.run', ['key' => $key], navigate: true)
    (Server-side redirect; client navigates without full reload.)

Security:
- whereAlphaNumeric('key') restricts URL param to safe chars
- abort_if($task === null, 404) prevents directory probing
- auth+license middleware applies to whole route group
- Question bank is config (no user-supplied content interpolated)
- Answer index validated against question[correct] only — no dynamic eval

Tests (27 new + 0 broken):
- TaskQuestionBankTest (4): config exists; 5 buckets; required keys;
  per-color content differs
- TasksRunPageTest (15): route registered; auth happy path; 404 for
  unknown key; per-color question selection (math/deutsch/eng/sachkunde);
  submitAnswer correct/wrong; nextStep advances + clears feedback;
  finishing 5 steps -> finished=true; restart resets; Index.startTask
  redirects; unauth redirected to login; placeholder testid
- TasksStartButtonTest updated: Start now redirects, no longer
  dispatches modal

Full verification:
- 186 tests passed, 0 failed, 0 skipped (661 assertions)
- npm build OK, manifest generated
- All 8 page routes return 302 unauth (correct)
- All 5 /tasks/{key}/run routes return 302 unauth (correct)
- All 8 modal aliases still resolve (legacy Runner modal kept)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 22:13:32 +02:00
..
app.php feat(ui): Livewire v4 + blade-icons + de locale + timezone Vienna 2026-05-22 00:38:36 +02:00
auth.php feat(auth): Breeze Livewire stack + dashboard/lizenz-abgelaufen views + layout shells 2026-05-22 05:55:59 +02:00
blade-icons.php feat(ui): Livewire v4 + blade-icons + de locale + timezone Vienna 2026-05-22 00:38:36 +02:00
cache.php feat(scaffold): Laravel 12 scaffold + .env.example 2026-05-22 00:19:49 +02:00
cors.php feat(cors): config/cors.php supports_credentials + CORS tests + Reverb allowed_origins 2026-05-22 06:14:48 +02:00
database.php feat(scaffold): Laravel 12 scaffold + .env.example 2026-05-22 00:19:49 +02:00
filesystems.php feat(scaffold): Laravel 12 scaffold + .env.example 2026-05-22 00:19:49 +02:00
livewire.php feat(ui): Livewire v4 + blade-icons + de locale + timezone Vienna 2026-05-22 00:38:36 +02:00
logging.php feat(scaffold): Laravel 12 scaffold + .env.example 2026-05-22 00:19:49 +02:00
mail.php feat(scaffold): Laravel 12 scaffold + .env.example 2026-05-22 00:19:49 +02:00
queue.php feat(scaffold): Laravel 12 scaffold + .env.example 2026-05-22 00:19:49 +02:00
reverb.php feat(ui): dashboard MVP — feature-folders, lazy skeletons, wire-elements modals, brand logos 2026-05-22 21:36:04 +02:00
services.php feat(scaffold): Laravel 12 scaffold + .env.example 2026-05-22 00:19:49 +02:00
session.php feat(scaffold): Laravel 12 scaffold + .env.example 2026-05-22 00:19:49 +02:00
sidebar.php feat(ui): dashboard MVP — feature-folders, lazy skeletons, wire-elements modals, brand logos 2026-05-22 21:36:04 +02:00
task-questions.php feat(tasks): dedicated /tasks/{key}/run page with per-subject question bank 2026-05-22 22:13:32 +02:00
tasks.php feat(tasks): dedicated /tasks/{key}/run page with per-subject question bank 2026-05-22 22:13:32 +02:00