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(),
];
}
}