Commit Graph

738 Commits

Author SHA1 Message Date
Benjamin Admin 22f08a232d Pin solver-service to linux/amd64 (no ARM wheels on PyPI)
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 28s
CI / test-go-edu-search (push) Successful in 29s
CI / test-python-klausur (push) Failing after 3m16s
CI / test-python-agent-core (push) Successful in 18s
CI / test-nodejs-website (push) Successful in 25s
timefold-solver only publishes x86_64 wheels — pip resolves to no
candidate on linux/arm64. Switch the compose entry to linux/amd64
so the image builds via QEMU/Rosetta on the Mac Mini. The cloud
target for this service is x86_64 anyway.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 00:22:34 +02:00
Benjamin Admin 1f2f304724 Use default-jdk-headless on python:slim instead of temurin:alpine
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 36s
CI / test-go-edu-search (push) Successful in 29s
CI / test-python-klausur (push) Failing after 2m34s
CI / test-python-agent-core (push) Successful in 17s
CI / test-nodejs-website (push) Successful in 23s
ARM64 has no eclipse-temurin:17-jdk-alpine manifest. Switch to the
Debian-based python:3.11-slim and install OpenJDK via apt so the
image builds on both Mac Mini (ARM) and x86_64 hosts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 00:19:03 +02:00
Benjamin Admin 53cfe9238f Apply gofmt to timetable_solution_migrations.go
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 38s
CI / test-go-edu-search (push) Successful in 30s
CI / test-python-klausur (push) Failing after 2m32s
CI / test-python-agent-core (push) Successful in 21s
CI / test-nodejs-website (push) Successful in 28s
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 00:18:00 +02:00
Benjamin Admin f042f2896b Phase 5: Timefold timetable-solver-service + solution persistence
school-service additions:
  - tt_solution + tt_lesson migration. tt_lesson carries three UNIQUEs
    (solution+class, solution+teacher, solution+room per slot) so the
    DB itself rejects any double-booking the solver might emit by
    mistake.
  - Solution CRUD + GET solutions/:id/lessons endpoint with joined
    class/subject/teacher/room names for display.
  - POST /timetable/solutions creates the row then fires off the
    solver-service via HTTP (5s timeout, mark failed if unreachable).
  - SOLVER_SERVICE_URL config wired through main.go/handlers.

New service timetable-solver-service:
  - Python 3.11 + FastAPI + Timefold Solver 1.21 (Apache-2.0). Dockerfile
    bundles OpenJDK 17 since Timefold for Python is a JPype bridge.
  - app/domain.py — Timefold @planning_entity Lesson with timeslot+room
    as PlanningVariables; @planning_solution Timetable holds problem
    facts (rooms/teachers/etc.) AND rule-fact collections.
  - app/rules.py — frozen dataclasses mirroring 6 of the 15 tt_
    constraint_* tables initially.
  - app/constraints.py — ConstraintProvider with 3 universal hard
    constraints (no double-booking) + 5 DB-driven constraints
    (teacher_unavailable_day/window, teacher_excluded_room,
    room_unavailable, room_requires_type) + 1 quality soft constraint
    (subject_preferred_period). Remaining 9 constraint types ready to
    plug in via the same join pattern.
  - app/repository.py — async loaders for stammdaten + rules; builds
    one Lesson per (curriculum row × weekly_hours), skipping rows
    without a tt_assignment teacher.
  - app/runner.py — runs solver in ThreadPoolExecutor so the FastAPI
    event loop stays responsive. Updates tt_solution status
    pending→running→completed|infeasible|failed.
  - app/main.py — POST /api/v1/solve (202 Accepted, background task),
    GET /api/v1/jobs/{id}, /health. School-service polls tt_solution
    directly instead of GET /jobs for the typical case.
  - docker-compose.yml adds the service on port 8095, depending on
    core-health-check.

Tests:
  - school-service: validator test for CreateTimetableSolutionRequest
    (allows empty name).
  - solver-service: tests/test_domain.py + tests/test_rules.py cover
    construction + hashability of the planning facts. Full solve flow
    deferred to Phase 8 integration with seed data.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 00:16:52 +02:00
