Perbaikan modul data karyawan dan staff + modul SOP import
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
@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; }
|
||||
</style>
|
||||
|
||||
<div class="max-w-5xl mx-auto px-4 py-8" x-data="{ questionType: '{{ old('question_type', '') }}' }">
|
||||
<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">
|
||||
<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">Buat Soal Baru</h2>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-sm border border-slate-200 overflow-hidden">
|
||||
<form action="{{ url('admin/exams/questions') }}" method="POST" class="p-6 sm:p-8 space-y-6">
|
||||
@csrf
|
||||
|
||||
<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" required>
|
||||
<option value="">-- Pilih Judul Materi --</option>
|
||||
@foreach($subjects as $subj)
|
||||
<option value="{{ $subj->id }}" {{ old('subject_id') == $subj->id ? 'selected' : '' }}>{{ $subj->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</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">
|
||||
<option value="">-- Global (Semua Dept) --</option>
|
||||
@foreach($departments as $dept)
|
||||
<option value="{{ $dept->id }}" {{ old('department_id') == $dept->id ? 'selected' : '' }}>{{ $dept->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</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">
|
||||
<option value="">-- Global (Semua Jabatan) --</option>
|
||||
@foreach($positions as $pos)
|
||||
<option value="{{ $pos->id }}" {{ old('position_id') == $pos->id ? 'selected' : '' }}>{{ $pos->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</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" required x-model="questionType">
|
||||
<option value="">-- Pilih Tipe --</option>
|
||||
<option value="single">Single Choice (1 Kunci)</option>
|
||||
<option value="multiple">Multiple Choice (Multi Kunci)</option>
|
||||
<option value="true_false">True / False</option>
|
||||
<option value="descriptive">Descriptive (Esai)</option>
|
||||
</select>
|
||||
</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" required>
|
||||
<option value="mudah" {{ old('question_level') == 'mudah' ? 'selected' : '' }}>Mudah</option>
|
||||
<option value="sedang" {{ old('question_level', 'sedang') == 'sedang' ? 'selected' : '' }}>Sedang</option>
|
||||
<option value="sulit" {{ old('question_level') == 'sulit' ? 'selected' : '' }}>Sulit</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-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', 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', 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 border-slate-200 rounded-xl text-sm focus:bg-white focus:ring-2 focus:ring-blue-500/20" placeholder="Tuliskan pertanyaan di sini...">{{ old('question_text') }}</textarea>
|
||||
</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 }}" 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 }}" 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">
|
||||
@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 }}" 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 }}" 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" 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" 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">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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pt-6 flex justify-end space-x-3 border-t border-slate-100">
|
||||
<a href="{{ route('admin.exams.questions') }}" 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 Soal</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('.select2').select2({ width: '100%' });
|
||||
|
||||
// Integrasi Select2 dengan Alpine.js
|
||||
$('select[name="question_type"]').on('change', function() {
|
||||
let el = document.querySelector('[x-data]');
|
||||
let data = Alpine.$data(el);
|
||||
data.questionType = $(this).val();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,49 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@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">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-sm border border-slate-200 overflow-hidden p-6 sm:p-8">
|
||||
<div class="mb-8 p-4 bg-blue-50 border border-blue-100 rounded-xl text-sm text-blue-800 flex items-start gap-4">
|
||||
<svg class="w-6 h-6 text-blue-500 mt-0.5" 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>
|
||||
<div>
|
||||
<p class="font-bold mb-1">Panduan Import</p>
|
||||
<p>Gunakan format Excel standar untuk mengimpor soal. Pastikan kolom Departemen ID, Posisi ID, dan Subject ID diisi menggunakan ID numerik (bukan nama/teks). Tipe soal ditulis sebagai <code>single</code>, <code>multiple</code>, <code>true_false</code>, atau <code>descriptive</code>.</p>
|
||||
<a href="#" class="inline-block mt-3 px-4 py-2 bg-white text-blue-600 font-bold border border-blue-200 rounded-lg hover:bg-blue-100 transition-colors shadow-sm text-xs">Unduh Template Excel</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form action="{{ url('admin/exams/questions/import') }}" method="POST" enctype="multipart/form-data" class="space-y-6">
|
||||
@csrf
|
||||
|
||||
<div class="border-2 border-dashed border-slate-300 rounded-2xl p-10 text-center hover:bg-slate-50 transition-colors relative">
|
||||
<input type="file" name="file" required accept=".xlsx,.xls,.csv" class="absolute inset-0 w-full h-full opacity-0 cursor-pointer" id="file_upload">
|
||||
<svg class="w-12 h-12 text-slate-400 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 13h6m-3-3v6m5 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg>
|
||||
<p class="text-sm font-bold text-slate-700">Klik untuk memilih file excel</p>
|
||||
<p class="text-xs text-slate-500 mt-1" id="file_name">atau seret dan lepas (Drag & Drop) di sini (.xlsx, .csv)</p>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-4">
|
||||
<button type="submit" class="px-6 py-2.5 text-sm font-bold text-white bg-slate-800 hover:bg-slate-900 rounded-xl shadow-md transition-colors w-full md:w-auto">
|
||||
Mulai Proses Import
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Script sederhana untuk menampilkan nama file yang dipilih
|
||||
document.getElementById('file_upload').addEventListener('change', function(e) {
|
||||
if(e.target.files.length > 0) {
|
||||
document.getElementById('file_name').innerHTML = '<span class="text-blue-600 font-bold">' + e.target.files[0].name + '</span> siap diunggah.';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,90 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('content')
|
||||
<div class="max-w-4xl mx-auto px-4 py-8">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<div class="flex items-center space-x-3">
|
||||
<a href="{{ route('admin.exams.questions') }}" 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">Pratinjau Soal</h2>
|
||||
</div>
|
||||
<div class="space-x-2">
|
||||
<a href="{{ url('admin/exams/questions/'.$question->id.'/edit') }}" class="px-4 py-2 bg-amber-500 text-white rounded-lg text-sm font-bold hover:bg-amber-600 shadow-sm transition-colors">Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-sm border border-slate-200 p-6 mb-6">
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||
<div>
|
||||
<span class="block text-xs font-semibold text-slate-400 uppercase">Q.ID</span>
|
||||
<span class="block text-sm font-mono font-bold text-slate-900 mt-1">#{{ $question->id }} <span class="font-normal text-slate-400">(Old: {{ $question->old_id ?? '-' }})</span></span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="block text-xs font-semibold text-slate-400 uppercase">Materi</span>
|
||||
<span class="block text-sm font-bold text-slate-800 mt-1">{{ $question->subject->name ?? 'Umum' }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="block text-xs font-semibold text-slate-400 uppercase">Tipe Soal</span>
|
||||
<span class="inline-block mt-1 px-2 py-0.5 bg-indigo-50 text-indigo-700 rounded text-[10px] font-bold uppercase border border-indigo-100">
|
||||
{{ str_replace('_', ' ', $question->question_type) }}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="block text-xs font-semibold text-slate-400 uppercase">Level</span>
|
||||
<span class="block text-sm font-bold mt-1 {{ $question->question_level == 'mudah' ? 'text-emerald-500' : ($question->question_level == 'sedang' ? 'text-amber-500' : 'text-red-500') }}">
|
||||
{{ strtoupper($question->question_level) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-t border-slate-100 mt-4 pt-4 grid grid-cols-1 md:grid-cols-3 gap-4 text-xs text-slate-500">
|
||||
<div><b>Departemen:</b> {{ $question->department->name ?? 'Global' }}</div>
|
||||
<div><b>Posisi:</b> {{ $question->position->name ?? 'Global' }}</div>
|
||||
<div><b>Kriteria:</b> PG: {{ $question->passing_grade }} | Durasi: {{ $question->duration }} dtk</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-2xl shadow-sm border border-slate-200 p-6 sm:p-8">
|
||||
<div class="text-slate-800 text-base font-medium mb-8 leading-relaxed">
|
||||
{!! nl2br(e($question->question_text)) !!}
|
||||
</div>
|
||||
|
||||
<h4 class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-4 border-b border-slate-100 pb-2">Struktur Kunci & Pilihan</h4>
|
||||
|
||||
<div class="space-y-3">
|
||||
@php
|
||||
$ans = $question->correct_answer;
|
||||
$arrKeys = is_string($ans) ? json_decode($ans, true) : (is_array($ans) ? $ans : [$ans]);
|
||||
if(!is_array($arrKeys)) $arrKeys = [];
|
||||
@endphp
|
||||
|
||||
@if($question->question_type === 'descriptive')
|
||||
<div class="p-4 bg-slate-50 border border-slate-200 rounded-xl text-sm text-slate-700">
|
||||
{!! nl2br(e($question->option_a)) !!}
|
||||
</div>
|
||||
@elseif($question->question_type === 'true_false')
|
||||
<div class="p-3 rounded-xl border {{ in_array('True', $arrKeys) || in_array('opt_a', $arrKeys) ? 'bg-emerald-50 border-emerald-200 font-bold text-emerald-900' : 'border-slate-100 text-slate-600' }}">True (Benar)</div>
|
||||
<div class="p-3 rounded-xl border {{ in_array('False', $arrKeys) || in_array('opt_b', $arrKeys) ? 'bg-emerald-50 border-emerald-200 font-bold text-emerald-900' : 'border-slate-100 text-slate-600' }}">False (Salah)</div>
|
||||
@else
|
||||
@foreach(['A' => 'option_a', 'B' => 'option_b', 'C' => 'option_c', 'D' => 'option_d', 'E' => 'option_e'] as $k => $col)
|
||||
@if(!empty($question->$col))
|
||||
<div class="flex items-center justify-between p-3 rounded-xl border {{ in_array($k, $arrKeys) || in_array($col, $arrKeys) ? 'bg-emerald-50 border-emerald-200 text-emerald-900 font-bold shadow-sm' : 'border-slate-100 text-slate-600 bg-slate-50' }}">
|
||||
<div class="flex items-center">
|
||||
<span class="w-7 h-7 rounded-md text-xs font-mono font-bold flex items-center justify-center mr-3 {{ in_array($k, $arrKeys) || in_array($col, $arrKeys) ? 'bg-emerald-500 text-white' : 'bg-slate-200 text-slate-500' }}">{{ $k }}</span>
|
||||
<span>{{ $question->$col }}</span>
|
||||
</div>
|
||||
@if(in_array($k, $arrKeys) || in_array($col, $arrKeys))
|
||||
<span class="text-[10px] bg-emerald-600 text-white px-2 py-1 rounded font-bold uppercase tracking-widest">Kunci</span>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="mt-8 pt-4 border-t border-slate-100 text-xs text-slate-400 text-right">
|
||||
Dibuat oleh: <span class="font-semibold">{{ $question->creator->first_name ?? 'ID '.$question->created_by }}</span> pada {{ $question->created_at->format('d M Y') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
Reference in New Issue
Block a user