From 93ddee8fd6f47b2e2b6331c558f47785521e75e2 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Tue, 1 Sep 2026 00:46:21 +0700 Subject: [PATCH] feat: implement many-to-many relationship for academic advising logs and students --- .../AcademicAdvisingLogController.php | 39 --- .../Services/AcademicAdvisingLogRequest.php | 29 +- app/Models/AcademicAdvisingLog.php | 12 +- .../Services/AcademicAdvisingLogService.php | 38 ++- ...05_create_academic_advising_logs_table.php | 2 - ...te_academic_advising_log_student_table.php | 23 ++ .../seeders/AcademicAdvisingLogSeeder.php | 17 +- database/seeders/RolePermissionSeeder.php | 2 + resources/js/components/app-sidebar.tsx | 11 +- .../academic-advising-logs/columns.tsx | 118 +++++---- .../services/academic-advising-logs/index.tsx | 247 ++++++++++++++---- .../academic-advising-logs/columns.tsx | 49 ---- .../lecturer/academic-advising-logs/index.tsx | 156 ----------- resources/js/types/academic-advising-log.ts | 5 +- routes/admin.php | 5 - 15 files changed, 323 insertions(+), 430 deletions(-) create mode 100644 database/migrations/2026_08_31_172620_create_academic_advising_log_student_table.php delete mode 100644 resources/js/pages/lecturer/academic-advising-logs/columns.tsx delete mode 100644 resources/js/pages/lecturer/academic-advising-logs/index.tsx diff --git a/app/Http/Controllers/Admin/Services/AcademicAdvisingLogController.php b/app/Http/Controllers/Admin/Services/AcademicAdvisingLogController.php index 309db1e..3a51511 100644 --- a/app/Http/Controllers/Admin/Services/AcademicAdvisingLogController.php +++ b/app/Http/Controllers/Admin/Services/AcademicAdvisingLogController.php @@ -6,12 +6,10 @@ use App\Http\Requests\Admin\Services\AcademicAdvisingLogRequest; use App\Http\Requests\PaginatedRequest; use App\Models\AcademicAdvisingLog; -use App\Models\Lecturer; use App\Services\Admin\Services\AcademicAdvisingLogService; use App\Services\Admin\Users\LecturerService; use App\Services\Admin\Users\StudentService; use Illuminate\Http\RedirectResponse; -use Illuminate\Http\Request; use Inertia\Inertia; use Inertia\Response; @@ -36,36 +34,8 @@ public function index(PaginatedRequest $request): Response ]); } - public function mine(Request $request): Response - { - $lecturer = $this->currentLecturer($request); - - return Inertia::render('lecturer/academic-advising-logs/index', [ - 'logs' => $this->service->forLecturer($lecturer), - 'advisees' => $lecturer->advisees() - ->with('user.profile') - ->get(['id', 'user_id', 'student_number']), - ]); - } - public function store(AcademicAdvisingLogRequest $request): RedirectResponse { - if ($request->routeIs('lecturer.*')) { - $lecturer = $this->currentLecturer($request); - - $this->service->create([ - 'student_id' => $request->validated('student_id'), - 'lecturer_id' => $lecturer->id, - 'topic' => $request->validated('topic'), - 'notes' => $request->validated('notes'), - 'session_date' => $request->validated('session_date'), - ]); - - Inertia::flash('toast', ['type' => 'success', 'message' => 'Bimbingan akademik berhasil dicatat.']); - - return to_route('lecturer.academic-advising-logs.index'); - } - $this->service->create($request->validated()); Inertia::flash('toast', ['type' => 'success', 'message' => 'Bimbingan akademik berhasil ditambahkan.']); @@ -88,13 +58,4 @@ public function destroy(AcademicAdvisingLog $academicAdvisingLog): RedirectRespo return Inertia::flash('toast', ['type' => 'success', 'message' => 'Bimbingan akademik berhasil dihapus.'])->back(); } - - private function currentLecturer(Request $request): Lecturer - { - $lecturer = $request->user()->lecturer; - - abort_if(! $lecturer, 403); - - return $lecturer; - } } diff --git a/app/Http/Requests/Admin/Services/AcademicAdvisingLogRequest.php b/app/Http/Requests/Admin/Services/AcademicAdvisingLogRequest.php index 88bd081..9eededa 100644 --- a/app/Http/Requests/Admin/Services/AcademicAdvisingLogRequest.php +++ b/app/Http/Requests/Admin/Services/AcademicAdvisingLogRequest.php @@ -9,40 +9,17 @@ class AcademicAdvisingLogRequest extends FormRequest { public function authorize(): bool { - if ($this->routeIs('lecturer.*')) { - return (bool) $this->user()->lecturer; - } - return $this->user()->can($this->isMethod('post') ? 'create-academic-advising-logs' : 'update-academic-advising-logs'); } public function rules(): array { - if ($this->routeIs('lecturer.*')) { - $lecturer = $this->user()->lecturer; - $adviseeIds = $lecturer ? $lecturer->advisees()->pluck('id') : collect(); - - return [ - 'student_id' => ['required', 'integer', Rule::in($adviseeIds)], - 'topic' => ['nullable', 'string', 'max:150'], - 'notes' => ['nullable', 'string'], - 'session_date' => ['nullable', 'date'], - ]; - } - return [ - 'student_id' => ['required', 'integer', Rule::exists('students', 'id')], + 'student_ids' => ['required', 'array', 'min:1'], + 'student_ids.*' => ['integer', Rule::exists('students', 'id')], 'lecturer_id' => ['required', 'integer', Rule::exists('lecturers', 'id')], - 'topic' => ['nullable', 'string', 'max:150'], + 'topic' => ['required', 'string', 'max:150'], 'notes' => ['nullable', 'string'], - 'session_date' => ['nullable', 'date'], - ]; - } - - public function messages(): array - { - return [ - 'student_id.in' => 'Mahasiswa yang dipilih bukan mahasiswa bimbingan Anda.', ]; } } diff --git a/app/Models/AcademicAdvisingLog.php b/app/Models/AcademicAdvisingLog.php index 875b416..bb16d8d 100644 --- a/app/Models/AcademicAdvisingLog.php +++ b/app/Models/AcademicAdvisingLog.php @@ -6,22 +6,16 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; #[Guarded(['id'])] class AcademicAdvisingLog extends Model { use HasFactory; - protected function casts(): array + public function students(): BelongsToMany { - return [ - 'session_date' => 'datetime', - ]; - } - - public function student(): BelongsTo - { - return $this->belongsTo(Student::class); + return $this->belongsToMany(Student::class); } public function lecturer(): BelongsTo diff --git a/app/Services/Admin/Services/AcademicAdvisingLogService.php b/app/Services/Admin/Services/AcademicAdvisingLogService.php index 02ee307..2d360f0 100644 --- a/app/Services/Admin/Services/AcademicAdvisingLogService.php +++ b/app/Services/Admin/Services/AcademicAdvisingLogService.php @@ -3,61 +3,53 @@ namespace App\Services\Admin\Services; use App\Models\AcademicAdvisingLog; -use App\Models\Lecturer; use Illuminate\Contracts\Pagination\LengthAwarePaginator; -use Illuminate\Database\Eloquent\Collection; class AcademicAdvisingLogService { - /** - * @return Collection - */ - public function forLecturer(Lecturer $lecturer): Collection - { - return AcademicAdvisingLog::query() - ->where('lecturer_id', $lecturer->id) - ->with('student.user.profile') - ->latest() - ->get(); - } - public function paginated(int $perPage = 25, string $search = '', ?int $lecturerId = null): LengthAwarePaginator { + $user = auth()->user(); + return AcademicAdvisingLog::query() - ->select(['id', 'student_id', 'lecturer_id', 'topic', 'notes', 'session_date', 'created_at']) + ->select(['id', 'lecturer_id', 'topic', 'notes', 'created_at']) ->with([ - 'student.user.profile', - 'student.department', + 'students.user.profile', + 'students.department', 'lecturer.user.profile', + 'lecturer.departments', ]) ->when($search, fn ($q) => $q->where('topic', 'like', "%{$search}%") - ->orWhereHas('student', fn ($q) => $q->where('student_number', 'like', "%{$search}%") + ->orWhereHas('students', fn ($q) => $q->where('student_number', 'like', "%{$search}%") ->orWhereHas('user.profile', fn ($q) => $q->where('full_name', 'like', "%{$search}%")))) ->when($lecturerId, fn ($q) => $q->where('lecturer_id', $lecturerId)) + ->when($user->hasRole('dosen'), fn ($q) => $q->where('lecturer_id', $user->lecturer?->id)) ->latest() ->paginate($perPage); } public function create(array $data): AcademicAdvisingLog { - return AcademicAdvisingLog::create([ - 'student_id' => $data['student_id'], + $log = AcademicAdvisingLog::create([ 'lecturer_id' => $data['lecturer_id'], 'topic' => $data['topic'] ?? null, 'notes' => $data['notes'] ?? null, - 'session_date' => $data['session_date'] ?? now(), ]); + + $log->students()->attach($data['student_ids']); + + return $log; } public function update(AcademicAdvisingLog $log, array $data): AcademicAdvisingLog { - $log->student_id = $data['student_id']; $log->lecturer_id = $data['lecturer_id']; $log->topic = $data['topic'] ?? null; $log->notes = $data['notes'] ?? null; - $log->session_date = $data['session_date'] ?? now(); $log->update(); + $log->students()->sync($data['student_ids']); + return $log; } diff --git a/database/migrations/2026_08_25_000005_create_academic_advising_logs_table.php b/database/migrations/2026_08_25_000005_create_academic_advising_logs_table.php index fd9f7cb..cbcef49 100644 --- a/database/migrations/2026_08_25_000005_create_academic_advising_logs_table.php +++ b/database/migrations/2026_08_25_000005_create_academic_advising_logs_table.php @@ -10,11 +10,9 @@ public function up(): void { Schema::create('academic_advising_logs', function (Blueprint $table) { $table->id(); - $table->foreignId('student_id')->constrained()->cascadeOnDelete(); $table->foreignId('lecturer_id')->constrained()->cascadeOnDelete(); $table->string('topic', 150)->nullable(); $table->text('notes')->nullable(); - $table->timestamp('session_date')->nullable()->useCurrent(); $table->timestamps(); }); } diff --git a/database/migrations/2026_08_31_172620_create_academic_advising_log_student_table.php b/database/migrations/2026_08_31_172620_create_academic_advising_log_student_table.php new file mode 100644 index 0000000..095cccb --- /dev/null +++ b/database/migrations/2026_08_31_172620_create_academic_advising_log_student_table.php @@ -0,0 +1,23 @@ +id(); + $table->foreignId('academic_advising_log_id')->constrained()->cascadeOnDelete(); + $table->foreignId('student_id')->constrained()->cascadeOnDelete(); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('academic_advising_log_student'); + } +}; diff --git a/database/seeders/AcademicAdvisingLogSeeder.php b/database/seeders/AcademicAdvisingLogSeeder.php index c9b7d7f..a3e8d79 100644 --- a/database/seeders/AcademicAdvisingLogSeeder.php +++ b/database/seeders/AcademicAdvisingLogSeeder.php @@ -9,25 +9,30 @@ class AcademicAdvisingLogSeeder extends Seeder { public function run(): void { - AcademicAdvisingLog::insert([ + $logs = [ [ - 'student_id' => 1, 'lecturer_id' => 1, 'topic' => 'Konsultasi KRS Semester 5', 'notes' => 'Mahasiswa mengambil 21 SKS, disetujui', - 'session_date' => '2026-01-20 13:00:00', 'created_at' => '2026-01-20 13:00:00', 'updated_at' => '2026-01-20 13:00:00', + 'student_ids' => [1], ], [ - 'student_id' => 2, 'lecturer_id' => 2, 'topic' => 'Bimbingan Tugas Akhir Bab 1', 'notes' => 'Perlu perbaikan rumusan masalah', - 'session_date' => '2026-07-15 13:00:00', 'created_at' => '2026-07-15 13:00:00', 'updated_at' => '2026-07-15 13:00:00', + 'student_ids' => [2], ], - ]); + ]; + + foreach ($logs as $log) { + $studentIds = $log['student_ids']; + unset($log['student_ids']); + + AcademicAdvisingLog::create($log)->students()->attach($studentIds); + } } } diff --git a/database/seeders/RolePermissionSeeder.php b/database/seeders/RolePermissionSeeder.php index bc542b5..1c3d853 100644 --- a/database/seeders/RolePermissionSeeder.php +++ b/database/seeders/RolePermissionSeeder.php @@ -50,6 +50,8 @@ public function run(): void 'dosen' => [ 'view-dashboard', 'view-courses', + 'view-academic-advising-logs', + 'view-students', ...$feedbackSelfService, ], 'staff-admin' => [ diff --git a/resources/js/components/app-sidebar.tsx b/resources/js/components/app-sidebar.tsx index 0e764be..c47d08c 100644 --- a/resources/js/components/app-sidebar.tsx +++ b/resources/js/components/app-sidebar.tsx @@ -54,7 +54,6 @@ import { index as letterRequestsRoute } from '@/routes/admin/services/letter-req import { index as administratorsRoute } from '@/routes/admin/users/administrators'; import { index as lecturersRoute } from '@/routes/admin/users/lecturers'; import { index as studentsRoute } from '@/routes/admin/users/students'; -import { index as lecturerAcademicAdvisingLogsRoute } from '@/routes/lecturer/academic-advising-logs'; import { index as studentCourseRegistrationsRoute } from '@/routes/student/course-registrations'; import type { Auth } from '@/types/auth'; @@ -62,11 +61,9 @@ const STAFF_ROLES = ['developer', 'staff-admin', 'staff-keuangan', 'kaprodi']; function buildNavMain({ isMahasiswa, - isDosen, permissions, }: { isMahasiswa: boolean; - isDosen: boolean; permissions: string[]; }): (NavGroup | NavItem)[] { const can = (permission: string) => permissions.includes(permission); @@ -206,13 +203,11 @@ function buildNavMain({ }, ] : []), - ...(isDosen || can('view-academic-advising-logs') + ...(can('view-academic-advising-logs') ? [ { name: 'Bimbingan Akademik', - url: isDosen - ? lecturerAcademicAdvisingLogsRoute.url() - : academicAdvisingLogsRoute.url(), + url: academicAdvisingLogsRoute.url(), icon: MessageCircle, }, ] @@ -297,11 +292,9 @@ export function AppSidebar({ ...props }: React.ComponentProps) { const roleNames = auth?.user?.roles?.map((role) => role.name) ?? []; const isStaff = roleNames.some((role) => STAFF_ROLES.includes(role)); const isMahasiswa = !isStaff && roleNames.includes('mahasiswa'); - const isDosen = !isStaff && roleNames.includes('dosen'); const permissions = auth?.permissions ?? []; const navMain = buildNavMain({ isMahasiswa, - isDosen, permissions, }); diff --git a/resources/js/pages/admin/services/academic-advising-logs/columns.tsx b/resources/js/pages/admin/services/academic-advising-logs/columns.tsx index cebb22a..844206f 100644 --- a/resources/js/pages/admin/services/academic-advising-logs/columns.tsx +++ b/resources/js/pages/admin/services/academic-advising-logs/columns.tsx @@ -1,12 +1,12 @@ import type { ColumnDef } from '@tanstack/react-table'; -import { format } from 'date-fns'; -import { Pencil, Trash2 } from 'lucide-react'; +import { Eye, Pencil, Trash2 } from 'lucide-react'; import { RowActions } from '@/components/row-actions'; import type { AcademicAdvisingLog } from '@/types/academic-advising-log'; export type { AcademicAdvisingLog } from '@/types/academic-advising-log'; type CreateColumnsParams = { + handleView: (log: AcademicAdvisingLog) => void; handleEdit: (log: AcademicAdvisingLog) => void; handleDeleteClick: (log: AcademicAdvisingLog) => void; canUpdate: boolean; @@ -16,24 +16,42 @@ type CreateColumnsParams = { export function createAcademicAdvisingLogColumns( params: CreateColumnsParams, ): ColumnDef[] { - const { handleEdit, handleDeleteClick, canUpdate, canDelete } = params; + const { handleView, handleEdit, handleDeleteClick, canUpdate, canDelete } = + params; const columns: ColumnDef[] = [ { - accessorKey: 'student.student_number', + accessorKey: 'students', header: () => Mahasiswa, cell: ({ row }) => { - const student = row.original.student; + const students = row.original.students; + + if (students.length === 0) { + return -; + } + + const visible = students.slice(0, 2); + const remaining = students.length - visible.length; return ( -
-

