fix bug player course
This commit is contained in:
parent
e3458433f2
commit
e4ee4b3f72
@ -81,19 +81,6 @@ class CurriculumController extends Controller
|
||||
$data['video_type'] = $request->lesson_provider;
|
||||
$data['lesson_src'] = $request->lesson_src;
|
||||
|
||||
if (empty($request->duration)) {
|
||||
$data['duration'] = '00:00:00';
|
||||
} else {
|
||||
$duration_formatter = explode(':', $request->duration);
|
||||
$hour = sprintf('%02d', $duration_formatter[0]);
|
||||
$min = sprintf('%02d', $duration_formatter[1]);
|
||||
$sec = sprintf('%02d', $duration_formatter[2]);
|
||||
$data['duration'] = $hour . ':' . $min . ':' . $sec;
|
||||
}
|
||||
} elseif ($request->lesson_type == 'html5') {
|
||||
|
||||
$data['video_type'] = $request->lesson_provider;
|
||||
$data['lesson_src'] = $request->lesson_src;
|
||||
if (empty($request->duration)) {
|
||||
$data['duration'] = '00:00:00';
|
||||
} else {
|
||||
@ -121,39 +108,6 @@ class CurriculumController extends Controller
|
||||
}
|
||||
$data['attachment'] = $file;
|
||||
$data['attachment_type'] = $request->attachment_type;
|
||||
} elseif ($request->lesson_type == 'scorm') {
|
||||
if ($request->scorm_file == '') {
|
||||
$file = '';
|
||||
} else {
|
||||
$item = $request->file('scorm_file');
|
||||
$file_name = strtotime('now') . random(4) . '.' . $item->getClientOriginalExtension();
|
||||
|
||||
$upload_path = public_path('uploads/lesson_file/scorm_content');
|
||||
|
||||
if (! File::isDirectory($upload_path)) {
|
||||
File::makeDirectory($upload_path, 0777, true, true);
|
||||
}
|
||||
|
||||
$item->move($upload_path, $file_name);
|
||||
|
||||
$zip = new \ZipArchive();
|
||||
$zip_path = $upload_path . '/' . $file_name;
|
||||
$extract_path = $upload_path . '/' . pathinfo($file_name, PATHINFO_FILENAME);
|
||||
|
||||
if ($zip->open($zip_path) === true) {
|
||||
$zip->extractTo($extract_path);
|
||||
$zip->close();
|
||||
|
||||
File::delete($zip_path);
|
||||
} else {
|
||||
return response()->json(['error' => 'Failed to extract the SCORM file.'], 500);
|
||||
}
|
||||
|
||||
$file = pathinfo($file_name, PATHINFO_FILENAME);
|
||||
}
|
||||
|
||||
$data['attachment'] = $file;
|
||||
$data['attachment_type'] = $request->scorm_provider;
|
||||
} elseif ($request->lesson_type == 'image') {
|
||||
|
||||
if ($request->attachment == '') {
|
||||
@ -186,7 +140,6 @@ class CurriculumController extends Controller
|
||||
$data['duration'] = $hour . ':' . $min . ':' . $sec;
|
||||
}
|
||||
} elseif ($request->lesson_type == 'iframe') {
|
||||
|
||||
$data['lesson_src'] = $request->iframe_source;
|
||||
} elseif ($request->lesson_type == 'google_drive') {
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ use App\Models\Lesson;
|
||||
use App\Models\Watch_history;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class PlayerController extends Controller
|
||||
{
|
||||
@ -49,11 +50,12 @@ class PlayerController extends Controller
|
||||
//cek histori tontonan
|
||||
$check_lesson_history = Watch_history::where('course_id', $course->id)
|
||||
->where('student_id', auth()->user()->id)->first();
|
||||
$first_lesson_of_course = Lesson::where('course_id', $course->id)->orderBy('sort', 'asc')->value('id');
|
||||
$first_lesson_of_course = Lesson::where('course_id', $course->id)->orderBy('section_id', 'asc')->value('id');
|
||||
if ($id == '') {
|
||||
$id = $check_lesson_history->watching_lesson_id ?? $first_lesson_of_course;
|
||||
}
|
||||
|
||||
|
||||
// if user has any watched history or not
|
||||
if (! $check_lesson_history && $id > 0) {
|
||||
$data = [
|
||||
@ -110,14 +112,11 @@ class PlayerController extends Controller
|
||||
$watch_history = Watch_history::where('course_id', $request->course_id)
|
||||
->where('student_id', auth()->user()->id)->first();
|
||||
|
||||
|
||||
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);
|
||||
} else {
|
||||
while (($key = array_search($request->lesson_id, $lessons)) !== false) {
|
||||
unset($lessons[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$data['completed_lesson'] = json_encode($lessons);
|
||||
|
||||
@ -3,10 +3,13 @@
|
||||
namespace App\Http\Controllers\student;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Enrollment;
|
||||
use App\Models\Course;
|
||||
use App\Models\Lesson;
|
||||
use App\Models\Question;
|
||||
use App\Models\Watch_history;
|
||||
use App\Models\Certificate;
|
||||
use App\Models\QuizSubmission;
|
||||
use App\Models\Question;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
||||
@ -16,79 +19,107 @@ class QuizController extends Controller
|
||||
{
|
||||
$quiz = Lesson::findOrFail($request->quiz_id);
|
||||
|
||||
// 1. Cek Retake (Kesempatan mengulang)
|
||||
$retake = $quiz->retake;
|
||||
$submit = QuizSubmission::where('quiz_id', $quiz->id)
|
||||
$submit_count = QuizSubmission::where('quiz_id', $quiz->id)
|
||||
->where('user_id', auth()->user()->id)
|
||||
->count();
|
||||
|
||||
if ($submit >= $retake) {
|
||||
if ($submit_count >= $retake) {
|
||||
Session::flash('warning', get_phrase('Attempt has been over.'));
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
$inputs = collect($request->all());
|
||||
$quiz_id = $inputs->pull('quiz_id');
|
||||
$inputs->forget(['_token', 'quiz_id']);
|
||||
// 2. Rapikan Input Jawaban
|
||||
$inputs = $request->except(['_token', 'quiz_id']);
|
||||
$submits = [];
|
||||
|
||||
$submits = $inputs->whereNotNull();
|
||||
foreach ($submits as $key => $submit) {
|
||||
if (is_string($submit) && ($submit != 'true' && $submit != 'false')) {
|
||||
$submits[$key] = array_column(json_decode($submit), 'value');
|
||||
// Loop input untuk membersihkan format data (terutama jika dari JS frontend)
|
||||
foreach ($inputs as $question_id => $answer) {
|
||||
if ($answer !== null) {
|
||||
// Jika input berupa string JSON (kecuali 'true'/'false'), decode dulu
|
||||
if (is_string($answer) && !in_array($answer, ['true', 'false'])) {
|
||||
$decoded = json_decode($answer, true);
|
||||
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
|
||||
// Ambil value jika strukturnya array of objects
|
||||
$submits[$question_id] = array_column($decoded, 'value');
|
||||
} else {
|
||||
$submits[$question_id] = $answer;
|
||||
}
|
||||
} else {
|
||||
$submits[$question_id] = $answer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$question_ids = $submits->keys();
|
||||
$submitted_answers = $submits->values();
|
||||
// 3. Ambil Soal dari Database
|
||||
// Kita ambil soal berdasarkan key dari jawaban yang dikirim
|
||||
$question_ids = array_keys($submits);
|
||||
$questions = Question::whereIn('id', $question_ids)->get();
|
||||
|
||||
$right_answers = $wrong_answers = [];
|
||||
$right_answers = [];
|
||||
$wrong_answers = [];
|
||||
|
||||
foreach ($questions as $key => $question) {
|
||||
// 4. Proses Koreksi Jawaban
|
||||
foreach ($questions as $question) {
|
||||
// PENTING: Ambil jawaban user berdasarkan ID Soal, bukan urutan index loop
|
||||
if (!isset($submits[$question->id])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$submitted = $submits[$question->id];
|
||||
$correct_answer = json_decode($question->answer, true);
|
||||
$submitted = $submitted_answers[$key];
|
||||
$isCorrect = false;
|
||||
|
||||
// Logika per tipe soal
|
||||
if ($question->type == 'mcq') {
|
||||
// Cek array diff (bolak-balik) untuk memastikan isi array sama persis
|
||||
$isCorrect = empty(array_diff($correct_answer, $submitted))
|
||||
&& empty(array_diff($submitted, $correct_answer));
|
||||
|
||||
} elseif ($question->type == 'fill_blanks') {
|
||||
$isCorrect = count($correct_answer) === count($submitted);
|
||||
if ($isCorrect) {
|
||||
foreach ($correct_answer as $i => $answer) {
|
||||
if (strtolower($answer) !== strtolower($submitted[$i])) {
|
||||
if (count($correct_answer) === count($submitted)) {
|
||||
$isCorrect = true;
|
||||
foreach ($correct_answer as $i => $ans) {
|
||||
// Trim dan strtolower agar tidak case-sensitive
|
||||
if (trim(strtolower($ans)) !== trim(strtolower($submitted[$i]))) {
|
||||
$isCorrect = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} elseif ($question->type == 'true_false') {
|
||||
$isCorrect = strtolower(json_encode($correct_answer)) === strtolower($submitted);
|
||||
}
|
||||
|
||||
$isCorrect
|
||||
? $right_answers[] = $question->id
|
||||
: $wrong_answers[] = $question->id;
|
||||
// Masukkan ke bucket Benar/Salah
|
||||
if ($isCorrect) {
|
||||
$right_answers[] = $question->id;
|
||||
} else {
|
||||
$wrong_answers[] = $question->id;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Simpan Hasil Submission
|
||||
QuizSubmission::create([
|
||||
'quiz_id' => $quiz->id,
|
||||
'user_id' => auth()->user()->id,
|
||||
'correct_answer' => $right_answers ? json_encode($right_answers) : null,
|
||||
'wrong_answer' => $wrong_answers ? json_encode($wrong_answers) : null,
|
||||
'submits' => $submits->count() ? json_encode($submits->toArray()) : null,
|
||||
'correct_answer' => json_encode($right_answers),
|
||||
'wrong_answer' => json_encode($wrong_answers),
|
||||
'submits' => json_encode($submits),
|
||||
]);
|
||||
|
||||
// ✅ HITUNG NILAI
|
||||
// 6. Hitung Nilai
|
||||
$total_questions = $questions->count();
|
||||
$mark_per_question = $total_questions > 0
|
||||
? ($quiz->total_mark / $total_questions)
|
||||
: 0;
|
||||
|
||||
$mark_per_question = $total_questions > 0 ? ($quiz->total_mark / $total_questions) : 0;
|
||||
$obtained_mark = count($right_answers) * $mark_per_question;
|
||||
|
||||
// ✅ JIKA LULUS → MARK LESSON COMPLETED
|
||||
// 7. Cek Kelulusan
|
||||
if ($obtained_mark >= $quiz->pass_mark) {
|
||||
update_watch_history_manually(
|
||||
|
||||
// PANGGIL FUNGSI YANG BERADA DI CLASS YANG SAMA
|
||||
$this->update_watch_history2(
|
||||
$quiz->id,
|
||||
$quiz->course_id,
|
||||
auth()->user()->id
|
||||
@ -102,6 +133,89 @@ class QuizController extends Controller
|
||||
return redirect()->back();
|
||||
}
|
||||
|
||||
public function update_watch_history2($lesson_id, $course_id, $student_id)
|
||||
{
|
||||
// Validasi Course
|
||||
$course = Course::where('id', $course_id)->first();
|
||||
if (!$course) return;
|
||||
|
||||
// Validasi Enrollment
|
||||
$enrollment = Enrollment::where('course_id', $course->id)
|
||||
->where('user_id', $student_id)
|
||||
->exists();
|
||||
|
||||
// Cek Role (Jika admin/instruktur boleh bypass enrollment check)
|
||||
$currentUserRole = auth()->check() ? auth()->user()->role : 'student';
|
||||
if (!$enrollment && ($currentUserRole != 'admin' && !is_course_instructor($course->id))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ambil semua lesson ID untuk hitung progress manual
|
||||
$total_lesson_ids = Lesson::where('course_id', $course_id)->pluck('id')->toArray();
|
||||
|
||||
// Ambil data history user
|
||||
$watch_history = Watch_history::where('course_id', $course_id)
|
||||
->where('student_id', $student_id)
|
||||
->first();
|
||||
|
||||
$completed_lessons = [];
|
||||
|
||||
// Parse data lama
|
||||
if ($watch_history && $watch_history->completed_lesson) {
|
||||
$decoded = json_decode($watch_history->completed_lesson, true);
|
||||
if (is_array($decoded)) {
|
||||
$completed_lessons = $decoded;
|
||||
}
|
||||
}
|
||||
|
||||
// Tambahkan lesson_id baru jika belum ada
|
||||
if (!in_array($lesson_id, $completed_lessons)) {
|
||||
$completed_lessons[] = $lesson_id;
|
||||
}
|
||||
|
||||
// Tentukan apakah course selesai
|
||||
$is_completed = count($total_lesson_ids) <= count($completed_lessons);
|
||||
|
||||
$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,
|
||||
];
|
||||
|
||||
// Simpan ke DB
|
||||
if ($watch_history) {
|
||||
Watch_history::where('id', $watch_history->id)->update($data);
|
||||
} else {
|
||||
// Gunakan create agar timestamp created_at terisi otomatis
|
||||
Watch_history::create($data);
|
||||
}
|
||||
|
||||
// --- LOGIKA SERTIFIKAT ---
|
||||
// Hitung progress secara manual agar lebih akurat
|
||||
$progress_percentage = 0;
|
||||
if (count($total_lesson_ids) > 0) {
|
||||
$progress_percentage = (count($completed_lessons) / count($total_lesson_ids)) * 100;
|
||||
}
|
||||
|
||||
if ($progress_percentage >= 100) {
|
||||
$certificateExists = Certificate::where('user_id', $student_id)
|
||||
->where('course_id', $course_id)
|
||||
->exists();
|
||||
|
||||
if (!$certificateExists) {
|
||||
Certificate::create([
|
||||
'user_id' => $student_id,
|
||||
'course_id' => $course_id,
|
||||
'identifier' => substr(md5(uniqid(mt_rand(), true)), 0, 10), // Generate random ID
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function load_result(Request $request)
|
||||
{
|
||||
$page_data['quiz'] = Lesson::where('id', $request->quiz_id)->first();
|
||||
|
||||
@ -9,6 +9,13 @@ class Certificate extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'course_id',
|
||||
'identifier',
|
||||
'created_at'
|
||||
];
|
||||
|
||||
public function course()
|
||||
{
|
||||
return $this->belongsTo(Course::class);
|
||||
|
||||
@ -6,6 +6,8 @@
|
||||
@php
|
||||
$certificate = App\Models\Certificate::where('course_id', $course_details->id)->where('user_id', auth()->user()->id);
|
||||
@endphp
|
||||
<!-- @dump($certificate->count());
|
||||
@dump($course_progress_out_of_100); -->
|
||||
@if ($certificate->count() > 0 && $course_progress_out_of_100 >= 100)
|
||||
<div class="alert alert-success text-center" role="alert">
|
||||
<h4 class="alert-heading mb-4 mt-3">{{ get_phrase('Congratulations!') }}</h4>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<script>
|
||||
$(document).on('click', '.lesson-link', function (e) {
|
||||
e.preventDefault(); // ⛔ stop redirect sementara
|
||||
e.preventDefault();
|
||||
|
||||
let url = $(this).attr('href');
|
||||
let lessonId = $(this).data('lesson-id');
|
||||
@ -19,22 +19,53 @@ $(document).on('click', '.lesson-link', function (e) {
|
||||
success: function (response) {
|
||||
console.log('Watch history updated:', response);
|
||||
|
||||
// 🔥 optional: refresh sidebar sebelum pindah
|
||||
// Refresh sidebar jika di halaman course player
|
||||
if (typeof reloadSidebar === 'function') {
|
||||
reloadSidebar();
|
||||
}
|
||||
|
||||
// ⏩ lanjutkan navigasi
|
||||
setTimeout(function () {
|
||||
window.location.href = url;
|
||||
}, 200);
|
||||
},
|
||||
error: function () {
|
||||
// kalau gagal, tetap lanjutkan navigasi
|
||||
error: function (xhr, status, error) {
|
||||
console.error('AJAX Error:', error);
|
||||
window.location.href = url;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
// Hanya definisikan fungsi jika di halaman yang punya course_details
|
||||
@if(isset($course_details) && isset($lesson_details))
|
||||
function reloadSidebar() {
|
||||
let courseId = '{{ $course_details->id ?? 0 }}';
|
||||
let lessonId = '{{ $lesson_details->id ?? 0 }}';
|
||||
|
||||
// Pastikan lessonId valid
|
||||
if (!lessonId || lessonId == 0) {
|
||||
console.log('No lesson ID available for sidebar reload');
|
||||
return;
|
||||
}
|
||||
|
||||
let url = "{{ route('course.sidebar.reload', ['course_id' => '__COURSE__', 'lesson_id' => '__LESSON__']) }}";
|
||||
url = url.replace('__COURSE__', courseId)
|
||||
.replace('__LESSON__', lessonId);
|
||||
|
||||
$('#player_side_bar').load(url, function () {
|
||||
console.log('Sidebar updated');
|
||||
});
|
||||
}
|
||||
@else
|
||||
// Placeholder function untuk halaman lain
|
||||
function reloadSidebar() {
|
||||
console.log('Sidebar reload not available on this page');
|
||||
}
|
||||
@endif
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
|
||||
@ -90,6 +90,7 @@
|
||||
->first();
|
||||
|
||||
$lesson = App\Models\Lesson::where('course_id', $course->course_id)
|
||||
->orderBy('section_id', 'asc')
|
||||
->orderBy('sort', 'asc')
|
||||
->first();
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user