diff --git a/.idea/ntspi-new.iml b/.idea/ntspi-new.iml index 6aafdfe..3cc4d86 100644 --- a/.idea/ntspi-new.iml +++ b/.idea/ntspi-new.iml @@ -179,6 +179,10 @@ + + + + diff --git a/.idea/php.xml b/.idea/php.xml index 39687c1..1a2252c 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -190,6 +190,21 @@ + + + + + + + + + + + + + + + diff --git a/app/Filament/Resources/SlideResource.php b/app/Filament/Resources/SlideResource.php new file mode 100644 index 0000000..5a44f89 --- /dev/null +++ b/app/Filament/Resources/SlideResource.php @@ -0,0 +1,214 @@ +schema([ + Forms\Components\Section::make('Быстрая настройка слайда')->schema([ + Forms\Components\Grid::make()->schema([ + Forms\Components\Select::make('model_select') + ->name('') + ->label('Выбор типа данных') + ->options([ + 'Post' => 'Новость', + 'Page' => 'Страница', + 'Event' => 'Мероприятие', + 'Custom' => 'Кастомная ссылка', + ]) + ->afterStateUpdated(function (string $operation, string|null $state, Forms\Set $set, Forms\Get $get) { + if ($get('model_select') === 'Custom') { + $set('model', null); + $set('title', null); + $set('content', null); + $set('link', null); + }; + })->live(onBlur: true), + + Forms\Components\Select::make('model') + ->label('Поиск данных') + ->name('') + ->live(onBlur: true) + ->searchable() + ->live(onBlur: true) + ->afterStateUpdated(function (string $operation, string|null $state, Forms\Set $set, Forms\Get $get) { + if ($get('model_select') === 'Post') { + $post = Post::find($state); + $set('title', $post->title); + $relativeUrl = parse_url(route('client.post.show', $post->slug), PHP_URL_PATH); + $set('link', $relativeUrl); + }; + if ($get('model_select') === 'Page') { + $page = Page::find($state); + $set('title', $page->title); + $set('link', $page->path); + }; + if ($get('model_select') === 'Event') { + $event = Event::find($state); + $set('title', $event->title); + $relativeUrl = parse_url(route('client.event.show', $event->slug), PHP_URL_PATH); + $set('link', $relativeUrl); + }; + + }) + ->options(function (Forms\Get $get) { + if ($get('model_select') === 'Post') { + return Post::where('status', '=', 'published')->pluck('title', 'id'); + }; + if ($get('model_select') === 'Page') { + return Page::where('title', '!=', null)->pluck('title', 'id'); + }; + if ($get('model_select') === 'Event') { + return Event::all()->pluck('title', 'id'); + }; + if ($get('model_select') === 'Custom') { + return []; + }; + }), + ]), + ]), + Forms\Components\Section::make('Слайдер')->schema([ + + Forms\Components\Section::make('Информация слайда')->schema([ + Forms\Components\TextInput::make('title') + ->label('Заголовок слайда'), + Forms\Components\Textarea::make('content') + ->label('Текст слайда'), + Forms\Components\Grid::make()->schema([ + ColorPicker::make('color_theme') + ->label('Цвет текста') + ->default('#ffffff') + ->required(), + Forms\Components\ToggleButtons::make('settings.text_position') + ->options([ + 'left' => 'Текст слева', + 'center' => 'Текст по середине', + 'right' => 'Текст справа' + ]) + ->inline()->default('left')->grouped() + ->label('Позиция текста на слайде'), + ]), + Forms\Components\Grid::make()->schema([ + Toggle::make('active_button') + ->label('Использовать кнопку для ссылки (Ссылка будет открываться при нажатии на слайд)') + ->inline(false) + ->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('Читать') + ->label('Текст кнопки') + ->disabled(fn (Forms\Get $get) => !$get('active_button')) + ]), + ]), + Forms\Components\Section::make('Изображение')->schema([ + FileUpload::make('image.url') + ->label('Изображение') + ->image() + ->optimize('webp') + ->resize(50) + ->disk('public') + ->directory('images') + ->imageEditor() + ->required(), + ToggleButtons::make('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('start_time') + ->label('Слайд начинается с') + ->native() + ->displayFormat('d/m/Y') + ->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()), + ]), + Forms\Components\TextInput::make('link') + ->label('Ссылка кнопки') + ->required(), + ]), + + Toggle::make('is_active')->default(true)->label('Активный слайдер')->inline(false), + ]), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->reorderable('sort') + ->defaultSort('sort') + ->columns([ + Tables\Columns\TextColumn::make('title'), + Tables\Columns\ToggleColumn::make('is_active') + ]) + ->filters([ + // + ]) + ->actions([ + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + ]), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListSlides::route('/'), + 'create' => Pages\CreateSlide::route('/create'), + 'edit' => Pages\EditSlide::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Resources/SlideResource/Pages/CreateSlide.php b/app/Filament/Resources/SlideResource/Pages/CreateSlide.php new file mode 100644 index 0000000..451c52f --- /dev/null +++ b/app/Filament/Resources/SlideResource/Pages/CreateSlide.php @@ -0,0 +1,19 @@ +schema([ + Forms\Components\Section::make()->schema([ + Forms\Components\TextInput::make('title') + ->label('Заголовок слайдера'), + Toggle::make('is_active')->default(true)->label('Активный слайдер')->inline(false), + ]), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + // + ]) + ->filters([ + // + ]) + ->actions([ + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + ]), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListSliders::route('/'), + 'create' => Pages\CreateSlider::route('/create'), + 'edit' => Pages\EditSlider::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Resources/SliderResource/Pages/CreateSlider.php b/app/Filament/Resources/SliderResource/Pages/CreateSlider.php new file mode 100644 index 0000000..6db7b76 --- /dev/null +++ b/app/Filament/Resources/SliderResource/Pages/CreateSlider.php @@ -0,0 +1,12 @@ + 'array', + 'image' => 'array', + ]; + + public function slider(): BelongsTo + { + return $this->belongsTo(Slider::class); + } } diff --git a/app/Models/Slider.php b/app/Models/Slider.php index 76524bc..ed5b598 100644 --- a/app/Models/Slider.php +++ b/app/Models/Slider.php @@ -4,6 +4,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\HasMany; class Slider extends Model { @@ -11,13 +12,10 @@ class Slider extends Model protected $guarded = false; - protected $casts = [ - 'settings' => 'array', - 'image' => 'array', - ]; - public function slidable() + public function slides(): HasMany { - return $this->morphTo(); + return $this->hasMany(Slide::class); } + } diff --git a/database/migrations/2025_03_13_223400_create_sliders_table.php b/database/migrations/2025_03_13_223400_create_sliders_table.php new file mode 100644 index 0000000..27bbf66 --- /dev/null +++ b/database/migrations/2025_03_13_223400_create_sliders_table.php @@ -0,0 +1,29 @@ +id(); + $table->string('title'); + $table->boolean('is_active'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('sliders'); + } +}; diff --git a/database/migrations/2025_03_13_223411_create_slides_table.php b/database/migrations/2025_03_13_223411_create_slides_table.php new file mode 100644 index 0000000..0561321 --- /dev/null +++ b/database/migrations/2025_03_13_223411_create_slides_table.php @@ -0,0 +1,36 @@ +id(); + $table->string('title'); + $table->text('content'); + $table->string('image'); + $table->string('link'); + $table->string('link_text'); + $table->string('color_theme'); + $table->boolean('is_active')->default(true); + $table->integer('sort')->nullable(); + $table->foreignId('slider_id')->references('id')->on('sliders')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('slides'); + } +}; diff --git a/package-lock.json b/package-lock.json index fa12f12..02c218c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,8 @@ "js-cookie": "^3.0.5", "preline": "^1.9.0", "slugify": "^1.6.6", - "vue3-yandex-smartcaptcha": "^1.0.0" + "vue3-yandex-smartcaptcha": "^1.0.0", + "vuex": "^4.1.0" }, "devDependencies": { "@inertiajs/vue3": "^1.0.0", @@ -1037,6 +1038,11 @@ "@vue/shared": "3.5.11" } }, + "node_modules/@vue/devtools-api": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==" + }, "node_modules/@vue/reactivity": { "version": "3.5.11", "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.11.tgz", @@ -6218,6 +6224,17 @@ "vue": "^3.3.8" } }, + "node_modules/vuex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.1.0.tgz", + "integrity": "sha512-hmV6UerDrPcgbSy9ORAtNXDr9M4wlNP4pEFKye4ujJF8oqgFFuxDCdOLS3eNoRTtq5O3hoBDh9Doj1bQMYHRbQ==", + "dependencies": { + "@vue/devtools-api": "^6.0.0-beta.11" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index d3665d5..28cfd65 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,8 @@ "js-cookie": "^3.0.5", "preline": "^1.9.0", "slugify": "^1.6.6", - "vue3-yandex-smartcaptcha": "^1.0.0" + "vue3-yandex-smartcaptcha": "^1.0.0", + "vuex": "^4.1.0" }, "name": "ntspi-reborn", "description": "

\"Laravel

", diff --git a/resources/js/Components/ClientMainSlider.vue b/resources/js/Components/ClientMainSlider.vue index d0cc5fa..f0b1b55 100644 --- a/resources/js/Components/ClientMainSlider.vue +++ b/resources/js/Components/ClientMainSlider.vue @@ -69,6 +69,8 @@