wip
This commit is contained in:
198
Workouts/Views/Splits/SplitDetailView.swift
Normal file
198
Workouts/Views/Splits/SplitDetailView.swift
Normal file
@ -0,0 +1,198 @@
|
||||
//
|
||||
// SplitDetailView.swift
|
||||
// Workouts
|
||||
//
|
||||
// Created by rzen on 7/25/25 at 3:27 PM.
|
||||
//
|
||||
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SwiftData
|
||||
|
||||
struct SplitDetailView: View {
|
||||
@Environment(\.modelContext) private var modelContext
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
|
||||
@State var split: Split
|
||||
|
||||
@State private var showingAddSheet: Bool = false
|
||||
@State private var itemToEdit: Exercise? = nil
|
||||
@State private var itemToDelete: Exercise? = nil
|
||||
@State private var createdWorkout: Workout? = nil
|
||||
@State private var showingDeleteConfirmation: Bool = false
|
||||
|
||||
var body: some View {
|
||||
|
||||
NavigationStack {
|
||||
Form {
|
||||
Section (header: Text("What is a Split?")) {
|
||||
Text("A “split” is simply how you divide (or “split up”) your weekly training across different days. Instead of working every muscle group every session, you assign certain muscle groups, movement patterns, or training emphases to specific days.")
|
||||
.font(.caption)
|
||||
}
|
||||
|
||||
Section (header: Text("Exercises")) {
|
||||
List {
|
||||
if let assignments = split.exercises, !assignments.isEmpty {
|
||||
let sortedAssignments = assignments.sorted(by: { $0.order == $1.order ? $0.name < $1.name : $0.order < $1.order })
|
||||
|
||||
ForEach(sortedAssignments) { item in
|
||||
ListItem(
|
||||
title: item.name,
|
||||
subtitle: "\(item.sets) × \(item.reps) × \(item.weight) lbs \(item.order)"
|
||||
)
|
||||
.swipeActions {
|
||||
Button {
|
||||
itemToDelete = item
|
||||
} label: {
|
||||
Label("Delete", systemImage: "trash")
|
||||
}
|
||||
.tint(.red)
|
||||
Button {
|
||||
itemToEdit = item
|
||||
} label: {
|
||||
Label("Edit", systemImage: "pencil")
|
||||
}
|
||||
.tint(.indigo)
|
||||
}
|
||||
}
|
||||
.onMove(perform: { indices, destination in
|
||||
var exerciseArray = Array(sortedAssignments)
|
||||
exerciseArray.move(fromOffsets: indices, toOffset: destination)
|
||||
for (index, exercise) in exerciseArray.enumerated() {
|
||||
exercise.order = index
|
||||
}
|
||||
if let modelContext = exerciseArray.first?.modelContext {
|
||||
do {
|
||||
try modelContext.save()
|
||||
} catch {
|
||||
print("Error saving after reordering: \(error)")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Button {
|
||||
showingAddSheet = true
|
||||
} label: {
|
||||
ListItem(title: "Add Exercise")
|
||||
}
|
||||
|
||||
} else {
|
||||
Text("No exercises added yet.")
|
||||
Button(action: { showingAddSheet.toggle() }) {
|
||||
ListItem(title: "Add Exercise")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button ("Delete This Split", role: .destructive) {
|
||||
showingDeleteConfirmation = true
|
||||
}
|
||||
.tint(.red)
|
||||
}
|
||||
.navigationTitle("\(split.name)")
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button("Start This Split") {
|
||||
let workout = Workout(start: Date(), end: Date(), split: split)
|
||||
modelContext.insert(workout)
|
||||
if let exercises = split.exercises {
|
||||
for assignment in exercises {
|
||||
let workoutLog = WorkoutLog(
|
||||
workout: workout,
|
||||
exerciseName: assignment.name,
|
||||
date: Date(),
|
||||
order: assignment.order,
|
||||
sets: assignment.sets,
|
||||
reps: assignment.reps,
|
||||
weight: assignment.weight
|
||||
)
|
||||
modelContext.insert(workoutLog)
|
||||
}
|
||||
}
|
||||
try? modelContext.save()
|
||||
|
||||
// Set the created workout to trigger navigation
|
||||
createdWorkout = workout
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationDestination(item: $createdWorkout, destination: { workout in
|
||||
WorkoutLogListView(workout: workout)
|
||||
})
|
||||
.sheet (isPresented: $showingAddSheet) {
|
||||
ExercisePickerView(onExerciseSelected: { exerciseNames in
|
||||
let splitId = split.persistentModelID
|
||||
print("exerciseNames: \(exerciseNames)")
|
||||
if exerciseNames.count == 1 {
|
||||
itemToEdit = Exercise(
|
||||
split: split,
|
||||
exerciseName: exerciseNames.first ?? "Exercise.unnamed",
|
||||
order: 0,
|
||||
sets: 3,
|
||||
reps: 10,
|
||||
weight: 40
|
||||
)
|
||||
} else {
|
||||
for exerciseName in exerciseNames {
|
||||
var duplicateExercise: [Exercise]? = nil
|
||||
do {
|
||||
duplicateExercise = try modelContext.fetch(FetchDescriptor<Exercise>(predicate: #Predicate{ exercise in
|
||||
exerciseName == exercise.name && splitId == exercise.split?.persistentModelID
|
||||
}))
|
||||
} catch {
|
||||
print("ERROR: failed to fetch \(exerciseName)")
|
||||
}
|
||||
|
||||
if let dup = duplicateExercise, dup.count > 0 {
|
||||
print("Skipping duplicate \(exerciseName) found \(dup.count) duplicate(s)")
|
||||
} else {
|
||||
print("Creating \(exerciseName) for \(split.name)")
|
||||
modelContext.insert(Exercise(
|
||||
split: split,
|
||||
exerciseName: exerciseName,
|
||||
order: 0,
|
||||
sets: 3,
|
||||
reps: 10,
|
||||
weight: 40
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
try? modelContext.save()
|
||||
}, allowMultiSelect: true)
|
||||
}
|
||||
.sheet(item: $itemToEdit) { item in
|
||||
ExerciseAddEditView(model: item)
|
||||
}
|
||||
.confirmationDialog(
|
||||
"Delete Exercise?",
|
||||
isPresented: .constant(itemToDelete != nil),
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Delete", role: .destructive) {
|
||||
if let item = itemToDelete {
|
||||
withAnimation {
|
||||
modelContext.delete(item)
|
||||
try? modelContext.save()
|
||||
itemToDelete = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.confirmationDialog(
|
||||
"Delete This Split?",
|
||||
isPresented: $showingDeleteConfirmation,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Delete", role: .destructive) {
|
||||
modelContext.delete(split)
|
||||
try? modelContext.save()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user