// // WeightProgressionChartView.swift // Workouts // // Created on 7/20/25. // // Copyright 2025 Rouslan Zenetl. All Rights Reserved. // import SwiftUI import Charts import SwiftData struct WeightProgressionChartView: View { @Environment(\.modelContext) private var modelContext let exerciseName: String @State private var weightData: [WeightDataPoint] = [] @State private var isLoading: Bool = true @State private var motivationalMessage: String = "" var body: some View { VStack(alignment: .leading) { if isLoading { ProgressView("Loading data...") } else 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) { value 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() .onAppear { loadWeightData() } } private func loadWeightData() { isLoading = true // Create a fetch descriptor to get workout logs for this exercise let descriptor = FetchDescriptor( predicate: #Predicate { log in log.exerciseName == exerciseName && log.completed == true }, sortBy: [SortDescriptor(\WorkoutLog.date)] ) // Fetch the data if let logs = try? modelContext.fetch(descriptor) { // Convert to data points weightData = logs.map { log in WeightDataPoint(date: log.date, weight: log.weight) } // Generate motivational message based on progress generateMotivationalMessage() } isLoading = false } private func generateMotivationalMessage() { guard weightData.count >= 2 else { motivationalMessage = "Complete more workouts to track your progress!" return } // Calculate progress metrics let firstWeight = weightData.first?.weight ?? 0 let currentWeight = weightData.last?.weight ?? 0 let weightDifference = currentWeight - firstWeight // Generate appropriate message based on progress if weightDifference > 0 { let percentIncrease = Int((Double(weightDifference) / Double(firstWeight)) * 100) if percentIncrease >= 20 { motivationalMessage = "Amazing progress! You've increased your weight by \(weightDifference) lbs (\(percentIncrease)%)! 💪" } else if percentIncrease >= 10 { motivationalMessage = "Great job! You've increased your weight by \(weightDifference) lbs (\(percentIncrease)%)! 🎉" } else { motivationalMessage = "You're making progress! Weight increased by \(weightDifference) lbs. Keep it up! 👍" } } else if weightDifference == 0 { motivationalMessage = "You're maintaining consistent weight. Focus on form and consider increasing when ready!" } else { motivationalMessage = "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 } #Preview { WeightProgressionChartView(exerciseName: "Bench Press") .modelContainer(for: [WorkoutLog.self], inMemory: true) }