No Participants Yet
-Waiting for race data...
+WAITING FOR DRIVERS
+Race data will appear here
diff --git a/src/app/Filament/Resources/ParticipantResource.php b/src/app/Filament/Resources/ParticipantResource.php index 30631f1..3f613d9 100644 --- a/src/app/Filament/Resources/ParticipantResource.php +++ b/src/app/Filament/Resources/ParticipantResource.php @@ -29,30 +29,34 @@ public static function form(Form $form): Form Forms\Components\TextInput::make('name') ->required() ->maxLength(255) - ->placeholder('Enter participant name'), - Forms\Components\TextInput::make('car_number') - ->label('Car Number') + ->placeholder('Driver name'), + Forms\Components\TextInput::make('number') + ->label('Number') ->numeric() ->minValue(1) ->maxValue(999) ->placeholder('e.g., 44'), - ])->columns(2), + Forms\Components\Select::make('status') + ->options(Participant::statuses()) + ->default('ready') + ->required(), + ])->columns(3), - Forms\Components\Section::make('Timing') + Forms\Components\Section::make('Best Time') ->schema([ - Forms\Components\Grid::make(4) + Forms\Components\Grid::make(3) ->schema([ Forms\Components\TextInput::make('time_minutes') ->label('Minutes') ->numeric() ->minValue(0) ->maxValue(59) - ->default(0) - ->placeholder('00') + ->default(null) + ->placeholder('MM') ->dehydrated(false) ->afterStateHydrated(function ($component, $state, $record) { - if ($record && $record->time_ms) { - $component->state(floor($record->time_ms / 60000)); + if ($record && $record->best_time_ms) { + $component->state(floor($record->best_time_ms / 60000)); } }), Forms\Components\TextInput::make('time_seconds') @@ -60,12 +64,12 @@ public static function form(Form $form): Form ->numeric() ->minValue(0) ->maxValue(59) - ->default(0) - ->placeholder('00') + ->default(null) + ->placeholder('SS') ->dehydrated(false) ->afterStateHydrated(function ($component, $state, $record) { - if ($record && $record->time_ms) { - $component->state(floor(($record->time_ms % 60000) / 1000)); + if ($record && $record->best_time_ms) { + $component->state(floor(($record->best_time_ms % 60000) / 1000)); } }), Forms\Components\TextInput::make('time_milliseconds') @@ -73,26 +77,18 @@ public static function form(Form $form): Form ->numeric() ->minValue(0) ->maxValue(999) - ->default(0) - ->placeholder('000') + ->default(null) + ->placeholder('ms') ->dehydrated(false) ->afterStateHydrated(function ($component, $state, $record) { - if ($record && $record->time_ms) { - $component->state($record->time_ms % 1000); + if ($record && $record->best_time_ms) { + $component->state($record->best_time_ms % 1000); } }), - Forms\Components\Select::make('status') - ->options([ - 'pending' => 'Pending', - 'completed' => 'Completed', - 'dnf' => 'DNF', - ]) - ->default('pending') - ->required(), ]), - Forms\Components\Hidden::make('time_ms'), + Forms\Components\Hidden::make('best_time_ms'), ]) - ->description('Enter time as MM:SS.ms (e.g., 01:23.456)'), + ->description('Enter best lap time as MM:SS.ms (leave blank if no time set)'), ]); } @@ -107,7 +103,7 @@ public static function table(Table $table): Table }) ->alignCenter() ->weight('bold'), - Tables\Columns\TextColumn::make('car_number') + Tables\Columns\TextColumn::make('number') ->label('#') ->alignCenter() ->placeholder('-') @@ -117,29 +113,28 @@ public static function table(Table $table): Table ->sortable() ->weight('bold'), Tables\Columns\TextColumn::make('formatted_time') - ->label('Time') - ->placeholder('No time') + ->label('Best Time') + ->placeholder('—') ->fontFamily('mono') ->alignCenter(), Tables\Columns\BadgeColumn::make('status') + ->formatStateUsing(fn (string $state): string => Participant::statuses()[$state] ?? $state) ->colors([ - 'warning' => 'pending', - 'success' => 'completed', + 'gray' => 'ready', + 'info' => 'running', + 'success' => 'finished', + 'warning' => 'pit', 'danger' => 'dnf', ]), Tables\Columns\TextColumn::make('updated_at') - ->label('Last Update') - ->dateTime('M j, H:i') + ->label('Updated') + ->dateTime('H:i:s') ->sortable(), ]) - ->defaultSort('time_ms', 'asc') + ->defaultSort('best_time_ms', 'asc') ->filters([ Tables\Filters\SelectFilter::make('status') - ->options([ - 'pending' => 'Pending', - 'completed' => 'Completed', - 'dnf' => 'DNF', - ]), + ->options(Participant::statuses()), ]) ->actions([ Tables\Actions\EditAction::make(), @@ -168,24 +163,24 @@ public static function getPages(): array public static function mutateFormDataBeforeCreate(array $data): array { - return static::calculateTimeMs($data); + return static::calculateBestTimeMs($data); } public static function mutateFormDataBeforeSave(array $data): array { - return static::calculateTimeMs($data); + return static::calculateBestTimeMs($data); } - protected static function calculateTimeMs(array $data): array + protected static function calculateBestTimeMs(array $data): array { - $minutes = (int) ($data['time_minutes'] ?? 0); - $seconds = (int) ($data['time_seconds'] ?? 0); - $milliseconds = (int) ($data['time_milliseconds'] ?? 0); + $minutes = $data['time_minutes'] ?? null; + $seconds = $data['time_seconds'] ?? null; + $milliseconds = $data['time_milliseconds'] ?? null; - if ($minutes > 0 || $seconds > 0 || $milliseconds > 0) { - $data['time_ms'] = ($minutes * 60000) + ($seconds * 1000) + $milliseconds; + if ($minutes !== null || $seconds !== null || $milliseconds !== null) { + $data['best_time_ms'] = ((int)$minutes * 60000) + ((int)$seconds * 1000) + (int)$milliseconds; } else { - $data['time_ms'] = null; + $data['best_time_ms'] = null; } return $data; diff --git a/src/app/Filament/Resources/ParticipantResource/Pages/CreateParticipant.php b/src/app/Filament/Resources/ParticipantResource/Pages/CreateParticipant.php index a8aef90..dfd308a 100644 --- a/src/app/Filament/Resources/ParticipantResource/Pages/CreateParticipant.php +++ b/src/app/Filament/Resources/ParticipantResource/Pages/CreateParticipant.php @@ -11,14 +11,14 @@ class CreateParticipant extends CreateRecord protected function mutateFormDataBeforeCreate(array $data): array { - $minutes = (int) ($data['time_minutes'] ?? 0); - $seconds = (int) ($data['time_seconds'] ?? 0); - $milliseconds = (int) ($data['time_milliseconds'] ?? 0); + $minutes = $data['time_minutes'] ?? null; + $seconds = $data['time_seconds'] ?? null; + $milliseconds = $data['time_milliseconds'] ?? null; - if ($minutes > 0 || $seconds > 0 || $milliseconds > 0) { - $data['time_ms'] = ($minutes * 60000) + ($seconds * 1000) + $milliseconds; + if ($minutes !== null || $seconds !== null || $milliseconds !== null) { + $data['best_time_ms'] = ((int)$minutes * 60000) + ((int)$seconds * 1000) + (int)$milliseconds; } else { - $data['time_ms'] = null; + $data['best_time_ms'] = null; } unset($data['time_minutes'], $data['time_seconds'], $data['time_milliseconds']); diff --git a/src/app/Filament/Resources/ParticipantResource/Pages/EditParticipant.php b/src/app/Filament/Resources/ParticipantResource/Pages/EditParticipant.php index 4b4dbaa..b37d689 100644 --- a/src/app/Filament/Resources/ParticipantResource/Pages/EditParticipant.php +++ b/src/app/Filament/Resources/ParticipantResource/Pages/EditParticipant.php @@ -19,14 +19,14 @@ protected function getHeaderActions(): array protected function mutateFormDataBeforeSave(array $data): array { - $minutes = (int) ($data['time_minutes'] ?? 0); - $seconds = (int) ($data['time_seconds'] ?? 0); - $milliseconds = (int) ($data['time_milliseconds'] ?? 0); + $minutes = $data['time_minutes'] ?? null; + $seconds = $data['time_seconds'] ?? null; + $milliseconds = $data['time_milliseconds'] ?? null; - if ($minutes > 0 || $seconds > 0 || $milliseconds > 0) { - $data['time_ms'] = ($minutes * 60000) + ($seconds * 1000) + $milliseconds; + if ($minutes !== null || $seconds !== null || $milliseconds !== null) { + $data['best_time_ms'] = ((int)$minutes * 60000) + ((int)$seconds * 1000) + (int)$milliseconds; } else { - $data['time_ms'] = null; + $data['best_time_ms'] = null; } unset($data['time_minutes'], $data['time_seconds'], $data['time_milliseconds']); diff --git a/src/app/Models/Participant.php b/src/app/Models/Participant.php index 2cab5d6..3004703 100644 --- a/src/app/Models/Participant.php +++ b/src/app/Models/Participant.php @@ -11,26 +11,40 @@ class Participant extends Model protected $fillable = [ 'name', - 'car_number', - 'time_ms', + 'number', + 'best_time_ms', 'status', ]; protected $casts = [ - 'car_number' => 'integer', - 'time_ms' => 'integer', + 'number' => 'integer', + 'best_time_ms' => 'integer', ]; - /** - * Format time_ms as mm:ss.ms (e.g., 01:23.456) - */ + const STATUS_READY = 'ready'; + const STATUS_RUNNING = 'running'; + const STATUS_FINISHED = 'finished'; + const STATUS_PIT = 'pit'; + const STATUS_DNF = 'dnf'; + + public static function statuses(): array + { + return [ + self::STATUS_READY => 'READY', + self::STATUS_RUNNING => 'RUNNING', + self::STATUS_FINISHED => 'FINISHED', + self::STATUS_PIT => 'IN PIT', + self::STATUS_DNF => 'DNF', + ]; + } + public function getFormattedTimeAttribute(): ?string { - if ($this->time_ms === null) { + if ($this->best_time_ms === null) { return null; } - $totalMs = $this->time_ms; + $totalMs = $this->best_time_ms; $minutes = floor($totalMs / 60000); $seconds = floor(($totalMs % 60000) / 1000); $milliseconds = $totalMs % 1000; @@ -38,20 +52,14 @@ public function getFormattedTimeAttribute(): ?string return sprintf('%02d:%02d.%03d', $minutes, $seconds, $milliseconds); } - /** - * Scope to get ranked participants (fastest first, null times last) - */ - public function scopeRanked($query) + public function getStatusLabelAttribute(): string { - return $query->orderByRaw('CASE WHEN time_ms IS NULL THEN 1 ELSE 0 END') - ->orderBy('time_ms', 'asc'); + return self::statuses()[$this->status] ?? strtoupper($this->status); } - /** - * Scope to get only completed participants - */ - public function scopeCompleted($query) + public function scopeRanked($query) { - return $query->where('status', 'completed'); + return $query->orderByRaw('CASE WHEN best_time_ms IS NULL THEN 1 ELSE 0 END') + ->orderBy('best_time_ms', 'asc'); } } diff --git a/src/database/migrations/2026_03_25_000001_create_participants_table.php b/src/database/migrations/2026_03_25_000001_create_participants_table.php index 512294b..4d99be3 100644 --- a/src/database/migrations/2026_03_25_000001_create_participants_table.php +++ b/src/database/migrations/2026_03_25_000001_create_participants_table.php @@ -11,9 +11,9 @@ public function up(): void Schema::create('participants', function (Blueprint $table) { $table->id(); $table->string('name'); - $table->integer('car_number')->nullable(); - $table->integer('time_ms')->nullable(); // Time in milliseconds for accurate sorting - $table->string('status')->default('pending'); // pending, completed, dnf + $table->integer('number')->nullable(); + $table->integer('best_time_ms')->nullable(); // Best lap time in milliseconds + $table->string('status')->default('ready'); // ready, running, finished, pit, dnf $table->timestamps(); }); } diff --git a/src/resources/views/leaderboard.blade.php b/src/resources/views/leaderboard.blade.php index c33dcca..7bf2d26 100644 --- a/src/resources/views/leaderboard.blade.php +++ b/src/resources/views/leaderboard.blade.php @@ -4,9 +4,9 @@ -
Race Results
+ + + +Waiting for race data...
+Race data will appear here