Detailing fungsi semua modul dari bugs error
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||
|
||||
<style>
|
||||
/* Styling Select2 Single (Materi/SOP) untuk Tailwind */
|
||||
.select2-container .select2-selection--single { height: 42px; border-radius: 0.75rem; border-color: #e2e8f0; background-color: #f8fafc; }
|
||||
.select2-container--default .select2-selection--single .select2-selection__rendered { line-height: 42px; font-size: 0.875rem; color: #334155; padding-left: 0.75rem; }
|
||||
.select2-container--default .select2-selection--single .select2-selection__arrow { height: 40px; right: 10px; }
|
||||
|
||||
/* Styling Select2 Multiple (Pilih Karyawan Bulk) untuk Tailwind */
|
||||
.select2-container--default .select2-selection--multiple { min-height: 44px; border-radius: 0.75rem; border-color: #e2e8f0; background-color: #f8fafc; padding: 4px 8px; }
|
||||
.select2-container--default.select2-container--focus .select2-selection--multiple { border-color: #2563eb; ring: 2px; --tw-ring-color: rgba(37, 99, 235, 0.2); }
|
||||
.select2-container--default .select2-selection--multiple .select2-selection__choice { bg-color: #eff6ff; border: 1px solid #bfdbfe; color: #1e40af; font-weight: 700; font-size: 0.75rem; border-radius: 0.5rem; padding: 2px 8px; margin-top: 4px; }
|
||||
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove { color: #3b82f6; margin-right: 6px; font-weight: bold; }
|
||||
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover { color: #1d4ed8; }
|
||||
|
||||
/* Global Dropdown Search Field */
|
||||
.select2-container--default .select2-search--dropdown .select2-search__field { border-radius: 0.5rem; border-color: #cbd5e1; outline: none; padding: 0.5rem; }
|
||||
</style>
|
||||
|
||||
<div class="max-w-6xl mx-auto px-4 py-8"
|
||||
x-data="{
|
||||
trainingType: 'matrix_plant',
|
||||
sops: [{ id: Date.now() }], // State untuk Dynamic Array
|
||||
|
||||
addSop() {
|
||||
this.sops.push({ id: Date.now() });
|
||||
// Panggil ulang Select2 untuk elemen yang baru dirender
|
||||
setTimeout(() => { initDynamicSelect2(); }, 50);
|
||||
},
|
||||
removeSop(id) {
|
||||
if(this.sops.length > 1) {
|
||||
this.sops = this.sops.filter(sop => sop.id !== id);
|
||||
}
|
||||
}
|
||||
}">
|
||||
|
||||
<div class="flex items-center space-x-3 mb-6">
|
||||
<a href="{{ route('admin.exams.index') }}" class="text-slate-400 hover:text-blue-600 transition-colors">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path></svg>
|
||||
</a>
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold text-slate-800 tracking-tight">Assign Training & Ujian Baru</h2>
|
||||
<p class="text-sm text-slate-500">Tugaskan modul SOP ke dalam sesi Ujian (CBT).</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if(session('info'))
|
||||
<div class="mb-6 p-4 bg-blue-50 text-blue-700 rounded-xl border border-blue-100 text-sm font-bold flex items-center">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
|
||||
{{ session('info') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('admin.exams.store') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<div class="bg-white p-6 rounded-2xl shadow-sm border border-slate-200 mb-6">
|
||||
<label class="block text-base font-bold text-slate-800 mb-4">1. Pilih Jenis Training <span class="text-red-500">*</span></label>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<label class="flex items-center p-4 bg-slate-50 border rounded-xl cursor-pointer transition-all hover:bg-blue-50"
|
||||
:class="trainingType === 'matrix_plant' ? 'border-blue-500 ring-2 ring-blue-500/20' : 'border-slate-200'">
|
||||
<input type="radio" x-model="trainingType" name="training_type" value="matrix_plant" class="w-5 h-5 text-blue-600 focus:ring-blue-500">
|
||||
<div class="ml-3">
|
||||
<span class="block text-sm font-bold text-slate-800">Training Matrix (Plant)</span>
|
||||
<span class="block text-xs text-slate-500 mt-0.5">SOP & Prosedur Teknis Pabrik</span>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="flex items-center p-4 bg-slate-50 border rounded-xl cursor-pointer transition-all hover:bg-purple-50"
|
||||
:class="trainingType === 'general' ? 'border-purple-500 ring-2 ring-purple-500/20' : 'border-slate-200'">
|
||||
<input type="radio" x-model="trainingType" name="training_type" value="general" class="w-5 h-5 text-purple-600 focus:ring-purple-500">
|
||||
<div class="ml-3">
|
||||
<span class="block text-sm font-bold text-slate-800">General (Non-Plant / Produk)</span>
|
||||
<span class="block text-xs text-slate-500 mt-0.5">Marketing, Sales, & Modul Umum</span>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label class="flex items-center p-4 bg-slate-50 border rounded-xl cursor-pointer transition-all hover:bg-emerald-50"
|
||||
:class="trainingType === 'external' ? 'border-emerald-500 ring-2 ring-emerald-500/20' : 'border-slate-200'">
|
||||
<input type="radio" x-model="trainingType" name="training_type" value="external" class="w-5 h-5 text-emerald-600 focus:ring-emerald-500">
|
||||
<div class="ml-3">
|
||||
<span class="block text-sm font-bold text-slate-800">External Training</span>
|
||||
<span class="block text-xs text-slate-500 mt-0.5">Pelatihan via Vendor / HR</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-2xl shadow-sm border border-slate-200 mb-6" x-show="trainingType !== 'external'">
|
||||
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center mb-6">
|
||||
<div>
|
||||
<label class="block text-base font-bold text-slate-800">2. Konfigurasi Modul & Aturan CBT</label>
|
||||
<p class="text-sm text-slate-500">Anda dapat menugaskan banyak SOP sekaligus dalam satu kali simpan.</p>
|
||||
</div>
|
||||
<button type="button" @click="addSop()" class="mt-3 sm:mt-0 px-4 py-2 bg-indigo-50 text-indigo-700 font-bold rounded-xl border border-indigo-100 hover:bg-indigo-100 transition-colors flex items-center shadow-sm">
|
||||
<svg class="w-4 h-4 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path></svg>
|
||||
Tambah Modul SOP
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="space-y-5">
|
||||
<template x-for="(sop, index) in sops" :key="sop.id">
|
||||
<div class="p-5 bg-slate-50 border border-slate-200 rounded-xl relative hover:border-blue-300 transition-colors">
|
||||
|
||||
<button type="button" x-show="sops.length > 1" @click="removeSop(sop.id)" class="absolute top-4 right-4 p-1.5 text-slate-400 hover:text-red-500 hover:bg-red-50 rounded-lg transition-colors" title="Hapus Modul">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg>
|
||||
</button>
|
||||
|
||||
<div class="flex items-center space-x-2 mb-4">
|
||||
<span class="flex items-center justify-center w-6 h-6 rounded-full bg-slate-200 text-slate-600 text-xs font-bold" x-text="index + 1"></span>
|
||||
<h4 class="text-sm font-bold text-slate-700" x-text="trainingType === 'matrix_plant' ? 'Dokumen SOP' : 'Subject / Modul'"></h4>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-12 gap-5">
|
||||
|
||||
<div class="md:col-span-5">
|
||||
<label class="block text-[11px] font-bold text-slate-500 uppercase tracking-wide mb-1.5">Pilih Materi <span class="text-red-500">*</span></label>
|
||||
<select :name="'exams['+index+'][subject_id]'" required class="select2-dynamic w-full">
|
||||
<option value="">-- Ketik untuk mencari materi/SOP --</option>
|
||||
@foreach($subjects as $subject)
|
||||
<option value="{{ $subject->id }}">{{ $subject->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="md:col-span-7 md:pr-8">
|
||||
<label class="block text-[11px] font-bold text-slate-500 uppercase tracking-wide mb-1.5">Nama Quiz Khusus (Opsional)</label>
|
||||
<input type="text" :name="'exams['+index+'][title]'" class="w-full px-3 py-2 bg-white border border-slate-200 rounded-lg text-sm focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all" placeholder="Kosongkan jika ingin mengikuti nama materi...">
|
||||
</div>
|
||||
|
||||
<div class="md:col-span-3">
|
||||
<label class="block text-[11px] font-bold text-slate-500 uppercase tracking-wide mb-1.5">Durasi (Menit) <span class="text-red-500">*</span></label>
|
||||
<input type="number" :name="'exams['+index+'][duration_minutes]'" value="60" min="1" required class="w-full px-3 py-2 bg-white border border-slate-200 rounded-lg text-sm focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all">
|
||||
</div>
|
||||
|
||||
<div class="md:col-span-3">
|
||||
<label class="block text-[11px] font-bold text-slate-500 uppercase tracking-wide mb-1.5">Nilai KKM <span class="text-red-500">*</span></label>
|
||||
<div class="relative">
|
||||
<input type="number" :name="'exams['+index+'][passing_grade]'" value="80" min="1" max="100" required class="w-full px-3 py-2 bg-white border border-slate-200 rounded-lg text-sm focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all">
|
||||
<span class="absolute right-3 top-2.5 text-slate-400 text-xs font-bold">/ 100</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="md:col-span-6 md:pr-8">
|
||||
<label class="block text-[11px] font-bold text-slate-500 uppercase tracking-wide mb-1.5">Jatah Remedial <span class="text-red-500">*</span></label>
|
||||
<div class="flex items-center space-x-3">
|
||||
<input type="number" :name="'exams['+index+'][max_retakes]'" value="1" min="0" required class="w-24 px-3 py-2 bg-white border border-slate-200 rounded-lg text-sm focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all">
|
||||
<p class="text-[11px] leading-tight text-slate-500 font-medium">Kali. Jika melebihi jatah, <span class="text-red-500 font-bold">Ujian terkunci (Offline)</span>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 pt-5 border-t border-slate-200">
|
||||
<label class="flex items-center cursor-pointer p-3 bg-indigo-50/50 border border-indigo-100 rounded-xl hover:bg-indigo-50 transition-colors w-max">
|
||||
<input type="checkbox" name="is_random_order" value="1" checked class="w-5 h-5 text-indigo-600 border-slate-300 rounded focus:ring-indigo-500">
|
||||
<span class="ml-3 text-sm font-bold text-indigo-900">Acak Urutan Soal (Terapkan ke seluruh modul di atas)</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white p-6 rounded-2xl shadow-sm border border-slate-200 mb-6" x-show="trainingType !== 'external'">
|
||||
<div class="mb-4 border-b border-slate-100 pb-4">
|
||||
<label class="block text-base font-bold text-slate-800">3. Pilih Peserta & Deadline Pelatihan</label>
|
||||
<p class="text-sm text-slate-500">Karyawan yang dipilih akan langsung menerima seluruh modul di atas pada dashboard mereka.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
<div class="md:col-span-2">
|
||||
<label class="block text-[11px] font-bold text-slate-500 uppercase tracking-wide mb-2">Pilih Karyawan Pelatihan (Bisa Bulk / Banyak) <span class="text-red-500">*</span></label>
|
||||
<select name="employee_ids[]" id="employeeSelect" multiple="multiple" class="w-full" required>
|
||||
@foreach($employees as $emp)
|
||||
<option value="{{ $emp->id }}">
|
||||
{{ $emp->first_name }} {{ $emp->last_name }} - {{ $emp->nik ?? 'No NIK' }} ({{ $emp->position->name ?? '-' }})
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-[11px] font-bold text-slate-500 uppercase tracking-wide mb-2">Batas Akhir Penyelesaian (Deadline) <span class="text-red-500">*</span></label>
|
||||
<input type="datetime-local" name="deadline_date" required class="w-full px-4 py-2 bg-white border border-slate-200 rounded-xl text-sm focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all" style="height: 44px;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div x-show="trainingType === 'external'" x-cloak class="bg-white p-10 rounded-2xl shadow-sm border border-slate-200 mb-6 text-center">
|
||||
<div class="w-20 h-20 bg-emerald-50 rounded-full flex items-center justify-center mx-auto mb-4 border border-emerald-100">
|
||||
<svg class="w-10 h-10 text-emerald-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"></path></svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-slate-800 mb-2">Modul External Training Segera Hadir</h3>
|
||||
<p class="text-slate-500 max-w-lg mx-auto">Sistem sedang menyiapkan integrasi dengan Database Vendor Pelatihan dari HRD (Mencakup Nama Vendor, Kategori Kompetensi, Sertifikat, dan JPL).</p>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end space-x-3">
|
||||
<a href="{{ route('admin.exams.index') }}" class="px-6 py-3 text-sm font-bold text-slate-600 hover:bg-slate-200 bg-slate-100 rounded-xl transition-colors">Batal</a>
|
||||
<button type="submit" class="px-8 py-3 text-sm font-bold text-white bg-blue-600 hover:bg-blue-700 rounded-xl shadow-md transition-all transform hover:-translate-y-0.5">
|
||||
Simpan & Distribusikan Training
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function initDynamicSelect2() {
|
||||
$('.select2-dynamic').each(function() {
|
||||
if ($(this).hasClass("select2-hidden-accessible")) {
|
||||
$(this).select2('destroy');
|
||||
}
|
||||
});
|
||||
|
||||
$('.select2-dynamic').select2({
|
||||
placeholder: "-- Ketik untuk mencari materi/SOP --",
|
||||
width: '100%',
|
||||
allowClear: true
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
// Inisialisasi awal dropdown dinamis SOP
|
||||
initDynamicSelect2();
|
||||
|
||||
// Inisialisasi dropdown multiple karyawan bulk
|
||||
$('#employeeSelect').select2({
|
||||
placeholder: "Ketik nama atau NIK karyawan...",
|
||||
allowClear: true,
|
||||
width: '100%',
|
||||
language: {
|
||||
noResults: function() { return "Karyawan tidak ditemukan"; }
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,86 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="max-w-6xl mx-auto px-4 py-8">
|
||||
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h2 class="text-2xl font-bold text-slate-800 tracking-tight">Manajemen Sesi Ujian (CBT)</h2>
|
||||
<a href="{{ route('admin.exams.create') }}" class="px-4 py-2 bg-blue-600 text-white rounded-xl text-sm font-bold hover:bg-blue-700 shadow-sm transition-colors flex items-center">
|
||||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"></path></svg>
|
||||
Buat Ujian / Assign Training
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="mb-6 p-4 bg-emerald-50 text-emerald-700 rounded-xl border border-emerald-100 text-sm font-bold flex items-center">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path></svg>
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-sm border border-slate-200 overflow-hidden">
|
||||
<div class="overflow-x-auto">
|
||||
<table class="w-full text-left text-sm text-slate-600">
|
||||
<thead class="bg-slate-50 border-b border-slate-200 text-slate-500 uppercase text-xs tracking-wider">
|
||||
<tr>
|
||||
<th class="px-6 py-4 font-bold">Detail Ujian</th>
|
||||
<th class="px-6 py-4 font-bold">Materi & Jenis</th>
|
||||
<th class="px-6 py-4 font-bold text-center">Aturan</th>
|
||||
<th class="px-6 py-4 font-bold text-center">Status</th>
|
||||
<th class="px-6 py-4 font-bold text-right">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
@forelse($exams as $exam)
|
||||
<tr class="hover:bg-slate-50 transition-colors">
|
||||
<td class="px-6 py-4">
|
||||
<div class="font-bold text-slate-800 text-base">{{ $exam->title }}</div>
|
||||
<div class="text-xs text-slate-400 mt-1 flex items-center">
|
||||
<svg class="w-3.5 h-3.5 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
|
||||
{{ $exam->duration_minutes }} Menit
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4">
|
||||
<div class="font-bold text-indigo-700">{{ $exam->subject->name ?? 'Materi Terhapus' }}</div>
|
||||
<div class="text-[11px] font-bold uppercase mt-1.5 px-2 py-0.5 rounded inline-block
|
||||
{{ $exam->training_type == 'matrix_plant' ? 'bg-amber-100 text-amber-700' : ($exam->training_type == 'product' ? 'bg-purple-100 text-purple-700' : 'bg-slate-100 text-slate-700') }}">
|
||||
{{ str_replace('_', ' ', $exam->training_type) }}
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-center">
|
||||
<div class="text-xs font-bold text-slate-700">KKM: <span class="text-emerald-600">{{ $exam->passing_grade }}</span></div>
|
||||
<div class="text-[10px] text-slate-500 mt-1">Remedial: {{ $exam->max_retakes }}x</div>
|
||||
</td>
|
||||
<td class="px-6 py-4 text-center">
|
||||
@if($exam->is_active)
|
||||
<span class="px-2 py-1 bg-emerald-50 text-emerald-600 rounded text-xs font-bold border border-emerald-200">Aktif</span>
|
||||
@else
|
||||
<span class="px-2 py-1 bg-red-50 text-red-600 rounded text-xs font-bold border border-red-200">Draft</span>
|
||||
@endif
|
||||
</td>
|
||||
<td class="px-6 py-4 flex justify-end space-x-2">
|
||||
<button class="p-2 text-indigo-500 hover:bg-indigo-50 rounded-lg transition-colors" title="Kelola Soal Ujian">
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"></path></svg>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="px-6 py-8 text-center text-slate-500">
|
||||
<div class="flex flex-col items-center justify-center">
|
||||
<svg class="w-12 h-12 text-slate-300 mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg>
|
||||
Belum ada jadwal / sesi ujian.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="p-4 border-t border-slate-100 bg-slate-50">
|
||||
{{ $exams->links() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -124,8 +124,9 @@
|
||||
</div>
|
||||
|
||||
<div x-show="questionType === 'descriptive'">
|
||||
<label class="block text-sm font-bold text-slate-700 mb-2">Panduan Jawaban Kunci (Opsional)</label>
|
||||
<textarea name="option_a" rows="3" class="w-full px-4 py-3 bg-white border border-slate-200 rounded-xl text-sm focus:ring-blue-500" placeholder="Tuliskan kata kunci poin yang harus ada dalam esai..."></textarea>
|
||||
<label class="block text-sm font-bold text-slate-700 mb-2">Keyword Jawaban (Pisahkan dengan koma)</label>
|
||||
<textarea name="essay_keywords" rows="3" class="w-full px-4 py-3 bg-white border border-slate-200 rounded-xl text-sm focus:ring-blue-500" placeholder="Contoh: mesin washing, suhu 80 derajat, steril">{{ old('essay_keywords') }}</textarea>
|
||||
<p class="text-[11px] text-slate-500 mt-2">* Sistem akan mendeteksi kata kunci ini pada teks jawaban peserta untuk memberikan nilai hitungan awal secara otomatis.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||
|
||||
<style>
|
||||
.select2-container .select2-selection--single { height: 42px; border-radius: 0.75rem; border-color: #e2e8f0; background-color: #f8fafc; }
|
||||
.select2-container--default .select2-selection--single .select2-selection__rendered { line-height: 42px; font-size: 0.875rem; color: #334155; padding-left: 1rem; }
|
||||
.select2-container--default .select2-selection--single .select2-selection__arrow { height: 40px; right: 10px; }
|
||||
/* Menambahkan warna border merah jika error */
|
||||
.has-error .select2-selection { border-color: #ef4444 !important; background-color: #fef2f2 !important; }
|
||||
</style>
|
||||
|
||||
<div id="exam-edit-container" class="max-w-5xl mx-auto px-4 py-8" x-data="{ questionType: '{{ old('question_type', $question->question_type ?? '') }}' }">
|
||||
<div class="mb-6 flex items-center space-x-3">
|
||||
<a href="{{ route('admin.question-bank.index') }}" class="text-slate-400 hover:text-blue-600 transition-colors">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path></svg>
|
||||
</a>
|
||||
<h2 class="text-2xl font-bold text-slate-800 tracking-tight">Edit Soal: #{{ $question->id }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-sm border border-slate-200 overflow-hidden">
|
||||
<form action="{{ url('admin/exams/questions/'.$question->id) }}" method="POST" class="p-6 sm:p-8 space-y-6">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div>
|
||||
<label class="block text-sm font-bold text-slate-700 mb-2">Subject / Materi <span class="text-red-500">*</span></label>
|
||||
<select name="subject_id" class="select2 w-full text-sm @error('subject_id') has-error @enderror" required>
|
||||
<option value="">-- Pilih Judul Materi --</option>
|
||||
@foreach($subjects as $subj)
|
||||
<option value="{{ $subj->id }}" {{ old('subject_id', $question->subject_id) == $subj->id ? 'selected' : '' }}>{{ $subj->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('subject_id') <p class="text-xs text-red-500 mt-1.5 font-medium">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-bold text-slate-700 mb-2">Departemen</label>
|
||||
<select name="department_id" class="select2 w-full text-sm @error('department_id') has-error @enderror">
|
||||
<option value="">-- Global (Semua Dept) --</option>
|
||||
@foreach($departments as $dept)
|
||||
<option value="{{ $dept->id }}" {{ old('department_id', $question->department_id) == $dept->id ? 'selected' : '' }}>{{ $dept->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('department_id') <p class="text-xs text-red-500 mt-1.5 font-medium">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-bold text-slate-700 mb-2">Jabatan / Posisi</label>
|
||||
<select name="position_id" class="select2 w-full text-sm @error('position_id') has-error @enderror">
|
||||
<option value="">-- Global (Semua Jabatan) --</option>
|
||||
@foreach($positions as $pos)
|
||||
<option value="{{ $pos->id }}" {{ old('position_id', $question->position_id) == $pos->id ? 'selected' : '' }}>{{ $pos->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('position_id') <p class="text-xs text-red-500 mt-1.5 font-medium">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-bold text-slate-700 mb-2">Tipe Soal <span class="text-red-500">*</span></label>
|
||||
<select name="question_type" class="select2 w-full text-sm @error('question_type') has-error @enderror" required>
|
||||
<option value="">-- Pilih Tipe --</option>
|
||||
<option value="single" {{ old('question_type', $question->question_type) == 'single' ? 'selected' : '' }}>Single Choice (1 Kunci)</option>
|
||||
<option value="multiple" {{ old('question_type', $question->question_type) == 'multiple' ? 'selected' : '' }}>Multiple Choice (Multi Kunci)</option>
|
||||
<option value="true_false" {{ old('question_type', $question->question_type) == 'true_false' ? 'selected' : '' }}>True / False</option>
|
||||
<option value="descriptive" {{ old('question_type', $question->question_type) == 'descriptive' ? 'selected' : '' }}>Descriptive (Esai)</option>
|
||||
</select>
|
||||
@error('question_type') <p class="text-xs text-red-500 mt-1.5 font-medium">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-bold text-slate-700 mb-2">Level <span class="text-red-500">*</span></label>
|
||||
<select name="question_level" class="select2 w-full text-sm @error('question_level') has-error @enderror" required>
|
||||
<option value="mudah" {{ old('question_level', $question->question_level) == 'mudah' ? 'selected' : '' }}>Mudah</option>
|
||||
<option value="sedang" {{ old('question_level', $question->question_level) == 'sedang' ? 'selected' : '' }}>Sedang</option>
|
||||
<option value="sulit" {{ old('question_level', $question->question_level) == 'sulit' ? 'selected' : '' }}>Sulit</option>
|
||||
</select>
|
||||
@error('question_level') <p class="text-xs text-red-500 mt-1.5 font-medium">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-bold text-slate-700 mb-2">Bobot Nilai (Score) <span class="text-red-500">*</span></label>
|
||||
<input type="number" name="score_weight" value="{{ old('score_weight', $question->score_weight ?? 1) }}" class="w-full px-3 py-2 bg-slate-50 border @error('score_weight') border-red-500 bg-red-50 @else border-slate-200 @enderror rounded-xl text-sm focus:ring-blue-500" min="1" required>
|
||||
@error('score_weight') <p class="text-xs text-red-500 mt-1.5 font-medium">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-3 lg:col-span-3">
|
||||
<div>
|
||||
<label class="block text-sm font-bold text-slate-700 mb-2">Passing Grade</label>
|
||||
<input type="number" name="passing_grade" value="{{ old('passing_grade', $question->passing_grade ?? 0) }}" class="w-full px-3 py-2 bg-slate-50 border border-slate-200 rounded-xl text-sm focus:ring-blue-500" min="0" max="100">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-bold text-slate-700 mb-2">Durasi (Detik)</label>
|
||||
<input type="number" name="duration" value="{{ old('duration', $question->duration ?? 0) }}" class="w-full px-3 py-2 bg-slate-50 border border-slate-200 rounded-xl text-sm focus:ring-blue-500">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border-t border-slate-100 pt-6">
|
||||
<label class="block text-sm font-bold text-slate-700 mb-2">Teks Pertanyaan <span class="text-red-500">*</span></label>
|
||||
<textarea name="question_text" rows="4" required class="w-full px-4 py-3 bg-slate-50 border @error('question_text') border-red-500 bg-red-50 @else border-slate-200 @enderror rounded-xl text-sm focus:bg-white focus:ring-2 focus:ring-blue-500/20" placeholder="Tuliskan pertanyaan di sini...">{{ old('question_text', $question->question_text) }}</textarea>
|
||||
@error('question_text') <p class="text-xs text-red-500 mt-1.5 font-medium">{{ $message }}</p> @enderror
|
||||
</div>
|
||||
|
||||
<div class="border-t border-slate-100 pt-6 bg-slate-50/50 p-6 rounded-xl mt-6 border" x-show="questionType !== ''" x-cloak>
|
||||
<h3 class="text-sm font-bold text-slate-800 uppercase tracking-wider mb-4">Pengaturan Opsi & Kunci Jawaban</h3>
|
||||
|
||||
<div x-show="questionType === 'single'" class="space-y-3">
|
||||
@foreach(['A' => 'option_a', 'B' => 'option_b', 'C' => 'option_c', 'D' => 'option_d', 'E' => 'option_e'] as $key => $col)
|
||||
<div class="flex items-center gap-3">
|
||||
<input type="radio" name="correct_answer" value="{{ $key }}" {{ old('correct_answer', $question->correct_answer) == $key ? 'checked' : '' }} class="w-5 h-5 text-blue-600 border-slate-300">
|
||||
<span class="font-bold text-slate-500">{{ $key }}.</span>
|
||||
<input type="text" name="{{ $col }}" value="{{ old($col, $question->$col) }}" placeholder="Opsi {{ $key }}" class="flex-1 px-4 py-2 bg-white border border-slate-200 rounded-lg text-sm focus:ring-blue-500">
|
||||
</div>
|
||||
@endforeach
|
||||
<p class="text-xs text-slate-500 mt-2">* Klik radio button (bulatan) untuk menandai kunci jawaban yang benar.</p>
|
||||
</div>
|
||||
|
||||
<div x-show="questionType === 'multiple'" class="space-y-3">
|
||||
@php
|
||||
$correctAnswers = is_array($question->correct_answer) ? $question->correct_answer : json_decode($question->correct_answer, true) ?? explode(',', $question->correct_answer ?? '');
|
||||
@endphp
|
||||
|
||||
@foreach(['A' => 'option_a', 'B' => 'option_b', 'C' => 'option_c', 'D' => 'option_d', 'E' => 'option_e'] as $key => $col)
|
||||
<div class="flex items-center gap-3">
|
||||
<input type="checkbox" name="correct_answer[]" value="{{ $key }}" {{ in_array($key, (array) old('correct_answer', $correctAnswers)) ? 'checked' : '' }} class="w-5 h-5 text-blue-600 border-slate-300 rounded">
|
||||
<span class="font-bold text-slate-500">{{ $key }}.</span>
|
||||
<input type="text" name="{{ $col }}" value="{{ old($col, $question->$col) }}" placeholder="Opsi {{ $key }}" class="flex-1 px-4 py-2 bg-white border border-slate-200 rounded-lg text-sm focus:ring-blue-500">
|
||||
</div>
|
||||
@endforeach
|
||||
<p class="text-xs text-slate-500 mt-2">* Centang kotak (checkbox) untuk menandai lebih dari satu kunci jawaban.</p>
|
||||
</div>
|
||||
|
||||
<div x-show="questionType === 'true_false'" class="flex gap-4">
|
||||
<label class="flex items-center gap-3 p-4 bg-white border border-slate-200 rounded-xl cursor-pointer hover:border-emerald-500 hover:bg-emerald-50 transition-colors w-48">
|
||||
<input type="radio" name="correct_answer" value="True" {{ old('correct_answer', $question->correct_answer) == 'True' ? 'checked' : '' }} class="w-5 h-5 text-emerald-600 border-slate-300">
|
||||
<span class="font-bold text-slate-700">True (Benar)</span>
|
||||
</label>
|
||||
<label class="flex items-center gap-3 p-4 bg-white border border-slate-200 rounded-xl cursor-pointer hover:border-red-500 hover:bg-red-50 transition-colors w-48">
|
||||
<input type="radio" name="correct_answer" value="False" {{ old('correct_answer', $question->correct_answer) == 'False' ? 'checked' : '' }} class="w-5 h-5 text-red-600 border-slate-300">
|
||||
<span class="font-bold text-slate-700">False (Salah)</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div x-show="questionType === 'descriptive'">
|
||||
<label class="block text-sm font-bold text-slate-700 mb-2">Keyword Jawaban (Pisahkan dengan koma)</label>
|
||||
<textarea name="essay_keywords" rows="3" class="w-full px-4 py-3 bg-white border @error('essay_keywords') border-red-500 bg-red-50 @else border-slate-200 @enderror rounded-xl text-sm focus:ring-blue-500" placeholder="Contoh: mesin washing, suhu 80 derajat, steril">{{ old('essay_keywords', $question->essay_keywords) }}</textarea>
|
||||
@error('essay_keywords') <p class="text-xs text-red-500 mt-1.5 font-medium">{{ $message }}</p> @enderror
|
||||
<p class="text-[11px] text-slate-500 mt-2">* Sistem akan mendeteksi kata kunci ini pada teks jawaban peserta untuk memberikan nilai hitungan awal secara otomatis.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-6 flex justify-end space-x-3 border-t border-slate-100">
|
||||
<a href="{{ route('admin.question-bank.index') }}" class="px-5 py-2 text-sm font-semibold text-slate-600 hover:bg-slate-100 rounded-xl transition-colors">Batal</a>
|
||||
<button type="submit" class="px-6 py-2 text-sm font-bold text-white bg-blue-600 hover:bg-blue-700 rounded-xl shadow-sm transition-colors">Simpan Perubahan</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Inisialisasi Select2
|
||||
$('.select2').select2({ width: '100%' });
|
||||
|
||||
// Memastikan AlpineJS selalu selaras ketika Select2 diubah
|
||||
$('select[name="question_type"]').on('change', function() {
|
||||
let container = document.getElementById('exam-edit-container');
|
||||
if(container) {
|
||||
Alpine.$data(container).questionType = $(this).val();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -3,7 +3,7 @@
|
||||
@section('content')
|
||||
<div class="max-w-3xl mx-auto px-4 py-8">
|
||||
<div class="mb-6 flex items-center space-x-3">
|
||||
<a href="{{ route('admin.exams.questions') }}" class="text-slate-400 hover:text-blue-600 transition-colors">
|
||||
<a href="{{ route('admin.question-bank.index') }}" class="text-slate-400 hover:text-blue-600 transition-colors">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path></svg>
|
||||
</a>
|
||||
<h2 class="text-2xl font-bold text-slate-800 tracking-tight">Import Bank Soal</h2>
|
||||
|
||||
Reference in New Issue
Block a user