diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index 6413e9e..057030c 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -49,13 +49,21 @@ class DashboardController extends Controller ->groupBy(function ($s) { return $s->created_at->format('Y-m-d'); }) ->map->count(); + // Body weight trend + $weightLogs = $user->weightLogs()->orderBy('logged_at')->get(['id', 'weight_kg', 'logged_at']); + $weightChange = $weightLogs->count() >= 2 + ? round($weightLogs->last()->weight_kg - $weightLogs->first()->weight_kg, 2) + : null; + return view('dashboard', compact( 'workoutsCount', 'sessionsCount', 'completedSessions', 'completionRate', 'recentSessions', - 'dailyActivity' + 'dailyActivity', + 'weightLogs', + 'weightChange' )); } } diff --git a/app/Http/Controllers/WeightLogController.php b/app/Http/Controllers/WeightLogController.php new file mode 100644 index 0000000..bcbcb5d --- /dev/null +++ b/app/Http/Controllers/WeightLogController.php @@ -0,0 +1,42 @@ +validate([ + 'weight_kg' => ['required', 'numeric', 'min:20', 'max:400'], + 'logged_at' => ['nullable', 'date', 'before_or_equal:today'], + ]); + + $loggedAt = $data['logged_at'] ?? now()->toDateString(); + + // updateOrCreate's raw match array compares logged_at as a literal string, + // which doesn't reliably match how the `date` cast stores it - use a + // date-aware lookup instead. + $entry = WeightLog::where('user_id', Auth::id())->whereDate('logged_at', $loggedAt)->first() + ?? new WeightLog(['user_id' => Auth::id(), 'logged_at' => $loggedAt]); + $entry->weight_kg = $data['weight_kg']; + $entry->save(); + + return back()->with('status', __('app.weight_log.saved')); + } + + public function destroy(WeightLog $weightLog): RedirectResponse + { + if ($weightLog->user_id !== Auth::id()) { + abort(403); + } + + $weightLog->delete(); + + return back()->with('status', __('app.weight_log.deleted')); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index e82ca67..67c3e9a 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -84,6 +84,11 @@ class User extends Authenticatable return $this->belongsToMany(YogaPose::class, 'yoga_pose_favorites')->withTimestamps(); } + public function weightLogs() + { + return $this->hasMany(WeightLog::class); + } + // Helpers public function hasFavoritedExercise(Exercise $exercise): bool { diff --git a/app/Models/WeightLog.php b/app/Models/WeightLog.php new file mode 100644 index 0000000..e494c94 --- /dev/null +++ b/app/Models/WeightLog.php @@ -0,0 +1,28 @@ + 'decimal:2', + 'logged_at' => 'date', + ]; + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } +} diff --git a/database/factories/WeightLogFactory.php b/database/factories/WeightLogFactory.php new file mode 100644 index 0000000..e908fac --- /dev/null +++ b/database/factories/WeightLogFactory.php @@ -0,0 +1,21 @@ + + */ +class WeightLogFactory extends Factory +{ + public function definition(): array + { + return [ + 'user_id' => User::factory(), + 'weight_kg' => fake()->randomFloat(1, 55, 110), + 'logged_at' => fake()->dateTimeBetween('-60 days', 'now')->format('Y-m-d'), + ]; + } +} diff --git a/database/migrations/2026_08_10_090532_create_weight_logs_table.php b/database/migrations/2026_08_10_090532_create_weight_logs_table.php new file mode 100644 index 0000000..4fe9249 --- /dev/null +++ b/database/migrations/2026_08_10_090532_create_weight_logs_table.php @@ -0,0 +1,32 @@ +id(); + $table->foreignId('user_id')->constrained()->cascadeOnDelete(); + $table->decimal('weight_kg', 5, 2); + $table->date('logged_at'); + $table->timestamps(); + + $table->unique(['user_id', 'logged_at']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('weight_logs'); + } +}; diff --git a/lang/en/app.php b/lang/en/app.php index 07fb39b..cfaed6f 100644 --- a/lang/en/app.php +++ b/lang/en/app.php @@ -231,6 +231,19 @@ return [ 'activity_30d_help' => 'Number of sessions per day (higher is better).', ], + 'weight_log' => [ + 'title' => 'Body Weight', + 'change_since_first' => 'Change since your first entry:', + 'weight_kg' => 'Weight (kg)', + 'date' => 'Date', + 'log' => 'Log', + 'none' => 'No weight entries yet. Log your first one to start tracking your trend.', + 'saved' => 'Weight logged', + 'deleted' => 'Entry deleted', + 'delete' => 'Delete', + 'confirm_delete' => 'Delete this entry?', + ], + 'admin' => [ 'title' => 'Admin Dashboard', 'subtitle' => 'Manage content and see overall site statistics.', diff --git a/lang/ro/app.php b/lang/ro/app.php index 0586de6..c119d78 100644 --- a/lang/ro/app.php +++ b/lang/ro/app.php @@ -231,6 +231,19 @@ return [ 'activity_30d_help' => 'Număr de sesiuni pe zi (mai mult este mai bine).', ], + 'weight_log' => [ + 'title' => 'Greutate corporală', + 'change_since_first' => 'Schimbare de la prima înregistrare:', + 'weight_kg' => 'Greutate (kg)', + 'date' => 'Data', + 'log' => 'Adaugă', + 'none' => 'Nicio înregistrare încă. Adaugă prima pentru a-ți urmări evoluția.', + 'saved' => 'Greutate înregistrată', + 'deleted' => 'Înregistrare ștearsă', + 'delete' => 'Șterge', + 'confirm_delete' => 'Ștergi această înregistrare?', + ], + 'admin' => [ 'title' => 'Panou de administrare', 'subtitle' => 'Gestionează conținutul și vezi statisticile generale ale site‑ului.', diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index 8d58be7..0a5befc 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -88,5 +88,51 @@ + +
+ {{ __('app.weight_log.change_since_first') }} + + {{ $weightChange > 0 ? '+' : '' }}{{ $weightChange }} kg + +
+ @endif +