mirror of
https://github.com/nihonbuzz/nihonbuzz-academy.git
synced 2026-01-26 05:25:37 +07:00
130 lines
4.3 KiB
PHP
130 lines
4.3 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Course;
|
|
use App\Models\Lesson;
|
|
use App\Models\UserProgress;
|
|
use Illuminate\Http\Request;
|
|
use Inertia\Inertia;
|
|
use Inertia\Response;
|
|
|
|
class ExamController extends Controller
|
|
{
|
|
/**
|
|
* Show the exam interface.
|
|
*/
|
|
public function show(Request $request, string $courseSlug, string $lessonSlug): Response
|
|
{
|
|
$user = $request->user();
|
|
|
|
$course = Course::where('slug', $courseSlug)->firstOrFail();
|
|
$lesson = Lesson::where('slug', $lessonSlug)->where('type', 'quiz')->firstOrFail();
|
|
|
|
// Check if already completed
|
|
$progress = UserProgress::where('user_id', $user->id)
|
|
->where('lesson_id', $lesson->id)
|
|
->first();
|
|
|
|
$previousResut = null;
|
|
if ($progress && $progress->completed_at) {
|
|
$previousResut = $progress->metadata['score'] ?? 0;
|
|
}
|
|
|
|
// Parse questions from metadata
|
|
// Assuming structure: metadata->questions = [{ id, question, options[], correct_answer }]
|
|
// We do NOT send 'correct_answer' to frontend
|
|
$questions = collect($lesson->metadata['questions'] ?? [])->map(function ($q) {
|
|
return [
|
|
'id' => $q['id'],
|
|
'question' => $q['question'],
|
|
'options' => $q['options'],
|
|
];
|
|
});
|
|
|
|
// Use same layout data as CoursePlayer (simplified modules list)
|
|
// Ideally refactor this shared logic or use a Service
|
|
$modules = $course->modules()->with('lessons')->get()->map(function ($module) use ($user) {
|
|
return [
|
|
'id' => $module->id,
|
|
'title' => $module->title,
|
|
'lessons' => $module->lessons->map(function ($l) {
|
|
return [
|
|
'id' => $l->id,
|
|
'title' => $l->title,
|
|
'slug' => $l->slug,
|
|
'type' => $l->type,
|
|
'is_completed' => false // Simplified for now
|
|
];
|
|
})
|
|
];
|
|
});
|
|
|
|
return Inertia::render('Courses/Exam', [
|
|
'course' => [
|
|
'title' => $course->title,
|
|
'slug' => $course->slug,
|
|
],
|
|
'modules' => $modules,
|
|
'lesson' => [
|
|
'id' => $lesson->id,
|
|
'title' => $lesson->title,
|
|
'slug' => $lesson->slug,
|
|
'questions' => $questions,
|
|
],
|
|
'previousResult' => $previousResut,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Handle exam submission.
|
|
*/
|
|
public function store(Request $request, string $courseSlug, string $lessonSlug)
|
|
{
|
|
$lesson = Lesson::where('slug', $lessonSlug)->firstOrFail();
|
|
$user = $request->user();
|
|
|
|
$answers = $request->input('answers', []); // [question_id => selected_option]
|
|
$questions = $lesson->metadata['questions'] ?? [];
|
|
|
|
$score = 0;
|
|
$total = count($questions);
|
|
$correctAnswers = 0;
|
|
|
|
foreach ($questions as $q) {
|
|
$userAnswer = $answers[$q['id']] ?? null;
|
|
if ($userAnswer === $q['correct_answer']) {
|
|
$correctAnswers++;
|
|
}
|
|
}
|
|
|
|
$minPassPercentage = 70; // Hardcoded requirement for certificate
|
|
$accuracy = ($total > 0) ? round(($correctAnswers / $total) * 100) : 0;
|
|
|
|
$passed = $accuracy >= $minPassPercentage;
|
|
|
|
// Save progress with score
|
|
UserProgress::updateOrCreate(
|
|
['user_id' => $user->id, 'lesson_id' => $lesson->id],
|
|
[
|
|
'completed_at' => $passed ? now() : null, // Only mark complete if passed? Or mark complete but with failing score? Let's say only if passed.
|
|
'metadata' => [
|
|
'score' => $accuracy,
|
|
'answers' => $answers, // Store user answers if needed for review
|
|
'passed' => $passed
|
|
]
|
|
]
|
|
);
|
|
|
|
if ($passed) {
|
|
$user->increment('xp_points', 100);
|
|
}
|
|
|
|
return back()->with('flash', [
|
|
'score' => $accuracy,
|
|
'passed' => $passed,
|
|
'message' => $passed ? 'Selamat! Anda lulus ujian.' : 'Maaf, nilai anda belum mencukupi. Silahkan coba lagi.'
|
|
]);
|
|
}
|
|
}
|