// // WorkoutLogListView.swift // Workouts Watch App // // Copyright 2025 Rouslan Zenetl. All Rights Reserved. // import SwiftUI import CoreData struct WorkoutLogListView: View { @Environment(\.managedObjectContext) private var viewContext @ObservedObject var workout: Workout @State private var showingExercisePicker = false @State private var selectedLog: WorkoutLog? var sortedWorkoutLogs: [WorkoutLog] { workout.logsArray } var body: some View { List { Section(header: Text(workout.label)) { ForEach(sortedWorkoutLogs, id: \.objectID) { log in Button { selectedLog = log } label: { WorkoutLogRowLabel(log: log) } .buttonStyle(.plain) } } Section { Button { showingExercisePicker = true } label: { HStack { Image(systemName: "plus.circle.fill") .foregroundColor(.green) Text("Add Exercise") } } } } .overlay { if sortedWorkoutLogs.isEmpty { ContentUnavailableView( "No Exercises", systemImage: "figure.strengthtraining.traditional", description: Text("Tap + to add exercises.") ) } } .navigationTitle(workout.split?.name ?? Split.unnamed) .navigationDestination(item: $selectedLog) { log in ExerciseProgressView(workoutLog: log) } .sheet(isPresented: $showingExercisePicker) { ExercisePickerView(workout: workout) } } } // MARK: - Workout Log Row Label struct WorkoutLogRowLabel: View { @ObservedObject var log: WorkoutLog var body: some View { HStack { statusIcon .foregroundColor(statusColor) VStack(alignment: .leading, spacing: 2) { Text(log.exerciseName) .font(.headline) .lineLimit(1) Text(subtitle) .font(.caption2) .foregroundColor(.secondary) } Spacer() } } private var statusIcon: Image { switch log.status { case .completed: Image(systemName: "checkmark.circle.fill") case .inProgress: Image(systemName: "circle.dotted") case .notStarted: Image(systemName: "circle") case .skipped: Image(systemName: "xmark.circle") } } private var statusColor: Color { switch log.status { case .completed: .green case .inProgress: .orange case .notStarted: .secondary case .skipped: .secondary } } private var subtitle: String { if log.loadTypeEnum == .duration { let mins = log.durationMinutes let secs = log.durationSeconds if mins > 0 && secs > 0 { return "\(log.sets) × \(mins)m \(secs)s" } else if mins > 0 { return "\(log.sets) × \(mins) min" } else { return "\(log.sets) × \(secs) sec" } } else { return "\(log.sets) × \(log.reps) × \(log.weight) lbs" } } } // MARK: - Exercise Picker View struct ExercisePickerView: View { @Environment(\.managedObjectContext) private var viewContext @Environment(\.dismiss) private var dismiss @ObservedObject var workout: Workout private var availableExercises: [Exercise] { guard let split = workout.split else { return [] } let existingNames = Set(workout.logsArray.map { $0.exerciseName }) return split.exercisesArray.filter { !existingNames.contains($0.name) } } var body: some View { NavigationStack { List { if availableExercises.isEmpty { Text("All exercises added") .foregroundColor(.secondary) } else { ForEach(availableExercises, id: \.objectID) { exercise in Button { addExercise(exercise) } label: { VStack(alignment: .leading) { Text(exercise.name) .font(.headline) Text(exerciseSubtitle(exercise)) .font(.caption2) .foregroundColor(.secondary) } } } } } .navigationTitle("Add Exercise") .toolbar { ToolbarItem(placement: .cancellationAction) { Button("Cancel") { dismiss() } } } } } private func addExercise(_ exercise: Exercise) { let log = WorkoutLog(context: viewContext) log.exerciseName = exercise.name log.date = Date() log.order = Int32(workout.logsArray.count) log.sets = exercise.sets log.reps = exercise.reps log.weight = exercise.weight log.loadType = exercise.loadType log.duration = exercise.duration log.status = .notStarted log.workout = workout // Update workout start if first exercise if workout.logsArray.count == 1 { workout.start = Date() } try? viewContext.save() // Sync to iOS WatchConnectivityManager.shared.syncToiOS() dismiss() } private func exerciseSubtitle(_ exercise: Exercise) -> String { let loadType = LoadType(rawValue: Int(exercise.loadType)) ?? .weight if loadType == .duration { let mins = exercise.durationMinutes let secs = exercise.durationSeconds if mins > 0 && secs > 0 { return "\(exercise.sets) × \(mins)m \(secs)s" } else if mins > 0 { return "\(exercise.sets) × \(mins) min" } else { return "\(exercise.sets) × \(secs) sec" } } else { return "\(exercise.sets) × \(exercise.reps) × \(exercise.weight) lbs" } } } #Preview { WorkoutLogListView(workout: Workout()) .environment(\.managedObjectContext, PersistenceController.preview.viewContext) }