feat(schedule): group games by sport instead of date

Games in schedule view now display in sport sections (MLB, NBA, etc.)
with games sorted by date within each section. Each game row shows its
date since the section header now shows sport instead.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-01-10 17:36:22 -06:00
parent 6db0bdefcd
commit 5ed4e309bd
3 changed files with 39 additions and 11 deletions

View File

@@ -37,7 +37,7 @@ Build a functional iOS sports trip planning app that helps users plan multi-stop
## Active Tasks ## Active Tasks
- [x] Fix "By Games" Mode Game Selection - [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 - [ ] Remove Buffer Days from Trip Planner
## Completed Tasks ## Completed Tasks

View File

@@ -50,6 +50,21 @@ final class ScheduleViewModel {
return grouped.sorted { $0.key < $1.key }.map { ($0.key, $0.value) } 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 { var hasFilters: Bool {
selectedSports.count < Sport.supported.count || !searchText.isEmpty selectedSports.count < Sport.supported.count || !searchText.isEmpty
} }

View File

@@ -89,14 +89,17 @@ struct ScheduleListView: View {
.listRowBackground(Color.clear) .listRowBackground(Color.clear)
} }
ForEach(viewModel.gamesByDate, id: \.date) { dateGroup in ForEach(viewModel.gamesBySport, id: \.sport) { sportGroup in
Section { Section {
ForEach(dateGroup.games) { richGame in ForEach(sportGroup.games) { richGame in
GameRowView(game: richGame) GameRowView(game: richGame, showDate: true)
} }
} header: { } header: {
Text(formatSectionDate(dateGroup.date)) HStack(spacing: 8) {
.font(.headline) Image(systemName: sportGroup.sport.iconName)
Text(sportGroup.sport.rawValue)
}
.font(.headline)
} }
.listRowBackground(Theme.cardBackground(colorScheme)) .listRowBackground(Theme.cardBackground(colorScheme))
} }
@@ -204,9 +207,18 @@ struct SportFilterChip: View {
struct GameRowView: View { struct GameRowView: View {
let game: RichGame let game: RichGame
var showDate: Bool = false
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 8) { VStack(alignment: .leading, spacing: 8) {
// Date (when grouped by sport)
if showDate {
Text(formattedDate)
.font(.caption)
.fontWeight(.medium)
.foregroundStyle(.secondary)
}
// Teams // Teams
HStack { HStack {
VStack(alignment: .leading, spacing: 4) { VStack(alignment: .leading, spacing: 4) {
@@ -220,11 +232,6 @@ struct GameRowView: View {
} }
Spacer() Spacer()
// Sport badge
Image(systemName: game.game.sport.iconName)
.font(.caption)
.foregroundStyle(.secondary)
} }
// Game info // Game info
@@ -241,6 +248,12 @@ struct GameRowView: View {
} }
.padding(.vertical, 4) .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 // MARK: - Team Badge