Files
Sportstime/SportsTime/Features/Progress/Views/StadiumVisitHistoryView.swift
Trey t d63d311cab feat: add WCAG AA accessibility app-wide, fix CloudKit container config, remove debug logs
- Add VoiceOver labels, hints, and element grouping across all 60+ views
- Add Reduce Motion support (Theme.Animation.prefersReducedMotion) to all animations
- Replace fixed font sizes with semantic Dynamic Type styles
- Hide decorative elements from VoiceOver with .accessibilityHidden(true)
- Add .minimumHitTarget() modifier ensuring 44pt touch targets
- Add AccessibilityAnnouncer utility for VoiceOver announcements
- Improve color contrast values in Theme.swift for WCAG AA compliance
- Extract CloudKitContainerConfig for explicit container identity
- Remove PostHog debug console log from AnalyticsManager

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 09:27:23 -06:00

111 lines
3.1 KiB
Swift

import SwiftUI
import SwiftData
struct StadiumVisitHistoryView: View {
let stadium: Stadium
@Environment(\.modelContext) private var modelContext
@Environment(\.dismiss) private var dismiss
@State private var visits: [StadiumVisit] = []
@State private var isLoading = true
@State private var showingAddVisit = false
var body: some View {
NavigationStack {
Group {
if isLoading {
LoadingSpinner(size: .medium)
} else if visits.isEmpty {
EmptyVisitHistoryView()
} else {
VisitHistoryList(visits: visits)
}
}
.navigationTitle(stadium.name)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Done") { dismiss() }
}
ToolbarItem(placement: .primaryAction) {
Button {
showingAddVisit = true
} label: {
Image(systemName: "plus")
}
.accessibilityLabel("Add visit to this stadium")
}
}
.sheet(isPresented: $showingAddVisit) {
StadiumVisitSheet(initialStadium: stadium)
}
}
.task {
await loadVisits()
}
}
private func loadVisits() async {
isLoading = true
defer { isLoading = false }
let stadiumId = stadium.id
let descriptor = FetchDescriptor<StadiumVisit>(
predicate: #Predicate { $0.stadiumId == stadiumId },
sortBy: [SortDescriptor(\.visitDate, order: .reverse)]
)
do {
visits = try modelContext.fetch(descriptor)
} catch {
visits = []
}
}
}
private struct VisitHistoryList: View {
let visits: [StadiumVisit]
var body: some View {
ScrollView {
VStack(spacing: 12) {
// Visit count header
HStack {
Text("\(visits.count) Visit\(visits.count == 1 ? "" : "s")")
.font(.headline)
Spacer()
}
.padding(.horizontal)
// Visit cards
ForEach(visits, id: \.id) { visit in
VisitListCard(visit: visit)
.padding(.horizontal)
}
}
.padding(.vertical)
}
.background(Color(.systemGroupedBackground))
}
}
private struct EmptyVisitHistoryView: View {
var body: some View {
VStack(spacing: 16) {
Image(systemName: "calendar.badge.plus")
.font(.largeTitle)
.foregroundStyle(.secondary)
Text("No visits recorded")
.font(.headline)
Text("Tap + to add your first visit to this stadium")
.font(.subheadline)
.foregroundStyle(.secondary)
.multilineTextAlignment(.center)
}
.padding()
}
}