diff --git a/lib/core/database.dart b/lib/core/database.dart index d23cb1b..cacb5fa 100644 --- a/lib/core/database.dart +++ b/lib/core/database.dart @@ -41,7 +41,8 @@ class DatabaseHelper { await _initializePredefinedExercises(db); } - Future _upgradeDatabase(Database db, int oldVersion, int newVersion) async { + Future _upgradeDatabase( + Database db, int oldVersion, int newVersion) async { // Placeholder for future upgrade logic if (oldVersion < newVersion) { // Example: if (oldVersion < 2) { @@ -104,9 +105,8 @@ class DatabaseHelper { Future> getExerciseDates() async { final db = await database; - final List> datesResult = await db.rawQuery( - 'SELECT DISTINCT date(timestamp) as date FROM exercises' - ); + final List> datesResult = await db + .rawQuery('SELECT DISTINCT date(timestamp) as date FROM exercises'); return datesResult.map((row) { return DateTime.parse(row['date']); @@ -136,8 +136,8 @@ class DatabaseHelper { ); } - - Future isNewHighScore(String exerciseName, double newWeight, int newReps) async { + Future isNewHighScore( + String exerciseName, double newWeight, int newReps) async { final db = await database; // Query to get the current highest weight and corresponding reps for that weight @@ -157,7 +157,8 @@ class DatabaseHelper { final maxReps = row['reps'] as int; // Check if both weight and reps are greater than the current record - if (newWeight > maxWeight || (newWeight == maxWeight && newReps > maxReps)) { + if (newWeight > maxWeight || + (newWeight == maxWeight && newReps > maxReps)) { return true; // New record found } } @@ -234,7 +235,8 @@ class DatabaseHelper { Future> getPredefinedExercises() async { final db = await database; - final List> result = await db.query('predefined_exercises'); + final List> result = + await db.query('predefined_exercises'); return result.map((row) => row['name'] as String).toList(); } @@ -243,7 +245,8 @@ class DatabaseHelper { await db.insert( 'predefined_exercises', {'name': exerciseName}, - conflictAlgorithm: ConflictAlgorithm.ignore, // Handle if exercise already exists + conflictAlgorithm: + ConflictAlgorithm.ignore, // Handle if exercise already exists ); } @@ -251,8 +254,7 @@ class DatabaseHelper { final db = await database; // Query to get the maximum weight and corresponding highest reps for each exercise - final List> results = await db.rawQuery( - ''' + final List> results = await db.rawQuery(''' SELECT exercise, weight, reps FROM exercises WHERE (exercise, CAST(weight AS REAL)) IN ( @@ -261,8 +263,7 @@ class DatabaseHelper { GROUP BY exercise ) ORDER BY exercise, CAST(weight AS REAL) DESC, reps DESC - ''' - ); + '''); Map> maxWeights = {}; for (var result in results) { @@ -291,8 +292,8 @@ class DatabaseHelper { return maxWeights; } - - Future?> getLastLoggedExercise(String exerciseName) async { + Future?> getLastLoggedExercise( + String exerciseName) async { final db = await database; final List> result = await db.query( 'exercises', @@ -306,12 +307,12 @@ class DatabaseHelper { final row = result.first; return { 'exercise': row['exercise'], - 'weight': double.tryParse(row['weight']) ?? 0.0, // Convert weight to double + 'weight': + double.tryParse(row['weight']) ?? 0.0, // Convert weight to double 'reps': row['reps'] as int, 'sets': row['sets'] as int, }; } return null; } - } diff --git a/lib/tabs/home.dart b/lib/tabs/home.dart index b0e1d0f..2ba277f 100644 --- a/lib/tabs/home.dart +++ b/lib/tabs/home.dart @@ -24,7 +24,8 @@ class ExerciseStoreHomePage extends StatefulWidget { _ExerciseStoreHomePageState createState() => _ExerciseStoreHomePageState(); } -class _ExerciseStoreHomePageState extends State with SingleTickerProviderStateMixin { +class _ExerciseStoreHomePageState extends State + with SingleTickerProviderStateMixin { final DatabaseHelper _dbHelper = DatabaseHelper(); DateTime _selectedDay = DateTime.now(); late TabController _tabController; @@ -91,7 +92,8 @@ class _ExerciseStoreHomePageState extends State with Sing builder: (BuildContext context) { return AlertDialog( title: const Text('⚠️ Confirm Deletion'), - content: const Text('🚨 Clicking this button deletes all the recorded exercise data. Are you sure you want to do this?'), + content: const Text( + '🚨 Clicking this button deletes all the recorded exercise data. Are you sure you want to do this?'), actions: [ TextButton( child: const Text('No'), @@ -112,7 +114,8 @@ class _ExerciseStoreHomePageState extends State with Sing builder: (BuildContext context) { return AlertDialog( title: const Text('❗️ Are you really sure?'), - content: const Text('💥 Are you really sure you want to lose all your data? There is no going back!'), + content: const Text( + '💥 Are you really sure you want to lose all your data? There is no going back!'), actions: [ TextButton( child: const Text('No'), @@ -164,7 +167,9 @@ class _ExerciseStoreHomePageState extends State with Sing tabs: const [ Tab(icon: Icon(Icons.add), text: 'Log\nExercise'), Tab(icon: Icon(Icons.calendar_today), text: 'Summary'), - Tab(icon: Icon(Icons.celebration), text: 'Records'), // New Records tab + Tab( + icon: Icon(Icons.celebration), + text: 'Records'), // New Records tab Tab(icon: Icon(Icons.show_chart), text: 'Visualize\nData'), Tab(icon: Icon(Icons.table_chart), text: 'View\nTable'), ], @@ -174,7 +179,8 @@ class _ExerciseStoreHomePageState extends State with Sing bucket: bucket, child: PageView( controller: _pageController, - physics: const NeverScrollableScrollPhysics(), // Disable swipe gesture + physics: + const NeverScrollableScrollPhysics(), // Disable swipe gesture onPageChanged: (index) { _tabController.animateTo(index); }, @@ -213,43 +219,44 @@ class _ExerciseStoreHomePageState extends State with Sing } final exercises = snapshot.data!; return DataTable( - columns: const [ - DataColumn(label: Text('ID')), - DataColumn(label: Text('Exercise')), - DataColumn(label: Text('Weight')), - DataColumn(label: Text('Reps')), - DataColumn(label: Text('Sets')), - DataColumn(label: Text('Timestamp')), - DataColumn(label: Text('Actions')), - ], - rows: exercises.map((exercise) { - return DataRow(cells: [ - DataCell(Text(exercise['id'].toString())), - DataCell(Text(exercise['exercise'])), - DataCell(Text(exercise['weight'])), - DataCell(Text(exercise['reps'].toString())), - DataCell(Text(exercise['sets'].toString())), - DataCell(Text(exercise['timestamp'])), // Display the timestamp - DataCell( - Row( - children: [ - IconButton( - icon: const Icon(Icons.edit), - onPressed: () { - _showEditDialog(exercise); - }, - ), - IconButton( - icon: const Icon(Icons.delete), - onPressed: () async { - await _deleteExercise(exercise['id']); - }, - ), - ], - ), + columns: const [ + DataColumn(label: Text('ID')), + DataColumn(label: Text('Exercise')), + DataColumn(label: Text('Weight')), + DataColumn(label: Text('Reps')), + DataColumn(label: Text('Sets')), + DataColumn(label: Text('Timestamp')), + DataColumn(label: Text('Actions')), + ], + rows: exercises.map((exercise) { + return DataRow(cells: [ + DataCell(Text(exercise['id'].toString())), + DataCell(Text(exercise['exercise'])), + DataCell(Text(exercise['weight'])), + DataCell(Text(exercise['reps'].toString())), + DataCell(Text(exercise['sets'].toString())), + DataCell(Text( + exercise['timestamp'])), // Display the timestamp + DataCell( + Row( + children: [ + IconButton( + icon: const Icon(Icons.edit), + onPressed: () { + _showEditDialog(exercise); + }, + ), + IconButton( + icon: const Icon(Icons.delete), + onPressed: () async { + await _deleteExercise(exercise['id']); + }, + ), + ], ), - ]); - }).toList(), + ), + ]); + }).toList(), ); }, ), diff --git a/lib/tabs/inputs.dart b/lib/tabs/inputs.dart index c762d52..17018fa 100644 --- a/lib/tabs/inputs.dart +++ b/lib/tabs/inputs.dart @@ -37,7 +37,8 @@ class _ExerciseSetterState extends State { List exercises = await _dbHelper.getPredefinedExercises(); setState(() { _predefinedExercises = exercises; - _selectedExercise = _predefinedExercises.isNotEmpty ? _predefinedExercises.first : null; + _selectedExercise = + _predefinedExercises.isNotEmpty ? _predefinedExercises.first : null; if (_selectedExercise != null) { _loadLastLoggedExercise(); } @@ -46,7 +47,8 @@ class _ExerciseSetterState extends State { Future _loadLastLoggedExercise() async { if (_selectedExercise != null) { - final lastLogged = await _dbHelper.getLastLoggedExercise(_selectedExercise!); + final lastLogged = + await _dbHelper.getLastLoggedExercise(_selectedExercise!); if (lastLogged != null) { setState(() { _lastExerciseName = lastLogged['exercise']; @@ -71,7 +73,8 @@ class _ExerciseSetterState extends State { final reps = int.parse(_repsController.text); final sets = int.parse(_setsController.text); - final isNewHighScore = await _dbHelper.isNewHighScore(exerciseName, weight, reps); + final isNewHighScore = + await _dbHelper.isNewHighScore(exerciseName, weight, reps); await _dbHelper.insertExercise( exercise: exerciseName, @@ -169,10 +172,16 @@ class _ExerciseSetterState extends State { child: GestureDetector( onTap: _openExerciseSelectionSheet, child: InputDecorator( - decoration: const InputDecoration(labelText: 'Select Exercise'), + decoration: + const InputDecoration(labelText: 'Select Exercise'), child: Text( - _isAddingNewExercise ? 'Add New Exercise' : _selectedExercise ?? 'Select Exercise', - style: TextStyle(color: _selectedExercise == null ? Colors.grey : Colors.black), + _isAddingNewExercise + ? 'Add New Exercise' + : _selectedExercise ?? 'Select Exercise', + style: TextStyle( + color: _selectedExercise == null + ? Colors.grey + : Colors.black), ), ), ), @@ -184,9 +193,11 @@ class _ExerciseSetterState extends State { padding: const EdgeInsets.only(top: 16.0), child: TextFormField( controller: _newExerciseController, - decoration: const InputDecoration(labelText: 'New Exercise Name'), + decoration: + const InputDecoration(labelText: 'New Exercise Name'), validator: (value) { - if (_isAddingNewExercise && (value == null || value.isEmpty)) { + if (_isAddingNewExercise && + (value == null || value.isEmpty)) { return 'Please enter a new exercise name'; } return null; diff --git a/lib/tabs/summary.dart b/lib/tabs/summary.dart index 36702cf..81786ea 100644 --- a/lib/tabs/summary.dart +++ b/lib/tabs/summary.dart @@ -39,7 +39,8 @@ class _SummaryTabState extends State { const Text('Select Day: '), TextButton( onPressed: () => _selectDate(context), - child: Text('${widget.selectedDay.year}-${widget.selectedDay.month}-${widget.selectedDay.day}'), + child: Text( + '${widget.selectedDay.year}-${widget.selectedDay.month}-${widget.selectedDay.day}'), ), ], ), @@ -65,7 +66,8 @@ class _SummaryTabState extends State { final totalSets = details['totalSets']; final totalReps = details['totalReps']; final avgWeight = details['avgWeight']; - final records = details['records'] as List>; + final records = + details['records'] as List>; return Card( child: ExpansionTile( diff --git a/lib/tabs/visualization.dart b/lib/tabs/visualization.dart index ffe5417..56363c1 100644 --- a/lib/tabs/visualization.dart +++ b/lib/tabs/visualization.dart @@ -27,7 +27,10 @@ class _VisualizationTabState extends State { Future _fetchExerciseNames() async { try { final variables = await _dbHelper.getExercises(); - final names = variables.map((exercise) => exercise['exercise'] as String).toSet().toList(); + final names = variables + .map((exercise) => exercise['exercise'] as String) + .toSet() + .toList(); setState(() { _exerciseNames = names; }); @@ -39,11 +42,14 @@ class _VisualizationTabState extends State { Future _fetchDataPoints(String exerciseName) async { try { final exercises = await _dbHelper.getExercises(); - final filteredExercises = exercises.where((exercise) => exercise['exercise'] == exerciseName).toList(); + final filteredExercises = exercises + .where((exercise) => exercise['exercise'] == exerciseName) + .toList(); final groupedByDate = >{}; for (var exercise in filteredExercises) { - final dateTime = DateUtils.dateOnly(DateTime.parse(exercise['timestamp'])); + final dateTime = + DateUtils.dateOnly(DateTime.parse(exercise['timestamp'])); final weight = double.parse(exercise['weight']); if (groupedByDate.containsKey(dateTime)) { groupedByDate[dateTime]!.add(weight); @@ -53,7 +59,8 @@ class _VisualizationTabState extends State { } final aggregatedDataPoints = []; - final earliestDate = DateUtils.dateOnly(DateTime.parse(filteredExercises.last['timestamp'])); + final earliestDate = DateUtils.dateOnly( + DateTime.parse(filteredExercises.last['timestamp'])); groupedByDate.forEach((date, weights) { double value; switch (_aggregationMethod) { @@ -208,7 +215,8 @@ class _VisualizationTabState extends State { scatterSpots: _dataPoints, scatterTouchData: ScatterTouchData( touchTooltipData: ScatterTouchTooltipData( - getTooltipColor: (ScatterSpot touchedSpot) => Colors.blueAccent, + getTooltipColor: (ScatterSpot touchedSpot) => + Colors.blueAccent, ), enabled: true, ), diff --git a/lib/widgets/exercise_edit_dialog.dart b/lib/widgets/exercise_edit_dialog.dart index 4657376..a34dd05 100644 --- a/lib/widgets/exercise_edit_dialog.dart +++ b/lib/widgets/exercise_edit_dialog.dart @@ -20,11 +20,16 @@ class _ExerciseEditDialogState extends State { @override void initState() { super.initState(); - _exerciseController = TextEditingController(text: widget.exerciseData['exercise']); - _weightController = TextEditingController(text: widget.exerciseData['weight']); - _repsController = TextEditingController(text: widget.exerciseData['reps'].toString()); - _setsController = TextEditingController(text: widget.exerciseData['sets'].toString()); - _timestampController = TextEditingController(text: widget.exerciseData['timestamp']); + _exerciseController = + TextEditingController(text: widget.exerciseData['exercise']); + _weightController = + TextEditingController(text: widget.exerciseData['weight']); + _repsController = + TextEditingController(text: widget.exerciseData['reps'].toString()); + _setsController = + TextEditingController(text: widget.exerciseData['sets'].toString()); + _timestampController = + TextEditingController(text: widget.exerciseData['timestamp']); } @override @@ -90,7 +95,8 @@ class _ExerciseEditDialogState extends State { ), TextFormField( controller: _timestampController, - decoration: const InputDecoration(labelText: 'Timestamp (ISO8601)'), + decoration: + const InputDecoration(labelText: 'Timestamp (ISO8601)'), keyboardType: TextInputType.datetime, validator: (value) { if (value == null || value.isEmpty) {