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('Выберите группу для которой создается расписание'), Toggle::make('is_zaoch') ->label('Форма обучения') ->inline(false) ->onColor('success') ->offColor('primary') ->helperText('Очная | Заочная') ->afterStateHydrated(function (Toggle $component, $state) { $component->state((bool) $state); }), ]), ]), 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(5120) // 5MB ->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() ->defaultItems(1), ]), ]); } public static function table(Table $table): Table { return $table ->columns([ TextColumn::make('educational_group.title') ->label('Учебная группа') ->sortable() ->searchable(), TextColumn::make('file_count') ->label('Файлов') ->getStateUsing(fn ($record) => count($record->file ?? [])) ->badge(), IconColumn::make('is_zaoch') ->label('Форма обучения') ->boolean() ->trueIcon('heroicon-o-academic-cap') ->falseIcon('heroicon-o-building-office') ->trueColor('success') ->falseColor('primary'), 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('Добавить расписание'), ]) ->defaultSort('educational_group.title'); } 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'), ]; } }