// // TeamPickerStep.swift // SportsTime // // Team selection step for "Follow Team" planning mode. // Uses sheet-based drill-down: Sport → Team. // import SwiftUI struct TeamPickerStep: View { @Environment(\.colorScheme) private var colorScheme @Binding var selectedSport: Sport? @Binding var selectedTeamId: String? @State private var showTeamPicker = false private var selectedTeam: Team? { guard let teamId = selectedTeamId else { return nil } return AppDataProvider.shared.teams.first { $0.id == teamId } } var body: some View { VStack(alignment: .leading, spacing: Theme.Spacing.md) { StepHeader( title: "Which team do you want to follow?", subtitle: "See their home and away games" ) if let team = selectedTeam { HStack(spacing: Theme.Spacing.sm) { Button { showTeamPicker = true } label: { HStack { Circle() .fill(team.primaryColor.map { Color(hex: $0) } ?? team.sport.themeColor) .frame(width: 24, height: 24) VStack(alignment: .leading, spacing: 2) { Text(team.fullName) .font(.subheadline) .fontWeight(.medium) .foregroundStyle(Theme.textPrimary(colorScheme)) Text(team.sport.rawValue) .font(.caption) .foregroundStyle(Theme.textMuted(colorScheme)) } Spacer() } .contentShape(Rectangle()) } .buttonStyle(.plain) Button { selectedTeamId = nil selectedSport = nil } label: { Image(systemName: "xmark.circle.fill") .foregroundStyle(Theme.textMuted(colorScheme)) } .buttonStyle(.plain) .minimumHitTarget() .accessibilityLabel("Clear team selection") } .padding(Theme.Spacing.md) .background(Theme.cardBackgroundElevated(colorScheme)) .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.medium)) .overlay( RoundedRectangle(cornerRadius: Theme.CornerRadius.medium) .stroke(Theme.warmOrange, lineWidth: 2) ) } else { // Selection button Button { showTeamPicker = true } label: { HStack { Image(systemName: "person.2.fill") .foregroundStyle(Theme.warmOrange) .accessibilityHidden(true) Text("Select a team") .font(.subheadline) .foregroundStyle(Theme.textMuted(colorScheme)) Spacer() Image(systemName: "chevron.right") .font(.caption) .foregroundStyle(Theme.textMuted(colorScheme)) .accessibilityHidden(true) } .padding(Theme.Spacing.md) .background(Theme.cardBackgroundElevated(colorScheme)) .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.medium)) .contentShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.medium)) .overlay( RoundedRectangle(cornerRadius: Theme.CornerRadius.medium) .stroke(Theme.textMuted(colorScheme).opacity(0.3), lineWidth: 1) ) } .buttonStyle(.plain) } } .padding(Theme.Spacing.lg) .background(Theme.cardBackground(colorScheme)) .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.large)) .overlay { RoundedRectangle(cornerRadius: Theme.CornerRadius.large) .stroke(Theme.surfaceGlow(colorScheme), lineWidth: 1) } .sheet(isPresented: $showTeamPicker) { TeamPickerSheet( selectedSport: $selectedSport, selectedTeamId: $selectedTeamId, isPresented: $showTeamPicker ) } } } // MARK: - Team Picker Sheet private struct TeamPickerSheet: View { @Environment(\.colorScheme) private var colorScheme @Binding var selectedSport: Sport? @Binding var selectedTeamId: String? @Binding var isPresented: Bool var body: some View { NavigationStack { List { ForEach(Sport.supported, id: \.self) { sport in NavigationLink { TeamListView( sport: sport, selectedTeamId: $selectedTeamId, onSelect: { teamId in selectedSport = sport selectedTeamId = teamId isPresented = false } ) } label: { HStack(spacing: Theme.Spacing.sm) { Image(systemName: sport.iconName) .font(.title2) .foregroundStyle(sport.themeColor) .frame(width: 32) Text(sport.rawValue) .font(.body) .foregroundStyle(Theme.textPrimary(colorScheme)) Spacer() Text("\(teamsCount(for: sport)) teams") .font(.caption) .foregroundStyle(Theme.textMuted(colorScheme)) } .padding(.vertical, Theme.Spacing.xs) } } } .listStyle(.plain) .navigationTitle("Select Sport") .navigationBarTitleDisplayMode(.inline) .toolbar { ToolbarItem(placement: .cancellationAction) { Button("Cancel") { isPresented = false } } } } .presentationDetents([.large]) } private func teamsCount(for sport: Sport) -> Int { AppDataProvider.shared.teams.filter { $0.sport == sport }.count } } // MARK: - Team List View private struct TeamListView: View { @Environment(\.colorScheme) private var colorScheme let sport: Sport @Binding var selectedTeamId: String? let onSelect: (String) -> Void @State private var searchText = "" private var teams: [Team] { let allTeams = AppDataProvider.shared.teams .filter { $0.sport == sport } .sorted { $0.fullName < $1.fullName } if searchText.isEmpty { return allTeams } return allTeams.filter { $0.fullName.localizedCaseInsensitiveContains(searchText) || $0.city.localizedCaseInsensitiveContains(searchText) } } var body: some View { List { ForEach(teams) { team in Button { onSelect(team.id) } label: { HStack(spacing: Theme.Spacing.sm) { Circle() .fill(team.primaryColor.map { Color(hex: $0) } ?? sport.themeColor) .frame(width: 28, height: 28) VStack(alignment: .leading, spacing: 2) { Text(team.fullName) .font(.body) .foregroundStyle(Theme.textPrimary(colorScheme)) Text(team.city) .font(.caption) .foregroundStyle(Theme.textMuted(colorScheme)) } Spacer() if selectedTeamId == team.id { Image(systemName: "checkmark.circle.fill") .foregroundStyle(Theme.warmOrange) .accessibilityHidden(true) } } .padding(.vertical, Theme.Spacing.xs) .accessibilityElement(children: .combine) } .buttonStyle(.plain) .accessibilityAddTraits(selectedTeamId == team.id ? .isSelected : []) } } .listStyle(.plain) .searchable(text: $searchText, prompt: "Search teams") .navigationTitle(sport.rawValue) .navigationBarTitleDisplayMode(.inline) } } // MARK: - Preview #Preview { TeamPickerStep( selectedSport: .constant(.mlb), selectedTeamId: .constant(nil) ) .padding() .themedBackground() }