diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..8f0de65c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[docker-compose.yml] +indent_size = 4 diff --git a/src/Events/ContentUpdating.php b/src/Events/ContentUpdating.php index fce14115..402ee284 100644 --- a/src/Events/ContentUpdating.php +++ b/src/Events/ContentUpdating.php @@ -6,6 +6,7 @@ use Illuminate\Foundation\Events\Dispatchable; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Carbon; +use Illuminate\Support\Str; use Portable\FilaCms\Models\AbstractContentModel; class ContentUpdating @@ -20,9 +21,12 @@ class ContentUpdating public function __construct(public AbstractContentModel $page) { $page->updated_user_id = auth()->user() ? auth()->user()->id : $page->created_user_id; - if(!$page->is_draft && is_null($page->publish_at)) { + if (!$page->is_draft && is_null($page->publish_at)) { $page->publish_at = Carbon::now()->subMinute(); } + if ($page->slug === null) { + $page->slug = Str::slug($page->title); + } } } diff --git a/src/Filament/Forms/Components/StatusBadge.php b/src/Filament/Forms/Components/StatusBadge.php new file mode 100644 index 00000000..ae5a3567 --- /dev/null +++ b/src/Filament/Forms/Components/StatusBadge.php @@ -0,0 +1,15 @@ +label('Draft?') - ->offIcon('heroicon-m-eye') - ->onIcon('heroicon-m-eye-slash')->columnSpanFull(), - TextInput::make('title') - ->required(), - Select::make('author_id') - ->label('Author') - ->options(Author::all()->pluck('display_name', 'id')) - ->searchable(), - DatePicker::make('publish_at') - ->label('Publish Date'), - DatePicker::make('expire_at') - ->label('Expiry Date'), - static::tiptapEditor(), + Group::make() + ->schema([ + Tabs::make() + ->tabs([ + Tabs\Tab::make('Content') + ->schema([ + + TextInput::make('title') + ->columnSpanFull() + ->required(), + static::tiptapEditor(), + ]), + Tabs\Tab::make('Taxonomies') + ->schema([ + ...static::getTaxonomyFields(), + ]), + ]), + ]) + ->columnSpan(2), + Group::make() + ->schema([ + Section::make() + ->schema([ + TextInput::make('slug') + ->maxLength(255), + Toggle::make('is_draft') + ->label('Draft?') + ->offIcon('heroicon-m-eye') + ->onIcon('heroicon-m-eye-slash')->columnSpanFull(), + Select::make('author_id') + ->label('Author') + ->options(Author::all()->pluck('display_name', 'id')) + ->searchable(), + View::make('fila-cms::components.hr'), + DatePicker::make('publish_at') + ->label('Publish Date') + ->live(), + DatePicker::make('expire_at') + ->label('Expiry Date'), + ]) + ->columns(1), + Fieldset::make() + ->schema([ + Placeholder::make('publish_at_view') + ->label('Published') + ->visible(fn (?Model $record): bool => $record && $record->status === 'Published') + ->content(function (?Model $record): string { + return $record->publish_at ?? '?'; + }), + Placeholder::make('created_at_view') + ->label('Created') + ->visible(fn (?Model $record): bool => $record !== null) + ->content(function (?Model $record): string { + return $record->created_at ?? '?'; + }), + StatusBadge::make('status') + ->live() + ->badge() + ->color(fn (string $state): string => match ($state) { + 'Draft' => 'info', + 'Pending' => 'warning', + 'Published' => 'success', + 'Expired' => 'danger', + }) + ->default('Draft'), + + ]) + ->columns(1) + ]) + ->columnSpan(1), ]; - TaxonomyResource::where('resource_class', static::class)->get()->each(function (TaxonomyResource $taxonomyResource) use (&$fields) { + return $form->schema($fields)->columns(['lg' => 3]); + } + + public static function getTaxonomyFields(): array + { + $taxonomyFields = []; + TaxonomyResource::where('resource_class', static::class)->get()->each(function (TaxonomyResource $taxonomyResource) use (&$taxonomyFields) { $fieldName = Str::slug(Str::plural($taxonomyResource->taxonomy->name), '_'); - $fields[] = CheckboxList::make($fieldName.'_ids') + $taxonomyFields[] = CheckboxList::make($fieldName . '_ids') ->label($taxonomyResource->taxonomy->name) ->options($taxonomyResource->taxonomy->terms->pluck('name', 'id')); }); - return $form->schema($fields); + return $taxonomyFields; } public static function tiptapEditor($name = 'contents'): TiptapEditor @@ -85,7 +154,7 @@ public static function table(Table $table): Table return $table ->columns([ TextColumn::make('title') - ->description(fn (Page $page): string => substr($page->contents, 0, 50).'...') + ->description(fn (Page $page): string => substr($page->contents, 0, 50) . '...') ->sortable(), TextColumn::make('author.display_name')->label('Author') ->sortable(), diff --git a/src/Filament/Resources/AbstractContentResource/Pages/CreateAbstractContentResource.php b/src/Filament/Resources/AbstractContentResource/Pages/CreateAbstractContentResource.php index 84f5c503..4179397a 100644 --- a/src/Filament/Resources/AbstractContentResource/Pages/CreateAbstractContentResource.php +++ b/src/Filament/Resources/AbstractContentResource/Pages/CreateAbstractContentResource.php @@ -3,9 +3,17 @@ namespace Portable\FilaCms\Filament\Resources\AbstractContentResource\Pages; use Filament\Resources\Pages\CreateRecord; +use Illuminate\Support\Str; use Portable\FilaCms\Filament\Resources\AbstractContentResource; class CreateAbstractContentResource extends CreateRecord { protected static string $resource = AbstractContentResource::class; + + protected function mutateFormDataBeforeCreate(array $data): array + { + $data['slug'] = $data['slug'] ? Str::slug($data['slug']) : null; + + return $data; + } } diff --git a/src/Filament/Resources/AbstractContentResource/Pages/EditAbstractContentResource.php b/src/Filament/Resources/AbstractContentResource/Pages/EditAbstractContentResource.php index e7d37c4d..353c8245 100644 --- a/src/Filament/Resources/AbstractContentResource/Pages/EditAbstractContentResource.php +++ b/src/Filament/Resources/AbstractContentResource/Pages/EditAbstractContentResource.php @@ -4,6 +4,7 @@ use Filament\Actions; use Filament\Resources\Pages\EditRecord; +use Illuminate\Support\Str; use Portable\FilaCms\Filament\Resources\AbstractContentResource; class EditAbstractContentResource extends EditRecord @@ -16,4 +17,16 @@ protected function getHeaderActions(): array Actions\DeleteAction::make(), ]; } + + protected function mutateFormDataBeforeSave(array $data): array + { + $data['slug'] = $data['slug'] ? Str::slug($data['slug']) : null; + + return $data; + } + + protected function getRedirectUrl(): string + { + return $this->getResource()::getUrl('edit', ['record' => $this->record]); + } } diff --git a/tests/Unit/PageEventsTest.php b/tests/Unit/PageEventsTest.php index 8b55c74b..784e3669 100644 --- a/tests/Unit/PageEventsTest.php +++ b/tests/Unit/PageEventsTest.php @@ -4,6 +4,7 @@ use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\WithFaker; +use Illuminate\Support\Str; use Portable\FilaCms\Models\Author; use Portable\FilaCms\Models\Page; use Portable\FilaCms\Tests\TestCase; @@ -53,4 +54,28 @@ public function test_update_sets_publish() $page->save(); $this->assertNotNull($page->publish_at); } + + public function test_update_sets_slug() + { + $author = Author::create([ + 'first_name' => $this->faker->firstName, + 'last_name' => $this->faker->lastName, + 'is_individual' => 1 + ]); + + $data = [ + 'title' => $this->faker->words(15, true), + 'is_draft' => 0, + 'contents' => $this->faker->words($this->faker->numberBetween(50, 150), true), + 'author_Id' => $author->id, + ]; + $page = Page::create($data); + $this->assertNotNull($page->slug); + $newTitle = $this->faker->words(15, true); + $page->title = $newTitle; + $page->slug = null; + $page->save(); + $this->assertModelExists($page); + $this->assertDatabaseHas('pages', ['slug' => Str::slug($newTitle)]); + } } diff --git a/views/components/hr.blade.php b/views/components/hr.blade.php new file mode 100644 index 00000000..23471aa6 --- /dev/null +++ b/views/components/hr.blade.php @@ -0,0 +1 @@ +
diff --git a/views/filament/forms/components/status-badge.blade.php b/views/filament/forms/components/status-badge.blade.php new file mode 100644 index 00000000..b51187aa --- /dev/null +++ b/views/filament/forms/components/status-badge.blade.php @@ -0,0 +1,8 @@ + +
+ + + {{ $getState() }} + +
+