refactor: remove Category resource and related components for cleanup

This commit is contained in:
Yoga Pangestu 2026-04-06 14:59:32 +07:00
parent 70cf767111
commit 785e425dee
23 changed files with 12 additions and 820 deletions

View File

@ -1,46 +0,0 @@
<?php
namespace App\Filament\Resources\Master\Categories;
use App\Filament\Resources\Master\Categories\Pages\ManageCategories;
use App\Filament\Resources\Master\Categories\Schemas\CategoryForm;
use App\Filament\Resources\Master\Categories\Tables\CategoryTable;
use App\Models\Category;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
use UnitEnum;
class CategoryResource extends Resource
{
protected static ?string $model = Category::class;
protected static string|UnitEnum|null $navigationGroup = 'Master';
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedListBullet;
protected static ?string $navigationLabel = 'Kategori';
protected static ?int $navigationSort = 5;
protected static ?string $slug = 'master/categories';
public static function form(Schema $schema): Schema
{
return CategoryForm::configure($schema);
}
public static function table(Table $table): Table
{
return CategoryTable::configure($table);
}
public static function getPages(): array
{
return [
'index' => ManageCategories::route('/'),
];
}
}

View File

@ -1,33 +0,0 @@
<?php
namespace App\Filament\Resources\Master\Categories\Pages;
use App\Filament\Actions\Cheerful\CreateAction;
use App\Filament\Resources\Master\Categories\CategoryResource;
use App\Filament\Support\CheerfulNotification;
use Filament\Resources\Pages\ManageRecords;
use Filament\Support\Enums\Width;
class ManageCategories extends ManageRecords
{
protected static string $resource = CategoryResource::class;
protected static ?string $title = 'Kategori';
protected function getHeaderActions(): array
{
return [
CreateAction::make()
->label('Tambah')
->modalHeading(fn () => CheerfulNotification::getByKey('category.create_title'))
->modalDescription(fn () => CheerfulNotification::getByKey('category.create_desc'))
->modalSubmitActionLabel('Simpan')
->modalCancelActionLabel('Batal')
->extraModalFooterActions(fn (CreateAction $action): array => [
$action->makeModalSubmitAction('createAknother', arguments: ['another' => true])
->label('Simpan dan Tambah Lagi'),
])
->modalWidth(Width::Large),
];
}
}

View File

@ -1,24 +0,0 @@
<?php
namespace App\Filament\Resources\Master\Categories\Schemas;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Schema;
class CategoryForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
TextInput::make('name')
->label('Nama Kategori')
->placeholder('Ekonomi')
->autocomplete(false)
->autofocus()
->required()
->maxLength(50),
])
->columns(1);
}
}

View File

@ -1,84 +0,0 @@
<?php
namespace App\Filament\Resources\Master\Categories\Tables;
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\Support\CheerfulNotification;
use App\Models\Category;
use Filament\Actions\BulkActionGroup;
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;
class CategoryTable
{
public static function configure(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();
}
}

View File

