From 62bc02fbacbb55cfb5a450b7f0f327d508f76867 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Mon, 6 Apr 2026 11:02:31 +0700 Subject: [PATCH] feat: Refactor Journalist and Partner resources by introducing dedicated form and table schemas for improved organization and maintainability --- .../Manage/Journalists/JournalistResource.php | 194 +------- .../Journalists/Schemas/JournalistForm.php | 103 +++++ .../Journalists/Tables/JournalistTable.php | 115 +++++ .../Partners/Infolists/PartnerInfolist.php | 306 +++++++++++++ .../Manage/Partners/PartnerResource.php | 416 +----------------- .../Manage/Partners/Tables/PartnerTable.php | 135 ++++++ .../Master/Categories/CategoryResource.php | 86 +--- .../Categories/Schemas/CategoryForm.php | 24 + .../Categories/Tables/CategoryTable.php | 84 ++++ .../ClassificationResource.php | 86 +--- .../Schemas/ClassificationForm.php | 24 + .../Tables/ClassificationTable.php | 84 ++++ .../Master/Departments/DepartmentResource.php | 97 +--- .../Departments/Schemas/DepartmentForm.php | 31 ++ .../Departments/Tables/DepartmentTable.php | 88 ++++ .../Master/Locations/LocationResource.php | 86 +--- .../Master/Locations/Schemas/LocationForm.php | 24 + .../Master/Locations/Tables/LocationTable.php | 84 ++++ .../Schemas/SubClassificationForm.php | 34 ++ .../SubClassificationResource.php | 113 +---- .../Tables/SubClassificationTable.php | 101 +++++ .../SubLocations/Schemas/SubLocationForm.php | 34 ++ .../SubLocations/SubLocationResource.php | 110 +---- .../SubLocations/Tables/SubLocationTable.php | 98 +++++ .../Master/Themes/Schemas/ThemeForm.php | 24 + .../Master/Themes/Tables/ThemeTable.php | 78 ++++ .../Resources/Master/Themes/ThemeResource.php | 80 +--- .../Master/Users/Schemas/UserForm.php | 67 +++ .../Master/Users/Tables/UserTable.php | 125 ++++++ .../Resources/Master/Users/UserResource.php | 167 +------ .../Announcements/AnnouncementResource.php | 137 +----- .../Schemas/AnnouncementForm.php | 60 +++ .../Tables/AnnouncementTable.php | 101 +++++ 33 files changed, 1868 insertions(+), 1528 deletions(-) create mode 100644 app/Filament/Resources/Manage/Journalists/Schemas/JournalistForm.php create mode 100644 app/Filament/Resources/Manage/Journalists/Tables/JournalistTable.php create mode 100644 app/Filament/Resources/Manage/Partners/Infolists/PartnerInfolist.php create mode 100644 app/Filament/Resources/Manage/Partners/Tables/PartnerTable.php create mode 100644 app/Filament/Resources/Master/Categories/Schemas/CategoryForm.php create mode 100644 app/Filament/Resources/Master/Categories/Tables/CategoryTable.php create mode 100644 app/Filament/Resources/Master/Classifications/Schemas/ClassificationForm.php create mode 100644 app/Filament/Resources/Master/Classifications/Tables/ClassificationTable.php create mode 100644 app/Filament/Resources/Master/Departments/Schemas/DepartmentForm.php create mode 100644 app/Filament/Resources/Master/Departments/Tables/DepartmentTable.php create mode 100644 app/Filament/Resources/Master/Locations/Schemas/LocationForm.php create mode 100644 app/Filament/Resources/Master/Locations/Tables/LocationTable.php create mode 100644 app/Filament/Resources/Master/SubClassifications/Schemas/SubClassificationForm.php create mode 100644 app/Filament/Resources/Master/SubClassifications/Tables/SubClassificationTable.php create mode 100644 app/Filament/Resources/Master/SubLocations/Schemas/SubLocationForm.php create mode 100644 app/Filament/Resources/Master/SubLocations/Tables/SubLocationTable.php create mode 100644 app/Filament/Resources/Master/Themes/Schemas/ThemeForm.php create mode 100644 app/Filament/Resources/Master/Themes/Tables/ThemeTable.php create mode 100644 app/Filament/Resources/Master/Users/Schemas/UserForm.php create mode 100644 app/Filament/Resources/Master/Users/Tables/UserTable.php create mode 100644 app/Filament/Resources/Publication/Announcements/Schemas/AnnouncementForm.php create mode 100644 app/Filament/Resources/Publication/Announcements/Tables/AnnouncementTable.php diff --git a/app/Filament/Resources/Manage/Journalists/JournalistResource.php b/app/Filament/Resources/Manage/Journalists/JournalistResource.php index 3934a6f..e8cb190 100644 --- a/app/Filament/Resources/Manage/Journalists/JournalistResource.php +++ b/app/Filament/Resources/Manage/Journalists/JournalistResource.php @@ -3,32 +3,14 @@ namespace App\Filament\Resources\Manage\Journalists; use App\Enums\RoleEnum; -use App\Enums\VerificationStatus; -use App\Filament\Actions\Cheerful\ForceDeleteAction; -use App\Filament\Actions\Cheerful\RestoreAction; -use App\Filament\Columns\RowIndexColumn; -use App\Filament\Columns\TimestampColumns; -use App\Filament\Pages\Media; -use App\Filament\Resources\Manage\Journalists\Actions\DeleteJournalistAction; -use App\Filament\Resources\Manage\Journalists\Actions\EditJournalistAction; use App\Filament\Resources\Manage\Journalists\Pages\ManageJournalists; -use App\Filament\Support\CheerfulNotification; +use App\Filament\Resources\Manage\Journalists\Schemas\JournalistForm; +use App\Filament\Resources\Manage\Journalists\Tables\JournalistTable; use App\Models\Journalist; -use Asmit\FilamentUpload\Forms\Components\AdvancedFileUpload; use BackedEnum; -use Filament\Actions\Action; -use Filament\Actions\BulkActionGroup; -use Filament\Actions\DeleteBulkAction; -use Filament\Actions\ForceDeleteBulkAction; -use Filament\Actions\RestoreBulkAction; -use Filament\Forms\Components\Group; -use Filament\Forms\Components\Textarea; -use Filament\Forms\Components\TextInput; use Filament\Resources\Resource; use Filament\Schemas\Schema; use Filament\Support\Icons\Heroicon; -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Filters\TrashedFilter; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; @@ -52,180 +34,12 @@ class JournalistResource extends Resource public static function form(Schema $schema): Schema { - $documents = [ - [ - 'title' => 'Dokumen Pers', - 'text_name' => 'press_card', - 'placeholder' => '*****', - 'max' => 100, - 'max_size' => 1024 * 10, - 'file_name' => 'press_card_docs', - 'accept' => ['application/pdf'], - 'folder' => 'press-card/', - ], - [ - 'title' => 'Sertifikat UKW', - 'text_name' => 'ukw_certificate', - 'placeholder' => '*****', - 'max' => 100, - 'max_size' => 1024 * 10, - 'file_name' => 'ukw_certificate_docs', - 'accept' => ['application/pdf'], - 'folder' => 'ukw-certificate/', - ], - ]; - - return $schema - ->components([ - TextInput::make('name') - ->label('Nama Lengkap') - ->placeholder('John Doe') - ->autocomplete(false) - ->autofocus() - ->required() - ->maxLength(100), - - TextInput::make('email') - ->label('Alamat Surel') - ->placeholder('johndoe@example.com') - ->autocomplete(false) - ->required() - ->maxLength(254) - ->email() - ->prefixIcon('heroicon-o-envelope'), - - TextInput::make('phone_number') - ->label('Nomor Telepon') - ->placeholder('08xx xxxx xxxx') - ->autocomplete(false) - ->required() - ->maxLength(20) - ->tel() - ->prefixIcon('heroicon-o-phone'), - - Group::make() - ->schema( - collect($documents) - ->map(function (array $doc): array { - return [ - TextInput::make($doc['text_name']) - ->label('Nomor Identitas ('.$doc['title'].')') - ->placeholder($doc['placeholder']) - ->autocomplete(false) - ->required() - ->maxLength($doc['max']), - - AdvancedFileUpload::make($doc['file_name']) - ->label('File Pendukung ('.$doc['title'].') (PDF)') - ->disk(config('filesystems.default')) - ->acceptedFileTypes($doc['accept']) - ->directory(fn (): string => 'journalists/'.$doc['folder'].now()->toDateString()) - ->maxSize($doc['max_size']) - ->helperText('Format yang diterima: '.implode(', ', array_map(fn ($t) => strtoupper(str_replace('application/', '', $t)), $doc['accept'])).'. Ukuran maksimum: '.($doc['max_size'] / 1024).' MB.') - ->required(), - ]; - }) - ->flatten() - ->toArray() - ), - - Textarea::make('change_reason') - ->label('Alasan Perubahan') - ->placeholder('Jelaskan alasan perubahan data ini...') - ->rows(3) - ->required() - ->visible(fn () => auth()->user()->company?->verificationRequest?->status === VerificationStatus::APPROVED && auth()->user()->hasRole(RoleEnum::PERUSAHAAN->value)), - ]) - ->columns(1); + return JournalistForm::configure($schema); } public static function table(Table $table): Table { - return $table - ->columns([ - ...RowIndexColumn::make(), - - TextColumn::make('name') - ->label('Nama') - ->searchable() - ->sortable(), - - TextColumn::make('email') - ->label('Alamat Surel') - ->searchable() - ->sortable(), - - TextColumn::make('phone_number') - ->label('Nomor Telepon') - ->searchable() - ->sortable(), - - TextColumn::make('press_card') - ->label('Dokumen Pers') - ->searchable() - ->sortable() - ->wrap(), - - TextColumn::make('ukw_certificate') - ->label('Sertifikat UKW') - ->searchable() - ->sortable() - ->wrap(), - - ...TimestampColumns::make(), - ]) - ->filters([ - TrashedFilter::make() - ->native(false) - ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), - ]) - ->checkIfRecordIsSelectableUsing( - fn () => ! ( - in_array(auth()->user()->company?->verificationRequest?->status, [VerificationStatus::PENDING, VerificationStatus::REJECTED]) || - auth()->user()->company?->verificationRequest()->pending()->exists() - ) - ) - ->recordActions([ - EditJournalistAction::make('edit') - ->visible(fn () => ! (in_array(auth()->user()->company?->verificationRequest?->status, [VerificationStatus::PENDING, VerificationStatus::REJECTED]) || auth()->user()->company?->verificationRequest()->pending()->exists())), - - DeleteJournalistAction::make('delete') - ->visible(fn () => ! (in_array(auth()->user()->company?->verificationRequest?->status, [VerificationStatus::PENDING, VerificationStatus::REJECTED]) || auth()->user()->company?->verificationRequest()->pending()->exists())), - - ForceDeleteAction::make() - ->visible(fn () => ! (in_array(auth()->user()->company?->verificationRequest?->status, [VerificationStatus::PENDING, VerificationStatus::REJECTED]) || auth()->user()->company?->verificationRequest()->pending()->exists())), - - RestoreAction::make() - ->visible(fn () => ! (in_array(auth()->user()->company?->verificationRequest?->status, [VerificationStatus::PENDING, VerificationStatus::REJECTED]) || auth()->user()->company?->verificationRequest()->pending()->exists())), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - DeleteBulkAction::make(), - - ForceDeleteBulkAction::make(), - - RestoreBulkAction::make(), - ])->visible(fn () => ! (in_array(auth()->user()->company?->verificationRequest?->status, [VerificationStatus::PENDING, VerificationStatus::REJECTED]) || auth()->user()->company?->verificationRequest()->pending()->exists())), - ]) - ->emptyStateIcon(Heroicon::OutlinedIdentification) - ->emptyStateHeading(fn () => CheerfulNotification::getByKey('journalist.empty_state_heading')) - ->emptyStateDescription(fn () => CheerfulNotification::getByKey('journalist.empty_state')) - ->defaultSort('created_at', 'desc') - ->deferFilters(false) - ->paginated([25, 50, 100, 'all']) - ->deferLoading() - ->when(fn () => auth()->user()->hasRole(RoleEnum::PERUSAHAAN->value) && ! auth()->user()->company?->partnerMedia, function (Table $table): Table { - return $table - ->emptyStateHeading(fn () => CheerfulNotification::getByKey('access.restricted')) - ->emptyStateDescription(fn () => CheerfulNotification::getByKey('access.complete_media_data')) - ->emptyStateIcon(Heroicon::OutlinedLockClosed) - ->emptyStateActions([ - Action::make('complete_media') - ->label('Lengkapi Data Media') - ->url(Media::getUrl()) - ->button(), - ]); - }); + return JournalistTable::configure($table); } public static function getPages(): array diff --git a/app/Filament/Resources/Manage/Journalists/Schemas/JournalistForm.php b/app/Filament/Resources/Manage/Journalists/Schemas/JournalistForm.php new file mode 100644 index 0000000..2c4cdef --- /dev/null +++ b/app/Filament/Resources/Manage/Journalists/Schemas/JournalistForm.php @@ -0,0 +1,103 @@ + 'Dokumen Pers', + 'text_name' => 'press_card', + 'placeholder' => '*****', + 'max' => 100, + 'max_size' => 1024 * 10, + 'file_name' => 'press_card_docs', + 'accept' => ['application/pdf'], + 'folder' => 'press-card/', + ], + [ + 'title' => 'Sertifikat UKW', + 'text_name' => 'ukw_certificate', + 'placeholder' => '*****', + 'max' => 100, + 'max_size' => 1024 * 10, + 'file_name' => 'ukw_certificate_docs', + 'accept' => ['application/pdf'], + 'folder' => 'ukw-certificate/', + ], + ]; + + return $schema + ->components([ + TextInput::make('name') + ->label('Nama Lengkap') + ->placeholder('John Doe') + ->autocomplete(false) + ->autofocus() + ->required() + ->maxLength(100), + + TextInput::make('email') + ->label('Alamat Surel') + ->placeholder('johndoe@example.com') + ->autocomplete(false) + ->required() + ->maxLength(254) + ->email() + ->prefixIcon('heroicon-o-envelope'), + + TextInput::make('phone_number') + ->label('Nomor Telepon') + ->placeholder('08xx xxxx xxxx') + ->autocomplete(false) + ->required() + ->maxLength(20) + ->tel() + ->prefixIcon('heroicon-o-phone'), + + Group::make() + ->schema( + collect($documents) + ->map(function (array $doc): array { + return [ + TextInput::make($doc['text_name']) + ->label('Nomor Identitas ('.$doc['title'].')') + ->placeholder($doc['placeholder']) + ->autocomplete(false) + ->required() + ->maxLength($doc['max']), + + AdvancedFileUpload::make($doc['file_name']) + ->label('File Pendukung ('.$doc['title'].') (PDF)') + ->disk(config('filesystems.default')) + ->acceptedFileTypes($doc['accept']) + ->directory(fn (): string => 'journalists/'.$doc['folder'].now()->toDateString()) + ->maxSize($doc['max_size']) + ->helperText('Format yang diterima: '.implode(', ', array_map(fn ($t) => strtoupper(str_replace('application/', '', $t)), $doc['accept'])).'. Ukuran maksimum: '.($doc['max_size'] / 1024).' MB.') + ->required(), + ]; + }) + ->flatten() + ->toArray() + ), + + Textarea::make('change_reason') + ->label('Alasan Perubahan') + ->placeholder('Jelaskan alasan perubahan data ini...') + ->rows(3) + ->required() + ->visible(fn () => auth()->user()->company?->verificationRequest?->status === VerificationStatus::APPROVED && auth()->user()->hasRole(RoleEnum::PERUSAHAAN->value)), + ]) + ->columns(1); + } +} diff --git a/app/Filament/Resources/Manage/Journalists/Tables/JournalistTable.php b/app/Filament/Resources/Manage/Journalists/Tables/JournalistTable.php new file mode 100644 index 0000000..401c7ed --- /dev/null +++ b/app/Filament/Resources/Manage/Journalists/Tables/JournalistTable.php @@ -0,0 +1,115 @@ +columns([ + ...RowIndexColumn::make(), + + TextColumn::make('name') + ->label('Nama') + ->searchable() + ->sortable(), + + TextColumn::make('email') + ->label('Alamat Surel') + ->searchable() + ->sortable(), + + TextColumn::make('phone_number') + ->label('Nomor Telepon') + ->searchable() + ->sortable(), + + TextColumn::make('press_card') + ->label('Dokumen Pers') + ->searchable() + ->sortable() + ->wrap(), + + TextColumn::make('ukw_certificate') + ->label('Sertifikat UKW') + ->searchable() + ->sortable() + ->wrap(), + + ...TimestampColumns::make(), + ]) + ->filters([ + TrashedFilter::make() + ->native(false) + ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), + ]) + ->checkIfRecordIsSelectableUsing( + fn () => ! ( + in_array(auth()->user()->company?->verificationRequest?->status, [VerificationStatus::PENDING, VerificationStatus::REJECTED]) || + auth()->user()->company?->verificationRequest()->pending()->exists() + ) + ) + ->recordActions([ + EditJournalistAction::make('edit') + ->visible(fn () => ! (in_array(auth()->user()->company?->verificationRequest?->status, [VerificationStatus::PENDING, VerificationStatus::REJECTED]) || auth()->user()->company?->verificationRequest()->pending()->exists())), + + DeleteJournalistAction::make('delete') + ->visible(fn () => ! (in_array(auth()->user()->company?->verificationRequest?->status, [VerificationStatus::PENDING, VerificationStatus::REJECTED]) || auth()->user()->company?->verificationRequest()->pending()->exists())), + + ForceDeleteAction::make() + ->visible(fn () => ! (in_array(auth()->user()->company?->verificationRequest?->status, [VerificationStatus::PENDING, VerificationStatus::REJECTED]) || auth()->user()->company?->verificationRequest()->pending()->exists())), + + RestoreAction::make() + ->visible(fn () => ! (in_array(auth()->user()->company?->verificationRequest?->status, [VerificationStatus::PENDING, VerificationStatus::REJECTED]) || auth()->user()->company?->verificationRequest()->pending()->exists())), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + + ForceDeleteBulkAction::make(), + + RestoreBulkAction::make(), + ])->visible(fn () => ! (in_array(auth()->user()->company?->verificationRequest?->status, [VerificationStatus::PENDING, VerificationStatus::REJECTED]) || auth()->user()->company?->verificationRequest()->pending()->exists())), + ]) + ->emptyStateIcon(Heroicon::OutlinedIdentification) + ->emptyStateHeading(fn () => CheerfulNotification::getByKey('journalist.empty_state_heading')) + ->emptyStateDescription(fn () => CheerfulNotification::getByKey('journalist.empty_state')) + ->defaultSort('created_at', 'desc') + ->deferFilters(false) + ->paginated([25, 50, 100, 'all']) + ->deferLoading() + ->when(fn () => auth()->user()->hasRole(RoleEnum::PERUSAHAAN->value) && ! auth()->user()->company?->partnerMedia, function (Table $table): Table { + return $table + ->emptyStateHeading(fn () => CheerfulNotification::getByKey('access.restricted')) + ->emptyStateDescription(fn () => CheerfulNotification::getByKey('access.complete_media_data')) + ->emptyStateIcon(Heroicon::OutlinedLockClosed) + ->emptyStateActions([ + Action::make('complete_media') + ->label('Lengkapi Data Media') + ->url(Media::getUrl()) + ->button(), + ]); + }); + } +} diff --git a/app/Filament/Resources/Manage/Partners/Infolists/PartnerInfolist.php b/app/Filament/Resources/Manage/Partners/Infolists/PartnerInfolist.php new file mode 100644 index 0000000..5042e08 --- /dev/null +++ b/app/Filament/Resources/Manage/Partners/Infolists/PartnerInfolist.php @@ -0,0 +1,306 @@ +schema([ + Section::make(fn () => CheerfulNotification::getByKey('company.account_title')) + ->description(fn () => CheerfulNotification::getByKey('company.account_desc')) + ->schema([ + Grid::make(3) + ->schema([ + TextEntry::make('name') + ->label('Nama'), + + TextEntry::make('email') + ->label('Email'), + + TextEntry::make('username') + ->label('Nama Pengguna'), + + TextEntry::make('is_active') + ->label('Status') + ->badge(), + + TextEntry::make('email_verified_at') + ->label('Email Verifikasi') + ->placeholder('Akun Belum Diverifikasi') + ->dateTime('l, d F Y H:i'), + ]), + ]), + + Section::make(fn () => CheerfulNotification::getByKey('company.company_title')) + ->description(fn () => CheerfulNotification::getByKey('company.company_desc')) + ->schema([ + Grid::make(3) + ->schema([ + TextEntry::make('company.name') + ->label('Nama'), + + TextEntry::make('company.email') + ->label('Email'), + + TextEntry::make('company.phone_number') + ->label('Nomor Telepon'), + + TextEntry::make('company.address') + ->label('Alamat'), + + TextEntry::make('company.phone_number') + ->label('No. Telepon'), + + TextEntry::make('company.director_name') + ->label('Nama Direktur'), + + TextEntry::make('company.verificationRequest.created_at') + ->label('Tanggal Pengajuan Verifikasi') + ->dateTime('d F Y H:i'), + + TextEntry::make('company.verificationRequest.latestReview.decision') + ->label('Status Verifikasi') + ->badge(), + + TextEntry::make('company.verificationRequest.latestReview.created_at') + ->label('Tanggal Verifikasi') + ->dateTime('d F Y H:i') + ->visible(fn (User $record): ?string => $record->company?->verificationRequest?->latestReview), + + TextEntry::make('company.verificationRequest.latestReview.note') + ->label('Alasan Penolakan') + ->visible(fn (User $record): ?string => $record->company?->verificationRequest?->latestReview?->decision === DecisionAdmin::REJECTED), + ]), + + Grid::make(3) + ->schema([ + Section::make(fn () => CheerfulNotification::getByKey('company.director_nik')) + ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-user' : null) + ->schema([ + TextEntry::make('company.director_nik') + ->hiddenLabel(), + + TextEntry::make('company.director_nik_docs') + ->hiddenLabel() + ->html() + ->getStateUsing(function (User $record): string { + $media = $record->company + ?->getMedia('companies') + ->where('custom_properties.doc_type', 'director-nik') + ->sortByDesc('created_at') + ->first(); + + if (! $media) { + return ''; + } + + $url = $media->getUrl(); + $mime = $media->mime_type; + + if (str_contains($mime, 'image')) { + return 'Dokumen NIK Direktur'; + } + + if (str_contains($mime, 'pdf')) { + return ''; + } + + return 'Unduh Dokumen'; + }), + ]), + + Section::make(fn () => CheerfulNotification::getByKey('company.establishment_deed')) + ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) + ->schema([ + TextEntry::make('company.deed_incorporation') + ->hiddenLabel(), + + self::fileEntry('company.deed_incorporation'), + ]), + + Section::make(fn () => CheerfulNotification::getByKey('company.siup_nib')) + ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) + ->schema([ + TextEntry::make('company.trade_license') + ->hiddenLabel(), + + self::fileEntry('company.trade_license'), + ]), + + Section::make(fn () => CheerfulNotification::getByKey('company.npwp')) + ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) + ->schema([ + TextEntry::make('company.tax_id_number') + ->hiddenLabel(), + + self::fileEntry('company.tax_id_number'), + ]), + + Section::make(fn () => CheerfulNotification::getByKey('company.pkp')) + ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) + ->schema([ + TextEntry::make('company.taxable_enterprise') + ->hiddenLabel(), + + self::fileEntry('company.taxable_enterprise'), + ]), + + Section::make(fn () => CheerfulNotification::getByKey('company.annual_tax')) + ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) + ->schema([ + TextEntry::make('company.annual_tax_return') + ->hiddenLabel(), + + self::fileEntry('company.annual_tax_return'), + ]), + + Section::make(fn () => CheerfulNotification::getByKey('company.domicile')) + ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) + ->schema([ + TextEntry::make('company.domicile_certificate') + ->hiddenLabel(), + + self::fileEntry('company.domicile_certificate'), + ]), + + Section::make(fn () => CheerfulNotification::getByKey('company.company_profile')) + ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) + ->schema([ + TextEntry::make('company.profile') + ->hiddenLabel(), + + self::fileEntry('company.profile'), + ]), + ]), + ]), + + Section::make(fn () => CheerfulNotification::getByKey('company.media_title')) + ->description(fn () => CheerfulNotification::getByKey('company.media_desc')) + ->schema([ + Grid::make(4) + ->schema([ + TextEntry::make('company.partnerMedia.name') + ->label('Nama'), + + TextEntry::make('company.partnerMedia.link') + ->label('Tautan') + ->url(fn (User $record): ?string => $record->company?->partnerMedia?->link, true), + + TextEntry::make('company.partnerMedia.type') + ->label('Jenis') + ->badge(), + + TextEntry::make('company.partnerMedia.classification') + ->label('Klasifikasi') + ->badge(), + + TextEntry::make('company.partnerMedia.address') + ->label('Alamat') + ->columnSpanFull(), + ]), + + Grid::make(2) + ->schema([ + Section::make(fn () => CheerfulNotification::getByKey('company.journalism_org_cert')) + ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) + ->schema([ + TextEntry::make('company.partnerMedia.journalism_organization') + ->hiddenLabel(), + + self::fileEntry('company.partnerMedia.journalism_organization', 'company.partnerMedia', 'partnerMedia'), + ]), + + Section::make(fn () => CheerfulNotification::getByKey('company.press_council_cert')) + ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) + ->schema([ + TextEntry::make('company.partnerMedia.press_council_certificate') + ->hiddenLabel(), + + self::fileEntry('company.partnerMedia.press_council_certificate', 'company.partnerMedia', 'partnerMedia'), + ]), + ]), + ]), + + Section::make(fn () => CheerfulNotification::getByKey('company.journalist_title')) + ->description(fn () => CheerfulNotification::getByKey('company.journalist_desc')) + ->schema([ + RepeatableEntry::make('company.partnerMedia.journalists') + ->hiddenLabel() + ->schema([ + Grid::make(3) + ->schema([ + TextEntry::make('name') + ->label('Nama'), + + TextEntry::make('email') + ->label('Email'), + + TextEntry::make('phone_number') + ->label('No. Telepon'), + ]), + Grid::make(2) + ->schema([ + Section::make(fn () => CheerfulNotification::getByKey('company.press_card')) + ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-identification' : null) + ->schema([ + TextEntry::make('press_card') + ->hiddenLabel(), + + self::fileEntry('press_card', null, 'journalists'), + ]), + + Section::make(fn () => CheerfulNotification::getByKey('company.ukw_cert')) + ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) + ->schema([ + TextEntry::make('ukw_certificate') + ->hiddenLabel(), + + self::fileEntry('ukw_certificate', null, 'journalists'), + ]), + ]), + ]), + ]), + ]) + ->columns(1); + } + + private static function fileEntry( + string $name, + ?string $mediaOwner = 'company', + string $collection = 'companies' + ): PdfViewerEntry { + return PdfViewerEntry::make($name) + ->hiddenLabel() + ->getStateUsing(function (Model $record) use ($name, $mediaOwner, $collection): ?string { + $owner = $mediaOwner ? data_get($record, $mediaOwner) : $record; + + if (! $owner) { + return null; + } + + $docType = str($name) + ->afterLast('.') + ->slug('-'); + + return $owner->getMedia($collection) + ->where('custom_properties.doc_type', (string) $docType) + ->sortByDesc('created_at') + ->first() + ?->getUrl(); + }); + } +} diff --git a/app/Filament/Resources/Manage/Partners/PartnerResource.php b/app/Filament/Resources/Manage/Partners/PartnerResource.php index 11cf693..32cfc93 100644 --- a/app/Filament/Resources/Manage/Partners/PartnerResource.php +++ b/app/Filament/Resources/Manage/Partners/PartnerResource.php @@ -2,36 +2,17 @@ namespace App\Filament\Resources\Manage\Partners; -use App\Enums\DecisionAdmin; -use App\Enums\MediaClassification; -use App\Enums\MediaType; -use App\Enums\NotifStyle; -use App\Enums\RoleEnum; -use App\Enums\VerificationStatus; -use App\Filament\Actions\DefaultBulkActions; -use App\Filament\Columns\RowIndexColumn; +use App\Filament\Resources\Manage\Partners\Infolists\PartnerInfolist; use App\Filament\Resources\Manage\Partners\Pages\ManagePartners; use App\Filament\Resources\Manage\Partners\Pages\ViewPartner; -use App\Filament\Support\CheerfulNotification; +use App\Filament\Resources\Manage\Partners\Tables\PartnerTable; use App\Models\User; use BackedEnum; -use Filament\Actions\BulkActionGroup; -use Filament\Actions\ViewAction; -use Filament\Infolists\Components\RepeatableEntry; -use Filament\Infolists\Components\TextEntry; use Filament\Resources\Resource; -use Filament\Schemas\Components\Grid; -use Filament\Schemas\Components\Section; use Filament\Schemas\Schema; use Filament\Support\Icons\Heroicon; -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Filters\SelectFilter; -use Filament\Tables\Filters\TrashedFilter; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; -use Illuminate\Database\Eloquent\Model; -use Illuminate\Support\HtmlString; -use Joaopaulolndev\FilamentPdfViewer\Infolists\Components\PdfViewerEntry; use UnitEnum; class PartnerResource extends Resource @@ -52,375 +33,12 @@ class PartnerResource extends Resource public static function infolist(Schema $schema): Schema { - return $schema - ->schema([ - Section::make(fn () => CheerfulNotification::getByKey('company.account_title')) - ->description(fn () => CheerfulNotification::getByKey('company.account_desc')) - ->schema([ - Grid::make(3) - ->schema([ - TextEntry::make('name') - ->label('Nama'), - - TextEntry::make('email') - ->label('Email'), - - TextEntry::make('username') - ->label('Nama Pengguna'), - - TextEntry::make('is_active') - ->label('Status') - ->badge(), - - TextEntry::make('email_verified_at') - ->label('Email Verifikasi') - ->placeholder('Akun Belum Diverifikasi') - ->dateTime('l, d F Y H:i'), - ]), - ]), - - Section::make(fn () => CheerfulNotification::getByKey('company.company_title')) - ->description(fn () => CheerfulNotification::getByKey('company.company_desc')) - ->schema([ - Grid::make(3) - ->schema([ - TextEntry::make('company.name') - ->label('Nama'), - - TextEntry::make('company.email') - ->label('Email'), - - TextEntry::make('company.phone_number') - ->label('Nomor Telepon'), - - TextEntry::make('company.address') - ->label('Alamat'), - - TextEntry::make('company.phone_number') - ->label('No. Telepon'), - - TextEntry::make('company.director_name') - ->label('Nama Direktur'), - - TextEntry::make('company.verificationRequest.created_at') - ->label('Tanggal Pengajuan Verifikasi') - ->dateTime('d F Y H:i'), - - TextEntry::make('company.verificationRequest.latestReview.decision') - ->label('Status Verifikasi') - ->badge(), - - TextEntry::make('company.verificationRequest.latestReview.created_at') - ->label('Tanggal Verifikasi') - ->dateTime('d F Y H:i') - ->visible(fn (User $record): ?string => $record->company?->verificationRequest?->latestReview), - - TextEntry::make('company.verificationRequest.latestReview.note') - ->label('Alasan Penolakan') - ->visible(fn (User $record): ?string => $record->company?->verificationRequest?->latestReview?->decision === DecisionAdmin::REJECTED), - ]), - - Grid::make(3) - ->schema([ - Section::make(fn () => CheerfulNotification::getByKey('company.director_nik')) - ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-user' : null) - ->schema([ - TextEntry::make('company.director_nik') - ->hiddenLabel(), - - TextEntry::make('company.director_nik_docs') - ->hiddenLabel() - ->html() - ->getStateUsing(function (User $record): string { - $media = $record->company - ?->getMedia('companies') - ->where('custom_properties.doc_type', 'director-nik') - ->sortByDesc('created_at') - ->first(); - - if (! $media) { - return ''; - } - - $url = $media->getUrl(); - $mime = $media->mime_type; - - if (str_contains($mime, 'image')) { - return 'Dokumen NIK Direktur'; - } - - if (str_contains($mime, 'pdf')) { - return ''; - } - - return 'Unduh Dokumen'; - }), - ]), - - Section::make(fn () => CheerfulNotification::getByKey('company.establishment_deed')) - ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) - ->schema([ - TextEntry::make('company.deed_incorporation') - ->hiddenLabel(), - - self::fileEntry('company.deed_incorporation'), - ]), - - Section::make(fn () => CheerfulNotification::getByKey('company.siup_nib')) - ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) - ->schema([ - TextEntry::make('company.trade_license') - ->hiddenLabel(), - - self::fileEntry('company.trade_license'), - ]), - - Section::make(fn () => CheerfulNotification::getByKey('company.npwp')) - ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) - ->schema([ - TextEntry::make('company.tax_id_number') - ->hiddenLabel(), - - self::fileEntry('company.tax_id_number'), - ]), - - Section::make(fn () => CheerfulNotification::getByKey('company.pkp')) - ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) - ->schema([ - TextEntry::make('company.taxable_enterprise') - ->hiddenLabel(), - - self::fileEntry('company.taxable_enterprise'), - ]), - - Section::make(fn () => CheerfulNotification::getByKey('company.annual_tax')) - ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) - ->schema([ - TextEntry::make('company.annual_tax_return') - ->hiddenLabel(), - - self::fileEntry('company.annual_tax_return'), - ]), - - Section::make(fn () => CheerfulNotification::getByKey('company.domicile')) - ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) - ->schema([ - TextEntry::make('company.domicile_certificate') - ->hiddenLabel(), - - self::fileEntry('company.domicile_certificate'), - ]), - - Section::make(fn () => CheerfulNotification::getByKey('company.company_profile')) - ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) - ->schema([ - TextEntry::make('company.profile') - ->hiddenLabel(), - - self::fileEntry('company.profile'), - ]), - ]), - ]), - - Section::make(fn () => CheerfulNotification::getByKey('company.media_title')) - ->description(fn () => CheerfulNotification::getByKey('company.media_desc')) - ->schema([ - Grid::make(4) - ->schema([ - TextEntry::make('company.partnerMedia.name') - ->label('Nama'), - - TextEntry::make('company.partnerMedia.link') - ->label('Tautan') - ->url(fn (User $record): ?string => $record->company?->partnerMedia?->link, true), - - TextEntry::make('company.partnerMedia.type') - ->label('Jenis') - ->badge(), - - TextEntry::make('company.partnerMedia.classification') - ->label('Klasifikasi') - ->badge(), - - TextEntry::make('company.partnerMedia.address') - ->label('Alamat') - ->columnSpanFull(), - ]), - - Grid::make(2) - ->schema([ - Section::make(fn () => CheerfulNotification::getByKey('company.journalism_org_cert')) - ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) - ->schema([ - TextEntry::make('company.partnerMedia.journalism_organization') - ->hiddenLabel(), - - self::fileEntry('company.partnerMedia.journalism_organization', 'company.partnerMedia', 'partnerMedia'), - ]), - - Section::make(fn () => CheerfulNotification::getByKey('company.press_council_cert')) - ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) - ->schema([ - TextEntry::make('company.partnerMedia.press_council_certificate') - ->hiddenLabel(), - - self::fileEntry('company.partnerMedia.press_council_certificate', 'company.partnerMedia', 'partnerMedia'), - ]), - ]), - ]), - - Section::make(fn () => CheerfulNotification::getByKey('company.journalist_title')) - ->description(fn () => CheerfulNotification::getByKey('company.journalist_desc')) - ->schema([ - RepeatableEntry::make('company.partnerMedia.journalists') - ->hiddenLabel() - ->schema([ - Grid::make(3) - ->schema([ - TextEntry::make('name') - ->label('Nama'), - - TextEntry::make('email') - ->label('Email'), - - TextEntry::make('phone_number') - ->label('No. Telepon'), - ]), - Grid::make(2) - ->schema([ - Section::make(fn () => CheerfulNotification::getByKey('company.press_card')) - ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-identification' : null) - ->schema([ - TextEntry::make('press_card') - ->hiddenLabel(), - - self::fileEntry('press_card', null, 'journalists'), - ]), - - Section::make(fn () => CheerfulNotification::getByKey('company.ukw_cert')) - ->icon(fn () => CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-document-text' : null) - ->schema([ - TextEntry::make('ukw_certificate') - ->hiddenLabel(), - - self::fileEntry('ukw_certificate', null, 'journalists'), - ]), - ]), - ]), - ]), - ]) - ->columns(1); + return PartnerInfolist::configure($schema); } public static function table(Table $table): Table { - return $table - ->columns([ - ...RowIndexColumn::make(), - - TextColumn::make('company.name') - ->label('Perusahaan') - ->searchable(query: function ($query, $search) { - $query->whereHas('company', function ($q) use ($search) { - $q->where('companies.name', 'like', "%{$search}%") - ->orWhere('companies.director_name', 'like', "%{$search}%"); - }); - }) - ->sortable() - ->description( - fn (User $record) => new HtmlString( - 'Akun: '.$record->name. - '
Direktur: '.$record->company?->director_name - ) - ), - - TextColumn::make('company.partnerMedia.name') - ->label('Media') - ->searchable(query: function ($query, $search) { - $query->whereHas('company.partnerMedia', function ($q) use ($search) { - $q->where('partner_media.name', 'like', "%{$search}%"); - }); - }) - ->sortable(), - - TextColumn::make('company.journalists.name') - ->label('Jurnalis') - ->searchable(query: function ($query, $search) { - $query->whereHas('company.journalists', function ($q) use ($search) { - $q->where('journalists.name', 'like', "%{$search}%"); - }); - }) - ->sortable() - ->badge(), - - TextColumn::make('status') - ->label('Status') - ->badge() - ->getStateUsing(function ($record) { - return $record->company->verificationRequest - ? $record->company->verificationRequest->status->getLabel() - : 'Belum Mengajukan'; - }) - ->color(function ($record) { - return $record->company->verificationRequest - ? $record->company->verificationRequest->status->getColor() - : 'gray'; - }), - - TextColumn::make('created_at') - ->label('Tgl Daftar') - ->date() - ->sortable() - ->dateTime('l, d F Y H:i:s') - ->wrap(), - ]) - ->filters([ - SelectFilter::make('media_type') - ->label('Jenis Media') - ->options(MediaType::class) - ->query(fn (Builder $query, array $data) => $query->when( - $data['value'], - fn (Builder $query, $value) => $query->whereHas('company.partnerMedia', fn ($q) => $q->where('type', $value)) - )) - ->native(false), - - SelectFilter::make('media_classification') - ->label('Klasifikasi Media') - ->options(MediaClassification::class) - ->query(fn (Builder $query, array $data) => $query->when( - $data['value'], - fn (Builder $query, $value) => $query->whereHas('company.partnerMedia', fn ($q) => $q->where('classification', $value)) - )) - ->native(false), - - SelectFilter::make('verification_status') - ->label('Status Verifikasi') - ->options(VerificationStatus::class) - ->query(fn (Builder $query, array $data) => $query->when( - $data['value'], - fn (Builder $query, $value) => $query->whereHas('company.verificationRequest', fn ($q) => $q->where('status', $value)) - )) - ->native(false), - - TrashedFilter::make() - ->native(false) - ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), - ]) - ->recordActions([ - ViewAction::make(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - ...DefaultBulkActions::make('rekanan'), - ]), - ]) - ->emptyStateIcon(Heroicon::OutlinedBuildingOffice2) - ->emptyStateHeading(fn () => CheerfulNotification::getByKey('company.partner_empty_heading')) - ->emptyStateDescription(fn () => CheerfulNotification::getByKey('company.partner_empty')) - ->defaultSort('created_at', 'desc') - ->deferFilters(false) - ->paginated([25, 50, 100, 'all']) - ->deferLoading(); + return PartnerTable::configure($table); } public static function getEloquentQuery(): Builder @@ -437,30 +55,4 @@ public static function getPages(): array 'view' => ViewPartner::route('/{record}'), ]; } - - private static function fileEntry( - string $name, - ?string $mediaOwner = 'company', - string $collection = 'companies' - ): PdfViewerEntry { - return PdfViewerEntry::make($name) - ->hiddenLabel() - ->getStateUsing(function (Model $record) use ($name, $mediaOwner, $collection): ?string { - $owner = data_get($record, $mediaOwner); - - if (! $owner) { - return null; - } - - $docType = str($name) - ->afterLast('.') - ->slug('-'); - - return $owner->getMedia($collection) - ->where('custom_properties.doc_type', (string) $docType) - ->sortByDesc('created_at') - ->first() - ?->getUrl(); - }); - } } diff --git a/app/Filament/Resources/Manage/Partners/Tables/PartnerTable.php b/app/Filament/Resources/Manage/Partners/Tables/PartnerTable.php new file mode 100644 index 0000000..e9549d4 --- /dev/null +++ b/app/Filament/Resources/Manage/Partners/Tables/PartnerTable.php @@ -0,0 +1,135 @@ +columns([ + ...RowIndexColumn::make(), + + TextColumn::make('company.name') + ->label('Perusahaan') + ->searchable(query: function ($query, $search) { + $query->whereHas('company', function ($q) use ($search) { + $q->where('companies.name', 'like', "%{$search}%") + ->orWhere('companies.director_name', 'like', "%{$search}%"); + }); + }) + ->sortable() + ->description( + fn (User $record) => new HtmlString( + 'Akun: '.$record->name. + '
Direktur: '.$record->company?->director_name + ) + ), + + TextColumn::make('company.partnerMedia.name') + ->label('Media') + ->searchable(query: function ($query, $search) { + $query->whereHas('company.partnerMedia', function ($q) use ($search) { + $q->where('partner_media.name', 'like', "%{$search}%"); + }); + }) + ->sortable(), + + TextColumn::make('company.journalists.name') + ->label('Jurnalis') + ->searchable(query: function ($query, $search) { + $query->whereHas('company.journalists', function ($q) use ($search) { + $q->where('journalists.name', 'like', "%{$search}%"); + }); + }) + ->sortable() + ->badge(), + + TextColumn::make('status') + ->label('Status') + ->badge() + ->getStateUsing(function ($record) { + return $record->company->verificationRequest + ? $record->company->verificationRequest->status->getLabel() + : 'Belum Mengajukan'; + }) + ->color(function ($record) { + return $record->company->verificationRequest + ? $record->company->verificationRequest->status->getColor() + : 'gray'; + }), + + TextColumn::make('created_at') + ->label('Tgl Daftar') + ->date() + ->sortable() + ->dateTime('l, d F Y H:i:s') + ->wrap(), + ]) + ->filters([ + SelectFilter::make('media_type') + ->label('Jenis Media') + ->options(MediaType::class) + ->query(fn (Builder $query, array $data) => $query->when( + $data['value'], + fn (Builder $query, $value) => $query->whereHas('company.partnerMedia', fn ($q) => $q->where('type', $value)) + )) + ->native(false), + + SelectFilter::make('media_classification') + ->label('Klasifikasi Media') + ->options(MediaClassification::class) + ->query(fn (Builder $query, array $data) => $query->when( + $data['value'], + fn (Builder $query, $value) => $query->whereHas('company.partnerMedia', fn ($q) => $q->where('classification', $value)) + )) + ->native(false), + + SelectFilter::make('verification_status') + ->label('Status Verifikasi') + ->options(VerificationStatus::class) + ->query(fn (Builder $query, array $data) => $query->when( + $data['value'], + fn (Builder $query, $value) => $query->whereHas('company.verificationRequest', fn ($q) => $q->where('status', $value)) + )) + ->native(false), + + TrashedFilter::make() + ->native(false) + ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), + ]) + ->recordActions([ + ViewAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + ...DefaultBulkActions::make('rekanan'), + ]), + ]) + ->emptyStateIcon(Heroicon::OutlinedBuildingOffice2) + ->emptyStateHeading(fn () => CheerfulNotification::getByKey('company.partner_empty_heading')) + ->emptyStateDescription(fn () => CheerfulNotification::getByKey('company.partner_empty')) + ->defaultSort('created_at', 'desc') + ->deferFilters(false) + ->paginated([25, 50, 100, 'all']) + ->deferLoading(); + } +} diff --git a/app/Filament/Resources/Master/Categories/CategoryResource.php b/app/Filament/Resources/Master/Categories/CategoryResource.php index 2e566ae..c43bfdd 100644 --- a/app/Filament/Resources/Master/Categories/CategoryResource.php +++ b/app/Filament/Resources/Master/Categories/CategoryResource.php @@ -2,29 +2,14 @@ namespace App\Filament\Resources\Master\Categories; -use App\Enums\IsActive; -use App\Enums\RoleEnum; -use App\Filament\Actions\Cheerful\DeleteAction; -use App\Filament\Actions\Cheerful\EditAction; -use App\Filament\Actions\Cheerful\ForceDeleteAction; -use App\Filament\Actions\Cheerful\RestoreAction; -use App\Filament\Actions\DefaultBulkActions; -use App\Filament\Columns\RowIndexColumn; -use App\Filament\Columns\TimestampColumns; use App\Filament\Resources\Master\Categories\Pages\ManageCategories; -use App\Filament\Support\CheerfulNotification; +use App\Filament\Resources\Master\Categories\Schemas\CategoryForm; +use App\Filament\Resources\Master\Categories\Tables\CategoryTable; use App\Models\Category; use BackedEnum; -use Filament\Actions\BulkActionGroup; -use Filament\Forms\Components\TextInput; use Filament\Resources\Resource; use Filament\Schemas\Schema; -use Filament\Support\Enums\Width; use Filament\Support\Icons\Heroicon; -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Columns\ToggleColumn; -use Filament\Tables\Filters\SelectFilter; -use Filament\Tables\Filters\TrashedFilter; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; @@ -48,75 +33,12 @@ class CategoryResource extends Resource public static function form(Schema $schema): Schema { - return $schema - ->components([ - TextInput::make('name') - ->label('Nama Kategori') - ->placeholder('Ekonomi') - ->autocomplete(false) - ->autofocus() - ->required() - ->maxLength(50), - ]) - ->columns(1); + return CategoryForm::configure($schema); } public static function table(Table $table): Table { - return $table - ->columns([ - ...RowIndexColumn::make(), - - TextColumn::make('name') - ->label('Nama') - ->searchable() - ->sortable(), - - ToggleColumn::make('is_active') - ->label('Aktif?') - ->sortable() - ->getStateUsing(fn (Category $record): bool => $record->is_active === IsActive::ACTIVE) - ->updateStateUsing(function (Category $record, bool $state): void { - $record->is_active = $state ? IsActive::ACTIVE : IsActive::INACTIVE; - $record->save(); - - CheerfulNotification::statusUpdated()->send(); - }), - - ...TimestampColumns::make(), - ]) - ->filters([ - SelectFilter::make('is_active') - ->label('Status') - ->options(IsActive::class) - ->native(false), - - TrashedFilter::make() - ->native(false) - ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), - ]) - ->recordActions([ - EditAction::make() - ->modalWidth(Width::Large), - - DeleteAction::make(), - - ForceDeleteAction::make(), - - RestoreAction::make(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - ...DefaultBulkActions::make('kategori'), - ]), - ]) - ->emptyStateIcon(Heroicon::OutlinedListBullet) - ->emptyStateHeading(fn () => CheerfulNotification::getByKey('category.empty_state_heading')) - ->emptyStateDescription(fn () => CheerfulNotification::getByKey('category.empty_state')) - ->defaultSort('created_at', 'desc') - ->deferFilters(false) - ->paginated([25, 50, 100, 'all']) - ->deferLoading(); + return CategoryTable::configure($table); } public static function getPages(): array diff --git a/app/Filament/Resources/Master/Categories/Schemas/CategoryForm.php b/app/Filament/Resources/Master/Categories/Schemas/CategoryForm.php new file mode 100644 index 0000000..094f900 --- /dev/null +++ b/app/Filament/Resources/Master/Categories/Schemas/CategoryForm.php @@ -0,0 +1,24 @@ +components([ + TextInput::make('name') + ->label('Nama Kategori') + ->placeholder('Ekonomi') + ->autocomplete(false) + ->autofocus() + ->required() + ->maxLength(50), + ]) + ->columns(1); + } +} diff --git a/app/Filament/Resources/Master/Categories/Tables/CategoryTable.php b/app/Filament/Resources/Master/Categories/Tables/CategoryTable.php new file mode 100644 index 0000000..7bc287b --- /dev/null +++ b/app/Filament/Resources/Master/Categories/Tables/CategoryTable.php @@ -0,0 +1,84 @@ +columns([ + ...RowIndexColumn::make(), + + TextColumn::make('name') + ->label('Nama') + ->searchable() + ->sortable(), + + ToggleColumn::make('is_active') + ->label('Aktif?') + ->sortable() + ->getStateUsing(fn (Category $record): bool => $record->is_active === IsActive::ACTIVE) + ->updateStateUsing(function (Category $record, bool $state): void { + $record->is_active = $state ? IsActive::ACTIVE : IsActive::INACTIVE; + $record->save(); + + CheerfulNotification::statusUpdated()->send(); + }), + + ...TimestampColumns::make(), + ]) + ->filters([ + SelectFilter::make('is_active') + ->label('Status') + ->options(IsActive::class) + ->native(false), + + TrashedFilter::make() + ->native(false) + ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), + ]) + ->recordActions([ + EditAction::make() + ->modalWidth(Width::Large), + + DeleteAction::make(), + + ForceDeleteAction::make(), + + RestoreAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + ...DefaultBulkActions::make('kategori'), + ]), + ]) + ->emptyStateIcon(Heroicon::OutlinedListBullet) + ->emptyStateHeading(fn () => CheerfulNotification::getByKey('category.empty_state_heading')) + ->emptyStateDescription(fn () => CheerfulNotification::getByKey('category.empty_state')) + ->defaultSort('created_at', 'desc') + ->deferFilters(false) + ->paginated([25, 50, 100, 'all']) + ->deferLoading(); + } +} diff --git a/app/Filament/Resources/Master/Classifications/ClassificationResource.php b/app/Filament/Resources/Master/Classifications/ClassificationResource.php index 4b420ea..db0631d 100644 --- a/app/Filament/Resources/Master/Classifications/ClassificationResource.php +++ b/app/Filament/Resources/Master/Classifications/ClassificationResource.php @@ -2,29 +2,14 @@ namespace App\Filament\Resources\Master\Classifications; -use App\Enums\IsActive; -use App\Enums\RoleEnum; -use App\Filament\Actions\Cheerful\DeleteAction; -use App\Filament\Actions\Cheerful\EditAction; -use App\Filament\Actions\Cheerful\ForceDeleteAction; -use App\Filament\Actions\Cheerful\RestoreAction; -use App\Filament\Actions\DefaultBulkActions; -use App\Filament\Columns\RowIndexColumn; -use App\Filament\Columns\TimestampColumns; use App\Filament\Resources\Master\Classifications\Pages\ManageClassifications; -use App\Filament\Support\CheerfulNotification; +use App\Filament\Resources\Master\Classifications\Schemas\ClassificationForm; +use App\Filament\Resources\Master\Classifications\Tables\ClassificationTable; use App\Models\Classification; use BackedEnum; -use Filament\Actions\BulkActionGroup; -use Filament\Forms\Components\TextInput; use Filament\Resources\Resource; use Filament\Schemas\Schema; -use Filament\Support\Enums\Width; use Filament\Support\Icons\Heroicon; -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Columns\ToggleColumn; -use Filament\Tables\Filters\SelectFilter; -use Filament\Tables\Filters\TrashedFilter; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; @@ -48,75 +33,12 @@ class ClassificationResource extends Resource public static function form(Schema $schema): Schema { - return $schema - ->components([ - TextInput::make('name') - ->label('Nama Klasifikasi') - ->placeholder('Pelaporan') - ->autocomplete(false) - ->autofocus() - ->required() - ->maxLength(50), - ]) - ->columns(1); + return ClassificationForm::configure($schema); } public static function table(Table $table): Table { - return $table - ->columns([ - ...RowIndexColumn::make(), - - TextColumn::make('name') - ->label('Nama') - ->searchable() - ->sortable(), - - ToggleColumn::make('is_active') - ->label('Aktif?') - ->sortable() - ->getStateUsing(fn (Classification $record): bool => $record->is_active === IsActive::ACTIVE) - ->updateStateUsing(function (Classification $record, bool $state): void { - $record->is_active = $state ? IsActive::ACTIVE : IsActive::INACTIVE; - $record->save(); - - CheerfulNotification::statusUpdated()->send(); - }), - - ...TimestampColumns::make(), - ]) - ->filters([ - SelectFilter::make('is_active') - ->label('Status') - ->options(IsActive::class) - ->native(false), - - TrashedFilter::make() - ->native(false) - ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), - ]) - ->recordActions([ - EditAction::make() - ->modalWidth(Width::Large), - - DeleteAction::make(), - - ForceDeleteAction::make(), - - RestoreAction::make(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - ...DefaultBulkActions::make('klasifikasi'), - ]), - ]) - ->emptyStateIcon(Heroicon::OutlinedBookOpen) - ->emptyStateHeading(fn () => CheerfulNotification::getByKey('classification.empty_state_heading')) - ->emptyStateDescription(fn () => CheerfulNotification::getByKey('classification.empty_state')) - ->defaultSort('created_at', 'desc') - ->deferFilters(false) - ->paginated([25, 50, 100, 'all']) - ->deferLoading(); + return ClassificationTable::configure($table); } public static function getPages(): array diff --git a/app/Filament/Resources/Master/Classifications/Schemas/ClassificationForm.php b/app/Filament/Resources/Master/Classifications/Schemas/ClassificationForm.php new file mode 100644 index 0000000..8937f7a --- /dev/null +++ b/app/Filament/Resources/Master/Classifications/Schemas/ClassificationForm.php @@ -0,0 +1,24 @@ +components([ + TextInput::make('name') + ->label('Nama Klasifikasi') + ->placeholder('Pelaporan') + ->autocomplete(false) + ->autofocus() + ->required() + ->maxLength(50), + ]) + ->columns(1); + } +} diff --git a/app/Filament/Resources/Master/Classifications/Tables/ClassificationTable.php b/app/Filament/Resources/Master/Classifications/Tables/ClassificationTable.php new file mode 100644 index 0000000..0a73ca3 --- /dev/null +++ b/app/Filament/Resources/Master/Classifications/Tables/ClassificationTable.php @@ -0,0 +1,84 @@ +columns([ + ...RowIndexColumn::make(), + + TextColumn::make('name') + ->label('Nama') + ->searchable() + ->sortable(), + + ToggleColumn::make('is_active') + ->label('Aktif?') + ->sortable() + ->getStateUsing(fn (Classification $record): bool => $record->is_active === IsActive::ACTIVE) + ->updateStateUsing(function (Classification $record, bool $state): void { + $record->is_active = $state ? IsActive::ACTIVE : IsActive::INACTIVE; + $record->save(); + + CheerfulNotification::statusUpdated()->send(); + }), + + ...TimestampColumns::make(), + ]) + ->filters([ + SelectFilter::make('is_active') + ->label('Status') + ->options(IsActive::class) + ->native(false), + + TrashedFilter::make() + ->native(false) + ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), + ]) + ->recordActions([ + EditAction::make() + ->modalWidth(Width::Large), + + DeleteAction::make(), + + ForceDeleteAction::make(), + + RestoreAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + ...DefaultBulkActions::make('klasifikasi'), + ]), + ]) + ->emptyStateIcon(Heroicon::OutlinedBookOpen) + ->emptyStateHeading(fn () => CheerfulNotification::getByKey('classification.empty_state_heading')) + ->emptyStateDescription(fn () => CheerfulNotification::getByKey('classification.empty_state')) + ->defaultSort('created_at', 'desc') + ->deferFilters(false) + ->paginated([25, 50, 100, 'all']) + ->deferLoading(); + } +} diff --git a/app/Filament/Resources/Master/Departments/DepartmentResource.php b/app/Filament/Resources/Master/Departments/DepartmentResource.php index 3b28c42..32851a3 100644 --- a/app/Filament/Resources/Master/Departments/DepartmentResource.php +++ b/app/Filament/Resources/Master/Departments/DepartmentResource.php @@ -2,29 +2,14 @@ namespace App\Filament\Resources\Master\Departments; -use App\Enums\IsActive; -use App\Enums\RoleEnum; -use App\Filament\Actions\Cheerful\DeleteAction; -use App\Filament\Actions\Cheerful\EditAction; -use App\Filament\Actions\Cheerful\ForceDeleteAction; -use App\Filament\Actions\Cheerful\RestoreAction; -use App\Filament\Actions\DefaultBulkActions; -use App\Filament\Columns\RowIndexColumn; -use App\Filament\Columns\TimestampColumns; use App\Filament\Resources\Master\Departments\Pages\ManageDepartments; -use App\Filament\Support\CheerfulNotification; +use App\Filament\Resources\Master\Departments\Schemas\DepartmentForm; +use App\Filament\Resources\Master\Departments\Tables\DepartmentTable; use App\Models\Department; use BackedEnum; -use Filament\Actions\BulkActionGroup; -use Filament\Forms\Components\TextInput; use Filament\Resources\Resource; use Filament\Schemas\Schema; -use Filament\Support\Enums\Width; use Filament\Support\Icons\Heroicon; -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Columns\ToggleColumn; -use Filament\Tables\Filters\SelectFilter; -use Filament\Tables\Filters\TrashedFilter; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; @@ -48,86 +33,12 @@ class DepartmentResource extends Resource public static function form(Schema $schema): Schema { - return $schema - ->components([ - TextInput::make('name') - ->label('Nama OPD') - ->placeholder('Dinas Komunikasi dan Informatika') - ->autocomplete(false) - ->autofocus() - ->required() - ->maxLength(100), - - TextInput::make('alias') - ->label('Alias') - ->placeholder('Diskominfo') - ->autocomplete(false) - ->required() - ->maxLength(20), - ]) - ->columns(1); + return DepartmentForm::configure($schema); } public static function table(Table $table): Table { - return $table - ->columns([ - ...RowIndexColumn::make(), - - TextColumn::make('name') - ->label('Nama') - ->searchable() - ->sortable(), - - TextColumn::make('alias') - ->searchable() - ->sortable(), - - ToggleColumn::make('is_active') - ->label('Aktif?') - ->sortable() - ->getStateUsing(fn (Department $record): bool => $record->is_active === IsActive::ACTIVE) - ->updateStateUsing(function (Department $record, bool $state): void { - $record->is_active = $state ? IsActive::ACTIVE : IsActive::INACTIVE; - $record->save(); - - CheerfulNotification::statusUpdated()->send(); - }), - - ...TimestampColumns::make(), - ]) - ->filters([ - SelectFilter::make('is_active') - ->label('Status') - ->options(IsActive::class) - ->native(false), - - TrashedFilter::make() - ->native(false) - ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), - ]) - ->recordActions([ - EditAction::make() - ->modalWidth(Width::Large), - - DeleteAction::make(), - - ForceDeleteAction::make(), - - RestoreAction::make(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - ...DefaultBulkActions::make('OPD'), - ]), - ]) - ->emptyStateIcon(Heroicon::OutlinedBuildingLibrary) - ->emptyStateHeading(fn () => CheerfulNotification::getByKey('opd.empty_state_heading')) - ->emptyStateDescription(fn () => CheerfulNotification::getByKey('opd.empty_state')) - ->defaultSort('created_at', 'desc') - ->deferFilters(false) - ->paginated([25, 50, 100, 'all']) - ->deferLoading(); + return DepartmentTable::configure($table); } public static function getPages(): array diff --git a/app/Filament/Resources/Master/Departments/Schemas/DepartmentForm.php b/app/Filament/Resources/Master/Departments/Schemas/DepartmentForm.php new file mode 100644 index 0000000..0608441 --- /dev/null +++ b/app/Filament/Resources/Master/Departments/Schemas/DepartmentForm.php @@ -0,0 +1,31 @@ +components([ + TextInput::make('name') + ->label('Nama OPD') + ->placeholder('Dinas Komunikasi dan Informatika') + ->autocomplete(false) + ->autofocus() + ->required() + ->maxLength(100), + + TextInput::make('alias') + ->label('Alias') + ->placeholder('Diskominfo') + ->autocomplete(false) + ->required() + ->maxLength(20), + ]) + ->columns(1); + } +} diff --git a/app/Filament/Resources/Master/Departments/Tables/DepartmentTable.php b/app/Filament/Resources/Master/Departments/Tables/DepartmentTable.php new file mode 100644 index 0000000..e23723d --- /dev/null +++ b/app/Filament/Resources/Master/Departments/Tables/DepartmentTable.php @@ -0,0 +1,88 @@ +columns([ + ...RowIndexColumn::make(), + + TextColumn::make('name') + ->label('Nama') + ->searchable() + ->sortable(), + + TextColumn::make('alias') + ->searchable() + ->sortable(), + + ToggleColumn::make('is_active') + ->label('Aktif?') + ->sortable() + ->getStateUsing(fn (Department $record): bool => $record->is_active === IsActive::ACTIVE) + ->updateStateUsing(function (Department $record, bool $state): void { + $record->is_active = $state ? IsActive::ACTIVE : IsActive::INACTIVE; + $record->save(); + + CheerfulNotification::statusUpdated()->send(); + }), + + ...TimestampColumns::make(), + ]) + ->filters([ + SelectFilter::make('is_active') + ->label('Status') + ->options(IsActive::class) + ->native(false), + + TrashedFilter::make() + ->native(false) + ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), + ]) + ->recordActions([ + EditAction::make() + ->modalWidth(Width::Large), + + DeleteAction::make(), + + ForceDeleteAction::make(), + + RestoreAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + ...DefaultBulkActions::make('OPD'), + ]), + ]) + ->emptyStateIcon(Heroicon::OutlinedBuildingLibrary) + ->emptyStateHeading(fn () => CheerfulNotification::getByKey('opd.empty_state_heading')) + ->emptyStateDescription(fn () => CheerfulNotification::getByKey('opd.empty_state')) + ->defaultSort('created_at', 'desc') + ->deferFilters(false) + ->paginated([25, 50, 100, 'all']) + ->deferLoading(); + } +} diff --git a/app/Filament/Resources/Master/Locations/LocationResource.php b/app/Filament/Resources/Master/Locations/LocationResource.php index c38ac21..d32abe5 100644 --- a/app/Filament/Resources/Master/Locations/LocationResource.php +++ b/app/Filament/Resources/Master/Locations/LocationResource.php @@ -2,29 +2,14 @@ namespace App\Filament\Resources\Master\Locations; -use App\Enums\IsActive; -use App\Enums\RoleEnum; -use App\Filament\Actions\Cheerful\DeleteAction; -use App\Filament\Actions\Cheerful\EditAction; -use App\Filament\Actions\Cheerful\ForceDeleteAction; -use App\Filament\Actions\Cheerful\RestoreAction; -use App\Filament\Actions\DefaultBulkActions; -use App\Filament\Columns\RowIndexColumn; -use App\Filament\Columns\TimestampColumns; use App\Filament\Resources\Master\Locations\Pages\ManageLocations; -use App\Filament\Support\CheerfulNotification; +use App\Filament\Resources\Master\Locations\Schemas\LocationForm; +use App\Filament\Resources\Master\Locations\Tables\LocationTable; use App\Models\Location; use BackedEnum; -use Filament\Actions\BulkActionGroup; -use Filament\Forms\Components\TextInput; use Filament\Resources\Resource; use Filament\Schemas\Schema; -use Filament\Support\Enums\Width; use Filament\Support\Icons\Heroicon; -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Columns\ToggleColumn; -use Filament\Tables\Filters\SelectFilter; -use Filament\Tables\Filters\TrashedFilter; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; @@ -48,75 +33,12 @@ class LocationResource extends Resource public static function form(Schema $schema): Schema { - return $schema - ->components([ - TextInput::make('name') - ->label('Nama Lokus') - ->placeholder('Purwakarta') - ->autocomplete(false) - ->autofocus() - ->required() - ->maxLength(50), - ]) - ->columns(1); + return LocationForm::configure($schema); } public static function table(Table $table): Table { - return $table - ->columns([ - ...RowIndexColumn::make(), - - TextColumn::make('name') - ->label('Nama') - ->searchable() - ->sortable(), - - ToggleColumn::make('is_active') - ->label('Aktif?') - ->sortable() - ->getStateUsing(fn (Location $record): bool => $record->is_active === IsActive::ACTIVE) - ->updateStateUsing(function (Location $record, bool $state): void { - $record->is_active = $state ? IsActive::ACTIVE : IsActive::INACTIVE; - $record->save(); - - CheerfulNotification::statusUpdated()->send(); - }), - - ...TimestampColumns::make(), - ]) - ->filters([ - SelectFilter::make('is_active') - ->label('Status') - ->options(IsActive::class) - ->native(false), - - TrashedFilter::make() - ->native(false) - ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), - ]) - ->recordActions([ - EditAction::make() - ->modalWidth(Width::Large), - - DeleteAction::make(), - - ForceDeleteAction::make(), - - RestoreAction::make(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - ...DefaultBulkActions::make('Lokus'), - ]), - ]) - ->emptyStateIcon(Heroicon::OutlinedMapPin) - ->emptyStateHeading(fn () => CheerfulNotification::getByKey('locus.empty_state_heading')) - ->emptyStateDescription(fn () => CheerfulNotification::getByKey('locus.empty_state')) - ->defaultSort('created_at', 'desc') - ->deferFilters(false) - ->paginated([25, 50, 100, 'all']) - ->deferLoading(); + return LocationTable::configure($table); } public static function getPages(): array diff --git a/app/Filament/Resources/Master/Locations/Schemas/LocationForm.php b/app/Filament/Resources/Master/Locations/Schemas/LocationForm.php new file mode 100644 index 0000000..707cb04 --- /dev/null +++ b/app/Filament/Resources/Master/Locations/Schemas/LocationForm.php @@ -0,0 +1,24 @@ +components([ + TextInput::make('name') + ->label('Nama Lokus') + ->placeholder('Purwakarta') + ->autocomplete(false) + ->autofocus() + ->required() + ->maxLength(50), + ]) + ->columns(1); + } +} diff --git a/app/Filament/Resources/Master/Locations/Tables/LocationTable.php b/app/Filament/Resources/Master/Locations/Tables/LocationTable.php new file mode 100644 index 0000000..f250d6f --- /dev/null +++ b/app/Filament/Resources/Master/Locations/Tables/LocationTable.php @@ -0,0 +1,84 @@ +columns([ + ...RowIndexColumn::make(), + + TextColumn::make('name') + ->label('Nama') + ->searchable() + ->sortable(), + + ToggleColumn::make('is_active') + ->label('Aktif?') + ->sortable() + ->getStateUsing(fn (Location $record): bool => $record->is_active === IsActive::ACTIVE) + ->updateStateUsing(function (Location $record, bool $state): void { + $record->is_active = $state ? IsActive::ACTIVE : IsActive::INACTIVE; + $record->save(); + + CheerfulNotification::statusUpdated()->send(); + }), + + ...TimestampColumns::make(), + ]) + ->filters([ + SelectFilter::make('is_active') + ->label('Status') + ->options(IsActive::class) + ->native(false), + + TrashedFilter::make() + ->native(false) + ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), + ]) + ->recordActions([ + EditAction::make() + ->modalWidth(Width::Large), + + DeleteAction::make(), + + ForceDeleteAction::make(), + + RestoreAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + ...DefaultBulkActions::make('Lokus'), + ]), + ]) + ->emptyStateIcon(Heroicon::OutlinedMapPin) + ->emptyStateHeading(fn () => CheerfulNotification::getByKey('locus.empty_state_heading')) + ->emptyStateDescription(fn () => CheerfulNotification::getByKey('locus.empty_state')) + ->defaultSort('created_at', 'desc') + ->deferFilters(false) + ->paginated([25, 50, 100, 'all']) + ->deferLoading(); + } +} diff --git a/app/Filament/Resources/Master/SubClassifications/Schemas/SubClassificationForm.php b/app/Filament/Resources/Master/SubClassifications/Schemas/SubClassificationForm.php new file mode 100644 index 0000000..675ad54 --- /dev/null +++ b/app/Filament/Resources/Master/SubClassifications/Schemas/SubClassificationForm.php @@ -0,0 +1,34 @@ +components([ + Select::make('classification_id') + ->label('Klasifikasi Utama') + ->options(Classification::query()->active()->pluck('name', 'id')) + ->searchable() + ->native(false) + ->required() + ->rules(['exists:classifications,id']) + ->autofocus(), + + TextInput::make('name') + ->label('Nama Subklasifikasi') + ->placeholder('Korupsi') + ->autocomplete(false) + ->required() + ->maxLength(50), + ]) + ->columns(1); + } +} diff --git a/app/Filament/Resources/Master/SubClassifications/SubClassificationResource.php b/app/Filament/Resources/Master/SubClassifications/SubClassificationResource.php index 1b0e9ad..0fff8a6 100644 --- a/app/Filament/Resources/Master/SubClassifications/SubClassificationResource.php +++ b/app/Filament/Resources/Master/SubClassifications/SubClassificationResource.php @@ -2,32 +2,14 @@ namespace App\Filament\Resources\Master\SubClassifications; -use App\Enums\IsActive; -use App\Enums\RoleEnum; -use App\Filament\Actions\Cheerful\DeleteAction; -use App\Filament\Actions\Cheerful\EditAction; -use App\Filament\Actions\Cheerful\ForceDeleteAction; -use App\Filament\Actions\Cheerful\RestoreAction; -use App\Filament\Actions\DefaultBulkActions; -use App\Filament\Columns\RowIndexColumn; -use App\Filament\Columns\TimestampColumns; use App\Filament\Resources\Master\SubClassifications\Pages\ManageSubClassifications; -use App\Filament\Support\CheerfulNotification; -use App\Models\Classification; +use App\Filament\Resources\Master\SubClassifications\Schemas\SubClassificationForm; +use App\Filament\Resources\Master\SubClassifications\Tables\SubClassificationTable; use App\Models\SubClassification; use BackedEnum; -use Filament\Actions\BulkActionGroup; -use Filament\Forms\Components\Select; -use Filament\Forms\Components\TextInput; use Filament\Resources\Resource; use Filament\Schemas\Schema; -use Filament\Support\Enums\Width; use Filament\Support\Icons\Heroicon; -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Columns\ToggleColumn; -use Filament\Tables\Filters\SelectFilter; -use Filament\Tables\Filters\TrashedFilter; -use Filament\Tables\Grouping\Group; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; @@ -51,99 +33,12 @@ class SubClassificationResource extends Resource public static function form(Schema $schema): Schema { - return $schema - ->components([ - Select::make('classification_id') - ->label('Klasifikasi Utama') - ->options(Classification::query()->active()->pluck('name', 'id')) - ->searchable() - ->native(false) - ->required() - ->rules(['exists:classifications,id']) - ->autofocus(), - - TextInput::make('name') - ->label('Nama Subklasifikasi') - ->placeholder('Korupsi') - ->autocomplete(false) - ->required() - ->maxLength(50), - ]) - ->columns(1); + return SubClassificationForm::configure($schema); } public static function table(Table $table): Table { - return $table - ->columns([ - ...RowIndexColumn::make(), - - TextColumn::make('name') - ->label('Nama') - ->searchable() - ->sortable(), - - ToggleColumn::make('is_active') - ->label('Aktif?') - ->sortable() - ->getStateUsing(fn (SubClassification $record): bool => $record->is_active === IsActive::ACTIVE) - ->updateStateUsing(function (SubClassification $record, bool $state): void { - $record->is_active = $state ? IsActive::ACTIVE : IsActive::INACTIVE; - $record->save(); - - CheerfulNotification::success( - CheerfulNotification::getByKey('status.updated'), - CheerfulNotification::getByKey('status.update_success') - )->send(); - }), - - ...TimestampColumns::make(), - ]) - ->filters([ - SelectFilter::make('is_active') - ->label('Status') - ->options(IsActive::class) - ->native(false), - - SelectFilter::make('classification') - ->label('Klasifikasi') - ->relationship('classification', 'name') - ->searchable() - ->preload() - ->native(false), - - TrashedFilter::make() - ->native(false) - ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), - ]) - ->recordActions([ - EditAction::make() - ->modalWidth(Width::Large), - - DeleteAction::make(), - - ForceDeleteAction::make(), - - RestoreAction::make(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - ...DefaultBulkActions::make('Subklasifikasi'), - ]), - ]) - ->emptyStateIcon(Heroicon::OutlinedQueueList) - ->emptyStateHeading(fn () => CheerfulNotification::getByKey('classification.sub.empty_state_heading')) - ->emptyStateDescription(fn () => CheerfulNotification::getByKey('classification.sub.empty_state')) - ->defaultSort('created_at', 'desc') - ->deferFilters(false) - ->paginated([25, 50, 100, 'all']) - ->deferLoading() - ->groups([ - Group::make('classification.name') - ->label('Klasifikasi') - ->collapsible(), - ]) - ->defaultGroup('classification.name'); + return SubClassificationTable::configure($table); } public static function getPages(): array diff --git a/app/Filament/Resources/Master/SubClassifications/Tables/SubClassificationTable.php b/app/Filament/Resources/Master/SubClassifications/Tables/SubClassificationTable.php new file mode 100644 index 0000000..6bafdbd --- /dev/null +++ b/app/Filament/Resources/Master/SubClassifications/Tables/SubClassificationTable.php @@ -0,0 +1,101 @@ +columns([ + ...RowIndexColumn::make(), + + TextColumn::make('name') + ->label('Nama') + ->searchable() + ->sortable(), + + ToggleColumn::make('is_active') + ->label('Aktif?') + ->sortable() + ->getStateUsing(fn (SubClassification $record): bool => $record->is_active === IsActive::ACTIVE) + ->updateStateUsing(function (SubClassification $record, bool $state): void { + $record->is_active = $state ? IsActive::ACTIVE : IsActive::INACTIVE; + $record->save(); + + CheerfulNotification::success( + CheerfulNotification::getByKey('status.updated'), + CheerfulNotification::getByKey('status.update_success') + )->send(); + }), + + ...TimestampColumns::make(), + ]) + ->filters([ + SelectFilter::make('is_active') + ->label('Status') + ->options(IsActive::class) + ->native(false), + + SelectFilter::make('classification') + ->label('Klasifikasi') + ->relationship('classification', 'name') + ->searchable() + ->preload() + ->native(false), + + TrashedFilter::make() + ->native(false) + ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), + ]) + ->recordActions([ + EditAction::make() + ->modalWidth(Width::Large), + + DeleteAction::make(), + + ForceDeleteAction::make(), + + RestoreAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + ...DefaultBulkActions::make('Subklasifikasi'), + ]), + ]) + ->emptyStateIcon(Heroicon::OutlinedQueueList) + ->emptyStateHeading(fn () => CheerfulNotification::getByKey('classification.sub.empty_state_heading')) + ->emptyStateDescription(fn () => CheerfulNotification::getByKey('classification.sub.empty_state')) + ->defaultSort('created_at', 'desc') + ->deferFilters(false) + ->paginated([25, 50, 100, 'all']) + ->deferLoading() + ->groups([ + Group::make('classification.name') + ->label('Klasifikasi') + ->collapsible(), + ]) + ->defaultGroup('classification.name'); + } +} diff --git a/app/Filament/Resources/Master/SubLocations/Schemas/SubLocationForm.php b/app/Filament/Resources/Master/SubLocations/Schemas/SubLocationForm.php new file mode 100644 index 0000000..0123014 --- /dev/null +++ b/app/Filament/Resources/Master/SubLocations/Schemas/SubLocationForm.php @@ -0,0 +1,34 @@ +components([ + Select::make('location_id') + ->label('Lokus Utama') + ->options(Location::query()->pluck('name', 'id')) + ->searchable() + ->native(false) + ->required() + ->rules(['exists:locations,id']) + ->autofocus(), + + TextInput::make('name') + ->label('Nama Sublokus') + ->placeholder('Kecamatan Purwakarta') + ->autocomplete(false) + ->required() + ->maxLength(50), + ]) + ->columns(1); + } +} diff --git a/app/Filament/Resources/Master/SubLocations/SubLocationResource.php b/app/Filament/Resources/Master/SubLocations/SubLocationResource.php index d8c41fb..21cf6e6 100644 --- a/app/Filament/Resources/Master/SubLocations/SubLocationResource.php +++ b/app/Filament/Resources/Master/SubLocations/SubLocationResource.php @@ -2,32 +2,14 @@ namespace App\Filament\Resources\Master\SubLocations; -use App\Enums\IsActive; -use App\Enums\RoleEnum; -use App\Filament\Actions\Cheerful\DeleteAction; -use App\Filament\Actions\Cheerful\EditAction; -use App\Filament\Actions\Cheerful\ForceDeleteAction; -use App\Filament\Actions\Cheerful\RestoreAction; -use App\Filament\Actions\DefaultBulkActions; -use App\Filament\Columns\RowIndexColumn; -use App\Filament\Columns\TimestampColumns; use App\Filament\Resources\Master\SubLocations\Pages\ManageSubLocations; -use App\Filament\Support\CheerfulNotification; -use App\Models\Location; +use App\Filament\Resources\Master\SubLocations\Schemas\SubLocationForm; +use App\Filament\Resources\Master\SubLocations\Tables\SubLocationTable; use App\Models\SubLocation; use BackedEnum; -use Filament\Actions\BulkActionGroup; -use Filament\Forms\Components\Select; -use Filament\Forms\Components\TextInput; use Filament\Resources\Resource; use Filament\Schemas\Schema; -use Filament\Support\Enums\Width; use Filament\Support\Icons\Heroicon; -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Columns\ToggleColumn; -use Filament\Tables\Filters\SelectFilter; -use Filament\Tables\Filters\TrashedFilter; -use Filament\Tables\Grouping\Group; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; @@ -51,96 +33,12 @@ class SubLocationResource extends Resource public static function form(Schema $schema): Schema { - return $schema - ->components([ - Select::make('location_id') - ->label('Lokus Utama') - ->options(Location::query()->pluck('name', 'id')) - ->searchable() - ->native(false) - ->required() - ->rules(['exists:locations,id']) - ->autofocus(), - - TextInput::make('name') - ->label('Nama Sublokus') - ->placeholder('Kecamatan Purwakarta') - ->autocomplete(false) - ->required() - ->maxLength(50), - ]) - ->columns(1); + return SubLocationForm::configure($schema); } public static function table(Table $table): Table { - return $table - ->columns([ - ...RowIndexColumn::make(), - - TextColumn::make('name') - ->label('Nama') - ->searchable() - ->sortable(), - - ToggleColumn::make('is_active') - ->label('Aktif?') - ->sortable() - ->getStateUsing(fn (SubLocation $record): bool => $record->is_active === IsActive::ACTIVE) - ->updateStateUsing(function (SubLocation $record, bool $state): void { - $record->is_active = $state ? IsActive::ACTIVE : IsActive::INACTIVE; - $record->save(); - - CheerfulNotification::statusUpdated()->send(); - }), - - ...TimestampColumns::make(), - ]) - ->filters([ - SelectFilter::make('is_active') - ->label('Status') - ->options(IsActive::class) - ->native(false), - - SelectFilter::make('location') - ->label('Lokus') - ->relationship('location', 'name') - ->searchable() - ->preload() - ->native(false), - - TrashedFilter::make() - ->native(false) - ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), - ]) - ->recordActions([ - EditAction::make() - ->modalWidth(Width::Large), - - DeleteAction::make(), - - ForceDeleteAction::make(), - - RestoreAction::make(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - ...DefaultBulkActions::make('Sublokus'), - ]), - ]) - ->emptyStateIcon(Heroicon::OutlinedNumberedList) - ->emptyStateHeading(fn () => CheerfulNotification::getByKey('locus.sub.empty_state_heading')) - ->emptyStateDescription(fn () => CheerfulNotification::getByKey('locus.sub.empty_state')) - ->defaultSort('created_at', 'desc') - ->deferFilters(false) - ->paginated([25, 50, 100, 'all']) - ->deferLoading() - ->groups([ - Group::make('location.name') - ->label('Lokus') - ->collapsible(), - ]) - ->defaultGroup('location.name'); + return SubLocationTable::configure($table); } public static function getPages(): array diff --git a/app/Filament/Resources/Master/SubLocations/Tables/SubLocationTable.php b/app/Filament/Resources/Master/SubLocations/Tables/SubLocationTable.php new file mode 100644 index 0000000..482afe2 --- /dev/null +++ b/app/Filament/Resources/Master/SubLocations/Tables/SubLocationTable.php @@ -0,0 +1,98 @@ +columns([ + ...RowIndexColumn::make(), + + TextColumn::make('name') + ->label('Nama') + ->searchable() + ->sortable(), + + ToggleColumn::make('is_active') + ->label('Aktif?') + ->sortable() + ->getStateUsing(fn (SubLocation $record): bool => $record->is_active === IsActive::ACTIVE) + ->updateStateUsing(function (SubLocation $record, bool $state): void { + $record->is_active = $state ? IsActive::ACTIVE : IsActive::INACTIVE; + $record->save(); + + CheerfulNotification::statusUpdated()->send(); + }), + + ...TimestampColumns::make(), + ]) + ->filters([ + SelectFilter::make('is_active') + ->label('Status') + ->options(IsActive::class) + ->native(false), + + SelectFilter::make('location') + ->label('Lokus') + ->relationship('location', 'name') + ->searchable() + ->preload() + ->native(false), + + TrashedFilter::make() + ->native(false) + ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), + ]) + ->recordActions([ + EditAction::make() + ->modalWidth(Width::Large), + + DeleteAction::make(), + + ForceDeleteAction::make(), + + RestoreAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + ...DefaultBulkActions::make('Sublokus'), + ]), + ]) + ->emptyStateIcon(Heroicon::OutlinedNumberedList) + ->emptyStateHeading(fn () => CheerfulNotification::getByKey('locus.sub.empty_state_heading')) + ->emptyStateDescription(fn () => CheerfulNotification::getByKey('locus.sub.empty_state')) + ->defaultSort('created_at', 'desc') + ->deferFilters(false) + ->paginated([25, 50, 100, 'all']) + ->deferLoading() + ->groups([ + Group::make('location.name') + ->label('Lokus') + ->collapsible(), + ]) + ->defaultGroup('location.name'); + } +} diff --git a/app/Filament/Resources/Master/Themes/Schemas/ThemeForm.php b/app/Filament/Resources/Master/Themes/Schemas/ThemeForm.php new file mode 100644 index 0000000..e771c04 --- /dev/null +++ b/app/Filament/Resources/Master/Themes/Schemas/ThemeForm.php @@ -0,0 +1,24 @@ +components([ + TextInput::make('name') + ->label('Nama Tema') + ->placeholder('Pendidikan') + ->autocomplete(false) + ->autofocus() + ->required() + ->maxLength(50), + ]) + ->columns(1); + } +} diff --git a/app/Filament/Resources/Master/Themes/Tables/ThemeTable.php b/app/Filament/Resources/Master/Themes/Tables/ThemeTable.php new file mode 100644 index 0000000..64cd5c6 --- /dev/null +++ b/app/Filament/Resources/Master/Themes/Tables/ThemeTable.php @@ -0,0 +1,78 @@ +columns([ + ...RowIndexColumn::make(), + + TextColumn::make('name') + ->label('Nama') + ->searchable() + ->sortable(), + + ToggleColumn::make('is_active') + ->label('Aktif?') + ->sortable() + ->getStateUsing(fn (Theme $record): bool => $record->is_active === IsActive::ACTIVE) + ->updateStateUsing(function (Theme $record, bool $state): void { + $record->is_active = $state ? IsActive::ACTIVE : IsActive::INACTIVE; + $record->save(); + + CheerfulNotification::statusUpdated()->send(); + }), + + ...TimestampColumns::make(), + ]) + ->filters([ + TrashedFilter::make() + ->native(false) + ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), + ]) + ->recordActions([ + EditAction::make() + ->modalWidth(Width::Large), + + DeleteAction::make(), + + ForceDeleteAction::make(), + + RestoreAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + ...DefaultBulkActions::make('Tema'), + ]), + ]) + ->emptyStateIcon(Heroicon::OutlinedPuzzlePiece) + ->emptyStateHeading(fn () => CheerfulNotification::getByKey('theme.empty_state_heading')) + ->emptyStateDescription(fn () => CheerfulNotification::getByKey('theme.empty_state')) + ->defaultSort('created_at', 'desc') + ->deferFilters(false) + ->paginated([25, 50, 100, 'all']) + ->deferLoading(); + } +} diff --git a/app/Filament/Resources/Master/Themes/ThemeResource.php b/app/Filament/Resources/Master/Themes/ThemeResource.php index b3c5b09..7afccfb 100644 --- a/app/Filament/Resources/Master/Themes/ThemeResource.php +++ b/app/Filament/Resources/Master/Themes/ThemeResource.php @@ -2,28 +2,14 @@ namespace App\Filament\Resources\Master\Themes; -use App\Enums\IsActive; -use App\Enums\RoleEnum; -use App\Filament\Actions\Cheerful\DeleteAction; -use App\Filament\Actions\Cheerful\EditAction; -use App\Filament\Actions\Cheerful\ForceDeleteAction; -use App\Filament\Actions\Cheerful\RestoreAction; -use App\Filament\Actions\DefaultBulkActions; -use App\Filament\Columns\RowIndexColumn; -use App\Filament\Columns\TimestampColumns; use App\Filament\Resources\Master\Themes\Pages\ManageThemes; -use App\Filament\Support\CheerfulNotification; +use App\Filament\Resources\Master\Themes\Schemas\ThemeForm; +use App\Filament\Resources\Master\Themes\Tables\ThemeTable; use App\Models\Theme; use BackedEnum; -use Filament\Actions\BulkActionGroup; -use Filament\Forms\Components\TextInput; use Filament\Resources\Resource; use Filament\Schemas\Schema; -use Filament\Support\Enums\Width; use Filament\Support\Icons\Heroicon; -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Columns\ToggleColumn; -use Filament\Tables\Filters\TrashedFilter; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; @@ -47,70 +33,12 @@ class ThemeResource extends Resource public static function form(Schema $schema): Schema { - return $schema - ->components([ - TextInput::make('name') - ->label('Nama Tema') - ->placeholder('Pendidikan') - ->autocomplete(false) - ->autofocus() - ->required() - ->maxLength(50), - ]) - ->columns(1); + return ThemeForm::configure($schema); } public static function table(Table $table): Table { - return $table - ->columns([ - ...RowIndexColumn::make(), - - TextColumn::make('name') - ->label('Nama') - ->searchable() - ->sortable(), - - ToggleColumn::make('is_active') - ->label('Aktif?') - ->sortable() - ->getStateUsing(fn (Theme $record): bool => $record->is_active === IsActive::ACTIVE) - ->updateStateUsing(function (Theme $record, bool $state): void { - $record->is_active = $state ? IsActive::ACTIVE : IsActive::INACTIVE; - $record->save(); - - CheerfulNotification::statusUpdated()->send(); - }), - - ...TimestampColumns::make(), - ]) - ->filters([ - TrashedFilter::make() - ->native(false) - ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), - ]) - ->recordActions([ - EditAction::make() - ->modalWidth(Width::Large), - - DeleteAction::make(), - - ForceDeleteAction::make(), - - RestoreAction::make(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - ...DefaultBulkActions::make('Tema'), - ]), - ]) - ->emptyStateIcon(Heroicon::OutlinedPuzzlePiece) - ->emptyStateHeading(fn () => CheerfulNotification::getByKey('theme.empty_state_heading')) - ->emptyStateDescription(fn () => CheerfulNotification::getByKey('theme.empty_state')) - ->defaultSort('created_at', 'desc') - ->deferFilters(false) - ->paginated([25, 50, 100, 'all']) - ->deferLoading(); + return ThemeTable::configure($table); } public static function getPages(): array diff --git a/app/Filament/Resources/Master/Users/Schemas/UserForm.php b/app/Filament/Resources/Master/Users/Schemas/UserForm.php new file mode 100644 index 0000000..7201484 --- /dev/null +++ b/app/Filament/Resources/Master/Users/Schemas/UserForm.php @@ -0,0 +1,67 @@ +components([ + TextInput::make('name') + ->label('Nama Lengkap') + ->placeholder('John Doe') + ->autocomplete(false) + ->autofocus() + ->required() + ->maxLength(100), + + Grid::make(1) + ->schema([ + TextInput::make('email') + ->label('Alamat Surel') + ->placeholder('johndoe@example.com') + ->autocomplete(false) + ->required() + ->maxLength(254) + ->email() + ->unique(ignoreRecord: true) + ->prefixIcon('heroicon-o-envelope'), + + TextInput::make('username') + ->label('Nama Pengguna') + ->placeholder('johndoe') + ->autocomplete(false) + ->required() + ->minLength(5) + ->maxLength(20) + ->regex('/^[a-zA-Z0-9_.-]+$/') + ->unique(ignoreRecord: true) + ->prefixIcon('heroicon-o-at-symbol'), + + Select::make('roles') + ->label('Peran') + ->required() + ->relationship('roles', 'name', function (Builder $query): Builder { + return $query->whereNotIn('name', [RoleEnum::DEVELOPER, RoleEnum::PERUSAHAAN]); + }) + ->multiple() + ->preload() + ->searchable(), + + Hidden::make('password') + ->default(fn (): string => config('auth.password_default')) + ->visibleOn('create'), + ]), + ]) + ->columns(1); + } +} diff --git a/app/Filament/Resources/Master/Users/Tables/UserTable.php b/app/Filament/Resources/Master/Users/Tables/UserTable.php new file mode 100644 index 0000000..ee46664 --- /dev/null +++ b/app/Filament/Resources/Master/Users/Tables/UserTable.php @@ -0,0 +1,125 @@ +columns([ + ...RowIndexColumn::make(), + + TextColumn::make('name') + ->label('Nama') + ->searchable() + ->sortable(), + + TextColumn::make('email') + ->label('Alamat Surel') + ->searchable() + ->sortable(), + + TextColumn::make('username') + ->label('Nama Pengguna') + ->searchable() + ->sortable(), + + ToggleColumn::make('is_active') + ->label('Status') + ->sortable() + ->getStateUsing(fn (User $record): bool => $record->is_active === IsActive::ACTIVE) + ->updateStateUsing(function (User $record, bool $state): void { + $record->is_active = $state ? IsActive::ACTIVE : IsActive::INACTIVE; + $record->save(); + + if ($record->is_active === IsActive::INACTIVE) { + DB::table('sessions') + ->where('user_id', $record->id) + ->delete(); + } + + CheerfulNotification::success( + $state ? CheerfulNotification::getByKey('user.activated') : CheerfulNotification::getByKey('user.deactivated'), + $state ? CheerfulNotification::getByKey('user.status_activated_success') : CheerfulNotification::getByKey('user.status_activated_success') + )->send(); + }) + ->disabled(fn (User $record): bool => $record->hasRole(RoleEnum::PERUSAHAAN->value)), + + TextColumn::make('roles.name') + ->label('Peran') + ->searchable() + ->badge() + ->getStateUsing(fn (User $record): array => $record->roles->pluck('name', 'id')->toArray()), + + ...TimestampColumns::make(), + ]) + ->filters([ + SelectFilter::make('is_active') + ->label('Status') + ->options(IsActive::class) + ->native(false), + + SelectFilter::make('roles') + ->label('Peran') + ->relationship('roles', 'name', function (Builder $query): Builder { + return $query->whereNotIn('name', [RoleEnum::DEVELOPER, RoleEnum::PERUSAHAAN]); + }) + ->multiple() + ->preload() + ->searchable() + ->native(false), + + TrashedFilter::make() + ->native(false) + ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), + ]) + ->recordActions([ + EditAction::make() + ->modalWidth(Width::Large), + + DeleteAction::make(), + + ForceDeleteAction::make(), + + RestoreAction::make(), + + ChangePasswordAction::make('changePassword'), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + ...DefaultBulkActions::make('Pengguna'), + ]), + ]) + ->emptyStateIcon(Heroicon::OutlinedUserGroup) + ->emptyStateHeading(fn () => CheerfulNotification::getByKey('user.empty_state_heading')) + ->emptyStateDescription(fn () => CheerfulNotification::getByKey('user.empty_state')) + ->defaultSort('created_at', 'desc') + ->deferFilters(false) + ->paginated([25, 50, 100, 'all']) + ->deferLoading(); + } +} diff --git a/app/Filament/Resources/Master/Users/UserResource.php b/app/Filament/Resources/Master/Users/UserResource.php index 38ae072..2fb7de2 100644 --- a/app/Filament/Resources/Master/Users/UserResource.php +++ b/app/Filament/Resources/Master/Users/UserResource.php @@ -2,37 +2,17 @@ namespace App\Filament\Resources\Master\Users; -use App\Enums\IsActive; -use App\Enums\RoleEnum; -use App\Filament\Actions\Cheerful\DeleteAction; -use App\Filament\Actions\Cheerful\EditAction; -use App\Filament\Actions\Cheerful\ForceDeleteAction; -use App\Filament\Actions\Cheerful\RestoreAction; -use App\Filament\Actions\DefaultBulkActions; -use App\Filament\Columns\RowIndexColumn; -use App\Filament\Columns\TimestampColumns; -use App\Filament\Resources\Master\Users\Actions\ChangePasswordAction; use App\Filament\Resources\Master\Users\Pages\ManageUsers; -use App\Filament\Support\CheerfulNotification; +use App\Filament\Resources\Master\Users\Schemas\UserForm; +use App\Filament\Resources\Master\Users\Tables\UserTable; use App\Models\User; use BackedEnum; -use Filament\Actions\BulkActionGroup; -use Filament\Forms\Components\Hidden; -use Filament\Forms\Components\Select; -use Filament\Forms\Components\TextInput; use Filament\Resources\Resource; -use Filament\Schemas\Components\Grid; use Filament\Schemas\Schema; -use Filament\Support\Enums\Width; use Filament\Support\Icons\Heroicon; -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Columns\ToggleColumn; -use Filament\Tables\Filters\SelectFilter; -use Filament\Tables\Filters\TrashedFilter; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; -use Illuminate\Support\Facades\DB; use UnitEnum; class UserResource extends Resource @@ -53,151 +33,12 @@ class UserResource extends Resource public static function form(Schema $schema): Schema { - return $schema - ->components([ - TextInput::make('name') - ->label('Nama Lengkap') - ->placeholder('John Doe') - ->autocomplete(false) - ->autofocus() - ->required() - ->maxLength(100), - - Grid::make(1) - ->schema([ - TextInput::make('email') - ->label('Alamat Surel') - ->placeholder('johndoe@example.com') - ->autocomplete(false) - ->required() - ->maxLength(254) - ->email() - ->unique(ignoreRecord: true) - ->prefixIcon('heroicon-o-envelope'), - - TextInput::make('username') - ->label('Nama Pengguna') - ->placeholder('johndoe') - ->autocomplete(false) - ->required() - ->minLength(5) - ->maxLength(20) - ->regex('/^[a-zA-Z0-9_.-]+$/') - ->unique(ignoreRecord: true) - ->prefixIcon('heroicon-o-at-symbol'), - - Select::make('roles') - ->label('Peran') - ->required() - ->relationship('roles', 'name', function (Builder $query): Builder { - return $query->whereNotIn('name', [RoleEnum::DEVELOPER, RoleEnum::PERUSAHAAN]); - }) - ->multiple() - ->preload() - ->searchable(), - - Hidden::make('password') - ->default(fn (): string => config('auth.password_default')) - ->visibleOn('create'), - ]), - ]) - ->columns(1); + return UserForm::configure($schema); } public static function table(Table $table): Table { - return $table - ->columns([ - ...RowIndexColumn::make(), - - TextColumn::make('name') - ->label('Nama') - ->searchable() - ->sortable(), - - TextColumn::make('email') - ->label('Alamat Surel') - ->searchable() - ->sortable(), - - TextColumn::make('username') - ->label('Nama Pengguna') - ->searchable() - ->sortable(), - - ToggleColumn::make('is_active') - ->label('Status') - ->sortable() - ->getStateUsing(fn (User $record): bool => $record->is_active === IsActive::ACTIVE) - ->updateStateUsing(function (User $record, bool $state): void { - $record->is_active = $state ? IsActive::ACTIVE : IsActive::INACTIVE; - $record->save(); - - if ($record->is_active === IsActive::INACTIVE) { - DB::table('sessions') - ->where('user_id', $record->id) - ->delete(); - } - - CheerfulNotification::success( - $state ? CheerfulNotification::getByKey('user.activated') : CheerfulNotification::getByKey('user.deactivated'), - $state ? CheerfulNotification::getByKey('user.status_activated_success') : CheerfulNotification::getByKey('user.status_activated_success') - )->send(); - }) - ->disabled(fn (User $record): bool => $record->hasRole(RoleEnum::PERUSAHAAN->value)), - - TextColumn::make('roles.name') - ->label('Peran') - ->searchable() - ->badge() - ->getStateUsing(fn (User $record): array => $record->roles->pluck('name', 'id')->toArray()), - - ...TimestampColumns::make(), - ]) - ->filters([ - SelectFilter::make('is_active') - ->label('Status') - ->options(IsActive::class) - ->native(false), - - SelectFilter::make('roles') - ->label('Peran') - ->relationship('roles', 'name', function (Builder $query): Builder { - return $query->whereNotIn('name', [RoleEnum::DEVELOPER, RoleEnum::PERUSAHAAN]); - }) - ->multiple() - ->preload() - ->searchable() - ->native(false), - - TrashedFilter::make() - ->native(false) - ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), - ]) - ->recordActions([ - EditAction::make() - ->modalWidth(Width::Large), - - DeleteAction::make(), - - ForceDeleteAction::make(), - - RestoreAction::make(), - - ChangePasswordAction::make('changePassword'), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - ...DefaultBulkActions::make('Pengguna'), - ]), - ]) - ->emptyStateIcon(Heroicon::OutlinedUserGroup) - ->emptyStateHeading(fn () => CheerfulNotification::getByKey('user.empty_state_heading')) - ->emptyStateDescription(fn () => CheerfulNotification::getByKey('user.empty_state')) - ->defaultSort('created_at', 'desc') - ->deferFilters(false) - ->paginated([25, 50, 100, 'all']) - ->deferLoading(); + return UserTable::configure($table); } public static function getPages(): array diff --git a/app/Filament/Resources/Publication/Announcements/AnnouncementResource.php b/app/Filament/Resources/Publication/Announcements/AnnouncementResource.php index 125495c..e296b4a 100644 --- a/app/Filament/Resources/Publication/Announcements/AnnouncementResource.php +++ b/app/Filament/Resources/Publication/Announcements/AnnouncementResource.php @@ -2,33 +2,14 @@ namespace App\Filament\Resources\Publication\Announcements; -use App\Enums\AnnouncementType; -use App\Enums\RoleEnum; -use App\Filament\Actions\Cheerful\DeleteAction; -use App\Filament\Actions\Cheerful\EditAction; -use App\Filament\Actions\Cheerful\ForceDeleteAction; -use App\Filament\Actions\Cheerful\RestoreAction; -use App\Filament\Actions\DefaultBulkActions; -use App\Filament\Columns\RowIndexColumn; -use App\Filament\Columns\TimestampColumns; use App\Filament\Resources\Publication\Announcements\Pages\ManageAnnouncements; -use App\Filament\Support\CheerfulNotification; +use App\Filament\Resources\Publication\Announcements\Schemas\AnnouncementForm; +use App\Filament\Resources\Publication\Announcements\Tables\AnnouncementTable; use App\Models\Announcement; use BackedEnum; -use Filament\Actions\Action; -use Filament\Actions\BulkActionGroup; -use Filament\Forms\Components\CheckboxList; -use Filament\Forms\Components\Radio; -use Filament\Forms\Components\RichEditor; -use Filament\Forms\Components\TextInput; use Filament\Resources\Resource; -use Filament\Schemas\Components\Utilities\Get; use Filament\Schemas\Schema; -use Filament\Support\Enums\Width; use Filament\Support\Icons\Heroicon; -use Filament\Tables\Columns\TextColumn; -use Filament\Tables\Filters\SelectFilter; -use Filament\Tables\Filters\TrashedFilter; use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\SoftDeletingScope; @@ -52,122 +33,12 @@ class AnnouncementResource extends Resource public static function form(Schema $schema): Schema { - return $schema - ->components([ - TextInput::make('title') - ->label('Judul Pengumuman') - ->placeholder('Pencairan Dana Kerja Sama Media') - ->autocomplete(false) - ->autofocus() - ->required() - ->columnSpanFull(), - - RichEditor::make('content') - ->label('Isi Pengumuman') - ->placeholder('Tuliskan detail pengumuman di sini...') - ->required() - ->columnSpanFull(), - - Radio::make('type') - ->label('Tipe Pengumuman') - ->options(AnnouncementType::class) - ->required() - ->default(AnnouncementType::PUBLIC->value) - ->live() - ->inline(), - - CheckboxList::make('partnerMedia') - ->label('Penerima Khusus') - ->relationship('partnerMedia', 'name', fn (Builder $query): Builder => $query->verified()) - ->visible(fn (Get $get): bool => in_array($get('type'), [AnnouncementType::COMPANY, AnnouncementType::COMPANY->value])) - ->required(fn (Get $get): bool => in_array($get('type'), [AnnouncementType::COMPANY, AnnouncementType::COMPANY->value])) - ->searchable() - ->noSearchResultsMessage('Tidak ada media yang ditemukan.') - ->helperText('Pilih media yang akan menerima pengumuman ini.') - ->columnSpanFull() - ->bulkToggleable() - ->selectAllAction( - fn (Action $action): Action => $action->label('Pilih Semua Media'), - ) - ->dehydrated(), - ]) - ->columns(1); + return AnnouncementForm::configure($schema); } public static function table(Table $table): Table { - return $table - ->columns([ - ...RowIndexColumn::make(), - - TextColumn::make('title') - ->label('Judul') - ->searchable() - ->sortable() - ->wrap(), - - TextColumn::make('content') - ->label('Konten') - ->searchable() - ->sortable() - ->wrap() - ->limit(100) - ->html() - ->toggleable(isToggledHiddenByDefault: true), - - TextColumn::make('type') - ->label('Tipe') - ->badge() - ->sortable(), - - TextColumn::make('partnerMedia') - ->label('Penerima') - ->getStateUsing( - fn ($record) => $record->partnerMedia->pluck('name')->toArray() - ) - ->listWithLineBreaks() - ->limitList(3) - ->expandableLimitedList() - ->badge() - ->placeholder( - fn (Announcement $record) => $record->type === AnnouncementType::PUBLIC ? 'Publik' : 'Tidak ada penerima' - ), - - ...TimestampColumns::make(), - ]) - ->filters([ - SelectFilter::make('type') - ->label('Tipe') - ->options(AnnouncementType::class) - ->native(false), - - TrashedFilter::make() - ->native(false) - ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), - ]) - ->recordActions([ - EditAction::make() - ->modalWidth(Width::FiveExtraLarge) - ->visible(fn (Announcement $record): bool => $record->type === AnnouncementType::PUBLIC), - - DeleteAction::make(), - - ForceDeleteAction::make(), - - RestoreAction::make(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - ...DefaultBulkActions::make('Pengumuman'), - ]), - ]) - ->emptyStateIcon(Heroicon::OutlinedMegaphone) - ->emptyStateHeading(fn () => CheerfulNotification::getByKey('announcement.empty_state_heading')) - ->emptyStateDescription(fn () => CheerfulNotification::getByKey('announcement.empty_state')) - ->defaultSort('created_at', 'desc') - ->deferFilters(false) - ->paginated([25, 50, 100, 'all']) - ->deferLoading(); + return AnnouncementTable::configure($table); } public static function getPages(): array diff --git a/app/Filament/Resources/Publication/Announcements/Schemas/AnnouncementForm.php b/app/Filament/Resources/Publication/Announcements/Schemas/AnnouncementForm.php new file mode 100644 index 0000000..28ca82e --- /dev/null +++ b/app/Filament/Resources/Publication/Announcements/Schemas/AnnouncementForm.php @@ -0,0 +1,60 @@ +components([ + TextInput::make('title') + ->label('Judul Pengumuman') + ->placeholder('Pencairan Dana Kerja Sama Media') + ->autocomplete(false) + ->autofocus() + ->required() + ->columnSpanFull(), + + RichEditor::make('content') + ->label('Isi Pengumuman') + ->placeholder('Tuliskan detail pengumuman di sini...') + ->required() + ->columnSpanFull(), + + Radio::make('type') + ->label('Tipe Pengumuman') + ->options(AnnouncementType::class) + ->required() + ->default(AnnouncementType::PUBLIC->value) + ->live() + ->inline(), + + CheckboxList::make('partnerMedia') + ->label('Penerima Khusus') + ->relationship('partnerMedia', 'name', fn (Builder $query): Builder => $query->verified()) + ->visible(fn (Get $get): bool => in_array($get('type'), [AnnouncementType::COMPANY, AnnouncementType::COMPANY->value])) + ->required(fn (Get $get): bool => in_array($get('type'), [AnnouncementType::COMPANY, AnnouncementType::COMPANY->value])) + ->searchable() + ->noSearchResultsMessage('Tidak ada media yang ditemukan.') + ->helperText('Pilih media yang akan menerima pengumuman ini.') + ->columnSpanFull() + ->bulkToggleable() + ->selectAllAction( + fn (Action $action): Action => $action->label('Pilih Semua Media'), + ) + ->dehydrated(), + ]) + ->columns(1); + } +} diff --git a/app/Filament/Resources/Publication/Announcements/Tables/AnnouncementTable.php b/app/Filament/Resources/Publication/Announcements/Tables/AnnouncementTable.php new file mode 100644 index 0000000..da75526 --- /dev/null +++ b/app/Filament/Resources/Publication/Announcements/Tables/AnnouncementTable.php @@ -0,0 +1,101 @@ +columns([ + ...RowIndexColumn::make(), + + TextColumn::make('title') + ->label('Judul') + ->searchable() + ->sortable() + ->wrap(), + + TextColumn::make('content') + ->label('Konten') + ->searchable() + ->sortable() + ->wrap() + ->limit(100) + ->html() + ->toggleable(isToggledHiddenByDefault: true), + + TextColumn::make('type') + ->label('Tipe') + ->badge() + ->sortable(), + + TextColumn::make('partnerMedia') + ->label('Penerima') + ->getStateUsing( + fn ($record) => $record->partnerMedia->pluck('name')->toArray() + ) + ->listWithLineBreaks() + ->limitList(3) + ->expandableLimitedList() + ->badge() + ->placeholder( + fn (Announcement $record) => $record->type === AnnouncementType::PUBLIC ? 'Publik' : 'Tidak ada penerima' + ), + + ...TimestampColumns::make(), + ]) + ->filters([ + SelectFilter::make('type') + ->label('Tipe') + ->options(AnnouncementType::class) + ->native(false), + + TrashedFilter::make() + ->native(false) + ->visible(fn () => auth()->user()?->hasRole(RoleEnum::DEVELOPER->value)), + ]) + ->recordActions([ + EditAction::make() + ->modalWidth(Width::FiveExtraLarge) + ->visible(fn (Announcement $record): bool => $record->type === AnnouncementType::PUBLIC), + + DeleteAction::make(), + + ForceDeleteAction::make(), + + RestoreAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + ...DefaultBulkActions::make('Pengumuman'), + ]), + ]) + ->emptyStateIcon(Heroicon::OutlinedMegaphone) + ->emptyStateHeading(fn () => CheerfulNotification::getByKey('announcement.empty_state_heading')) + ->emptyStateDescription(fn () => CheerfulNotification::getByKey('announcement.empty_state')) + ->defaultSort('created_at', 'desc') + ->deferFilters(false) + ->paginated([25, 50, 100, 'all']) + ->deferLoading(); + } +}