feat: add semester parity validation for course duplication and enhance UI for target term selection
This commit is contained in:
parent
5613078c00
commit
c57787eb20
@ -3,6 +3,8 @@
|
|||||||
namespace App\Http\Requests\Admin\Manage;
|
namespace App\Http\Requests\Admin\Manage;
|
||||||
|
|
||||||
use App\Enums\ClassMethod;
|
use App\Enums\ClassMethod;
|
||||||
|
use App\Enums\Semester;
|
||||||
|
use App\Models\AcademicTerm;
|
||||||
use App\Models\Course;
|
use App\Models\Course;
|
||||||
use App\Models\Lecturer;
|
use App\Models\Lecturer;
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
@ -18,6 +20,26 @@ public function authorize(): bool
|
|||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
$academicTermId = $this->input('academic_term_id');
|
$academicTermId = $this->input('academic_term_id');
|
||||||
|
$academicTerm = $academicTermId ? AcademicTerm::find($academicTermId) : null;
|
||||||
|
|
||||||
|
$semesterParityRule = function ($attribute, $value, $fail) use ($academicTerm) {
|
||||||
|
if (! $academicTerm) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$semesterNumber = Course::find($value)?->semester_number;
|
||||||
|
|
||||||
|
if ($semesterNumber === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$isOddSemester = $semesterNumber % 2 === 1;
|
||||||
|
$matches = $academicTerm->semester === Semester::odd ? $isOddSemester : ! $isOddSemester;
|
||||||
|
|
||||||
|
if (! $matches) {
|
||||||
|
$fail('Mata kuliah semester '.$semesterNumber.' tidak sesuai dengan periode '.$academicTerm->semester->label().'.');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if ($this->isMethod('post')) {
|
if ($this->isMethod('post')) {
|
||||||
return [
|
return [
|
||||||
@ -28,6 +50,7 @@ public function rules(): array
|
|||||||
Rule::unique('course_classes', 'course_id')
|
Rule::unique('course_classes', 'course_id')
|
||||||
->where('academic_term_id', $academicTermId)
|
->where('academic_term_id', $academicTermId)
|
||||||
->whereNull('deleted_at'),
|
->whereNull('deleted_at'),
|
||||||
|
$semesterParityRule,
|
||||||
],
|
],
|
||||||
'lecturer_id' => [
|
'lecturer_id' => [
|
||||||
'required',
|
'required',
|
||||||
@ -61,6 +84,7 @@ function ($attribute, $value, $fail) {
|
|||||||
->where('academic_term_id', $academicTermId)
|
->where('academic_term_id', $academicTermId)
|
||||||
->whereNull('deleted_at')
|
->whereNull('deleted_at')
|
||||||
->ignore($this->route('course_class')?->id),
|
->ignore($this->route('course_class')?->id),
|
||||||
|
$semesterParityRule,
|
||||||
],
|
],
|
||||||
'lecturer_id' => [
|
'lecturer_id' => [
|
||||||
'required',
|
'required',
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Http\Requests\Admin\Manage;
|
namespace App\Http\Requests\Admin\Manage;
|
||||||
|
|
||||||
|
use App\Models\AcademicTerm;
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
use Illuminate\Validation\Validator;
|
use Illuminate\Validation\Validator;
|
||||||
@ -26,6 +27,15 @@ public function withValidator(Validator $validator): void
|
|||||||
$validator->after(function (Validator $validator) {
|
$validator->after(function (Validator $validator) {
|
||||||
if ($this->input('source_academic_term_id') === $this->input('target_academic_term_id')) {
|
if ($this->input('source_academic_term_id') === $this->input('target_academic_term_id')) {
|
||||||
$validator->errors()->add('target_academic_term_id', 'Periode tujuan harus berbeda dari periode sumber.');
|
$validator->errors()->add('target_academic_term_id', 'Periode tujuan harus berbeda dari periode sumber.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sourceTerm = AcademicTerm::find($this->input('source_academic_term_id'));
|
||||||
|
$targetTerm = AcademicTerm::find($this->input('target_academic_term_id'));
|
||||||
|
|
||||||
|
if ($sourceTerm && $targetTerm && $sourceTerm->semester !== $targetTerm->semester) {
|
||||||
|
$validator->errors()->add('target_academic_term_id', 'Periode tujuan harus semester yang sama dengan periode sumber (Ganjil ke Ganjil, Genap ke Genap).');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -810,6 +810,15 @@ function DuplicateForm({
|
|||||||
const [sourceTermId, setSourceTermId] = useState('');
|
const [sourceTermId, setSourceTermId] = useState('');
|
||||||
const [targetTermId, setTargetTermId] = useState('');
|
const [targetTermId, setTargetTermId] = useState('');
|
||||||
|
|
||||||
|
const sourceTerm = academicTerms.find(
|
||||||
|
(term) => String(term.id) === sourceTermId,
|
||||||
|
);
|
||||||
|
const targetOptions = academicTerms.filter(
|
||||||
|
(term) =>
|
||||||
|
String(term.id) !== sourceTermId &&
|
||||||
|
(!sourceTerm || term.semester === sourceTerm.semester),
|
||||||
|
);
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
setSourceTermId('');
|
setSourceTermId('');
|
||||||
setTargetTermId('');
|
setTargetTermId('');
|
||||||
@ -834,8 +843,10 @@ function DuplicateForm({
|
|||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Menyalin semua kelas mata kuliah (mata kuliah, dosen
|
Menyalin semua kelas mata kuliah (mata kuliah, dosen
|
||||||
pengampu, metode) dari periode sumber ke periode
|
pengampu, metode) dari periode sumber ke periode
|
||||||
tujuan. Mata kuliah yang sudah punya kelas di periode
|
tujuan. Periode tujuan hanya bisa dipilih dari
|
||||||
tujuan akan dilewati.
|
semester yang sama (Ganjil ke Ganjil, Genap ke Genap).
|
||||||
|
Mata kuliah yang sudah punya kelas di periode tujuan
|
||||||
|
akan dilewati.
|
||||||
</p>
|
</p>
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
<Label>
|
<Label>
|
||||||
@ -849,7 +860,10 @@ function DuplicateForm({
|
|||||||
/>
|
/>
|
||||||
<Select
|
<Select
|
||||||
value={sourceTermId}
|
value={sourceTermId}
|
||||||
onValueChange={setSourceTermId}
|
onValueChange={(value) => {
|
||||||
|
setSourceTermId(value);
|
||||||
|
setTargetTermId('');
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-full">
|
<SelectTrigger className="w-full">
|
||||||
<SelectValue placeholder="Pilih periode sumber" />
|
<SelectValue placeholder="Pilih periode sumber" />
|
||||||
@ -882,12 +896,19 @@ function DuplicateForm({
|
|||||||
<Select
|
<Select
|
||||||
value={targetTermId}
|
value={targetTermId}
|
||||||
onValueChange={setTargetTermId}
|
onValueChange={setTargetTermId}
|
||||||
|
disabled={!sourceTermId}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-full">
|
<SelectTrigger className="w-full">
|
||||||
<SelectValue placeholder="Pilih periode tujuan" />
|
<SelectValue
|
||||||
|
placeholder={
|
||||||
|
sourceTermId
|
||||||
|
? 'Pilih periode tujuan'
|
||||||
|
: 'Pilih periode sumber terlebih dahulu'
|
||||||
|
}
|
||||||
|
/>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{academicTerms.map((term) => (
|
{targetOptions.map((term) => (
|
||||||
<SelectItem
|
<SelectItem
|
||||||
key={term.id}
|
key={term.id}
|
||||||
value={String(term.id)}
|
value={String(term.id)}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user