211 lines
7.1 KiB
Swift
211 lines
7.1 KiB
Swift
|
|
import SwiftUI
|
|
|
|
struct AddEditGymView: View {
|
|
let gymId: UUID?
|
|
@EnvironmentObject var dataManager: ClimbingDataManager
|
|
@Environment(\.dismiss) private var dismiss
|
|
|
|
@State private var name = ""
|
|
@State private var location = ""
|
|
@State private var notes = ""
|
|
@State private var selectedClimbTypes = Set<ClimbType>()
|
|
@State private var selectedDifficultySystems = Set<DifficultySystem>()
|
|
@State private var customDifficultyGrades: [String] = []
|
|
@State private var isEditing = false
|
|
|
|
private var existingGym: Gym? {
|
|
guard let gymId = gymId else { return nil }
|
|
return dataManager.gym(withId: gymId)
|
|
}
|
|
|
|
private var availableDifficultySystems: [DifficultySystem] {
|
|
if selectedClimbTypes.isEmpty {
|
|
return []
|
|
} else {
|
|
return selectedClimbTypes.flatMap { climbType in
|
|
DifficultySystem.systemsForClimbType(climbType)
|
|
}.removingDuplicates()
|
|
}
|
|
}
|
|
|
|
init(gymId: UUID? = nil) {
|
|
self.gymId = gymId
|
|
}
|
|
|
|
var body: some View {
|
|
NavigationView {
|
|
Form {
|
|
BasicInfoSection()
|
|
ClimbTypesSection()
|
|
DifficultySystemsSection()
|
|
NotesSection()
|
|
}
|
|
.navigationTitle(isEditing ? "Edit Gym" : "Add Gym")
|
|
.navigationBarTitleDisplayMode(.inline)
|
|
.toolbar {
|
|
ToolbarItem(placement: .navigationBarLeading) {
|
|
Button("Cancel") {
|
|
dismiss()
|
|
}
|
|
}
|
|
|
|
ToolbarItem(placement: .navigationBarTrailing) {
|
|
Button("Save") {
|
|
saveGym()
|
|
}
|
|
.disabled(!canSave)
|
|
}
|
|
}
|
|
}
|
|
.onAppear {
|
|
loadExistingGym()
|
|
}
|
|
.onChange(of: selectedClimbTypes) {
|
|
updateAvailableDifficultySystems()
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func BasicInfoSection() -> some View {
|
|
Section("Basic Information") {
|
|
TextField("Gym Name", text: $name)
|
|
|
|
TextField("Location (Optional)", text: $location)
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func ClimbTypesSection() -> some View {
|
|
Section("Supported Climb Types") {
|
|
ForEach(ClimbType.allCases, id: \.self) { climbType in
|
|
HStack {
|
|
Text(climbType.displayName)
|
|
Spacer()
|
|
if selectedClimbTypes.contains(climbType) {
|
|
Image(systemName: "checkmark.circle.fill")
|
|
.foregroundColor(.blue)
|
|
} else {
|
|
Image(systemName: "circle")
|
|
.foregroundColor(.gray)
|
|
}
|
|
}
|
|
.contentShape(Rectangle())
|
|
.onTapGesture {
|
|
if selectedClimbTypes.contains(climbType) {
|
|
selectedClimbTypes.remove(climbType)
|
|
} else {
|
|
selectedClimbTypes.insert(climbType)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func DifficultySystemsSection() -> some View {
|
|
Section("Difficulty Systems") {
|
|
if selectedClimbTypes.isEmpty {
|
|
Text("Select climb types first to see available difficulty systems")
|
|
.foregroundColor(.secondary)
|
|
.font(.caption)
|
|
} else {
|
|
ForEach(availableDifficultySystems, id: \.self) { system in
|
|
HStack {
|
|
Text(system.displayName)
|
|
Spacer()
|
|
if selectedDifficultySystems.contains(system) {
|
|
Image(systemName: "checkmark.circle.fill")
|
|
.foregroundColor(.blue)
|
|
} else {
|
|
Image(systemName: "circle")
|
|
.foregroundColor(.gray)
|
|
}
|
|
}
|
|
.contentShape(Rectangle())
|
|
.onTapGesture {
|
|
if selectedDifficultySystems.contains(system) {
|
|
selectedDifficultySystems.remove(system)
|
|
} else {
|
|
selectedDifficultySystems.insert(system)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func NotesSection() -> some View {
|
|
Section("Notes (Optional)") {
|
|
TextEditor(text: $notes)
|
|
.frame(minHeight: 100)
|
|
}
|
|
}
|
|
|
|
private var canSave: Bool {
|
|
!name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && !selectedClimbTypes.isEmpty
|
|
&& !selectedDifficultySystems.isEmpty
|
|
}
|
|
|
|
private func loadExistingGym() {
|
|
if let gym = existingGym {
|
|
isEditing = true
|
|
name = gym.name
|
|
location = gym.location ?? ""
|
|
notes = gym.notes ?? ""
|
|
selectedClimbTypes = Set(gym.supportedClimbTypes)
|
|
selectedDifficultySystems = Set(gym.difficultySystems)
|
|
customDifficultyGrades = gym.customDifficultyGrades
|
|
}
|
|
}
|
|
|
|
private func updateAvailableDifficultySystems() {
|
|
// Remove selected systems that are no longer available
|
|
let availableSet = Set(availableDifficultySystems)
|
|
selectedDifficultySystems = selectedDifficultySystems.intersection(availableSet)
|
|
}
|
|
|
|
private func saveGym() {
|
|
let trimmedName = name.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
let trimmedLocation = location.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
let trimmedNotes = notes.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
|
|
if isEditing, let gym = existingGym {
|
|
let updatedGym = gym.updated(
|
|
name: trimmedName,
|
|
location: trimmedLocation.isEmpty ? nil : trimmedLocation,
|
|
supportedClimbTypes: Array(selectedClimbTypes),
|
|
difficultySystems: Array(selectedDifficultySystems),
|
|
customDifficultyGrades: customDifficultyGrades,
|
|
notes: trimmedNotes.isEmpty ? nil : trimmedNotes
|
|
)
|
|
dataManager.updateGym(updatedGym)
|
|
} else {
|
|
let newGym = Gym(
|
|
name: trimmedName,
|
|
location: trimmedLocation.isEmpty ? nil : trimmedLocation,
|
|
supportedClimbTypes: Array(selectedClimbTypes),
|
|
difficultySystems: Array(selectedDifficultySystems),
|
|
customDifficultyGrades: customDifficultyGrades,
|
|
notes: trimmedNotes.isEmpty ? nil : trimmedNotes
|
|
)
|
|
dataManager.addGym(newGym)
|
|
}
|
|
|
|
dismiss()
|
|
}
|
|
}
|
|
|
|
extension Array where Element: Hashable {
|
|
func removingDuplicates() -> [Element] {
|
|
var seen = Set<Element>()
|
|
return filter { seen.insert($0).inserted }
|
|
}
|
|
}
|
|
|
|
#Preview {
|
|
AddEditGymView()
|
|
.environmentObject(ClimbingDataManager.preview)
|
|
}
|