// // ExportableInsightsViews.swift // Reflect // // Exportable insights views with sample AI-generated insights for screenshots. // import SwiftUI // MARK: - Sample Insights Data struct SampleInsightsData { static let monthInsights: [Insight] = [ Insight( icon: "chart.line.uptrend.xyaxis", title: "Weekend Mood Boost", description: "Your mood is consistently 23% higher on weekends. Saturday mornings seem particularly positive for you.", mood: .great ), Insight( icon: "moon.stars", title: "Sleep Connection", description: "Days following 7+ hours of sleep show notably better moods. Prioritizing rest could help maintain your wellbeing.", mood: .good ), Insight( icon: "figure.walk", title: "Movement Matters", description: "Your mood averages 'Good' on days with 6,000+ steps, compared to 'Average' on less active days.", mood: .good ), Insight( icon: "calendar.badge.checkmark", title: "Consistent Logger", description: "You've logged your mood 28 out of 31 days this month. This consistency helps reveal meaningful patterns.", mood: nil ), Insight( icon: "arrow.up.right", title: "Upward Trend", description: "Your average mood has improved from 'Average' to 'Good' over the past two weeks. Keep it up!", mood: .good ) ] static let yearInsights: [Insight] = [ Insight( icon: "sun.max", title: "Summer Peak", description: "June through August showed your highest mood scores of the year, with 67% of days rated 'Good' or better.", mood: .great ), Insight( icon: "leaf", title: "Seasonal Patterns", description: "Your mood tends to dip slightly in late winter. Consider planning mood-boosting activities for February.", mood: .average ), Insight( icon: "star.fill", title: "Personal Best", description: "You achieved a 45-day streak of logging in March - your longest streak this year!", mood: nil ), Insight( icon: "heart.fill", title: "Most Common Mood", description: "'Good' has been your most frequent mood this year, appearing 42% of the time.", mood: .good ), Insight( icon: "chart.bar.fill", title: "Year in Review", description: "You've logged 312 mood entries this year. That's 85% consistency - well above average!", mood: nil ) ] static let allTimeInsights: [Insight] = [ Insight( icon: "trophy.fill", title: "Mood Champion", description: "Since you started, your overall mood average has steadily improved by 18%. Real progress!", mood: .great ), Insight( icon: "flame.fill", title: "Longest Streak", description: "Your all-time best logging streak is 89 days. You're building a powerful habit.", mood: nil ), Insight( icon: "calendar", title: "Tuesday Tendency", description: "Historically, Tuesdays are your most challenging day. Consider scheduling something enjoyable.", mood: .average ), Insight( icon: "sparkles", title: "Growth Mindset", description: "Your 'Great' days have increased from 12% to 24% since you started tracking. Wonderful progress!", mood: .great ), Insight( icon: "person.fill.checkmark", title: "Self-Awareness", description: "With 847 total entries, you've built remarkable insight into your emotional patterns.", mood: nil ) ] } // MARK: - Exportable Insights View struct ExportableInsightsView: View { let colorScheme: ColorScheme let moodTint: MoodTints let imagePack: MoodImages private var textColor: Color { colorScheme == .dark ? .white : .black } private var backgroundColor: Color { colorScheme == .dark ? Color(red: 0.11, green: 0.11, blue: 0.12) : Color(red: 0.95, green: 0.95, blue: 0.97) } var body: some View { VStack(spacing: 0) { // Status bar area (dynamic island space) Color.clear .frame(height: 59) // Main content VStack(spacing: 20) { // Header - matches InsightsView exactly HStack { Text("Insights") .font(.title.weight(.bold)) .foregroundColor(textColor) Spacer() // AI badge HStack(spacing: 4) { Image(systemName: "sparkles") .font(.caption.weight(.medium)) Text("AI") .font(.caption.weight(.semibold)) } .foregroundColor(.white) .padding(.horizontal, 8) .padding(.vertical, 4) .background( LinearGradient( colors: [.purple, .blue], startPoint: .leading, endPoint: .trailing ) ) .clipShape(Capsule()) } .padding(.horizontal) // This Month Section (show first 3 insights to fit) ExportableInsightsSectionView( title: "This Month", icon: "calendar", insights: Array(SampleInsightsData.monthInsights.prefix(3)), textColor: textColor, moodTint: moodTint, imagePack: imagePack, colorScheme: colorScheme ) // This Year Section (show first 2 insights to fit) ExportableInsightsSectionView( title: "This Year", icon: "calendar.badge.clock", insights: Array(SampleInsightsData.yearInsights.prefix(2)), textColor: textColor, moodTint: moodTint, imagePack: imagePack, colorScheme: colorScheme ) Spacer() } .padding(.top, 8) // Tab bar ExportableTabBar(colorScheme: colorScheme, selectedTab: "Insights") } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(backgroundColor) } } // MARK: - Exportable Insights Section View struct ExportableInsightsSectionView: View { let title: String let icon: String let insights: [Insight] let textColor: Color let moodTint: MoodTints let imagePack: MoodImages let colorScheme: ColorScheme var body: some View { VStack(spacing: 0) { // Section Header - matches InsightsSectionView exactly HStack { Image(systemName: icon) .font(.headline.weight(.medium)) .foregroundColor(textColor.opacity(0.6)) Text(title) .font(.title3.weight(.bold)) .foregroundColor(textColor) Spacer() Image(systemName: "chevron.up") .font(.caption.weight(.semibold)) .foregroundColor(textColor.opacity(0.4)) } .padding(.horizontal, 16) .padding(.vertical, 14) // Insights List - matches InsightsSectionView exactly VStack(spacing: 10) { ForEach(insights) { insight in ExportableInsightCardView( insight: insight, textColor: textColor, moodTint: moodTint, imagePack: imagePack, colorScheme: colorScheme ) } } .padding(.horizontal, 16) .padding(.bottom, 16) } .background( RoundedRectangle(cornerRadius: 16) .fill(colorScheme == .dark ? Color(.systemGray6) : .white) ) .padding(.horizontal) } } // MARK: - Exportable Insight Card View struct ExportableInsightCardView: View { let insight: Insight let textColor: Color let moodTint: MoodTints let imagePack: MoodImages let colorScheme: ColorScheme private var accentColor: Color { if let mood = insight.mood { return moodTint.color(forMood: mood) } return .accentColor } var body: some View { // Matches InsightCardView exactly HStack(alignment: .top, spacing: 14) { // Icon ZStack { Circle() .fill(accentColor.opacity(0.15)) .frame(width: 44, height: 44) if let mood = insight.mood { imagePack.icon(forMood: mood) .resizable() .aspectRatio(contentMode: .fit) .frame(width: 22, height: 22) .foregroundColor(accentColor) } else { Image(systemName: insight.icon) .font(.headline.weight(.semibold)) .foregroundColor(accentColor) } } // Text Content VStack(alignment: .leading, spacing: 4) { Text(insight.title) .font(.subheadline.weight(.semibold)) .foregroundColor(textColor) Text(insight.description) .font(.subheadline) .foregroundColor(textColor.opacity(0.7)) .fixedSize(horizontal: false, vertical: true) } Spacer() } .padding(14) .background( RoundedRectangle(cornerRadius: 12) .fill(colorScheme == .dark ? Color(.systemGray5) : Color(.systemGray6)) ) } } // MARK: - Exportable Tab Bar struct ExportableTabBar: View { let colorScheme: ColorScheme let selectedTab: String private var textColor: Color { colorScheme == .dark ? .white : .black } private var tabBarBackground: Color { colorScheme == .dark ? Color(red: 0.11, green: 0.11, blue: 0.12) : .white } var body: some View { HStack(spacing: 0) { tabItem(icon: "list.bullet", title: "Day", isSelected: selectedTab == "Day") tabItem(icon: "calendar", title: "Month", isSelected: selectedTab == "Month") tabItem(icon: "chart.bar.fill", title: "Year", isSelected: selectedTab == "Year") tabItem(icon: "lightbulb.fill", title: "Insights", isSelected: selectedTab == "Insights") tabItem(icon: "gearshape.fill", title: "Settings", isSelected: selectedTab == "Settings") } .padding(.top, 8) .padding(.bottom, 28) // Home indicator space .background(tabBarBackground) } private func tabItem(icon: String, title: String, isSelected: Bool) -> some View { VStack(spacing: 4) { Image(systemName: icon) .font(.system(size: 22)) Text(title) .font(.caption2) } .foregroundColor(isSelected ? .accentColor : textColor.opacity(0.4)) .frame(maxWidth: .infinity) } } // MARK: - Insights Export Container struct ExportableInsightsContainer: View { let width: CGFloat let height: CGFloat let colorScheme: ColorScheme let content: Content init(width: CGFloat, height: CGFloat, colorScheme: ColorScheme, @ViewBuilder content: () -> Content) { self.width = width self.height = height self.colorScheme = colorScheme self.content = content() } private var backgroundColor: Color { colorScheme == .dark ? Color(red: 0.11, green: 0.11, blue: 0.12) : Color(red: 0.95, green: 0.95, blue: 0.97) } var body: some View { content .environment(\.colorScheme, colorScheme) .frame(width: width, height: height) .background(backgroundColor) } }