fix: resolve specificStadium achievement ID mismatch

The Green Monster (Fenway) and Ivy League (Wrigley) achievements
weren't working because:
1. Symbolic IDs use lowercase sport (stadium_mlb_bos)
2. Sport enum uses uppercase raw values (MLB)
3. Visits store stadium UUIDs, not symbolic IDs

Added resolveSymbolicStadiumId() helper that:
- Uppercases the sport string before Sport(rawValue:)
- Looks up team by abbreviation and sport
- Returns the team's stadiumId as UUID string

Also fixed:
- getStadiumIdsForLeague returns UUID strings (not symbolic IDs)
- AchievementProgress.isEarned computed from progress OR stored record
- getStadiumIdsForDivision queries CanonicalTeam properly

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-01-11 22:22:29 -06:00
parent dcd5edb229
commit 5c13650742
20 changed files with 1619 additions and 141 deletions

View File

@@ -194,25 +194,25 @@ struct VisitDetailView: View {
}
if let matchup = visit.matchupDescription {
HStack {
VStack(alignment: .leading, spacing: 4) {
Text("Matchup")
.foregroundStyle(Theme.textSecondary(colorScheme))
Spacer()
Text(matchup)
.foregroundStyle(Theme.textPrimary(colorScheme))
.fontWeight(.medium)
}
.frame(maxWidth: .infinity, alignment: .leading)
}
if let score = visit.finalScore {
HStack {
VStack(alignment: .leading, spacing: 4) {
Text("Final Score")
.foregroundStyle(Theme.textSecondary(colorScheme))
Spacer()
Text(score)
.foregroundStyle(Theme.textPrimary(colorScheme))
.fontWeight(.bold)
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}
.font(.body)
@@ -238,42 +238,42 @@ struct VisitDetailView: View {
}
// Date
HStack {
VStack(alignment: .leading, spacing: 4) {
Text("Date")
.foregroundStyle(Theme.textSecondary(colorScheme))
Spacer()
Text(formattedDate)
.foregroundStyle(Theme.textPrimary(colorScheme))
}
.frame(maxWidth: .infinity, alignment: .leading)
// Seat location
if let seat = visit.seatLocation, !seat.isEmpty {
HStack {
VStack(alignment: .leading, spacing: 4) {
Text("Seat")
.foregroundStyle(Theme.textSecondary(colorScheme))
Spacer()
Text(seat)
.foregroundStyle(Theme.textPrimary(colorScheme))
}
.frame(maxWidth: .infinity, alignment: .leading)
}
// Source
HStack {
VStack(alignment: .leading, spacing: 4) {
Text("Source")
.foregroundStyle(Theme.textSecondary(colorScheme))
Spacer()
Text(visit.source.displayName)
.foregroundStyle(Theme.textMuted(colorScheme))
}
.frame(maxWidth: .infinity, alignment: .leading)
// Created date
HStack {
VStack(alignment: .leading, spacing: 4) {
Text("Logged")
.foregroundStyle(Theme.textSecondary(colorScheme))
Spacer()
Text(formattedCreatedDate)
.foregroundStyle(Theme.textMuted(colorScheme))
}
.frame(maxWidth: .infinity, alignment: .leading)
}
.font(.body)
.padding(Theme.Spacing.lg)