@ -9,14 +9,12 @@
use Filament\Forms\Components\Hidden;
use Filament\Forms\Components\Radio;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\SpatieMediaLibraryFileUpload;
use Filament\Forms\Components\TagsInput;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Utilities\Set;
use Filament\Schemas\Schema;
use Illuminate\Database\Eloquent\Builder;
class NewsForm
{
@ -52,8 +50,8 @@ public static function configure(Schema $schema): Schema
->columnSpanFull(),
])->columnSpan(2),
Section::make(CheerfulNotification::getByKey('news.content.category_title'))
->description(CheerfulNotification::getByKey('news.content.category_desc'))
Section::make(CheerfulNotification::getByKey('news.content.publication_title'))
->description(CheerfulNotification::getByKey('news.content.publication_desc'))
->icon(CheerfulNotification::getNotifStyle() === NotifStyle::CHEERFUL ? 'heroicon-o-tag' : null)
->schema([
TextInput::make('link')
@ -64,16 +62,6 @@ public static function configure(Schema $schema): Schema
->maxLength(255)
->url(),
Select::make('categories')
->label('Kategori')
->relationship('categories', 'name', function (Builder $query): Builder {
return $query->active();
})
->native(false)
->multiple()
->preload()
->required(),
TagsInput::make('tags')
->label('Tag Berita')
->reactive()

View File

@ -42,14 +42,6 @@ public static function configure(Table $table): Table
->sortable()
->placeholder('Tidak ada'),
TextColumn::make('categories.name')
->label('Kategori')
->listWithLineBreaks()
->limitList(3)
->expandableLimitedList()
->badge()
->placeholder('Tidak ada kategori'),
TextColumn::make('tags.name')
->label('Tag')
->listWithLineBreaks()
@ -85,13 +77,6 @@ public static function configure(Table $table): Table
->options(NewsStatus::class)
->native(false),
SelectFilter::make('categories')
->label('Kategori')
->relationship('categories', 'name')
->multiple()
->preload()
->native(false),
SelectFilter::make('tags')
->label('Tag')
->relationship('tags', 'name')

View File

@ -2,7 +2,6 @@
namespace App\Livewire\Home\News;
use App\Models\Category;
use App\Models\News;
use Livewire\Attributes\Title;
use Livewire\Attributes\Url;
@ -18,9 +17,6 @@ class Index extends Component
#[Url(as: 'q')]
public $search = '';
#[Url(as: 'category')]
public $category = '';
#[Url(as: 'tag')]
public $tag = '';
@ -29,11 +25,6 @@ public function updatedSearch()
$this->resetPage();
}
public function updatedCategory()
{
$this->resetPage();
}
public function updatedTag()
{
$this->resetPage();
@ -42,16 +33,11 @@ public function updatedTag()
public function render()
{
$news = News::published()
->with(['categories', 'author'])
->with('author')
->when($this->search, function ($query) {
$query->where('title', 'like', '%'.$this->search.'%')
->orWhere('content', 'like', '%'.$this->search.'%');
})
->when($this->category, function ($query) {
$query->whereHas('categories', function ($query) {
$query->where('categories.id', $this->category);
});
})
->when($this->tag, function ($query) {
$query->withAnyTags([$this->tag]);
})
@ -67,13 +53,6 @@ public function render()
return $news;
});
$categories = Category::active()
->withCount(['news' => function ($query) {
$query->published();
}])
->orderBy('name')
->get();
$popularPosts = News::published()
->latest('views')
->limit(3)
@ -90,7 +69,6 @@ public function render()
'pageDescription' => 'Jelajahi berita terbaru seputar purwakarta.',
'news' => $news,
'popularPosts' => $popularPosts,
'categories' => $categories,
'tags' => Tag::all(),
]);
}

View File

@ -24,7 +24,7 @@ public function mount(News $news)
#[Title('Detail Berita')]
public function render()
{
$this->news->load(['categories', 'author', 'tags']);
$this->news->load(['author', 'tags']);
// Format main news
$this->news->thumbnail = $this->news->getFirstMediaUrl('news') ?: 'https://images.pexels.com/photos/12199409/pexels-photo-12199409.jpeg';
@ -35,26 +35,14 @@ public function render()
$relatedNews = News::published()
->where('id', '!=', $this->news->id)
->whereHas('categories', function ($query) {
$query->whereIn('categories.id', $this->news->categories->pluck('id'));
})
->latest('published_at')
->limit(4)
->get();
if ($relatedNews->isEmpty()) {
$relatedNews = News::published()
->where('id', '!=', $this->news->id)
->latest('published_at')
->limit(4)
->get();
}
// Format related news
$relatedNews = $relatedNews->map(function ($news) {
$news->thumbnail = $news->getFirstMediaUrl('news') ?: 'https://images.pexels.com/photos/12199409/pexels-photo-12199409.jpeg';
$news->formatted_date = $news->published_at?->translatedFormat('d F Y') ?: '-';
$news->primary_category = $news->categories->first();
return $news;
});

View File

@ -1,43 +0,0 @@
<?php
namespace App\Models;
use App\Enums\IsActive;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Swindon\FilamentHashids\Traits\HasHashid;
class Category extends Model
{
use HasFactory, HasHashid, SoftDeletes;
protected $guarded = ['id'];
protected function casts(): array
{
return [
'is_active' => IsActive::class,
];
}
#[Scope]
protected function active(Builder $query): void
{
$query->where('is_active', IsActive::ACTIVE);
}
#[Scope]
protected function inactive(Builder $query): void
{
$query->where('is_active', IsActive::INACTIVE);
}
public function news(): BelongsToMany
{
return $this->belongsToMany(News::class);
}
}

View File

@ -1,25 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Swindon\FilamentHashids\Traits\HasHashid;
class CategoryNews extends Model
{
use HasFactory, HasHashid;
protected $guarded = ['id'];
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
public function news(): BelongsTo
{
return $this->belongsTo(News::class);
}
}

View File

@ -8,7 +8,6 @@
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
@ -61,9 +60,4 @@ public function author(): BelongsTo
{
return $this->belongsTo(User::class, 'author_id');
}
public function categories(): BelongsToMany
{
return $this->belongsToMany(Category::class);
}
}

