diff --git a/app/Filament/Resources/Master/Lecturers/LecturerResource.php b/app/Filament/Resources/Master/Lecturers/LecturerResource.php new file mode 100644 index 0000000..af85609 --- /dev/null +++ b/app/Filament/Resources/Master/Lecturers/LecturerResource.php @@ -0,0 +1,52 @@ + ListLecturers::route('/'), + 'create' => CreateLecturer::route('/create'), + 'edit' => EditLecturer::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Resources/Master/Lecturers/Pages/CreateLecturer.php b/app/Filament/Resources/Master/Lecturers/Pages/CreateLecturer.php new file mode 100644 index 0000000..c2e1903 --- /dev/null +++ b/app/Filament/Resources/Master/Lecturers/Pages/CreateLecturer.php @@ -0,0 +1,59 @@ +url(ListLecturers::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']); + + return $this->getResource()::getModel()::create( + array_merge($data, [ + 'user_id' => $user->id, + ]) + ); + }); + } +} diff --git a/app/Filament/Resources/Master/Lecturers/Pages/EditLecturer.php b/app/Filament/Resources/Master/Lecturers/Pages/EditLecturer.php new file mode 100644 index 0000000..68093dc --- /dev/null +++ b/app/Filament/Resources/Master/Lecturers/Pages/EditLecturer.php @@ -0,0 +1,36 @@ +url(ListLecturers::getUrl()), + ]; + } + + protected function getRedirectUrl(): string + { + return $this->getResource()::getUrl('index'); + } + + protected function getSavedNotification(): ?Notification + { + return SystemNotification::update(); + } +} diff --git a/app/Filament/Resources/Master/Lecturers/Pages/ListLecturers.php b/app/Filament/Resources/Master/Lecturers/Pages/ListLecturers.php new file mode 100644 index 0000000..a6a1ad5 --- /dev/null +++ b/app/Filament/Resources/Master/Lecturers/Pages/ListLecturers.php @@ -0,0 +1,22 @@ +label('Tambah'), + ]; + } +} diff --git a/app/Filament/Resources/Master/Lecturers/Schemas/LecturerForm.php b/app/Filament/Resources/Master/Lecturers/Schemas/LecturerForm.php new file mode 100644 index 0000000..6966240 --- /dev/null +++ b/app/Filament/Resources/Master/Lecturers/Schemas/LecturerForm.php @@ -0,0 +1,121 @@ +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 Dosen') + ->schema([ + TextInput::make('full_name') + ->label('Nama Lengkap') + ->placeholder('John Doe') + ->autocomplete(false) + ->required() + ->maxLength(100) + ->minLength(3) + ->autofocus() + ->helperText('Masukkan nama lengkap dosen beserta gelar akademik.') + ->columnSpanFull(), + + Grid::make(3) + ->schema([ + TextInput::make('lecturer_identification_number') + ->label('NIDN') + ->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(), + + Grid::make(2) + ->schema([ + DatePicker::make('date_of_birth') + ->label('Tanggal Lahir') + ->placeholder('Pilih Tanggal') + ->required() + ->native(false) + ->displayFormat('l, d F Y') + ->maxDate(Carbon::now()->subYears(25)), + + TextInput::make('place_of_birth') + ->label('Tempat Lahir') + ->placeholder('Purwakarta') + ->autocomplete(false) + ->required() + ->maxLength(50) + ->minLength(3), + ]) + ->columnSpanFull(), + ]) + ->columns(2), + ]) + ->columns(1); + } +} diff --git a/app/Filament/Resources/Master/Lecturers/Tables/LecturersTable.php b/app/Filament/Resources/Master/Lecturers/Tables/LecturersTable.php new file mode 100644 index 0000000..6ac45b2 --- /dev/null +++ b/app/Filament/Resources/Master/Lecturers/Tables/LecturersTable.php @@ -0,0 +1,113 @@ +columns([ + TextColumn::make('user.email') + ->label('Akun') + ->formatStateUsing(fn ($state, Lecturer $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('lecturer_identification_number', 'like', "%{$search}%"); + }) + ->sortable() + ->description(fn (Lecturer $record) => $record->lecturer_identification_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 (Lecturer $record) => $record->place_of_birth ? $record->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('Dosen'), + ]), + ]) + ->emptyStateIcon(Heroicon::OutlinedUserGroup) + ->emptyStateDescription('Setelah Anda membuat data pertama, maka akan muncul disini.') + ->defaultSort('created_at', 'desc') + ->deferFilters(false) + ->paginated([25, 50, 100, 'all']) + ->defaultPaginationPageOption(25); + } +} diff --git a/app/Models/Lecturer.php b/app/Models/Lecturer.php new file mode 100644 index 0000000..46543b2 --- /dev/null +++ b/app/Models/Lecturer.php @@ -0,0 +1,43 @@ + 'date', + 'sex' => Sex::class, + ]; + } + + #[Scope] + protected function female(Builder $query): void + { + $query->where('sex', Sex::Female); + } + + #[Scope] + protected function male(Builder $query): void + { + $query->where('sex', Sex::Male); + } + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } +} diff --git a/database/migrations/2026_02_25_024502_create_lecturers_table.php b/database/migrations/2026_02_25_024502_create_lecturers_table.php new file mode 100644 index 0000000..bf55e04 --- /dev/null +++ b/database/migrations/2026_02_25_024502_create_lecturers_table.php @@ -0,0 +1,38 @@ +id(); + $table->foreignId('user_id')->unique()->constrained()->cascadeOnDelete(); + $table->string('lecturer_identification_number', 10)->unique(); + $table->string('full_name', 100); + $table->string('phone_number', 20); + $table->enum('sex', Sex::cases()); + $table->text('address')->nullable(); + $table->date('date_of_birth'); + $table->string('place_of_birth', 50); + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->nullable()->useCurrentOnUpdate(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('lecturers'); + } +};