Files
Ascently/ios/OpenClimb/Views/GymsView.swift

199 lines
5.8 KiB
Swift

import SwiftUI
struct GymsView: View {
@EnvironmentObject var dataManager: ClimbingDataManager
@State private var showingAddGym = false
var body: some View {
NavigationView {
VStack {
if dataManager.gyms.isEmpty {
EmptyGymsView()
} else {
GymsList()
}
}
.navigationTitle("Gyms")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("Add") {
showingAddGym = true
}
}
}
.sheet(isPresented: $showingAddGym) {
AddEditGymView()
}
}
}
}
struct GymsList: View {
@EnvironmentObject var dataManager: ClimbingDataManager
@State private var gymToDelete: Gym?
@State private var gymToEdit: Gym?
var body: some View {
List(dataManager.gyms, id: \.id) { gym in
NavigationLink(destination: GymDetailView(gymId: gym.id)) {
GymRow(gym: gym)
}
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
Button(role: .destructive) {
gymToDelete = gym
} label: {
Label("Delete", systemImage: "trash")
}
Button {
gymToEdit = gym
} label: {
Label("Edit", systemImage: "pencil")
}
.tint(.blue)
}
}
.alert("Delete Gym", isPresented: .constant(gymToDelete != nil)) {
Button("Cancel", role: .cancel) {
gymToDelete = nil
}
Button("Delete", role: .destructive) {
if let gym = gymToDelete {
dataManager.deleteGym(gym)
gymToDelete = nil
}
}
} message: {
Text(
"Are you sure you want to delete this gym? This will also delete all associated problems and sessions."
)
}
.sheet(item: $gymToEdit) { gym in
AddEditGymView(gymId: gym.id)
}
}
}
struct GymRow: View {
let gym: Gym
@EnvironmentObject var dataManager: ClimbingDataManager
private var problemCount: Int {
dataManager.problems(forGym: gym.id).count
}
private var sessionCount: Int {
dataManager.sessions(forGym: gym.id).count
}
var body: some View {
VStack(alignment: .leading, spacing: 12) {
// Header
VStack(alignment: .leading, spacing: 4) {
Text(gym.name)
.font(.headline)
.fontWeight(.bold)
if let location = gym.location, !location.isEmpty {
Text(location)
.font(.subheadline)
.foregroundColor(.secondary)
}
}
// Climb Types
if !gym.supportedClimbTypes.isEmpty {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 8) {
ForEach(gym.supportedClimbTypes, id: \.self) { climbType in
Text(climbType.displayName)
.font(.caption)
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(
RoundedRectangle(cornerRadius: 8)
.fill(.blue.opacity(0.1))
)
.foregroundColor(.blue)
}
}
}
}
// Difficulty Systems
if !gym.difficultySystems.isEmpty {
Text(
"Systems: \(gym.difficultySystems.map { $0.displayName }.joined(separator: ", "))"
)
.font(.caption)
.foregroundColor(.secondary)
}
// Stats
HStack {
Label("\(problemCount)", systemImage: "star.fill")
.font(.caption)
.foregroundColor(.orange)
Label("\(sessionCount)", systemImage: "play.fill")
.font(.caption)
.foregroundColor(.green)
Spacer()
}
// Notes preview
if let notes = gym.notes, !notes.isEmpty {
Text(notes)
.font(.caption)
.foregroundColor(.secondary)
.lineLimit(2)
}
}
.padding(.vertical, 8)
}
}
struct EmptyGymsView: View {
@State private var showingAddGym = false
var body: some View {
VStack(spacing: 20) {
Spacer()
Image(systemName: "location.fill")
.font(.system(size: 60))
.foregroundColor(.secondary)
VStack(spacing: 8) {
Text("No Gyms Added")
.font(.title2)
.fontWeight(.bold)
Text("Add your favorite climbing gyms to start tracking your progress!")
.font(.body)
.foregroundColor(.secondary)
.multilineTextAlignment(.center)
.padding(.horizontal)
}
Button("Add Gym") {
showingAddGym = true
}
.buttonStyle(.borderedProminent)
.controlSize(.large)
Spacer()
}
.sheet(isPresented: $showingAddGym) {
AddEditGymView()
}
}
}
#Preview {
GymsView()
.environmentObject(ClimbingDataManager.preview)
}