diff --git a/app/Concerns/BelongsToElection.php b/app/Concerns/BelongsToElection.php new file mode 100644 index 0000000..556829d --- /dev/null +++ b/app/Concerns/BelongsToElection.php @@ -0,0 +1,40 @@ +check()) { + return; + } + + if (! Filament::hasTenancy()) { + return; + } + + $model->election()->associate(Filament::getTenant()); + }); + + static::addGlobalScope(new BelongsToElectionScope); + } + + public function election(): BelongsTo + { + return $this->belongsTo(Election::class); + } +} diff --git a/app/Filament/Resources/ElectionResource.php b/app/Filament/Resources/ElectionResource.php new file mode 100644 index 0000000..de73f5f --- /dev/null +++ b/app/Filament/Resources/ElectionResource.php @@ -0,0 +1,196 @@ +schema([ + Forms\Components\Section::make() + ->schema([ + Select::make('type') + ->label(__('admin.field.type')) + ->relationship('type', 'name') + ->required(), + + TextInput::make('title') + ->label(__('admin.field.title')) + ->required(), + + TextInput::make('subtitle') + ->label(__('admin.field.subtitle')) + ->nullable(), + + /* + * @see https://dev.mysql.com/doc/refman/8.4/en/year.html Documentation for the YEAR data type + */ + TextInput::make('year') + ->label(__('admin.field.year')) + ->minValue(1901) + ->maxValue(2155) + ->numeric() + ->default(today()->year) + ->required(), + + Toggle::make('is_live') + ->label(__('admin.field.is_live')) + ->default(false), + + ]), + ]); + } + + public static function infolist(Infolist $infolist): Infolist + { + return $infolist + ->columns(3) + ->schema([ + Infolists\Components\Section::make() + ->columnSpan(2) + ->columns(2) + ->schema([ + TextEntry::make('title') + ->label(__('admin.field.title')), + + TextEntry::make('subtitle') + ->label(__('admin.field.subtitle')), + + TextEntry::make('year') + ->label(__('admin.field.year')), + + IconEntry::make('is_live') + ->label(__('admin.field.is_live')) + ->boolean(), + ]), + + Infolists\Components\Section::make() + ->columnSpan(1) + ->schema([ + TextEntry::make('created_at') + ->label(__('admin.field.created_at')) + ->dateTime(), + + TextEntry::make('updated_at') + ->label(__('admin.field.updated_at')) + ->dateTime(), + ]), + + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + TextColumn::make('id') + ->label(__('admin.field.id')) + ->sortable() + ->shrink(), + + TextColumn::make('type.name') + ->label(__('admin.field.type')) + ->sortable(), + + TextColumn::make('title') + ->label(__('admin.field.title')) + ->searchable() + ->sortable() + ->description(fn (Election $record) => $record->subtitle), + + TextColumn::make('year') + ->label(__('admin.field.year')) + ->sortable(), + + IconColumn::make('is_live') + ->label(__('admin.field.is_live')) + ->boolean(), + ]) + ->filters([ + SelectFilter::make('type') + ->label(__('admin.field.type')) + ->relationship('type', 'name'), + ]) + ->filtersLayout(FiltersLayout::AboveContent) + ->actions([ + Tables\Actions\ViewAction::make() + ->iconButton(), + + Tables\Actions\EditAction::make() + ->iconButton(), + ]) + ->defaultSort('id', 'desc'); + } + + public static function getRecordSubNavigation(Page $page): array + { + return []; + if ($page instanceof EditRecord) { + return []; + } + + return $page->generateNavigationItems([ + Pages\ViewElection::class, + Pages\EditElection::class, + // Pages\ElectionRounds\ManageElectionRounds::class, + ]); + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListElections::route('/'), + 'create' => Pages\CreateElection::route('/create'), + 'view' => Pages\ViewElection::route('/{record}'), + 'edit' => Pages\EditElection::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Resources/ElectionResource/Pages/CreateElection.php b/app/Filament/Resources/ElectionResource/Pages/CreateElection.php new file mode 100644 index 0000000..981a863 --- /dev/null +++ b/app/Filament/Resources/ElectionResource/Pages/CreateElection.php @@ -0,0 +1,13 @@ +getRecord()->title; + } + + public function getSubheading(): ?string + { + return $this->getRecord()->subtitle; + } +} diff --git a/app/Models/Election.php b/app/Models/Election.php new file mode 100644 index 0000000..f8274bd --- /dev/null +++ b/app/Models/Election.php @@ -0,0 +1,32 @@ + */ + use HasFactory; + + protected static string $factory = ElectionFactory::class; + + protected $fillable = [ + 'title', + 'subtitle', + 'year', + 'is_live', + ]; + + protected function casts(): array + { + return [ + 'year' => 'int', + 'is_live' => 'boolean', + ]; + } +} diff --git a/app/Models/Scopes/BelongsToElectionScope.php b/app/Models/Scopes/BelongsToElectionScope.php new file mode 100644 index 0000000..dd880ae --- /dev/null +++ b/app/Models/Scopes/BelongsToElectionScope.php @@ -0,0 +1,29 @@ +check()) { + return; + } + + if (! Filament::hasTenancy()) { + return; + } + + $builder->whereBelongsTo(Filament::getTenant()); + } +} diff --git a/app/Providers/Filament/AdminPanelProvider.php b/app/Providers/Filament/AdminPanelProvider.php index 73f62e1..b24e791 100644 --- a/app/Providers/Filament/AdminPanelProvider.php +++ b/app/Providers/Filament/AdminPanelProvider.php @@ -75,7 +75,8 @@ public function panel(Panel $panel): Panel ->authMiddleware([ Authenticate::class, ]) - ->sidebarCollapsibleOnDesktop(); + ->sidebarCollapsibleOnDesktop() + ->collapsibleNavigationGroups(false); } public function register(): void diff --git a/composer.json b/composer.json index 58fbe95..52eeac2 100644 --- a/composer.json +++ b/composer.json @@ -11,7 +11,6 @@ "php": "^8.2", "filament/filament": "^3.2", "filament/spatie-laravel-media-library-plugin": "^3.2", - "guava/filament-nested-resources": "^1.2", "jeffgreco13/filament-breezy": "^2.4", "laravel/framework": "^11.20", "laravel/tinker": "^2.9", diff --git a/composer.lock b/composer.lock index 44dac29..d93bda7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6fa4f0272d6df289d7720f7f747caa13", + "content-hash": "11892da806ece7d1d17df92f76d96bb0", "packages": [ { "name": "anourvalar/eloquent-serialize", @@ -1921,65 +1921,6 @@ ], "time": "2024-07-20T21:45:45+00:00" }, - { - "name": "guava/filament-nested-resources", - "version": "1.2.0", - "source": { - "type": "git", - "url": "https://github.com/GuavaCZ/filament-nested-resources.git", - "reference": "83dc0559b5abfb0880b9a33f143f6f7a5b55dcfb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/GuavaCZ/filament-nested-resources/zipball/83dc0559b5abfb0880b9a33f143f6f7a5b55dcfb", - "reference": "83dc0559b5abfb0880b9a33f143f6f7a5b55dcfb", - "shasum": "" - }, - "require": { - "filament/filament": "^3.0.0", - "livewire/livewire": "^3.0", - "spatie/laravel-package-tools": "^1.15" - }, - "require-dev": { - "filament/spatie-laravel-translatable-plugin": "^3.2", - "laravel/pint": "^1.11", - "orchestra/testbench": "^8.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Guava\\FilamentNestedResources\\NestedResourcesServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Guava\\FilamentNestedResources\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Lukas Frey", - "email": "lukas.frey@guava.cz" - } - ], - "support": { - "issues": "https://github.com/GuavaCZ/filament-nested-resources/issues", - "source": "https://github.com/GuavaCZ/filament-nested-resources/tree/1.2.0" - }, - "funding": [ - { - "url": "https://github.com/GuavaCZ", - "type": "github" - } - ], - "time": "2024-06-27T06:50:21+00:00" - }, { "name": "guzzlehttp/guzzle", "version": "7.9.2",