Files
Reflect/Shared/Services/ExportableInsightsViews.swift
Trey t 0442eab1f8 Rebrand entire project from Feels to Reflect
Complete rename across all bundle IDs, App Groups, CloudKit containers,
StoreKit product IDs, data store filenames, URL schemes, logger subsystems,
Swift identifiers, user-facing strings (7 languages), file names, directory
names, Xcode project, schemes, assets, and documentation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 11:47:16 -06:00

381 lines
12 KiB
Swift

//
// ExportableInsightsViews.swift
// Reflect
//
// Exportable insights views with sample AI-generated insights for screenshots.
//
#if DEBUG
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<Content: View>: 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)
}
}
#endif