Detailing fungsi semua modul dari bugs error
This commit is contained in:
@@ -9,110 +9,331 @@ use App\Models\Position;
|
||||
use App\Models\Subject;
|
||||
use App\Models\User;
|
||||
use App\Models\QuestionBank;
|
||||
use App\Models\Exam;
|
||||
|
||||
class ExamManagementController extends Controller
|
||||
{
|
||||
public function questionBank(Request $request)
|
||||
// ==============================================================================
|
||||
// 1. AREA MANAJEMEN UJIAN (CBT)
|
||||
// ==============================================================================
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
// 1. Ambil ID unik siapa saja yang PERNAH membuat soal di tabel question_banks
|
||||
$creatorIds = \App\Models\QuestionBank::whereNotNull('created_by')
|
||||
->distinct()
|
||||
->pluck('created_by');
|
||||
$query = Exam::with('subject');
|
||||
|
||||
// 2. Tarik data User HANYA berdasarkan ID pembuat soal tersebut
|
||||
$creators = \App\Models\User::whereIn('id', $creatorIds)
|
||||
->orderBy('first_name', 'asc')
|
||||
->get();
|
||||
|
||||
// 3. Tarik data pendukung lainnya untuk filter & dropdown
|
||||
$subjects = \App\Models\Subject::orderBy('name', 'asc')->get();
|
||||
$departments = \App\Models\Department::orderBy('name', 'asc')->get();
|
||||
$positions = \App\Models\Position::orderBy('name', 'asc')->get();
|
||||
|
||||
// 4. Hitung Total Soal Keseluruhan (TANPA FILTER) untuk Badge di View
|
||||
$totalQuestions = \App\Models\QuestionBank::count(); // <--- TAMBAHKAN BARIS INI
|
||||
|
||||
// 5. Query utama Bank Soal (dengan fitur pencarian/filter)
|
||||
$query = \App\Models\QuestionBank::with(['subject', 'department', 'creator', 'position']);
|
||||
|
||||
// Filter berdasarkan pembuat soal
|
||||
if ($request->filled('creator_id')) {
|
||||
$query->where('created_by', $request->creator_id);
|
||||
if ($request->filled('search')) {
|
||||
$query->where('title', 'LIKE', '%' . $request->search . '%');
|
||||
}
|
||||
|
||||
$exams = $query->latest()->paginate(20);
|
||||
return view('pages.admin.exams.index', compact('exams'));
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
// Tarik data materi untuk Select2 Modul
|
||||
$subjects = Subject::orderBy('name', 'asc')->get();
|
||||
|
||||
// Filter berdasarkan subject/materi
|
||||
if ($request->filled('subject_id')) {
|
||||
$query->where('subject_id', $request->subject_id);
|
||||
// Tarik data karyawan untuk Select2 Peserta (Bisa pilih banyak)
|
||||
$employees = User::orderBy('first_name', 'asc')->get();
|
||||
|
||||
return view('pages.admin.exams.create', compact('subjects', 'employees'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'training_type' => 'required|in:matrix_plant,general,external',
|
||||
'is_random_order' => 'nullable|boolean',
|
||||
]);
|
||||
|
||||
if ($request->training_type === 'external') {
|
||||
return back()->with('info', 'Fitur External Training dengan database HR sedang dalam tahap pengembangan.');
|
||||
}
|
||||
|
||||
// Filter berdasarkan departemen
|
||||
if ($request->filled('department_id')) {
|
||||
$query->where('department_id', $request->department_id);
|
||||
// Validasi Tambahan: Peserta, Deadline, dan Array Ujian
|
||||
$request->validate([
|
||||
'employee_ids' => 'required|array|min:1', // Wajib pilih minimal 1 peserta
|
||||
'employee_ids.*' => 'exists:users,id',
|
||||
'deadline_date' => 'required|date|after_or_equal:today', // Deadline tidak boleh masa lalu
|
||||
|
||||
'exams' => 'required|array|min:1',
|
||||
'exams.*.subject_id' => 'required|exists:subjects,id',
|
||||
'exams.*.title' => 'nullable|string|max:255',
|
||||
'exams.*.duration_minutes' => 'required|integer|min:1',
|
||||
'exams.*.passing_grade' => 'required|integer|min:1|max:100',
|
||||
'exams.*.max_retakes' => 'required|integer|min:0',
|
||||
]);
|
||||
|
||||
$isRandom = $request->has('is_random_order');
|
||||
$trainingType = $request->training_type === 'general' ? 'product' : 'matrix_plant';
|
||||
|
||||
// 1. Simpan setiap modul sebagai sesi ujian tersendiri
|
||||
foreach ($request->exams as $examData) {
|
||||
$exam = Exam::create([
|
||||
'title' => $examData['title'] ?: Subject::find($examData['subject_id'])->name,
|
||||
'subject_id' => $examData['subject_id'],
|
||||
'training_type' => $trainingType,
|
||||
'duration_minutes' => $examData['duration_minutes'],
|
||||
'passing_grade' => $examData['passing_grade'],
|
||||
'max_retakes' => $examData['max_retakes'],
|
||||
'is_random_order' => $isRandom,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
// 2. Tugaskan (Assign) ujian yang baru dibuat ini ke SEMUA karyawan yang dipilih
|
||||
$assignments = [];
|
||||
foreach ($request->employee_ids as $userId) {
|
||||
$assignments[] = [
|
||||
'user_id' => $userId,
|
||||
'exam_id' => $exam->id,
|
||||
'deadline_date' => $request->deadline_date,
|
||||
'status' => 'pending',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
];
|
||||
}
|
||||
|
||||
// Insert massal ke database agar prosesnya secepat kilat
|
||||
\Illuminate\Support\Facades\DB::table('exam_assignments')->insert($assignments);
|
||||
}
|
||||
|
||||
// Filter berdasarkan posisi/jabatan
|
||||
if ($request->filled('position_id')) {
|
||||
$query->where('position_id', $request->position_id);
|
||||
return redirect()->route('admin.exams.index')
|
||||
->with('success', count($request->exams) . ' Modul Ujian berhasil dibuat dan ditugaskan ke ' . count($request->employee_ids) . ' Karyawan!');
|
||||
}
|
||||
|
||||
|
||||
// ==============================================================================
|
||||
// 2. AREA MANAJEMEN BANK SOAL
|
||||
// ==============================================================================
|
||||
|
||||
public function questionBank(Request $request)
|
||||
{
|
||||
$creatorIds = QuestionBank::whereNotNull('created_by')->distinct()->pluck('created_by');
|
||||
$creators = User::whereIn('id', $creatorIds)->orderBy('first_name', 'asc')->get();
|
||||
|
||||
$subjects = Subject::orderBy('name', 'asc')->get();
|
||||
$departments = Department::orderBy('name', 'asc')->get();
|
||||
$positions = Position::orderBy('name', 'asc')->get();
|
||||
|
||||
$totalQuestions = QuestionBank::count();
|
||||
|
||||
// Query utama dengan relasi
|
||||
$query = QuestionBank::with(['subject', 'department', 'creator', 'position']);
|
||||
|
||||
// 1. FILTER GLOBAL SEARCH (Mencari di semua kolom & relasi)
|
||||
if ($request->filled('search')) {
|
||||
$search = $request->search;
|
||||
$query->where(function($q) use ($search) {
|
||||
// Cari di ID atau Teks Soal
|
||||
$q->where('id', 'LIKE', "%{$search}%")
|
||||
->orWhere('question_text', 'LIKE', "%{$search}%")
|
||||
->orWhere('question_type', 'LIKE', "%{$search}%")
|
||||
->orWhere('question_level', 'LIKE', "%{$search}%")
|
||||
// Cari di Nama Materi
|
||||
->orWhereHas('subject', function($sub) use ($search) {
|
||||
$sub->where('name', 'LIKE', "%{$search}%");
|
||||
})
|
||||
// Cari di Nama Departemen
|
||||
->orWhereHas('department', function($dep) use ($search) {
|
||||
$dep->where('name', 'LIKE', "%{$search}%");
|
||||
})
|
||||
// Cari di Nama Jabatan
|
||||
->orWhereHas('position', function($pos) use ($search) {
|
||||
$pos->where('name', 'LIKE', "%{$search}%");
|
||||
})
|
||||
// Cari di Nama Pembuat Soal
|
||||
->orWhereHas('creator', function($usr) use ($search) {
|
||||
$usr->where('first_name', 'LIKE', "%{$search}%")
|
||||
->orWhere('last_name', 'LIKE', "%{$search}%");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Pagination
|
||||
// 2. FILTER DROPDOWN SPESIFIK
|
||||
if ($request->filled('creator_id')) { $query->where('created_by', $request->creator_id); }
|
||||
if ($request->filled('subject_id')) { $query->where('subject_id', $request->subject_id); }
|
||||
if ($request->filled('department_id')) { $query->where('department_id', $request->department_id); }
|
||||
if ($request->filled('position_id')) { $query->where('position_id', $request->position_id); }
|
||||
if ($request->filled('question_type')) { $query->where('question_type', $request->question_type); }
|
||||
if ($request->filled('question_level')) { $query->where('question_level', $request->question_level); }
|
||||
|
||||
// ==============================================================================
|
||||
// 3. INTERCEPTOR UNTUK EXPORT (Excel & PDF)
|
||||
// ==============================================================================
|
||||
if ($request->action === 'export_excel' || $request->action === 'export_pdf') {
|
||||
$exportData = $query->latest()->get();
|
||||
$exporterName = auth()->user()->first_name . ' ' . auth()->user()->last_name;
|
||||
$exportTime = now()->format('d F Y, H:i:s');
|
||||
|
||||
// LOGIKA EXPORT EXCEL (Download Paksa)
|
||||
if ($request->action === 'export_excel') {
|
||||
$fileName = 'Data_Bank_Soal_' . now()->format('Ymd_His') . '.xls';
|
||||
|
||||
return response()->streamDownload(function () use ($exportData, $exporterName, $exportTime, $request) {
|
||||
echo view('pages.admin.question-banks.export-report', compact('exportData', 'exporterName', 'exportTime', 'request'))->render();
|
||||
}, $fileName, [
|
||||
'Content-Type' => 'application/vnd.ms-excel',
|
||||
'Content-Disposition' => 'attachment; filename="'.$fileName.'"',
|
||||
]);
|
||||
}
|
||||
|
||||
// LOGIKA EXPORT PDF (Menggunakan Facade DomPDF)
|
||||
if ($request->action === 'export_pdf') {
|
||||
$fileName = 'Data_Bank_Soal_' . now()->format('Ymd_His') . '.pdf';
|
||||
|
||||
// Gunakan library PDF yang sudah ada di project Anda
|
||||
$pdf = \Barryvdh\DomPDF\Facade\Pdf::loadView('pages.admin.question-banks.export-report', compact('exportData', 'exporterName', 'exportTime', 'request'))
|
||||
->setPaper('a4', 'landscape'); // Format landscape agar tabel muat
|
||||
|
||||
return $pdf->download($fileName);
|
||||
}
|
||||
}
|
||||
// 4. JIKA BUKAN EXPORT, LANJUTKAN KE TAMPILAN TABEL NORMAL DENGAN PAGINATION
|
||||
$questions = $query->latest()->paginate(20);
|
||||
|
||||
// Pastikan $totalQuestions ikut dikirim ke compact
|
||||
return view('pages.admin.question-banks.question-bank', compact('questions', 'creators', 'subjects', 'departments', 'positions', 'totalQuestions'));
|
||||
}
|
||||
|
||||
// Fungsi untuk menampilkan halaman Tambah Soal
|
||||
public function create()
|
||||
// Nama diubah agar tidak bentrok dengan create() ujian
|
||||
public function createQuestion()
|
||||
{
|
||||
$departments = Department::orderBy('name', 'asc')->get();
|
||||
$positions = Position::orderBy('name', 'asc')->get();
|
||||
$subjects = Subject::orderBy('name', 'asc')->get(); // Kirim Subject ke form
|
||||
$subjects = Subject::orderBy('name', 'asc')->get();
|
||||
|
||||
return view('pages.admin.question-banks.create', compact('departments', 'positions', 'subjects'));
|
||||
}
|
||||
|
||||
// Fungsi untuk menampilkan halaman Edit Soal
|
||||
public function edit($id)
|
||||
// Nama diubah agar spesifik untuk soal
|
||||
public function editQuestion($id)
|
||||
{
|
||||
$question = QuestionBank::findOrFail($id);
|
||||
$departments = Department::orderBy('name', 'asc')->get();
|
||||
$positions = Position::orderBy('name', 'asc')->get();
|
||||
$subjects = Subject::orderBy('name', 'asc')->get(); // Kirim Subject ke form
|
||||
$subjects = Subject::orderBy('name', 'asc')->get();
|
||||
|
||||
return view('pages.admin.question-banks.edit', compact('question', 'departments', 'positions', 'subjects'));
|
||||
}
|
||||
|
||||
// Fungsi Show untuk Detail Soal
|
||||
public function show($id)
|
||||
// Nama diubah agar spesifik untuk soal
|
||||
public function showQuestion($id)
|
||||
{
|
||||
$question = QuestionBank::with(['subject', 'department', 'position', 'creator'])->findOrFail($id);
|
||||
return view('pages.admin.question-banks.show', compact('question'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Menghapus banyak soal sekaligus (Bulk Delete)
|
||||
*/
|
||||
public function bulkDelete(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'question_ids' => 'required|array',
|
||||
'question_ids.*' => 'exists:questions,id'
|
||||
'question_ids.*' => 'exists:question_banks,id' // Diperbaiki dari tabel questions ke question_banks
|
||||
]);
|
||||
|
||||
try {
|
||||
Question::whereIn('id', $request->question_ids)->delete();
|
||||
// Diperbaiki dari model Question ke QuestionBank
|
||||
QuestionBank::whereIn('id', $request->question_ids)->delete();
|
||||
return back()->with('success', count($request->question_ids) . ' Soal berhasil dihapus secara massal.');
|
||||
} catch (\Exception $e) {
|
||||
return back()->with('error', 'Gagal menghapus soal: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Menampilkan Halaman Import Soal
|
||||
*/
|
||||
public function importView()
|
||||
{
|
||||
return view('pages.admin.exams.questions.import');
|
||||
}
|
||||
|
||||
// ==============================================================================
|
||||
// FUNGSI PROSES SIMPAN, EDIT, DAN HAPUS BANK SOAL
|
||||
// ==============================================================================
|
||||
|
||||
public function storeQuestion(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'subject_id' => 'required|exists:subjects,id',
|
||||
'department_id' => 'nullable|exists:departments,id',
|
||||
'position_id' => 'nullable|exists:positions,id',
|
||||
// VALIDASI INI SUDAH DISAMAKAN DENGAN FORM UI
|
||||
'question_type' => 'required|in:single,multiple,true_false,descriptive',
|
||||
'question_text' => 'required|string',
|
||||
'score_weight' => 'required|numeric|min:1',
|
||||
'duration' => 'nullable|numeric',
|
||||
'passing_grade' => 'nullable|numeric',
|
||||
]);
|
||||
|
||||
$data = $request->all();
|
||||
$data['created_by'] = auth()->id(); // Rekam siapa yang membuat soal
|
||||
|
||||
// Bersihkan data yang tidak relevan berdasarkan jenis soal
|
||||
if ($request->question_type === 'descriptive') {
|
||||
$data['option_a'] = null;
|
||||
$data['option_b'] = null;
|
||||
$data['option_c'] = null;
|
||||
$data['option_d'] = null;
|
||||
$data['option_e'] = null;
|
||||
$data['correct_answer'] = null;
|
||||
} else {
|
||||
$data['essay_keywords'] = null;
|
||||
}
|
||||
|
||||
// Jika tipe soal adalah Multiple (bisa centang lebih dari 1), ubah array menjadi JSON
|
||||
// if (isset($request->correct_answer) && is_array($request->correct_answer)) {
|
||||
// $data['correct_answer'] = json_encode($request->correct_answer);
|
||||
// }
|
||||
|
||||
QuestionBank::create($data);
|
||||
|
||||
return redirect()->route('admin.question-bank.index')->with('success', 'Soal baru berhasil ditambahkan ke Bank Soal!');
|
||||
}
|
||||
|
||||
public function updateQuestion(Request $request, $id)
|
||||
{
|
||||
$request->validate([
|
||||
'subject_id' => 'required|exists:subjects,id',
|
||||
'department_id' => 'nullable|exists:departments,id',
|
||||
'position_id' => 'nullable|exists:positions,id',
|
||||
'question_type' => 'required|in:single,multiple,true_false,descriptive',
|
||||
'question_text' => 'required|string',
|
||||
'score_weight' => 'required|numeric|min:1',
|
||||
'duration' => 'nullable|numeric',
|
||||
'passing_grade' => 'nullable|numeric',
|
||||
]);
|
||||
|
||||
$question = QuestionBank::findOrFail($id);
|
||||
$data = $request->all();
|
||||
|
||||
// 1. Bersihkan data yang tidak relevan berdasarkan jenis soal
|
||||
if ($request->question_type === 'descriptive') {
|
||||
$data['option_a'] = null;
|
||||
$data['option_b'] = null;
|
||||
$data['option_c'] = null;
|
||||
$data['option_d'] = null;
|
||||
$data['option_e'] = null;
|
||||
$data['correct_answer'] = null;
|
||||
} else {
|
||||
$data['essay_keywords'] = null;
|
||||
}
|
||||
|
||||
// 2. Jika tipe soal adalah Multiple (bisa centang lebih dari 1), ubah array menjadi JSON
|
||||
// if (is_array($request->correct_answer)) {
|
||||
// $data['correct_answer'] = json_encode($request->correct_answer);
|
||||
// }
|
||||
|
||||
// 3. Simpan pembaruan
|
||||
$question->update($data);
|
||||
|
||||
return redirect()->route('admin.question-bank.index')->with('success', 'Perubahan soal berhasil disimpan!');
|
||||
}
|
||||
|
||||
public function destroyQuestion($id)
|
||||
{
|
||||
try {
|
||||
$question = QuestionBank::findOrFail($id);
|
||||
$question->delete();
|
||||
return back()->with('success', 'Soal berhasil dihapus.');
|
||||
} catch (\Exception $e) {
|
||||
return back()->with('error', 'Gagal menghapus soal: ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user