feat: update LetterRequestRequest validation and enhance student selection with Combobox in forms
This commit is contained in:
parent
8be0d57e16
commit
04f8f770bb
@ -30,7 +30,7 @@ public function rules(): array
|
||||
'student_id' => ['required', 'integer', Rule::exists('students', 'id')],
|
||||
'letter_type' => ['required', 'string', 'max:100'],
|
||||
'purpose' => ['nullable', 'string'],
|
||||
'status' => ['nullable', Rule::enum(LetterStatus::class)],
|
||||
'status' => ['required', Rule::enum(LetterStatus::class)],
|
||||
'result' => ['nullable', 'file', 'max:10240', 'mimes:pdf,jpg,jpeg,png,doc,docx'],
|
||||
];
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ export function FormDialog({
|
||||
<DialogHeader className="shrink-0">
|
||||
<DialogTitle>{title}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="grid flex-1 gap-4 overflow-y-auto py-4">
|
||||
<div className="grid flex-1 gap-4 overflow-x-hidden overflow-y-auto py-4">
|
||||
{typeof children === 'function'
|
||||
? children({ errors, processing })
|
||||
: children}
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
import { Head, Link, router } from '@inertiajs/react';
|
||||
import { format } from 'date-fns';
|
||||
import { Info, Plus } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import type { PaginationState } from '@/components/data-table';
|
||||
import { DataTable } from '@/components/data-table';
|
||||
import { DeleteConfirmDialog } from '@/components/delete-confirm-dialog';
|
||||
@ -27,10 +31,6 @@ import {
|
||||
} from '@/routes/admin/feedback';
|
||||
import type { Feedback } from '@/types/feedback';
|
||||
import { FeedbackStatusLabels, FeedbackTypeLabels } from '@/types/feedback';
|
||||
import { Head, Link, router } from '@inertiajs/react';
|
||||
import { format } from 'date-fns';
|
||||
import { Info, Plus } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { createFeedbackColumns, FeedbackStatusVariants } from './columns';
|
||||
|
||||
type FeedbackTypeOption = { value: string; label: string };
|
||||
@ -159,8 +159,8 @@ export default function FeedbackIndex({
|
||||
<Info />
|
||||
<AlertTitle>Ubah status Kritik dan Saran</AlertTitle>
|
||||
<AlertDescription>
|
||||
Klik badge Status pada tabel untuk mengubah status Kritik dan Saran
|
||||
secara langsung.
|
||||
Klik badge Status pada tabel untuk mengubah status
|
||||
Kritik dan Saran secara langsung.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
@ -217,7 +217,7 @@ function ViewDetailDialog({
|
||||
</DialogHeader>
|
||||
|
||||
{feedback && (
|
||||
<div className="grid min-h-0 flex-1 gap-4 overflow-y-auto">
|
||||
<div className="grid min-h-0 flex-1 gap-4 overflow-x-hidden overflow-y-auto">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Badge variant="outline">
|
||||
{FeedbackTypeLabels[feedback.type]}
|
||||
|
||||
@ -11,15 +11,16 @@ import { FormDialog } from '@/components/form-dialog';
|
||||
import InputError from '@/components/input-error';
|
||||
import { PageHeader } from '@/components/page-header';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Combobox,
|
||||
ComboboxContent,
|
||||
ComboboxEmpty,
|
||||
ComboboxInput,
|
||||
ComboboxItem,
|
||||
ComboboxList,
|
||||
} from '@/components/ui/combobox';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { usePermissions } from '@/hooks/use-permissions';
|
||||
import { useServerTable } from '@/hooks/use-server-table';
|
||||
@ -218,60 +219,80 @@ function AdvisingLogFields({
|
||||
students: AcademicAdvisingLogStudent[];
|
||||
lecturers: AcademicAdvisingLogLecturer[];
|
||||
}) {
|
||||
const [student, setStudent] = useState<AcademicAdvisingLogStudent | null>(
|
||||
editing?.student ?? null,
|
||||
);
|
||||
const [lecturer, setLecturer] =
|
||||
useState<AcademicAdvisingLogLecturer | null>(editing?.lecturer ?? null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid gap-2">
|
||||
<Label>
|
||||
Mahasiswa <span className="text-destructive">*</span>
|
||||
</Label>
|
||||
{!editing && <input type="hidden" name="student_id" />}
|
||||
<Select
|
||||
<input
|
||||
type="hidden"
|
||||
name="student_id"
|
||||
defaultValue={
|
||||
editing ? String(editing.student_id) : undefined
|
||||
}
|
||||
value={student?.id ?? ''}
|
||||
/>
|
||||
<Combobox
|
||||
items={students}
|
||||
value={student}
|
||||
onValueChange={setStudent}
|
||||
itemToStringLabel={(s) => studentLabel(s)}
|
||||
isItemEqualToValue={(a, b) => a.id === b.id}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="Pilih mahasiswa" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{students.map((student) => (
|
||||
<SelectItem
|
||||
key={student.id}
|
||||
value={String(student.id)}
|
||||
>
|
||||
{studentLabel(student)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<ComboboxInput
|
||||
placeholder="Pilih mahasiswa"
|
||||
className="w-full"
|
||||
/>
|
||||
<ComboboxContent>
|
||||
<ComboboxEmpty>
|
||||
Mahasiswa tidak ditemukan.
|
||||
</ComboboxEmpty>
|
||||
<ComboboxList>
|
||||
{(s: AcademicAdvisingLogStudent) => (
|
||||
<ComboboxItem key={s.id} value={s}>
|
||||
{studentLabel(s)}
|
||||
</ComboboxItem>
|
||||
)}
|
||||
</ComboboxList>
|
||||
</ComboboxContent>
|
||||
</Combobox>
|
||||
<InputError message={errors.student_id} />
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label>
|
||||
Dosen Wali <span className="text-destructive">*</span>
|
||||
</Label>
|
||||
{!editing && <input type="hidden" name="lecturer_id" />}
|
||||
<Select
|
||||
<input
|
||||
type="hidden"
|
||||
name="lecturer_id"
|
||||
defaultValue={
|
||||
editing ? String(editing.lecturer_id) : undefined
|
||||
}
|
||||
value={lecturer?.id ?? ''}
|
||||
/>
|
||||
<Combobox
|
||||
items={lecturers}
|
||||
value={lecturer}
|
||||
onValueChange={setLecturer}
|
||||
itemToStringLabel={(l) => lecturerLabel(l)}
|
||||
isItemEqualToValue={(a, b) => a.id === b.id}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="Pilih dosen" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{lecturers.map((lecturer) => (
|
||||
<SelectItem
|
||||
key={lecturer.id}
|
||||
value={String(lecturer.id)}
|
||||
>
|
||||
{lecturerLabel(lecturer)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<ComboboxInput
|
||||
placeholder="Pilih dosen"
|
||||
className="w-full"
|
||||
/>
|
||||
<ComboboxContent>
|
||||
<ComboboxEmpty>Dosen tidak ditemukan.</ComboboxEmpty>
|
||||
<ComboboxList>
|
||||
{(l: AcademicAdvisingLogLecturer) => (
|
||||
<ComboboxItem key={l.id} value={l}>
|
||||
{lecturerLabel(l)}
|
||||
</ComboboxItem>
|
||||
)}
|
||||
</ComboboxList>
|
||||
</ComboboxContent>
|
||||
</Combobox>
|
||||
<InputError message={errors.lecturer_id} />
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
@ -330,6 +351,7 @@ function CreateForm({
|
||||
{({ errors }) => (
|
||||
<div className="grid gap-4">
|
||||
<AdvisingLogFields
|
||||
key={open ? 'open' : 'closed'}
|
||||
errors={errors}
|
||||
students={students}
|
||||
lecturers={lecturers}
|
||||
|
||||
@ -11,6 +11,14 @@ import { FormDialog } from '@/components/form-dialog';
|
||||
import InputError from '@/components/input-error';
|
||||
import { PageHeader } from '@/components/page-header';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
Combobox,
|
||||
ComboboxContent,
|
||||
ComboboxEmpty,
|
||||
ComboboxInput,
|
||||
ComboboxItem,
|
||||
ComboboxList,
|
||||
} from '@/components/ui/combobox';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import {
|
||||
@ -210,33 +218,45 @@ function LetterRequestFields({
|
||||
students: LetterRequestStudent[];
|
||||
resetKey?: string;
|
||||
}) {
|
||||
const [student, setStudent] = useState<LetterRequestStudent | null>(
|
||||
editing?.student ?? null,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid gap-2">
|
||||
<Label>
|
||||
Mahasiswa <span className="text-destructive">*</span>
|
||||
</Label>
|
||||
{!editing && <input type="hidden" name="student_id" />}
|
||||
<Select
|
||||
<input
|
||||
type="hidden"
|
||||
name="student_id"
|
||||
defaultValue={
|
||||
editing ? String(editing.student_id) : undefined
|
||||
}
|
||||
value={student?.id ?? ''}
|
||||
/>
|
||||
<Combobox
|
||||
items={students}
|
||||
value={student}
|
||||
onValueChange={setStudent}
|
||||
itemToStringLabel={(s) => studentLabel(s)}
|
||||
isItemEqualToValue={(a, b) => a.id === b.id}
|
||||
>
|
||||
<SelectTrigger className="w-full">
|
||||
<SelectValue placeholder="Pilih mahasiswa" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{students.map((student) => (
|
||||
<SelectItem
|
||||
key={student.id}
|
||||
value={String(student.id)}
|
||||
>
|
||||
{studentLabel(student)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<ComboboxInput
|
||||
placeholder="Pilih mahasiswa"
|
||||
className="w-full"
|
||||
/>
|
||||
<ComboboxContent>
|
||||
<ComboboxEmpty>
|
||||
Mahasiswa tidak ditemukan.
|
||||
</ComboboxEmpty>
|
||||
<ComboboxList>
|
||||
{(s: LetterRequestStudent) => (
|
||||
<ComboboxItem key={s.id} value={s}>
|
||||
{studentLabel(s)}
|
||||
</ComboboxItem>
|
||||
)}
|
||||
</ComboboxList>
|
||||
</ComboboxContent>
|
||||
</Combobox>
|
||||
<InputError message={errors.student_id} />
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
@ -264,7 +284,9 @@ function LetterRequestFields({
|
||||
<InputError message={errors.purpose} />
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label>Status</Label>
|
||||
<Label>
|
||||
Status <span className="text-destructive">*</span>
|
||||
</Label>
|
||||
<input type="hidden" name="status" />
|
||||
<Select
|
||||
name="status"
|
||||
@ -316,6 +338,7 @@ function CreateForm({
|
||||
{({ errors }) => (
|
||||
<div className="grid gap-4">
|
||||
<LetterRequestFields
|
||||
key={open ? 'open' : 'closed'}
|
||||
errors={errors}
|
||||
students={students}
|
||||
resetKey={open ? 'open' : 'closed'}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user