feat(journalist): implement journalist crud and create migration and model

This commit is contained in:
Yoga Pangestu 2025-12-05 08:53:27 +07:00
parent a697511a99
commit 0341d452a0
4 changed files with 413 additions and 0 deletions

View File

@ -0,0 +1,286 @@
<?php
namespace App\Filament\Resources\Manage\Journalists;
use App\Filament\Resources\Manage\Journalists\Pages\ManageJournalists;
use App\Models\Journalist;
use Asmit\FilamentUpload\Forms\Components\AdvancedFileUpload;
use BackedEnum;
use Filament\Actions\BulkActionGroup;
use Filament\Actions\DeleteAction;
use Filament\Actions\DeleteBulkAction;
use Filament\Actions\EditAction;
use Filament\Actions\ForceDeleteAction;
use Filament\Actions\ForceDeleteBulkAction;
use Filament\Actions\RestoreAction;
use Filament\Actions\RestoreBulkAction;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Resource;
use Filament\Schemas\Components\Group;
use Filament\Schemas\Components\Section;
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\Model;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Illuminate\Support\Facades\Storage;
use UnitEnum;
class JournalistResource extends Resource
{
protected static ?string $model = Journalist::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::Identification;
protected static ?string $navigationLabel = 'Jurnalis';
protected static string|UnitEnum|null $navigationGroup = 'Kelola';
protected static ?string $slug = 'manage/journalists';
protected static ?string $recordTitleAttribute = 'name';
protected static ?int $navigationSort = 11;
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' => '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,
]);
}
}

View File

@ -0,0 +1,70 @@
<?php
namespace App\Filament\Resources\Manage\Journalists\Pages;
use App\Filament\Resources\Manage\Journalists\JournalistResource;
use Filament\Actions\CreateAction;
use Filament\Resources\Pages\ManageRecords;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
class ManageJournalists extends ManageRecords
{
protected static ?string $title = 'Jurnalis';
protected static string $resource = JournalistResource::class;
protected function getHeaderActions(): array
{
return [
CreateAction::make()
->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;
}),
];
}
}

21
app/Models/Journalist.php Normal file
View File

@ -0,0 +1,21 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
class Journalist extends Model implements HasMedia
{
use InteractsWithMedia, SoftDeletes;
protected $guarded = ['id'];
public function partnerMedia(): BelongsTo
{
return $this->belongsTo(PartnerMedia::class);
}
}

View File

@ -0,0 +1,36 @@
<?php
use App\Models\PartnerMedia;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('journalists', function (Blueprint $table) {
$table->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');
}
};