// // 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(SyncEngine.self) private var sync @Environment(AppServices.self) private var services @Environment(\.dismiss) private var dismiss var split: Split @State private var showingExerciseAddSheet: Bool = false @State private var showingSplitEditSheet: Bool = false @State private var itemToEdit: Exercise? = nil @State private var itemToDelete: Exercise? = nil var body: some View { 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")) { let sortedExercises = split.exercisesArray if !sortedExercises.isEmpty { ForEach(sortedExercises) { item in ListItem( title: item.name, subtitle: "\(item.sets) × \(item.reps) × \(item.weight) lbs" ) .swipeActions { Button { itemToDelete = item } label: { Label("Delete", systemImage: "trash") } .tint(.red) Button { itemToEdit = item } label: { Label("Edit", systemImage: "pencil") } .tint(.indigo) } } .onMove(perform: moveExercises) Button { showingExerciseAddSheet = true } label: { ListItem(systemName: "plus.circle", title: "Add Exercise") } } else { Text("No exercises added yet.") Button(action: { showingExerciseAddSheet.toggle() }) { ListItem(title: "Add Exercise") } } } } .navigationTitle(split.name) .toolbar { ToolbarItem(placement: .primaryAction) { Button { showingSplitEditSheet = true } label: { Image(systemName: "pencil") } } } .sheet(isPresented: $showingExerciseAddSheet) { ExercisePickerView(onExerciseSelected: { exerciseNames in addExercises(names: exerciseNames) }, allowMultiSelect: true) } .sheet(isPresented: $showingSplitEditSheet) { SplitAddEditView(split: split) { dismiss() } } .sheet(item: $itemToEdit) { item in ExerciseAddEditView(exercise: item, split: split) } .confirmationDialog( "Delete Exercise?", isPresented: Binding( get: { itemToDelete != nil }, set: { if !$0 { itemToDelete = nil } } ), titleVisibility: .visible, presenting: itemToDelete ) { item in Button("Delete", role: .destructive) { deleteExercise(item) itemToDelete = nil } Button("Cancel", role: .cancel) { itemToDelete = nil } } message: { item in Text("Remove \"\(item.name)\" from this split?") } // Editing this split (or any of its exercises, all reached from here) parks any // active watch run sourced from it — matched by splitID — so the watch can't keep // performing an exercise whose plan we're reconfiguring. .onAppear { services.watchBridge.setEditingSplit(split.id) } .onDisappear { services.watchBridge.setEditingSplit(nil) } } private func moveExercises(from source: IndexSet, to destination: Int) { var exercises = split.exercisesArray exercises.move(fromOffsets: source, toOffset: destination) var doc = SplitDocument(from: split) doc.exercises = exercises.enumerated().map { i, ex in var ed = ExerciseDocument(from: ex) ed.order = i return ed } doc.updatedAt = Date() Task { await sync.save(split: doc) } } private func addExercises(names: [String]) { var doc = SplitDocument(from: split) let existingNames = Set(doc.exercises.map { $0.name }) let base = doc.exercises.count let newDocs = names .filter { !existingNames.contains($0) } .enumerated() .map { i, exName in ExerciseDocument( id: ULID.make(), name: exName, order: base + i, sets: 3, reps: 10, weight: 40, loadType: LoadType.weight.rawValue, durationSeconds: 0, weightLastUpdated: nil, weightReminderWeeks: 2 ) } doc.exercises.append(contentsOf: newDocs) doc.updatedAt = Date() Task { await sync.save(split: doc) } // If a single exercise was added, open the edit sheet once the cache refreshes. // We rely on the observer to populate it — no direct entity reference needed. } private func deleteExercise(_ exercise: Exercise) { var doc = SplitDocument(from: split) doc.exercises.removeAll { $0.id == exercise.id } // Re-number orders after removal for i in doc.exercises.indices { doc.exercises[i].order = i } doc.updatedAt = Date() Task { await sync.save(split: doc) } } }