diff --git a/app/Enums/PacePreference.php b/app/Enums/PacePreference.php index 61da46a..dd76877 100644 --- a/app/Enums/PacePreference.php +++ b/app/Enums/PacePreference.php @@ -2,9 +2,20 @@ namespace App\Enums; -enum PacePreference: string +use Filament\Support\Contracts\HasLabel; + +enum PacePreference: string implements HasLabel { case SLOW = 'slow'; case NORMAL = 'normal'; case FAST = 'fast'; + + public function getLabel(): string + { + return match ($this) { + self::SLOW => 'Lent', + self::NORMAL => 'Normal', + self::FAST => 'Rapide', + }; + } } diff --git a/app/Enums/PhysicalActivityLevel.php b/app/Enums/PhysicalActivityLevel.php index e2f6292..f547f16 100644 --- a/app/Enums/PhysicalActivityLevel.php +++ b/app/Enums/PhysicalActivityLevel.php @@ -2,11 +2,24 @@ namespace App\Enums; -enum PhysicalActivityLevel: string +use Filament\Support\Contracts\HasLabel; + +enum PhysicalActivityLevel: string implements HasLabel { case SEDENTARY = 'sedentary'; case LIGHTLY_ACTIVE = 'lightly_active'; case MODERATELY_ACTIVE = 'moderately_active'; case VERY_ACTIVE = 'very_active'; case ATHLETE = 'athlete'; + + public function getLabel(): string + { + return match ($this) { + self::SEDENTARY => 'Sédentaire', + self::LIGHTLY_ACTIVE => 'Légèrement actif', + self::MODERATELY_ACTIVE => 'Modérément actif', + self::VERY_ACTIVE => 'Très actif', + self::ATHLETE => 'Athlète', + }; + } } diff --git a/app/Enums/UserSex.php b/app/Enums/UserSex.php index a208a10..c5409de 100644 --- a/app/Enums/UserSex.php +++ b/app/Enums/UserSex.php @@ -2,10 +2,22 @@ namespace App\Enums; -enum UserSex: string +use Filament\Support\Contracts\HasLabel; + +enum UserSex: string implements HasLabel { case MAN = 'man'; case WOMAN = 'woman'; case OTHER = 'other'; case UNKNOWN = 'unknown'; + + public function getLabel(): string + { + return match ($this) { + self::MAN => 'Homme', + self::WOMAN => 'Femme', + self::OTHER => 'Autre', + self::UNKNOWN => 'Non renseigné', + }; + } } diff --git a/app/Enums/WeightGoal.php b/app/Enums/WeightGoal.php index b93c0f8..6e9754e 100644 --- a/app/Enums/WeightGoal.php +++ b/app/Enums/WeightGoal.php @@ -2,9 +2,20 @@ namespace App\Enums; -enum WeightGoal: string +use Filament\Support\Contracts\HasLabel; + +enum WeightGoal: string implements HasLabel { case LOSE_WEIGHT = 'lose_weight'; case MAINTAIN_WEIGHT = 'maintain_weight'; case GAIN_WEIGHT = 'gain_weight'; + + public function getLabel(): string + { + return match ($this) { + self::LOSE_WEIGHT => 'Perdre du poids', + self::MAINTAIN_WEIGHT => 'Maintenir le poids', + self::GAIN_WEIGHT => 'Prendre du poids', + }; + } } diff --git a/app/Filament/Resources/Users/Schemas/UserForm.php b/app/Filament/Resources/Users/Schemas/UserForm.php index db8d04b..6e31784 100644 --- a/app/Filament/Resources/Users/Schemas/UserForm.php +++ b/app/Filament/Resources/Users/Schemas/UserForm.php @@ -2,10 +2,19 @@ namespace App\Filament\Resources\Users\Schemas; +use App\Enums\PacePreference; +use App\Enums\PhysicalActivityLevel; +use App\Enums\UserSex; +use App\Enums\WeightGoal; +use Filament\Forms\Components\DatePicker; use Filament\Forms\Components\DateTimePicker; use Filament\Forms\Components\FileUpload; +use Filament\Forms\Components\Select; +use Filament\Forms\Components\Textarea; use Filament\Forms\Components\TextInput; +use Filament\Schemas\Components\Section; use Filament\Schemas\Schema; +use Illuminate\Validation\Rules\Password; class UserForm { @@ -13,21 +22,118 @@ class UserForm { return $schema ->components([ - TextInput::make('name') - ->required(), - TextInput::make('email') - ->label('Email address') - ->email() - ->required(), - FileUpload::make('avatar_url') - ->label('Avatar') - ->image() - ->disk('public') - ->directory('avatars'), - DateTimePicker::make('email_verified_at'), - TextInput::make('password') - ->password() - ->required(), + Section::make('Compte') + ->columns(2) + ->schema([ + TextInput::make('name') + ->label('Nom') + ->required() + ->maxLength(255), + TextInput::make('email') + ->label('Adresse email') + ->email() + ->required() + ->unique(ignoreRecord: true) + ->maxLength(255), + FileUpload::make('avatar_url') + ->label('Avatar') + ->image() + ->disk('public') + ->directory('avatars') + ->visibility('public') + ->maxSize(2048), + TextInput::make('password') + ->label('Mot de passe') + ->password() + ->revealable() + ->rule(Password::default()) + ->required(fn (string $operation): bool => $operation === 'create') + ->formatStateUsing(fn (): ?string => null) + ->dehydrated(fn ($state): bool => filled($state)) + ->autocomplete('new-password'), + DateTimePicker::make('email_verified_at') + ->label('Email vérifié le'), + DateTimePicker::make('account_verified_at') + ->label('Compte vérifié le'), + ]), + Section::make('Profil') + ->columns(2) + ->schema([ + Textarea::make('bio') + ->label('Bio') + ->rows(4) + ->columnSpanFull(), + TextInput::make('height') + ->label('Taille') + ->integer() + ->minValue(0) + ->maxValue(300) + ->suffix('cm'), + DatePicker::make('date_of_birth') + ->label('Date de naissance') + ->minDate('1900-01-01') + ->maxDate(now()->subDay()), + Select::make('sex') + ->label('Sexe') + ->options(UserSex::class) + ->default(UserSex::UNKNOWN->value) + ->required(), + ]), + Section::make('Objectifs nutritionnels') + ->columns(4) + ->schema([ + TextInput::make('daily_calorie_goal') + ->label('Calories') + ->integer() + ->minValue(0) + ->maxValue(100000) + ->suffix('kcal') + ->default(2000) + ->required(), + TextInput::make('daily_protein_goal') + ->label('Protéines') + ->numeric() + ->minValue(0) + ->maxValue(10000) + ->suffix('g') + ->default(120) + ->required(), + TextInput::make('daily_carbs_goal') + ->label('Glucides') + ->numeric() + ->minValue(0) + ->maxValue(10000) + ->suffix('g') + ->default(250) + ->required(), + TextInput::make('daily_fats_goal') + ->label('Lipides') + ->numeric() + ->minValue(0) + ->maxValue(10000) + ->suffix('g') + ->default(70) + ->required(), + ]), + Section::make('Activité') + ->columns(3) + ->schema([ + Select::make('physical_activity_level') + ->label('Niveau d’activité') + ->options(PhysicalActivityLevel::class) + ->default(PhysicalActivityLevel::SEDENTARY->value) + ->required(), + Select::make('weight_goal') + ->label('Objectif de poids') + ->options(WeightGoal::class) + ->default(WeightGoal::LOSE_WEIGHT->value) + ->required(), + Select::make('pace_preference') + ->label('Rythme') + ->options(PacePreference::class) + ->default(PacePreference::SLOW->value) + ->required(), + ]), ]); } } diff --git a/app/Filament/Resources/Users/Schemas/UserInfolist.php b/app/Filament/Resources/Users/Schemas/UserInfolist.php index 9733bbb..a4200fc 100644 --- a/app/Filament/Resources/Users/Schemas/UserInfolist.php +++ b/app/Filament/Resources/Users/Schemas/UserInfolist.php @@ -4,6 +4,7 @@ namespace App\Filament\Resources\Users\Schemas; use Filament\Infolists\Components\ImageEntry; use Filament\Infolists\Components\TextEntry; +use Filament\Schemas\Components\Section; use Filament\Schemas\Schema; class UserInfolist @@ -12,21 +13,96 @@ class UserInfolist { return $schema ->components([ - TextEntry::make('name'), - TextEntry::make('email') - ->label('Email address'), - TextEntry::make('email_verified_at') - ->dateTime() - ->placeholder('-'), - ImageEntry::make('avatar_url') - ->label('Avatar') - ->placeholder('-'), - TextEntry::make('created_at') - ->dateTime() - ->placeholder('-'), - TextEntry::make('updated_at') - ->dateTime() - ->placeholder('-'), + Section::make('Compte') + ->columns(3) + ->schema([ + TextEntry::make('id') + ->label('ID') + ->copyable(), + TextEntry::make('name') + ->label('Nom'), + TextEntry::make('email') + ->label('Adresse email') + ->copyable(), + TextEntry::make('email_verified_at') + ->label('Email vérifié le') + ->dateTime() + ->placeholder('Non vérifié'), + TextEntry::make('account_verified_at') + ->label('Compte vérifié le') + ->dateTime() + ->placeholder('Non vérifié'), + ImageEntry::make('avatar_url') + ->label('Avatar') + ->disk('public') + ->circular() + ->placeholder('-'), + ]), + Section::make('Profil') + ->columns(3) + ->schema([ + TextEntry::make('bio') + ->label('Bio') + ->placeholder('-') + ->columnSpanFull(), + TextEntry::make('height') + ->label('Taille') + ->numeric(decimalPlaces: 0) + ->suffix(' cm') + ->placeholder('-'), + TextEntry::make('date_of_birth') + ->label('Date de naissance') + ->date() + ->placeholder('-'), + TextEntry::make('sex') + ->label('Sexe') + ->badge(), + ]), + Section::make('Objectifs nutritionnels') + ->columns(4) + ->schema([ + TextEntry::make('daily_calorie_goal') + ->label('Calories') + ->numeric(decimalPlaces: 0) + ->suffix(' kcal'), + TextEntry::make('daily_protein_goal') + ->label('Protéines') + ->numeric(maxDecimalPlaces: 2) + ->suffix(' g'), + TextEntry::make('daily_carbs_goal') + ->label('Glucides') + ->numeric(maxDecimalPlaces: 2) + ->suffix(' g'), + TextEntry::make('daily_fats_goal') + ->label('Lipides') + ->numeric(maxDecimalPlaces: 2) + ->suffix(' g'), + ]), + Section::make('Activité') + ->columns(3) + ->schema([ + TextEntry::make('physical_activity_level') + ->label('Niveau d’activité') + ->badge(), + TextEntry::make('weight_goal') + ->label('Objectif de poids') + ->badge(), + TextEntry::make('pace_preference') + ->label('Rythme') + ->badge(), + ]), + Section::make('Métadonnées') + ->columns(2) + ->schema([ + TextEntry::make('created_at') + ->label('Créé le') + ->dateTime() + ->placeholder('-'), + TextEntry::make('updated_at') + ->label('Mis à jour le') + ->dateTime() + ->placeholder('-'), + ]), ]); } } diff --git a/app/Filament/Resources/Users/Tables/UsersTable.php b/app/Filament/Resources/Users/Tables/UsersTable.php index e261909..c1c9729 100644 --- a/app/Filament/Resources/Users/Tables/UsersTable.php +++ b/app/Filament/Resources/Users/Tables/UsersTable.php @@ -2,12 +2,18 @@ namespace App\Filament\Resources\Users\Tables; +use App\Enums\PacePreference; +use App\Enums\PhysicalActivityLevel; +use App\Enums\UserSex; +use App\Enums\WeightGoal; use Filament\Actions\BulkActionGroup; use Filament\Actions\DeleteBulkAction; use Filament\Actions\EditAction; use Filament\Actions\ViewAction; use Filament\Tables\Columns\ImageColumn; use Filament\Tables\Columns\TextColumn; +use Filament\Tables\Filters\SelectFilter; +use Filament\Tables\Filters\TernaryFilter; use Filament\Tables\Table; class UsersTable @@ -16,29 +22,120 @@ class UsersTable { return $table ->columns([ + TextColumn::make('id') + ->label('ID') + ->searchable() + ->copyable() + ->toggleable(isToggledHiddenByDefault: true), TextColumn::make('name') + ->label('Nom') ->searchable(), TextColumn::make('email') - ->label('Email address') + ->label('Adresse email') ->searchable(), TextColumn::make('email_verified_at') + ->label('Email vérifié le') ->dateTime() - ->sortable(), + ->sortable() + ->placeholder('Non vérifié'), + TextColumn::make('account_verified_at') + ->label('Compte vérifié le') + ->dateTime() + ->sortable() + ->placeholder('Non vérifié') + ->toggleable(isToggledHiddenByDefault: true), ImageColumn::make('avatar_url') ->label('Avatar') ->disk('public') ->circular(), + TextColumn::make('sex') + ->label('Sexe') + ->badge() + ->sortable(), + TextColumn::make('date_of_birth') + ->label('Date de naissance') + ->date() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + TextColumn::make('height') + ->label('Taille') + ->numeric(decimalPlaces: 0) + ->suffix(' cm') + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + TextColumn::make('physical_activity_level') + ->label('Activité') + ->badge() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + TextColumn::make('weight_goal') + ->label('Objectif poids') + ->badge() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + TextColumn::make('pace_preference') + ->label('Rythme') + ->badge() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + TextColumn::make('daily_calorie_goal') + ->label('Calories') + ->numeric(decimalPlaces: 0) + ->suffix(' kcal') + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + TextColumn::make('daily_protein_goal') + ->label('Protéines') + ->numeric(maxDecimalPlaces: 2) + ->suffix(' g') + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + TextColumn::make('daily_carbs_goal') + ->label('Glucides') + ->numeric(maxDecimalPlaces: 2) + ->suffix(' g') + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + TextColumn::make('daily_fats_goal') + ->label('Lipides') + ->numeric(maxDecimalPlaces: 2) + ->suffix(' g') + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + TextColumn::make('bio') + ->label('Bio') + ->limit(50) + ->toggleable(isToggledHiddenByDefault: true), TextColumn::make('created_at') + ->label('Créé le') ->dateTime() ->sortable() ->toggleable(isToggledHiddenByDefault: true), TextColumn::make('updated_at') + ->label('Mis à jour le') ->dateTime() ->sortable() ->toggleable(isToggledHiddenByDefault: true), ]) ->filters([ - // + SelectFilter::make('sex') + ->label('Sexe') + ->options(UserSex::class), + SelectFilter::make('physical_activity_level') + ->label('Activité') + ->options(PhysicalActivityLevel::class), + SelectFilter::make('weight_goal') + ->label('Objectif poids') + ->options(WeightGoal::class), + SelectFilter::make('pace_preference') + ->label('Rythme') + ->options(PacePreference::class), + TernaryFilter::make('email_verified_at') + ->label('Email vérifié') + ->nullable(), + TernaryFilter::make('account_verified_at') + ->label('Compte vérifié') + ->nullable(), ]) ->recordActions([ ViewAction::make(), diff --git a/app/Models/User.php b/app/Models/User.php index fa87107..2fa8fea 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -31,10 +31,12 @@ class User extends Authenticatable implements FilamentUser, MustVerifyEmail protected $fillable = [ 'name', 'email', + 'email_verified_at', 'password', 'avatar_url', 'height', 'bio', + 'account_verified_at', 'daily_calorie_goal', 'daily_protein_goal', 'daily_carbs_goal',