From f9f1e60b2eb60bb95db2318ef7a6e047167e5ce6 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Tue, 9 Dec 2025 15:36:50 +0700 Subject: [PATCH] feat(content recap): implement crud - create model and migration - relation to classification - create resource - create enum for support option --- app/Enums/ContentType.php | 23 +++++ app/Enums/SocialMedia.php | 22 +++++ .../ContentRecaps/ContentRecapResource.php | 69 ++++++++++++++ .../Pages/CreateContentRecap.php | 30 ++++++ .../ContentRecaps/Pages/EditContentRecap.php | 28 ++++++ .../ContentRecaps/Pages/ListContentRecaps.php | 22 +++++ .../Schemas/ContentRecapForm.php | 91 +++++++++++++++++++ .../Tables/ContentRecapsTable.php | 80 ++++++++++++++++ app/Models/Classification.php | 5 + app/Models/ContentRecap.php | 21 +++++ ..._09_113331_create_content_recaps_table.php | 38 ++++++++ 11 files changed, 429 insertions(+) create mode 100644 app/Enums/ContentType.php create mode 100644 app/Enums/SocialMedia.php create mode 100644 app/Filament/Resources/Monitoring/ContentRecaps/ContentRecapResource.php create mode 100644 app/Filament/Resources/Monitoring/ContentRecaps/Pages/CreateContentRecap.php create mode 100644 app/Filament/Resources/Monitoring/ContentRecaps/Pages/EditContentRecap.php create mode 100644 app/Filament/Resources/Monitoring/ContentRecaps/Pages/ListContentRecaps.php create mode 100644 app/Filament/Resources/Monitoring/ContentRecaps/Schemas/ContentRecapForm.php create mode 100644 app/Filament/Resources/Monitoring/ContentRecaps/Tables/ContentRecapsTable.php create mode 100644 app/Models/ContentRecap.php create mode 100644 database/migrations/2025_12_09_113331_create_content_recaps_table.php diff --git a/app/Enums/ContentType.php b/app/Enums/ContentType.php new file mode 100644 index 0000000..7ca952a --- /dev/null +++ b/app/Enums/ContentType.php @@ -0,0 +1,23 @@ + $status->value, self::cases()), + array_map(fn ($status) => $status->value, self::cases()) + ); + } +} diff --git a/app/Enums/SocialMedia.php b/app/Enums/SocialMedia.php new file mode 100644 index 0000000..2b81ff9 --- /dev/null +++ b/app/Enums/SocialMedia.php @@ -0,0 +1,22 @@ + $status->value, self::cases()), + array_map(fn ($status) => $status->value, self::cases()) + ); + } +} diff --git a/app/Filament/Resources/Monitoring/ContentRecaps/ContentRecapResource.php b/app/Filament/Resources/Monitoring/ContentRecaps/ContentRecapResource.php new file mode 100644 index 0000000..45f158e --- /dev/null +++ b/app/Filament/Resources/Monitoring/ContentRecaps/ContentRecapResource.php @@ -0,0 +1,69 @@ + ListContentRecaps::route('/'), + 'create' => CreateContentRecap::route('/create'), + 'edit' => EditContentRecap::route('/{record}/edit'), + ]; + } + + public static function getRecordRouteBindingEloquentQuery(): Builder + { + return parent::getRecordRouteBindingEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Filament/Resources/Monitoring/ContentRecaps/Pages/CreateContentRecap.php b/app/Filament/Resources/Monitoring/ContentRecaps/Pages/CreateContentRecap.php new file mode 100644 index 0000000..868bc4d --- /dev/null +++ b/app/Filament/Resources/Monitoring/ContentRecaps/Pages/CreateContentRecap.php @@ -0,0 +1,30 @@ +getResource()::getUrl('index'); + } + + protected function getHeaderActions(): array + { + return [ + Action::make('back') + ->label('Kembali') + ->url(fn (): string => route('filament.dashboard.resources.monitoring.content-recaps.index')) + ->outlined() + ->color('secondary'), + ]; + } +} diff --git a/app/Filament/Resources/Monitoring/ContentRecaps/Pages/EditContentRecap.php b/app/Filament/Resources/Monitoring/ContentRecaps/Pages/EditContentRecap.php new file mode 100644 index 0000000..3a7489d --- /dev/null +++ b/app/Filament/Resources/Monitoring/ContentRecaps/Pages/EditContentRecap.php @@ -0,0 +1,28 @@ +label('Kembali') + ->url(fn (): string => route('filament.dashboard.resources.monitoring.media-monitorings.index')) + ->outlined() + ->color('secondary'), + ]; + } + + protected function getRedirectUrl(): string + { + return $this->getResource()::getUrl('index'); + } +} diff --git a/app/Filament/Resources/Monitoring/ContentRecaps/Pages/ListContentRecaps.php b/app/Filament/Resources/Monitoring/ContentRecaps/Pages/ListContentRecaps.php new file mode 100644 index 0000000..3a7e8a9 --- /dev/null +++ b/app/Filament/Resources/Monitoring/ContentRecaps/Pages/ListContentRecaps.php @@ -0,0 +1,22 @@ +label('Tambah'), + ]; + } +} diff --git a/app/Filament/Resources/Monitoring/ContentRecaps/Schemas/ContentRecapForm.php b/app/Filament/Resources/Monitoring/ContentRecaps/Schemas/ContentRecapForm.php new file mode 100644 index 0000000..6f87923 --- /dev/null +++ b/app/Filament/Resources/Monitoring/ContentRecaps/Schemas/ContentRecapForm.php @@ -0,0 +1,91 @@ +components([ + Section::make([ + TextInput::make('title') + ->label('Judul') + ->placeholder('Reels Kerja Sama DPMPTSP') + ->autocomplete(false) + ->autofocus() + ->required() + ->maxLength(255), + + TextInput::make('link') + ->label('Tautan') + ->placeholder('https://www.pwknews.com') + ->autocomplete(false) + ->nullable() + ->maxLength(50) + ->url(), + + Grid::make(2) + ->schema([ + Select::make('type') + ->label('Tipe') + ->options(ContentType::options()) + ->native(false) + ->required() + ->in(implode(',', array_column(ContentType::cases(), 'value'))), + + Select::make('channel') + ->label('Kanal') + ->options(Channel::options()) + ->native(false) + ->required() + ->in(implode(',', array_column(Channel::cases(), 'value'))), + + Select::make('social_media') + ->label('Media Sosial') + ->options(SocialMedia::options()) + ->native(false) + ->required() + ->in(implode(',', array_column(SocialMedia::cases(), 'value'))), + + DatePicker::make('posting_date') + ->label('Tanggal Posting') + ->placeholder(fn () => now()->format('Y-m-d')) + ->native(false) + ->displayFormat('l, d F Y') + ->required(), + ]), + ])->columnSpan(2), + + Section::make([ + Select::make('classification_id') + ->label('Klasifikasi') + ->relationship('classification', 'name') + ->native(false) + ->preload() + ->required(), + + SpatieMediaLibraryFileUpload::make('image') + ->label('Gambar') + ->disk(config('filesystems.default')) + ->acceptedFileTypes(['images/*']) + ->maxSize(1024 * 3) + ->collection('content-recpaps') + ->image() + ->required(), + ]), + ]) + ->columns(3); + } +} diff --git a/app/Filament/Resources/Monitoring/ContentRecaps/Tables/ContentRecapsTable.php b/app/Filament/Resources/Monitoring/ContentRecaps/Tables/ContentRecapsTable.php new file mode 100644 index 0000000..ae76a68 --- /dev/null +++ b/app/Filament/Resources/Monitoring/ContentRecaps/Tables/ContentRecapsTable.php @@ -0,0 +1,80 @@ +columns([ + TextColumn::make('title') + ->label('Judul') + ->searchable() + ->sortable() + ->wrap(), + + TextColumn::make('type') + ->label('Tipe') + ->searchable() + ->sortable(), + + TextColumn::make('channel') + ->label('Kanal') + ->searchable() + ->sortable(), + + TextColumn::make('social_media') + ->label('Media Sosial') + ->searchable() + ->sortable(), + + TextColumn::make('classification.name') + ->label('Klasifikasi') + ->searchable() + ->sortable(), + + TextColumn::make('link') + ->label('Tautan') + ->searchable() + ->sortable(), + + TextColumn::make('posting_date') + ->label('Tgl Posting') + ->searchable() + ->sortable() + ->date('l, d F Y'), + + ...TimestampColumns::make(), + ]) + ->filters([ + TrashedFilter::make()->native(false), + ]) + ->recordActions([ + EditAction::make(), + DeleteAction::make(), + ForceDeleteAction::make(), + RestoreAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + ...DefaultBulkActions::make('Rekap Konten'), + ]), + ]) + ->emptyStateIcon('heroicon-o-bookmark') + ->emptyStateDescription('Setelah Anda membubat data pertama, maka akan muncul disini.') + ->defaultSort('created_at', 'desc') + ->deferFilters(false); + } +} diff --git a/app/Models/Classification.php b/app/Models/Classification.php index 4d4f431..b773bdb 100644 --- a/app/Models/Classification.php +++ b/app/Models/Classification.php @@ -25,4 +25,9 @@ public function subClassifications(): HasMany { return $this->hasMany(SubClassification::class); } + + public function contentRecaps(): HasMany + { + return $this->hasMany(ContentRecap::class); + } } diff --git a/app/Models/ContentRecap.php b/app/Models/ContentRecap.php new file mode 100644 index 0000000..c3aaaa1 --- /dev/null +++ b/app/Models/ContentRecap.php @@ -0,0 +1,21 @@ +belongsTo(Classification::class); + } +} diff --git a/database/migrations/2025_12_09_113331_create_content_recaps_table.php b/database/migrations/2025_12_09_113331_create_content_recaps_table.php new file mode 100644 index 0000000..160d0cf --- /dev/null +++ b/database/migrations/2025_12_09_113331_create_content_recaps_table.php @@ -0,0 +1,38 @@ +id(); + $table->foreignIdFor(Classification::class)->constrained(); + $table->string('title', 255); + $table->text('link', 50)->nullable(); + $table->date('posting_date'); + $table->enum('type', [ContentType::values()])->default(ContentType::FOTO); + $table->string('channel', 20); + $table->string('social_media', 50); + $table->timestamp('created_at')->useCurrent(); + $table->timestamp('updated_at')->nullable()->useCurrentOnUpdate(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('content_recaps'); + } +};