This commit is contained in:
2025-07-13 21:54:09 -04:00
parent 0545f5dbc7
commit bdaa406876
33 changed files with 984 additions and 714 deletions

View File

@ -1,48 +0,0 @@
//
// ExerciseTypeAddEditView.swift
// Workouts
//
// Created by rzen on 7/13/25 at 11:33AM.
//
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
//
import SwiftUI
struct ExerciseTypeAddEditView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) private var modelContext
@Bindable var model: ExerciseType
var body: some View {
NavigationStack {
Form {
Section (header: Text("Nname")) {
TextField("Name", text: $model.name)
.bold()
}
Section(header: Text("Description")) {
TextEditor(text: $model.descr)
.frame(minHeight: 100)
.padding(.vertical, 4)
}
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Cancel") {
dismiss()
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button("Save") {
try? modelContext.save()
dismiss()
}
}
}
}
}
}

View File

@ -1,75 +0,0 @@
//
// ExerciseTypeListView.swift
// Workouts
//
// Created by rzen on 7/13/25 at 11:27AM.
//
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
//
import SwiftUI
import SwiftData
struct ExerciseTypeListView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) private var modelContext
@Query(sort: [SortDescriptor(\ExerciseType.name)]) var items: [ExerciseType]
@State var itemToEdit: ExerciseType? = nil
@State var itemToDelete: ExerciseType? = nil
private func save () {
try? modelContext.save()
}
var body: some View {
NavigationStack {
Form {
List {
ForEach (items) { item in
ListItem(title: item.name)
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button (role: .destructive) {
itemToDelete = item
} label: {
Label("Delete", systemImage: "trash")
}
Button {
itemToEdit = item
} label: {
Label("Edit", systemImage: "pencil")
}
.tint(.indigo)
}
}
}
}
.navigationTitle("Exercise Types")
.sheet(item: $itemToEdit) {item in
ExerciseTypeAddEditView(model: item)
}
.confirmationDialog(
"Delete?",
isPresented: Binding<Bool>(
get: { itemToDelete != nil },
set: { if !$0 { itemToDelete = nil } }
),
titleVisibility: .visible
) {
Button("Delete", role: .destructive) {
if let itemToDelete = itemToDelete {
modelContext.delete(itemToDelete)
try? modelContext.save()
self.itemToDelete = nil
}
}
Button("Cancel", role: .cancel) {
itemToDelete = nil
}
} message: {
Text("Are you sure you want to delete \(itemToDelete?.name ?? "this item")?")
}
}
}
}

View File

@ -1,96 +0,0 @@
import SwiftUI
import SwiftData
struct ExerciseAddEditView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) private var modelContext
@Query(sort: [SortDescriptor(\ExerciseType.name)]) var exerciseTypes: [ExerciseType]
@State var model: Exercise
init(model: Exercise? = nil) {
_model = State(initialValue: model ?? Exercise(name: "", setup: "", descr: "", sets: 3, reps: 10, weight: 30))
}
var body: some View {
Form {
Section (header: Text("Name")) {
TextField("Name", text: $model.name)
.bold()
}
Section (header: Text("Exercise Type")) {
Picker("Type", selection: $model.type) {
Text("Select a type").tag(nil as ExerciseType?)
ForEach(exerciseTypes) { type in
Text(type.name).tag(type as ExerciseType?)
}
}
}
Section(header: Text("Description")) {
TextEditor(text: $model.descr)
.frame(minHeight: 100)
.padding(.vertical, 4)
}
Section (header: Text("Setup")) {
TextEditor(text: $model.setup)
.frame(minHeight: 100)
.padding(.vertical, 4)
// } footer: {
// Text("Describe concisely how equipment should be configured")
}
Section (header: Text("Weight")) {
HStack {
Text("\(model.weight)")
.bold()
Text("lbs")
Spacer()
Stepper("", value: $model.weight, in: 0...1000)
}
}
// Section(header: Text("Target Muscles")) {
// Button(action: {
// showingMuscleSelection = true
// }) {
// HStack {
// if selectedMuscles.isEmpty {
// Text("None selected")
// .foregroundColor(.secondary)
// } else {
// Text(selectedMuscles.map { $0.name }.joined(separator: ", "))
// .foregroundColor(.primary)
// .multilineTextAlignment(.leading)
// }
// Spacer()
// Image(systemName: "chevron.right")
// .foregroundColor(.secondary)
// .font(.caption)
// }
// }
// }
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Cancel") {
dismiss()
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button("Save") {
try? modelContext.save()
dismiss()
}
}
}
}
}

