Skip to content

Commit 33b8b65

Browse files
authored
Add Article functionality (#186)
1 parent 06f3aa5 commit 33b8b65

File tree

23 files changed

+758
-5
lines changed

23 files changed

+758
-5
lines changed

app/Enum/NewsStatus.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Enum;
6+
7+
use App\Concerns\Enums\Arrayable;
8+
use App\Concerns\Enums\Comparable;
9+
use App\Concerns\Enums\HasLabel;
10+
11+
enum NewsStatus: string
12+
{
13+
use Arrayable;
14+
use Comparable;
15+
use HasLabel;
16+
17+
case published = 'published';
18+
case archived = 'archived';
19+
case drafted = 'drafted';
20+
21+
protected function labelKeyPrefix(): ?string
22+
{
23+
return 'news.status';
24+
}
25+
}
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Filament\Resources;
6+
7+
use App\Enum\NewsStatus;
8+
use App\Filament\Resources\NewsResource\Pages;
9+
use App\Models\News;
10+
use Filament\Forms\Components\Card;
11+
use Filament\Forms\Components\Group;
12+
use Filament\Forms\Components\RichEditor;
13+
use Filament\Forms\Components\Select;
14+
use Filament\Forms\Components\SpatieMediaLibraryFileUpload;
15+
use Filament\Forms\Components\TextInput;
16+
use Filament\Resources\Form;
17+
use Filament\Resources\Resource;
18+
use Filament\Resources\Table;
19+
use Filament\Tables;
20+
use Filament\Tables\Columns\SpatieMediaLibraryImageColumn;
21+
use Filament\Tables\Columns\TextColumn;
22+
use Filament\Tables\Filters\Layout;
23+
use Filament\Tables\Filters\SelectFilter;
24+
use Illuminate\Database\Eloquent\Builder;
25+
26+
class NewsResource extends Resource
27+
{
28+
protected static ?string $model = News::class;
29+
30+
protected static ?string $navigationIcon = 'heroicon-o-newspaper';
31+
32+
protected static ?int $navigationSort = 5;
33+
34+
public static function getModelLabel(): string
35+
{
36+
return __('news.label.singular');
37+
}
38+
39+
public static function getPluralModelLabel(): string
40+
{
41+
return __('news.label.plural');
42+
}
43+
44+
public static function form(Form $form): Form
45+
{
46+
return $form
47+
->schema([
48+
Card::make()
49+
->columns(1)
50+
->schema([
51+
52+
Group::make()
53+
->hidden(fn () => auth()->user()->belongsToOrganisation())
54+
->columns()
55+
->columnSpanFull()
56+
->schema([
57+
Select::make('organisation_id')
58+
->label(__('news.field.organisation'))
59+
->relationship('organisation', 'name')
60+
->required(),
61+
]),
62+
63+
SpatieMediaLibraryFileUpload::make('cover_photo')
64+
->collection('cover_photos')
65+
->enableOpen()
66+
->maxFiles(1)
67+
->conversion('thumb')
68+
->visibility('public')
69+
->label(__('news.field.cover_photo'))
70+
->image(),
71+
72+
TextInput::make('title')
73+
->label(__('news.field.title'))
74+
->maxLength(200)
75+
->required(),
76+
77+
RichEditor::make('body')
78+
->label(__('news.field.body'))
79+
->required(),
80+
81+
SpatieMediaLibraryFileUpload::make('media_files')
82+
->collection('media_files')
83+
->enableOpen()
84+
->multiple()
85+
->conversion('thumb')
86+
->visibility('public')
87+
->label(__('news.field.media_files'))
88+
->image(),
89+
]),
90+
]);
91+
}
92+
93+
public static function table(Table $table): Table
94+
{
95+
return $table
96+
->columns([
97+
TextColumn::make('id')
98+
->label('ID')
99+
->sortable(),
100+
101+
TextColumn::make('organisation.name')
102+
->label(__('news.field.organisation'))
103+
->hidden(fn () => auth()->user()->belongsToOrganisation())
104+
->sortable()
105+
->toggleable(),
106+
107+
SpatieMediaLibraryImageColumn::make('cover_photo')
108+
->collection('cover_photos')
109+
->conversion('thumb')
110+
->extraImgAttributes([
111+
'class' => 'object-contain',
112+
])
113+
->width(80)
114+
->height(40)
115+
->visibility('private')
116+
->toggleable(),
117+
118+
TextColumn::make('title')
119+
->label(__('news.field.title'))
120+
->sortable()
121+
->toggleable()
122+
->searchable(),
123+
124+
TextColumn::make('media_count')
125+
->label(__('news.field.media_files_count'))
126+
->counts([
127+
'media' => fn (Builder $query) => $query->where('collection_name', 'media_files'),
128+
])
129+
->sortable()
130+
->toggleable(),
131+
132+
TextColumn::make('status')
133+
->label(__('news.field.status'))
134+
->sortable()
135+
->toggleable()
136+
->searchable(),
137+
138+
Tables\Columns\BadgeColumn::make('status')
139+
->colors([
140+
'secondary' => NewsStatus::drafted->value,
141+
'warning' => NewsStatus::archived->value,
142+
'success' => NewsStatus::published->value,
143+
])
144+
->enum(NewsStatus::options()),
145+
146+
TextColumn::make('created_at')
147+
->label(__('general.created_at'))
148+
->formatStateUsing(fn ($state) => $state->toDateTimeString())
149+
->searchable()
150+
->sortable()
151+
->toggleable(),
152+
153+
TextColumn::make('updated_at')
154+
->label(__('general.updated_at'))
155+
->formatStateUsing(fn ($state) => $state->toDateTimeString())
156+
->searchable()
157+
->sortable()
158+
->toggleable(),
159+
])
160+
->defaultSort('id', 'desc')
161+
->filters([
162+
163+
SelectFilter::make('status')
164+
->multiple()
165+
->label(__('news.field.status'))
166+
->options(NewsStatus::options())
167+
->query(function (Builder $query, array $state) {
168+
$values = $state['values'] ?? null;
169+
170+
if (blank($values)) {
171+
// Don't apply any filtering if no values are selected
172+
return $query;
173+
}
174+
175+
return $query->whereIn('status', $values);
176+
}),
177+
178+
])
179+
->filtersLayout(Layout::AboveContent)
180+
->actions([
181+
Tables\Actions\ViewAction::make(),
182+
])
183+
->bulkActions([
184+
//
185+
])
186+
->headerActions([
187+
]);
188+
}
189+
190+
public static function getRelations(): array
191+
{
192+
return [
193+
//
194+
];
195+
}
196+
197+
public static function getPages(): array
198+
{
199+
return [
200+
'index' => Pages\ListNews::route('/'),
201+
'create' => Pages\CreateNews::route('/create'),
202+
'view' => Pages\ViewNews::route('/{record}'),
203+
'edit' => Pages\EditNews::route('/{record}/edit'),
204+
];
205+
}
206+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Filament\Resources\NewsResource\Actions;
6+
7+
use App\Models\News;
8+
use Filament\Pages\Actions\Action;
9+
10+
class ArchiveNewsAction extends Action
11+
{
12+
public static function getDefaultName(): ?string
13+
{
14+
return 'archive_news';
15+
}
16+
17+
protected function setUp(): void
18+
{
19+
parent::setUp();
20+
21+
$this->color('warning');
22+
23+
$this->action(function (News $record, Action $action) {
24+
$record->archive();
25+
$action->success();
26+
});
27+
28+
$this->requiresConfirmation();
29+
30+
$this->label(__('news.action.change_status.archive.button'));
31+
32+
$this->modalHeading(__('news.action.change_status.archive.heading'));
33+
$this->modalSubheading(__('news.action.change_status.archive.subheading'));
34+
$this->modalButton(__('news.action.change_status.archive.button'));
35+
36+
$this->successNotificationTitle(__('news.action.change_status.archive.success'));
37+
}
38+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Filament\Resources\NewsResource\Actions;
6+
7+
use App\Models\News;
8+
use Filament\Pages\Actions\Action;
9+
10+
class DraftNewsAction extends Action
11+
{
12+
public static function getDefaultName(): ?string
13+
{
14+
return 'draft_news';
15+
}
16+
17+
protected function setUp(): void
18+
{
19+
parent::setUp();
20+
21+
$this->color('secondary');
22+
23+
$this->action(function (News $record, Action $action) {
24+
$record->draft();
25+
$action->success();
26+
});
27+
28+
$this->requiresConfirmation();
29+
30+
$this->label(__('news.action.change_status.draft.button'));
31+
32+
$this->modalHeading(__('news.action.change_status.draft.heading'));
33+
$this->modalSubheading(__('news.action.change_status.draft.subheading'));
34+
$this->modalButton(__('news.action.change_status.draft.button'));
35+
36+
$this->successNotificationTitle(__('news.action.change_status.draft.success'));
37+
}
38+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Filament\Resources\NewsResource\Actions;
6+
7+
use App\Models\News;
8+
use Filament\Pages\Actions\Action;
9+
10+
class PublishNewsAction extends Action
11+
{
12+
public static function getDefaultName(): ?string
13+
{
14+
return 'publish_news';
15+
}
16+
17+
protected function setUp(): void
18+
{
19+
parent::setUp();
20+
21+
$this->color('success');
22+
23+
$this->action(function (News $record, Action $action) {
24+
$record->publish();
25+
$action->success();
26+
});
27+
28+
$this->requiresConfirmation();
29+
30+
$this->label(__('news.action.change_status.publish.button'));
31+
32+
$this->modalHeading(__('news.action.change_status.publish.heading'));
33+
$this->modalSubheading(__('news.action.change_status.publish.subheading'));
34+
$this->modalButton(__('news.action.change_status.publish.button'));
35+
36+
$this->successNotificationTitle(__('news.action.change_status.publish.success'));
37+
}
38+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Filament\Resources\NewsResource\Pages;
6+
7+
use App\Filament\Resources\NewsResource;
8+
use Filament\Resources\Pages\CreateRecord;
9+
10+
class CreateNews extends CreateRecord
11+
{
12+
protected static string $resource = NewsResource::class;
13+
14+
protected static bool $canCreateAnother = false;
15+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Filament\Resources\NewsResource\Pages;
6+
7+
use App\Filament\Resources\NewsResource;
8+
use Filament\Resources\Pages\EditRecord;
9+
10+
class EditNews extends EditRecord
11+
{
12+
protected static string $resource = NewsResource::class;
13+
14+
protected function getActions(): array
15+
{
16+
return [
17+
//
18+
];
19+
}
20+
21+
protected function getRedirectUrl(): ?string
22+
{
23+
return static::getResource()::getUrl('view', $this->getRecord());
24+
}
25+
}

0 commit comments

Comments
 (0)