From 710554a28d1089b5ae2d43a300a561094db7fb60 Mon Sep 17 00:00:00 2001 From: Nurmuhammet Allanov Date: Sat, 30 Aug 2025 17:21:47 +0500 Subject: [PATCH] Enhance Group and Document resources; add leader and helper teacher relationships, update navigation icons, and adjust navigation sorting. --- .../Resources/Documents/DocumentResource.php | 11 +- .../Resources/Groups/GroupResource.php | 5 +- .../Resources/Groups/Schemas/GroupForm.php | 15 ++ .../Resources/Groups/Tables/GroupsTable.php | 5 + .../Teachers/Pages/CreateTeacher.php | 11 ++ .../Resources/Teachers/Pages/EditTeacher.php | 19 ++ .../Resources/Teachers/Pages/ListTeachers.php | 19 ++ .../Teachers/Schemas/TeacherForm.php | 16 ++ .../Teachers/Tables/TeachersTable.php | 30 +++ .../Resources/Teachers/TeacherResource.php | 101 ++++++++++ app/Http/Requests/StoreTeacherRequest.php | 28 +++ app/Http/Requests/UpdateTeacherRequest.php | 28 +++ app/Models/Group.php | 13 +- app/Models/Teacher.php | 24 +++ app/Providers/Filament/PanelPanelProvider.php | 9 +- config/blade-icons.php | 183 ++++++++++++++++++ config/filament.php | 120 ++++++++++++ database/factories/TeacherFactory.php | 24 +++ ...025_08_30_164831_create_teachers_table.php | 30 +++ ..._add_leader_teacher_id_to_groups_table.php | 29 +++ ...8_30_164952_create_group_teacher_table.php | 29 +++ database/seeders/DatabaseSeeder.php | 3 +- database/seeders/TeacherSeeder.php | 18 ++ resources/svg/flight-takeoff.svg | 1 + 24 files changed, 760 insertions(+), 11 deletions(-) create mode 100644 app/Filament/Resources/Teachers/Pages/CreateTeacher.php create mode 100644 app/Filament/Resources/Teachers/Pages/EditTeacher.php create mode 100644 app/Filament/Resources/Teachers/Pages/ListTeachers.php create mode 100644 app/Filament/Resources/Teachers/Schemas/TeacherForm.php create mode 100644 app/Filament/Resources/Teachers/Tables/TeachersTable.php create mode 100644 app/Filament/Resources/Teachers/TeacherResource.php create mode 100644 app/Http/Requests/StoreTeacherRequest.php create mode 100644 app/Http/Requests/UpdateTeacherRequest.php create mode 100644 app/Models/Teacher.php create mode 100644 config/blade-icons.php create mode 100644 config/filament.php create mode 100644 database/factories/TeacherFactory.php create mode 100644 database/migrations/2025_08_30_164831_create_teachers_table.php create mode 100644 database/migrations/2025_08_30_164935_add_leader_teacher_id_to_groups_table.php create mode 100644 database/migrations/2025_08_30_164952_create_group_teacher_table.php create mode 100644 database/seeders/TeacherSeeder.php create mode 100644 resources/svg/flight-takeoff.svg diff --git a/app/Filament/Resources/Documents/DocumentResource.php b/app/Filament/Resources/Documents/DocumentResource.php index f8ee717..89dea7f 100644 --- a/app/Filament/Resources/Documents/DocumentResource.php +++ b/app/Filament/Resources/Documents/DocumentResource.php @@ -11,20 +11,23 @@ use App\Models\Document; use BackedEnum; use Filament\Resources\Resource; use Filament\Schemas\Schema; -use Filament\Support\Icons\Heroicon; use Filament\Tables\Table; +use Illuminate\Contracts\Support\Htmlable; class DocumentResource extends Resource { protected static ?string $model = Document::class; - protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack; - protected static ?string $navigationLabel = 'Resminamalar'; protected static ?string $pluralLabel = 'Resminamalar'; - protected static ?int $navigationSort = 3; + protected static ?int $navigationSort = 4; + + public static function getNavigationIcon(): string | BackedEnum | Htmlable | null + { + return 'heroicon-o-document-text'; + } public static function form(Schema $schema): Schema { diff --git a/app/Filament/Resources/Groups/GroupResource.php b/app/Filament/Resources/Groups/GroupResource.php index 257849c..5e986ab 100644 --- a/app/Filament/Resources/Groups/GroupResource.php +++ b/app/Filament/Resources/Groups/GroupResource.php @@ -12,7 +12,6 @@ use App\Models\Group; use BackedEnum; use Filament\Resources\Resource; use Filament\Schemas\Schema; -use Filament\Support\Icons\Heroicon; use Filament\Tables\Table; use Illuminate\Contracts\Support\Htmlable; @@ -20,8 +19,6 @@ class GroupResource extends Resource { protected static ?string $model = Group::class; - protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack; - protected static ?string $navigationLabel = 'Toparlar'; protected static ?string $pluralLabel = 'Toparlar'; @@ -32,7 +29,7 @@ class GroupResource extends Resource public static function getNavigationIcon(): string | BackedEnum | Htmlable | null { - return 'heroicon-o-user-group'; + return 'icon-flight-takeoff'; } public static function form(Schema $schema): Schema diff --git a/app/Filament/Resources/Groups/Schemas/GroupForm.php b/app/Filament/Resources/Groups/Schemas/GroupForm.php index aeedd5c..dd0ba4a 100644 --- a/app/Filament/Resources/Groups/Schemas/GroupForm.php +++ b/app/Filament/Resources/Groups/Schemas/GroupForm.php @@ -2,8 +2,10 @@ namespace App\Filament\Resources\Groups\Schemas; +use App\Models\Teacher; use Carbon\Carbon; use Filament\Forms\Components\DatePicker; +use Filament\Forms\Components\Select; use Filament\Schemas\Components\Utilities\Set; use Filament\Schemas\Schema; @@ -22,6 +24,19 @@ class GroupForm DatePicker::make('end_date') ->label('Gutarýan senesi') ->required(), + + Select::make('leader_teacher_id') + ->label('Topar başy') + ->options(Teacher::query()->pluck('name', 'id')) + ->searchable() + ->required(), + + Select::make('helperTeachers') + ->label('Topar kömekçileri') + ->relationship('helperTeachers', 'name') + ->multiple() + ->searchable() + ->preload(), ]); } } diff --git a/app/Filament/Resources/Groups/Tables/GroupsTable.php b/app/Filament/Resources/Groups/Tables/GroupsTable.php index d5234ec..8c1a879 100644 --- a/app/Filament/Resources/Groups/Tables/GroupsTable.php +++ b/app/Filament/Resources/Groups/Tables/GroupsTable.php @@ -14,6 +14,11 @@ class GroupsTable { return $table ->columns([ + TextColumn::make('leaderTeacher.name') + ->label('Topar başy') + ->searchable() + ->sortable(), + TextColumn::make('start_date') ->label('Başlanýan senesi') ->date() diff --git a/app/Filament/Resources/Teachers/Pages/CreateTeacher.php b/app/Filament/Resources/Teachers/Pages/CreateTeacher.php new file mode 100644 index 0000000..c70f984 --- /dev/null +++ b/app/Filament/Resources/Teachers/Pages/CreateTeacher.php @@ -0,0 +1,11 @@ +components([ + // + ]); + } +} diff --git a/app/Filament/Resources/Teachers/Tables/TeachersTable.php b/app/Filament/Resources/Teachers/Tables/TeachersTable.php new file mode 100644 index 0000000..3cc8ac7 --- /dev/null +++ b/app/Filament/Resources/Teachers/Tables/TeachersTable.php @@ -0,0 +1,30 @@ +columns([ + // + ]) + ->filters([ + // + ]) + ->recordActions([ + EditAction::make(), + ]) + ->toolbarActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ]), + ]); + } +} diff --git a/app/Filament/Resources/Teachers/TeacherResource.php b/app/Filament/Resources/Teachers/TeacherResource.php new file mode 100644 index 0000000..f3db799 --- /dev/null +++ b/app/Filament/Resources/Teachers/TeacherResource.php @@ -0,0 +1,101 @@ +schema([ + Forms\Components\TextInput::make('name') + ->required() + ->maxLength(255), + Forms\Components\FileUpload::make('photo') + ->image(), + Forms\Components\Textarea::make('bio') + ->columnSpanFull(), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\ImageColumn::make('photo') + ->label('Surat') + ->circular(), + + Tables\Columns\TextColumn::make('name') + ->label('Ady') + ->searchable(), + + Tables\Columns\TextColumn::make('created_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + + Tables\Columns\TextColumn::make('updated_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + // + ]) + ->recordActions([ + ActionsEditAction::make(), + ]) + ->bulkActions([ + BulkActionGroup::make([ + DeleteBulkAction::make(), + ]), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => ListTeachers::route('/'), + 'create' => CreateTeacher::route('/create'), + 'edit' => EditTeacher::route('/{record}/edit'), + ]; + } +} diff --git a/app/Http/Requests/StoreTeacherRequest.php b/app/Http/Requests/StoreTeacherRequest.php new file mode 100644 index 0000000..266e529 --- /dev/null +++ b/app/Http/Requests/StoreTeacherRequest.php @@ -0,0 +1,28 @@ +|string> + */ + public function rules(): array + { + return [ + // + ]; + } +} diff --git a/app/Http/Requests/UpdateTeacherRequest.php b/app/Http/Requests/UpdateTeacherRequest.php new file mode 100644 index 0000000..5437c3e --- /dev/null +++ b/app/Http/Requests/UpdateTeacherRequest.php @@ -0,0 +1,28 @@ +|string> + */ + public function rules(): array + { + return [ + // + ]; + } +} diff --git a/app/Models/Group.php b/app/Models/Group.php index 3038198..2898832 100644 --- a/app/Models/Group.php +++ b/app/Models/Group.php @@ -3,11 +3,12 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Factories\HasFactory; /** - * @property string $name * @property date $start_date * @property date $end_date * @property Pilgrim[] $pilgrims @@ -22,4 +23,14 @@ class Group extends Model { return $this->hasMany(Pilgrim::class); } + + public function leaderTeacher(): BelongsTo + { + return $this->belongsTo(Teacher::class, 'leader_teacher_id'); + } + + public function helperTeachers(): BelongsToMany + { + return $this->belongsToMany(Teacher::class, 'group_teacher'); + } } diff --git a/app/Models/Teacher.php b/app/Models/Teacher.php new file mode 100644 index 0000000..77089ca --- /dev/null +++ b/app/Models/Teacher.php @@ -0,0 +1,24 @@ + */ + use HasFactory; + + public function ledGroups(): HasMany + { + return $this->hasMany(Group::class, 'leader_teacher_id'); + } + + public function assistedGroups(): BelongsToMany + { + return $this->belongsToMany(Group::class, 'group_teacher'); + } +} diff --git a/app/Providers/Filament/PanelPanelProvider.php b/app/Providers/Filament/PanelPanelProvider.php index 4f006d0..307d832 100644 --- a/app/Providers/Filament/PanelPanelProvider.php +++ b/app/Providers/Filament/PanelPanelProvider.php @@ -39,7 +39,6 @@ class PanelPanelProvider extends PanelProvider ->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\Filament\Widgets') ->widgets([ AccountWidget::class, - FilamentInfoWidget::class, ]) ->middleware([ EncryptCookies::class, @@ -54,6 +53,14 @@ class PanelPanelProvider extends PanelProvider ]) ->authMiddleware([ Authenticate::class, + ]) + ->colors([ + 'danger' => Color::Rose, + 'gray' => Color::Gray, + 'info' => Color::Blue, + 'primary' => Color::Indigo, + 'success' => Color::Emerald, + 'warning' => Color::Orange, ]); } } diff --git a/config/blade-icons.php b/config/blade-icons.php new file mode 100644 index 0000000..8430486 --- /dev/null +++ b/config/blade-icons.php @@ -0,0 +1,183 @@ + [ + + 'default' => [ + + /* + |----------------------------------------------------------------- + | Icons Path + |----------------------------------------------------------------- + | + | Provide the relative path from your app root to your SVG icons + | directory. Icons are loaded recursively so there's no need to + | list every sub-directory. + | + | Relative to the disk root when the disk option is set. + | + */ + + 'path' => 'resources/svg', + + /* + |----------------------------------------------------------------- + | Filesystem Disk + |----------------------------------------------------------------- + | + | Optionally, provide a specific filesystem disk to read + | icons from. When defining a disk, the "path" option + | starts relatively from the disk root. + | + */ + + 'disk' => '', + + /* + |----------------------------------------------------------------- + | Default Prefix + |----------------------------------------------------------------- + | + | This config option allows you to define a default prefix for + | your icons. The dash separator will be applied automatically + | to every icon name. It's required and needs to be unique. + | + */ + + 'prefix' => 'icon', + + /* + |----------------------------------------------------------------- + | Fallback Icon + |----------------------------------------------------------------- + | + | This config option allows you to define a fallback + | icon when an icon in this set cannot be found. + | + */ + + 'fallback' => '', + + /* + |----------------------------------------------------------------- + | Default Set Classes + |----------------------------------------------------------------- + | + | This config option allows you to define some classes which + | will be applied by default to all icons within this set. + | + */ + + 'class' => '', + + /* + |----------------------------------------------------------------- + | Default Set Attributes + |----------------------------------------------------------------- + | + | This config option allows you to define some attributes which + | will be applied by default to all icons within this set. + | + */ + + 'attributes' => [ + // 'width' => 50, + // 'height' => 50, + ], + + ], + + ], + + /* + |-------------------------------------------------------------------------- + | Global Default Classes + |-------------------------------------------------------------------------- + | + | This config option allows you to define some classes which + | will be applied by default to all icons. + | + */ + + 'class' => '', + + /* + |-------------------------------------------------------------------------- + | Global Default Attributes + |-------------------------------------------------------------------------- + | + | This config option allows you to define some attributes which + | will be applied by default to all icons. + | + */ + + 'attributes' => [ + // 'width' => 50, + // 'height' => 50, + ], + + /* + |-------------------------------------------------------------------------- + | Global Fallback Icon + |-------------------------------------------------------------------------- + | + | This config option allows you to define a global fallback + | icon when an icon in any set cannot be found. It can + | reference any icon from any configured set. + | + */ + + 'fallback' => '', + + /* + |-------------------------------------------------------------------------- + | Components + |-------------------------------------------------------------------------- + | + | These config options allow you to define some + | settings related to Blade Components. + | + */ + + 'components' => [ + + /* + |---------------------------------------------------------------------- + | Disable Components + |---------------------------------------------------------------------- + | + | This config option allows you to disable Blade components + | completely. It's useful to avoid performance problems + | when working with large icon libraries. + | + */ + + 'disabled' => false, + + /* + |---------------------------------------------------------------------- + | Default Icon Component Name + |---------------------------------------------------------------------- + | + | This config option allows you to define the name + | for the default Icon class component. + | + */ + + 'default' => 'icon', + + ], + +]; diff --git a/config/filament.php b/config/filament.php new file mode 100644 index 0000000..0092bca --- /dev/null +++ b/config/filament.php @@ -0,0 +1,120 @@ + [ + + // 'echo' => [ + // 'broadcaster' => 'pusher', + // 'key' => env('VITE_PUSHER_APP_KEY'), + // 'cluster' => env('VITE_PUSHER_APP_CLUSTER'), + // 'wsHost' => env('VITE_PUSHER_HOST'), + // 'wsPort' => env('VITE_PUSHER_PORT'), + // 'wssPort' => env('VITE_PUSHER_PORT'), + // 'authEndpoint' => '/broadcasting/auth', + // 'disableStats' => true, + // 'encrypted' => true, + // 'forceTLS' => true, + // ], + + ], + + /* + |-------------------------------------------------------------------------- + | Default Filesystem Disk + |-------------------------------------------------------------------------- + | + | This is the storage disk Filament will use to store files. You may use + | any of the disks defined in the `config/filesystems.php`. + | + */ + + 'default_filesystem_disk' => env('FILESYSTEM_DISK', 'local'), + + /* + |-------------------------------------------------------------------------- + | Assets Path + |-------------------------------------------------------------------------- + | + | This is the directory where Filament's assets will be published to. It + | is relative to the `public` directory of your Laravel application. + | + | After changing the path, you should run `php artisan filament:assets`. + | + */ + + 'assets_path' => null, + + /* + |-------------------------------------------------------------------------- + | Cache Path + |-------------------------------------------------------------------------- + | + | This is the directory that Filament will use to store cache files that + | are used to optimize the registration of components. + | + | After changing the path, you should run `php artisan filament:cache-components`. + | + */ + + 'cache_path' => base_path('bootstrap/cache/filament'), + + /* + |-------------------------------------------------------------------------- + | Livewire Loading Delay + |-------------------------------------------------------------------------- + | + | This sets the delay before loading indicators appear. + | + | Setting this to 'none' makes indicators appear immediately, which can be + | desirable for high-latency connections. Setting it to 'default' applies + | Livewire's standard 200ms delay. + | + */ + + 'livewire_loading_delay' => 'default', + + /* + |-------------------------------------------------------------------------- + | File Generation + |-------------------------------------------------------------------------- + | + | Artisan commands that generate files can be configured here by setting + | configuration flags that will impact their location or content. + | + | Often, this is useful to preserve file generation behavior from a + | previous version of Filament, to ensure consistency between older and + | newer generated files. These flags are often documented in the upgrade + | guide for the version of Filament you are upgrading to. + | + */ + + 'file_generation' => [ + 'flags' => [], + ], + + /* + |-------------------------------------------------------------------------- + | System Route Prefix + |-------------------------------------------------------------------------- + | + | This is the prefix used for the system routes that Filament registers, + | such as the routes for downloading exports and failed import rows. + | + */ + + 'system_route_prefix' => 'filament', + +]; diff --git a/database/factories/TeacherFactory.php b/database/factories/TeacherFactory.php new file mode 100644 index 0000000..9cd3b92 --- /dev/null +++ b/database/factories/TeacherFactory.php @@ -0,0 +1,24 @@ + + */ +class TeacherFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'name' => $this->faker->name(), + 'bio' => $this->faker->paragraph(), + ]; + } +} diff --git a/database/migrations/2025_08_30_164831_create_teachers_table.php b/database/migrations/2025_08_30_164831_create_teachers_table.php new file mode 100644 index 0000000..12a7c97 --- /dev/null +++ b/database/migrations/2025_08_30_164831_create_teachers_table.php @@ -0,0 +1,30 @@ +id(); + $table->string('name'); + $table->string('photo')->nullable(); + $table->text('bio')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('teachers'); + } +}; diff --git a/database/migrations/2025_08_30_164935_add_leader_teacher_id_to_groups_table.php b/database/migrations/2025_08_30_164935_add_leader_teacher_id_to_groups_table.php new file mode 100644 index 0000000..b1dd986 --- /dev/null +++ b/database/migrations/2025_08_30_164935_add_leader_teacher_id_to_groups_table.php @@ -0,0 +1,29 @@ +foreignId('leader_teacher_id')->nullable()->constrained('teachers')->nullOnDelete(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('groups', function (Blueprint $table) { + $table->dropForeign(['leader_teacher_id']); + $table->dropColumn('leader_teacher_id'); + }); + } +}; diff --git a/database/migrations/2025_08_30_164952_create_group_teacher_table.php b/database/migrations/2025_08_30_164952_create_group_teacher_table.php new file mode 100644 index 0000000..8653d92 --- /dev/null +++ b/database/migrations/2025_08_30_164952_create_group_teacher_table.php @@ -0,0 +1,29 @@ +id(); + $table->foreignId('group_id')->constrained()->cascadeOnDelete(); + $table->foreignId('teacher_id')->constrained()->cascadeOnDelete(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('group_teacher'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index c6286d0..a8b7758 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -11,8 +11,9 @@ class DatabaseSeeder extends Seeder */ public function run(): void { + // User::factory(10)->create(); $this->call([ - UserTableSeeder::class, + TeacherSeeder::class, ]); } } diff --git a/database/seeders/TeacherSeeder.php b/database/seeders/TeacherSeeder.php new file mode 100644 index 0000000..4dde49a --- /dev/null +++ b/database/seeders/TeacherSeeder.php @@ -0,0 +1,18 @@ +create(); + } +} diff --git a/resources/svg/flight-takeoff.svg b/resources/svg/flight-takeoff.svg new file mode 100644 index 0000000..8af324d --- /dev/null +++ b/resources/svg/flight-takeoff.svg @@ -0,0 +1 @@ + \ No newline at end of file