Skip to content

Commit 4453f98

Browse files
feat:[LAR-34] Add filament tags crud in cpanel with feature test
1 parent 4d67906 commit 4453f98

File tree

6 files changed

+250
-1
lines changed

6 files changed

+250
-1
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
npm-debug.log
55
yarn-error.log
66
yarn.lock
7-
7+
package-lock.json
88
/vendor
99
composer.phar
1010

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Filament\Resources;
6+
7+
use App\Filament\Resources\TagResource\Pages;
8+
use App\Models\Tag;
9+
use Filament\Forms;
10+
use Filament\Forms\Components\Select;
11+
use Filament\Forms\Form;
12+
use Filament\Forms\Get;
13+
use Filament\Forms\Set;
14+
use Filament\Resources\Resource;
15+
use Filament\Tables;
16+
use Filament\Tables\Table;
17+
use Illuminate\Support\Str;
18+
19+
final class TagResource extends Resource
20+
{
21+
protected static ?string $model = Tag::class;
22+
23+
protected static ?string $navigationIcon = 'heroicon-o-tag';
24+
25+
public static function form(Form $form): Form
26+
{
27+
return $form
28+
->schema([
29+
Forms\Components\TextInput::make('name')
30+
->required()
31+
->live(onBlur: true)
32+
->afterStateUpdated(function (Get $get, Set $set, ?string $old, ?string $state): void {
33+
if (($get('slug') ?? '') !== Str::slug($old)) {
34+
return;
35+
}
36+
$set('slug', Str::slug($state));
37+
}),
38+
Forms\Components\TextInput::make('slug')
39+
->required(),
40+
Select::make('concerns')
41+
->multiple()
42+
->options([
43+
'post' => 'Post',
44+
'tutorial' => 'Tutorial',
45+
'discussion' => 'Discussion',
46+
])
47+
->required()
48+
->columnSpanFull(),
49+
Forms\Components\Textarea::make('description')
50+
->columnSpanFull(),
51+
]);
52+
}
53+
54+
public static function table(Table $table): Table
55+
{
56+
return $table
57+
->columns([
58+
Tables\Columns\TextColumn::make('name')
59+
->searchable(),
60+
Tables\Columns\TextColumn::make('slug')
61+
->searchable(),
62+
Tables\Columns\TextColumn::make(name: 'concerns'),
63+
])
64+
->filters([
65+
//
66+
])
67+
->actions([
68+
\Filament\Tables\Actions\ActionGroup::make([
69+
Tables\Actions\DeleteAction::make(),
70+
Tables\Actions\EditAction::make()
71+
->color('warning'),
72+
]),
73+
])
74+
->bulkActions([
75+
Tables\Actions\BulkActionGroup::make([
76+
Tables\Actions\DeleteBulkAction::make(),
77+
]),
78+
]);
79+
}
80+
81+
public static function getPages(): array
82+
{
83+
return [
84+
'index' => Pages\ListTags::route('/'),
85+
'create' => Pages\CreateTag::route('/create'),
86+
'edit' => Pages\EditTag::route('/{record}/edit'),
87+
];
88+
}
89+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Filament\Resources\TagResource\Pages;
6+
7+
use App\Filament\Resources\TagResource;
8+
use Filament\Resources\Pages\CreateRecord;
9+
10+
final class CreateTag extends CreateRecord
11+
{
12+
protected static string $resource = TagResource::class;
13+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Filament\Resources\TagResource\Pages;
6+
7+
use App\Filament\Resources\TagResource;
8+
use Filament\Actions;
9+
use Filament\Resources\Pages\EditRecord;
10+
11+
final class EditTag extends EditRecord
12+
{
13+
protected static string $resource = TagResource::class;
14+
15+
protected function getHeaderActions(): array
16+
{
17+
return [
18+
Actions\DeleteAction::make(),
19+
];
20+
}
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Filament\Resources\TagResource\Pages;
6+
7+
use App\Filament\Resources\TagResource;
8+
use Filament\Actions;
9+
use Filament\Resources\Pages\ListRecords;
10+
11+
final class ListTags extends ListRecords
12+
{
13+
protected static string $resource = TagResource::class;
14+
15+
protected function getHeaderActions(): array
16+
{
17+
return [
18+
Actions\CreateAction::make(),
19+
];
20+
}
21+
}

tests/Feature/Filament/TagTest.php

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use App\Filament\Resources\TagResource;
6+
use App\Filament\Resources\TagResource\Pages\CreateTag;
7+
use App\Filament\Resources\TagResource\Pages\ListTags;
8+
use App\Models\Tag;
9+
use Filament\Actions\EditAction;
10+
use Illuminate\Database\Eloquent\Factories\Sequence;
11+
use Livewire\Livewire;
12+
13+
beforeEach(function (): void {
14+
$this->user = $this->login();
15+
$this->tags = Tag::factory()
16+
->count(10)
17+
->state(new Sequence(
18+
['concerns' => ['post', 'tutorial']],
19+
['concerns' => ['post']],
20+
))
21+
->create();
22+
});
23+
24+
describe(TagResource::class, function (): void {
25+
26+
it('page can display table with records', function (): void {
27+
Livewire::test(ListTags::class)
28+
->assertCanSeeTableRecords($this->tags);
29+
});
30+
31+
it('can automatically generate a slug from the title', function (): void {
32+
$name = fake()->name();
33+
34+
Livewire::test(CreateTag::class)
35+
->fillForm([
36+
'name' => $name,
37+
])
38+
->assertFormSet([
39+
'slug' => Str::slug($name),
40+
]);
41+
});
42+
43+
it('can validate input is value is null or empty', function (): void {
44+
$name = fake()->name();
45+
46+
Livewire::test(CreateTag::class)
47+
->fillForm([
48+
'name' => null,
49+
'concerns' => [],
50+
'description' => 'Description du tag '.$name,
51+
])
52+
->call('create')
53+
->assertHasFormErrors(['name' => 'required', 'concerns' => 'required']);
54+
});
55+
56+
it('Admin user can create tag', function (): void {
57+
$name = fake()->name();
58+
59+
Livewire::test(CreateTag::class)
60+
->fillForm([
61+
'name' => $name,
62+
'concerns' => ['post', 'tutorial'],
63+
'description' => 'Description du tag '.$name,
64+
])
65+
->call('create');
66+
});
67+
68+
it('Generate tag if tag already exist', function (): void {
69+
70+
$name = fake()->name();
71+
Tag::factory()->create([
72+
'name' => $name,
73+
'slug' => Str::slug($name),
74+
'concerns' => ['discussion'],
75+
]);
76+
77+
Livewire::test(CreateTag::class)
78+
->fillForm([
79+
'name' => $name,
80+
'concerns' => ['post', 'tutorial'],
81+
'description' => 'Description du tag '.$name,
82+
])
83+
->call('create');
84+
85+
expect(Tag::orderByDesc('id')->first()->slug)
86+
->toBe(Str::slug($name).'-1');
87+
88+
});
89+
90+
it('Admin user can edit tag', function (): void {
91+
$tag = Tag::factory()->create();
92+
93+
Livewire::test(ListTags::class)
94+
->callTableAction(EditAction::class, $tag, data: [
95+
'name' => 'Edited tag',
96+
])
97+
->assertHasNoTableActionErrors();
98+
99+
$tag->refresh();
100+
101+
expect($tag->name)
102+
->toBe('Edited tag');
103+
});
104+
105+
})->group('tags');

0 commit comments

Comments
 (0)