View File

@ -1,95 +0,0 @@
//
// ExercisesListView.swift
// Workouts
//
// Created by rzen on 7/13/25 at 4:30PM.
//
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
//
import SwiftUI
import SwiftData
struct ExercisesListView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) private var modelContext
@Query(sort: [SortDescriptor(\ExerciseType.name)]) var groups: [ExerciseType]
@State var showingAddSheet = false
@State var itemToEdit: Exercise? = nil
@State var itemToDelete: Exercise? = nil
private func save () {
try? modelContext.save()
}
var body: some View {
NavigationStack {
Form {
ForEach (groups) { group in
let items = group.exercises ?? []
let itemCount = items.count
if itemCount > 0 {
Section (header: Text("\(group.name) (\(itemCount))")) {
ForEach (items) { item in
ListItem(title: item.name)
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button (role: .destructive) {
itemToDelete = item
} label: {
Label("Delete", systemImage: "trash")
}
Button {
itemToEdit = item
} label: {
Label("Edit", systemImage: "pencil")
}
.tint(.indigo)
}
}
}
}
}
}
.navigationTitle("Exercises")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
showingAddSheet.toggle()
}) {
Image(systemName: "plus")
}
}
}
.sheet(isPresented: $showingAddSheet) {
ExerciseAddEditView()
}
.sheet(item: $itemToEdit) { item in
ExerciseAddEditView(model: item)
}
.confirmationDialog(
"Delete?",
isPresented: Binding<Bool>(
get: { itemToDelete != nil },
set: { if !$0 { itemToDelete = nil } }
),
titleVisibility: .visible
) {
Button("Delete", role: .destructive) {
if let itemToDelete = itemToDelete {
modelContext.delete(itemToDelete)
try? modelContext.save()
self.itemToDelete = nil
}
}
Button("Cancel", role: .cancel) {
itemToDelete = nil
}
} message: {
Text("Are you sure you want to delete \(itemToDelete?.name ?? "this item")?")
}
}
}
}

View File

@ -1,55 +0,0 @@
import SwiftUI
//
// MuscleGroupAddEditView.swift
// Workouts
//
// Created by rzen on 7/13/25 at 12:14 PM.
//
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
//
struct MuscleGroupAddEditView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) private var modelContext
@State var model: MuscleGroup
init(model: MuscleGroup? = nil) {
_model = State(initialValue: model ?? MuscleGroup(name: "", descr: ""))
}
var body: some View {
NavigationStack {
Form {
Section (header: Text("Name")) {
TextField("Name", text: $model.name)
.bold()
}
Section(header: Text("Description")) {
TextEditor(text: $model.descr)
.frame(minHeight: 100)
.padding(.vertical, 4)
}
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Cancel") {
dismiss()
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button("Save") {
if model.modelContext == nil {
modelContext.insert(model)
}
try? modelContext.save()
dismiss()
}
}
}
}
}
}

View File

