diff --git a/assets/icons/dumbbell.png b/assets/icons/dumbbell.png new file mode 100644 index 000000000..c76a3341d Binary files /dev/null and b/assets/icons/dumbbell.png differ diff --git a/assets/icons/rest.png b/assets/icons/rest.png new file mode 100644 index 000000000..427211267 Binary files /dev/null and b/assets/icons/rest.png differ diff --git a/assets/icons/training-plan.png b/assets/icons/training-plan.png new file mode 100644 index 000000000..de0bff8de Binary files /dev/null and b/assets/icons/training-plan.png differ diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index f768dcd6a..213338284 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -880,6 +880,11 @@ "@newRoutine": {}, "noRoutines": "Du hast keine Routinen", "@noRoutines": {}, + "noRoutinesSubtitle": "Erstelle deine erste Routine, um deine Trainings mitzuverfolgen", + "noWorkoutScheduled": "Kein Training geplant", + "workoutCompleted": "Erledigt", + "workoutUpcoming": "Geplant", + "workoutMissed": "Verpasst", "sets": "Sätze", "@sets": { "description": "The number of sets to be done for one exercise" diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 003c43ea6..472caf25f 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -178,6 +178,11 @@ "routines": "Routines", "newRoutine": "New routine", "noRoutines": "You have no routines", + "noRoutinesSubtitle": "Create your first routine to start tracking your workouts", + "noWorkoutScheduled": "No workout scheduled", + "workoutCompleted": "Completed", + "workoutUpcoming": "Upcoming", + "workoutMissed": "Missed", "reps": "Reps", "@reps": { "description": "Shorthand for repetitions, used when space constraints are tighter" @@ -1114,5 +1119,13 @@ "themeMode": "Theme mode", "darkMode": "Always dark mode", "lightMode": "Always light mode", - "systemMode": "System settings" + "systemMode": "System settings", + "showAll": "Show all", + "@showAll": { + "description": "Text for expand button to show all items" + }, + "showLess": "Show less", + "@showLess": { + "description": "Text for collapse button to hide items" + } } diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 395cd1e2f..a1095ecb5 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -796,6 +796,11 @@ "@newRoutine": {}, "noRoutines": "No hay rutinas", "@noRoutines": {}, + "noRoutinesSubtitle": "Crea tu primera rutina para comenzar a seguir tus entrenamientos", + "noWorkoutScheduled": "Sin entrenamiento programado", + "workoutCompleted": "Completado", + "workoutUpcoming": "Próximo", + "workoutMissed": "Perdido", "restTime": "Tiempo de descanso", "@restTime": {}, "sets": "El número de rondas para cada ejercicio", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 902d4c582..205349bf8 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -870,9 +870,14 @@ "@useColors": {}, "newRoutine": "Nouvelle routine", "@newRoutine": {}, - "noRoutines": "Vous n’avez aucune de routine", + "noRoutines": "Vous n'avez aucune de routine", "@noRoutines": {}, - "useUsernameAndPassword": "Utiliser le nom d’utilisateur et mot de passe", + "noRoutinesSubtitle": "Créez votre première routine pour commencer à suivre vos entraînements", + "noWorkoutScheduled": "Aucun entraînement prévu", + "workoutCompleted": "Terminé", + "workoutUpcoming": "À venir", + "workoutMissed": "Manqué", + "useUsernameAndPassword": "Utiliser le nom d'utilisateur et mot de passe", "@useUsernameAndPassword": {}, "routines": "Routines", "@routines": {}, diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index 7277acf93..7b1f99be8 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -949,6 +949,11 @@ "@newRoutine": {}, "noRoutines": "Non hai nessuna routine", "@noRoutines": {}, + "noRoutinesSubtitle": "Crea la tua prima routine per iniziare a monitorare i tuoi allenamenti", + "noWorkoutScheduled": "Nessun allenamento programmato", + "workoutCompleted": "Completato", + "workoutUpcoming": "In arrivo", + "workoutMissed": "Saltato", "restTime": "Tempo di riposo", "@restTime": {}, "exerciseNr": "Esercizio {nr}", diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 4cba50c47..58f127993 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -878,6 +878,11 @@ }, "noRoutines": "Ty nie masz żadnej rutyny", "@noRoutines": {}, + "noRoutinesSubtitle": "Stwórz swoją pierwszą rutynę, aby rozpocząć śledzenie swoich treningów", + "noWorkoutScheduled": "Brak zaplanowanego treningu", + "workoutCompleted": "Ukończony", + "workoutUpcoming": "Nadchodzący", + "workoutMissed": "Pominięty", "restDay": "Dzień odpoczynkowy", "@restDay": {}, "errorInfoDescription2": "Możesz kontynuować używanie tej aplikacji, ale niektóre funkcje mogą być niedostępne.", diff --git a/lib/theme/theme.dart b/lib/theme/theme.dart index 6a78e3bd7..53e0e9712 100644 --- a/lib/theme/theme.dart +++ b/lib/theme/theme.dart @@ -30,6 +30,7 @@ const Color wgerPrimaryColorLight = Color(0xff94B2DB); const Color wgerSecondaryColor = Color(0xffe63946); const Color wgerSecondaryColorLight = Color(0xffF6B4BA); const Color wgerTertiaryColor = Color(0xFF6CA450); +const Color wgerAccentColor = Color(0xFF3B82F6); const FlexSubThemesData wgerSubThemeData = FlexSubThemesData( fabSchemeColor: SchemeColor.secondary, diff --git a/lib/widgets/dashboard/widgets/routines.dart b/lib/widgets/dashboard/widgets/routines.dart index 5077d9052..cd782b60a 100644 --- a/lib/widgets/dashboard/widgets/routines.dart +++ b/lib/widgets/dashboard/widgets/routines.dart @@ -17,194 +17,614 @@ */ import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; -import 'package:wger/helpers/date.dart'; import 'package:wger/l10n/generated/app_localizations.dart'; import 'package:wger/models/workouts/day_data.dart'; import 'package:wger/models/workouts/routine.dart'; import 'package:wger/providers/routines.dart'; +import 'package:wger/screens/form_screen.dart'; import 'package:wger/screens/gym_mode.dart'; import 'package:wger/screens/routine_screen.dart'; import 'package:wger/theme/theme.dart'; -import 'package:wger/widgets/core/core.dart'; -import 'package:wger/widgets/dashboard/widgets/nothing_found.dart'; import 'package:wger/widgets/routines/forms/routine.dart'; +bool _isSameDay(DateTime a, DateTime b) { + return a.year == b.year && a.month == b.month && a.day == b.day; +} + class DashboardRoutineWidget extends StatefulWidget { const DashboardRoutineWidget(); @override - _DashboardRoutineWidgetState createState() => _DashboardRoutineWidgetState(); + State createState() => _DashboardRoutineWidgetState(); } class _DashboardRoutineWidgetState extends State { - var _showDetail = false; - bool _hasContent = false; + bool _isExpanded = false; @override Widget build(BuildContext context) { final routine = context.watch().currentRoutine; - _hasContent = routine != null; - final dateFormat = DateFormat.yMd(Localizations.localeOf(context).languageCode); - - return Card( - child: Column( - children: [ - ListTile( - title: Text( - _hasContent ? routine!.name : AppLocalizations.of(context).labelWorkoutPlan, - style: Theme.of(context).textTheme.headlineSmall, - ), - subtitle: Text( - _hasContent - ? '${dateFormat.format(routine!.start)} - ${dateFormat.format(routine!.end)}' - : '', - ), - leading: Icon( - Icons.fitness_center, - color: Theme.of(context).textTheme.headlineSmall!.color, - ), - trailing: _hasContent - ? Tooltip( - message: AppLocalizations.of(context).toggleDetails, - child: _showDetail ? const Icon(Icons.info) : const Icon(Icons.info_outline), - ) - : const SizedBox(), - onTap: () { - setState(() { - _showDetail = !_showDetail; - }); - }, + final hasContent = routine != null; + + final now = DateTime.now(); + final allDays = hasContent + ? routine.dayDataCurrentIteration.where((d) => d.day != null).toList() + : []; + final todayIndex = allDays.indexWhere((d) => _isSameDay(d.date, now)); + final todayData = todayIndex >= 0 ? allDays[todayIndex] : null; + final otherDays = allDays.where((d) => !_isSameDay(d.date, now)).toList(); + + return Padding( + padding: const EdgeInsets.all(4.0), + child: Material( + elevation: 0, + color: Colors.transparent, + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).brightness == Brightness.light + ? Colors.white + : Theme.of(context).cardColor, + borderRadius: BorderRadius.circular(20), + boxShadow: [ + BoxShadow( + color: Colors.black.withValues(alpha: 0.08), + blurRadius: 20, + offset: const Offset(0, 4), + spreadRadius: 0, + ), + BoxShadow( + color: Colors.black.withValues(alpha: 0.04), + blurRadius: 10, + offset: const Offset(0, 2), + spreadRadius: 0, + ), + ], ), - if (_hasContent) ...[ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: DetailContentWidget(routine!.dayDataCurrentIteration, _showDetail), - ), - Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - TextButton( - child: Text(AppLocalizations.of(context).goToDetailPage), - onPressed: () { - Navigator.of(context).pushNamed( - RoutineScreen.routeName, - arguments: routine.id, - ); - }, + child: Column( + children: [ + // Header - navigates to routine details + InkWell( + onTap: hasContent + ? () { + Navigator.of(context).pushNamed( + RoutineScreen.routeName, + arguments: routine.id, + ); + } + : null, + borderRadius: const BorderRadius.vertical(top: Radius.circular(20)), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + hasContent + ? routine.name + : AppLocalizations.of(context).labelWorkoutPlan, + style: Theme.of(context).textTheme.titleLarge?.copyWith( + fontWeight: FontWeight.bold, + ), + ), + if (hasContent) ...[ + const SizedBox(height: 4), + Builder( + builder: (context) { + final isDarkMode = + Theme.of(context).brightness == Brightness.dark; + final startDate = MaterialLocalizations.of( + context, + ).formatCompactDate(routine.start); + final endDate = MaterialLocalizations.of( + context, + ).formatCompactDate(routine.end); + return Text( + '$startDate - $endDate', + style: TextStyle( + color: isDarkMode + ? Colors.grey.shade400 + : Colors.grey.shade600, + fontSize: 13, + fontWeight: FontWeight.w500, + ), + ); + }, + ), + ], + ], + ), + ), + if (hasContent) + Icon( + Icons.chevron_right, + color: Colors.grey.shade400, + size: 28, + ), + ], + ), ), - ], - ), - ] else - NothingFound( - AppLocalizations.of(context).noRoutines, - AppLocalizations.of(context).newRoutine, - RoutineForm(Routine.empty()), - ), - ], + ), + + // Content + if (hasContent) + Padding( + padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), + child: Column( + children: [ + // Today's workout (always visible) - show rest day card if no workout scheduled + _TodayCard(dayData: todayData), + + // Expandable section for other days + if (otherDays.isNotEmpty) ...[ + const SizedBox(height: 16), + // Expand/collapse button + Builder( + builder: (context) { + final isDarkMode = Theme.of(context).brightness == Brightness.dark; + return Material( + color: Colors.transparent, + child: InkWell( + onTap: () => setState(() => _isExpanded = !_isExpanded), + borderRadius: BorderRadius.circular(10), + child: Container( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 12), + decoration: BoxDecoration( + border: Border.all( + color: isDarkMode + ? Colors.grey.shade700 + : Colors.grey.shade200, + width: 1, + ), + borderRadius: BorderRadius.circular(10), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + _isExpanded + ? AppLocalizations.of(context).showLess + : '${AppLocalizations.of(context).showAll} (${otherDays.length})', + style: TextStyle( + color: isDarkMode + ? Colors.grey.shade300 + : Colors.grey.shade700, + fontWeight: FontWeight.w600, + fontSize: 13, + ), + ), + const SizedBox(width: 6), + AnimatedRotation( + turns: _isExpanded ? 0.5 : 0, + duration: const Duration(milliseconds: 200), + child: Icon( + Icons.keyboard_arrow_down, + color: isDarkMode + ? Colors.grey.shade400 + : Colors.grey.shade600, + size: 18, + ), + ), + ], + ), + ), + ), + ); + }, + ), + + // Animated expandable list + AnimatedCrossFade( + firstChild: const SizedBox.shrink(), + secondChild: Column( + children: [ + const SizedBox(height: 8), + for (int i = 0; i < otherDays.length; i++) ...[ + _CompactDayRow(dayData: otherDays[i]), + if (i < otherDays.length - 1) + Divider(height: 1, color: Colors.grey.shade200), + ], + ], + ), + crossFadeState: _isExpanded + ? CrossFadeState.showSecond + : CrossFadeState.showFirst, + duration: const Duration(milliseconds: 200), + ), + ], + ], + ), + ) + else + // Empty state - inviting the user to create their first routine + Padding( + padding: const EdgeInsets.fromLTRB(16, 8, 16, 24), + child: Column( + children: [ + const SizedBox(height: 16), + // 3D training plan icon + Image.asset( + 'assets/icons/training-plan.png', + width: 120, + height: 120, + ), + const SizedBox(height: 20), + // Inviting title + Text( + AppLocalizations.of(context).noRoutines, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: Theme.of(context).brightness == Brightness.dark + ? Colors.grey.shade300 + : Colors.grey.shade800, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 8), + // Subtitle + Text( + AppLocalizations.of(context).noRoutinesSubtitle, + style: TextStyle( + fontSize: 14, + color: Theme.of(context).brightness == Brightness.dark + ? Colors.grey.shade400 + : Colors.grey.shade600, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 24), + // Modern button to create routine + Material( + color: wgerAccentColor, + borderRadius: BorderRadius.circular(12), + child: InkWell( + onTap: () { + Navigator.pushNamed( + context, + FormScreen.routeName, + arguments: FormScreenArguments( + AppLocalizations.of(context).newRoutine, + hasListView: true, + RoutineForm(Routine.empty()), + ), + ); + }, + borderRadius: BorderRadius.circular(12), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon( + Icons.add_rounded, + color: Colors.white, + size: 20, + ), + const SizedBox(width: 8), + Text( + AppLocalizations.of(context).newRoutine, + style: const TextStyle( + color: Colors.white, + fontSize: 15, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ), + ), + const SizedBox(height: 8), + ], + ), + ), + ], + ), + ), ), ); } } -class DetailContentWidget extends StatelessWidget { - final List dayDataList; - final bool showDetail; +/// Featured card for today's workout - more prominent styling +class _TodayCard extends StatelessWidget { + final DayData? dayData; - const DetailContentWidget(this.dayDataList, this.showDetail, {super.key}); + const _TodayCard({required this.dayData}); @override Widget build(BuildContext context) { - return Column( - children: [ - ...dayDataList.where((dayData) => dayData.day != null).map((dayData) { - return Column( + // If no workout scheduled for today, treat as rest/off day + final isRestDay = dayData == null || dayData!.day == null || dayData!.day!.isRest; + final exerciseCount = dayData?.slots.fold(0, (sum, slot) => sum + slot.setConfigs.length) ?? 0; + final isDarkMode = Theme.of(context).brightness == Brightness.dark; + + return Material( + color: isRestDay + ? (isDarkMode ? Colors.grey.shade900 : Colors.grey.shade50) + : wgerAccentColor.withValues(alpha: isDarkMode ? 0.15 : 0.06), + borderRadius: BorderRadius.circular(16), + child: InkWell( + onTap: isRestDay || dayData == null + ? null + : () { + Navigator.of(context).pushNamed( + GymModeScreen.routeName, + arguments: GymModeArguments( + dayData!.day!.routineId, + dayData!.day!.id!, + dayData!.iteration, + ), + ); + }, + borderRadius: BorderRadius.circular(16), + child: Container( + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + border: Border.all( + color: isRestDay + ? (isDarkMode ? Colors.grey.shade600 : Colors.grey.shade200) + : wgerAccentColor.withValues(alpha: isDarkMode ? 0.4 : 0.2), + width: 1.5, + ), + ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, children: [ - SizedBox( - width: double.infinity, - child: Row( + // Icon using PNG assets - use rest icon for rest/no workout days + Image.asset( + isRestDay ? 'assets/icons/rest.png' : 'assets/icons/dumbbell.png', + width: 40, + height: 40, + ), + const SizedBox(width: 16), + + // Text content + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ - if (dayData.date.isSameDayAs(DateTime.now())) const Icon(Icons.today), - Expanded( - child: Text( - dayData.day == null || dayData.day!.isRest - ? AppLocalizations.of(context).restDay - : dayData.day!.nameWithType, - style: const TextStyle(fontWeight: FontWeight.bold), - overflow: TextOverflow.ellipsis, - ), + // Today badge and exercise count + Row( + children: [ + Container( + padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3), + decoration: BoxDecoration( + color: isRestDay + ? (isDarkMode ? Colors.grey.shade700 : Colors.grey.shade400) + : wgerAccentColor, + borderRadius: BorderRadius.circular(4), + ), + child: Text( + AppLocalizations.of(context).today.toUpperCase(), + style: TextStyle( + color: isRestDay && isDarkMode ? Colors.grey.shade200 : Colors.white, + fontSize: 9, + fontWeight: FontWeight.bold, + letterSpacing: 0.5, + ), + ), + ), + if (!isRestDay) ...[ + const SizedBox(width: 8), + Text( + '$exerciseCount ${AppLocalizations.of(context).exercises}', + style: TextStyle( + color: isDarkMode ? Colors.grey.shade400 : Colors.grey.shade600, + fontSize: 11, + fontWeight: FontWeight.w500, + ), + ), + ], + ], ), - Expanded( - child: MutedText( - dayData.day != null ? dayData.day!.description : '', - textAlign: TextAlign.right, - overflow: TextOverflow.ellipsis, + const SizedBox(height: 8), + + // Workout name or rest day message + Text( + dayData == null + ? AppLocalizations.of(context).noWorkoutScheduled + : (isRestDay + ? AppLocalizations.of(context).restDay + : dayData!.day!.nameWithType), + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18, + color: isDarkMode + ? (isRestDay ? Colors.grey.shade200 : Colors.white) + : (isRestDay ? Colors.grey.shade700 : Colors.black87), + height: 1.2, ), + maxLines: 2, + overflow: TextOverflow.ellipsis, ), - if (dayData.day == null || dayData.day!.isRest) - const Icon(Icons.hotel) - else - IconButton( - icon: const Icon(Icons.play_arrow), - color: wgerPrimaryButtonColor, - onPressed: () { - Navigator.of(context).pushNamed( - GymModeScreen.routeName, - arguments: GymModeArguments( - dayData.day!.routineId, - dayData.day!.id!, - dayData.iteration, - ), - ); - }, - ), ], ), ), - ...dayData.slots.map( - (slotData) => SizedBox( - width: double.infinity, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ...slotData.setConfigs.map( - (s) => showDetail - ? Column( - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - s.exercise - .getTranslation( - Localizations.localeOf(context).languageCode, - ) - .name, - ), - const SizedBox(width: 10), - Expanded( - child: MutedText( - s.textRepr, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - const SizedBox(height: 10), - ], - ) - : Container(), - ), - ], + + // Arrow for workout days + if (!isRestDay) ...[ + const SizedBox(width: 12), + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: wgerAccentColor, + borderRadius: BorderRadius.circular(12), + ), + child: const Icon( + Icons.play_arrow_rounded, + color: Colors.white, + size: 24, ), ), + ], + ], + ), + ), + ), + ); + } +} + +/// Compact row for non-today days in the expanded list +class _CompactDayRow extends StatelessWidget { + final DayData dayData; + + const _CompactDayRow({required this.dayData}); + + @override + Widget build(BuildContext context) { + final isRestDay = dayData.day == null || dayData.day!.isRest; + final exerciseCount = dayData.slots.fold(0, (sum, slot) => sum + slot.setConfigs.length); + final now = DateTime.now(); + final isPast = dayData.date.isBefore(now) && !_isSameDay(dayData.date, now); + final isFuture = dayData.date.isAfter(now); + final isDarkMode = Theme.of(context).brightness == Brightness.dark; + + // Check if this workout has been completed + final routine = context.watch().currentRoutine; + final hasSession = + routine?.sessions.any((session) { + return session.session.dayId == dayData.day?.id && + _isSameDay(session.session.date, dayData.date); + }) ?? + false; + + // Determine indicator color based on status + Color indicatorColor; + if (isRestDay) { + indicatorColor = isDarkMode ? Colors.grey.shade600 : Colors.grey.shade300; + } else if (hasSession) { + indicatorColor = Colors.green.shade600; + } else if (isFuture) { + indicatorColor = isDarkMode ? Colors.grey.shade600 : Colors.grey.shade400; + } else { + indicatorColor = Colors.red.shade400; + } + + return InkWell( + onTap: isRestDay + ? null + : () { + Navigator.of(context).pushNamed( + GymModeScreen.routeName, + arguments: GymModeArguments( + dayData.day!.routineId, + dayData.day!.id!, + dayData.iteration, + ), + ); + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12), + child: Row( + children: [ + // Day indicator (colored dot matching status) + Container( + width: 8, + height: 8, + decoration: BoxDecoration( + color: indicatorColor, + shape: BoxShape.circle, + ), + ), + const SizedBox(width: 12), + + // Day info + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + isRestDay ? AppLocalizations.of(context).restDay : dayData.day!.nameWithType, + style: TextStyle( + fontWeight: FontWeight.w500, + fontSize: 14, + color: isDarkMode + ? (isRestDay ? Colors.grey.shade400 : Colors.white) + : (isRestDay ? Colors.grey.shade500 : Colors.black87), + ), + overflow: TextOverflow.ellipsis, + ), + if (!isRestDay) + Text( + '$exerciseCount ${AppLocalizations.of(context).exercises}', + style: TextStyle( + color: isDarkMode ? Colors.grey.shade400 : Colors.grey.shade500, + fontSize: 12, + ), + ), + ], ), - const Divider(), + ), + + // Status label for workout days + if (!isRestDay) ...[ + if (hasSession) + // Completed - green label + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: isDarkMode + ? Colors.green.shade900.withValues(alpha: 0.3) + : Colors.green.shade50, + borderRadius: BorderRadius.circular(6), + ), + child: Text( + AppLocalizations.of(context).workoutCompleted, + style: TextStyle( + color: isDarkMode ? Colors.green.shade300 : Colors.green.shade600, + fontSize: 11, + fontWeight: FontWeight.w600, + ), + ), + ) + else if (isFuture) + // Future workout - gray label + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: isDarkMode ? Colors.grey.shade800 : Colors.grey.shade100, + borderRadius: BorderRadius.circular(6), + ), + child: Text( + AppLocalizations.of(context).workoutUpcoming, + style: TextStyle( + color: isDarkMode ? Colors.grey.shade400 : Colors.grey.shade600, + fontSize: 11, + fontWeight: FontWeight.w600, + ), + ), + ) + else if (isPast) + // Missed workout - red label + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: isDarkMode + ? Colors.red.shade900.withValues(alpha: 0.3) + : Colors.red.shade50, + borderRadius: BorderRadius.circular(6), + ), + child: Text( + AppLocalizations.of(context).workoutMissed, + style: TextStyle( + color: isDarkMode ? Colors.red.shade300 : Colors.red.shade600, + fontSize: 11, + fontWeight: FontWeight.w600, + ), + ), + ), ], - ); - }), - ], + ], + ), + ), ); } }