diff --git a/app/Dto/MainSliderDTO.php b/app/Dto/MainSliderDTO.php index dd585d2..f36f798 100644 --- a/app/Dto/MainSliderDTO.php +++ b/app/Dto/MainSliderDTO.php @@ -9,9 +9,9 @@ class MainSliderDTO public function __construct( public ?string $title, public ?string $content, - public ?string $image, + public ?array $image, public ?string $link, - public ?string $link_text, + public ?array $settings, public ?string $color_theme, public ?bool $is_active, public ?Carbon $start_time, @@ -27,7 +27,7 @@ class MainSliderDTO content: $data['content'] ?? null, image: $data['image'] ?? null, link: $data['link'] ?? null, - link_text: $data['link_text'] ?? null, + settings: $data['settings'] ?? null, color_theme: $data['color_theme'] ?? 'white', is_active: $data['is_active'] ?? true, start_time: isset($data['start_time']) ? Carbon::parse($data['start_time']) : null, @@ -40,19 +40,16 @@ class MainSliderDTO public function toArray(): array { return [ - 'id' => $this->id, 'title' => $this->title, 'content' => $this->content, 'image' => $this->image, 'link' => $this->link, - 'link_text' => $this->link_text, + 'settings' => $this->settings, 'color_theme' => $this->color_theme, 'is_active' => $this->is_active, 'start_time' => $this->start_time?->toDateTimeString(), 'end_time' => $this->end_time?->toDateTimeString(), 'sort' => $this->sort, - 'created_at' => $this->created_at?->toDateTimeString(), - 'updated_at' => $this->updated_at?->toDateTimeString(), ]; } } \ No newline at end of file diff --git a/app/Filament/Components/Forms/PostForm.php b/app/Filament/Components/Forms/PostForm.php index 962c4aa..f2e762c 100644 --- a/app/Filament/Components/Forms/PostForm.php +++ b/app/Filament/Components/Forms/PostForm.php @@ -25,6 +25,7 @@ use Filament\Forms\Components\SpatieTagsInput; use Filament\Forms\Components\Tabs; use Filament\Forms\Components\TextInput; use Filament\Forms\Components\Toggle; +use Filament\Forms\Components\ToggleButtons; use Filament\Forms\Form; use Filament\Resources\Components\Tab; use Filament\Resources\Pages\CreateRecord; @@ -124,46 +125,76 @@ class PostForm Toggle::make('is_slider_enabled') ->label('Добавить новый слайд') ->live() + ->hidden(fn (string $context): bool => $context === 'edit') ->dehydrated(false) ->default(false), Section::make() ->schema([ - Forms\Components\TextInput::make('slide.title') - ->label('Заголовок слайда'), - Forms\Components\Textarea::make('slide.content') - ->label('Текст слайда'), - FileUpload::make('slide.image') - ->label('Изображение') - ->image() - ->optimize('webp') - ->resize(50) - ->disk('public') - ->directory('images') - ->imageEditor() - ->required(), - Grid::make(2)->schema([ - Toggle::make('disable_link_text') - ->label('Отключить текст кнопки (ссылка будет открываться при нажатии на слайд)') - ->live() - ->inline(false) - ->dehydrated(false) - ->default(false), - Forms\Components\TextInput::make('slide.link_text') - ->default('Читать') - ->label('Текст кнопки') - ->disabled(fn (Forms\Get $get) => $get('disable_link_text')), + Forms\Components\Section::make('Информация слайда')->schema([ + Forms\Components\TextInput::make('slide.title') + ->label('Заголовок слайда'), + Forms\Components\Textarea::make('slide.content') + ->label('Текст слайда'), + Forms\Components\Grid::make()->schema([ + ColorPicker::make('slide.color_theme') + ->label('Цвет текста') + ->default('#ffffff') + ->required(), + Forms\Components\ToggleButtons::make('slide.settings.text_position') + ->options([ + 'left' => 'Текст слева', + 'center' => 'Текст по середине', + 'right' => 'Текст справа' + ]) + ->inline()->default('left')->grouped() + ->label('Позиция текста на слайде'), + ]), + Forms\Components\Grid::make()->schema([ + Toggle::make('active_button') + ->label('Использовать кнопку для ссылки (Ссылка будет открываться при нажатии на слайд)') + ->inline(false) + ->live() + ->afterStateHydrated(function (Toggle $component, $state, $get) { + $component->state(true); + }) + ->dehydrated(false), + Forms\Components\TextInput::make('slide.settings.link_text') + ->default('Читать') + ->label('Текст кнопки') + ->disabled(fn (Forms\Get $get) => !$get('active_button')) + ]), + ]), + Forms\Components\Section::make('Изображение')->schema([ + FileUpload::make('slide.image.url') + ->label('Изображение') + ->image() + ->optimize('webp') + ->resize(50) + ->disk('public') + ->directory('images') + ->imageEditor() + ->required(), + ToggleButtons::make('slide.image.shading')->inline()->grouped()->label('Уровень затемнения изображения')->options([ + '1' => 'Без затемнения', + '0.7' => 'Слабое затемнение', + '0.5' => 'Среднее затемнение', + '0.3' => 'Сильное затемнение', + ]), + ]), + Forms\Components\Section::make('Общая часть')->schema([ + Forms\Components\Grid::make()->schema([ + DateTimePicker::make('slide.end_time') + ->label('Слайд действует до') + ->native() + ->displayFormat('d/m/Y') + ->minDate(Carbon::now()) + ->maxDate(Carbon::now()->addMonth()), + ]), ]), - - DateTimePicker::make('slide.end_time') - ->label('Слайд действует до') - ->native() - ->displayFormat('d/m/Y') - ->minDate(Carbon::now()) - ->maxDate(Carbon::now()->addWeek()), ]) - ->disabled(fn (Forms\Get $get) => !$get('is_slider_enabled')) // Отключаем секцию, если Toggle выключен - ->hidden(fn (Forms\Get $get) => !$get('is_slider_enabled')), // Скрываем секцию, если Toggle выключен - ])->hidden(fn (string $context): bool => $context === 'edit'), + + + ]), ]), ]) ]); diff --git a/app/Filament/Resources/MainSliderResource.php b/app/Filament/Resources/MainSliderResource.php index 05cc612..b2c62ad 100644 --- a/app/Filament/Resources/MainSliderResource.php +++ b/app/Filament/Resources/MainSliderResource.php @@ -124,8 +124,13 @@ class MainSliderResource extends Resource Toggle::make('active_button') ->label('Использовать кнопку для ссылки (Ссылка будет открываться при нажатии на слайд)') ->inline(false) - ->default(true) + ->default(true) // Проверяем, есть ли текст в link_text ->live() + ->afterStateHydrated(function (Toggle $component, $state, $get) { + if ($state === null && !empty($get('settings.link_text'))) { + $component->state(true); // Устанавливаем значение по умолчанию + } + }) ->dehydrated(false), Forms\Components\TextInput::make('settings.link_text') ->default('Читать') @@ -156,12 +161,13 @@ class MainSliderResource extends Resource ->label('Слайд начинается с') ->native() ->displayFormat('d/m/Y') - ->minDate(Carbon::now()->subDay()) - ->maxDate(Carbon::now()->addWeek()), + ->default(Carbon::now()) + ->maxDate(Carbon::now()->addWeeks(2)), DateTimePicker::make('end_time') ->label('Слайд действует до') ->native() ->displayFormat('d/m/Y') + ->default(Carbon::now()->addWeeks(2)) ->minDate(Carbon::now()) ->maxDate(Carbon::now()->addMonth()), ]), diff --git a/app/Filament/Resources/PostResource/Pages/CreatePost.php b/app/Filament/Resources/PostResource/Pages/CreatePost.php index 5de74b6..19dff94 100644 --- a/app/Filament/Resources/PostResource/Pages/CreatePost.php +++ b/app/Filament/Resources/PostResource/Pages/CreatePost.php @@ -60,7 +60,7 @@ class CreatePost extends CreateRecord $this->slideData['start_time'] = $this->record->publish_at; $sliderDTO = MainSliderDTO::fromArray($this->slideData); - (new PostSliderService($sliderDTO, $this->record->slug))->create(); + (new PostSliderService($sliderDTO, $this->record))->create(); } protected function generateSeo(): void diff --git a/app/Filament/Resources/PostResource/Pages/EditPost.php b/app/Filament/Resources/PostResource/Pages/EditPost.php index e316761..5a35db6 100644 --- a/app/Filament/Resources/PostResource/Pages/EditPost.php +++ b/app/Filament/Resources/PostResource/Pages/EditPost.php @@ -5,6 +5,7 @@ namespace App\Filament\Resources\PostResource\Pages; use App\Dto\MainSliderDTO; use App\Enums\PostStatus; use App\Filament\Resources\PostResource; +use App\Models\Post; use App\Services\Filament\Domain\Posts\PostDataProcessor; use App\Services\Filament\Domain\Posts\PostNotificationService; use App\Services\Filament\Domain\Posts\PostSeoGenerator; @@ -23,6 +24,12 @@ class EditPost extends EditRecord protected array $slideData; + protected function mutateFormDataBeforeFill(array $data): array + { + $post = Post::query()->with(['seo', 'mainSlider'])->find($data['id']); + $data['slide'] = $post->mainSlider->toArray() ?? null; + return $data; + } protected function mutateFormDataBeforeSave(array $data): array { @@ -61,7 +68,7 @@ class EditPost extends EditRecord $this->slideData['start_time'] = $this->record->publish_at; $sliderDTO = MainSliderDTO::fromArray($this->slideData); - (new PostSliderService($sliderDTO, $this->record->slug))->update(); + (new PostSliderService($sliderDTO, $this->record))->update(); } protected function generateSeo(): void diff --git a/app/Models/MainSlider.php b/app/Models/MainSlider.php index 832fa6e..c4bea32 100644 --- a/app/Models/MainSlider.php +++ b/app/Models/MainSlider.php @@ -4,6 +4,8 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\MorphTo; +use Illuminate\Database\Eloquent\Relations\MorphToMany; class MainSlider extends Model { @@ -16,4 +18,11 @@ class MainSlider extends Model 'settings' => 'array', 'image' => 'array', ]; + + public function slidable() + { + return $this->morphTo(); + } + + } diff --git a/app/Models/Post.php b/app/Models/Post.php index 8a88f8b..b7e8877 100644 --- a/app/Models/Post.php +++ b/app/Models/Post.php @@ -7,6 +7,8 @@ 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\Relations\MorphOne; +use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Support\Facades\Cache; use Spatie\Tags\HasTags; @@ -39,12 +41,16 @@ class Post extends Model return $this->belongsTo(User::class, 'user_id'); } - public function seo() { return $this->morphOne(Seo::class, 'seoable'); } + public function mainSlider() + { + return $this->morphOne(MainSlider::class, 'slidable'); + } + protected $casts = [ 'content' => 'array', 'authors' => 'array', diff --git a/app/Observers/MainSliderObserver.php b/app/Observers/MainSliderObserver.php index 02dd4e7..929507e 100644 --- a/app/Observers/MainSliderObserver.php +++ b/app/Observers/MainSliderObserver.php @@ -19,12 +19,14 @@ class MainSliderObserver */ public function created(MainSlider $mainSlider): void { + + // Устанавливаем сортировку для новой записи $mainSlider->sort = 1; $mainSlider->save(); // Обновляем сортировку для всех остальных записей - $this->updateSortOrder(); + $this->updateSortOrder($mainSlider->id); $this->cacheService->clearAllCacheByModel(); } @@ -61,15 +63,16 @@ class MainSliderObserver // } - protected function updateSortOrder(): void + protected function updateSortOrder($id): void { // Получаем все записи, отсортированные по текущему значению sort - $slides = MainSlider::orderBy('sort', 'asc')->get(); + $slides = MainSlider::orderBy('sort', 'asc')->where('id', '!=', $id)->get(); - // Обновляем сортировку для каждой записи - foreach ($slides as $index => $slide) { - $slide->sort = $index + 1; // Начинаем с 1 - $slide->save(); + if ($slides->count() > 0) { + foreach ($slides as $index => $slide) { + $slide->sort = $index + 2; // Начинаем с 1 + $slide->save(); + } } } } diff --git a/app/Services/Filament/Domain/Posts/PostDataProcessor.php b/app/Services/Filament/Domain/Posts/PostDataProcessor.php index e6e35aa..3d5b747 100644 --- a/app/Services/Filament/Domain/Posts/PostDataProcessor.php +++ b/app/Services/Filament/Domain/Posts/PostDataProcessor.php @@ -35,6 +35,7 @@ class PostDataProcessor // Устанавливаем ID текущего пользователя $data['user_id'] = auth()->id(); + return $data; } @@ -62,12 +63,16 @@ class PostDataProcessor * @param string $status * @return Carbon|null */ - private function setPublishDateTime(array $publishSetting, string|PostStatus $status): ?Carbon + private function setPublishDateTime(array $publishSetting, $status): ?Carbon { if ($publishSetting['publish_after'] === true) { return Carbon::parse($publishSetting['publish_at']); } + if ($status === PostStatus::PUBLISHED->value) { + return Carbon::now(); + } + if ($status === PostStatus::PUBLISHED) { return Carbon::now(); } diff --git a/app/Services/Filament/Domain/Posts/PostSliderService.php b/app/Services/Filament/Domain/Posts/PostSliderService.php index 0d8244b..81c0945 100644 --- a/app/Services/Filament/Domain/Posts/PostSliderService.php +++ b/app/Services/Filament/Domain/Posts/PostSliderService.php @@ -4,56 +4,96 @@ namespace App\Services\Filament\Domain\Posts; use App\Dto\MainSliderDTO; use App\Models\MainSlider; +use App\Models\Post; +use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Log; class PostSliderService { public function __construct( - readonly private MainSliderDTO $dto, - readonly private string $postSlug, - ){} + private readonly MainSliderDTO $dto, + private readonly Model $post, + ) {} + /** + * Create a new MainSlider record associated with the post. + * + * @throws \Exception + */ public function create(): void { try { - MainSlider::create([ + $this->post->mainSlider()->create([ 'title' => $this->dto->title, 'content' => $this->dto->content, 'image' => $this->dto->image, - 'link' => parse_url(route('client.post.show', $this->postSlug), PHP_URL_PATH), - 'link_text' => $this->dto->link_text, - 'is_active' => $this->dto->is_active, + 'link' => $this->generatePostLink(), + 'settings' => $this->dto->settings, 'color_theme' => $this->dto->color_theme, + 'is_active' => $this->dto->is_active, 'start_time' => $this->dto->start_time, 'end_time' => $this->dto->end_time, ]); - Log::info('MainSlider created successfully', ['postSlug' => $this->postSlug]); + Log::info('MainSlider created successfully', ['postTitle' => $this->post->title]); } catch (\Exception $e) { - Log::error('Failed to create MainSlider', ['error' => $e->getMessage()]); + Log::error('Failed to create MainSlider', [ + 'postTitle' => $this->post->title, + 'error' => $e->getMessage(), + ]); throw $e; } } + /** + * Update an existing MainSlider record associated with the post. + * + * @throws \Exception + */ public function update(): void { try { - MainSlider::update([ - 'title' => $this->dto->title, - 'content' => $this->dto->content, - 'image' => $this->dto->image, - 'link' => parse_url(route('client.post.show', $this->postSlug), PHP_URL_PATH), - 'link_text' => $this->dto->link_text, - 'is_active' => $this->dto->is_active, - 'color_theme' => $this->dto->color_theme, - 'start_time' => $this->dto->start_time, - 'end_time' => $this->dto->end_time, - ]); + // Retrieve the MainSlider instance + $mainSlider = $this->post->mainSlider; - Log::info('MainSlider updated successfully', ['postSlug' => $this->postSlug]); + + if ($mainSlider) { + + // Update properties and save to trigger model events + $mainSlider->fill([ + 'title' => $this->dto->title, + 'content' => $this->dto->content, + 'image' => $this->dto->image, + 'link' => $this->generatePostLink(), + 'settings' => $this->dto->settings, + 'color_theme' => $this->dto->color_theme, + 'is_active' => $this->dto->is_active, + 'start_time' => $this->dto->start_time, + 'end_time' => $this->dto->end_time, + ]); + + $mainSlider->save(); // This will trigger updating and updated observer events + + Log::info('MainSlider updated successfully', ['postTitle' => $this->post->title]); + } else { + Log::warning('MainSlider not found for update', ['postTitle' => $this->post->title]); + } } catch (\Exception $e) { - Log::error('Failed to update MainSlider', ['error' => $e->getMessage()]); + Log::error('Failed to update MainSlider', [ + 'postTitle' => $this->post->title, + 'error' => $e->getMessage(), + ]); throw $e; } } -} \ No newline at end of file + + /** + * Generate the link for the post. + * + * @return string + */ + private function generatePostLink(): string + { + return parse_url(route('client.post.show', $this->post->slug), PHP_URL_PATH); + } +} diff --git a/database/migrations/2025_01_30_142319_add_morphs_to_main_sliders_table.php b/database/migrations/2025_01_30_142319_add_morphs_to_main_sliders_table.php new file mode 100644 index 0000000..a9612f9 --- /dev/null +++ b/database/migrations/2025_01_30_142319_add_morphs_to_main_sliders_table.php @@ -0,0 +1,29 @@ +unsignedBigInteger('slidable_id')->nullable(); // Поле для идентификатора + $table->string('slidable_type')->nullable(); // Поле для типа модели + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('main_sliders', function (Blueprint $table) { + $table->dropColumn(['slidable_id', 'slidable_type']); + }); + } +}; diff --git a/database/migrations/2025_03_11_154932_add_settings_column_to_pages.php b/database/migrations/2025_03_11_154932_add_settings_column_to_pages.php new file mode 100644 index 0000000..b083602 --- /dev/null +++ b/database/migrations/2025_03_11_154932_add_settings_column_to_pages.php @@ -0,0 +1,28 @@ +longText('settings')->nullable()->after('sub_section_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('pages', function (Blueprint $table) { + $table->dropColumn('settings'); + }); + } +}; diff --git a/database/migrations/2025_03_11_155031_delete_tempalte_column_to_pages.php b/database/migrations/2025_03_11_155031_delete_tempalte_column_to_pages.php new file mode 100644 index 0000000..0126eec --- /dev/null +++ b/database/migrations/2025_03_11_155031_delete_tempalte_column_to_pages.php @@ -0,0 +1,28 @@ +dropColumn('template'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('pages', function (Blueprint $table) { + $table->string('template')->nullable(); + }); + } +}; diff --git a/resources/js/Components/ClientMainSlider.vue b/resources/js/Components/ClientMainSlider.vue index 4e1c750..5aed35f 100644 --- a/resources/js/Components/ClientMainSlider.vue +++ b/resources/js/Components/ClientMainSlider.vue @@ -27,9 +27,18 @@ - - {{ item.settings.link_text }} - +
+ + {{ item.settings.link_text }} + + + + +