@ -1,81 +0,0 @@
//
// MuscleGroupsListView.swift
// Workouts
//
// Created by rzen on 7/13/25 at 12:14 PM.
//
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
//
import SwiftUI
import SwiftData
struct MuscleGroupsListView: View {
@Environment(\.modelContext) private var modelContext
@Query(sort: [SortDescriptor(\MuscleGroup.name)]) var items: [MuscleGroup]
@State var showingAddSheet = false
@State var itemToEdit: MuscleGroup? = nil
@State var itemToDelete: MuscleGroup? = nil
var body: some View {
Form {
List {
ForEach (items) { item in
ListItem(title: item.name, count: item.muscles?.count)
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button (role: .destructive) {
itemToDelete = item
} label: {
Label("Delete", systemImage: "trash")
}
Button {
itemToEdit = item
} label: {
Label("Edit", systemImage: "pencil")
}
.tint(.indigo)
}
}
}
}
.navigationTitle("Muscle Groups")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
showingAddSheet.toggle()
}) {
Image(systemName: "plus")
}
}
}
.sheet(isPresented: $showingAddSheet) {
MuscleGroupAddEditView()
}
.sheet(item: $itemToEdit) {item in
MuscleGroupAddEditView(model: item)
}
.confirmationDialog(
"Delete?",
isPresented: Binding<Bool>(
get: { itemToDelete != nil },
set: { if !$0 { itemToDelete = nil } }
),
titleVisibility: .visible
) {
Button("Delete", role: .destructive) {
if let itemToDelete = itemToDelete {
modelContext.delete(itemToDelete)
try? modelContext.save()
self.itemToDelete = nil
}
}
Button("Cancel", role: .cancel) {
itemToDelete = nil
}
} message: {
Text("Are you sure you want to delete \(itemToDelete?.name ?? "this item")?")
}
}
}

View File

@ -1,59 +0,0 @@
//
// MuscleAddEditView.swift
// Workouts
//
// Created by rzen on 7/13/25 at 11:55AM.
//
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
//
import SwiftUI
import SwiftData
struct MuscleAddEditView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) private var modelContext
@Query(sort: [SortDescriptor(\MuscleGroup.name)]) var muscleGroups: [MuscleGroup]
@Bindable var model: Muscle
var body: some View {
NavigationStack {
Form {
Section (header: Text("Name")) {
TextField("Name", text: $model.name)
.bold()
}
Section(header: Text("Muscle Group")) {
Picker("Muscle Group", selection: $model.muscleGroup) {
Text("Select a muscle group").tag(nil as MuscleGroup?)
ForEach(muscleGroups) { group in
Text(group.name).tag(group as MuscleGroup?)
}
}
}
Section(header: Text("Description")) {
TextEditor(text: $model.descr)
.frame(minHeight: 100)
.padding(.vertical, 4)
}
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Cancel") {
dismiss()
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button("Save") {
try? modelContext.save()
dismiss()
}
}
}
}
}
}

View File

@ -1,79 +0,0 @@
//
// MuscleGroupsListView.swift
// Workouts
//
// Created by rzen on 7/13/25 at 12:14 PM.
//
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
//
import SwiftUI
import SwiftData
struct MusclesListView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) private var modelContext
@Query(sort: [SortDescriptor(\MuscleGroup.name)]) var groups: [MuscleGroup]
@State var itemToEdit: Muscle? = nil
@State var itemToDelete: Muscle? = nil
private func save () {
try? modelContext.save()
}
var body: some View {
NavigationStack {
Form {
ForEach (groups) { group in
Section (header: Text("\(group.name) (\(group.muscles?.count ?? 0))")) {
let items = group.muscles ?? []
ForEach (items) { item in
ListItem(title: item.name)
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button (role: .destructive) {
itemToDelete = item
} label: {
Label("Delete", systemImage: "trash")
}
Button {
itemToEdit = item
} label: {
Label("Edit", systemImage: "pencil")
}
.tint(.indigo)
}
}
}
}
}
.navigationTitle("Muscles")
.sheet(item: $itemToEdit) { item in
MuscleAddEditView(model: item)
}
.confirmationDialog(
"Delete?",
isPresented: Binding<Bool>(
get: { itemToDelete != nil },
set: { if !$0 { itemToDelete = nil } }
),
titleVisibility: .visible
) {
Button("Delete", role: .destructive) {
if let itemToDelete = itemToDelete {
modelContext.delete(itemToDelete)
try? modelContext.save()
self.itemToDelete = nil
}
}
Button("Cancel", role: .cancel) {
itemToDelete = nil
}
} message: {
Text("Are you sure you want to delete \(itemToDelete?.name ?? "this item")?")
}
}
}
}

