From e0aefa4eb679664750e9a1564513e52f3e36cbbf Mon Sep 17 00:00:00 2001 From: Baghiz Zuhdi Adzin <74885652+baghizzhd@users.noreply.github.com> Date: Fri, 30 Jan 2026 14:48:46 +0700 Subject: [PATCH] pembenaran update projek --- app/Http/Controllers/PlayerController.php | 99 ++++-- .../Controllers/frontend/HomeController.php | 191 ++++++++--- .../instructor/ProjectController.php | 309 ++++++++++++++++++ .../Controllers/instructor/QuizController.php | 7 +- .../Controllers/student/ProjectController.php | 51 +++ .../Controllers/student/QuizController.php | 74 +++-- app/Models/ProjectSubmission.php | 24 ++ resources/views/course_player/index.blade.php | 2 +- .../views/course_player/player_page.blade.php | 16 +- .../course_player/project/index.blade.php | 163 +++++++++ .../instructor/course/curriculum.blade.php | 18 +- .../instructor/course/lesson_add.blade.php | 8 - .../views/instructor/project/create.blade.php | 50 +++ .../views/instructor/project/edit.blade.php | 58 ++++ .../project/grading/index.blade.php | 121 +++++++ .../project/grading/preview.blade.php | 182 +++++++++++ .../views/instructor/quiz/create.blade.php | 2 +- routes/admin.php | 12 +- routes/instructor.php | 11 + routes/student.php | 7 + 20 files changed, 1291 insertions(+), 114 deletions(-) create mode 100644 app/Http/Controllers/instructor/ProjectController.php create mode 100644 app/Http/Controllers/student/ProjectController.php create mode 100644 app/Models/ProjectSubmission.php create mode 100644 resources/views/course_player/project/index.blade.php create mode 100644 resources/views/instructor/project/create.blade.php create mode 100644 resources/views/instructor/project/edit.blade.php create mode 100644 resources/views/instructor/project/grading/index.blade.php create mode 100644 resources/views/instructor/project/grading/preview.blade.php diff --git a/app/Http/Controllers/PlayerController.php b/app/Http/Controllers/PlayerController.php index 13bbd83..28cb1f8 100644 --- a/app/Http/Controllers/PlayerController.php +++ b/app/Http/Controllers/PlayerController.php @@ -108,41 +108,86 @@ class PlayerController extends Controller $data['student_id'] = auth()->user()->id; $total_lesson = Lesson::where('course_id', $request->course_id)->pluck('id')->toArray(); - - $watch_history = Watch_history::where('course_id', $request->course_id) - ->where('student_id', auth()->user()->id)->first(); + $watch_history = Watch_history::where('course_id', $request->course_id)->where('student_id', auth()->user()->id)->first(); + $student_id = auth()->user()->id; - if (isset($watch_history) && $watch_history->id) { - $lessons = (array) json_decode($watch_history->completed_lesson); - if (! in_array($request->lesson_id, $lessons)) { - array_push($lessons, $request->lesson_id); - } - - $data['completed_lesson'] = json_encode($lessons); - $data['watching_lesson_id'] = $request->lesson_id; - $data['completed_date'] = (count($total_lesson) == count($lessons)) ? time() : null; - Watch_history::where('course_id', $request->course_id)->where('student_id', auth()->user()->id)->update($data); + $completed_lessons = []; + if (!empty($watch_history->completed_lesson)) { + $decoded = json_decode($watch_history->completed_lesson, true); + if (is_array($decoded)) { + $completed_lessons = array_map('intval', $decoded); + } else { + // Fallback jika format tidak valid + $completed_lesson_data = trim($watch_history->completed_lesson, '[]"\' '); + if (!empty($completed_lesson_data)) { + $completed_lessons = array_map('intval', explode(',', $completed_lesson_data)); + } + } + } + + $completed_lessons = array_unique($completed_lessons); + sort($completed_lessons); + + $lesson_id_int = (int)$request->lesson_id; + if (!in_array($lesson_id_int, $completed_lessons)) { + $completed_lessons[] = $lesson_id_int; + sort($completed_lessons); + } + + $data['completed_lesson'] = json_encode($completed_lessons); + $data['watching_lesson_id'] = $lesson_id_int; + + Log::info('Updated lessons (integer array):', $completed_lessons); + + Watch_history::where('course_id', $request->course_id) + ->where('student_id', $student_id) + ->update($data); } else { - $lessons = [$request->lesson_id]; - $data['completed_lesson'] = json_encode($lessons); - $data['watching_lesson_id'] = $request->lesson_id; - $data['completed_date'] = (count($total_lesson) == count($lessons)) ? time() : null; - Watch_history::insert($data); + $lesson_id_int = (int)$request->lesson_id; + $completed_lessons = [$lesson_id_int]; + + $data['completed_lesson'] = json_encode($completed_lessons); + $data['watching_lesson_id'] = $lesson_id_int; + + Log::info('New lessons (integer array):', $completed_lessons); + + Watch_history::create($data); } + + // **LOGIKA SERTIFIKAT - dengan progress yang benar** + $completed_lessons = $completed_lessons ?? []; + $completed_in_course = array_intersect($completed_lessons, $total_lesson); + $completed_count = count($completed_in_course); + $total_count = count($total_lesson); + + $progress_percentage = 0; + if ($total_count > 0) { + $progress_percentage = ($completed_count / $total_count) * 100; + } + Log::info('Progress percentage: ' . $progress_percentage); + // Jika progress 100% dan status project diterima, buat sertifikat + if ($progress_percentage >= 100) { + $certificateExists = Certificate::where('user_id', $student_id) + ->where('course_id', $request->course_id) + ->exists(); - if (progress_bar($request->course_id) >= 100) { - $certificate = Certificate::where('user_id', auth()->user()->id)->where('course_id', $request->course_id); - - if ($certificate->count() == 0) { - $certificate_data['user_id'] = auth()->user()->id; - $certificate_data['course_id'] = $request->course_id; - $certificate_data['identifier'] = $this->generateIdentifier(12); - $certificate_data['created_at'] = date('Y-m-d H:i:s'); - Certificate::insert($certificate_data); + if (!$certificateExists) { + if (method_exists($this, 'generateIdentifier')) { + $identifier = $this->generateIdentifier(12); + } else { + $identifier = \Illuminate\Support\Str::random(12); + } + + Certificate::create([ + 'user_id' => $student_id, + 'course_id' => $request->course_id, + 'identifier' => $identifier, + ]); } } + return redirect()->back(); } diff --git a/app/Http/Controllers/frontend/HomeController.php b/app/Http/Controllers/frontend/HomeController.php index 7addf79..6b3d13a 100644 --- a/app/Http/Controllers/frontend/HomeController.php +++ b/app/Http/Controllers/frontend/HomeController.php @@ -8,6 +8,7 @@ use App\Models\Builder_page; use App\Models\Category; use App\Models\Certificate; use App\Models\Course; +use App\Models\Lesson; use App\Models\Message; use App\Models\Message_code; use App\Models\Review; @@ -15,6 +16,7 @@ use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Session; use SimpleSoftwareIO\QrCode\Facades\QrCode; +use Illuminate\Support\Facades\Log; use DB; use App\Http\Controllers\NewsletterController; @@ -70,13 +72,13 @@ class HomeController extends Controller public function update_watch_history_with_duration(Request $request) { - $userId = auth()->user()->id; // Get the logged-in user's ID + $userId = auth()->user()->id; $courseProgress = 0; $isCompleted = 0; // Retrieve and sanitize input data - $courseId = htmlspecialchars($request->input('course_id')); - $lessonId = htmlspecialchars($request->input('lesson_id')); + $courseId = (int) htmlspecialchars($request->input('course_id')); + $lessonId = (int) htmlspecialchars($request->input('lesson_id')); $currentDuration = htmlspecialchars($request->input('current_duration')); // Fetch current watch history record @@ -90,17 +92,29 @@ class HomeController extends Controller // Fetch course details $courseDetails = DB::table('courses')->where('id', $courseId)->first(); - $dripContentSettings = json_decode($courseDetails->drip_content_settings, true); + + if (!$courseDetails) { + return response()->json([ + 'lesson_id' => $lessonId, + 'course_progress' => 0, + 'is_completed' => 0, + 'error' => 'Course not found' + ]); + } + + $dripContentSettings = json_decode($courseDetails->drip_content_settings, true) ?? []; if ($currentHistory) { $watchedDurationArr = json_decode($currentHistory->watched_counter, true); - if (!is_array($watchedDurationArr)) $watchedDurationArr = []; - - if (!in_array($currentDuration, $watchedDurationArr)) { - array_push($watchedDurationArr, $currentDuration); + if (!is_array($watchedDurationArr)) { + $watchedDurationArr = []; } - $watchedDurationJson = json_encode($watchedDurationArr); + // Pastikan tidak ada duplikat + if (!in_array($currentDuration, $watchedDurationArr)) { + $watchedDurationArr[] = $currentDuration; + sort($watchedDurationArr); + } DB::table('watch_durations') ->where([ @@ -109,8 +123,9 @@ class HomeController extends Controller 'watched_student_id' => $userId, ]) ->update([ - 'watched_counter' => $watchedDurationJson, + 'watched_counter' => json_encode($watchedDurationArr), 'current_duration' => $currentDuration, + 'updated_at' => now(), ]); } else { $watchedDurationArr = [$currentDuration]; @@ -120,36 +135,63 @@ class HomeController extends Controller 'watched_student_id' => $userId, 'current_duration' => $currentDuration, 'watched_counter' => json_encode($watchedDurationArr), + 'created_at' => now(), + 'updated_at' => now(), ]); } + // Jika drip content tidak aktif, return early if ($courseDetails->enable_drip_content != 1) { return response()->json([ 'lesson_id' => $lessonId, - 'course_progress' => null, - 'is_completed' => null + 'course_progress' => 0, + 'is_completed' => 0 ]); } // Fetch lesson details for duration calculations $lessonTotalDuration = DB::table('lessons')->where('id', $lessonId)->value('duration'); + if (!$lessonTotalDuration) { + return response()->json([ + 'lesson_id' => $lessonId, + 'course_progress' => 0, + 'is_completed' => 0, + 'error' => 'Lesson duration not found' + ]); + } + $lessonTotalDurationArr = explode(':', $lessonTotalDuration); - $lessonTotalSeconds = ($lessonTotalDurationArr[0] * 3600) + ($lessonTotalDurationArr[1] * 60) + $lessonTotalDurationArr[2]; - $currentTotalSeconds = count($watchedDurationArr) * 5; // Assuming each increment represents 5 seconds + if (count($lessonTotalDurationArr) < 3) { + return response()->json([ + 'lesson_id' => $lessonId, + 'course_progress' => 0, + 'is_completed' => 0, + 'error' => 'Invalid duration format' + ]); + } + + $lessonTotalSeconds = ($lessonTotalDurationArr[0] * 3600) + + ($lessonTotalDurationArr[1] * 60) + + $lessonTotalDurationArr[2]; + $currentTotalSeconds = count($watchedDurationArr) * 5; // Drip content completion logic - if ($dripContentSettings['lesson_completion_role'] == 'duration') { - if ($currentTotalSeconds >= $dripContentSettings['minimum_duration']) { - $isCompleted = 1; - } elseif (($currentTotalSeconds + 4) >= $lessonTotalSeconds) { - $isCompleted = 1; - } - } else { - $requiredDuration = ($lessonTotalSeconds / 100) * $dripContentSettings['minimum_percentage']; - if ($currentTotalSeconds >= $requiredDuration) { - $isCompleted = 1; - } elseif (($currentTotalSeconds + 4) >= $lessonTotalSeconds) { - $isCompleted = 1; + if (!empty($dripContentSettings) && isset($dripContentSettings['lesson_completion_role'])) { + if ($dripContentSettings['lesson_completion_role'] == 'duration') { + $minimumDuration = $dripContentSettings['minimum_duration'] ?? 0; + if ($currentTotalSeconds >= $minimumDuration) { + $isCompleted = 1; + } elseif (($currentTotalSeconds + 4) >= $lessonTotalSeconds) { + $isCompleted = 1; + } + } else { + $minimumPercentage = $dripContentSettings['minimum_percentage'] ?? 0; + $requiredDuration = ($lessonTotalSeconds / 100) * $minimumPercentage; + if ($currentTotalSeconds >= $requiredDuration) { + $isCompleted = 1; + } elseif (($currentTotalSeconds + 4) >= $lessonTotalSeconds) { + $isCompleted = 1; + } } } @@ -164,27 +206,92 @@ class HomeController extends Controller if ($watchHistory) { $lessonIds = json_decode($watchHistory->completed_lesson, true); - $courseProgress = $watchHistory->course_progress; - - if (!is_array($lessonIds)) $lessonIds = []; + + if (!is_array($lessonIds)) { + $lessonIds = []; + } else { + // Konversi semua nilai ke integer + $lessonIds = array_map('intval', $lessonIds); + $lessonIds = array_values(array_unique($lessonIds)); + sort($lessonIds); + } if (!in_array($lessonId, $lessonIds)) { - array_push($lessonIds, $lessonId); + // Tambahkan lesson baru + $lessonIds[] = $lessonId; + $lessonIds = array_unique($lessonIds); + sort($lessonIds); + + // Hitung total lesson yang valid di course $totalLesson = DB::table('lessons')->where('course_id', $courseId)->count(); - $courseProgress = (100 / $totalLesson) * count($lessonIds); - - $completedDate = ($courseProgress >= 100 && !$watchHistory->completed_date) - ? time() - : $watchHistory->completed_date; - + + // Hitung progress yang akurat + if ($totalLesson > 0) { + // Ambil semua lesson ID yang valid di course ini + $validLessonIds = DB::table('lessons') + ->where('course_id', $courseId) + ->pluck('id') + ->map(function($id) { + return (int)$id; + }) + ->toArray(); + + // Hitung hanya lesson yang valid (ada di course ini) + $completedValidLessons = array_intersect($lessonIds, $validLessonIds); + $completedCount = count($completedValidLessons); + + $courseProgress = ($completedCount / $totalLesson) * 100; + $courseProgress = round($courseProgress, 2); + $courseProgress = min(100, $courseProgress); // Pastikan tidak lebih dari 100% + } + + // Tentukan completed_date + $updateData = [ + 'course_progress' => $courseProgress, + 'completed_lesson' => json_encode($lessonIds, JSON_NUMERIC_CHECK), // Flag penting! + 'watching_lesson_id' => $lessonId, + 'updated_at' => now(), + ]; + + // Set completed_date hanya jika progress 100% dan belum ada + if ($courseProgress >= 100 && !$watchHistory->completed_date) { + $updateData['completed_date'] = now(); + } elseif ($watchHistory->completed_date) { + $updateData['completed_date'] = $watchHistory->completed_date; + } + DB::table('watch_histories') ->where('id', $watchHistory->id) - ->update([ - 'course_progress' => $courseProgress, - 'completed_lesson' => json_encode($lessonIds), - 'completed_date' => $completedDate, - ]); + ->update($updateData); + + } else { + // Jika lesson sudah ada, ambil progress yang ada + $courseProgress = $watchHistory->course_progress ?? 0; } + } else { + // Buat record baru di watch_histories + $lessonIds = [$lessonId]; + $totalLesson = DB::table('lessons')->where('course_id', $courseId)->count(); + + if ($totalLesson > 0) { + $courseProgress = (1 / $totalLesson) * 100; + $courseProgress = round($courseProgress, 2); + } + + $isCourseCompleted = $courseProgress >= 100; + + dd(json_encode($lessonIds, JSON_NUMERIC_CHECK)); + + DB::table('watch_histories')->insert([ + 'course_id' => $courseId, + 'student_id' => $userId, + 'watching_lesson_id' => $lessonId, + 'completed_lesson' => json_encode($lessonIds, JSON_NUMERIC_CHECK), + 'course_progress' => $courseProgress, + 'completed_date' => $isCourseCompleted ? now() : null, + 'created_at' => now(), + 'updated_at' => now(), + ]); } } @@ -193,6 +300,8 @@ class HomeController extends Controller 'lesson_id' => $lessonId, 'course_progress' => round($courseProgress), 'is_completed' => $isCompleted, + 'current_total_seconds' => $currentTotalSeconds, + 'lesson_total_seconds' => $lessonTotalSeconds, ]); } diff --git a/app/Http/Controllers/instructor/ProjectController.php b/app/Http/Controllers/instructor/ProjectController.php new file mode 100644 index 0000000..42db41d --- /dev/null +++ b/app/Http/Controllers/instructor/ProjectController.php @@ -0,0 +1,309 @@ +validate([ + 'status' => 'required|in:1,2', + 'comment' => 'required_if:status,2', + ]); + + $submission = ProjectSubmission::findOrFail($id); + + $submission->status = $request->status; + $submission->reviewed_by = auth()->id(); + $submission->reviewed_at = now(); + + if ($request->status == 1) { + $submission->comment = null; + } else { + $submission->comment = $request->comment; + } + + $submission->save(); + + return redirect()->back()->with('success', get_phrase('Project graded successfully')); + } + + public function store(Request $request) + { + $title = Lesson::join('sections', 'lessons.section_id', 'sections.id') + ->join('courses', 'sections.course_id', 'courses.id') + ->where('courses.user_id', auth()->user()->id) + ->where('lessons.title', $request->title) + ->first(); + if ($title) { + Session::flash('error', get_phrase('Title has been taken.')); + return redirect()->back(); + } + + $maxSort = Lesson::where('section_id', $request->section) + ->max('sort'); + + $data['user_id'] = auth()->user()->id; + $data['sort'] = $maxSort + 1; + $data['title'] = $request->title; + $data['course_id'] = $request->course_id; + $data['section_id'] = $request->section; + $data['description'] = $request->description; + $data['attachment'] = $request->attachment; + $data['summary'] = $request->summary; + $data['lesson_type'] = 'project'; + $data['status'] = 1; + + Lesson::insert($data); + + Session::flash('success', get_phrase('Project has been created.')); + return redirect()->back(); + } + + public function getIndex($id) + { + $participants = ProjectSubmission::join('users', 'project_submissions.user_id', '=', 'users.id') + ->where('project_submissions.lesson_id', $id) + ->select('users.name', 'users.id') + ->distinct() + ->get(); + + return view('instructor.project.grading.index', ['participants' => $participants, 'lesson_id' => $id]); + } + + public function getPreview(Request $request) + { + // Validasi input + $request->validate([ + 'lesson_id' => 'required|integer', + 'user_id' => 'required|integer' + ]); + + // Cari submission terbaru + $submission = ProjectSubmission::where('lesson_id', $request->lesson_id) + ->where('user_id', $request->user_id) + ->latest() + ->first(); + // dd($submission); // <-- INI HARUS DIHAPUS ATAU DI-KOMEN + + // Tentukan label komentar + $commentLabel = (!empty($submission) && !empty($submission->comment)) + ? "Revisi Sebelumnya" + : "Masukkan Revisi Anda"; + + // Return view preview + return view('instructor.project.grading.preview', compact('submission', 'commentLabel')); + } + + public function getParticipantSubmission(Request $request) + { + $submissions = ProjectSubmission::where('lesson_id', $request->quizId) + ->where('user_id', $request->participant) + ->orderBy('created_at', 'desc') + ->get(); + + $html = ''; + + if($submissions->count() > 0){ + foreach ($submissions as $submission) { + $date = date('d M Y, H:i', strtotime($submission->created_at)); + + $statusLabel = ''; + if($submission->status == 1) $statusLabel = ' (Lulus)'; + elseif($submission->status == 2) $statusLabel = ' (Revisi)'; + else $statusLabel = ' (Pending)'; + + $html .= ''; + } + } else { + $html .= ''; + } + + return response()->json($html); + } + + public function updateSubmission(Request $request, $id) + { + $request->validate([ + 'status' => 'required|in:1,2', + 'comment' => 'required_if:status,2', // Wajib isi jika status 2 (Revisi) + ]); + + // Menggunakan Model findOrFail + $submission = ProjectSubmission::findOrFail($id); + + $submission->status = $request->status; + $submission->reviewed_by = auth()->id(); + $submission->reviewed_at = now(); + + if ($request->status == 1) { + // Jika Diterima (1), Comment jadi NULL (sesuai request) + $submission->comment = null; + } else { + // Jika Perlu Perbaikan (2), Simpan Comment + $submission->comment = $request->comment; + } + + $submission->save(); + + // **LOGIKA WATCH HISTORY - DIPERBAIKI berdasarkan referensi** + + $lesson = Lesson::findOrFail($submission->lesson_id); + + $student_id = $submission->user_id; + $course_id = $lesson->course_id; + $lesson_id = $submission->lesson_id; + + // Ambil semua lesson ID untuk course ini + $total_lesson = Lesson::where('course_id', $course_id) + ->pluck('id') + ->map(function($id) { + return (int)$id; + }) + ->toArray(); + + // Cari watch_history untuk student ini + $watch_history = Watch_history::where('course_id', $course_id) + ->where('student_id', $student_id) + ->first(); + + // **LOGIKA UTAMA berdasarkan referensi set_watch_history** + if ($watch_history) { + // Parse data lama dengan handling yang robust + $completed_lessons = []; + + if (!empty($watch_history->completed_lesson)) { + $decoded = json_decode($watch_history->completed_lesson, true); + + if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) { + // Konversi semua nilai ke integer + $completed_lessons = array_map('intval', $decoded); + } else { + // Fallback jika format tidak valid + $completed_lesson_data = trim($watch_history->completed_lesson, '[]"\' '); + if (!empty($completed_lesson_data)) { + $completed_lessons = array_map('intval', explode(',', $completed_lesson_data)); + } + } + } + + // Hapus duplikat dan sortir + $completed_lessons = array_unique($completed_lessons); + sort($completed_lessons); + + // **HANYA tambahkan lesson jika project DITERIMA (status 1)** + if ($request->status == 1 && !in_array($lesson_id, $completed_lessons)) { + $completed_lessons[] = $lesson_id; + sort($completed_lessons); + } + + // Hitung completion dengan benar (seperti referensi) + $completed_in_course = array_intersect($completed_lessons, $total_lesson); + $is_completed = count($total_lesson) > 0 && + count($completed_in_course) >= count($total_lesson); + + $data = [ + 'course_id' => $course_id, + 'student_id' => $student_id, + 'watching_lesson_id' => $lesson_id, + 'completed_lesson' => json_encode($completed_lessons, JSON_NUMERIC_CHECK), // Gunakan JSON_NUMERIC_CHECK + 'completed_date' => $is_completed ? date('Y-m-d H:i:s') : null, // Format MySQL datetime + ]; + + // **FIX: Update dengan student_id yang benar (bukan auth user)** + Watch_history::where('course_id', $course_id) + ->where('student_id', $student_id) + ->update($data); + + // Log untuk debugging + \Log::info('Project submission watch history updated:', [ + 'project_submission_id' => $id, + 'student_id' => $student_id, + 'course_id' => $course_id, + 'lesson_id' => $lesson_id, + 'completed_lessons' => $completed_lessons, + 'is_completed' => $is_completed, + 'total_lessons_in_course' => count($total_lesson) + ]); + + } else { + // **Jika tidak ada watch_history, buat baru** + // Hanya buat jika project DITERIMA (status 1) + if ($request->status == 1) { + $completed_lessons = [$lesson_id]; + + // Hitung completion untuk data baru + $is_completed = count($total_lesson) == 1 && in_array($lesson_id, $total_lesson); + + $data = [ + 'course_id' => $course_id, + 'student_id' => $student_id, + 'watching_lesson_id' => $lesson_id, + 'completed_lesson' => json_encode($completed_lessons, JSON_NUMERIC_CHECK), + 'completed_date' => $is_completed ? date('Y-m-d H:i:s') : null, + 'created_at' => now(), + 'updated_at' => now(), + ]; + + Watch_history::create($data); + + \Log::info('New watch history created from project submission:', [ + 'project_submission_id' => $id, + 'student_id' => $student_id, + 'course_id' => $course_id, + 'lesson_id' => $lesson_id + ]); + } + } + + // **LOGIKA SERTIFIKAT - dengan progress yang benar** + $completed_lessons = $completed_lessons ?? []; + $completed_in_course = array_intersect($completed_lessons, $total_lesson); + $completed_count = count($completed_in_course); + $total_count = count($total_lesson); + + $progress_percentage = 0; + if ($total_count > 0) { + $progress_percentage = ($completed_count / $total_count) * 100; + } + + // Jika progress 100% dan status project diterima, buat sertifikat + if ($progress_percentage >= 100 && $request->status == 1) { + $certificateExists = Certificate::where('user_id', $student_id) + ->where('course_id', $course_id) + ->exists(); + + if (!$certificateExists) { + // Cek jika method generateIdentifier ada + if (method_exists($this, 'generateIdentifier')) { + $identifier = $this->generateIdentifier(12); + } else { + // Fallback ke random string + $identifier = \Illuminate\Support\Str::random(12); + } + + Certificate::create([ + 'user_id' => $student_id, + 'course_id' => $course_id, + 'identifier' => $identifier, + ]); + + \Log::info('Certificate created after project submission:', [ + 'student_id' => $student_id, + 'course_id' => $course_id, + 'project_submission_id' => $id + ]); + } + } + + return redirect()->back()->with('success', get_phrase('Project graded successfully')); + } +} diff --git a/app/Http/Controllers/instructor/QuizController.php b/app/Http/Controllers/instructor/QuizController.php index 00c8580..f1c02b2 100644 --- a/app/Http/Controllers/instructor/QuizController.php +++ b/app/Http/Controllers/instructor/QuizController.php @@ -56,8 +56,13 @@ class QuizController extends Controller return redirect()->back(); } + $maxSort = Lesson::where('section_id', $request->section) + ->max('sort'); + + $data['user_id'] = auth()->user()->id; + $data['sort'] = $maxSort + 1; $data['title'] = $request->title; - $data['course_id'] = $request->course_id; + $data['course_id'] = $request->course_id; $data['section_id'] = $request->section; $data['total_mark'] = $request->total_mark; $data['pass_mark'] = $request->pass_mark; diff --git a/app/Http/Controllers/student/ProjectController.php b/app/Http/Controllers/student/ProjectController.php new file mode 100644 index 0000000..47dc419 --- /dev/null +++ b/app/Http/Controllers/student/ProjectController.php @@ -0,0 +1,51 @@ +validate([ + 'lesson_id' => 'required|exists:lessons,id', + 'submission_link' => 'required|url', + ]); + + $userId = auth()->user()->id; + $lessonId = $request->lesson_id; + + $submission = ProjectSubmission::where('user_id', $userId) + ->where('lesson_id', $lessonId) + ->first(); + + if ($submission) { + $submission->drive_link = $request->submission_link; + $submission->status = 0; + $submission->submitted_at = Carbon::now(); + $submission->save(); + $message = get_phrase('Project successfully resubmitted.'); + + } else { + $submission = new ProjectSubmission(); + $submission->user_id = $userId; + $submission->lesson_id = $lessonId; + $submission->submitted_at = Carbon::now(); + $submission->drive_link = $request->submission_link; + $submission->status = 0; + $submission->save(); + + $message = get_phrase('Project successfully submitted.'); + } + + session()->flash('success', $message); + return redirect()->back(); + } +} diff --git a/app/Http/Controllers/student/QuizController.php b/app/Http/Controllers/student/QuizController.php index 5b04961..4ce1465 100644 --- a/app/Http/Controllers/student/QuizController.php +++ b/app/Http/Controllers/student/QuizController.php @@ -137,21 +137,24 @@ class QuizController extends Controller { // Validasi Course $course = Course::where('id', $course_id)->first(); - if (!$course) return; + if (!$course) return false; // Validasi Enrollment $enrollment = Enrollment::where('course_id', $course->id) ->where('user_id', $student_id) ->exists(); - // Cek Role (Jika admin/instruktur boleh bypass enrollment check) + // Cek Role $currentUserRole = auth()->check() ? auth()->user()->role : 'student'; if (!$enrollment && ($currentUserRole != 'admin' && !is_course_instructor($course->id))) { - return; + return false; } - // Ambil semua lesson ID untuk hitung progress manual - $total_lesson_ids = Lesson::where('course_id', $course_id)->pluck('id')->toArray(); + // Ambil semua lesson ID + $total_lesson_ids = Lesson::where('course_id', $course_id) + ->orderBy('id', 'asc') + ->pluck('id') + ->toArray(); // Ambil data history user $watch_history = Watch_history::where('course_id', $course_id) @@ -160,43 +163,72 @@ class QuizController extends Controller $completed_lessons = []; - // Parse data lama - if ($watch_history && $watch_history->completed_lesson) { - $decoded = json_decode($watch_history->completed_lesson, true); - if (is_array($decoded)) { + // Parse data lama dengan cara yang lebih aman + if ($watch_history && !empty($watch_history->completed_lesson)) { + $completed_lesson_data = $watch_history->completed_lesson; + + // Coba decode JSON + $decoded = json_decode($completed_lesson_data, true); + + if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) { $completed_lessons = $decoded; + } else { + // Jika bukan JSON valid, coba parsing manual + $completed_lesson_data = trim($completed_lesson_data, '[]"\' '); + if (!empty($completed_lesson_data)) { + $completed_lessons = array_map('intval', explode(',', $completed_lesson_data)); + $completed_lessons = array_unique($completed_lessons); // pastikan unik + } } } - // Tambahkan lesson_id baru jika belum ada - if (!in_array($lesson_id, $completed_lessons)) { - $completed_lessons[] = $lesson_id; + // HANYA tambahkan lesson saat ini jika belum ada + $lesson_id_int = (int)$lesson_id; + if (!in_array($lesson_id_int, $completed_lessons)) { + $completed_lessons[] = $lesson_id_int; } - // Tentukan apakah course selesai - $is_completed = count($total_lesson_ids) <= count($completed_lessons); + // Sort dan pastikan unique + sort($completed_lessons); + $completed_lessons = array_values(array_unique($completed_lessons)); + + // Course selesai jika semua lesson sudah completed + $all_lessons_completed = true; + foreach ($total_lesson_ids as $total_lesson_id) { + if (!in_array($total_lesson_id, $completed_lessons)) { + $all_lessons_completed = false; + break; + } + } + + $is_completed = $all_lessons_completed; $data = [ 'course_id' => $course_id, 'student_id' => $student_id, 'watching_lesson_id' => $lesson_id, 'completed_lesson' => json_encode($completed_lessons), - 'completed_date' => $is_completed ? time() : null, + 'completed_date' => $is_completed ? now() : null, ]; + // Debug final data + // dd($data); + // Simpan ke DB if ($watch_history) { - Watch_history::where('id', $watch_history->id)->update($data); + // Gunakan ini untuk memastikan hanya update yang diperlukan + $watch_history->update($data); } else { - // Gunakan create agar timestamp created_at terisi otomatis Watch_history::create($data); } - // --- LOGIKA SERTIFIKAT --- - // Hitung progress secara manual agar lebih akurat + // **LOGIKA SERTIFIKAT - dengan progress yang benar** + $completed_count = count(array_intersect($completed_lessons, $total_lesson_ids)); + $total_count = count($total_lesson_ids); + $progress_percentage = 0; - if (count($total_lesson_ids) > 0) { - $progress_percentage = (count($completed_lessons) / count($total_lesson_ids)) * 100; + if ($total_count > 0) { + $progress_percentage = ($completed_count / $total_count) * 100; } if ($progress_percentage >= 100) { diff --git a/app/Models/ProjectSubmission.php b/app/Models/ProjectSubmission.php new file mode 100644 index 0000000..b1b949e --- /dev/null +++ b/app/Models/ProjectSubmission.php @@ -0,0 +1,24 @@ +
Could not extract video ID from the provided URL.
+{{ get_phrase('Could not extract video ID from the provided URL.') }}
+ {{ get_phrase('Thank you! We have received your project and it is currently in the instructor\'s review queue.') }} +
+ +{{ get_phrase('The instructor has reviewed your project and provided feedback for improvement.') }}
+ ++ {{ get_phrase('Good work! Your project has met the assessment criteria.') }} +
+ @if($submission->comment) +