StreakFit — API Laravel 13 OSDD + Octane/FrankenPHP
Build & Deploy / build (push) Successful in 18s

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-19 03:00:17 +02:00
co-authored by Claude Opus 4.8
commit af32669931
300 changed files with 21235 additions and 0 deletions
@@ -0,0 +1,56 @@
<?php
namespace App\Filament\Resources\Exercises;
use App\Filament\Resources\Exercises\Pages\ListExercises;
use App\Filament\Resources\Exercises\Schemas\ExerciseInfolist;
use App\Filament\Resources\Exercises\Tables\ExercisesTable;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
use Functional\Exercises\Models\Exercise;
use UnitEnum;
/**
* Catalogue d'exercices consultation en lecture seule (1324 lignes).
* Filtrable par body_part / equipment / target, recherche sur le nom.
*/
class ExerciseResource extends Resource
{
protected static ?string $model = Exercise::class;
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack;
protected static string|UnitEnum|null $navigationGroup = 'Catalogue';
protected static ?string $modelLabel = 'exercice';
protected static ?string $pluralModelLabel = 'exercices';
protected static ?string $recordTitleAttribute = 'name';
public static function infolist(Schema $schema): Schema
{
return ExerciseInfolist::configure($schema);
}
public static function table(Table $table): Table
{
return ExercisesTable::configure($table);
}
// Catalogue en lecture seule : pas de création / édition / suppression.
public static function canCreate(): bool
{
return false;
}
public static function getPages(): array
{
return [
'index' => ListExercises::route('/'),
];
}
}
@@ -0,0 +1,17 @@
<?php
namespace App\Filament\Resources\Exercises\Pages;
use App\Filament\Resources\Exercises\ExerciseResource;
use Filament\Resources\Pages\ListRecords;
class ListExercises extends ListRecords
{
protected static string $resource = ExerciseResource::class;
// Catalogue en lecture seule : aucune action de création.
protected function getHeaderActions(): array
{
return [];
}
}
@@ -0,0 +1,46 @@
<?php
namespace App\Filament\Resources\Exercises\Schemas;
use Filament\Infolists\Components\ImageEntry;
use Filament\Infolists\Components\TextEntry;
use Filament\Schemas\Schema;
/**
* Fiche de consultation d'un exercice (modal "Voir").
* Affiche les métadonnées et les instructions du dataset (lecture seule).
*/
class ExerciseInfolist
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
ImageEntry::make('image_url')
->label('Aperçu')
->height(160)
->square(),
TextEntry::make('name')
->label('Nom'),
TextEntry::make('category')
->label('Catégorie')
->badge(),
TextEntry::make('body_part')
->label('Partie du corps')
->badge(),
TextEntry::make('equipment')
->label('Équipement')
->badge(),
TextEntry::make('target')
->label('Muscle cible')
->badge(),
TextEntry::make('muscle_group')
->label('Groupe musculaire'),
TextEntry::make('external_id')
->label('Identifiant externe'),
TextEntry::make('attribution')
->label('Attribution')
->placeholder('—'),
]);
}
}
@@ -0,0 +1,70 @@
<?php
namespace App\Filament\Resources\Exercises\Tables;
use Filament\Actions\ViewAction;
use Filament\Tables\Columns\ImageColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Table;
use Functional\Exercises\Models\Exercise;
/**
* Table du catalogue d'exercices : thumbnail + métadonnées, filtrable et consultable.
*/
class ExercisesTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
ImageColumn::make('image_url')
->label('Aperçu')
->square(),
TextColumn::make('name')
->label('Nom')
->searchable()
->sortable(),
TextColumn::make('body_part')
->label('Partie du corps')
->badge()
->sortable(),
TextColumn::make('equipment')
->label('Équipement')
->badge(),
TextColumn::make('target')
->label('Muscle cible')
->badge(),
])
->filters([
SelectFilter::make('body_part')
->label('Partie du corps')
->options(fn (): array => self::distinctOptions('body_part')),
SelectFilter::make('equipment')
->label('Équipement')
->options(fn (): array => self::distinctOptions('equipment')),
SelectFilter::make('target')
->label('Muscle cible')
->options(fn (): array => self::distinctOptions('target')),
])
->recordActions([
ViewAction::make(),
])
->defaultSort('name');
}
/**
* Valeurs distinctes d'une colonne pour alimenter un filtre (clé = valeur = libellé brut).
*
* @return array<string, string>
*/
private static function distinctOptions(string $column): array
{
return Exercise::query()
->whereNotNull($column)
->distinct()
->orderBy($column)
->pluck($column, $column)
->all();
}
}