schema([ Section::make('Основные настройки') ->description('Основная информация о расписании') ->collapsible() ->schema([ Grid::make(2) ->schema([ Select::make('educational_group_id') ->label('Учебная группа') ->options(EducationalGroup::query()->orderBy('title')->pluck('title', 'id')) ->searchable() ->preload() ->required() ->live() ->helperText('Выберите группу для которой создается расписание'), ]), ]), Section::make('Файлы расписания') ->description('Загрузите файлы с расписанием') ->collapsible() ->schema([ Forms\Components\Repeater::make('file') ->label('') ->addActionLabel('Добавить файл расписания') ->schema([ TextInput::make('title') ->label('Название файла') ->required() ->maxLength(255) ->placeholder('Например: "Расписание на весенний семестр 2024"') ->helperText('Укажите понятное название файла для идентификации'), FileUpload::make('path') ->label('Файл PDF') ->required() ->acceptedFileTypes(['application/pdf']) ->maxSize(10000) ->disk('public') ->directory('schedules') ->downloadable() ->openable() ->previewable(false) ->helperText('Только PDF файлы, макс. размер 5MB') ->getUploadedFileNameForStorageUsing( fn (TemporaryUploadedFile $file): string => str(Str::slug(pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME) . '-' . Carbon::now()->timestamp) . '.' . $file->getClientOriginalExtension() ) ) ->afterStateUpdated(function ($set, $state) { $set('title', pathinfo($state?->getClientOriginalName(), PATHINFO_FILENAME)); }) ->visibility('public'), ]) ->itemLabel(fn (array $state): ?string => $state['title'] ?? 'Новый файл') ->collapsible() ->cloneable() ->maxItems(1) ->defaultItems(1), ]), ]); } public static function table(Table $table): Table { return $table ->defaultGroup( Group::make('educationalGroup.title') ->titlePrefixedWithLabel(false) ->collapsible() ->getTitleFromRecordUsing(function ($record): string { $groupTitle = $record->educationalGroup->title; return 'Группа: ' . $groupTitle; }) ) ->columns([ TextColumn::make('title') ->label('Название') ->getStateUsing(fn ($record) => $record->file[0]['title']), TextColumn::make('educationalGroup.education_form_id') ->formatStateUsing(fn ($state) => FormEducation::tryFrom($state)->getLabel()) ->color(fn ($state) => FormEducation::tryFrom($state)?->getColor()) ->badge() ->label('Форма обучения'), TextColumn::make('updated_at') ->label('Обновлено') ->dateTime('d.m.Y H:i') ->sortable(), ]) ->filters([ Tables\Filters\SelectFilter::make('educational_group_id') ->label('Учебная группа') ->options(EducationalGroup::query()->orderBy('title')->pluck('title', 'id')) ->searchable(), Tables\Filters\TernaryFilter::make('is_zaoch') ->label('Форма обучения') ->placeholder('Все') ->trueLabel('Заочная') ->falseLabel('Очная'), ]) ->actions([ Tables\Actions\EditAction::make() ->iconButton() ->tooltip('Редактировать'), Tables\Actions\ViewAction::make() ->iconButton() ->tooltip('Просмотреть'), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make() ->label('Удалить выбранные') ->modalHeading('Удаление расписаний') ->modalDescription('Вы уверены, что хотите удалить выбранные расписания? Это действие нельзя отменить.'), ]), ]) ->emptyStateActions([ Tables\Actions\CreateAction::make() ->label('Добавить расписание'), ]); } public static function getRelations(): array { return []; } public static function getPages(): array { return [ 'index' => Pages\ListSchedules::route('/'), 'create' => Pages\CreateSchedule::route('/create'), 'edit' => Pages\EditSchedule::route('/{record}/edit'), 'upload' => Pages\ScheduleResource\Pages\UploadSchedules::route('/upload'), ]; } }