Benjamin Admin 082a5bb68c Strip orphan straight-quote pairings in JSX descriptions
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 30s
CI / test-go-edu-search (push) Successful in 31s
CI / test-python-klausur (push) Failing after 2m35s
CI / test-python-agent-core (push) Successful in 20s
CI / test-nodejs-website (push) Successful in 21s
The German „X" markers in the description prop combined a curly „
(U+201E) with a straight " (U+0022). The straight quote prematurely
terminated the JavaScript string inside the JSX expression. Removing
both markers around the example text keeps the description readable
and unambiguously valid JSX.

Test selector for the UnavailableWindow description updated to match
the new wording.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 23:52:09 +02:00
Benjamin Admin a315db0388 Fix JSX attribute syntax in constraint editor descriptions
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 28s
CI / test-go-edu-search (push) Successful in 33s
CI / test-python-klausur (push) Failing after 2m32s
CI / test-python-agent-core (push) Successful in 19s
CI / test-nodejs-website (push) Successful in 24s
German curly quotes („…") combined with a closing straight " inside
JSX attribute values were terminating the attribute prematurely, e.g.
`description="Beispiel: „X" (jugendgerecht)."` lost everything after
the inner straight quote. Switch all such descriptions to the JSX
expression form `description={"…"}` so the inner quotes are part of
a JavaScript string literal and parsed correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 23:41:24 +02:00
Benjamin Admin 7c96d89927 Stundenplan Phase 3d: all 15 constraint editors via shared shell
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 30s
CI / test-go-edu-search (push) Successful in 29s
CI / test-python-klausur (push) Failing after 2m38s
CI / test-python-agent-core (push) Successful in 18s
CI / test-nodejs-website (push) Successful in 20s
Backend was already complete in Phase 2; this finishes the UI.

  - regeln/_shell.tsx introduces useConstraintCrud (handles list/create/
    delete state + reload), ConstraintShell (header, prereq banner,
    form toggle, error display, empty/loading/table render), and
    useShellStyles for the recurring theme tokens. Each editor now
    only carries its schema-specific bits.
  - Existing 4 editors (TeacherUnavailableDay/Window, SubjectMax
    Consecutive/PreferredPeriod) refactored onto the shell — every
    Playwright selector preserved.
  - 11 new editors covering the remaining constraint tables:
      TeacherMaxHours{Day,Week}, TeacherExcluded{Subject,Room},
      Subject{MinDayGap,ContiguousWhenRepeated,DoubleLesson},
      Class{MaxHoursDay,NoGaps},
      Room{RequiresType,Unavailable}.
  - RegelnHub now references all 15 editors directly — no more 'soon'
    placeholders. The two duplicate 'Max. Stunden / Tag' entries
    (teacher + class) are intentional and disambiguated by group.

Tests:
  - e2e/stundenplan.spec.ts: mock routes added for all 11 new constraint
    endpoints. RegelnHub suite gains a single test that switches
    through 13 uniquely-labelled editors, plus a dedicated test for
    the two duplicate 'Max. Stunden / Tag' labels.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 23:27:34 +02:00
Benjamin Admin c2c09e1cd9 Stundenplan Phase 3c: complete Stammdaten + RegelnHub with 4 editors
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 31s
CI / test-go-edu-search (push) Successful in 30s
CI / test-python-klausur (push) Failing after 3m31s
CI / test-python-agent-core (push) Successful in 18s
CI / test-nodejs-website (push) Successful in 22s
Frontend additions in studio-v2:
  - PeriodsManager renders the weekly grid as a Mo–So table with one
    row per period_index. New entries auto-increment period_index so
    the user can hit Anlegen repeatedly for a full day's slots.
  - CurriculumManager joins classes + subjects; new entries refuse to
    open when either prerequisite list is empty (banner instead).
  - AssignmentsManager joins teacher × class × subject with the same
    prerequisite-banner pattern.
  - regeln/RegelnHub: vertical sidebar grouping all 15 constraint
    types by parent entity (Lehrer/Fach/Klasse/Raum). Implemented
    editors are clickable, the other 11 are visibly disabled with
    a 'soon' tag.
  - Three new editors:
      TeacherUnavailableWindowEditor (time-window pattern),
      SubjectMaxConsecutiveEditor (number-input pattern),
      SubjectPreferredPeriodEditor (number range pattern).
  - page.tsx wires every tab to its manager; the not-implemented
    placeholder is gone (no more empty tabs).

Test coverage:
  - e2e/stundenplan.spec.ts rewritten: 23 tests across 7 suites,
    covering all 8 tabs, the new managers' prerequisite banners,
    sub-tab switching in the RegelnHub, and the disabled state of
    not-yet-implemented constraint rules. Each test mocks the
    backend via page.route() so the suite stays hermetic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 23:08:15 +02:00
Benjamin Admin 4657589b89 Fix Playwright selector ambiguities in stundenplan.spec.ts
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 29s
CI / test-go-edu-search (push) Successful in 31s
CI / test-python-klausur (push) Failing after 2m36s
CI / test-python-agent-core (push) Successful in 20s
CI / test-nodejs-website (push) Successful in 22s
- 'Lehrer' label collided with a Sidebar entry; scope to <main nav>.
- 'Schmidt, Anna' lived in a closed <option>; assert on count via
  select.locator() instead of toBeVisible().

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 22:53:10 +02:00
Benjamin Admin 73636f76a2 Stundenplan Phase 3b: 3 more Stammdaten managers, first constraint editor, full test coverage
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 30s
CI / test-go-edu-search (push) Successful in 30s
CI / test-python-klausur (push) Failing after 3m6s
CI / test-python-agent-core (push) Successful in 20s
CI / test-nodejs-website (push) Successful in 21s
Frontend additions in studio-v2:
  - LehrerManager / FaecherManager / RaeumeManager — same CRUD pattern as
    Klassen, with entity-specific form fields and table columns.
  - regeln/TeacherUnavailableDayEditor — first constraint editor, joins
    against teachersApi to render a readable name in the dropdown and
    list. Falls back to a guidance banner when no teachers exist yet.
  - page.tsx wires up the new tabs; data-testid attributes added across
    managers so the Playwright suite can target them deterministically.

Tests:
  - school-service: timetable_constraints_more_test.go fills the
    remaining 9 constraint DTOs (TeacherMaxHoursDay/Week,
    TeacherExcludedSubject/Room, SubjectMinDayGap,
    SubjectContiguousWhenRepeated, SubjectDoubleLesson, ClassNoGaps,
    RoomRequiresType). 66 subtests total, all green.
  - studio-v2: e2e/stundenplan.spec.ts covers the page shell, tab
    navigation, Klassen CRUD with mocked backend, constraint editor's
    empty-teacher fallback, sidebar entry. All school-service calls
    intercepted via page.route() so the suite is hermetic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 22:44:39 +02:00
Benjamin Admin f21ecf293b Add Stundenplan frontend scaffolding in studio-v2
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 31s
CI / test-go-edu-search (push) Successful in 29s
CI / test-python-klausur (push) Failing after 2m36s
CI / test-python-agent-core (push) Successful in 20s
CI / test-nodejs-website (push) Successful in 22s
Phase 3 — initial UI for the timetable scheduler:
  - app/stundenplan/page.tsx with tab navigation (Klassen / Lehrer /
    Faecher / Raeume / Zeitraster / Stundentafel / Lehrauftraege /
    Regeln) and a dev-mode JWT entry to authenticate against
    school-service until full auth is wired up.
  - app/stundenplan/_components/KlassenManager.tsx as the working
    prototype for one entity (list / create / delete). Pattern can be
    copied for the other 6 stammdaten + 15 constraint editors.
  - lib/stundenplan/api.ts exposing typed clients for all 22 endpoints
    (7 stammdaten + 15 constraint tables). Constraints use a factory
    to keep the file tight.
  - app/api/school/[...path]/route.ts proxies the browser through
    Next.js to school-service so HTTPS studio-v2 can reach the plain
    HTTP backend.
  - Sidebar.tsx gains a Stundenplan entry with 26-language labels.
  - docker-compose.yml exposes SCHOOL_SERVICE_URL to studio-v2 and
    declares the school-service dependency.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 22:25:18 +02:00
Benjamin Admin 64e7176267 Apply gofmt to timetable.go
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 39s
CI / test-go-edu-search (push) Successful in 29s
CI / test-python-klausur (push) Failing after 2m35s
CI / test-python-agent-core (push) Successful in 19s
CI / test-nodejs-website (push) Successful in 21s
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 22:13:17 +02:00
Benjamin Admin e958f88a2d Add timetable scheduler Phases 1 + 2 to school-service
Phase 1 — Stammdaten (7 tables):
  tt_class, tt_period, tt_room, tt_subject, tt_teacher,
  tt_curriculum, tt_assignment with CRUD endpoints.

Phase 2 — Constraints (15 typed tables):
  Teacher (6): unavailable_day, unavailable_window, max_hours_day,
    max_hours_week, excluded_subject, excluded_room
  Subject (5): min_day_gap, max_consecutive, contiguous_when_repeated,
    preferred_period, double_lesson
  Class (2): max_hours_day, no_gaps
  Room (2): requires_type, unavailable

Each constraint row carries is_hard / weight / active / note /
created_by_user_id; ownership enforced via WHERE EXISTS against the
parent tt_teacher/tt_class/tt_subject/tt_room row.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 22:12:23 +02:00
Benjamin Admin a1488b2fec Fix: enrich non-EN Kaikki search results with translations from EN hub
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 33s
CI / test-go-edu-search (push) Successful in 30s
CI / test-python-klausur (push) Failing after 2m33s
CI / test-python-agent-core (push) Successful in 20s
CI / test-nodejs-website (push) Successful in 24s
When searching for DE/FR/ES/etc. words, the Kaikki entries have empty
translations. Now does a reverse lookup to find the EN entry and copies
its 24-language translations. This ensures wordInNative() works for
all languages, not just the original 7.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 18:40:29 +02:00
Benjamin Admin 8d53b1f6b9 Translate Sidebar nav labels into 26 languages
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 28s
CI / test-go-edu-search (push) Successful in 30s
CI / test-python-klausur (push) Failing after 2m22s
CI / test-python-agent-core (push) Successful in 19s
CI / test-nodejs-website (push) Successful in 20s
- Lernmodule, Eltern, Woerterbuch, Meet, KI-Assistent now translated
- Uses NAV_LABELS dict with fallback chain (lang → en → de)
- Fixed "nav_eltern" → shows "Eltern"/"Parents"/"Ebeveyn" etc.
- Fixed "Lernmodule" hardcoded → uses nav_lernmodule key
- Woerterbuch link now points to /vocabulary (not /vocab-worksheet)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 18:33:11 +02:00
Benjamin Admin 399ab88f5f Translate language dropdown tabs + add info box in 26 languages
CI / test-go-edu-search (push) Waiting to run
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 40s
CI / test-python-klausur (push) Failing after 2m35s
CI / test-python-agent-core (push) Successful in 18s
CI / test-nodejs-website (push) Successful in 28s
- Tab labels (Muttersprache/Schulsprache) now translate with selected language
  e.g. Turkish: "Ana dilim" / "Okul dili"
- Info box below tabs explains each setting in the user's native language
  e.g. "Evde konustunuz dil. Tum menuler bu dilde gorunecektir."
- Wider dropdown (w-72) to fit translated text
- All 26 European languages covered

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 18:23:05 +02:00
Benjamin Admin d52eb43a32 Fix: auto-fill target translation with best match (not only single result)
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 30s
CI / test-go-edu-search (push) Successful in 29s
CI / test-python-klausur (push) Failing after 2m18s
CI / test-python-agent-core (push) Successful in 19s
CI / test-nodejs-website (push) Successful in 22s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 18:00:19 +02:00
Benjamin Admin bde0d57b5a Add Schulsprache (school language) as second language setting
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 27s
CI / test-go-edu-search (push) Successful in 28s
CI / test-python-klausur (push) Failing after 2m21s
CI / test-python-agent-core (push) Successful in 18s
CI / test-nodejs-website (push) Successful in 22s
- LanguageContext: new schoolLanguage + setSchoolLanguage
- LanguageDropdown: two tabs (Muttersprache / Schulsprache) with flag selection
- UnitBuilder: defaults target language to schoolLanguage
- Stored in bp_school_language localStorage (default: de)
- Shows school flag badge next to main language when different

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 17:49:58 +02:00
Benjamin Admin fc49d87928 Add exercise translations for all 26 European languages
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 32s
CI / test-go-edu-search (push) Successful in 29s
CI / test-python-klausur (push) Failing after 2m26s
CI / test-python-agent-core (push) Successful in 34s
CI / test-nodejs-website (push) Successful in 23s
Buttons (Richtig/Falsch/Weiter), instructions, labels — now translated
for FR, ES, IT, PT, NL, RO, EL, BG, HR, CS, HU, SV, DA, FI, SK, SL, LT, LV, ET.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 17:30:35 +02:00
Benjamin Admin 0018076ed5 Unify language system: one setting for all modules
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 41s
CI / test-go-edu-search (push) Successful in 30s
CI / test-python-klausur (push) Failing after 2m29s
CI / test-python-agent-core (push) Successful in 19s
CI / test-nodejs-website (push) Successful in 26s
- Merge two separate language systems (bp_language + bp_native_language) into one
- NativeLanguageContext now reads from LanguageContext (same localStorage key)
- Extend i18n.ts to 26 languages with flags (UI falls back to EN/DE)
- Replace LanguageSwitcher with LanguageDropdown (flags) in learn + parent layouts
- Migration: old bp_native_language value auto-migrates to bp_language
- Onboarding page writes to bp_language (unified key)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 16:54:27 +02:00
Benjamin Admin a30f10a467 Widen AudioButton lang prop to string for multi-language support
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 43s
CI / test-go-edu-search (push) Successful in 35s
CI / test-python-klausur (push) Failing after 2m30s
CI / test-python-agent-core (push) Successful in 22s
CI / test-nodejs-website (push) Successful in 26s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 16:19:45 +02:00
Benjamin Admin a44d360cbc Fix useRef initial value for React 19 compatibility
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 39s
CI / test-go-edu-search (push) Successful in 31s
CI / test-python-klausur (push) Failing after 2m33s
CI / test-python-agent-core (push) Successful in 22s
CI / test-nodejs-website (push) Successful in 26s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 16:04:43 +02:00
Benjamin Admin 52a15b24fe Add custom word entry + language pair support for learning units
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 31s
CI / test-go-edu-search (push) Successful in 31s
CI / test-python-klausur (push) Failing after 2m29s
CI / test-python-agent-core (push) Successful in 24s
CI / test-nodejs-website (push) Successful in 22s
- New UnitBuilder component with language pair selector (DE⇄EN, ES, FR, etc.)
- Manual word entry form with auto-suggest from Kaikki dictionary (6M words)
- "No results" prompt to add multi-word terms (e.g. "schottisches Hochland")
- New backend endpoint GET /vocabulary/lookup-translation (any→any via EN hub)
- Updated POST /vocabulary/units: accepts custom_words + source_lang/target_lang
- Split unit endpoints into vocabulary/unit_api.py (500 LOC budget)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 15:24:13 +02:00
Benjamin Admin 855cc4caf4 Fix: make 'Alle zur Unit' button visible — full-width layout below word tags
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 43s
CI / test-go-edu-search (push) Successful in 30s
CI / test-python-klausur (push) Failing after 2m37s
CI / test-python-agent-core (push) Successful in 19s
CI / test-nodejs-website (push) Successful in 26s
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 13:47:28 +02:00
Benjamin Admin c09fc6c7bc Add 'Alle zur Unit' button + fix topic display
Two buttons on topic cards:
- "Anzeigen": Shows words in search results (for review)
- "Alle zur Unit": Adds all topic words to the unit builder directly

Both buttons load from Kaikki and respect selected language.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 13:17:38 +02:00
Benjamin Admin 387219682d Fix: Topic word labels translate to selected language
Topics API now accepts lang= parameter. When lang=de, the word
labels are translated from English via Kaikki translations:
"eye, pupil, iris" → "Auge, Pupille, Iris"

Frontend sends searchLang to /topics endpoint and displays
display_words (translated) instead of words (English).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 12:38:35 +02:00
Benjamin Admin 6f43224fda Simplify Sidebar: Remove Woerterbuch, rename to "Lernmodule"
Sidebar: Only "Lernmodule" link (no separate Woerterbuch).
/learn page: "Neue Lernunit erstellen" button links to /vocabulary
for the word selection flow. Teacher stays in one flow.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 12:08:13 +02:00
Benjamin Admin 9b96998654 Fix: Topic "Alle laden" always searches in EN (topics are English word lists)
When user selects DE and types "Auge", the topic "Eye/Auge" is found
correctly. But "Alle laden" must search words with lang=en because
the topic word list is English (eye, pupil, iris...).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 11:57:18 +02:00
Benjamin Admin 91e8b92bdc Add topic suggestions: search "banana" → suggests "Fruit/Obst" topic
31 curated topics with 683 words (Fruit, Animals, Body, Eye, Sports,
School, Family, Weather, etc.). When user types a word that belongs
to a topic, the topic appears as a suggestion with "Alle laden" button.

Clicking "Alle laden" fetches all words from that topic via Kaikki
and displays them for easy selection into a learning unit.

New endpoint: GET /api/vocabulary/topics?q=banana
New table: vocabulary_topics (topic, words[], word_count)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 10:56:36 +02:00
Benjamin Admin c2efb9934c Fix: Vocabulary search sends lang parameter + language dropdown
Search now sends lang= to API (was always defaulting to EN).
Users can select any of the 24 languages in the search bar dropdown.
Placeholder text changes based on selected language.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-29 09:10:00 +02:00
Benjamin Admin 0d2e79da66 Fix: Hardcode Kaikki stats (COUNT on 6M rows took 100s, blocked server)
SELECT COUNT(*) FROM vocabulary_kaikki was 100+ seconds without index,
blocking the entire backend. Hardcoded to 6,271,749 / 24 languages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 22:47:00 +02:00
Benjamin Admin cb4ea8e49a Connect frontend to Kaikki dictionary (6.27M words, 24 languages)
Search endpoint now defaults to source=kaikki, searching the
vocabulary_kaikki table with 6.27M Wiktionary entries.

/filters returns kaikki_total and kaikki_languages count.
/vocabulary header shows "6,271,749 Woerter in 24 Sprachen".

Manual vocabulary_words (27 entries) still accessible via source=manual.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 17:49:28 +02:00
Benjamin Admin d14826b199 Fix: Build error + explanation above exercise + aligned columns + impressum link
1. Removed stale 'explanations' export that broke build
2. Explanation banner now spans full width ABOVE the 2/3+1/3 layout
   so exercise cards and native words start at the same height
3. Both columns use items-start for visual alignment
4. Impressum link added at bottom of learn layout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 14:54:12 +02:00
Benjamin Admin 693989c1a6 Add exercise explanations in all 26 languages
exerciseExplanations.ts: Match and Flashcard explanations translated
into DE, EN, TR, AR, UK, RU, PL, FR, ES, IT, PT, NL, RO, EL, BG,
HR, CS, HU, SV, DA, FI, SK, SL, LT, LV, ET.

Each exercise type gets a parent-facing explanation in the user's
selected language. No more German fallback for unsupported languages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 14:14:48 +02:00
Benjamin Admin bd24fa6ba6 Fix: Cast entry to avoid TypeScript 'never' inference
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 13:58:09 +02:00
Benjamin Admin ef821831a4 Fix: Fallback to English (not German) for unsupported languages
When a language like PT, FR, IT is selected but has no translation
in exerciseTranslations.ts, the system now falls back to English
instead of German. English is more universally understood.

Applies to: exercise explanations, button labels, instructions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 13:37:19 +02:00
Benjamin Admin 93f7ef88e3 Add Impressum with attribution + expand language dropdown to 26 languages
Impressum page (/impressum) with rechtskonform attribution for:
- Wiktionary/Kaikki.org (CC BY-SA 3.0 + GFDL) — dictionary data
- ipa-dict (MIT) — IPA phonetic transcriptions
- Wikimedia Commons (CC BY-SA) — vocabulary images
- Piper TTS (MIT) — speech synthesis
- pyspellchecker (MIT) — spell checking

Language dropdown expanded from 7 to 26 European languages:
DE, EN, TR, AR, UK, RU, PL, FR, ES, IT, PT, NL, RO, EL, BG,
HR, CS, HU, SV, DA, FI, SK, SL, LT, LV, ET.

Dropdown now shows full name: "TR — Turkce", "AR — العربية", etc.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 12:49:03 +02:00
Benjamin Admin 6ea20fa1a3 Add persistent volume for ~/Arbeitsblaetter (learning units, QA, audio cache)
lehrer_arbeitsblaetter volume mounted at /root/Arbeitsblaetter.
Survives container restarts — learning units, QA items, translations,
and audio cache are no longer lost on rebuild.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 09:11:01 +02:00
Benjamin Admin bf2f7daaeb Fix: Wrap ternary else-branch in Fragment for SelectedImage
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 08:37:57 +02:00
Benjamin Admin fc2fe98bd9 Fix: Extract SelectedImage as component (IIFE breaks JSX parser)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 08:24:19 +02:00
Benjamin Admin 1a272371f4 Add image preview under selected word in Match exercise
When user clicks an EN word, the corresponding image (Wikipedia
photo or emoji) appears below the match grid. Emoji shown as
large text (6xl), Wikipedia photos as max-h 160px image.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-28 07:52:16 +02:00
Benjamin Admin fdde5d43b3 Fix: Wikipedia User-Agent (was 403 Forbidden)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 23:51:46 +02:00
Benjamin Admin f6caa3091f Add image service: Wikipedia photos + emoji fallback for vocabulary
image_service.py: Fetches thumbnail from Wikipedia REST API (free,
no account). Falls back to emoji for abstract words (40+ mapped).

Auto-enrichment: When a learning unit is created, images are
automatically fetched for all words that don't have one yet.

Manual endpoint: POST /api/vocabulary/enrich-images fills images
for existing words without images.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 23:36:48 +02:00
Benjamin Admin 91d6918e2c Fix: Explanation card visible + visual divider between 2/3 and 1/3
- exerciseType prop for correct explanation lookup (was using title)
- Vertical divider line between work area and native helper
- Cyan-tinted explanation card with lightbulb icon
- Padding between sections (pr-6 / pl-6 around divider)
- Explanation card has distinct background for visibility

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 23:07:57 +02:00
Benjamin Admin 82f5b4fbba Redesign: 2/3 + 1/3 layout for exercises with native helper panel
ExerciseLayout.tsx: Reusable layout component for all exercises.
- Left 2/3: Standard exercise area (EN + DE)
- Right 1/3: Native language helper (explanation + word list)
- Only shows right panel for non-DE/EN speakers
- Explanation card describes what the child should do
- Column headers are trilingual (TR · English · Deutsch)

Match page rebuilt using ExerciseLayout:
- EN+DE cards in 2/3 left area with equal height + audio
- Native words in 1/3 right panel with audio buttons
- Highlights native word when EN word is selected
- Progress bar with count, score counter

ExerciseLayout can be reused for flashcards, quiz, type, etc.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 22:30:07 +02:00
Benjamin Admin afe7a983d1 Fix Match: Equal card heights + progress bar with count
All cards (EN/DE/native) now have min-h-[48px] for consistent height.
Progress bar shows "4/12" count next to the fill bar.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 19:26:53 +02:00
Benjamin Admin 6d54ee8178 Fix Match: Progress bar + counter, audio on EN column too
- Progress bar under header (fills as pairs are matched)
- Counter with symbols: ✓ first-try, ↻ retry, ✗ errors
- EN column now also has audio buttons (small speaker icon)
- All 3 columns have consistent height (flex layout)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 18:35:00 +02:00
Benjamin Admin a1664ab12c Redesign Match exercise: 3 columns, audio, scoring, native language
Major improvements:
1. Third column shows native language translation (TR/AR/UK/RU)
2. Clicking EN word flashes native translation briefly (2s overlay)
3. German column has audio button on each word (speaker icon)
4. Native column has audio button for each translation
5. Scoring: tracks first-try correct vs retry vs errors separately
6. Full points only for error-free completion
7. "Nochmal" button always available to repeat the unit
8. Header shows live score: green/yellow/red counters
9. All buttons use translation system (t('back'), t('match'), etc.)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 18:03:35 +02:00
Benjamin Admin 9f21bd070a Fix: Language switch takes effect immediately (React Context)
Replaced localStorage-only hook with React Context Provider.
Layout and page components now share the same state — switching
language in the dropdown instantly updates all text on screen
without requiring a page reload.

NativeLanguageProvider added to root layout.tsx.
useNativeLanguage() re-exported from Context for backward compat.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 14:45:53 +02:00
Benjamin Admin 5012699aaf Add persistent language switcher across all learn/parent pages
useNativeLanguage: Now has setNativeLang() that persists to localStorage.
Language selection carries across all pages automatically.

LanguageSwitcher: Compact dropdown component added to learn/layout.tsx
and parent/layout.tsx — visible on every sub-page (top-right).

Parent portal: Language dropdown syncs both UI language and native
language. Parents can switch language mid-session (e.g. when both
parents speak different languages).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 09:23:01 +02:00