Add streaks and milestone badges

No gamification existed at all - nothing rewarded consistency or gave
users a reason to come back beyond the raw session stats already on
the dashboard. Adds a Gamification helper that derives (not stores)
a current/longest daily streak from completed WorkoutSessions and six
badges (first session, 10 and 50 sessions, 7- and 30-day streaks, a
100kg+ completed lift) from existing session/session-item data - no new
tables, since everything needed is already recorded.

Found and fixed a real Carbon 3 behavior change via testing: diffInDays()
no longer returns an absolute value by default (unlike Carbon 2) - it
returns a signed float. My longest-streak calculation used a strict
=== 1 check against the raw result, which silently always failed
because float(-1) or float(1) never strictly equals int 1. Fixed with
abs() plus an explicit (int) cast. Worth remembering for any other
diffIn*() usage in this codebase going forward.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 12:44:24 +03:00
co-authored by Claude Sonnet 5
parent e74aa2bda4
commit a9923f72ce
6 changed files with 338 additions and 2 deletions
+4
View File
@@ -228,7 +228,11 @@ return [
'sessions' => 'Sessions',
'completed' => 'Completed',
'completion_rate' => 'Completion rate',
'streak' => 'Current streak',
],
'days' => 'days',
'best_streak' => 'Best',
'badges' => 'Badges',
'recent_sessions' => 'Recent Sessions',
'no_sessions' => 'No sessions yet. Start a workout to track your progress.',
'completed' => 'Completed',
+4
View File
@@ -228,7 +228,11 @@ return [
'sessions' => 'Sesiuni',
'completed' => 'Finalizate',
'completion_rate' => 'Rată de finalizare',
'streak' => 'Serie curentă',
],
'days' => 'zile',
'best_streak' => 'Record',
'badges' => 'Insigne',
'recent_sessions' => 'Sesiuni recente',
'no_sessions' => 'Încă nu există sesiuni. Începe un antrenament pentru a-ți urmări progresul.',
'completed' => 'Finalizată',