pembenaran update projek
This commit is contained in:
parent
301fe0a3c4
commit
e0aefa4eb6
@ -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 {
|
||||
$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);
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (progress_bar($request->course_id) >= 100) {
|
||||
$certificate = Certificate::where('user_id', auth()->user()->id)->where('course_id', $request->course_id);
|
||||
$completed_lessons = array_unique($completed_lessons);
|
||||
sort($completed_lessons);
|
||||
|
||||
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);
|
||||
$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 {
|
||||
$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 (!$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();
|
||||
}
|
||||
|
||||
|
||||
@ -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,38 +135,65 @@ 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 (!empty($dripContentSettings) && isset($dripContentSettings['lesson_completion_role'])) {
|
||||
if ($dripContentSettings['lesson_completion_role'] == 'duration') {
|
||||
if ($currentTotalSeconds >= $dripContentSettings['minimum_duration']) {
|
||||
$minimumDuration = $dripContentSettings['minimum_duration'] ?? 0;
|
||||
if ($currentTotalSeconds >= $minimumDuration) {
|
||||
$isCompleted = 1;
|
||||
} elseif (($currentTotalSeconds + 4) >= $lessonTotalSeconds) {
|
||||
$isCompleted = 1;
|
||||
}
|
||||
} else {
|
||||
$requiredDuration = ($lessonTotalSeconds / 100) * $dripContentSettings['minimum_percentage'];
|
||||
$minimumPercentage = $dripContentSettings['minimum_percentage'] ?? 0;
|
||||
$requiredDuration = ($lessonTotalSeconds / 100) * $minimumPercentage;
|
||||
if ($currentTotalSeconds >= $requiredDuration) {
|
||||
$isCompleted = 1;
|
||||
} elseif (($currentTotalSeconds + 4) >= $lessonTotalSeconds) {
|
||||
$isCompleted = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update course progress if the lesson is completed
|
||||
if ($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);
|
||||
$totalLesson = DB::table('lessons')->where('course_id', $courseId)->count();
|
||||
$courseProgress = (100 / $totalLesson) * count($lessonIds);
|
||||
// Tambahkan lesson baru
|
||||
$lessonIds[] = $lessonId;
|
||||
$lessonIds = array_unique($lessonIds);
|
||||
sort($lessonIds);
|
||||
|
||||
$completedDate = ($courseProgress >= 100 && !$watchHistory->completed_date)
|
||||
? time()
|
||||
: $watchHistory->completed_date;
|
||||
// Hitung total lesson yang valid di course
|
||||
$totalLesson = DB::table('lessons')->where('course_id', $courseId)->count();
|
||||
|
||||
// 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,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
309
app/Http/Controllers/instructor/ProjectController.php
Normal file
309
app/Http/Controllers/instructor/ProjectController.php
Normal file
@ -0,0 +1,309 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\instructor;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Lesson;
|
||||
use App\Models\ProjectSubmission;
|
||||
use App\Models\Watch_history;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class ProjectController extends Controller
|
||||
{
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$request->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 = '<option value="">' . get_phrase('Select a submission') . '</option>';
|
||||
|
||||
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 .= '<option value="' . $submission->id . '">' . $date . $statusLabel . '</option>';
|
||||
}
|
||||
} else {
|
||||
$html .= '<option value="" disabled>' . get_phrase('No submission found') . '</option>';
|
||||
}
|
||||
|
||||
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'));
|
||||
}
|
||||
}
|
||||
@ -56,6 +56,11 @@ 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['section_id'] = $request->section;
|
||||
|
||||
51
app/Http/Controllers/student/ProjectController.php
Normal file
51
app/Http/Controllers/student/ProjectController.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\student;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Lesson;
|
||||
use App\Models\ProjectSubmission;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Carbon\Carbon;
|
||||
|
||||
class ProjectController extends Controller
|
||||
{
|
||||
public function submit(Request $request)
|
||||
{
|
||||
$request->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();
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
24
app/Models/ProjectSubmission.php
Normal file
24
app/Models/ProjectSubmission.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ProjectSubmission extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'lesson_id',
|
||||
'user_id',
|
||||
'drive_link',
|
||||
'status',
|
||||
'comment',
|
||||
'reviewed_at',
|
||||
'resubmitted_at',
|
||||
'reviewed_by',
|
||||
'created_at',
|
||||
'updated_at'
|
||||
];
|
||||
}
|
||||
@ -47,7 +47,7 @@
|
||||
<section class="video-playlist-section">
|
||||
<div class="my-container">
|
||||
<div class="row">
|
||||
<div class="col-lg-8" id="player_content">
|
||||
<div class="col-lg-8" id="player_content"> -->
|
||||
@if(in_array($lesson_details->id, get_locked_lesson_ids($course_details->id, auth()->user()->id)) && $course_details->enable_drip_content)
|
||||
@php
|
||||
$drip_content_settings = json_decode($course_details->drip_content_settings);
|
||||
|
||||
@ -40,8 +40,8 @@
|
||||
@include('course_player.player_config')
|
||||
@else
|
||||
<div class="alert alert-danger text-center py-5">
|
||||
<h5>Invalid Google Drive Link</h5>
|
||||
<p>Could not extract video ID from the provided URL.</p>
|
||||
<h5>{{ get_phrase('Invalid Google Drive Link') }}</h5>
|
||||
<p>{{ get_phrase('Could not extract video ID from the provided URL.') }}</p>
|
||||
</div>
|
||||
@endif
|
||||
@elseif($lesson_details->lesson_type == 'image')
|
||||
@ -62,7 +62,7 @@
|
||||
<div id="{{ $fullscreen_id }}" class="w-100 border rounded overflow-hidden bg-light">
|
||||
@if ($lesson_details->attachment_type == 'pdf')
|
||||
<a href="{{ $direct_pdf }}" target="_blank" class="btn btn-sm position-absolute top-0 end-0 mt-2 me-2 shadow" style="z-index: 1050; color: white; background: #001151;" title="Buka PDF di Tab Baru (tekan F11 untuk full screen)">
|
||||
<i class="bi bi-box-arrow-up-right"></i> Buka Full PDF
|
||||
<i class="bi bi-box-arrow-up-right"></i> {{ get_phrase('Open Full PDF') }}
|
||||
</a>
|
||||
<iframe src="{{ $pdf_src }}#toolbar=1&navpanes=0&scrollbar=1&view=FitH"
|
||||
class="w-100 border-0"
|
||||
@ -82,10 +82,10 @@
|
||||
|
||||
<div class="text-center mb-3">
|
||||
<p class="mt-3 mb-1 fw-bold">
|
||||
Dokumen tidak dapat ditampilkan secara langsung
|
||||
{{ get_phrase('The document cannot be displayed directly.') }}
|
||||
</p>
|
||||
<p class="text-muted small">
|
||||
Silakan unduh file untuk melihat isi dokumen
|
||||
{{ get_phrase('Please download the file to view the contents of the document.') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -94,7 +94,7 @@
|
||||
target="_blank"
|
||||
download>
|
||||
<i class="fa fa-download me-1"></i>
|
||||
Download Dokumen
|
||||
{{ get_phrase('Download Document') }}
|
||||
</a>
|
||||
</div>
|
||||
@endif
|
||||
@ -104,6 +104,10 @@
|
||||
<div class="course-video-area border-primary pb-5">
|
||||
@include('course_player.quiz.index')
|
||||
</div>
|
||||
@elseif($lesson_details->lesson_type == 'project')
|
||||
<div class="course-video-area border-primary pb-5">
|
||||
@include('course_player.project.index')
|
||||
</div>
|
||||
@else
|
||||
<iframe class="embed-responsive-item" width="100%" src="{{ $lesson_details->lesson_src }}" allowfullscreen></iframe>
|
||||
@endif
|
||||
|
||||
163
resources/views/course_player/project/index.blade.php
Normal file
163
resources/views/course_player/project/index.blade.php
Normal file
@ -0,0 +1,163 @@
|
||||
@php
|
||||
$project = App\Models\Lesson::where('id', request()->route()->parameter('id'))->firstOrFail();
|
||||
|
||||
$submission = DB::table('project_submissions')
|
||||
->where('lesson_id', $project->id)
|
||||
->where('user_id', auth()->id())
|
||||
->first();
|
||||
@endphp
|
||||
|
||||
<style>
|
||||
/* Mengatur agar list dan blockquote tidak memakan banyak ruang */
|
||||
.description blockquote{
|
||||
margin: 5px 0 !important;
|
||||
padding: 0 10px !important;
|
||||
}
|
||||
|
||||
.description ul, ul {
|
||||
list-style-type: disc !important;
|
||||
list-style-position: inside !important;
|
||||
margin-left: 10px !important;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.description li {
|
||||
display: list-item !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="row px-4">
|
||||
<div class="col-12">
|
||||
<h4 class="quiz-title text-center mt-4 fw-bold">
|
||||
<i class="fas fa-project-diagram me-2"></i> {{ $project->title }}
|
||||
</h4>
|
||||
<hr>
|
||||
|
||||
<div class="row mb-5">
|
||||
<div class="col-12 mb-2">
|
||||
<div class="card shadow-sm border-0">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-primary mb-3">
|
||||
<i class="fas fa-file-alt me-2"></i>{{ get_phrase('Project Details') }}
|
||||
</h5>
|
||||
<div class="description text-secondary">
|
||||
{!! $project->description !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<div class="card shadow-sm border-0 bg-light">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-danger mb-3">
|
||||
<i class="fas fa-clipboard-check me-2"></i>{{ get_phrase('Assessment criteria') }}
|
||||
</h5>
|
||||
<div class="description small text-muted">
|
||||
{!! $project->attachment !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="submission-area p-4 border rounded mb-5">
|
||||
<h5 class="mb-3">{{ get_phrase('Submit Assignment') }}</h5>
|
||||
|
||||
{{-- KONDISI 1: JIKA BELUM PERNAH SUBMIT --}}
|
||||
@if(!$submission)
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle me-1"></i> {{ get_phrase('Please enter your project link below to be assessed.') }}
|
||||
</div>
|
||||
|
||||
<form action="{{ route('student.project.submit') }}" method="POST">
|
||||
@csrf
|
||||
<input type="hidden" name="lesson_id" value="{{ $project->id }}">
|
||||
<div class="mb-3">
|
||||
<label for="project_link" class="form-label fw-bold">{{ get_phrase('Project Link (Google Drive/Github/Website)') }}</label>
|
||||
<input type="url" class="form-control" id="project_link" name="submission_link" placeholder="https://..." required>
|
||||
<div class="form-text">{{ get_phrase('Make sure the link is accessible to the instructor (Public/Anyone with link).') }}</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="eBtn gradient border-0">
|
||||
<i class="fas fa-paper-plane me-1"></i> {{ get_phrase('Submit Project') }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{{-- KONDISI 2: JIKA SUDAH PERNAH SUBMIT --}}
|
||||
@else
|
||||
|
||||
{{-- STATUS 0: MENUNGGU DIPERIKSA --}}
|
||||
@if($submission->status == 0)
|
||||
<div class="text-center p-5 border rounded bg-light">
|
||||
<i class="fas fa-clock fa-3x text-warning mb-3"></i>
|
||||
<h5 class="fw-bold">{{ get_phrase('Project is Being Reviewed') }}</h5>
|
||||
<p class="text-muted">
|
||||
{{ get_phrase('Thank you! We have received your project and it is currently in the instructor\'s review queue.') }}
|
||||
</p>
|
||||
<div class="mt-3">
|
||||
<span class="text-secondary small">{{ get_phrase('Link that you submitted :') }} </span><br>
|
||||
<a href="{{ $submission->drive_link }}" target="_blank" class="eBtn gradient border-0 btn-sm mt-1">
|
||||
<i class="fas fa-external-link-alt me-1"></i> {{ get_phrase('View Your Project') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- STATUS 2: PERLU REVISI --}}
|
||||
@elseif($submission->status == 2)
|
||||
<div class="alert alert-danger border-danger">
|
||||
<h5 class="alert-heading fw-bold">
|
||||
<i class="fas fa-exclamation-circle me-2"></i>{{ get_phrase('Project Needs Revision') }}
|
||||
</h5>
|
||||
<p>{{ get_phrase('The instructor has reviewed your project and provided feedback for improvement.') }}</p>
|
||||
|
||||
<hr>
|
||||
|
||||
{{-- Menampilkan Komentar Instruktur --}}
|
||||
<div class="mb-3">
|
||||
<label class="fw-bold text-danger">{{ get_phrase('Instructor\'s Notes / Feedback:') }}</label>
|
||||
<div class="bg-white p-3 rounded border border-danger text-dark mt-1">
|
||||
{!! $submission->comment !!}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Form Resubmit --}}
|
||||
<div class="mt-4">
|
||||
<h6 class="fw-bold">{{ get_phrase('Submit Revision:') }}</h6>
|
||||
<form action="{{ route('student.project.submit') }}" method="POST">
|
||||
@csrf
|
||||
<input type="hidden" name="lesson_id" value="{{ $project->id }}">
|
||||
|
||||
<div class="input-group">
|
||||
{{-- PERBAIKAN: name diubah jadi submission_link --}}
|
||||
<input type="url" class="form-control" name="submission_link" placeholder="Masukkan link projek yang sudah diperbaiki..." required>
|
||||
|
||||
<button class="btn btn-danger" type="submit">
|
||||
<i class="fas fa-redo me-1"></i> {{ get_phrase('Submit Revisions') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="form-text text-danger">{{ get_phrase('Make sure the new link includes the requested fix.') }} </div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- STATUS 1: DITERIMA / LULUS --}}
|
||||
@elseif($submission->status == 1)
|
||||
<div class="text-center p-5 border rounded bg-white border-success">
|
||||
<i class="fas fa-check-circle fa-3x text-success mb-3"></i>
|
||||
<h5 class="fw-bold text-success">{{ get_phrase('Congratulations! Project Accepted') }}</h5>
|
||||
<p class="text-muted">
|
||||
{{ get_phrase('Good work! Your project has met the assessment criteria.') }}
|
||||
</p>
|
||||
@if($submission->comment)
|
||||
<div class="alert alert-success d-inline-block text-start mt-2">
|
||||
<strong>{{ get_phrase('Instructor\'s Comment:') }}</strong> <br> {{ $submission->comment }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -9,7 +9,10 @@
|
||||
|
||||
<a href="#" onclick="ajaxModal('{{ route('modal', ['instructor.quiz.create', 'id' => $course_details->id]) }}', '{{ get_phrase('Add new quiz') }}')" class="btn ol-btn-light ol-btn-sm">{{ get_phrase('Add quiz') }}</a>
|
||||
|
||||
<a href="#" onclick="ajaxModal('{{ route('modal', ['instructor.project.create', 'id' => $course_details->id]) }}', '{{ get_phrase('Add new project') }}')" class="btn ol-btn-light ol-btn-sm">{{ get_phrase('Add project') }}</a>
|
||||
|
||||
<a href="#" onclick="ajaxModal('{{ route('modal', ['instructor.course.section_sort', 'id' => $course_details->id]) }}', '{{ get_phrase('Sort sections') }}')" class="btn ol-btn-light ol-btn-sm">{{ get_phrase('Sort Section') }}</a>
|
||||
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@ -58,7 +61,7 @@
|
||||
<div class="buttons">
|
||||
@if ($lesson->lesson_type == 'quiz')
|
||||
<a href="#" data-bs-toggle="tooltip" title="{{ get_phrase('Result') }}" onclick="ajaxModal('{{ route('modal', ['instructor.quiz_result.index', 'id' => $lesson->id]) }}', '{{ get_phrase('Result') }}', 'modal-xl')" class="edit-delete">
|
||||
<span class="fi fi-rr-clipboard-list-check"></span>
|
||||
<span class="fi fi-rr-paperclip-vertical"></span>
|
||||
</a>
|
||||
|
||||
<a href="#" data-bs-toggle="tooltip" title="{{ get_phrase('Questions') }}" onclick="ajaxModal('{{ route('modal', ['instructor.questions.index', 'id' => $lesson->id]) }}', '{{ get_phrase('Questions') }}', 'modal-lg')" class="edit-delete">
|
||||
@ -68,9 +71,20 @@
|
||||
<a href="#" data-bs-toggle="tooltip" title="{{ get_phrase('Edit quiz') }}" onclick="ajaxModal('{{ route('modal', ['instructor.quiz.edit', 'id' => $lesson->id]) }}', '{{ get_phrase('Edit quiz') }}')" class="edit-delete">
|
||||
<span class="fi-rr-pencil"></span>
|
||||
</a>
|
||||
@elseif ($lesson->lesson_type == 'project')
|
||||
<a href="javascript:void(0);"
|
||||
data-bs-toggle="tooltip"
|
||||
title="{{ get_phrase('Submissions') }}"
|
||||
onclick="ajaxModal('{{ route('instructor.project.grading.index', $lesson->id) }}', '{{ get_phrase('Submissions') }}', 'modal-xl')"
|
||||
class="edit-delete">
|
||||
<span class="fi fi-rr-clipboard-list"></span>
|
||||
</a>
|
||||
<a href="#" data-bs-toggle="tooltip" title="{{ get_phrase('Edit project') }}" onclick="ajaxModal('{{ route('modal', ['instructor.project.edit', 'id' => $lesson->id]) }}', '{{ get_phrase('Edit project') }}')" class="edit-delete">
|
||||
<span class="fi-rr-pencil"></span>
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@if ($lesson->lesson_type != 'quiz')
|
||||
@if ($lesson->lesson_type != 'quiz' && $lesson->lesson_type != 'project')
|
||||
<a href="#" data-bs-toggle="tooltip" title="{{ get_phrase('Edit lesson') }}" onclick="ajaxModal('{{ route('modal', ['instructor.course.lesson_edit', 'id' => $lesson->id]) }}', '{{ get_phrase('Edit lesson') }}')" class="edit-delete">
|
||||
<span class="fi-rr-pencil"></span>
|
||||
</a>
|
||||
|
||||
@ -14,8 +14,6 @@
|
||||
{{ get_phrase('Google drive video') }}
|
||||
@elseif($lesson_type == 'document')
|
||||
{{ get_phrase('Document file') }}
|
||||
@elseif ($lesson_type == 'scorm')
|
||||
{{ get_phrase('Scorm content') }}
|
||||
@else
|
||||
{{ ucfirst($lesson_type) }}
|
||||
@endif
|
||||
@ -47,14 +45,10 @@
|
||||
@include('instructor.course.youtube_type_lesson_add')
|
||||
@elseif ($lesson_type == 'academy_cloud')
|
||||
@include('instructor.course.academy_cloud_type_lesson_add')
|
||||
@elseif ($lesson_type == 'vimeo')
|
||||
@include('instructor.course.vimeo_type_lesson_add')
|
||||
@elseif ($lesson_type == 'html5')
|
||||
@include('instructor.course.html5_type_lesson_add')
|
||||
@elseif ($lesson_type == 'video')
|
||||
@include('instructor.course.video_type_lesson_add')
|
||||
@elseif ($lesson_type == 'amazon-s3')
|
||||
@include('amazon_s3_type_lesson_add.php')
|
||||
@elseif ($lesson_type == 'google_drive_video')
|
||||
@include('instructor.course.google_drive_type_lesson_add')
|
||||
@elseif ($lesson_type == 'document')
|
||||
@ -65,8 +59,6 @@
|
||||
@include('instructor.course.image_file_type_lesson_add')
|
||||
@elseif ($lesson_type == 'iframe')
|
||||
@include('instructor.course.iframe_type_lesson_add')
|
||||
@elseif ($lesson_type == 'scorm')
|
||||
@include('instructor.course.scorm_type_lesson_add')
|
||||
@endif
|
||||
|
||||
<div class="form-group mb-3">
|
||||
|
||||
50
resources/views/instructor/project/create.blade.php
Normal file
50
resources/views/instructor/project/create.blade.php
Normal file
@ -0,0 +1,50 @@
|
||||
<form action="{{ route('instructor.course.project.store') }}" method="post">@csrf
|
||||
|
||||
<input type="hidden" name="course_id" value="{{ $id }}">
|
||||
<div class="fpb7 mb-3">
|
||||
<label class="form-label ol-form-label" for="title">
|
||||
{{ get_phrase('Title') }}
|
||||
<span class="text-danger ms-1">*</span>
|
||||
</label>
|
||||
<input class="form-control ol-form-control" type="text" id="title" name="title" required>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm-12 fpb-7">
|
||||
<label class="form-label ol-form-label">
|
||||
{{ get_phrase('Section') }}
|
||||
<span class="text-danger ms-1">*</span>
|
||||
</label>
|
||||
<select class="form-control ol-form-control ol-select2" data-toggle="select2" name="section">
|
||||
<option value="">{{ get_phrase('Select an option') }}</option>
|
||||
@foreach (App\Models\Section::where('course_id', $id)->get() as $section)
|
||||
<option value="{{ $section->id }}">{{ $section->title }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fpb-7 mb-3">
|
||||
<label for="description"
|
||||
class="form-label ol-form-label col-form-label">{{ get_phrase('Project Assignment') }}</label>
|
||||
<textarea name="description" rows="5" class="form-control ol-form-control text_editor"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="fpb-7 mb-3">
|
||||
<label for="attachment"
|
||||
class="form-label ol-form-label col-form-label">{{ get_phrase('Assessment Criteria') }}</label>
|
||||
<textarea name="attachment" rows="5" class="form-control ol-form-control text_editor"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="fpb-7 mb-6">
|
||||
<label for="attachment"
|
||||
class="form-label ol-form-label col-form-label">{{ get_phrase('Summary') }}</label>
|
||||
<textarea name="attachment" rows="5" class="form-control ol-form-control text_editor"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="fpb7">
|
||||
<button type="submit" class="btn ol-btn-primary">{{ get_phrase('Add Project') }}</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@include('instructor.init')
|
||||
58
resources/views/instructor/project/edit.blade.php
Normal file
58
resources/views/instructor/project/edit.blade.php
Normal file
@ -0,0 +1,58 @@
|
||||
@php
|
||||
$project = App\Models\Lesson::join('sections', 'lessons.section_id', 'sections.id')
|
||||
->join('courses', 'sections.course_id', 'courses.id')
|
||||
->select('lessons.*', 'courses.id as course_id')
|
||||
->where('lessons.id', $id)
|
||||
->first();
|
||||
@endphp
|
||||
|
||||
<form action="{{ route('instructor.course.project.update', $id) }}" method="post">@csrf
|
||||
<div class="fpb7 mb-3">
|
||||
<label class="form-label ol-form-label" for="title">
|
||||
{{ get_phrase('Title') }}
|
||||
<span class="text-danger ms-1">*</span>
|
||||
</label>
|
||||
<input class="form-control ol-form-control" type="text" id="title" name="title" value="{{ $project->title }}"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm-12 fpb-7">
|
||||
<label class="form-label ol-form-label">
|
||||
{{ get_phrase('Section') }}
|
||||
<span class="text-danger ms-1">*</span>
|
||||
</label>
|
||||
<select class="form-control ol-form-control ol-select2" data-toggle="select2" name="section">
|
||||
<option value="">{{ get_phrase('Select an option') }}</option>
|
||||
@foreach (App\Models\Section::where('course_id', $project->course_id)->get() as $section)
|
||||
<option value="{{ $section->id }}" @if ($section->id == $project->section_id) selected @endif>
|
||||
{{ $section->title }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fpb-7 mb-3">
|
||||
<label for="description"
|
||||
class="form-label ol-form-label col-form-label">{{ get_phrase('Project Assignment') }}</label>
|
||||
<textarea name="description" rows="5" class="form-control ol-form-control text_editor">{!! $project->description !!}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="fpb-7 mb-3">
|
||||
<label for="attachment"
|
||||
class="form-label ol-form-label col-form-label">{{ get_phrase('Assessment Criteria') }}</label>
|
||||
<textarea name="attachment" rows="5" class="form-control ol-form-control text_editor">{!! $project->attachment !!}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="fpb-7 mb-6">
|
||||
<label for="summary"
|
||||
class="form-label ol-form-label col-form-label">{{ get_phrase('Summary') }}</label>
|
||||
<textarea name="summary" rows="5" class="form-control ol-form-control text_editor">{!! $project->summary !!}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="fpb7">
|
||||
<button type="submit" class="btn ol-btn-primary">{{ get_phrase('Update Project') }}</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@include('instructor.init')
|
||||
121
resources/views/instructor/project/grading/index.blade.php
Normal file
121
resources/views/instructor/project/grading/index.blade.php
Normal file
@ -0,0 +1,121 @@
|
||||
{{-- Container Utama --}}
|
||||
<div class="row g-0 h-100">
|
||||
|
||||
{{-- KOLOM KIRI: LIST SISWA --}}
|
||||
<div class="col-md-4 border-end" style="min-height: 400px; max-height: 70vh; overflow-y: auto;">
|
||||
<div class="p-3 border-bottom bg-light sticky-top">
|
||||
<h6 class="mb-0 fw-bold"><i class="fas fa-users me-2"></i> {{ get_phrase('Student List') }}</h6>
|
||||
</div>
|
||||
|
||||
<div class="list-group list-group-flush" id="studentList">
|
||||
@if($participants->isEmpty())
|
||||
<div class="text-center p-5 text-muted small">
|
||||
{{ get_phrase('No participants found.') }}
|
||||
</div>
|
||||
@else
|
||||
@foreach ($participants as $participant)
|
||||
<a href="javascript:void(0);"
|
||||
class="list-group-item list-group-item-action py-3 student-item"
|
||||
{{-- PERBAIKAN DISINI: Tambahkan parameter ke-3 yaitu $lesson_id --}}
|
||||
onclick="loadStudentSubmission(this, '{{ $participant->id }}', '{{ $lesson_id }}')">
|
||||
|
||||
<div class="d-flex align-items-center">
|
||||
<div class="me-3 d-flex align-items-center justify-content-center rounded-circle bg-primary text-white fw-bold"
|
||||
style="width: 44px; height: 36px; font-size: 14px;">
|
||||
{{ strtoupper(substr($participant->name, 0, 1)) }}
|
||||
</div>
|
||||
|
||||
<div class="w-100">
|
||||
<h6 class="mb-0 text-dark" style="font-size: 14px;">{{ $participant->name }}</h6>
|
||||
<small class="text-muted" style="font-size: 12px;">{{ get_phrase('Click to grade') }}</small>
|
||||
</div>
|
||||
<i class="fas fa-chevron-right text-muted small"></i>
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- KOLOM KANAN: AREA PREVIEW & GRADING --}}
|
||||
<div class="col-md-8 bg-white">
|
||||
{{-- ID ini penting: target AJAX --}}
|
||||
<div id="grading-area" class="h-100 position-relative">
|
||||
|
||||
{{-- State Awal (Belum Klik) --}}
|
||||
<div class="d-flex flex-column align-items-center justify-content-center h-100 text-center p-5">
|
||||
<div class="mb-3 p-4 rounded-circle bg-light">
|
||||
<i class="fi fi-rr-box-open fs-1 text-secondary opacity-50"></i>
|
||||
</div>
|
||||
<h6 class="text-muted">{{ get_phrase('Select a student to start grading') }}</h6>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
function loadStudentSubmission(element, userId, lessonId) {
|
||||
// Cegah event default
|
||||
event.preventDefault();
|
||||
|
||||
// Validasi parameter
|
||||
if (!userId || !lessonId) {
|
||||
console.error("Missing parameters. User ID:", userId, "Lesson ID:", lessonId);
|
||||
alert('Missing parameters');
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Visual Active State
|
||||
$('.student-item').removeClass('active bg-light');
|
||||
$(element).addClass('active bg-light');
|
||||
|
||||
// 2. Loading State
|
||||
$('#grading-area').html(`
|
||||
<div class="d-flex flex-column align-items-center justify-content-center h-100">
|
||||
<div class="spinner-border text-primary mb-2" role="status"></div>
|
||||
<small class="text-muted">Loading data...</small>
|
||||
</div>
|
||||
`);
|
||||
|
||||
// Debugging
|
||||
console.log("AJAX Call Parameters:");
|
||||
console.log("- Lesson ID:", lessonId);
|
||||
console.log("- User ID:", userId);
|
||||
console.log("- URL:", "{{ route('instructor.project.grading.preview') }}");
|
||||
console.log("- CSRF Token:", $('meta[name="csrf-token"]').attr('content'));
|
||||
|
||||
// 3. AJAX Call
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: "{{ route('instructor.project.grading.preview') }}",
|
||||
data: {
|
||||
lesson_id: lessonId,
|
||||
user_id: userId
|
||||
},
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
|
||||
},
|
||||
success: function(response) {
|
||||
console.log("Success Response:", response.substring(0, 200)); // Log 200 karakter pertama
|
||||
$('#grading-area').html(response);
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error("AJAX Error:", error);
|
||||
console.error("Status:", status);
|
||||
console.error("Response:", xhr.responseText);
|
||||
|
||||
$('#grading-area').html(`
|
||||
<div class="alert alert-danger m-3">
|
||||
<h6>Failed to load data</h6>
|
||||
<small>Error: ${error}</small>
|
||||
<br>
|
||||
<small>Check console for details</small>
|
||||
</div>
|
||||
`);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
182
resources/views/instructor/project/grading/preview.blade.php
Normal file
182
resources/views/instructor/project/grading/preview.blade.php
Normal file
@ -0,0 +1,182 @@
|
||||
{{-- HAPUS <div class="row"> jika ada, gunakan div biasa --}}
|
||||
<div class="h-100">
|
||||
|
||||
{{-- KONDISI 1: Belum Submit --}}
|
||||
@if(!$submission)
|
||||
<div class="d-flex flex-column align-items-center justify-content-center h-100 text-center p-5">
|
||||
<div class="mb-3 p-3 bg-light rounded-circle">
|
||||
<i class="fas fa-times text-danger fa-2x"></i>
|
||||
</div>
|
||||
<h5>{{ get_phrase('Not Submitted') }}</h5>
|
||||
<p class="text-muted">{{ get_phrase('This student has not submitted the project yet.') }}</p>
|
||||
</div>
|
||||
|
||||
{{-- KONDISI 2: Sudah Submit --}}
|
||||
@else
|
||||
<div class="p-4">
|
||||
{{-- Header Status --}}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4 pb-3 border-bottom">
|
||||
<div>
|
||||
<h5 class="fw-bold mb-1">{{ get_phrase('Project Submission') }}</h5>
|
||||
<small class="text-muted">
|
||||
<i class="far fa-clock me-1"></i>
|
||||
{{ date('d M Y, H:i', strtotime($submission->created_at)) }}
|
||||
</small>
|
||||
</div>
|
||||
<div>
|
||||
@if($submission->status == 1)
|
||||
<span class="badge bg-success px-3 py-2">{{ get_phrase('Accepted') }}</span>
|
||||
@elseif($submission->status == 2)
|
||||
<span class="badge bg-danger px-3 py-2">{{ get_phrase('Revision Needed') }}</span>
|
||||
@else
|
||||
<span class="badge bg-warning text-dark px-3 py-2">{{ get_phrase('Pending Review') }}</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Link Project --}}
|
||||
<div class="mb-4">
|
||||
<label class="fw-bold mb-2">{{ get_phrase('Student Project Link') }}</label>
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control bg-light" value="{{ $submission->drive_link }}" readonly>
|
||||
<a href="{{ $submission->drive_link }}" target="_blank" class="btn btn-primary">
|
||||
<i class="fas fa-external-link-alt"></i> {{ get_phrase('Open') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Form Grading --}}
|
||||
<div class="card border bg-light shadow-none">
|
||||
<div class="card-body">
|
||||
<form action="{{ route('instructor.project.grading.update', $submission->id) }}" method="post" id="gradingForm">
|
||||
@csrf
|
||||
<h6 class="fw-bold mb-3"><i class="fas fa-check-double me-2"></i>{{ get_phrase('Grading') }}</h6>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="btn-group w-100" role="group">
|
||||
<input type="radio" class="btn-check" name="status" id="accept_{{ $submission->id }}" value="1"
|
||||
{{ $submission->status == 1 ? 'checked' : '' }} onchange="toggleComment(this)">
|
||||
<label class="btn btn-outline-success" for="accept_{{ $submission->id }}">{{ get_phrase('Passed') }}</label>
|
||||
|
||||
<input type="radio" class="btn-check" name="status" id="revise_{{ $submission->id }}" value="2"
|
||||
{{ $submission->status == 2 ? 'checked' : '' }} onchange="toggleComment(this)">
|
||||
<label class="btn btn-outline-danger" for="revise_{{ $submission->id }}">{{ get_phrase('Revision Needed') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Area Komentar dengan WYSIWYG Editor --}}
|
||||
<div id="comment_box_area" style="display: {{ $submission->status == 2 ? 'block' : 'none' }};">
|
||||
<div class="mb-3">
|
||||
<label class="fw-bold text-danger mb-1">
|
||||
{{ empty($submission->comment) ? get_phrase('Add Revision Notes') : get_phrase('Previous Revision Notes') }} *
|
||||
</label>
|
||||
|
||||
{{-- Textarea untuk Summernote --}}
|
||||
<textarea name="comment" id="comment_editor" class="form-control">{{ $submission->comment }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary w-100">{{ get_phrase('Save Grading') }}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- Tambahkan CSS Summernote --}}
|
||||
<link href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-lite.min.css" rel="stylesheet">
|
||||
|
||||
{{-- Tambahkan Script Summernote --}}
|
||||
<script src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-lite.min.js"></script>
|
||||
|
||||
<script>
|
||||
let commentEditor = null;
|
||||
|
||||
function toggleComment(radio) {
|
||||
let box = document.getElementById('comment_box_area');
|
||||
|
||||
if (radio.value == '2') {
|
||||
$(box).slideDown();
|
||||
// Inisialisasi editor jika belum diinisialisasi
|
||||
initSummernote();
|
||||
// Fokus ke editor
|
||||
setTimeout(() => {
|
||||
if (commentEditor && commentEditor.summernote) {
|
||||
commentEditor.summernote('focus');
|
||||
}
|
||||
}, 300);
|
||||
} else {
|
||||
$(box).slideUp();
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk menginisialisasi Summernote
|
||||
function initSummernote() {
|
||||
if (!commentEditor) {
|
||||
$('#comment_editor').summernote({
|
||||
height: 200,
|
||||
toolbar: [
|
||||
['style', ['style']],
|
||||
['font', ['bold', 'italic', 'underline', 'clear']],
|
||||
['fontname', ['fontname']],
|
||||
['color', ['color']],
|
||||
['para', ['ul', 'ol', 'paragraph']],
|
||||
['table', ['table']],
|
||||
['insert', ['link', 'picture']],
|
||||
['view', ['fullscreen', 'codeview', 'help']]
|
||||
],
|
||||
placeholder: '{{ get_phrase("Explain what needs to be fixed...") }}'
|
||||
});
|
||||
commentEditor = $('#comment_editor');
|
||||
}
|
||||
}
|
||||
|
||||
// Fungsi untuk validasi form sebelum submit
|
||||
$('#gradingForm').on('submit', function(e) {
|
||||
const status = $('input[name="status"]:checked').val();
|
||||
|
||||
if (status == '2') {
|
||||
// Jika status "Revision Needed", pastikan ada konten di editor
|
||||
let commentContent = '';
|
||||
|
||||
if (commentEditor && commentEditor.summernote) {
|
||||
commentContent = commentEditor.summernote('code').trim();
|
||||
} else {
|
||||
commentContent = $('#comment_editor').val().trim();
|
||||
}
|
||||
|
||||
// Hapus tag HTML untuk cek konten sebenarnya
|
||||
const plainText = commentContent.replace(/<[^>]*>/g, '').trim();
|
||||
|
||||
if (plainText === '') {
|
||||
e.preventDefault();
|
||||
alert('{{ get_phrase("Please add revision notes before submitting.") }}');
|
||||
if (commentEditor && commentEditor.summernote) {
|
||||
commentEditor.summernote('focus');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Inisialisasi editor otomatis jika sudah ada komentar sebelumnya dan status adalah "Revision Needed"
|
||||
@if($submission && $submission->status == 2 && !empty($submission->comment))
|
||||
$(document).ready(function() {
|
||||
// Tunggu sebentar agar DOM siap
|
||||
setTimeout(() => {
|
||||
initSummernote();
|
||||
}, 100);
|
||||
});
|
||||
@endif
|
||||
|
||||
// Inisialisasi editor jika comment_box_area langsung terlihat
|
||||
$(document).ready(function() {
|
||||
@if($submission && $submission->status == 2)
|
||||
// Tunggu sedikit agar DOM selesai render
|
||||
setTimeout(() => {
|
||||
initSummernote();
|
||||
}, 200);
|
||||
@endif
|
||||
});
|
||||
</script>
|
||||
@ -32,7 +32,7 @@
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<input class="form-control ol-form-control" type="number" min="0" max="23" name="hour"
|
||||
placeholder="00 hour">
|
||||
placeholder="00 hour" required>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<input class="form-control ol-form-control" type="number" min="0" max="59" name="minute"
|
||||
|
||||
@ -27,7 +27,7 @@ use App\Http\Controllers\NewsletterController;
|
||||
use App\Http\Controllers\ReportController;
|
||||
use App\Http\Controllers\SeoController;
|
||||
use App\Http\Controllers\SettingController;
|
||||
use App\Http\Controllers\Updater;
|
||||
// use App\Http\Controllers\Updater;
|
||||
use App\Http\Controllers\UsersController;
|
||||
use App\Http\Controllers\Admin\TutorBookingController;
|
||||
use App\Http\Controllers\Admin\KnowledgeBaseController;
|
||||
@ -77,12 +77,12 @@ Route::name('admin.')->prefix('admin')->middleware('admin')->group(function () {
|
||||
//invoice
|
||||
Route::get('invoice/{id?}', [InvoiceController::class, 'invoice'])->name('admin.invoice');
|
||||
|
||||
Route::controller(Updater::class)->middleware('auth', 'verified')->group(function () {
|
||||
// Route::controller(Updater::class)->middleware('auth', 'verified')->group(function () {
|
||||
|
||||
Route::post('admin/addon/create', 'update')->name('addon.create');
|
||||
Route::post('admin/addon/update', 'update')->name('addon.update');
|
||||
Route::post('admin/product/update', 'update')->name('product.update');
|
||||
});
|
||||
// Route::post('admin/addon/create', 'update')->name('addon.create');
|
||||
// Route::post('admin/addon/update', 'update')->name('addon.update');
|
||||
// Route::post('admin/product/update', 'update')->name('product.update');
|
||||
// });
|
||||
|
||||
//curriculum
|
||||
Route::controller(CurriculumController::class)->group(function () {
|
||||
|
||||
@ -14,6 +14,7 @@ use App\Http\Controllers\instructor\PayoutController;
|
||||
use App\Http\Controllers\instructor\PayoutSettingsController;
|
||||
use App\Http\Controllers\instructor\QuestionController;
|
||||
use App\Http\Controllers\instructor\QuizController;
|
||||
use App\Http\Controllers\instructor\ProjectController;
|
||||
use App\Http\Controllers\instructor\SalesReportController;
|
||||
use App\Http\Controllers\instructor\SectionController;
|
||||
use App\Http\Controllers\instructor\TeamTrainingController;
|
||||
@ -76,6 +77,16 @@ Route::name('instructor.')->prefix('instructor')->middleware(['instructor'])->gr
|
||||
Route::get('quiz/result/preview', 'result_preview')->name('quiz.result.preview');
|
||||
});
|
||||
|
||||
// course project
|
||||
Route::controller(ProjectController::class)->group(function () {
|
||||
Route::post('course/project/store', 'store')->name('course.project.store');
|
||||
Route::post('course/project/update/{id}', 'update')->name('course.project.update');
|
||||
Route::get('/project/grading/preview', 'getPreview')->name('project.grading.preview');
|
||||
Route::get('/project/grading/{id}', 'getIndex')->name('project.grading.index');
|
||||
Route::get('/project/participant/submission', 'getParticipantSubmission')->name('project.participant.submission');
|
||||
Route::post('/project/grading/update/{id}', 'updateSubmission')->name('project.grading.update');
|
||||
});
|
||||
|
||||
// question route
|
||||
Route::controller(QuestionController::class)->group(function () {
|
||||
Route::post('course/question/store', 'store')->name('course.question.store');
|
||||
|
||||
@ -14,6 +14,7 @@ use App\Http\Controllers\student\MyCoursesController;
|
||||
use App\Http\Controllers\student\MyProfileController;
|
||||
use App\Http\Controllers\student\MyTeamPackageController;
|
||||
use App\Http\Controllers\student\OfflinePaymentController;
|
||||
use App\Http\Controllers\student\ProjectController;
|
||||
use App\Http\Controllers\student\VAPaymentController;
|
||||
use App\Http\Controllers\student\PurchaseController;
|
||||
use App\Http\Controllers\student\QuizController;
|
||||
@ -49,6 +50,12 @@ Route::middleware(['auth'])->group(function () {
|
||||
Route::get('load/quiz/questions/', 'load_questions')->name('load.quiz.questions');
|
||||
});
|
||||
|
||||
// project routes
|
||||
Route::controller(ProjectController::class)->group(function () {
|
||||
Route::post('project/submit', 'submit')->name('student.project.submit');
|
||||
});
|
||||
|
||||
|
||||
// purchase routes
|
||||
Route::controller(PurchaseController::class)->group(function () {
|
||||
Route::get('purchase/course/{course_id}', 'purchase_course')->name('purchase.course');
|
||||
|
||||
Loading…
Reference in New Issue
Block a user