diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index c1d68be..9cf74b3 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -38,6 +38,7 @@ public function definition(): array 'email_verified_at' => now(), 'password' => static::$password ??= Hash::make('password'), 'remember_token' => Str::random(10), + 'login_count' => 0, ]; } diff --git a/database/migrations/2025_03_15_000000_add_login_tracking_to_users.php b/database/migrations/2025_03_15_000000_add_login_tracking_to_users.php new file mode 100644 index 0000000..103c22e --- /dev/null +++ b/database/migrations/2025_03_15_000000_add_login_tracking_to_users.php @@ -0,0 +1,22 @@ +dateTime('last_login_at')->nullable(); + $table->integer('login_count')->default(0); + }); + } + + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn(['last_login_at', 'login_count']); + }); + } +}; diff --git a/src/EclipseServiceProvider.php b/src/EclipseServiceProvider.php index d6abc1e..da4f57b 100644 --- a/src/EclipseServiceProvider.php +++ b/src/EclipseServiceProvider.php @@ -5,10 +5,11 @@ use Eclipse\Core\Console\Commands\ClearCommand; use Eclipse\Core\Console\Commands\DeployCommand; use Eclipse\Core\Console\Commands\PostComposerUpdate; +use Eclipse\Core\Models\User; use Eclipse\Core\Providers\AdminPanelProvider; use Eclipse\Core\Providers\TelescopeServiceProvider; -use Illuminate\Database\Eloquent\Model; -use Illuminate\Support\Facades\DB; +use Illuminate\Auth\Events\Login; +use Illuminate\Support\Facades\Event; use Spatie\LaravelPackageTools\Package; use Spatie\LaravelPackageTools\PackageServiceProvider; @@ -40,6 +41,12 @@ public function register(): self require_once __DIR__.'/Helpers/helpers.php'; + Event::listen(Login::class, function ($event) { + if ($event->user instanceof User) { + $event->user->updateLoginTracking(); + } + }); + $this->app->register(AdminPanelProvider::class); if ($this->app->environment('local')) { diff --git a/src/Filament/Resources/UserResource.php b/src/Filament/Resources/UserResource.php index 470a480..6887e79 100644 --- a/src/Filament/Resources/UserResource.php +++ b/src/Filament/Resources/UserResource.php @@ -86,6 +86,16 @@ public static function table(Table $table): Table ->searchable() ->sortable() ->toggleable(), + Tables\Columns\TextColumn::make('last_login_at') + ->label('Last login') + ->dateTime() + ->sortable() + ->toggleable(), + Tables\Columns\TextColumn::make('login_count') + ->label('Total Logins') + ->sortable() + ->numeric() + ->formatStateUsing(fn (?int $state) => $state ?? 0), ]; if (config('eclipse.email_verification')) { @@ -145,6 +155,10 @@ public static function table(Table $table): Table ->label('Last name'), TextConstraint::make('name') ->label('Full name'), + TextConstraint::make('last_login_at') + ->label('Last login Date'), + TextConstraint::make('login_count') + ->label('Total Logins'), ]), ]; @@ -155,7 +169,8 @@ public static function table(Table $table): Table Tables\Actions\ActionGroup::make([ Tables\Actions\ViewAction::make(), Tables\Actions\EditAction::make(), - Tables\Actions\DeleteAction::make()->disabled(fn (User $user) => $user->id === auth()->user()->id), + Tables\Actions\DeleteAction::make() + ->disabled(fn (User $user) => $user->id === auth()->user()->id), ]), ]) ->bulkActions([ diff --git a/src/Models/User.php b/src/Models/User.php index 252ba57..b223ee3 100644 --- a/src/Models/User.php +++ b/src/Models/User.php @@ -44,6 +44,8 @@ class User extends Authenticatable implements FilamentUser, HasAvatar, HasMedia, 'last_name', 'email', 'password', + 'last_login_at', + 'login_count', ]; /** @@ -66,6 +68,7 @@ protected function casts(): array return [ 'email_verified_at' => 'datetime', 'password' => 'hashed', + 'last_login_at' => 'datetime', ]; } @@ -110,4 +113,16 @@ protected static function booted() $user->name = trim("$user->first_name $user->last_name"); }); } + + /** + * Update the user's last login timestamp and increment login count. + * + * @return void + */ + public function updateLoginTracking() + { + $this->last_login_at = now(); + $this->increment('login_count'); + $this->save(); + } } diff --git a/tests/Feature/LoginTrackingTest.php b/tests/Feature/LoginTrackingTest.php new file mode 100644 index 0000000..54cdab4 --- /dev/null +++ b/tests/Feature/LoginTrackingTest.php @@ -0,0 +1,34 @@ +create(); + + expect($user->last_login_at)->toBeNull(); + expect($user->login_count)->toBe(0); +}); + +test('user login updates last login timestamp and increments count', function () { + $user = User::factory()->create([ + 'last_login_at' => null, + 'login_count' => 0, + ]); + + // Simulate login + Auth::login($user); + $user->refresh(); // Reload from DB to reflect changes + + expect($user->last_login_at)->not->toBeNull(); + expect($user->login_count)->toBe(1); +}); + +test('guest users do not have login tracking data', function () { + $this->get('/admin')->assertRedirect('admin/login'); + + expect(Auth::user())->toBeNull(); +});