// // HomeView.swift // SportsTime // import SwiftUI import SwiftData struct HomeView: View { @Environment(\.modelContext) private var modelContext @Environment(\.colorScheme) private var colorScheme @Query(sort: \SavedTrip.updatedAt, order: .reverse) private var savedTrips: [SavedTrip] @State private var showNewTrip = false @State private var selectedTab = 0 var body: some View { TabView(selection: $selectedTab) { // Home Tab NavigationStack { ScrollView { VStack(spacing: Theme.Spacing.xl) { // Hero Card heroCard .staggeredAnimation(index: 0) // Quick Actions quickActions .staggeredAnimation(index: 1) // Saved Trips if !savedTrips.isEmpty { savedTripsSection .staggeredAnimation(index: 2) } // Featured / Tips tipsSection .staggeredAnimation(index: 3) } .padding(Theme.Spacing.md) } .themedBackground() .navigationTitle("SportsTime") .toolbar { ToolbarItem(placement: .primaryAction) { Button { showNewTrip = true } label: { Image(systemName: "plus.circle.fill") .font(.title2) .foregroundStyle(Theme.warmOrange) } } } } .tabItem { Label("Home", systemImage: "house.fill") } .tag(0) // Schedule Tab NavigationStack { ScheduleListView() } .tabItem { Label("Schedule", systemImage: "calendar") } .tag(1) // My Trips Tab NavigationStack { SavedTripsListView(trips: savedTrips) } .tabItem { Label("My Trips", systemImage: "suitcase.fill") } .tag(2) // Settings Tab NavigationStack { SettingsView() } .tabItem { Label("Settings", systemImage: "gear") } .tag(3) } .tint(Theme.warmOrange) .sheet(isPresented: $showNewTrip) { TripCreationView() } } // MARK: - Hero Card private var heroCard: some View { VStack(spacing: Theme.Spacing.lg) { VStack(alignment: .leading, spacing: Theme.Spacing.sm) { Text("Adventure Awaits") .font(.system(size: Theme.FontSize.heroTitle, weight: .bold, design: .rounded)) .foregroundStyle(Theme.textPrimary(colorScheme)) Text("Plan your ultimate sports road trip. Visit stadiums, catch games, and create unforgettable memories.") .font(.system(size: Theme.FontSize.body)) .foregroundStyle(Theme.textSecondary(colorScheme)) .fixedSize(horizontal: false, vertical: true) } .frame(maxWidth: .infinity, alignment: .leading) Button { showNewTrip = true } label: { HStack(spacing: Theme.Spacing.xs) { Image(systemName: "map.fill") Text("Start Planning") .fontWeight(.semibold) } .frame(maxWidth: .infinity) .padding(Theme.Spacing.md) .background(Theme.warmOrange) .foregroundStyle(.white) .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.medium)) } .pressableStyle() .glowEffect(color: Theme.warmOrange, radius: 12) } .padding(Theme.Spacing.lg) .background(Theme.cardBackground(colorScheme)) .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.xlarge)) .overlay { RoundedRectangle(cornerRadius: Theme.CornerRadius.xlarge) .stroke(Theme.surfaceGlow(colorScheme), lineWidth: 1) } .shadow(color: Theme.cardShadow(colorScheme), radius: 15, y: 8) } // MARK: - Quick Actions private var quickActions: some View { VStack(alignment: .leading, spacing: Theme.Spacing.sm) { Text("Quick Start") .font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded)) .foregroundStyle(Theme.textPrimary(colorScheme)) HStack(spacing: Theme.Spacing.sm) { ForEach(Sport.supported) { sport in QuickSportButton(sport: sport) { showNewTrip = true } } } } } // MARK: - Saved Trips private var savedTripsSection: some View { VStack(alignment: .leading, spacing: Theme.Spacing.sm) { HStack { Text("Recent Trips") .font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded)) .foregroundStyle(Theme.textPrimary(colorScheme)) Spacer() Button { selectedTab = 2 } label: { HStack(spacing: 4) { Text("See All") Image(systemName: "chevron.right") .font(.caption) } .font(.system(size: Theme.FontSize.caption, weight: .medium)) .foregroundStyle(Theme.warmOrange) } } ForEach(Array(savedTrips.prefix(3).enumerated()), id: \.element.id) { index, savedTrip in if let trip = savedTrip.trip { SavedTripCard(savedTrip: savedTrip, trip: trip) .staggeredAnimation(index: index, delay: 0.05) } } } } // MARK: - Tips private var tipsSection: some View { VStack(alignment: .leading, spacing: Theme.Spacing.sm) { Text("Planning Tips") .font(.system(size: Theme.FontSize.sectionTitle, weight: .bold, design: .rounded)) .foregroundStyle(Theme.textPrimary(colorScheme)) VStack(spacing: Theme.Spacing.xs) { TipRow(icon: "calendar.badge.clock", title: "Check schedules early", subtitle: "Game times can change, sync often") TipRow(icon: "car.fill", title: "Plan rest days", subtitle: "Don't overdo the driving") TipRow(icon: "star.fill", title: "Mark must-sees", subtitle: "Ensure your favorite matchups are included") } .padding(Theme.Spacing.md) .background(Theme.cardBackground(colorScheme)) .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.large)) .overlay { RoundedRectangle(cornerRadius: Theme.CornerRadius.large) .stroke(Theme.surfaceGlow(colorScheme), lineWidth: 1) } } } } // MARK: - Supporting Views struct QuickSportButton: View { let sport: Sport let action: () -> Void @Environment(\.colorScheme) private var colorScheme @State private var isPressed = false var body: some View { Button(action: action) { VStack(spacing: Theme.Spacing.xs) { ZStack { Circle() .fill(sport.themeColor.opacity(0.15)) .frame(width: 44, height: 44) Image(systemName: sport.iconName) .font(.title2) .foregroundStyle(sport.themeColor) } Text(sport.rawValue) .font(.system(size: Theme.FontSize.micro, weight: .medium)) .foregroundStyle(Theme.textSecondary(colorScheme)) } .frame(maxWidth: .infinity) .padding(.vertical, Theme.Spacing.sm) .background(Theme.cardBackground(colorScheme)) .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.medium)) .overlay { RoundedRectangle(cornerRadius: Theme.CornerRadius.medium) .stroke(Theme.surfaceGlow(colorScheme), lineWidth: 1) } .scaleEffect(isPressed ? 0.95 : 1.0) } .buttonStyle(.plain) .simultaneousGesture( DragGesture(minimumDistance: 0) .onChanged { _ in withAnimation(Theme.Animation.spring) { isPressed = true } } .onEnded { _ in withAnimation(Theme.Animation.spring) { isPressed = false } } ) } } struct SavedTripCard: View { let savedTrip: SavedTrip let trip: Trip @Environment(\.colorScheme) private var colorScheme var body: some View { NavigationLink { TripDetailView(trip: trip, games: savedTrip.games) } label: { HStack(spacing: Theme.Spacing.md) { // Route preview icon ZStack { Circle() .fill(Theme.warmOrange.opacity(0.15)) .frame(width: 44, height: 44) Image(systemName: "map.fill") .foregroundStyle(Theme.warmOrange) } VStack(alignment: .leading, spacing: 4) { Text(trip.name) .font(.system(size: Theme.FontSize.body, weight: .semibold)) .foregroundStyle(Theme.textPrimary(colorScheme)) Text(trip.formattedDateRange) .font(.system(size: Theme.FontSize.caption)) .foregroundStyle(Theme.textSecondary(colorScheme)) HStack(spacing: Theme.Spacing.sm) { HStack(spacing: 4) { Image(systemName: "mappin") .font(.caption2) Text("\(trip.stops.count) cities") } HStack(spacing: 4) { Image(systemName: "sportscourt") .font(.caption2) Text("\(trip.totalGames) games") } } .font(.system(size: Theme.FontSize.micro)) .foregroundStyle(Theme.textMuted(colorScheme)) } Spacer() Image(systemName: "chevron.right") .font(.caption) .foregroundStyle(Theme.textMuted(colorScheme)) } .padding(Theme.Spacing.md) .background(Theme.cardBackground(colorScheme)) .clipShape(RoundedRectangle(cornerRadius: Theme.CornerRadius.medium)) .overlay { RoundedRectangle(cornerRadius: Theme.CornerRadius.medium) .stroke(Theme.surfaceGlow(colorScheme), lineWidth: 1) } } .buttonStyle(.plain) } } struct TipRow: View { let icon: String let title: String let subtitle: String @Environment(\.colorScheme) private var colorScheme var body: some View { HStack(spacing: Theme.Spacing.sm) { ZStack { Circle() .fill(Theme.routeGold.opacity(0.15)) .frame(width: 36, height: 36) Image(systemName: icon) .font(.system(size: 14)) .foregroundStyle(Theme.routeGold) } VStack(alignment: .leading, spacing: 2) { Text(title) .font(.system(size: Theme.FontSize.caption, weight: .medium)) .foregroundStyle(Theme.textPrimary(colorScheme)) Text(subtitle) .font(.system(size: Theme.FontSize.micro)) .foregroundStyle(Theme.textSecondary(colorScheme)) } Spacer() } } } // MARK: - Saved Trips List View struct SavedTripsListView: View { let trips: [SavedTrip] @Environment(\.colorScheme) private var colorScheme var body: some View { Group { if trips.isEmpty { EmptyStateView( icon: "suitcase", title: "No Saved Trips", message: "Your planned adventures will appear here. Start planning your first sports road trip!" ) .frame(maxWidth: .infinity, maxHeight: .infinity) } else { ScrollView { LazyVStack(spacing: Theme.Spacing.md) { ForEach(Array(trips.enumerated()), id: \.element.id) { index, savedTrip in if let trip = savedTrip.trip { NavigationLink { TripDetailView(trip: trip, games: savedTrip.games) } label: { SavedTripListRow(trip: trip) } .buttonStyle(.plain) .staggeredAnimation(index: index) } } } .padding(Theme.Spacing.md) } } } .themedBackground() .navigationTitle("My Trips") } } struct SavedTripListRow: View { let trip: Trip @Environment(\.colorScheme) private var colorScheme var body: some View { HStack(spacing: Theme.Spacing.md) { // Route preview VStack(spacing: 4) { ForEach(0..