273 lines
7.3 KiB
Dart
273 lines
7.3 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:trainhub_flutter/core/constants/ui_constants.dart';
|
|
import 'package:trainhub_flutter/core/theme/app_colors.dart';
|
|
import 'package:trainhub_flutter/domain/entities/program_workout.dart';
|
|
|
|
class RecentActivitySection extends StatelessWidget {
|
|
const RecentActivitySection({super.key, required this.activity});
|
|
|
|
final List<ProgramWorkoutEntity> activity;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
'Recent Activity',
|
|
style: GoogleFonts.inter(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
color: AppColors.textPrimary,
|
|
),
|
|
),
|
|
),
|
|
if (activity.isNotEmpty)
|
|
Text(
|
|
'${activity.length} workout${activity.length == 1 ? '' : 's'}',
|
|
style: GoogleFonts.inter(
|
|
fontSize: 12,
|
|
color: AppColors.textMuted,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: UIConstants.spacing12),
|
|
if (activity.isEmpty)
|
|
const _EmptyActivity()
|
|
else
|
|
_ActivityList(activity: activity),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class _EmptyActivity extends StatelessWidget {
|
|
const _EmptyActivity();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.symmetric(
|
|
vertical: 40,
|
|
horizontal: UIConstants.spacing24,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: AppColors.surfaceContainer,
|
|
borderRadius: UIConstants.cardBorderRadius,
|
|
border: Border.all(color: AppColors.border),
|
|
),
|
|
child: Column(
|
|
children: [
|
|
const Icon(Icons.history, size: 32, color: AppColors.textMuted),
|
|
const SizedBox(height: UIConstants.spacing12),
|
|
Text(
|
|
'No completed workouts yet',
|
|
style: GoogleFonts.inter(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w500,
|
|
color: AppColors.textSecondary,
|
|
),
|
|
),
|
|
const SizedBox(height: UIConstants.spacing4),
|
|
Text(
|
|
'Your recent workout history will appear here.',
|
|
style: GoogleFonts.inter(
|
|
fontSize: 13,
|
|
color: AppColors.textMuted,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _ActivityList extends StatelessWidget {
|
|
const _ActivityList({required this.activity});
|
|
|
|
final List<ProgramWorkoutEntity> activity;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
decoration: BoxDecoration(
|
|
color: AppColors.surfaceContainer,
|
|
borderRadius: UIConstants.cardBorderRadius,
|
|
border: Border.all(color: AppColors.border),
|
|
),
|
|
child: ClipRRect(
|
|
borderRadius: UIConstants.cardBorderRadius,
|
|
child: Column(
|
|
children: [
|
|
for (int i = 0; i < activity.length; i++) ...[
|
|
if (i > 0)
|
|
const Divider(
|
|
height: 1,
|
|
thickness: 1,
|
|
color: AppColors.border,
|
|
),
|
|
_ActivityItem(workout: activity[i]),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _ActivityItem extends StatefulWidget {
|
|
const _ActivityItem({required this.workout});
|
|
|
|
final ProgramWorkoutEntity workout;
|
|
|
|
@override
|
|
State<_ActivityItem> createState() => _ActivityItemState();
|
|
}
|
|
|
|
class _ActivityItemState extends State<_ActivityItem> {
|
|
bool _isHovered = false;
|
|
|
|
Color get _typeColor {
|
|
switch (widget.workout.type.toLowerCase()) {
|
|
case 'strength':
|
|
return AppColors.accent;
|
|
case 'cardio':
|
|
return AppColors.info;
|
|
case 'flexibility':
|
|
case 'mobility':
|
|
return AppColors.purple;
|
|
case 'rest':
|
|
return AppColors.textMuted;
|
|
default:
|
|
return AppColors.success;
|
|
}
|
|
}
|
|
|
|
IconData get _typeIcon {
|
|
switch (widget.workout.type.toLowerCase()) {
|
|
case 'strength':
|
|
return Icons.fitness_center;
|
|
case 'cardio':
|
|
return Icons.directions_run;
|
|
case 'flexibility':
|
|
case 'mobility':
|
|
return Icons.self_improvement;
|
|
case 'rest':
|
|
return Icons.bedtime_outlined;
|
|
default:
|
|
return Icons.check_circle;
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return MouseRegion(
|
|
onEnter: (_) => setState(() => _isHovered = true),
|
|
onExit: (_) => setState(() => _isHovered = false),
|
|
child: AnimatedContainer(
|
|
duration: UIConstants.animationDuration,
|
|
color: _isHovered
|
|
? AppColors.surfaceContainerHigh.withValues(alpha: 0.5)
|
|
: Colors.transparent,
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: UIConstants.cardPadding,
|
|
vertical: UIConstants.spacing12,
|
|
),
|
|
child: Row(
|
|
children: [
|
|
_buildLeadingIcon(),
|
|
const SizedBox(width: UIConstants.spacing12),
|
|
_buildWorkoutInfo(),
|
|
_buildTypeBadge(),
|
|
const SizedBox(width: UIConstants.spacing12),
|
|
_buildStatusIcon(),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildLeadingIcon() {
|
|
return Container(
|
|
width: 36,
|
|
height: 36,
|
|
decoration: BoxDecoration(
|
|
color: widget.workout.completed
|
|
? _typeColor.withValues(alpha: 0.15)
|
|
: AppColors.zinc800,
|
|
borderRadius: UIConstants.smallCardBorderRadius,
|
|
),
|
|
child: Icon(
|
|
widget.workout.completed ? _typeIcon : Icons.circle_outlined,
|
|
size: 18,
|
|
color: widget.workout.completed ? _typeColor : AppColors.textMuted,
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildWorkoutInfo() {
|
|
return Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
widget.workout.name ?? 'Workout',
|
|
style: GoogleFonts.inter(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w500,
|
|
color: AppColors.textPrimary,
|
|
),
|
|
),
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
'Week ${widget.workout.weekId} · Day ${widget.workout.day}',
|
|
style: GoogleFonts.inter(
|
|
fontSize: 12,
|
|
color: AppColors.textMuted,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildTypeBadge() {
|
|
return Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: _typeColor.withValues(alpha: 0.12),
|
|
borderRadius: BorderRadius.circular(6),
|
|
),
|
|
child: Text(
|
|
widget.workout.type,
|
|
style: GoogleFonts.inter(
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w500,
|
|
color: _typeColor,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildStatusIcon() {
|
|
if (widget.workout.completed) {
|
|
return const Icon(
|
|
Icons.check_circle,
|
|
size: 18,
|
|
color: AppColors.success,
|
|
);
|
|
}
|
|
return const Icon(
|
|
Icons.radio_button_unchecked,
|
|
size: 18,
|
|
color: AppColors.textMuted,
|
|
);
|
|
}
|
|
}
|