This commit is contained in:
2025-07-17 07:04:38 -04:00
parent 2d0e327334
commit f63bb0ba41
25 changed files with 592 additions and 92 deletions

View File

@ -22,44 +22,38 @@ struct SplitPickerView: View {
var body: some View {
NavigationStack {
VStack {
Form {
Section (header: Text("This Split")) {
List {
ForEach(splits) { split in
Button(action: {
onSplitSelected(split)
dismiss()
}) {
HStack {
Text(split.name)
.font(.headline)
Spacer()
Text("\(split.exercises?.count ?? 0)")
.font(.caption)
}
.contentShape(Rectangle())
ScrollView {
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 16) {
ForEach(splits) { split in
Button(action: {
onSplitSelected(split)
dismiss()
}) {
VStack {
ZStack {
RoundedRectangle(cornerRadius: 12)
.fill(split.getColor())
.aspectRatio(1, contentMode: .fit)
.shadow(radius: 2)
Image(systemName: split.systemImage)
.font(.system(size: 30))
.foregroundColor(.white)
}
.buttonStyle(.plain)
Text(split.name)
.font(.headline)
.lineLimit(1)
Text("\(split.exercises?.count ?? 0) exercises")
.font(.caption)
.foregroundColor(.secondary)
}
}
.buttonStyle(PlainButtonStyle())
}
// Section (header: Text("Additional Exercises")) {
// List {
// ForEach(exercises) { exercise in
// Button(action: {
// onExerciseSelected(exercise)
// dismiss()
// }) {
// Text(exercise.name)
// }
// .contentShape(Rectangle())
// .buttonStyle(.plain)
// }
// }
// }
}
.padding()
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {

View File

@ -8,6 +8,7 @@
//
import SwiftUI
import SwiftData
struct WorkoutLogView: View {
@Environment(\.modelContext) private var modelContext
@ -21,7 +22,7 @@ struct WorkoutLogView: View {
var sortedWorkoutLogs: [WorkoutLog] {
if let logs = workout.logs {
logs.sorted(by: {
$0.completed == $1.completed ? $0.exercise!.name < $1.exercise!.name : !$0.completed
$0.order == $1.order ? $0.exercise!.name < $1.exercise!.name : $0.order < $1.order
})
} else {
[]
@ -33,33 +34,54 @@ struct WorkoutLogView: View {
Section (header: Text("\(workout.label)")) {
List {
ForEach (sortedWorkoutLogs) { log in
let badges = log.completed ? [Badge(text: "Completed", color: .green)] : []
ListItem(
title: log.exercise?.name ?? "Untitled Exercise",
subtitle: "\(log.sets) sets × \(log.reps) reps × \(log.weight) lbs",
badges: badges
// Handle optional status, defaulting to a status based on completed flag if nil
let _ = print("DEBUG: workoutLog.status=\(log.status)")
let workoutLogStatus = log.status?.checkboxStatus ?? (log.completed ? CheckboxStatus.checked : CheckboxStatus.unchecked)
CheckboxListItem(
status: workoutLogStatus,
title: log.exercise?.name ?? Exercise.unnamed,
subtitle: "\(log.sets) sets × \(log.reps) reps × \(log.weight) lbs"
)
.swipeActions(edge: .leading, allowsFullSwipe: false) {
if (log.completed) {
let status = log.status ?? WorkoutStatus.notStarted
if [.inProgress,.completed].contains(status) {
Button {
withAnimation {
log.completed = false
log.status = .notStarted
try? modelContext.save()
}
} label: {
Label("Complete", systemImage: "circle.fill")
Label("Not Started", systemImage: WorkoutStatus.notStarted.checkboxStatus.systemName)
}
.tint(.green)
} else {
.tint(WorkoutStatus.notStarted.checkboxStatus.color)
}
if [.notStarted,.completed].contains(status) {
Button {
withAnimation {
log.completed = true
log.status = .inProgress
try? modelContext.save()
}
} label: {
Label("Reset", systemImage: "checkmark.circle.fill")
Label("In Progress", systemImage: WorkoutStatus.inProgress.checkboxStatus.systemName)
}
.tint(.green)
.tint(WorkoutStatus.inProgress.checkboxStatus.color)
}
if [.notStarted,.inProgress].contains(status) {
Button {
withAnimation {
log.status = .completed
try? modelContext.save()
}
} label: {
Label("Complete", systemImage: WorkoutStatus.completed.checkboxStatus.systemName)
}
.tint(WorkoutStatus.completed.checkboxStatus.color)
}
}
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
@ -89,13 +111,14 @@ struct WorkoutLogView: View {
}
.sheet(isPresented: $showingAddSheet) {
ExercisePickerView { exercise in
let setsRepsWeight = getSetsRepsWeight(exercise, in: modelContext)
let workoutLog = WorkoutLog(
workout: workout,
exercise: exercise,
date: Date(),
sets: exercise.sets,
reps: exercise.reps,
weight: exercise.weight,
sets: setsRepsWeight.sets,
reps: setsRepsWeight.reps,
weight: setsRepsWeight.weight,
completed: false
)
workout.logs?.append(workoutLog)
@ -130,4 +153,34 @@ struct WorkoutLogView: View {
}
}
func getSetsRepsWeight(_ exercise: Exercise, in modelContext: ModelContext) -> SetsRepsWeight {
// Use a single expression predicate that works with SwiftData
let exerciseID = exercise.persistentModelID
print("Searching for exercise ID: \(exerciseID)")
var descriptor = FetchDescriptor<WorkoutLog>(
predicate: #Predicate<WorkoutLog> { log in
log.exercise?.persistentModelID == exerciseID
},
sortBy: [SortDescriptor(\WorkoutLog.date, order: .reverse)]
)
descriptor.fetchLimit = 1
let results = try? modelContext.fetch(descriptor)
if let log = results?.first {
return SetsRepsWeight(sets: log.sets, reps: log.reps, weight: log.weight)
} else {
return SetsRepsWeight(sets: 3, reps: 10, weight: 40)
}
}
}
struct SetsRepsWeight {
let sets: Int
let reps: Int
let weight: Int
}

View File

@ -100,10 +100,10 @@ struct WorkoutsView: View {
workout: workout,
exercise: exercise,
date: Date(),
order: assignment.order,
sets: assignment.sets,
reps: assignment.reps,
weight: assignment.weight,
completed: false
weight: assignment.weight
)
modelContext.insert(workoutLog)
} else {