View File

@ -1,69 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Policies;
use App\Models\Category;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Foundation\Auth\User as AuthUser;
class CategoryPolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:Category');
}
public function view(AuthUser $authUser, Category $category): bool
{
return $authUser->can('View:Category');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:Category');
}
public function update(AuthUser $authUser, Category $category): bool
{
return $authUser->can('Update:Category');
}
public function delete(AuthUser $authUser, Category $category): bool
{
return $authUser->can('Delete:Category');
}
public function restore(AuthUser $authUser, Category $category): bool
{
return $authUser->can('Restore:Category');
}
public function forceDelete(AuthUser $authUser, Category $category): bool
{
return $authUser->can('ForceDelete:Category');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:Category');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:Category');
}
public function replicate(AuthUser $authUser, Category $category): bool
{
return $authUser->can('Replicate:Category');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:Category');
}
}

View File

@ -1,26 +0,0 @@
<?php
namespace Database\Factories;
use App\Enums\IsActive;
use App\Models\Category;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<Category>
*/
class CategoryFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->unique()->word(),
'is_active' => IsActive::ACTIVE,
];
}
}

View File

@ -1,27 +0,0 @@
<?php
namespace Database\Factories;
use App\Models\Category;
use App\Models\CategoryNews;
use App\Models\News;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends Factory<CategoryNews>
*/
class CategoryNewsFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'category_id' => Category::factory(),
'news_id' => News::factory(),
];
}
}

View File

@ -1,32 +0,0 @@
<?php
use App\Enums\IsActive;
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('categories', function (Blueprint $table) {
$table->id();
$table->string('name', 50);
$table->enum('is_active', IsActive::values())->default(IsActive::ACTIVE->value)->comment(IsActive::comment());
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('categories');
}
};

View File

@ -1,33 +0,0 @@
<?php
use App\Models\Category;
use App\Models\News;
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('category_news', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(Category::class);
$table->foreignIdFor(News::class);
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('category_news');
}
};

View File

