diff --git a/PROJECT_STATE.md b/PROJECT_STATE.md index 74842ad..1c624eb 100644 --- a/PROJECT_STATE.md +++ b/PROJECT_STATE.md @@ -37,7 +37,7 @@ Build a functional iOS sports trip planning app that helps users plan multi-stop ## Active Tasks - [x] Fix "By Games" Mode Game Selection -- [ ] Group Schedule View Games by Sport +- [x] Group Schedule View Games by Sport - [ ] Remove Buffer Days from Trip Planner ## Completed Tasks diff --git a/SportsTime/Features/Schedule/ViewModels/ScheduleViewModel.swift b/SportsTime/Features/Schedule/ViewModels/ScheduleViewModel.swift index 60b6fbd..02270fe 100644 --- a/SportsTime/Features/Schedule/ViewModels/ScheduleViewModel.swift +++ b/SportsTime/Features/Schedule/ViewModels/ScheduleViewModel.swift @@ -50,6 +50,21 @@ final class ScheduleViewModel { return grouped.sorted { $0.key < $1.key }.map { ($0.key, $0.value) } } + var gamesBySport: [(sport: Sport, games: [RichGame])] { + let grouped = Dictionary(grouping: filteredGames) { game in + game.game.sport + } + // Sort by sport order (use allCases index for consistent ordering) + // Within each sport, games are sorted by date + return grouped + .sorted { lhs, rhs in + let lhsIndex = Sport.allCases.firstIndex(of: lhs.key) ?? 0 + let rhsIndex = Sport.allCases.firstIndex(of: rhs.key) ?? 0 + return lhsIndex < rhsIndex + } + .map { (sport: $0.key, games: $0.value.sorted { $0.game.dateTime < $1.game.dateTime }) } + } + var hasFilters: Bool { selectedSports.count < Sport.supported.count || !searchText.isEmpty } diff --git a/SportsTime/Features/Schedule/Views/ScheduleListView.swift b/SportsTime/Features/Schedule/Views/ScheduleListView.swift index d03e592..2d76cac 100644 --- a/SportsTime/Features/Schedule/Views/ScheduleListView.swift +++ b/SportsTime/Features/Schedule/Views/ScheduleListView.swift @@ -89,14 +89,17 @@ struct ScheduleListView: View { .listRowBackground(Color.clear) } - ForEach(viewModel.gamesByDate, id: \.date) { dateGroup in + ForEach(viewModel.gamesBySport, id: \.sport) { sportGroup in Section { - ForEach(dateGroup.games) { richGame in - GameRowView(game: richGame) + ForEach(sportGroup.games) { richGame in + GameRowView(game: richGame, showDate: true) } } header: { - Text(formatSectionDate(dateGroup.date)) - .font(.headline) + HStack(spacing: 8) { + Image(systemName: sportGroup.sport.iconName) + Text(sportGroup.sport.rawValue) + } + .font(.headline) } .listRowBackground(Theme.cardBackground(colorScheme)) } @@ -204,9 +207,18 @@ struct SportFilterChip: View { struct GameRowView: View { let game: RichGame + var showDate: Bool = false var body: some View { VStack(alignment: .leading, spacing: 8) { + // Date (when grouped by sport) + if showDate { + Text(formattedDate) + .font(.caption) + .fontWeight(.medium) + .foregroundStyle(.secondary) + } + // Teams HStack { VStack(alignment: .leading, spacing: 4) { @@ -220,11 +232,6 @@ struct GameRowView: View { } Spacer() - - // Sport badge - Image(systemName: game.game.sport.iconName) - .font(.caption) - .foregroundStyle(.secondary) } // Game info @@ -241,6 +248,12 @@ struct GameRowView: View { } .padding(.vertical, 4) } + + private var formattedDate: String { + let formatter = DateFormatter() + formatter.dateFormat = "EEE, MMM d" + return formatter.string(from: game.game.dateTime) + } } // MARK: - Team Badge