Rework frontend

This commit is contained in:
F4ilji
2025-03-23 00:38:16 +05:00
parent 17518ceed2
commit 49072e50c7
438 changed files with 14375 additions and 20015 deletions
@@ -11,6 +11,7 @@ use App\Models\CustomForm;
use App\Models\Page;
use App\Models\PageReferenceList;
use App\Models\Post;
use App\Models\Slider;
use Filament\Forms;
use Filament\Forms\Components\Builder;
use Filament\Forms\Components\FileUpload;
@@ -415,6 +416,13 @@ class ContentBuilderItem
->searchable()
->required(),
])->label('Контакты'),
Builder\Block::make('slider')
->schema([
Select::make('slider')
->options(Slider::query()->whereHas('slides')->where('is_active', true)->pluck('title', 'slug'))
->searchable()
->required(),
])->label('Слайдеры'),
])
->collapsed()
->blockNumbers(false)
@@ -192,6 +192,7 @@ class PostForm
]),
]),
])
->hidden(fn(Forms\Get $get) => !$get('is_slider_enabled'))
]),
@@ -31,11 +31,15 @@ class AdmissionCampaignResource extends Resource
{
return $form
->schema([
TextInput::make('name')->label('Название')->required()->columnSpanFull(),
Forms\Components\Select::make('academic_year')->label('Академический год')->required()
->options(['2024' => '2024/2025', '2025' => '2025/2026']),
Forms\Components\Select::make('status')->label('Статус')->required()
->options(['1' => 'Активный', '2' => 'Архивный', '3' => 'Скрыт']),
Forms\Components\Section::make()->schema([
TextInput::make('name')->label('Название')->required()->columnSpanFull(),
Forms\Components\Grid::make()->schema([
Forms\Components\Select::make('academic_year')->label('Академический год')->required()
->options(self::generateAcademicYears()),
Forms\Components\Select::make('status')->label('Статус')->required()
->options(['1' => 'Активный', '2' => 'Архивный', '3' => 'Скрыт']),
]),
]),
Forms\Components\Section::make()->schema([
Forms\Components\Repeater::make('info')->schema([
Forms\Components\Select::make('edu_name')->options(LevelEducational::class),
@@ -91,4 +95,19 @@ class AdmissionCampaignResource extends Resource
'edit' => Pages\EditAdmissionCampaign::route('/{record}/edit'),
];
}
protected static function generateAcademicYears(): array
{
$currentYear = (int) date('Y') - 5;
$yearsAhead = 10; // Количество лет вперед
$academicYears = [];
for ($i = 0; $i < $yearsAhead; $i++) {
$startYear = $currentYear + $i;
$endYear = $startYear + 1;
$academicYears[$startYear] = "{$startYear}/{$endYear}";
}
return $academicYears;
}
}
@@ -4,6 +4,7 @@ namespace App\Filament\Resources\AdmissionCampaignResource\Pages;
use App\Filament\Resources\AdmissionCampaignResource;
use Filament\Actions;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\ListRecords;
class ListAdmissionCampaigns extends ListRecords
@@ -13,7 +14,51 @@ class ListAdmissionCampaigns extends ListRecords
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
Actions\CreateAction::make(), // Стандартная кнопка "Создать"
Actions\Action::make('fetchData') // Кастомная кнопка
->label('Обновить данные')
->color('primary') // Цвет кнопки
->icon('heroicon-o-arrow-path') // Иконка
->action(function () {
// Отправляем запрос в фоновом режиме
$this->js(<<<JS
fetch('/api/get-data', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
},
})
.then(response => response.json())
.then(data => {
// Показываем уведомление об успешном завершении
window.dispatchEvent(new CustomEvent('filament-notify', {
detail: {
type: 'success',
title: 'Данные обновлены',
body: 'Данные успешно обновлены.',
},
}));
})
.catch(error => {
// Показываем уведомление об ошибке
window.dispatchEvent(new CustomEvent('filament-notify', {
detail: {
type: 'danger',
title: 'Ошибка',
body: 'Произошла ошибка при обновлении данных.',
},
}));
});
JS);
// Показываем уведомление
Notification::make()
->title('Данные обновляются')
->body('Данные обновляются в фоновом режиме.')
->success()
->send();
}),
];
}
}
}
@@ -2,7 +2,9 @@
namespace App\Filament\Resources;
use App\Enums\BudgetEducation;
use App\Enums\EducationalProgramStatus;
use App\Enums\FormEducation;
use App\Filament\Resources\AdmissionPlanResource\Pages;
use App\Filament\Resources\AdmissionPlanResource\RelationManagers;
use App\Filament\Resources\EducationalProgramResource\RelationManagers\AdmissionPlansRelationManager;
@@ -39,13 +41,15 @@ class AdmissionPlanResource extends Resource
{
return $form
->schema([
Forms\Components\Select::make('educational_programs_id')
->searchable()
->label('Образовательная программа')
->options(EducationalProgram::whereIn('status', [EducationalProgramStatus::PUBLISHED, EducationalProgramStatus::IN_PROGRESS])->pluck('name', 'id')),
Forms\Components\Select::make('admission_campaigns_id')
->label('Приемная компания')
->options(AdmissionCampaign::all()->pluck('name', 'id')),
Section::make()->schema([
Forms\Components\Select::make('educational_programs_id')
->searchable()
->label('Образовательная программа')
->options(EducationalProgram::whereIn('status', [EducationalProgramStatus::PUBLISHED, EducationalProgramStatus::IN_PROGRESS])->pluck('name', 'id')),
Forms\Components\Select::make('admission_campaigns_id')
->label('Приемная компания')
->options(AdmissionCampaign::all()->pluck('name', 'id')),
]),
Section::make('План приема')->schema([
Forms\Components\Repeater::make('exams')->label('Вступительные испытания')->schema([
TextInput::make('title')->label('Название-предмета'),
@@ -59,12 +63,11 @@ class AdmissionPlanResource extends Resource
return "Вступительное испытание #" . $count;
}),
Forms\Components\Repeater::make('contests')->label('Условия поступления')->schema([
Forms\Components\Select::make('form_education')->label('Форма образования')
->options(['och' => 'Очная форма обучения', 'zaoch' => 'Заочная форма обучения', 'och_zaoch' => 'Очно-заочная форма обучения']),
Forms\Components\Select::make('form_education')->label('Форма обучения')
->options(FormEducation::class),
Forms\Components\Select::make('financing_source')->label('Источник финансирования')
->options(['budget' => 'Бюджетное место', 'non_budget' => 'С оплатой обучения']),
TextInput::make('budget_quantity_position')->label('Количество бюджетных мест')->integer(),
TextInput::make('non_budget_quantity_position')->label('Количество платных мест')->integer()
->options(BudgetEducation::class),
TextInput::make('position_count')->label('Количество мест на прием')->integer(),
])->live()->maxItems(2)->collapsed()->addActionLabel('Добавить группу')->columns(3)
->itemLabel(function (Get $get) {
static $count = 0;
@@ -31,18 +31,16 @@ class DirectionStudyResource extends Resource
public static function form(Form $form): Form
{
return $form
->schema([
//
]);
->schema([]);
}
public static function table(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('name'),
Tables\Columns\TextColumn::make('code'),
Tables\Columns\TextColumn::make('lvl_edu')
Tables\Columns\TextColumn::make('name')->label('Название'),
Tables\Columns\TextColumn::make('code')->label('Код направления'),
Tables\Columns\TextColumn::make('lvl_edu')->label('Уровень образования')
->formatStateUsing(fn ($state) => $state->getLabel())
])
->filters([
@@ -241,9 +241,8 @@ class EducationalProgramResource extends Resource
{
return $table
->columns([
Tables\Columns\TextColumn::make('name')->sortable()->searchable(),
Tables\Columns\TextColumn::make('code_napr'),
Tables\Columns\TextColumn::make('directionStudy.lvl_edu')->limit(30),
Tables\Columns\TextColumn::make('name')->label('Название программы')->sortable()->searchable(),
Tables\Columns\TextColumn::make('directionStudy.lvl_edu')->label('Уровень образования')->limit(30),
])
->filters([
//
@@ -1,74 +0,0 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\ExternalVacancyResource\Pages;
use App\Filament\Resources\ExternalVacancyResource\RelationManagers;
use App\Models\ExternalVacancy;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class ExternalVacancyResource extends Resource
{
protected static ?string $model = ExternalVacancy::class;
protected static ?string $navigationGroup = 'Вакансии';
public static ?string $label = 'Вакансия для студентов';
protected static ?string $pluralLabel = 'Вакансии для студентов прочих организаций';
protected static ?string $navigationIcon = 'heroicon-o-briefcase';
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('city')->required()->columnSpan('full')->label('Город'),
Forms\Components\TagsInput::make('position')->label('Должность')->placeholder('')->required(),
Forms\Components\TextInput::make('salary')->integer()->label('Зарплата')->prefix('₽'),
Forms\Components\RichEditor::make('conditions')->label('Условия работы'),
Forms\Components\RichEditor::make('contacts')->label('Контакты')->required(),
]);
}
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\ListExternalVacancies::route('/'),
'create' => Pages\CreateExternalVacancy::route('/create'),
'edit' => Pages\EditExternalVacancy::route('/{record}/edit'),
];
}
}
@@ -1,12 +0,0 @@
<?php
namespace App\Filament\Resources\ExternalVacancyResource\Pages;
use App\Filament\Resources\ExternalVacancyResource;
use Filament\Actions;
use Filament\Resources\Pages\CreateRecord;
class CreateExternalVacancy extends CreateRecord
{
protected static string $resource = ExternalVacancyResource::class;
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\ExternalVacancyResource\Pages;
use App\Filament\Resources\ExternalVacancyResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditExternalVacancy extends EditRecord
{
protected static string $resource = ExternalVacancyResource::class;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\ExternalVacancyResource\Pages;
use App\Filament\Resources\ExternalVacancyResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListExternalVacancies extends ListRecords
{
protected static string $resource = ExternalVacancyResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}
@@ -1,177 +0,0 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\LibraryNewsResource\Pages;
use App\Filament\Resources\LibraryNewsResource\RelationManagers;
use App\Models\LibraryNews;
use Filament\Forms;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Hidden;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Filament\Forms\Components\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Mohamedsabil83\FilamentFormsTinyeditor\Components\TinyEditor;
class LibraryNewsResource extends Resource
{
protected static ?string $model = LibraryNews::class;
protected static ?string $navigationGroup = 'Библиотека';
public static ?string $label = 'Новость';
protected static ?string $pluralLabel = 'Новости библиотеки';
protected static ?string $navigationIcon = 'heroicon-o-newspaper';
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\Grid::make()->schema([
Forms\Components\TextInput::make('title')->required()->label('Заголовок'),
Forms\Components\TextInput::make('category')->required()->label('Категория (Необязательно)'),
]),
Forms\Components\Textarea::make('preview_text')->required()->label('Текст анонса')
->required()
->columnSpanFull(),
Section::make('Контент')->schema([
\Filament\Forms\Components\Builder::make('content')->label('')->blocks([
Builder\Block::make('heading')->label('Заголовок')
->schema([
TextInput::make('id')->hidden()->integer()->default(rand(2335235,324634264263426)),
TextInput::make('content')
->label('')
->live(onBlur: true)
->afterStateUpdated(function (string $operation, string $state, Forms\Set $set, $get) {
}),
]),
Builder\Block::make('paragraph')
->schema([
RichEditor::make('content')
->toolbarButtons([
'blockquote',
'bold',
'bulletList',
'italic',
'link',
'orderedList',
'redo',
'strike',
'underline',
'undo',
])
->label('')
->required()
]),
Builder\Block::make('image')
->schema([
FileUpload::make('url')
->label('Изображение(-я)')
->image()
->multiple()
->reorderable()
->maxFiles(5)
->disk('public')
->directory('images')
->imageEditor()
->required(),
TextInput::make('alt')
->label('Описание')
->placeholder('Необязяательно')
])->label('Изображение(-я)'),
Builder\Block::make('video')
->schema([
Hidden::make('mime'),
TextInput::make('title')
->required()
->maxLength(255)
->autofocus(),
FileUpload::make('path')
->required()
->acceptedFileTypes(['video/mp4','video/ogg','video/webm'])
->maxSize(512000)
->disk('videos')
->visibility('public')
->afterStateUpdated(fn (callable $set, $state) => $set('mime', $state?->getMimeType())),
]),
Builder\Block::make('files')
->schema([
TextInput::make('title')
->required()
->maxLength(255)
->autofocus(),
FileUpload::make('path')
->required()
->acceptedFileTypes([
'application/pdf',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'application/zip'
])
->maxSize(512000)
->disk('public')
->directory('files')
->downloadable()
->visibility('public')
])
])
->collapsed()
->blockNumbers(false)
->collapsible()
->addActionLabel('Добавить новый блок'),
]),
Forms\Components\Toggle::make('is_active')->required()->label('Активная заметка')->default(true),
]);
}
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\ListLibraryNews::route('/'),
'create' => Pages\CreateLibraryNews::route('/create'),
'edit' => Pages\EditLibraryNews::route('/{record}/edit'),
];
}
}
@@ -1,103 +0,0 @@
<?php
namespace App\Filament\Resources\LibraryNewsResource\Pages;
use App\Filament\Resources\LibraryNewsResource;
use Filament\Actions;
use Filament\Resources\Pages\CreateRecord;
use Illuminate\Support\Str;
class CreateLibraryNews extends CreateRecord
{
protected static string $resource = LibraryNewsResource::class;
protected array $seoData;
protected function mutateFormDataBeforeCreate(array $data): array
{
$this->seoData = $this->generateSeo($data);
$data['search_data'] = $this->generateSearchData($data['content']);
return $data;
}
protected function afterCreate(): void
{
$this->record->seo()->create($this->seoData);
}
private function generateSeo(array $data) : array
{
$title = $data['title'];
$rowData = $this->getFirstBlockByName('paragraph', $data['content']);
if ($rowData !== null) {
$description = strip_tags($rowData['data']['content']);
} else {
$description = null;
}
return [
'title' => $title,
'description' => Str::limit(htmlspecialchars($description, ENT_QUOTES, 'UTF-8'), 160),
];
}
private function generateSearchData(array $data) : string
{
$result = "";
foreach ($data as $block) {
$result .= $this->getDataFromBlocks($block);
}
// Удаляем лишние пробелы и переносы строк
$result = preg_replace('/\s+/', ' ', $result);
$result = trim($result);
return strtolower($result);
}
private function getFirstBlockByName(string $name, array $content) : array|null
{
$data = null;
foreach ($content as $block) {
$data = ($block['type'] === $name) ? $block : null;
break;
}
return $data;
}
private function getDataFromBlocks($block) : string
{
$data = "";
switch ($block['type']) {
case 'paragraph':
$data .= strip_tags($block['data']['content']) . " ";
break;
case 'heading':
$data .= strip_tags($block['data']['content']) . " ";
break;
case 'files':
foreach ($block['data']['file'] as $file) {
$data .= $file['title'] . " ";
}
break;
case 'person':
$data .= $block['data']['name'] . " ";
break;
case 'stepper':
$data .= $block['data']['step_name'] . " ";
foreach ($block['data']['steps'] as $step) {
$data .= $step['title'] . " ";
$data .= strip_tags($step['content']) . " ";
}
break;
case 'tabs':
foreach ($block['data']['tab'] as $item) {
foreach ($item['content'] as $block) {
$data .= $this->getDataFromBlocks($block);
};
};
break;
}
return $data;
}
}
@@ -1,112 +0,0 @@
<?php
namespace App\Filament\Resources\LibraryNewsResource\Pages;
use App\Filament\Resources\LibraryNewsResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
use Illuminate\Support\Str;
class EditLibraryNews extends EditRecord
{
protected static string $resource = LibraryNewsResource::class;
protected array $seoData;
protected function mutateFormDataBeforeSave(array $data): array
{
$this->seoData = $this->generateSeo($data);
$data['search_data'] = $this->generateSearchData($data['content']);
return $data;
}
protected function afterSave(): void
{
$this->record->seo()->update($this->seoData);
}
private function generateSeo(array $data) : array
{
$title = $data['title'];
$rowData = $this->getFirstBlockByName('paragraph', $data['content']);
if ($rowData !== null) {
$description = strip_tags($rowData['data']['content']);
} else {
$description = null;
}
return [
'title' => $title,
'description' => Str::limit(htmlspecialchars($description, ENT_QUOTES, 'UTF-8'), 160),
];
}
private function getDataFromBlocks($block) : string
{
$data = "";
switch ($block['type']) {
case 'paragraph':
$data .= strip_tags($block['data']['content']) . " ";
break;
case 'heading':
$data .= strip_tags($block['data']['content']) . " ";
break;
case 'files':
foreach ($block['data']['file'] as $file) {
$data .= $file['title'] . " ";
}
break;
case 'person':
$data .= $block['data']['name'] . " ";
break;
case 'stepper':
$data .= $block['data']['step_name'] . " ";
foreach ($block['data']['steps'] as $step) {
$data .= $step['title'] . " ";
$data .= strip_tags($step['content']) . " ";
}
break;
case 'tabs':
foreach ($block['data']['tab'] as $item) {
foreach ($item['content'] as $block) {
$data .= $this->getDataFromBlocks($block);
};
};
break;
}
return $data;
}
private function generateSearchData(array $data) : string
{
$result = "";
foreach ($data as $block) {
$result .= $this->getDataFromBlocks($block);
}
// Удаляем лишние пробелы и переносы строк
$result = preg_replace('/\s+/', ' ', $result);
$result = trim($result);
return strtolower($result);
}
private function getFirstBlockByName(string $name, array $content) : array|null
{
$data = null;
foreach ($content as $block) {
$data = ($block['type'] === $name) ? $block : null;
break;
}
return $data;
}
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\LibraryNewsResource\Pages;
use App\Filament\Resources\LibraryNewsResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListLibraryNews extends ListRecords
{
protected static string $resource = LibraryNewsResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}
+5 -1
View File
@@ -7,6 +7,7 @@ use App\Models\Event;
use App\Models\Page;
use App\Models\Post;
use App\Models\Slide;
use App\Models\Slider;
use Filament\Forms;
use Filament\Forms\Components\ColorPicker;
use Filament\Forms\Components\DateTimePicker;
@@ -92,7 +93,10 @@ class SlideResource extends Resource
}),
]),
]),
Forms\Components\Section::make('Слайдер')->schema([
Forms\Components\Section::make('Выбрать слайдер')->schema([
Forms\Components\Select::make('slider_id')->label('')->options(Slider::where('is_active', true)->pluck('title', 'id')),
]),
Forms\Components\Section::make('Тело слайда')->schema([
Forms\Components\Section::make('Информация слайда')->schema([
Forms\Components\TextInput::make('title')
+8 -1
View File
@@ -14,6 +14,7 @@ use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Illuminate\Support\Str;
class SliderResource extends Resource
{
@@ -27,7 +28,13 @@ class SliderResource extends Resource
->schema([
Forms\Components\Section::make()->schema([
Forms\Components\TextInput::make('title')
->label('Заголовок слайдера'),
->live(onBlur: true)
->afterStateUpdated(function (string $operation, string|null $state, Forms\Set $set) {
$set('slug', Str::slug($state));
})
->label('Заголовок слайдера')
->required(),
TextInput::make('slug')->label('Slug')->unique(ignoreRecord: true)->readOnly()->required(),
Toggle::make('is_active')->default(true)->label('Активный слайдер')->inline(false),
]),
]);
@@ -1,76 +0,0 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\VacantPositionResource\Pages;
use App\Filament\Resources\VacantPositionResource\RelationManagers;
use App\Models\VacantPosition;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class VacantPositionResource extends Resource
{
protected static ?string $model = VacantPosition::class;
protected static ?string $navigationGroup = 'Вакансии';
public static ?string $label = 'Вакансия института';
protected static ?string $pluralLabel = 'Вакансии института';
protected static ?string $navigationIcon = 'heroicon-o-briefcase';
public static function form(Form $form): Form
{
return $form
->schema([
Forms\Components\TextInput::make('department')->label('Стурктурное подразделение')->required(),
Forms\Components\TextInput::make('position')->label('Должность')->required(),
Forms\Components\TextInput::make('fraction')->integer()->maxValue(1)->step(0.1)->minValue(0)->label('Объем ставок')->required(),
Forms\Components\TextInput::make('salary')->integer()->required()->minValue(0)->label('Зарплата'),
Forms\Components\TextInput::make('notice')->label('Примечание'),
]);
}
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\ListVacantPositions::route('/'),
'create' => Pages\CreateVacantPosition::route('/create'),
'edit' => Pages\EditVacantPosition::route('/{record}/edit'),
];
}
}
@@ -1,12 +0,0 @@
<?php
namespace App\Filament\Resources\VacantPositionResource\Pages;
use App\Filament\Resources\VacantPositionResource;
use Filament\Actions;
use Filament\Resources\Pages\CreateRecord;
class CreateVacantPosition extends CreateRecord
{
protected static string $resource = VacantPositionResource::class;
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\VacantPositionResource\Pages;
use App\Filament\Resources\VacantPositionResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditVacantPosition extends EditRecord
{
protected static string $resource = VacantPositionResource::class;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\VacantPositionResource\Pages;
use App\Filament\Resources\VacantPositionResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListVacantPositions extends ListRecords
{
protected static string $resource = VacantPositionResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}
@@ -1,185 +0,0 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\VirtualExhibitionResource\Pages;
use App\Filament\Resources\VirtualExhibitionResource\RelationManagers;
use App\Models\VirtualExhibition;
use Filament\Forms;
use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Hidden;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Tabs;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Filament\Forms\Components\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Mohamedsabil83\FilamentFormsTinyeditor\Components\TinyEditor;
class VirtualExhibitionResource extends Resource
{
protected static ?string $model = VirtualExhibition::class;
protected static ?string $navigationGroup = 'Библиотека';
public static ?string $label = 'Виртуальная выставка';
protected static ?string $pluralLabel = 'Виртуальные выставки';
protected static ?string $navigationIcon = 'heroicon-o-cube-transparent';
public static function form(Form $form): Form
{
return $form
->schema([
Section::make()
->schema([
Tabs::make('Tabs')
->tabs([
Tabs\Tab::make('Основная информация')
->schema([
Forms\Components\Grid::make()->schema([
Forms\Components\TextInput::make('title')->required()->label('Заголовок'),
Forms\Components\TextInput::make('category')->required()->label('Категория (Необязательно)'),
]),
Forms\Components\Textarea::make('preview_text')->required()->label('Текст анонса')
->required()
->columnSpanFull(),
Forms\Components\Toggle::make('is_active')->required()->label('Активно')->default(true),
]),
Tabs\Tab::make('Содержание выставки')
->schema([
\Filament\Forms\Components\Builder::make('content')->label('')->blocks([
Builder\Block::make('heading')->label('Заголовок')
->schema([
TextInput::make('id')->hidden()->integer()->default(rand(2335235,324634264263426)),
TextInput::make('content')
->label('')
->live(onBlur: true)
->afterStateUpdated(function (string $operation, string $state, Forms\Set $set, $get) {
}),
]),
Builder\Block::make('paragraph')
->schema([
RichEditor::make('content')
->toolbarButtons([
'blockquote',
'bold',
'bulletList',
'italic',
'link',
'orderedList',
'redo',
'strike',
'underline',
'undo',
])
->label('')
->required()
]),
Builder\Block::make('image')
->schema([
FileUpload::make('url')
->label('Изображение(-я)')
->image()
->multiple()
->reorderable()
->maxFiles(5)
->disk('public')
->directory('images')
->imageEditor()
->required(),
TextInput::make('alt')
->label('Описание')
->placeholder('Необязяательно')
])->label('Изображение(-я)'),
Builder\Block::make('video')
->schema([
Hidden::make('mime'),
TextInput::make('title')
->required()
->maxLength(255)
->autofocus(),
FileUpload::make('path')
->required()
->acceptedFileTypes(['video/mp4','video/ogg','video/webm'])
->maxSize(512000)
->disk('videos')
->visibility('public')
->afterStateUpdated(fn (callable $set, $state) => $set('mime', $state?->getMimeType())),
]),
Builder\Block::make('files')
->schema([
TextInput::make('title')
->required()
->maxLength(255)
->autofocus(),
FileUpload::make('path')
->required()
->acceptedFileTypes([
'application/pdf',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'application/zip'
])
->maxSize(512000)
->disk('public')
->directory('files')
->downloadable()
->visibility('public')
])
])
->collapsed()
->blockNumbers(false)
->collapsible()
->addActionLabel('Добавить новый блок'),
]),
]),
]),
]);
}
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\ListVirtualExhibitions::route('/'),
'create' => Pages\CreateVirtualExhibition::route('/create'),
'edit' => Pages\EditVirtualExhibition::route('/{record}/edit'),
];
}
}
@@ -1,103 +0,0 @@
<?php
namespace App\Filament\Resources\VirtualExhibitionResource\Pages;
use App\Filament\Resources\VirtualExhibitionResource;
use Filament\Actions;
use Filament\Resources\Pages\CreateRecord;
use Illuminate\Support\Str;
class CreateVirtualExhibition extends CreateRecord
{
protected static string $resource = VirtualExhibitionResource::class;
protected array $seoData;
protected function mutateFormDataBeforeCreate(array $data): array
{
$this->seoData = $this->generateSeo($data);
$data['search_data'] = $this->generateSearchData($data['content']);
return $data;
}
protected function afterCreate(): void
{
$this->record->seo()->create($this->seoData);
}
private function generateSeo(array $data) : array
{
$title = $data['title'];
$rowData = $this->getFirstBlockByName('paragraph', $data['content']);
if ($rowData !== null) {
$description = strip_tags($rowData['data']['content']);
} else {
$description = null;
}
return [
'title' => $title,
'description' => Str::limit(htmlspecialchars($description, ENT_QUOTES, 'UTF-8'), 160),
];
}
private function generateSearchData(array $data) : string
{
$result = "";
foreach ($data as $block) {
$result .= $this->getDataFromBlocks($block);
}
// Удаляем лишние пробелы и переносы строк
$result = preg_replace('/\s+/', ' ', $result);
$result = trim($result);
return strtolower($result);
}
private function getFirstBlockByName(string $name, array $content) : array|null
{
$data = null;
foreach ($content as $block) {
$data = ($block['type'] === $name) ? $block : null;
break;
}
return $data;
}
private function getDataFromBlocks($block) : string
{
$data = "";
switch ($block['type']) {
case 'paragraph':
$data .= strip_tags($block['data']['content']) . " ";
break;
case 'heading':
$data .= strip_tags($block['data']['content']) . " ";
break;
case 'files':
foreach ($block['data']['file'] as $file) {
$data .= $file['title'] . " ";
}
break;
case 'person':
$data .= $block['data']['name'] . " ";
break;
case 'stepper':
$data .= $block['data']['step_name'] . " ";
foreach ($block['data']['steps'] as $step) {
$data .= $step['title'] . " ";
$data .= strip_tags($step['content']) . " ";
}
break;
case 'tabs':
foreach ($block['data']['tab'] as $item) {
foreach ($item['content'] as $block) {
$data .= $this->getDataFromBlocks($block);
};
};
break;
}
return $data;
}
}
@@ -1,129 +0,0 @@
<?php
namespace App\Filament\Resources\VirtualExhibitionResource\Pages;
use App\Filament\Resources\VirtualExhibitionResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
use Illuminate\Support\Str;
class EditVirtualExhibition extends EditRecord
{
protected static string $resource = VirtualExhibitionResource::class;
protected array $seoData;
protected function mutateFormDataBeforeSave(array $data): array
{
$this->seoData = $this->generateSeo($data);
$data['search_data'] = $this->generateSearchData($data['content']);
return $data;
}
protected function afterSave(): void
{
$this->record->seo()->update($this->seoData);
}
private function getBlockBySeoActiveState(string $name, array $content) : array|null
{
$data = [];
foreach ($content as $block) {
if ($block['type'] === $name) {
$data[] = $block;
}
}
$block = null;
foreach ($data as $item) {
if ($item['data']['seo_active'] === true) {
$block = $item;
}
}
return $block;
}
private function generateSeo(array $data) : array
{
$title = $data['title'];
$rowData = $this->getFirstBlockByName('paragraph', $data['content']);
if ($rowData !== null) {
$description = strip_tags($rowData['data']['content']);
} else {
$description = null;
}
return [
'title' => $title,
'description' => Str::limit(htmlspecialchars($description, ENT_QUOTES, 'UTF-8'), 160),
];
}
private function getDataFromBlocks($block) : string
{
$data = "";
switch ($block['type']) {
case 'paragraph':
$data .= strip_tags($block['data']['content']) . " ";
break;
case 'heading':
$data .= strip_tags($block['data']['content']) . " ";
break;
case 'files':
foreach ($block['data']['file'] as $file) {
$data .= $file['title'] . " ";
}
break;
case 'person':
$data .= $block['data']['name'] . " ";
break;
case 'stepper':
$data .= $block['data']['step_name'] . " ";
foreach ($block['data']['steps'] as $step) {
$data .= $step['title'] . " ";
$data .= strip_tags($step['content']) . " ";
}
break;
case 'tabs':
foreach ($block['data']['tab'] as $item) {
foreach ($item['content'] as $block) {
$data .= $this->getDataFromBlocks($block);
};
};
break;
}
return $data;
}
private function generateSearchData(array $data) : string
{
$result = "";
foreach ($data as $block) {
$result .= $this->getDataFromBlocks($block);
}
// Удаляем лишние пробелы и переносы строк
$result = preg_replace('/\s+/', ' ', $result);
$result = trim($result);
return strtolower($result);
}
private function getFirstBlockByName(string $name, array $content) : array|null
{
$data = null;
foreach ($content as $block) {
$data = ($block['type'] === $name) ? $block : null;
break;
}
return $data;
}
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
}
@@ -1,19 +0,0 @@
<?php
namespace App\Filament\Resources\VirtualExhibitionResource\Pages;
use App\Filament\Resources\VirtualExhibitionResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListVirtualExhibitions extends ListRecords
{
protected static string $resource = VirtualExhibitionResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}
+60 -32
View File
@@ -12,6 +12,7 @@ use App\Http\Resources\ClientNavigationResource;
use App\Models\Event;
use App\Models\EventCategory;
use App\Models\Page;
use App\Services\App\Breadcrumb\BreadcrumbService;
use Carbon\Carbon;
use DateTime;
use Illuminate\Http\Request;
@@ -19,58 +20,48 @@ use Inertia\Inertia;
class ClientEventController extends Controller
{
public function index(Request $request)
public function __construct(private readonly BreadcrumbService $breadcrumbService){}
public function index(Request $request): \Inertia\Response
{
$currentDate = $this->getCurrentDate($request);
$filters = $this->getFilters();
$eventDates = $this->getEventDates($filters); // Передаем фильтры
$eventDates = $this->getEventDates($filters);
$events = $this->getEvents($currentDate);
$categories = ClientEventCategoryResource::collection(EventCategory::has('events')->get());
$routeUrl = route('client.event.index');
$path = ltrim(parse_url($routeUrl, PHP_URL_PATH), '/');
$breadcrumbs = $this->breadcrumbService->generateBreadcrumbs('client.event.index');
$page = Page::where('path', '=', $path)->with('section.pages.section', 'section.mainSection')->first();
if (isset($page->section)) {
$breadcrumbs = [
'mainSection' => new ClientBreadcrumbSection($page->section->mainSection),
'subSection' => new ClientBreadcrumbSubSection($page->section),
'page' => new ClientBreadcrumbPage($page),
];
} else {
$breadcrumbs = null;
}
return Inertia::render('Client/Events/Index', compact('eventDates', 'events', 'currentDate', 'filters', 'categories', 'breadcrumbs'));
}
public function show(string $slug)
public function show(string $slug): \Inertia\Response
{
$event = new ClientEventFullResource(Event::where('slug', '=', $slug)->with('category')->first());
$routeUrl = route('client.event.index');
$path = ltrim(parse_url($routeUrl, PHP_URL_PATH), '/');
$page = Page::where('path', '=', $path)->with('section.pages.section', 'section.mainSection')->first();
if (isset($page->section)) {
$breadcrumbs = [
'mainSection' => new ClientBreadcrumbSection($page->section->mainSection),
'subSection' => new ClientBreadcrumbSubSection($page->section),
'page' => new ClientBreadcrumbPage($page),
];
} else {
$breadcrumbs = null;
}
$breadcrumbs = $this->breadcrumbService->generateBreadcrumbs('client.event.index');
$seo = $event->seo ?? null;
return Inertia::render('Client/Events/Show', compact('event', 'breadcrumbs', 'seo'));
}
public function archive(Request $request): \Inertia\Response
{
$filters = $this->getFilters();
$events = $this->getEventsArchive();
$categories = ClientEventCategoryResource::collection(EventCategory::has('events')->get());
$breadcrumbs = $this->breadcrumbService->generateBreadcrumbs('client.event.archive');
return Inertia::render('Client/Events/Archive', compact('events', 'filters', 'categories', 'breadcrumbs'));
}
private function getCurrentDate(Request $request): array
{
$dateInput = $request->input('date');
@@ -107,6 +98,9 @@ class ClientEventController extends Controller
->when(request()->input('is_online'), function ($query, $value) {
$this->applyOnlineFilter($query, $value);
})
->when(request()->input('search'), function ($query, $search) {
$query->whereRaw('LOWER(title) like ?', ["%".strtolower($search)."%"]);
})
->when(request()->input('category'), function ($query) {
$slugs = request()->input('category');
if (is_array($slugs)) {
@@ -119,6 +113,33 @@ class ClientEventController extends Controller
->get());
}
private function getEventsArchive()
{
return ClientEventResource::collection(Event::select('title', 'slug', 'event_date_start', 'event_time_start', 'address', 'is_online', 'category_id')
->whereDate('event_date_start', '<', now())
->with('category')
->when(request()->input('is_online'), function ($query, $value) {
$this->applyOnlineFilter($query, $value);
})
->when(request()->input('search'), function ($query, $search) {
$query->whereRaw('LOWER(title) like ?', ["%".strtolower($search)."%"]);
})
->when(request()->input('category'), function ($query) {
$slugs = request()->input('category');
if (is_array($slugs)) {
$query->whereHas('category', function ($query) use ($slugs) {
$query->whereIn('slug', $slugs);
});
}
})
->when(request()->input('sort', 'desc'), function ($query, $sort) {
$query->orderBy('event_date_start', $sort);
})
->orderBy('event_time_start', 'asc')
->paginate(6)
->withQueryString());
}
private function getEventDates(array $filters): \Illuminate\Support\Collection
{
// Получаем события с учетом фильтров
@@ -163,6 +184,8 @@ class ClientEventController extends Controller
->sortKeys() // Сортируем ключи по возрастанию
->values(); // Получаем массив без ключей
// Извлекаем уникальные даты из событий
return $mappingDates;
}
@@ -177,6 +200,11 @@ class ClientEventController extends Controller
}
return [
'search_filter' => [
'type' => 'search',
'value' => request()->input('search'),
'param' => 'search'
],
'category_filter' => [
'type' => 'category',
'value' => request()->input('category'),
@@ -231,7 +259,7 @@ class ClientEventController extends Controller
return $russianMonths[$month] ?? '';
}
private function applyOnlineFilter($query, $isOnline)
private function applyOnlineFilter($query, $isOnline): void
{
if ($isOnline === 'online') {
$query->where('is_online', true);
+5 -33
View File
@@ -17,6 +17,7 @@ use App\Models\MainSection;
use App\Models\Page;
use App\Models\Post;
use App\Models\Tag;
use App\Services\App\Breadcrumb\BreadcrumbService;
use Carbon\Carbon;
use Doctrine\DBAL\Schema\Column;
use Illuminate\Http\Request;
@@ -26,6 +27,8 @@ use Inertia\Inertia;
class ClientPostController extends Controller
{
public function __construct(private readonly BreadcrumbService $breadcrumbService){}
public function index(Request $request)
{
// Кешируем список тегов
@@ -129,23 +132,9 @@ class ClientPostController extends Controller
],
];
$routeUrl = route('client.post.index');
$path = ltrim(parse_url($routeUrl, PHP_URL_PATH), '/');
$breadcrumbs = $this->breadcrumbService->generateBreadcrumbs('client.post.index');
// Кешируем страницу
$page = Cache::remember('page_' . $path, now()->addHours(1), function () use ($path) {
return Page::where('path', '=', $path)->with('section.pages.section', 'section.mainSection')->first();
});
if (isset($page->section)) {
$breadcrumbs = [
'mainSection' => new ClientBreadcrumbSection($page->section->mainSection),
'subSection' => new ClientBreadcrumbSubSection($page->section),
'page' => new ClientBreadcrumbPage($page),
];
} else {
$breadcrumbs = null;
}
return Inertia::render('Client/Posts/Index', compact('filters', 'posts', 'categories', 'tags', 'breadcrumbs'));
}
@@ -165,24 +154,7 @@ class ClientPostController extends Controller
// Преобразуем пост в ресурс
$postResource = new PostResource($post);
// Получаем путь для страницы
$routeUrl = route('client.post.index');
$path = ltrim(parse_url($routeUrl, PHP_URL_PATH), '/');
// Получаем данные страницы
$page = Page::where('path', '=', $path)
->with('section.pages.section', 'section.mainSection')
->first();
// Формируем хлебные крошки
$breadcrumbs = null;
if (isset($page->section)) {
$breadcrumbs = [
'mainSection' => new ClientBreadcrumbSection($page->section->mainSection),
'subSection' => new ClientBreadcrumbSubSection($page->section),
'page' => new ClientBreadcrumbPage($page),
];
}
$breadcrumbs = $this->breadcrumbService->generateBreadcrumbs('client.post.index');
// SEO-данные
$seo = $post->seo ?? null;
@@ -45,6 +45,7 @@ class ClientProgramController extends Controller
DirectionStudy::query()
->withAdmissionCampaignByYear($activeCampaign->academic_year)
->withActivePrograms()
->with('programs.admission_plans')
->when($level, function ($query) use ($level) {
$query->where('lvl_edu', LevelEducational::fromName($level)->value);
})
@@ -149,12 +150,13 @@ class ClientProgramController extends Controller
{
$budgetValue = Str::of(BudgetEducation::fromName($budget)->value)->toString();
$query->whereHas('programs.admission_plans', function ($query) use ($budgetValue) {
$query->whereJsonContains('contests', [['places' => ['form_budget' => $budgetValue]]]);
$query->whereJsonContains('contests', ['financing_source' => $budgetValue]);
})
->with(['programs' => function ($query) use ($budgetValue) {
$query->whereHas('admission_plans', function ($query) use ($budgetValue) {
$query->whereJsonContains('contests', [['places' => ['form_budget' => $budgetValue]]]);
$query->whereJsonContains('contests', ['financing_source' => $budgetValue]);
});
}]);
}
@@ -0,0 +1,24 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Cache;
use App\Models\Slider;
class ClientWidgetSliderController extends Controller
{
public function show(string $slug): ?object
{
$cacheKey = 'slider_' . $slug;
return Cache::remember($cacheKey, now()->addHour(), function () use ($slug) {
$slider = Slider::query()
->where('slug', $slug)
->where('is_active', true)
->with('slides')
->first();
return $slider ?: null;
});
}
}
+10 -17
View File
@@ -43,21 +43,21 @@ class MainController extends Controller
// return $this->getAdmissionCampaign();
// });
$educations = Cache::remember('educations_data', now()->addHour(), function () {
return $this->getEducationsData();
});
// $educations = Cache::remember('educations_data', now()->addHour(), function () {
// return $this->getEducationsData();
// });
$sliders = Cache::remember('active_sliders', now()->addHour(), function () {
return $this->getActiveSliders();
});
$educations = $this->getEducationsData();
$posts = Cache::remember('recent_posts', now()->addHour(), function () {
return $this->getRecentPosts();
});
$events = Cache::remember('upcoming_events', now()->addHour(), function () {
return $this->getUpcomingEvents();
});
// $events = Cache::remember('upcoming_events', now()->addHour(), function () {
// return $this->getUpcomingEvents();
// });
$events = $this->getUpcomingEvents();
$path = route('index', null, false);
$page = Cache::remember('page_' . $path, now()->addHour(), function () use ($path) {
@@ -66,7 +66,7 @@ class MainController extends Controller
$seo = $page->seo ?? null;
return Inertia::render('Main', compact('posts', 'events', 'sliders', 'educations', 'seo'));
return Inertia::render('Main', compact('posts', 'events', 'educations', 'seo'));
}
private function getAdmissionCampaign()
@@ -99,13 +99,6 @@ class MainController extends Controller
];
}
private function getActiveSliders()
{
return (ClientMainSliderResource::collection(
MainSlider::where('is_active', true)
->orderBy('sort', 'asc')
->get()));
}
private function getRecentPosts()
{
@@ -7,9 +7,7 @@ use App\Http\Resources\EducationalProgramSearchResource;
use App\Http\Resources\EducationGroupSearchResource;
use App\Http\Resources\EventSearchResource;
use App\Http\Resources\FacultySearchResource;
use App\Http\Resources\PageResource;
use App\Http\Resources\PageSearchResource;
use App\Http\Resources\PostResource;
use App\Http\Resources\PostSearchResource;
use App\Http\Resources\UserSearchResource;
use App\Models\AdditionalEducation;
+9
View File
@@ -23,6 +23,15 @@ class Page extends Model
return $this->morphOne(Seo::class, 'seoable');
}
public function getBreadcrumbs(string $url) : array
{
$path = ltrim(parse_url(route($url), PHP_URL_PATH), '/');
$page = self::where('path', '=', $path)->with('section.pages.section', 'section.mainSection')->first();
dd($page);
}
protected $casts = [
'content' => 'array',
'settings' => 'array',
-108
View File
@@ -1,108 +0,0 @@
<?php
namespace App\Policies;
use App\Models\User;
use App\Models\ExternalVacancy;
use Illuminate\Auth\Access\HandlesAuthorization;
class ExternalVacancyPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_external::vacancy');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, ExternalVacancy $externalVacancy): bool
{
return $user->can('view_external::vacancy');
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->can('create_external::vacancy');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, ExternalVacancy $externalVacancy): bool
{
return $user->can('update_external::vacancy');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, ExternalVacancy $externalVacancy): bool
{
return $user->can('delete_external::vacancy');
}
/**
* Determine whether the user can bulk delete.
*/
public function deleteAny(User $user): bool
{
return $user->can('delete_any_external::vacancy');
}
/**
* Determine whether the user can permanently delete.
*/
public function forceDelete(User $user, ExternalVacancy $externalVacancy): bool
{
return $user->can('force_delete_external::vacancy');
}
/**
* Determine whether the user can permanently bulk delete.
*/
public function forceDeleteAny(User $user): bool
{
return $user->can('force_delete_any_external::vacancy');
}
/**
* Determine whether the user can restore.
*/
public function restore(User $user, ExternalVacancy $externalVacancy): bool
{
return $user->can('restore_external::vacancy');
}
/**
* Determine whether the user can bulk restore.
*/
public function restoreAny(User $user): bool
{
return $user->can('restore_any_external::vacancy');
}
/**
* Determine whether the user can replicate.
*/
public function replicate(User $user, ExternalVacancy $externalVacancy): bool
{
return $user->can('replicate_external::vacancy');
}
/**
* Determine whether the user can reorder.
*/
public function reorder(User $user): bool
{
return $user->can('reorder_external::vacancy');
}
}
@@ -3,10 +3,10 @@
namespace App\Policies;
use App\Models\User;
use App\Models\LibraryNews;
use App\Models\Slide;
use Illuminate\Auth\Access\HandlesAuthorization;
class LibraryNewsPolicy
class SlidePolicy
{
use HandlesAuthorization;
@@ -15,15 +15,15 @@ class LibraryNewsPolicy
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_library::news');
return $user->can('view_any_slide');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, LibraryNews $libraryNews): bool
public function view(User $user, Slide $slide): bool
{
return $user->can('view_library::news');
return $user->can('view_slide');
}
/**
@@ -31,23 +31,23 @@ class LibraryNewsPolicy
*/
public function create(User $user): bool
{
return $user->can('create_library::news');
return $user->can('create_slide');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, LibraryNews $libraryNews): bool
public function update(User $user, Slide $slide): bool
{
return $user->can('update_library::news');
return $user->can('update_slide');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, LibraryNews $libraryNews): bool
public function delete(User $user, Slide $slide): bool
{
return $user->can('delete_library::news');
return $user->can('delete_slide');
}
/**
@@ -55,15 +55,15 @@ class LibraryNewsPolicy
*/
public function deleteAny(User $user): bool
{
return $user->can('delete_any_library::news');
return $user->can('delete_any_slide');
}
/**
* Determine whether the user can permanently delete.
*/
public function forceDelete(User $user, LibraryNews $libraryNews): bool
public function forceDelete(User $user, Slide $slide): bool
{
return $user->can('force_delete_library::news');
return $user->can('force_delete_slide');
}
/**
@@ -71,15 +71,15 @@ class LibraryNewsPolicy
*/
public function forceDeleteAny(User $user): bool
{
return $user->can('force_delete_any_library::news');
return $user->can('force_delete_any_slide');
}
/**
* Determine whether the user can restore.
*/
public function restore(User $user, LibraryNews $libraryNews): bool
public function restore(User $user, Slide $slide): bool
{
return $user->can('restore_library::news');
return $user->can('restore_slide');
}
/**
@@ -87,15 +87,15 @@ class LibraryNewsPolicy
*/
public function restoreAny(User $user): bool
{
return $user->can('restore_any_library::news');
return $user->can('restore_any_slide');
}
/**
* Determine whether the user can replicate.
*/
public function replicate(User $user, LibraryNews $libraryNews): bool
public function replicate(User $user, Slide $slide): bool
{
return $user->can('replicate_library::news');
return $user->can('replicate_slide');
}
/**
@@ -103,6 +103,6 @@ class LibraryNewsPolicy
*/
public function reorder(User $user): bool
{
return $user->can('reorder_library::news');
return $user->can('reorder_slide');
}
}
@@ -3,10 +3,10 @@
namespace App\Policies;
use App\Models\User;
use App\Models\VacantPosition;
use App\Models\Slider;
use Illuminate\Auth\Access\HandlesAuthorization;
class VacantPositionPolicy
class SliderPolicy
{
use HandlesAuthorization;
@@ -15,15 +15,15 @@ class VacantPositionPolicy
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_vacant::position');
return $user->can('view_any_slider');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, VacantPosition $vacantPosition): bool
public function view(User $user, Slider $slider): bool
{
return $user->can('view_vacant::position');
return $user->can('view_slider');
}
/**
@@ -31,23 +31,23 @@ class VacantPositionPolicy
*/
public function create(User $user): bool
{
return $user->can('create_vacant::position');
return $user->can('create_slider');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, VacantPosition $vacantPosition): bool
public function update(User $user, Slider $slider): bool
{
return $user->can('update_vacant::position');
return $user->can('update_slider');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, VacantPosition $vacantPosition): bool
public function delete(User $user, Slider $slider): bool
{
return $user->can('delete_vacant::position');
return $user->can('delete_slider');
}
/**
@@ -55,15 +55,15 @@ class VacantPositionPolicy
*/
public function deleteAny(User $user): bool
{
return $user->can('delete_any_vacant::position');
return $user->can('delete_any_slider');
}
/**
* Determine whether the user can permanently delete.
*/
public function forceDelete(User $user, VacantPosition $vacantPosition): bool
public function forceDelete(User $user, Slider $slider): bool
{
return $user->can('force_delete_vacant::position');
return $user->can('force_delete_slider');
}
/**
@@ -71,15 +71,15 @@ class VacantPositionPolicy
*/
public function forceDeleteAny(User $user): bool
{
return $user->can('force_delete_any_vacant::position');
return $user->can('force_delete_any_slider');
}
/**
* Determine whether the user can restore.
*/
public function restore(User $user, VacantPosition $vacantPosition): bool
public function restore(User $user, Slider $slider): bool
{
return $user->can('restore_vacant::position');
return $user->can('restore_slider');
}
/**
@@ -87,15 +87,15 @@ class VacantPositionPolicy
*/
public function restoreAny(User $user): bool
{
return $user->can('restore_any_vacant::position');
return $user->can('restore_any_slider');
}
/**
* Determine whether the user can replicate.
*/
public function replicate(User $user, VacantPosition $vacantPosition): bool
public function replicate(User $user, Slider $slider): bool
{
return $user->can('replicate_vacant::position');
return $user->can('replicate_slider');
}
/**
@@ -103,6 +103,6 @@ class VacantPositionPolicy
*/
public function reorder(User $user): bool
{
return $user->can('reorder_vacant::position');
return $user->can('reorder_slider');
}
}
-108
View File
@@ -1,108 +0,0 @@
<?php
namespace App\Policies;
use App\Models\User;
use App\Models\VirtualExhibition;
use Illuminate\Auth\Access\HandlesAuthorization;
class VirtualExhibitionPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_virtual::exhibition');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, VirtualExhibition $virtualExhibition): bool
{
return $user->can('view_virtual::exhibition');
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->can('create_virtual::exhibition');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, VirtualExhibition $virtualExhibition): bool
{
return $user->can('update_virtual::exhibition');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, VirtualExhibition $virtualExhibition): bool
{
return $user->can('delete_virtual::exhibition');
}
/**
* Determine whether the user can bulk delete.
*/
public function deleteAny(User $user): bool
{
return $user->can('delete_any_virtual::exhibition');
}
/**
* Determine whether the user can permanently delete.
*/
public function forceDelete(User $user, VirtualExhibition $virtualExhibition): bool
{
return $user->can('force_delete_virtual::exhibition');
}
/**
* Determine whether the user can permanently bulk delete.
*/
public function forceDeleteAny(User $user): bool
{
return $user->can('force_delete_any_virtual::exhibition');
}
/**
* Determine whether the user can restore.
*/
public function restore(User $user, VirtualExhibition $virtualExhibition): bool
{
return $user->can('restore_virtual::exhibition');
}
/**
* Determine whether the user can bulk restore.
*/
public function restoreAny(User $user): bool
{
return $user->can('restore_any_virtual::exhibition');
}
/**
* Determine whether the user can replicate.
*/
public function replicate(User $user, VirtualExhibition $virtualExhibition): bool
{
return $user->can('replicate_virtual::exhibition');
}
/**
* Determine whether the user can reorder.
*/
public function reorder(User $user): bool
{
return $user->can('reorder_virtual::exhibition');
}
}
@@ -11,7 +11,6 @@ use Filament\Pages;
use Filament\Panel;
use Filament\PanelProvider;
use Filament\Support\Colors\Color;
use Filament\Widgets;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
@@ -20,7 +19,6 @@ use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
use ShuvroRoy\FilamentSpatieLaravelBackup\FilamentSpatieLaravelBackupPlugin;
use Vormkracht10\TwoFactorAuth\TwoFactorAuthPlugin;
class AdminPanelProvider extends PanelProvider
@@ -0,0 +1,40 @@
<?php
namespace App\Services\App\Breadcrumb;
use App\Http\Resources\ClientBreadcrumbPage;
use App\Http\Resources\ClientBreadcrumbSection;
use App\Http\Resources\ClientBreadcrumbSubSection;
use App\Models\Page;
use Illuminate\Support\Facades\Cache;
class BreadcrumbService
{
public function generateBreadcrumbs($routeName) : array|null
{
$path = $this->generatePath($routeName);
// Кешируем страницу
$page = Cache::remember('page_' . $path, now()->addHours(1), function () use ($path) {
return Page::where('path', '=', $path)->with('section.pages.section', 'section.mainSection')->first();
});
if (isset($page->section)) {
$breadcrumbs = [
'mainSection' => new ClientBreadcrumbSection($page->section->mainSection),
'subSection' => new ClientBreadcrumbSubSection($page->section),
'page' => new ClientBreadcrumbPage($page),
];
} else {
$breadcrumbs = null;
}
return $breadcrumbs;
}
private function generatePath($routeName) : string
{
$routeUrl = route($routeName);
return ltrim(parse_url($routeUrl, PHP_URL_PATH), '/');
}
}