@ -36,17 +36,6 @@ public function run(): void
"View:BudgetRealizationChart",
"ViewAny:Category",
"View:Category",
"Create:Category",
"Update:Category",
"Delete:Category",
"Restore:Category",
"ForceDelete:Category",
"ForceDeleteAny:Category",
"RestoreAny:Category",
"Replicate:Category",
"Reorder:Category",
"View:CompanyStatusChart",
@ -307,17 +296,6 @@ public function run(): void
"View:BudgetRealizationChart",
"ViewAny:Category",
"View:Category",
"Create:Category",
"Update:Category",
"Delete:Category",
"Restore:Category",
"ForceDelete:Category",
"ForceDeleteAny:Category",
"RestoreAny:Category",
"Replicate:Category",
"Reorder:Category",
"View:CompanyStatusChart",
@ -559,7 +537,7 @@ public function run(): void
"RestoreAny:Classification",
"Replicate:Classification",
"Reorder:Classification",
"View:Dashboard",
"ViewAny:IssueManagement",
@ -573,7 +551,7 @@ public function run(): void
"RestoreAny:IssueManagement",
"Replicate:IssueManagement",
"Reorder:IssueManagement",
"ViewAny:Location",
"View:Location",
"Create:Location",
@ -653,19 +631,6 @@ public function run(): void
"permissions": [
"View:Analytic",
"ViewAny:Category",
"View:Category",
"Create:Category",
"Update:Category",
"Delete:Category",
"Restore:Category",
"ForceDelete:Category",
"ForceDeleteAny:Category",
"RestoreAny:Category",
"Replicate:Category",
"Reorder:Category",
"ViewAny:Classification",
"View:Classification",
"Create:Classification",

View File

@ -787,14 +787,6 @@
'cheerful' => 'Masukkan informasi lengkap mengenai pemberitaan media biar makin lengkap! 📰✨',
'formal' => 'Masukkan informasi lengkap mengenai pemberitaan media.',
],
'category_title' => [
'cheerful' => 'Kategori & Metadata 🏷️',
'formal' => 'Kategori & Metadata',
],
'category_desc' => [
'cheerful' => 'Tentukan kategori berita dan informasi tambahan lainnya biar makin rapi! 🏷️✨',
'formal' => 'Tentukan kategori berita dan informasi tambahan lainnya.',
],
'empty_state' => [
'cheerful' => 'Belum ada data Monitoring Media nih! Mulai buat data pertama sekarang biar makin rame! 🚀✨',
'formal' => 'Belum ada data Monitoring Media yang tersedia saat ini.',
@ -812,13 +804,13 @@
'cheerful' => 'Yuk, tuliskan judul, ringkasan, dan isi lengkap berita Anda biar makin menarik! 🚀😊',
'formal' => 'Silakan tuliskan judul, ringkasan, dan isi lengkap berita Anda.',
],
'category_title' => [
'cheerful' => 'Kategori & Publikasi 🏷️',
'formal' => 'Kategori & Publikasi',
'publication_title' => [
'cheerful' => 'Publikasi & Metadata 🏷️',
'formal' => 'Publikasi & Metadata',
],
'category_desc' => [
'cheerful' => 'Tentukan kategori, tautan media, dan status publikasi biar berita kita gampang dicari! 🏷️✨',
'formal' => 'Tentukan kategori, tautan media, dan status publikasi.',
'publication_desc' => [
'cheerful' => 'Tentukan tautan media, tag, dan status publikasi agar berita lebih mudah ditemukan! 🏷️✨',
'formal' => 'Tentukan tautan media, tag, dan status publikasi.',
],
'empty_state' => [
'cheerful' => 'Belum ada data Berita nih! Mulai buat data pertama sekarang biar makin rame! 🚀✨',
@ -916,24 +908,6 @@
'formal' => 'Tidak Ada Data Tema',
],
],
'category' => [
'create_title' => [
'cheerful' => 'Tambah Kategori Baru 🏷️✨',
'formal' => 'Tambah Kategori',
],
'create_desc' => [
'cheerful' => 'Tambahkan kategori baru biar data makin teratur dan rapi! 📁😊',
'formal' => 'Silakan masukkan detail kategori data di bawah ini.',
],
'empty_state' => [
'cheerful' => 'Belum ada data Kategori nih! Mulai buat data pertama sekarang biar makin rame! 🚀✨',
'formal' => 'Belum ada data Kategori yang tersedia saat ini.',
],
'empty_state_heading' => [
'cheerful' => 'Belum Ada Data Kategori! 🏷️✨',
'formal' => 'Tidak Ada Data Kategori',
],
],
'classification' => [
'create_title' => [
'cheerful' => 'Tambah Klasifikasi Baru 📂✨',

View File

@ -41,13 +41,6 @@ class="bi bi-megaphone text-white icon-small me-10px"></i>{{ $pageTitle }}</span
<img src="{{ $item->thumbnail }}" alt="{{ $item->title }}"
class="w-100" />
</a>
<div class="blog-categories">
@foreach ($item->categories as $itemCategory)
<a href="javascript:void(0)"
wire:click="$set('category', '{{ $itemCategory->id }}')"
class="categories-btn bg-white text-dark-gray text-dark-gray-hover text-uppercase alt-font fw-600">{{ $itemCategory->name }}</a>
@endforeach
</div>
</div>
@endif
<div class="card-body p-9 bg-white">
@ -121,28 +114,6 @@ class="d-inline-block fs-15">{{ $post->formatted_date }}</a>
</ul>
</div>
<div class="mb-15 md-mb-50px xs-mb-35px">
<div
class="fw-600 fs-19 lh-22 ls-minus-05px text-dark-gray border-bottom border-color-dark-gray border-2 d-block mb-30px pb-15px position-relative">
Kategori</div>
<ul class="category-list-sidebar position-relative list-unstyled">
<li class="mb-10px">
<a href="javascript:void(0)" wire:click="$set('category', '')"
class="d-flex align-items-center {{ $this->category == '' ? 'text-base-color fw-600' : 'text-dark-gray' }}">
<span>Semua Kategori</span>
</a>
</li>
@foreach ($categories as $cat)
<li class="mb-10px border-bottom border-color-extra-light-gray pb-10px">
<a href="javascript:void(0)" wire:click="$set('category', '{{ $cat->id }}')"
class="d-flex align-items-center justify-content-between {{ $this->category == $cat->id ? 'text-base-color fw-600' : 'text-dark-gray' }}">
<span>{{ $cat->name }}</span>
<span class="fs-14 text-medium-gray">({{ $cat->news_count }})</span>
</a>
</li>
@endforeach
</ul>
</div>
@if ($tags->count() > 0)
<div class="mb-15 md-mb-50px xs-mb-35px">

View File

@ -33,10 +33,6 @@ class="bi bi-megaphone text-white icon-small me-10px"></i>{{ $pageTitle }}</span
<div class="row justify-content-center">
<div class="col-lg-10 overlap-section text-center">
<div class="p-10 box-shadow-extra-large border-radius-4px bg-white text-center">
@foreach ($news->categories as $category)
<a href="{{ route('news.index', ['category' => $category->id]) }}"
class="bg-solitude-blue text-uppercase fs-13 ps-25px pe-25px alt-font fw-500 text-base-color lh-40 sm-lh-55 border-radius-100px d-inline-block mb-3 sm-mb-15px">{{ $category->name }}</a>
@endforeach
<h3 class="alt-font text-dark-gray fw-600 ls-minus-1px mb-15px">{{ $news->title }}</h3>
<div class="lg-20px sm-mb-0">
<span>{{ $news->author_name }}</span>
@ -122,10 +118,6 @@ class="w-100" /></a>
</div>
<div class="card-body px-0 pb-30px pt-30px xs-pb-15px last-paragraph-no-margin">
<span class="fs-13 text-uppercase mb-5px d-block">
@if ($related->primary_category)
<a href="{{ route('news.index', ['category' => $related->primary_category->id]) }}"
class="text-dark-gray fw-500 categories-text">{{ $related->primary_category->name }}</a>
@endif
<a href="javascript:void(0)"
class="blog-date">{{ $related->formatted_date }}</a>
</span>

View File

@ -1,167 +0,0 @@
<?php
use App\Enums\IsActive;
use App\Enums\RoleEnum;
use App\Filament\Resources\Master\Categories\Pages\ManageCategories;
use App\Models\Category;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
uses(RefreshDatabase::class);
beforeEach(function () {
$this->user = User::factory()->create(['is_active' => IsActive::ACTIVE]);
// Setup Permissions
$permissions = [
'ViewAny:Category',
'Create:Category',
'Update:Category',
'Delete:Category',
'Restore:Category',
'ForceDelete:Category',
];
foreach ($permissions as $permission) {
Permission::firstOrCreate(['name' => $permission, 'guard_name' => 'web']);
}
$this->user->givePermissionTo($permissions);
});
test('can render category list page', function () {
$this->actingAs($this->user);
Livewire::test(ManageCategories::class)
->assertSuccessful();
});
test('can list categories', function () {
$categories = Category::factory()->count(5)->create();
$this->actingAs($this->user);
Livewire::test(ManageCategories::class)
->call('loadTable')
->assertCanSeeTableRecords($categories);
});
test('can create category', function () {
$this->actingAs($this->user);
Livewire::test(ManageCategories::class)
->mountAction('create')
->setActionData([
'name' => 'Kategori Baru',
])
->callMountedAction()
->assertHasNoActionErrors();
$this->assertDatabaseHas('categories', [
'name' => 'Kategori Baru',
'is_active' => IsActive::ACTIVE->value,
]);
});
test('cannot create category with empty name', function () {
$this->actingAs($this->user);
Livewire::test(ManageCategories::class)
->mountAction('create')
->setActionData(['name' => ''])
->callMountedAction()
->assertHasActionErrors(['name' => 'required']);
});
test('cannot create category with name exceeding 50 characters', function () {
$this->actingAs($this->user);
Livewire::test(ManageCategories::class)
->mountAction('create')
->setActionData(['name' => str_repeat('a', 51)])
->callMountedAction()
->assertHasActionErrors(['name' => 'max']);
});
test('can edit category', function () {
$category = Category::factory()->create();
$this->actingAs($this->user);
Livewire::test(ManageCategories::class)
->mountTableAction('edit', $category)
->setActionData([
'name' => 'Kategori Updated',
])
->callMountedTableAction()
->assertHasNoActionErrors();
expect($category->refresh()->name)->toBe('Kategori Updated');
});
test('can toggle is_active status', function () {
$category = Category::factory()->create(['is_active' => IsActive::INACTIVE]);
$this->actingAs($this->user);
Livewire::test(ManageCategories::class)
->call('updateTableColumnState', 'is_active', $category->getKey(), true);
expect($category->refresh()->is_active)->toBe(IsActive::ACTIVE);
});
test('unauthorized user cannot render category page', function () {
$unauthorizedUser = User::factory()->create(['is_active' => IsActive::ACTIVE]);
$this->actingAs($unauthorizedUser);
Livewire::test(ManageCategories::class)
->assertForbidden();
});
test('can delete category', function () {
$category = Category::factory()->create();
$this->actingAs($this->user);
Livewire::test(ManageCategories::class)
->callTableAction('delete', $category);
$this->assertSoftDeleted($category);
});
test('can restore deleted category', function () {
$roleDev = Role::firstOrCreate(['name' => RoleEnum::DEVELOPER->value, 'guard_name' => 'web']);
$this->user->assignRole($roleDev);
$category = Category::factory()->create(['deleted_at' => now()]);
$this->actingAs($this->user);
Livewire::test(ManageCategories::class)
->filterTable('trashed', 'with')
->callTableAction('restore', $category);
$this->assertDatabaseHas('categories', [
'id' => $category->id,
'deleted_at' => null,
]);
});
test('can force delete category', function () {
$roleDev = Role::firstOrCreate(['name' => RoleEnum::DEVELOPER->value, 'guard_name' => 'web']);
$this->user->assignRole($roleDev);
$category = Category::factory()->create(['deleted_at' => now()]);
$this->actingAs($this->user);
Livewire::test(ManageCategories::class)
->filterTable('trashed', 'only')
->callTableAction('forceDelete', $category);
$this->assertDatabaseMissing('categories', ['id' => $category->id]);
});

View File

@ -1,21 +0,0 @@
<?php
use App\Models\Category;
use App\Models\CategoryNews;
use App\Models\News;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
test('category news relation works', function () {
$news = News::factory()->create();
$category = Category::factory()->create();
$categoryNews = CategoryNews::factory()->create([
'news_id' => $news->id,
'category_id' => $category->id,
]);
expect($categoryNews->news->id)->toBe($news->id);
expect($categoryNews->category->id)->toBe($category->id);
});

View File

@ -1,13 +0,0 @@
<?php
use App\Enums\IsActive;
use App\Models\Category;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
test('category can be created using factory', function () {
$category = Category::factory()->create(['name' => 'Politik']);
expect($category->name)->toBe('Politik');
expect($category->is_active)->toBe(IsActive::ACTIVE);
});