View File

@ -75,3 +75,33 @@ struct SettingsView: View {
}
}
}
struct ExercisesListView: View {
var body: some View {
EntityListView<Exercise>(sort: [SortDescriptor(\Exercise.name)])
}
}
struct ExerciseTypeListView: View {
var body: some View {
EntityListView<ExerciseType>(sort: [SortDescriptor(\ExerciseType.name)])
}
}
struct MuscleGroupsListView: View {
var body: some View {
EntityListView<MuscleGroup>(sort: [SortDescriptor(\MuscleGroup.name)])
}
}
struct MusclesListView: View {
var body: some View {
EntityListView<Muscle>(sort: [SortDescriptor(\Muscle.name)])
}
}
struct SplitsListView: View {
var body: some View {
EntityListView<Split>(sort: [SortDescriptor(\Split.name)])
}
}

View File

@ -1,78 +0,0 @@
import SwiftUI
struct SplitAddEditView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) private var modelContext
@Bindable var model: Split
@State var itemToEdit: SplitExerciseAssignment? = nil
@State var itemToDelete: SplitExerciseAssignment? = nil
var body: some View {
NavigationStack {
Form {
Section (header: Text("Name")) {
TextField("Name", text: $model.name)
.bold()
}
Section(header: Text("Description")) {
TextEditor(text: $model.intro)
.frame(minHeight: 100)
.padding(.vertical, 4)
}
Section(header: Text("Exercises")) {
let item = model
if let assignments = item.exercises, !assignments.isEmpty {
ForEach(assignments, id: \.id) { item in
List {
ListItem(
title: item.exercise?.name ?? "Unnamed",
subtitle: "\(item.sets) × \(item.reps) @ \(item.weight) lbs"
)
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button (role: .destructive) {
itemToDelete = item
} label: {
Label("Delete", systemImage: "trash")
}
Button {
itemToEdit = item
} label: {
Label("Edit", systemImage: "pencil")
}
.tint(.indigo)
}
}
}
} else {
Text("No exercises added")
.foregroundColor(.secondary)
}
Button(action: {
}) {
Label("Add Exercise", systemImage: "plus.circle")
}
}
}
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Cancel") {
dismiss()
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button("Save") {
try? modelContext.save()
dismiss()
}
}
}
}
}
}

View File

@ -1,78 +0,0 @@
//
// SplitsListView.swift
// Workouts
//
// Created by rzen on 7/13/25 at 10:27AM.
//
// Copyright 2025 Rouslan Zenetl. All Rights Reserved.
//
import SwiftUI
import SwiftData
struct SplitsListView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) private var modelContext
@Query(sort: [SortDescriptor(\Split.name)]) var items: [Split]
@State var itemToEdit: Split? = nil
@State var itemToDelete: Split? = nil
private func save () {
try? modelContext.save()
}
var body: some View {
NavigationStack {
Form {
List {
ForEach (items) { item in
ListItem(
title: item.name,
count: item.exercises?.count
)
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button (role: .destructive) {
itemToDelete = item
} label: {
Label("Delete", systemImage: "trash")
}
Button {
itemToEdit = item
} label: {
Label("Edit", systemImage: "pencil")
}
.tint(.indigo)
}
}
}
.navigationTitle("Muscle Groups")
}
.sheet(item: $itemToEdit) {item in
SplitAddEditView(model: item)
}
.confirmationDialog(
"Delete?",
isPresented: Binding<Bool>(
get: { itemToDelete != nil },
set: { if !$0 { itemToDelete = nil } }
),
titleVisibility: .visible
) {
Button("Delete", role: .destructive) {
if let itemToDelete = itemToDelete {
modelContext.delete(itemToDelete)
try? modelContext.save()
self.itemToDelete = nil
}
}
Button("Cancel", role: .cancel) {
itemToDelete = nil
}
} message: {
Text("Are you sure you want to delete \(itemToDelete?.name ?? "this item")?")
}
}
}
}