Add some filament resources.
This commit is contained in:
parent
09bd1945b2
commit
2f34917652
26 changed files with 796 additions and 4 deletions
56
app/Filament/Resources/Fixtures/FixtureResource.php
Normal file
56
app/Filament/Resources/Fixtures/FixtureResource.php
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Fixtures;
|
||||
|
||||
use App\Filament\Resources\Fixtures\Pages\CreateFixture;
|
||||
use App\Filament\Resources\Fixtures\Pages\EditFixture;
|
||||
use App\Filament\Resources\Fixtures\Pages\ListFixtures;
|
||||
use App\Filament\Resources\Fixtures\Pages\ViewFixture;
|
||||
use App\Filament\Resources\Fixtures\Schemas\FixtureForm;
|
||||
use App\Filament\Resources\Fixtures\Schemas\FixtureInfolist;
|
||||
use App\Filament\Resources\Fixtures\Tables\FixturesTable;
|
||||
use App\Models\Fixture;
|
||||
use BackedEnum;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class FixtureResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Fixture::class;
|
||||
|
||||
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack;
|
||||
|
||||
public static function form(Schema $schema): Schema
|
||||
{
|
||||
return FixtureForm::configure($schema);
|
||||
}
|
||||
|
||||
public static function infolist(Schema $schema): Schema
|
||||
{
|
||||
return FixtureInfolist::configure($schema);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return FixturesTable::configure($table);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListFixtures::route('/'),
|
||||
'create' => CreateFixture::route('/create'),
|
||||
'view' => ViewFixture::route('/{record}'),
|
||||
'edit' => EditFixture::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
27
app/Filament/Resources/Fixtures/Pages/CreateFixture.php
Normal file
27
app/Filament/Resources/Fixtures/Pages/CreateFixture.php
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Fixtures\Pages;
|
||||
|
||||
use App\Enums\MatchStatus;
|
||||
use App\Filament\Resources\Fixtures\FixtureResource;
|
||||
use Carbon\Carbon;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateFixture extends CreateRecord
|
||||
{
|
||||
protected static string $resource = FixtureResource::class;
|
||||
|
||||
protected function mutateFormDataBeforeCreate(array $data): array
|
||||
{
|
||||
$startsAt = Carbon::parse($data['starts_at']);
|
||||
|
||||
$data['picks_lock_at'] = $startsAt->copy()->subMinutes(15);
|
||||
$data['status'] = MatchStatus::Scheduled->value;
|
||||
$data['home_score'] = null;
|
||||
$data['away_score'] = null;
|
||||
$data['result'] = null;
|
||||
$data['result_declared_at'] = null;
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
21
app/Filament/Resources/Fixtures/Pages/EditFixture.php
Normal file
21
app/Filament/Resources/Fixtures/Pages/EditFixture.php
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Fixtures\Pages;
|
||||
|
||||
use App\Filament\Resources\Fixtures\FixtureResource;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Actions\ViewAction;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditFixture extends EditRecord
|
||||
{
|
||||
protected static string $resource = FixtureResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
ViewAction::make(),
|
||||
DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
19
app/Filament/Resources/Fixtures/Pages/ListFixtures.php
Normal file
19
app/Filament/Resources/Fixtures/Pages/ListFixtures.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Fixtures\Pages;
|
||||
|
||||
use App\Filament\Resources\Fixtures\FixtureResource;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListFixtures extends ListRecords
|
||||
{
|
||||
protected static string $resource = FixtureResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
19
app/Filament/Resources/Fixtures/Pages/ViewFixture.php
Normal file
19
app/Filament/Resources/Fixtures/Pages/ViewFixture.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Fixtures\Pages;
|
||||
|
||||
use App\Filament\Resources\Fixtures\FixtureResource;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewFixture extends ViewRecord
|
||||
{
|
||||
protected static string $resource = FixtureResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
EditAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
48
app/Filament/Resources/Fixtures/Schemas/FixtureForm.php
Normal file
48
app/Filament/Resources/Fixtures/Schemas/FixtureForm.php
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Fixtures\Schemas;
|
||||
|
||||
use App\Enums\MatchOutcome;
|
||||
use App\Enums\MatchStatus;
|
||||
use Filament\Forms\Components\DateTimePicker;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Schemas\Schema;
|
||||
|
||||
class FixtureForm
|
||||
{
|
||||
public static function configure(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
Select::make('home_team_id')
|
||||
->relationship('homeTeam', 'name')
|
||||
->required(),
|
||||
Select::make('away_team_id')
|
||||
->relationship('awayTeam', 'name')
|
||||
->required(),
|
||||
TextInput::make('stage'),
|
||||
TextInput::make('venue'),
|
||||
DateTimePicker::make('starts_at')
|
||||
->required(),
|
||||
DateTimePicker::make('picks_lock_at')
|
||||
->hiddenOn('create'),
|
||||
Select::make('status')
|
||||
->options(MatchStatus::class)
|
||||
->default('scheduled')
|
||||
->hiddenOn('create')
|
||||
->required(),
|
||||
TextInput::make('home_score')
|
||||
->numeric()
|
||||
->hiddenOn('create'),
|
||||
TextInput::make('away_score')
|
||||
->numeric()
|
||||
->hiddenOn('create'),
|
||||
Select::make('result')
|
||||
->options(MatchOutcome::class)
|
||||
->hiddenOn('create'),
|
||||
DateTimePicker::make('result_declared_at')
|
||||
->hiddenOn('create'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
49
app/Filament/Resources/Fixtures/Schemas/FixtureInfolist.php
Normal file
49
app/Filament/Resources/Fixtures/Schemas/FixtureInfolist.php
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Fixtures\Schemas;
|
||||
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
use Filament\Schemas\Schema;
|
||||
|
||||
class FixtureInfolist
|
||||
{
|
||||
public static function configure(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
TextEntry::make('homeTeam.name')
|
||||
->label('Home team'),
|
||||
TextEntry::make('awayTeam.name')
|
||||
->label('Away team'),
|
||||
TextEntry::make('stage')
|
||||
->placeholder('-'),
|
||||
TextEntry::make('venue')
|
||||
->placeholder('-'),
|
||||
TextEntry::make('starts_at')
|
||||
->dateTime(),
|
||||
TextEntry::make('picks_lock_at')
|
||||
->dateTime()
|
||||
->placeholder('-'),
|
||||
TextEntry::make('status')
|
||||
->badge(),
|
||||
TextEntry::make('home_score')
|
||||
->numeric()
|
||||
->placeholder('-'),
|
||||
TextEntry::make('away_score')
|
||||
->numeric()
|
||||
->placeholder('-'),
|
||||
TextEntry::make('result')
|
||||
->badge()
|
||||
->placeholder('-'),
|
||||
TextEntry::make('result_declared_at')
|
||||
->dateTime()
|
||||
->placeholder('-'),
|
||||
TextEntry::make('created_at')
|
||||
->dateTime()
|
||||
->placeholder('-'),
|
||||
TextEntry::make('updated_at')
|
||||
->dateTime()
|
||||
->placeholder('-'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
69
app/Filament/Resources/Fixtures/Tables/FixturesTable.php
Normal file
69
app/Filament/Resources/Fixtures/Tables/FixturesTable.php
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Fixtures\Tables;
|
||||
|
||||
use Filament\Actions\BulkActionGroup;
|
||||
use Filament\Actions\DeleteBulkAction;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Actions\ViewAction;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class FixturesTable
|
||||
{
|
||||
public static function configure(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('homeTeam.name')
|
||||
->searchable(),
|
||||
TextColumn::make('awayTeam.name')
|
||||
->searchable(),
|
||||
TextColumn::make('stage')
|
||||
->searchable(),
|
||||
TextColumn::make('venue')
|
||||
->searchable(),
|
||||
TextColumn::make('starts_at')
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
TextColumn::make('picks_lock_at')
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
TextColumn::make('status')
|
||||
->badge()
|
||||
->searchable(),
|
||||
TextColumn::make('home_score')
|
||||
->numeric()
|
||||
->sortable(),
|
||||
TextColumn::make('away_score')
|
||||
->numeric()
|
||||
->sortable(),
|
||||
TextColumn::make('result')
|
||||
->badge()
|
||||
->searchable(),
|
||||
TextColumn::make('result_declared_at')
|
||||
->dateTime()
|
||||
->sortable(),
|
||||
TextColumn::make('created_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
TextColumn::make('updated_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
])
|
||||
->filters([
|
||||
//
|
||||
])
|
||||
->recordActions([
|
||||
ViewAction::make(),
|
||||
EditAction::make(),
|
||||
])
|
||||
->toolbarActions([
|
||||
BulkActionGroup::make([
|
||||
DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
11
app/Filament/Resources/Teams/Pages/CreateTeam.php
Normal file
11
app/Filament/Resources/Teams/Pages/CreateTeam.php
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Teams\Pages;
|
||||
|
||||
use App\Filament\Resources\Teams\TeamResource;
|
||||
use Filament\Resources\Pages\CreateRecord;
|
||||
|
||||
class CreateTeam extends CreateRecord
|
||||
{
|
||||
protected static string $resource = TeamResource::class;
|
||||
}
|
||||
21
app/Filament/Resources/Teams/Pages/EditTeam.php
Normal file
21
app/Filament/Resources/Teams/Pages/EditTeam.php
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Teams\Pages;
|
||||
|
||||
use App\Filament\Resources\Teams\TeamResource;
|
||||
use Filament\Actions\DeleteAction;
|
||||
use Filament\Actions\ViewAction;
|
||||
use Filament\Resources\Pages\EditRecord;
|
||||
|
||||
class EditTeam extends EditRecord
|
||||
{
|
||||
protected static string $resource = TeamResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
ViewAction::make(),
|
||||
DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
19
app/Filament/Resources/Teams/Pages/ListTeams.php
Normal file
19
app/Filament/Resources/Teams/Pages/ListTeams.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Teams\Pages;
|
||||
|
||||
use App\Filament\Resources\Teams\TeamResource;
|
||||
use Filament\Actions\CreateAction;
|
||||
use Filament\Resources\Pages\ListRecords;
|
||||
|
||||
class ListTeams extends ListRecords
|
||||
{
|
||||
protected static string $resource = TeamResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
19
app/Filament/Resources/Teams/Pages/ViewTeam.php
Normal file
19
app/Filament/Resources/Teams/Pages/ViewTeam.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Teams\Pages;
|
||||
|
||||
use App\Filament\Resources\Teams\TeamResource;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Resources\Pages\ViewRecord;
|
||||
|
||||
class ViewTeam extends ViewRecord
|
||||
{
|
||||
protected static string $resource = TeamResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
EditAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
31
app/Filament/Resources/Teams/Schemas/TeamForm.php
Normal file
31
app/Filament/Resources/Teams/Schemas/TeamForm.php
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Teams\Schemas;
|
||||
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Components\Toggle;
|
||||
use Filament\Schemas\Schema;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class TeamForm
|
||||
{
|
||||
public static function configure(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
TextInput::make('name')
|
||||
->required()
|
||||
->live(onBlur: true),
|
||||
TextInput::make('short_name'),
|
||||
TextInput::make('slug')
|
||||
->required()
|
||||
->hiddenOn('create')
|
||||
->dehydrateStateUsing(fn (?string $state): string => Str::slug((string) $state)),
|
||||
TextInput::make('fifa_code'),
|
||||
TextInput::make('flag_url')
|
||||
->url(),
|
||||
Toggle::make('is_active')
|
||||
->required(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
33
app/Filament/Resources/Teams/Schemas/TeamInfolist.php
Normal file
33
app/Filament/Resources/Teams/Schemas/TeamInfolist.php
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Teams\Schemas;
|
||||
|
||||
use Filament\Infolists\Components\IconEntry;
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
use Filament\Schemas\Schema;
|
||||
|
||||
class TeamInfolist
|
||||
{
|
||||
public static function configure(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->components([
|
||||
TextEntry::make('name'),
|
||||
TextEntry::make('short_name')
|
||||
->placeholder('-'),
|
||||
TextEntry::make('slug'),
|
||||
TextEntry::make('fifa_code')
|
||||
->placeholder('-'),
|
||||
TextEntry::make('flag_url')
|
||||
->placeholder('-'),
|
||||
IconEntry::make('is_active')
|
||||
->boolean(),
|
||||
TextEntry::make('created_at')
|
||||
->dateTime()
|
||||
->placeholder('-'),
|
||||
TextEntry::make('updated_at')
|
||||
->dateTime()
|
||||
->placeholder('-'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
53
app/Filament/Resources/Teams/Tables/TeamsTable.php
Normal file
53
app/Filament/Resources/Teams/Tables/TeamsTable.php
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Teams\Tables;
|
||||
|
||||
use Filament\Actions\BulkActionGroup;
|
||||
use Filament\Actions\DeleteBulkAction;
|
||||
use Filament\Actions\EditAction;
|
||||
use Filament\Actions\ViewAction;
|
||||
use Filament\Tables\Columns\IconColumn;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class TeamsTable
|
||||
{
|
||||
public static function configure(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->columns([
|
||||
TextColumn::make('name')
|
||||
->searchable(),
|
||||
TextColumn::make('short_name')
|
||||
->searchable(),
|
||||
TextColumn::make('slug')
|
||||
->searchable(),
|
||||
TextColumn::make('fifa_code')
|
||||
->searchable(),
|
||||
TextColumn::make('flag_url')
|
||||
->searchable(),
|
||||
IconColumn::make('is_active')
|
||||
->boolean(),
|
||||
TextColumn::make('created_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
TextColumn::make('updated_at')
|
||||
->dateTime()
|
||||
->sortable()
|
||||
->toggleable(isToggledHiddenByDefault: true),
|
||||
])
|
||||
->filters([
|
||||
//
|
||||
])
|
||||
->recordActions([
|
||||
ViewAction::make(),
|
||||
EditAction::make(),
|
||||
])
|
||||
->toolbarActions([
|
||||
BulkActionGroup::make([
|
||||
DeleteBulkAction::make(),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
56
app/Filament/Resources/Teams/TeamResource.php
Normal file
56
app/Filament/Resources/Teams/TeamResource.php
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Teams;
|
||||
|
||||
use App\Filament\Resources\Teams\Pages\CreateTeam;
|
||||
use App\Filament\Resources\Teams\Pages\EditTeam;
|
||||
use App\Filament\Resources\Teams\Pages\ListTeams;
|
||||
use App\Filament\Resources\Teams\Pages\ViewTeam;
|
||||
use App\Filament\Resources\Teams\Schemas\TeamForm;
|
||||
use App\Filament\Resources\Teams\Schemas\TeamInfolist;
|
||||
use App\Filament\Resources\Teams\Tables\TeamsTable;
|
||||
use App\Models\Team;
|
||||
use BackedEnum;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Filament\Tables\Table;
|
||||
|
||||
class TeamResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Team::class;
|
||||
|
||||
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack;
|
||||
|
||||
public static function form(Schema $schema): Schema
|
||||
{
|
||||
return TeamForm::configure($schema);
|
||||
}
|
||||
|
||||
public static function infolist(Schema $schema): Schema
|
||||
{
|
||||
return TeamInfolist::configure($schema);
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return TeamsTable::configure($table);
|
||||
}
|
||||
|
||||
public static function getRelations(): array
|
||||
{
|
||||
return [
|
||||
//
|
||||
];
|
||||
}
|
||||
|
||||
public static function getPages(): array
|
||||
{
|
||||
return [
|
||||
'index' => ListTeams::route('/'),
|
||||
'create' => CreateTeam::route('/create'),
|
||||
'view' => ViewTeam::route('/{record}'),
|
||||
'edit' => EditTeam::route('/{record}/edit'),
|
||||
];
|
||||
}
|
||||
}
|
||||
54
app/Filament/Widgets/UpcomingFixturesWidget.php
Normal file
54
app/Filament/Widgets/UpcomingFixturesWidget.php
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
namespace App\Filament\Widgets;
|
||||
|
||||
use App\Models\Fixture;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Filament\Widgets\TableWidget;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class UpcomingFixturesWidget extends TableWidget
|
||||
{
|
||||
protected int|string|array $columnSpan = 'full';
|
||||
|
||||
protected static ?int $sort = 2;
|
||||
|
||||
public function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
->heading('Próximos Partidos')
|
||||
->query($this->getTableQuery())
|
||||
->paginated(false)
|
||||
->columns([
|
||||
TextColumn::make('homeTeam.name')
|
||||
->label('Local')
|
||||
->weight('medium'),
|
||||
TextColumn::make('awayTeam.name')
|
||||
->label('Visitante')
|
||||
->weight('medium'),
|
||||
TextColumn::make('stage')
|
||||
->label('Etapa')
|
||||
->placeholder('-'),
|
||||
TextColumn::make('venue')
|
||||
->label('Estadio')
|
||||
->placeholder('-'),
|
||||
TextColumn::make('starts_at')
|
||||
->label('Empieza')
|
||||
->dateTime('M j, Y g:i A')
|
||||
->timezone('America/Mexico_City')
|
||||
->sortable(),
|
||||
])
|
||||
->defaultSort('starts_at')
|
||||
->recordUrl(null);
|
||||
}
|
||||
|
||||
protected function getTableQuery(): Builder
|
||||
{
|
||||
return Fixture::query()
|
||||
->with(['homeTeam', 'awayTeam'])
|
||||
->where('starts_at', '>=', now())
|
||||
->orderBy('starts_at')
|
||||
->limit(5);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ namespace App\Models;
|
|||
|
||||
use App\Enums\MatchOutcome;
|
||||
use App\Enums\MatchStatus;
|
||||
use Carbon\CarbonInterface;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
|
@ -26,6 +27,23 @@ class Fixture extends Model
|
|||
{
|
||||
protected $table = 'matches';
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::creating(function (self $fixture): void {
|
||||
if ($fixture->starts_at !== null && $fixture->picks_lock_at === null) {
|
||||
$startsAt = $fixture->starts_at instanceof CarbonInterface
|
||||
? $fixture->starts_at
|
||||
: now()->parse($fixture->starts_at);
|
||||
|
||||
$fixture->picks_lock_at = $startsAt->copy()->subMinutes(15);
|
||||
}
|
||||
|
||||
if ($fixture->status === null) {
|
||||
$fixture->status = MatchStatus::Scheduled;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace App\Models;
|
|||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
#[Fillable([
|
||||
'name',
|
||||
|
|
@ -16,6 +17,21 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
|||
])]
|
||||
class Team extends Model
|
||||
{
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::creating(function (self $team): void {
|
||||
if (blank($team->slug) && filled($team->name)) {
|
||||
$team->slug = Str::slug($team->name);
|
||||
}
|
||||
});
|
||||
|
||||
static::updating(function (self $team): void {
|
||||
if (blank($team->slug) && filled($team->name)) {
|
||||
$team->slug = Str::slug($team->name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ namespace App\Models;
|
|||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Database\Factories\UserFactory;
|
||||
use Filament\Models\Contracts\FilamentUser;
|
||||
use Filament\Panel;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Attributes\Hidden;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
|
|
@ -13,9 +15,9 @@ use Illuminate\Foundation\Auth\User as Authenticatable;
|
|||
use Illuminate\Notifications\Notifiable;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
|
||||
#[Fillable(['first_name', 'last_name', 'email', 'phone', 'password'])]
|
||||
#[Fillable(['first_name', 'last_name', 'email', 'phone', 'is_admin', 'password'])]
|
||||
#[Hidden(['password', 'remember_token'])]
|
||||
class User extends Authenticatable
|
||||
class User extends Authenticatable implements FilamentUser
|
||||
{
|
||||
/** @use HasFactory<UserFactory> */
|
||||
use HasApiTokens, HasFactory, Notifiable;
|
||||
|
|
@ -26,6 +28,7 @@ class User extends Authenticatable
|
|||
|
||||
'email',
|
||||
'phone',
|
||||
'is_admin',
|
||||
'password',
|
||||
];
|
||||
|
||||
|
|
@ -40,6 +43,7 @@ class User extends Authenticatable
|
|||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'is_admin' => 'boolean',
|
||||
'password' => 'hashed',
|
||||
];
|
||||
}
|
||||
|
|
@ -55,4 +59,9 @@ class User extends Authenticatable
|
|||
get: fn (): string => trim($this->first_name.' '.$this->last_name),
|
||||
);
|
||||
}
|
||||
|
||||
public function canAccessPanel(Panel $panel): bool
|
||||
{
|
||||
return $panel->getId() === 'admin' && $this->is_admin;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Providers\Filament;
|
||||
|
||||
use App\Filament\Widgets\UpcomingFixturesWidget;
|
||||
use Filament\Http\Middleware\Authenticate;
|
||||
use Filament\Http\Middleware\AuthenticateSession;
|
||||
use Filament\Http\Middleware\DisableBladeIconComponents;
|
||||
|
|
@ -38,8 +39,7 @@ class AdminPanelProvider extends PanelProvider
|
|||
])
|
||||
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\Filament\Widgets')
|
||||
->widgets([
|
||||
AccountWidget::class,
|
||||
FilamentInfoWidget::class,
|
||||
UpcomingFixturesWidget::class,
|
||||
])
|
||||
->middleware([
|
||||
EncryptCookies::class,
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ class UserFactory extends Factory
|
|||
'last_name' => fake()->lastName(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'phone' => fake()->phoneNumber(),
|
||||
'is_admin' => false,
|
||||
'email_verified_at' => now(),
|
||||
'password' => static::$password ??= Hash::make('password'),
|
||||
'remember_token' => Str::random(10),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->boolean('is_admin')->default(false)->after('phone');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('is_admin');
|
||||
});
|
||||
}
|
||||
};
|
||||
46
tests/Feature/FixtureDefaultsTest.php
Normal file
46
tests/Feature/FixtureDefaultsTest.php
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Enums\MatchStatus;
|
||||
use App\Models\Fixture;
|
||||
use App\Models\Team;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class FixtureDefaultsTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_fixture_creation_defaults_lock_time_and_status(): void
|
||||
{
|
||||
$homeTeam = Team::create([
|
||||
'name' => 'Mexico',
|
||||
'short_name' => 'Mexico',
|
||||
'slug' => 'mexico',
|
||||
'fifa_code' => 'MEX',
|
||||
]);
|
||||
|
||||
$awayTeam = Team::create([
|
||||
'name' => 'Canada',
|
||||
'short_name' => 'Canada',
|
||||
'slug' => 'canada',
|
||||
'fifa_code' => 'CAN',
|
||||
]);
|
||||
|
||||
$fixture = Fixture::create([
|
||||
'home_team_id' => $homeTeam->id,
|
||||
'away_team_id' => $awayTeam->id,
|
||||
'stage' => 'Group Stage',
|
||||
'venue' => 'Estadio BBVA',
|
||||
'starts_at' => '2026-06-11 20:00:00',
|
||||
]);
|
||||
|
||||
$this->assertSame('2026-06-11 19:45:00', $fixture->picks_lock_at?->format('Y-m-d H:i:s'));
|
||||
$this->assertSame(MatchStatus::Scheduled, $fixture->status);
|
||||
$this->assertNull($fixture->home_score);
|
||||
$this->assertNull($fixture->away_score);
|
||||
$this->assertNull($fixture->result);
|
||||
$this->assertNull($fixture->result_declared_at);
|
||||
}
|
||||
}
|
||||
24
tests/Feature/TeamSlugTest.php
Normal file
24
tests/Feature/TeamSlugTest.php
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\Team;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class TeamSlugTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_team_slug_is_generated_from_name_when_missing(): void
|
||||
{
|
||||
$team = Team::create([
|
||||
'name' => 'South Korea',
|
||||
'short_name' => 'Korea',
|
||||
'fifa_code' => 'KOR',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$this->assertSame('south-korea', $team->slug);
|
||||
}
|
||||
}
|
||||
45
tests/Unit/UserFilamentAccessTest.php
Normal file
45
tests/Unit/UserFilamentAccessTest.php
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Models\User;
|
||||
use Filament\Panel;
|
||||
use Mockery;
|
||||
use Tests\TestCase;
|
||||
|
||||
class UserFilamentAccessTest extends TestCase
|
||||
{
|
||||
public function test_admin_user_can_access_the_admin_panel(): void
|
||||
{
|
||||
$panel = Mockery::mock(Panel::class);
|
||||
$panel->shouldReceive('getId')->once()->andReturn('admin');
|
||||
|
||||
$user = new User([
|
||||
'first_name' => 'Admin',
|
||||
'last_name' => 'User',
|
||||
'email' => 'admin@example.com',
|
||||
'phone' => '8112345678',
|
||||
'is_admin' => true,
|
||||
'password' => 'password123',
|
||||
]);
|
||||
|
||||
$this->assertTrue($user->canAccessPanel($panel));
|
||||
}
|
||||
|
||||
public function test_non_admin_user_cannot_access_the_admin_panel(): void
|
||||
{
|
||||
$panel = Mockery::mock(Panel::class);
|
||||
$panel->shouldReceive('getId')->once()->andReturn('admin');
|
||||
|
||||
$user = new User([
|
||||
'first_name' => 'Regular',
|
||||
'last_name' => 'User',
|
||||
'email' => 'user@example.com',
|
||||
'phone' => '8112345678',
|
||||
'is_admin' => false,
|
||||
'password' => 'password123',
|
||||
]);
|
||||
|
||||
$this->assertFalse($user->canAccessPanel($panel));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue