diff --git a/app/Filament/Resources/Manage/Journalists/JournalistResource.php b/app/Filament/Resources/Manage/Journalists/JournalistResource.php new file mode 100644 index 0000000..7e32f0d --- /dev/null +++ b/app/Filament/Resources/Manage/Journalists/JournalistResource.php @@ -0,0 +1,286 @@ + 'Dokumen Pers', + 'text_name' => 'press_card', + 'placeholder' => '*****', + 'max' => 100, + 'max_size' => 1024 * 10, + 'file_name' => 'press_card_docs', + 'accept' => ['application/pdf'], + 'folder' => 'journalists/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' => 'journalists/ukw-certificate/', + ], + ]; + + return $schema + ->components([ + TextInput::make('name') + ->label('Nama') + ->placeholder('John Doe') + ->autocomplete(false) + ->autofocus() + ->required() + ->maxLength(100), + + TextInput::make('email') + ->label('Alamat Surel') + ->placeholder('johndoe@example.com') + ->autocomplete(false) + ->autofocus() + ->required() + ->maxLength(254) + ->unique('journalists', 'email', ignoreRecord: true) + ->email(), + + TextInput::make('phone_number') + ->label('Nomor Telepon') + ->placeholder('08xx xxxx xxxx') + ->autocomplete(false) + ->required() + ->maxLength(20) + ->tel(), + + Group::make() + ->schema( + collect($documents) + ->map( + fn ($doc) => Section::make($doc['title']) + ->schema([ + TextInput::make($doc['text_name']) + ->hiddenLabel() + ->placeholder($doc['placeholder']) + ->autocomplete(false) + ->required() + ->maxLength($doc['max']), + + AdvancedFileUpload::make($doc['file_name']) + ->hiddenLabel() + ->disk(config('filesystems.default')) + ->acceptedFileTypes($doc['accept']) + ->maxSize($doc['max_size']) + ->required(), + ]) + ) + ->toArray() + ), + ]) + ->columns(1); + } + + public static function table(Table $table): Table + { + return $table + ->recordTitleAttribute('name') + ->columns([ + 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(), + + TextColumn::make('created_at') + ->label('Dibuat') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + + TextColumn::make('updated_at') + ->label('Diperbarui') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + + TextColumn::make('deleted_at') + ->label('Dihapus') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + TrashedFilter::make()->native(false), + ]) + ->recordActions([ + EditAction::make() + ->modalWidth('lg') + ->fillForm(function (Journalist $journalist) { + $data = [ + 'name' => $journalist->name, + 'email' => $journalist->email, + 'phone_number' => $journalist->phone_number, + 'press_card' => $journalist->press_card, + 'ukw_certificate' => $journalist->ukw_certificate, + ]; + + $documents = [ + 'press_card', + 'ukw_certificate', + ]; + + $mediaItems = $journalist->getMedia('journalists'); + + foreach ($documents as $docType) { + $media = $mediaItems + ->where('custom_properties.doc_type', str($docType)->replace('_docs', '')->slug('-')) + ->sortByDesc('created_at') + ->first(); + + if ($media) { + $data["{$docType}_docs"] = [$media->getPathRelativeToRoot()]; + } + } + + return $data; + }) + ->using(function (Model $record, array $data): Model { + $record->update([ + 'name' => $data['name'], + 'email' => $data['email'], + 'phone_number' => $data['phone_number'], + 'press_card' => $data['press_card'], + 'ukw_certificate' => $data['ukw_certificate'], + ]); + + $documents = [ + 'press_card_docs', + 'ukw_certificate_docs', + ]; + + foreach ($documents as $field) { + if (empty($data[$field])) { + continue; + } + + foreach ((array) $data[$field] as $filePath) { + $fullPath = Storage::disk(config('filesystems.default'))->path($filePath); + + if (! file_exists($fullPath)) { + continue; + } + + $record + ->addMediaFromDisk($filePath, config('filesystems.default')) + ->preservingOriginal() + ->withCustomProperties([ + 'feature' => 'journalists', + 'date' => now()->toDateString(), + 'doc_type' => str($field) + ->replace('_docs', '') + ->slug('-'), + ]) + ->toMediaCollection('journalists'); + } + } + + return $record; + }), + DeleteAction::make(), + ForceDeleteAction::make(), + RestoreAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ForceDeleteBulkAction::make(), + RestoreBulkAction::make(), + ]), + ]) + ->emptyStateIcon('heroicon-o-bookmark') + ->emptyStateDescription('Setelah Anda membubat data pertama, maka akan muncul disini.') + ->defaultSort('created_at', 'desc') + ->deferFilters(false); + } + + public static function getPages(): array + { + return [ + 'index' => ManageJournalists::route('/'), + ]; + } + + public static function getRecordRouteBindingEloquentQuery(): Builder + { + return parent::getRecordRouteBindingEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Filament/Resources/Manage/Journalists/Pages/ManageJournalists.php b/app/Filament/Resources/Manage/Journalists/Pages/ManageJournalists.php new file mode 100644 index 0000000..52835f0 --- /dev/null +++ b/app/Filament/Resources/Manage/Journalists/Pages/ManageJournalists.php @@ -0,0 +1,70 @@ +label('Tambah') + ->modalHeading('Tambah Jurnalis') + ->modalSubmitActionLabel('Simpan') + ->modalCancelActionLabel('Batal') + ->extraModalFooterActions(fn (CreateAction $action): array => [ + $action->makeModalSubmitAction('createAnother', arguments: ['another' => true]) + ->label('Simpan dan Tambah Lagi'), + ]) + ->modalWidth('lg') + ->using(function (array $data, string $model): Model { + $data['partner_media_id'] = auth()->user()->company?->partnerMedia?->id; + + $journalist = $model::create($data); + + $documents = [ + 'press_card_docs', + 'ukw_certificate_docs', + ]; + + foreach ($documents as $field) { + if (empty($this->data[$field])) { + continue; + } + + foreach ((array) $this->data[$field] as $filePath) { + $fullPath = Storage::disk(config('filesystems.default'))->path($filePath); + + if (! file_exists($fullPath)) { + continue; + } + + $journalist + ->addMediaFromDisk($filePath, config('filesystems.default')) + ->preservingOriginal() + ->withCustomProperties([ + 'feature' => 'journalists', + 'date' => now()->toDateString(), + 'doc_type' => str($field) + ->replace('_docs', '') + ->slug('-'), + ]) + ->toMediaCollection('journalists'); + } + } + + return $journalist; + }), + ]; + } +} diff --git a/app/Models/Journalist.php b/app/Models/Journalist.php new file mode 100644 index 0000000..e37a778 --- /dev/null +++ b/app/Models/Journalist.php @@ -0,0 +1,21 @@ +belongsTo(PartnerMedia::class); + } +} diff --git a/database/migrations/2025_11_20_041138_create_journalists_table.php b/database/migrations/2025_11_20_041138_create_journalists_table.php new file mode 100644 index 0000000..f1802d3 --- /dev/null +++ b/database/migrations/2025_11_20_041138_create_journalists_table.php @@ -0,0 +1,36 @@ +id(); + $table->foreignIdFor(PartnerMedia::class); + $table->string('name', 100); + $table->string('email', 254); + $table->string('phone_number', 20); + $table->string('press_card', 100)->nullable(); + $table->string('ukw_certificate', 100)->nullable(); + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->nullable()->useCurrentOnUpdate(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('journalists'); + } +};