// // WeightProgressionChartView.swift // Workouts // // Created on 7/20/25. // // Copyright 2025 Rouslan Zenetl. All Rights Reserved. // import SwiftUI import Charts import SwiftData struct WeightProgressionChartView: View { let exerciseName: String /// Completed logs for this exercise, oldest first. @Query private var logs: [WorkoutLog] init(exerciseName: String) { self.exerciseName = exerciseName let name = exerciseName _logs = Query( filter: #Predicate { $0.exerciseName == name && $0.completed }, sort: \WorkoutLog.date, order: .forward ) } private var weightData: [WeightDataPoint] { logs.map { WeightDataPoint(date: $0.date, weight: $0.weight) } } var body: some View { VStack(alignment: .leading) { if weightData.isEmpty { Text("No weight history available yet.") .foregroundColor(.secondary) .frame(maxWidth: .infinity, alignment: .center) .padding() } else { Text("Weight Progression") .font(.headline) .padding(.bottom, 4) Chart { ForEach(weightData) { dataPoint in LineMark( x: .value("Date", dataPoint.date), y: .value("Weight", dataPoint.weight) ) .foregroundStyle(Color.blue.gradient) .interpolationMethod(.catmullRom) PointMark( x: .value("Date", dataPoint.date), y: .value("Weight", dataPoint.weight) ) .foregroundStyle(Color.blue) } } .chartYScale(domain: .automatic(includesZero: false)) .chartXAxis { AxisMarks(values: .automatic) { _ in AxisGridLine() AxisValueLabel(format: .dateTime.month().day()) } } .frame(height: 200) .padding(.bottom, 8) if !motivationalMessage.isEmpty { Text(motivationalMessage) .font(.subheadline) .foregroundColor(.primary) .padding() .background(Color.blue.opacity(0.1)) .cornerRadius(8) } } } .padding() } private var motivationalMessage: String { let data = weightData guard data.count >= 2 else { return "Complete more workouts to track your progress!" } let firstWeight = data.first?.weight ?? 0 let currentWeight = data.last?.weight ?? 0 let weightDifference = currentWeight - firstWeight if weightDifference > 0 { let percentIncrease = firstWeight > 0 ? Int((Double(weightDifference) / Double(firstWeight)) * 100) : 0 if percentIncrease >= 20 { return "Amazing progress! You've increased your weight by \(weightDifference) lbs (\(percentIncrease)%)!" } else if percentIncrease >= 10 { return "Great job! You've increased your weight by \(weightDifference) lbs (\(percentIncrease)%)!" } else { return "You're making progress! Weight increased by \(weightDifference) lbs. Keep it up!" } } else if weightDifference == 0 { return "You're maintaining consistent weight. Focus on form and consider increasing when ready!" } else { return "Your current weight is lower than when you started. Adjust your training as needed and keep pushing!" } } } // Data structure for chart points struct WeightDataPoint: Identifiable { let id = UUID() let date: Date let weight: Int }