From 6e46775f58795422a1fc330999b63a9cf80fb680 Mon Sep 17 00:00:00 2001 From: rzen Date: Sat, 19 Jul 2025 07:19:37 -0400 Subject: [PATCH] wip --- .../Views/Splits/DraggableSplitItem.swift | 39 +++--- Workouts/Views/Splits/ExerciseView.swift | 129 ++++++++++++++++++ .../Views/Splits/SplitExercisesListView.swift | 39 +++--- Workouts/Views/Workouts/WorkoutLogView.swift | 121 ++++++++-------- 4 files changed, 233 insertions(+), 95 deletions(-) create mode 100644 Workouts/Views/Splits/ExerciseView.swift diff --git a/Workouts/Views/Splits/DraggableSplitItem.swift b/Workouts/Views/Splits/DraggableSplitItem.swift index 04ecad1..867531c 100644 --- a/Workouts/Views/Splits/DraggableSplitItem.swift +++ b/Workouts/Views/Splits/DraggableSplitItem.swift @@ -31,26 +31,31 @@ struct DraggableSplitItem: View { .aspectRatio(1.618, contentMode: .fit) .shadow(radius: 2) - VStack { - // Icon in the center - Image(systemName: systemImageName) - .font(.system(size: 40, weight: .bold)) - .offset(y: -15) - - // Name at the bottom inside the rectangle - Text(name) - .font(.headline) - .lineLimit(1) - .padding(.horizontal, 8) + GeometryReader { geometry in + VStack(spacing: 4) { + Spacer() + + // Icon in the center - now using dynamic sizing + Image(systemName: systemImageName) + .font(.system(size: min(geometry.size.width * 0.3, 40), weight: .bold)) + .scaledToFit() + .frame(maxWidth: geometry.size.width * 0.6, maxHeight: geometry.size.height * 0.4) + .padding(.bottom, 4) + + // Name at the bottom inside the rectangle + Text(name) + .font(.headline) + .lineLimit(1) + .padding(.horizontal, 8) - Text("\(exerciseCount) exercises") - .font(.caption) - .padding(.bottom, 8) + Text("\(exerciseCount) exercises") + .font(.caption) + .padding(.bottom, 8) + } + .foregroundColor(.white) + .frame(width: geometry.size.width, height: geometry.size.height) } - .foregroundColor(.white) } } } } - - diff --git a/Workouts/Views/Splits/ExerciseView.swift b/Workouts/Views/Splits/ExerciseView.swift new file mode 100644 index 0000000..f58f797 --- /dev/null +++ b/Workouts/Views/Splits/ExerciseView.swift @@ -0,0 +1,129 @@ +// +// ExerciseView.swift +// Workouts +// +// Created by rzen on 7/18/25 at 5:44 PM. +// +// Copyright 2025 Rouslan Zenetl. All Rights Reserved. +// + +import SwiftUI +import SwiftData + +struct ExerciseView: View { + @Environment(\.modelContext) private var modelContext + @Environment(\.dismiss) private var dismiss + + @Bindable var workoutLog: WorkoutLog + + @State var allLogs: [WorkoutLog] + var currentIndex: Int = 0 + + @State private var progress: Int = 0 + @State private var navigateTo: WorkoutLog? = nil + + let notStartedColor = Color.white + let completedColor = Color.green + + var body: some View { + Form { + Section(header: Text("Navigation")) { + HStack { + Button(action: navigateToPrevious) { + HStack { + Image(systemName: "chevron.left") + Text("Previous") + } + } + .disabled(currentIndex <= 0) + + Spacer() + Text("\(currentIndex)") + Spacer() + + Button(action: navigateToNext) { + HStack { + Text("Next") + Image(systemName: "chevron.right") + } + } + .disabled(currentIndex >= allLogs.count - 1) + } + .padding(.vertical, 8) + } + + Section (header: Text("Progress")) { + LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: workoutLog.sets), spacing: 2) { + ForEach (1...workoutLog.sets, id: \.self) { index in + ZStack { + let completed = index <= progress + let color = completed ? completedColor : notStartedColor + RoundedRectangle(cornerRadius: 8) + .fill( + LinearGradient( + gradient: Gradient(colors: [color, color.darker(by: 0.2)]), + startPoint: .topLeading, + endPoint: .bottomTrailing + ) + ) + .aspectRatio(0.618, contentMode: .fit) + .shadow(radius: 2) + Text("\(index)") + .foregroundColor(.primary) + .colorInvert() + } + .onTapGesture { + if progress == index { + progress = 0 + } else { + progress = index + } + let _ = print("progress set to \(progress)") + } + } + } + } + + Section (header: Text("Plan")) { + Stepper("\(workoutLog.sets) sets", value: $workoutLog.sets, in: 1...10) + .font(.title) + Stepper("\(workoutLog.reps) reps", value: $workoutLog.reps, in: 1...25) + .font(.title) + HStack { + Text("\(workoutLog.weight) lbs") + VStack (alignment: .trailing) { + Stepper("", value: $workoutLog.weight, in: 1...200) + Stepper("", value: $workoutLog.weight, in: 1...200, step: 5) + } + } + .font(.title) + } + } + .navigationTitle("\(workoutLog.exerciseName)") + .navigationDestination(item: $navigateTo) { nextLog in + ExerciseView( + workoutLog: nextLog, + allLogs: allLogs, + currentIndex: allLogs.firstIndex(of: nextLog) ?? 0 + ) + } +// .onAppear { +// allLogs = modelContext.fetch(FetchDescriptor(sortBy: [ +// SortDescriptor(\WorkoutLog.order), +// SortDescriptor(\WorkoutLog.name) +// ])) +// } + } + + private func navigateToPrevious() { + guard currentIndex > 0 else { return } + let previousIndex = currentIndex - 1 + navigateTo = allLogs[previousIndex] + } + + private func navigateToNext() { + guard currentIndex < allLogs.count - 1 else { return } + let nextIndex = currentIndex + 1 + navigateTo = allLogs[nextIndex] + } +} diff --git a/Workouts/Views/Splits/SplitExercisesListView.swift b/Workouts/Views/Splits/SplitExercisesListView.swift index 4fdd612..f3e488a 100644 --- a/Workouts/Views/Splits/SplitExercisesListView.swift +++ b/Workouts/Views/Splits/SplitExercisesListView.swift @@ -61,8 +61,18 @@ struct SplitExercisesListView: View { } } }) + + Button { + showingAddSheet = true + } label: { + ListItem(title: "Add Exercise") + } + } else { Text("No exercises added yet.") + Button(action: { showingAddSheet.toggle() }) { + ListItem(title: "Add Exercise") + } } } } @@ -98,11 +108,6 @@ struct SplitExercisesListView: View { .navigationDestination(item: $createdWorkout, destination: { workout in WorkoutLogView(workout: workout) }) -// .sheet(item: $createdWorkout) { workout in -// NavigationStack { -// WorkoutLogView(workout: workout) -// } -// } // .toolbar { // ToolbarItem(placement: .navigationBarTrailing) { // Button(action: { showingAddSheet.toggle() }) { @@ -110,18 +115,18 @@ struct SplitExercisesListView: View { // } // } // } -// .sheet (isPresented: $showingAddSheet) { -// ExercisePickerView { exerciseName in -// itemToEdit = SplitExerciseAssignment( -// split: model, -// exerciseName: exerciseName, -// order: 0, -// sets: 3, -// reps: 10, -// weight: 40 -// ) -// } -// } + .sheet (isPresented: $showingAddSheet) { + ExercisePickerView { exerciseName in + itemToEdit = SplitExerciseAssignment( + split: model, + exerciseName: exerciseName, + order: 0, + sets: 3, + reps: 10, + weight: 40 + ) + } + } .sheet(item: $itemToEdit) { item in SplitExerciseAssignmentAddEditView(model: item) } diff --git a/Workouts/Views/Workouts/WorkoutLogView.swift b/Workouts/Views/Workouts/WorkoutLogView.swift index d84ace4..7e10f92 100644 --- a/Workouts/Views/Workouts/WorkoutLogView.swift +++ b/Workouts/Views/Workouts/WorkoutLogView.swift @@ -34,70 +34,69 @@ struct WorkoutLogView: View { Section (header: Text("\(workout.label)")) { List { ForEach (sortedWorkoutLogs) { log in - // 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.exerciseName, - subtitle: "\(log.sets) sets × \(log.reps) reps × \(log.weight) lbs" - ) + NavigationLink(destination: ExerciseView(workoutLog: log, allLogs: sortedWorkoutLogs)) { + CheckboxListItem( + status: workoutLogStatus, + title: log.exerciseName, + subtitle: "\(log.sets) sets × \(log.reps) reps × \(log.weight) lbs" + ) + .swipeActions(edge: .leading, allowsFullSwipe: false) { + let status = log.status ?? WorkoutStatus.notStarted + + if [.inProgress,.completed].contains(status) { + Button { + withAnimation { + log.status = .notStarted + try? modelContext.save() + } + } label: { + Label("Not Started", systemImage: WorkoutStatus.notStarted.checkboxStatus.systemName) + } + .tint(WorkoutStatus.notStarted.checkboxStatus.color) + } + + if [.notStarted,.completed].contains(status) { + Button { + withAnimation { + log.status = .inProgress + try? modelContext.save() + } + } label: { + Label("In Progress", systemImage: WorkoutStatus.inProgress.checkboxStatus.systemName) + } + .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) { + Button { + itemToDelete = log + } label: { + Label("Delete", systemImage: "trash") + } + .tint(.secondary) + Button { + itemToEdit = log + } label: { + Label("Edit", systemImage: "pencil") + } + .tint(.indigo) + } + } - .swipeActions(edge: .leading, allowsFullSwipe: false) { - let status = log.status ?? WorkoutStatus.notStarted - - if [.inProgress,.completed].contains(status) { - Button { - withAnimation { - log.status = .notStarted - try? modelContext.save() - } - } label: { - Label("Not Started", systemImage: WorkoutStatus.notStarted.checkboxStatus.systemName) - } - .tint(WorkoutStatus.notStarted.checkboxStatus.color) - } - - if [.notStarted,.completed].contains(status) { - Button { - withAnimation { - log.status = .inProgress - try? modelContext.save() - } - } label: { - Label("In Progress", systemImage: WorkoutStatus.inProgress.checkboxStatus.systemName) - } - .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) { - Button { - itemToDelete = log - } label: { - Label("Delete", systemImage: "trash") - } - .tint(.secondary) - Button { - itemToEdit = log - } label: { - Label("Edit", systemImage: "pencil") - } - .tint(.indigo) - } } } }