From 5e63ef2744af95f1f3cf9ca2e245af753333dfd8 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Tue, 10 Mar 2026 12:04:49 +0700 Subject: [PATCH] feat: Create Student resource with CRUD pages and form schema for managing student data in the admin panel. --- .../Master/Students/Pages/CreateStudent.php | 61 +++++++++ .../Master/Students/Pages/EditStudent.php | 36 ++++++ .../Master/Students/Pages/ListStudents.php | 22 ++++ .../Master/Students/Schemas/StudentForm.php | 116 ++++++++++++++++++ .../Master/Students/StudentResource.php | 52 ++++++++ .../Master/Students/Tables/StudentsTable.php | 113 +++++++++++++++++ 6 files changed, 400 insertions(+) create mode 100644 app/Filament/Resources/Master/Students/Pages/CreateStudent.php create mode 100644 app/Filament/Resources/Master/Students/Pages/EditStudent.php create mode 100644 app/Filament/Resources/Master/Students/Pages/ListStudents.php create mode 100644 app/Filament/Resources/Master/Students/Schemas/StudentForm.php create mode 100644 app/Filament/Resources/Master/Students/StudentResource.php create mode 100644 app/Filament/Resources/Master/Students/Tables/StudentsTable.php diff --git a/app/Filament/Resources/Master/Students/Pages/CreateStudent.php b/app/Filament/Resources/Master/Students/Pages/CreateStudent.php new file mode 100644 index 0000000..51f66a9 --- /dev/null +++ b/app/Filament/Resources/Master/Students/Pages/CreateStudent.php @@ -0,0 +1,61 @@ +url(ListStudents::getUrl()), + ]; + } + + protected function getRedirectUrl(): string + { + return $this->getResource()::getUrl('index'); + } + + protected function getCreatedNotification(): ?Notification + { + return SystemNotification::create(); + } + + public function handleRecordCreation(array $data): Model + { + return DB::transaction(function () use ($data) { + $user = User::create([ + 'email' => $data['email'], + 'username' => $data['username'], + 'password' => Hash::make('Minimal8@'), + ]); + + unset($data['email'], $data['username']); + + $student = $this->getResource()::getModel()::create( + array_merge($data, [ + 'user_id' => $user->id, + ]) + ); + + return $student; + }); + } +} diff --git a/app/Filament/Resources/Master/Students/Pages/EditStudent.php b/app/Filament/Resources/Master/Students/Pages/EditStudent.php new file mode 100644 index 0000000..4c066f1 --- /dev/null +++ b/app/Filament/Resources/Master/Students/Pages/EditStudent.php @@ -0,0 +1,36 @@ +url(ListStudents::getUrl()), + ]; + } + + protected function getRedirectUrl(): string + { + return $this->getResource()::getUrl('index'); + } + + protected function getSavedNotification(): ?Notification + { + return SystemNotification::update(); + } +} diff --git a/app/Filament/Resources/Master/Students/Pages/ListStudents.php b/app/Filament/Resources/Master/Students/Pages/ListStudents.php new file mode 100644 index 0000000..7b0c9b3 --- /dev/null +++ b/app/Filament/Resources/Master/Students/Pages/ListStudents.php @@ -0,0 +1,22 @@ +label('Tambah'), + ]; + } +} diff --git a/app/Filament/Resources/Master/Students/Schemas/StudentForm.php b/app/Filament/Resources/Master/Students/Schemas/StudentForm.php new file mode 100644 index 0000000..a01ddc8 --- /dev/null +++ b/app/Filament/Resources/Master/Students/Schemas/StudentForm.php @@ -0,0 +1,116 @@ +components([ + Section::make('Akun') + ->description(new HtmlString('Alamat Surel dan Nama Pengguna akan digunakan untuk masuk ke dalam sistem. Kata sandi bawaan adalah Minimal8@')) + ->schema([ + TextInput::make('email') + ->label('Alamat Surel') + ->placeholder('johndoe@example.com') + ->autocomplete(false) + ->required() + ->maxLength(254) + ->email() + ->unique('users', 'email', ignoreRecord: true), + + TextInput::make('username') + ->label('Nama Pengguna') + ->placeholder('johndoe') + ->autocomplete(false) + ->required() + ->minLength(5) + ->maxLength(20) + ->regex('/^[a-zA-Z0-9_.-]+$/') + ->unique('users', 'username', ignoreRecord: true), + ]) + ->columns(2) + ->hiddenOn('edit'), + + Section::make('Informasi Mahasiswa') + ->schema([ + TextInput::make('full_name') + ->label('Nama Lengkap') + ->placeholder('John Doe') + ->autocomplete(false) + ->required() + ->maxLength(100) + ->minLength(3) + ->autofocus() + ->columnSpanFull(), + + Grid::make(3) + ->schema([ + TextInput::make('student_number') + ->label('NIM') + ->placeholder('1234567890') + ->autocomplete(false) + ->required() + ->numeric() + ->rules(['digits:10']) + ->unique(ignoreRecord: true), + + TextInput::make('phone_number') + ->label('Nomor Telepon') + ->placeholder('0812 3456 78910') + ->autocomplete(false) + ->required() + ->maxLength(20) + ->minLength(10) + ->rules(['regex:/^[0-9 ]+$/']) + ->mask('9999 9999 99999'), + + Radio::make('sex') + ->label('Jenis Kelamin') + ->options(Sex::class) + ->required() + ->inline(), + ]) + ->columnSpanFull(), + + Textarea::make('address') + ->label('Alamat') + ->placeholder('Jl. Contoh No. 123, Kota, Provinsi') + ->autocomplete(false) + ->rows(3) + ->nullable() + ->columnSpanFull(), + + DatePicker::make('date_of_birth') + ->label('Tanggal Lahir') + ->placeholder('Pilih Tanggal') + ->required() + ->native(false) + ->displayFormat('l, d F Y') + ->maxDate(Carbon::now()->subYears(15)), + + TextInput::make('place_of_birth') + ->label('Tempat Lahir') + ->placeholder('Purwakarta') + ->autocomplete(false) + ->required() + ->maxLength(50) + ->minLength(3), + ]) + ->columns(2), + ]) + ->columns(1); + } +} diff --git a/app/Filament/Resources/Master/Students/StudentResource.php b/app/Filament/Resources/Master/Students/StudentResource.php new file mode 100644 index 0000000..3bb10df --- /dev/null +++ b/app/Filament/Resources/Master/Students/StudentResource.php @@ -0,0 +1,52 @@ + ListStudents::route('/'), + 'create' => CreateStudent::route('/create'), + 'edit' => EditStudent::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Resources/Master/Students/Tables/StudentsTable.php b/app/Filament/Resources/Master/Students/Tables/StudentsTable.php new file mode 100644 index 0000000..a8d91b8 --- /dev/null +++ b/app/Filament/Resources/Master/Students/Tables/StudentsTable.php @@ -0,0 +1,113 @@ +columns([ + TextColumn::make('user.email') + ->label('Akun') + ->formatStateUsing(fn ($state, Student $record) => " +
+
Alamat Surel: {$state}
+
Nama Pengguna: {$record->user?->username}
+
+ ") + ->html() + ->searchable(['email', 'username']) + ->sortable(), + + TextColumn::make('full_name') + ->label('Nama Lengkap') + ->searchable(query: function (Builder $query, string $search) { + $query->where('full_name', 'like', "%{$search}%") + ->orWhere('student_number', 'like', "%{$search}%"); + }) + ->sortable() + ->description(fn (Student $student) => $student->student_number), + + TextColumn::make('phone_number') + ->label('No. Telepon') + ->searchable() + ->sortable(), + + TextColumn::make('sex') + ->label('Jenis Kelamin') + ->badge() + ->sortable() + ->searchable(query: function ($query, string $search) { + $search = strtolower($search); + + if (str_contains('laki-laki', $search)) { + $query->orWhere('sex', 'male'); + } + + if (str_contains('perempuan', $search)) { + $query->orWhere('sex', 'female'); + } + }), + + TextColumn::make('date_of_birth') + ->label('TTL') + ->date('d F Y') + ->sortable() + ->prefix(fn (Student $student) => $student->place_of_birth ? $student->place_of_birth.', ' : ''), + + TextColumn::make('address') + ->label('Alamat') + ->limit(50), + + ...TimestampColumns::make(), + ]) + ->filters([ + SelectFilter::make('sex') + ->label('Jenis Kelamin') + ->options(Sex::class) + ->native(false), + + TrashedFilter::make() + ->native(false), + ]) + ->recordActions([ + EditAction::make() + ->modalWidth(Width::Large), + + DeleteAction::make(), + + ForceDeleteAction::make(), + + RestoreAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + ...DefaultBulkActions::make('Mahasiswa'), + ]), + ]) + ->emptyStateIcon(Heroicon::OutlinedUserPlus) + ->emptyStateDescription('Setelah Anda membuat data pertama, maka akan muncul disini.') + ->defaultSort('created_at', 'desc') + ->deferFilters(false) + ->paginated([25, 50, 100, 'all']) + ->defaultPaginationPageOption(25); + } +}