- {student?.user?.profile?.full_name ?? 'N/A'} -

-

- {student?.student_number} ·{' '} - {student?.department?.name ?? '-'} -

+
+ {visible.map((student) => ( +
+

+ {student.user?.profile?.full_name ?? + 'N/A'} +

+

+ {student.student_number} ·{' '} + {student.department?.name ?? '-'} +

+
+ ))} + {remaining > 0 && ( +

+ +{remaining} mahasiswa lainnya +

+ )}
); }, @@ -61,49 +79,41 @@ export function createAcademicAdvisingLogColumns( header: () => Topik, cell: ({ row }) => row.original.topic ?? '-', }, - { - accessorKey: 'session_date', - header: () => Tanggal Sesi, - cell: ({ row }) => { - const sessionDate = row.original.session_date; - - return sessionDate - ? format(new Date(sessionDate), 'd MMM yyyy, HH:mm') - : '-'; - }, - }, ]; - if (canUpdate || canDelete) { - columns.push({ - id: 'actions', - header: () => Aksi, - meta: { - className: 'w-[100px] text-center', - headerClassName: 'w-[100px] text-center', - }, - cell: ({ row }) => ( - , - show: canUpdate, - onClick: () => handleEdit(row.original), - }, - { - label: 'Hapus', - icon: ( - - ), - show: canDelete, - onClick: () => handleDeleteClick(row.original), - }, - ]} - /> - ), - }); - } + columns.push({ + id: 'actions', + header: () => Aksi, + meta: { + className: 'w-[100px] text-center', + headerClassName: 'w-[100px] text-center', + }, + cell: ({ row }) => ( + , + onClick: () => handleView(row.original), + }, + { + label: 'Edit', + icon: , + show: canUpdate, + onClick: () => handleEdit(row.original), + }, + { + label: 'Hapus', + icon: ( + + ), + show: canDelete, + onClick: () => handleDeleteClick(row.original), + }, + ]} + /> + ), + }); return columns; } diff --git a/resources/js/pages/admin/services/academic-advising-logs/index.tsx b/resources/js/pages/admin/services/academic-advising-logs/index.tsx index eb803c6..1faeffa 100644 --- a/resources/js/pages/admin/services/academic-advising-logs/index.tsx +++ b/resources/js/pages/admin/services/academic-advising-logs/index.tsx @@ -1,9 +1,9 @@ import { Head, router } from '@inertiajs/react'; +import { format } from 'date-fns'; import { Plus } from 'lucide-react'; import { useState } from 'react'; import type { PaginationState } from '@/components/data-table'; import { DataTable } from '@/components/data-table'; -import { DateTimeField } from '@/components/datetime-field'; import { DeleteConfirmDialog } from '@/components/delete-confirm-dialog'; import type { FilterField } from '@/components/filter-dialog'; import { FilterDialog } from '@/components/filter-dialog'; @@ -13,12 +13,22 @@ import { PageHeader } from '@/components/page-header'; import { Button } from '@/components/ui/button'; import { Combobox, + ComboboxChip, + ComboboxChips, + ComboboxChipsInput, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, + useComboboxAnchor, } from '@/components/ui/combobox'; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; @@ -71,6 +81,7 @@ export default function AcademicAdvisingLogIndex({ const [createOpen, setCreateOpen] = useState(false); const [editing, setEditing] = useState(null); const [deleting, setDeleting] = useState(null); + const [viewing, setViewing] = useState(null); const { hasPermission } = usePermissions(); const canCreate = hasPermission('create-academic-advising-logs'); const canUpdate = hasPermission('update-academic-advising-logs'); @@ -117,6 +128,7 @@ export default function AcademicAdvisingLogIndex({ } const columns = createAcademicAdvisingLogColumns({ + handleView: (log) => setViewing(log), handleEdit: (log) => setEditing(log), handleDeleteClick: (log) => setDeleting(log), canUpdate, @@ -172,6 +184,16 @@ export default function AcademicAdvisingLogIndex({ lecturers={lecturers} /> + { + if (!open) { + setViewing(null); + } + }} + log={viewing} + /> + - `Apakah Anda yakin ingin menghapus log bimbingan untuk "${log.student?.user?.profile?.full_name ?? 'mahasiswa ini'}"? Tindakan ini tidak dapat dibatalkan.` + `Apakah Anda yakin ingin menghapus log bimbingan untuk "${log.students.map((s) => s.user?.profile?.full_name ?? 'N/A').join(', ') || 'mahasiswa ini'}"? Tindakan ini tidak dapat dibatalkan.` } onConfirm={handleDelete} /> @@ -208,6 +230,84 @@ export default function AcademicAdvisingLogIndex({ ); } +function ViewDetailDialog({ + open, + onOpenChange, + log, +}: { + open: boolean; + onOpenChange: (open: boolean) => void; + log: AcademicAdvisingLog | null; +}) { + return ( + + + + Detail Bimbingan Akademik + + + {log && ( +
+ + Dicatat{' '} + {format( + new Date(log.created_at), + 'd MMM yyyy, HH:mm', + )} + + +
+ +

+ {log.lecturer?.user?.profile?.full_name ?? + 'N/A'}{' '} + · {log.lecturer?.lecturer_number} +

+
+ +
+ + {log.students.length === 0 ? ( +

-

+ ) : ( +
    + {log.students.map((student) => ( +
  • + {student.user?.profile + ?.full_name ?? 'N/A'}{' '} + · {student.student_number} +
  • + ))} +
+ )} +
+ +
+ +

{log.topic ?? '-'}

+
+ +
+ +

+ {log.notes ?? '-'} +

+
+
+ )} +
+
+ ); +} + function AdvisingLogFields({ errors, editing, @@ -219,49 +319,23 @@ function AdvisingLogFields({ students: AcademicAdvisingLogStudent[]; lecturers: AcademicAdvisingLogLecturer[]; }) { - const [student, setStudent] = useState( - editing?.student ?? null, - ); const [lecturer, setLecturer] = useState(editing?.lecturer ?? null); + const [selectedStudents, setSelectedStudents] = useState< + AcademicAdvisingLogStudent[] + >(editing?.students ?? []); + const studentAnchor = useComboboxAnchor(); + + const availableStudents = lecturer + ? students.filter((s) => + lecturer.departments.some( + (department) => department.id === s.department?.id, + ), + ) + : []; return ( <> -
- - - studentLabel(s)} - isItemEqualToValue={(a, b) => a.id === b.id} - > - - - - Mahasiswa tidak ditemukan. - - - {(s: AcademicAdvisingLogStudent) => ( - - {studentLabel(s)} - - )} - - - - -
- +
+ + +
+ {selectedStudents.map((s) => ( + + ))} + studentLabel(s)} + isItemEqualToValue={(a, b) => a.id === b.id} + > + + {selectedStudents.map((s) => ( + + {studentLabel(s)} + + ))} + 0 + ? '' + : lecturer + ? 'Pilih mahasiswa' + : 'Pilih dosen terlebih dahulu' + } + /> + + + + Mahasiswa tidak ditemukan. + + + {(s: AcademicAdvisingLogStudent) => ( + + {studentLabel(s)} + + )} + + + + +
+
+
- ); } diff --git a/resources/js/pages/lecturer/academic-advising-logs/columns.tsx b/resources/js/pages/lecturer/academic-advising-logs/columns.tsx deleted file mode 100644 index 0fdcc95..0000000 --- a/resources/js/pages/lecturer/academic-advising-logs/columns.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import type { ColumnDef } from '@tanstack/react-table'; -import { format } from 'date-fns'; -import type { AcademicAdvisingLog } from '@/types/academic-advising-log'; - -export const advisingLogColumns: ColumnDef[] = [ - { - accessorKey: 'student.student_number', - header: () => Mahasiswa, - cell: ({ row }) => { - const student = row.original.student; - - return ( -
-

- {student?.user?.profile?.full_name ?? 'N/A'} -

-

- {student?.student_number} -

-
- ); - }, - }, - { - accessorKey: 'topic', - header: () => Topik, - cell: ({ row }) => row.original.topic || '-', - }, - { - accessorKey: 'notes', - header: () => Catatan, - cell: ({ row }) => ( -

- {row.original.notes || '-'} -

- ), - }, - { - accessorKey: 'session_date', - header: () => Tanggal Sesi, - cell: ({ row }) => { - const sessionDate = row.original.session_date; - - return sessionDate - ? format(new Date(sessionDate), 'd MMM yyyy, HH:mm') - : '-'; - }, - }, -]; diff --git a/resources/js/pages/lecturer/academic-advising-logs/index.tsx b/resources/js/pages/lecturer/academic-advising-logs/index.tsx deleted file mode 100644 index 578e872..0000000 --- a/resources/js/pages/lecturer/academic-advising-logs/index.tsx +++ /dev/null @@ -1,156 +0,0 @@ -import { Head } from '@inertiajs/react'; -import { Plus } from 'lucide-react'; -import { useState } from 'react'; -import { DataTable } from '@/components/data-table'; -import { DateTimeField } from '@/components/datetime-field'; -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 { 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 { store } from '@/routes/lecturer/academic-advising-logs'; -import type { AcademicAdvisingLog } from '@/types/academic-advising-log'; -import { advisingLogColumns } from './columns'; - -type Advisee = { - id: number; - student_number: string; - user: { profile: { full_name: string } | null } | null; -}; - -type Props = { - logs: AcademicAdvisingLog[]; - advisees: Advisee[]; -}; - -function adviseeLabel(advisee: Advisee): string { - return `${advisee.user?.profile?.full_name ?? 'N/A'} - ${advisee.student_number}`; -} - -export default function LecturerAcademicAdvisingLogIndex({ - logs, - advisees, -}: Props) { - const [createOpen, setCreateOpen] = useState(false); - - return ( - <> - - -
- - - - } - /> - - - - -
- - ); -} - -function CreateForm({ - open, - onOpenChange, - advisees, -}: { - open: boolean; - onOpenChange: (open: boolean) => void; - advisees: Advisee[]; -}) { - return ( - onOpenChange(false)} - > - {({ errors }) => ( -
-
- - - {advisees.length === 0 && ( -

- Anda belum memiliki mahasiswa bimbingan. -

- )} - -
-
- - - -
-
- -