NOW()) ORDER BY a.created_at DESC ", [':cid' => $classId]); // ─── Freie Module (V1-Freigaben weiter genutzt) ────────────────── // V1: class_modules.module_id matcht module_info_v2.slug $freeRaw = []; try { $freeRaw = v2_db_all(" SELECT mi.slug, mi.display_name, mi.display_name_easy, mi.icon, mi.card_image_url, mi.short_description, mi.short_description_easy, mi.estimated_duration_min, mi.status FROM module_info_v2 mi WHERE mi.status IN ('beta', 'stable') AND mi.slug IN ( SELECT module_id FROM class_modules WHERE class_id = :cid AND enabled = 1 ) ORDER BY mi.display_name ", [':cid' => $classId]); } catch (Throwable $e) { // Fallback: alle 'stable' V2-Module wenn class_modules nicht existiert / leer ist $freeRaw = v2_db_all(" SELECT slug, display_name, display_name_easy, icon, card_image_url, short_description, short_description_easy, estimated_duration_min, status FROM module_info_v2 WHERE status IN ('beta', 'stable') ORDER BY display_name "); } // ─── Level-Unlocks ─────────────────────────────────────────────── $unlocks = v2_db_all(" SELECT module_slug, highest_unlocked_level, highest_completed_level FROM student_level_unlock_v2 WHERE student_id = :sid ", [':sid' => $studentId]); $unlocksBySlug = []; foreach ($unlocks as $u) { $unlocksBySlug[$u['module_slug']] = [ 'highestUnlocked' => $u['highest_unlocked_level'], 'highestCompleted' => $u['highest_completed_level'], ]; } // ─── Output formen ─────────────────────────────────────────────── $assignments = array_map(function ($a) { return [ 'assignmentId' => (int) $a['id'], 'moduleSlug' => $a['module_slug'], 'difficulty' => $a['difficulty'], 'displayName' => $a['display_name'] ?? $a['module_slug'], 'displayNameEasy' => $a['display_name_easy'] ?? $a['module_slug'], 'icon' => $a['icon'] ?? '📘', 'cardImageUrl' => $a['card_image_url'], 'shortDescription' => $a['short_description'] ?? '', 'shortDescriptionEasy' => $a['short_description_easy'] ?? '', 'estimatedDurationMin' => (int) ($a['estimated_duration_min'] ?? 0), 'endsAt' => $a['ends_at'], ]; }, $assignmentsRaw); $freeModules = array_map(function ($m) use ($unlocksBySlug) { return [ 'slug' => $m['slug'], 'displayName' => $m['display_name'], 'displayNameEasy' => $m['display_name_easy'], 'icon' => $m['icon'] ?? '📘', 'cardImageUrl' => $m['card_image_url'], 'shortDescription' => $m['short_description'] ?? '', 'shortDescriptionEasy' => $m['short_description_easy'] ?? '', 'estimatedDurationMin' => (int) ($m['estimated_duration_min'] ?? 0), 'status' => $m['status'], 'unlocked' => $unlocksBySlug[$m['slug']] ?? ['highestUnlocked' => 'L1', 'highestCompleted' => null], ]; }, $freeRaw); v2_response_ok([ 'assignments' => $assignments, 'freeModules' => $freeModules, ]);