Skip to content

Commit 83c68a4

Browse files
blog feature
1 parent 1b30454 commit 83c68a4

34 files changed

+2652
-60
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?php
2+
3+
namespace App\Filament\Imports\Blog;
4+
5+
use App\Models\Blog\Category;
6+
use Filament\Actions\Imports\ImportColumn;
7+
use Filament\Actions\Imports\Importer;
8+
use Filament\Actions\Imports\Models\Import;
9+
10+
class CategoryImporter extends Importer
11+
{
12+
protected static ?string $model = Category::class;
13+
14+
public static function getColumns(): array
15+
{
16+
return [
17+
ImportColumn::make('name')
18+
->requiredMapping()
19+
->rules(['required', 'max:255'])
20+
->example('Category A'),
21+
ImportColumn::make('slug')
22+
->requiredMapping()
23+
->rules(['required', 'max:255'])
24+
->example('category-a'),
25+
ImportColumn::make('description')
26+
->example('This is the description for Category A.'),
27+
ImportColumn::make('is_visible')
28+
->label('Visibility')
29+
->requiredMapping()
30+
->boolean()
31+
->rules(['required', 'boolean'])
32+
->example('yes'),
33+
ImportColumn::make('seo_title')
34+
->label('SEO title')
35+
->rules(['max:60'])
36+
->example('Awesome Category A'),
37+
ImportColumn::make('seo_description')
38+
->label('SEO description')
39+
->rules(['max:160'])
40+
->example('Wow! It\'s just so amazing.'),
41+
];
42+
}
43+
44+
public function resolveRecord(): ?Category
45+
{
46+
return Category::firstOrNew([
47+
'slug' => $this->data['slug'],
48+
]);
49+
}
50+
51+
public static function getCompletedNotificationBody(Import $import): string
52+
{
53+
$body = 'Your blog category import has completed and ' . number_format($import->successful_rows) . ' ' . str('row')->plural($import->successful_rows) . ' imported.';
54+
55+
if ($failedRowsCount = $import->getFailedRowsCount()) {
56+
$body .= ' ' . number_format($failedRowsCount) . ' ' . str('row')->plural($failedRowsCount) . ' failed to import.';
57+
}
58+
59+
return $body;
60+
}
61+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<?php
2+
3+
namespace App\Filament\Resources\Blog;
4+
5+
use App\Filament\Resources\Blog\CategoryResource\Pages;
6+
use App\Models\Blog\Category;
7+
use Filament\Forms;
8+
use Filament\Forms\Form;
9+
use Filament\Infolists\Components\IconEntry;
10+
use Filament\Infolists\Components\TextEntry;
11+
use Filament\Infolists\Infolist;
12+
use Filament\Notifications\Notification;
13+
use Filament\Resources\Resource;
14+
use Filament\Tables;
15+
use Filament\Tables\Table;
16+
use Illuminate\Support\Str;
17+
18+
class CategoryResource extends Resource
19+
{
20+
protected static ?string $model = Category::class;
21+
22+
protected static ?string $slug = 'blog/categories';
23+
24+
protected static ?string $recordTitleAttribute = 'name';
25+
26+
protected static ?string $navigationGroup = 'Blog';
27+
28+
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
29+
30+
protected static ?int $navigationSort = 1;
31+
32+
public static function form(Form $form): Form
33+
{
34+
return $form
35+
->schema([
36+
Forms\Components\TextInput::make('name')
37+
->required()
38+
->maxLength(255)
39+
->live(onBlur: true)
40+
->afterStateUpdated(fn (string $operation, $state, Forms\Set $set) => $operation === 'create' ? $set('slug', Str::slug($state)) : null),
41+
42+
Forms\Components\TextInput::make('slug')
43+
->disabled()
44+
->dehydrated()
45+
->required()
46+
->maxLength(255)
47+
->unique(Category::class, 'slug', ignoreRecord: true),
48+
49+
Forms\Components\MarkdownEditor::make('description')
50+
->columnSpan('full'),
51+
52+
Forms\Components\Toggle::make('is_visible')
53+
->label('Visible to customers.')
54+
->default(true),
55+
]);
56+
}
57+
58+
public static function table(Table $table): Table
59+
{
60+
return $table
61+
->columns([
62+
Tables\Columns\TextColumn::make('name')
63+
->searchable()
64+
->sortable(),
65+
Tables\Columns\TextColumn::make('slug')
66+
->searchable()
67+
->sortable(),
68+
Tables\Columns\IconColumn::make('is_visible')
69+
->label('Visibility'),
70+
Tables\Columns\TextColumn::make('updated_at')
71+
->label('Last Updated')
72+
->date(),
73+
])
74+
->filters([
75+
//
76+
])
77+
->actions([
78+
Tables\Actions\ViewAction::make(),
79+
Tables\Actions\EditAction::make(),
80+
Tables\Actions\DeleteAction::make(),
81+
])
82+
->groupedBulkActions([
83+
Tables\Actions\DeleteBulkAction::make()
84+
->action(function () {
85+
Notification::make()
86+
->title('Now, now, don\'t be cheeky, leave some records for others to play with!')
87+
->warning()
88+
->send();
89+
}),
90+
]);
91+
}
92+
93+
public static function infolist(Infolist $infolist): Infolist
94+
{
95+
return $infolist
96+
->schema([
97+
TextEntry::make('name'),
98+
TextEntry::make('slug'),
99+
TextEntry::make('description'),
100+
IconEntry::make('is_visible')
101+
->label('Visibility'),
102+
TextEntry::make('updated_at')
103+
->dateTime(),
104+
])
105+
->columns(1)
106+
->inlineLabel();
107+
}
108+
109+
public static function getRelations(): array
110+
{
111+
return [
112+
//
113+
];
114+
}
115+
116+
public static function getPages(): array
117+
{
118+
return [
119+
'index' => Pages\ManageCategories::route('/'),
120+
];
121+
}
122+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace App\Filament\Resources\Blog\CategoryResource\Pages;
4+
5+
use App\Filament\Imports\Blog\CategoryImporter;
6+
use App\Filament\Resources\Blog\CategoryResource;
7+
use Filament\Actions;
8+
use Filament\Resources\Pages\ManageRecords;
9+
10+
class ManageCategories extends ManageRecords
11+
{
12+
protected static string $resource = CategoryResource::class;
13+
14+
protected function getActions(): array
15+
{
16+
return [
17+
Actions\ImportAction::make()
18+
->importer(CategoryImporter::class),
19+
Actions\CreateAction::make(),
20+
];
21+
}
22+
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
<?php
2+
3+
namespace App\Filament\Resources\Blog;
4+
5+
use App\Filament\Resources\Blog\LinkResource\Pages;
6+
use App\Models\Blog\Link;
7+
use Filament\Forms;
8+
use Filament\Forms\Form;
9+
use Filament\Infolists\Components\ColorEntry;
10+
use Filament\Infolists\Components\ImageEntry;
11+
use Filament\Infolists\Components\TextEntry;
12+
use Filament\Infolists\Infolist;
13+
use Filament\Notifications\Notification;
14+
use Filament\Resources\Concerns\Translatable;
15+
use Filament\Resources\Resource;
16+
use Filament\Support\Enums\FontWeight;
17+
use Filament\Tables;
18+
use Filament\Tables\Table;
19+
20+
class LinkResource extends Resource
21+
{
22+
use Translatable;
23+
24+
protected static ?string $model = Link::class;
25+
26+
protected static ?string $navigationIcon = 'heroicon-o-link';
27+
28+
protected static ?string $navigationGroup = 'Blog';
29+
30+
protected static ?int $navigationSort = 3;
31+
32+
public static function form(Form $form): Form
33+
{
34+
return $form
35+
->schema([
36+
Forms\Components\TextInput::make('title')
37+
->maxLength(255)
38+
->required(),
39+
Forms\Components\ColorPicker::make('color')
40+
->required()
41+
->hex()
42+
->hexColor(),
43+
Forms\Components\Textarea::make('description')
44+
->maxLength(1024)
45+
->required()
46+
->columnSpanFull(),
47+
Forms\Components\TextInput::make('url')
48+
->label('URL')
49+
->required()
50+
->maxLength(255)
51+
->columnSpanFull(),
52+
Forms\Components\FileUpload::make('image')
53+
->image(),
54+
]);
55+
}
56+
57+
public static function infolist(Infolist $infolist): Infolist
58+
{
59+
return $infolist
60+
->schema([
61+
TextEntry::make('title'),
62+
ColorEntry::make('color'),
63+
TextEntry::make('description')
64+
->columnSpanFull(),
65+
TextEntry::make('url')
66+
->label('URL')
67+
->columnSpanFull()
68+
->url(fn (Link $record): string => '#' . urlencode($record->url)),
69+
ImageEntry::make('image'),
70+
]);
71+
}
72+
73+
public static function table(Table $table): Table
74+
{
75+
return $table
76+
->columns([
77+
Tables\Columns\Layout\Stack::make([
78+
Tables\Columns\ImageColumn::make('image')
79+
->height('100%')
80+
->width('100%'),
81+
Tables\Columns\Layout\Stack::make([
82+
Tables\Columns\TextColumn::make('title')
83+
->weight(FontWeight::Bold),
84+
Tables\Columns\TextColumn::make('url')
85+
->formatStateUsing(fn (string $state): string => str($state)->after('://')->ltrim('www.')->trim('/'))
86+
->color('gray')
87+
->limit(30),
88+
]),
89+
])->space(3),
90+
Tables\Columns\Layout\Panel::make([
91+
Tables\Columns\Layout\Split::make([
92+
Tables\Columns\ColorColumn::make('color')
93+
->grow(false),
94+
Tables\Columns\TextColumn::make('description')
95+
->color('gray'),
96+
]),
97+
])->collapsible(),
98+
])
99+
->filters([
100+
//
101+
])
102+
->contentGrid([
103+
'md' => 2,
104+
'xl' => 3,
105+
])
106+
->paginated([
107+
18,
108+
36,
109+
72,
110+
'all',
111+
])
112+
->actions([
113+
Tables\Actions\Action::make('visit')
114+
->label('Visit link')
115+
->icon('heroicon-m-arrow-top-right-on-square')
116+
->color('gray')
117+
->url(fn (Link $record): string => '#' . urlencode($record->url)),
118+
Tables\Actions\EditAction::make(),
119+
])
120+
->bulkActions([
121+
Tables\Actions\BulkActionGroup::make([
122+
Tables\Actions\DeleteBulkAction::make()
123+
->action(function () {
124+
Notification::make()
125+
->title('Now, now, don\'t be cheeky, leave some records for others to play with!')
126+
->warning()
127+
->send();
128+
}),
129+
]),
130+
]);
131+
}
132+
133+
public static function getRelations(): array
134+
{
135+
return [
136+
//
137+
];
138+
}
139+
140+
public static function getPages(): array
141+
{
142+
return [
143+
'index' => Pages\ListLinks::route('/'),
144+
'create' => Pages\CreateLink::route('/create'),
145+
'view' => Pages\ViewLink::route('/{record}'),
146+
'edit' => Pages\EditLink::route('/{record}/edit'),
147+
];
148+
}
149+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace App\Filament\Resources\Blog\LinkResource\Pages;
4+
5+
use App\Filament\Resources\Blog\LinkResource;
6+
use Filament\Actions;
7+
use Filament\Resources\Pages\CreateRecord;
8+
9+
class CreateLink extends CreateRecord
10+
{
11+
use CreateRecord\Concerns\Translatable;
12+
13+
protected static string $resource = LinkResource::class;
14+
15+
protected function getHeaderActions(): array
16+
{
17+
return [
18+
Actions\LocaleSwitcher::make(),
19+
];
20+
}
21+
}

0 commit comments

Comments
 (0)