diff --git a/Andromida/App/Localization/Localizable.xcstrings b/Andromida/App/Localization/Localizable.xcstrings index 35d7e26..8c753bb 100644 --- a/Andromida/App/Localization/Localizable.xcstrings +++ b/Andromida/App/Localization/Localizable.xcstrings @@ -1053,10 +1053,6 @@ } } }, - "Last 7 days average" : { - "comment" : "Explanation of the \"Weekly trend\" insight card, describing how the user's average completion rate over the last 7 days is displayed.", - "isCommentAutoGenerated" : true - }, "Last synced %@" : { "extractionState" : "stale", "localizations" : { @@ -2060,10 +2056,6 @@ "comment" : "An accessibility label for the weekly completion chart in the insight detail sheet.", "isCommentAutoGenerated" : true }, - "Weekly trend" : { - "comment" : "Title of an insight card that shows the user their average completion rate over the last 7 days.", - "isCommentAutoGenerated" : true - }, "What this means" : { "comment" : "A label displayed above the explanation text in the insight detail sheet.", "isCommentAutoGenerated" : true @@ -2134,12 +2126,8 @@ } } }, - "Your average completion rate over the last 7 days. This shows how consistent you've been recently and helps identify patterns." : { - "comment" : "Text for the \"Weekly trend\" insight card, describing its purpose and functionality.", - "isCommentAutoGenerated" : true - }, - "Your completion percentage for today across all rituals. This resets each morning, giving you a fresh start every day." : { - "comment" : "Explanation of the insight card that shows the user their completion percentage for today across all their active rituals. This percentage resets each morning, providing a fresh start every day.", + "Your completion percentage for today across all rituals. The chart shows your last 7 days—this helps you spot patterns and stay consistent." : { + "comment" : "Explanation of the insight card that shows the user their completion percentage for today across all their active rituals, with a chart displaying their last 7 days.", "isCommentAutoGenerated" : true }, "Your current streak of consecutive days with 100% habit completion. Complete all your habits today to keep the streak going!" : { diff --git a/Andromida/App/State/RitualStore.swift b/Andromida/App/State/RitualStore.swift index bf673da..5ca52e4 100644 --- a/Andromida/App/State/RitualStore.swift +++ b/Andromida/App/State/RitualStore.swift @@ -243,8 +243,11 @@ final class RitualStore: RitualStoreProviding { let completedToday = rituals.flatMap { $0.habits }.filter { isHabitCompletedToday($0) }.count let completionRate = totalHabits == 0 ? 0 : Int((Double(completedToday) / Double(totalHabits)) * 100) - // Build per-ritual breakdowns - let ritualDaysBreakdown = rituals.map { ritual in + // Days active = unique calendar days with at least one check-in + let daysActiveCount = datesWithActivity().count + + // Build per-ritual progress breakdown + let ritualProgressBreakdown = rituals.map { ritual in BreakdownItem( label: ritual.title, value: ritualDayLabel(for: ritual) @@ -259,7 +262,6 @@ final class RitualStore: RitualStoreProviding { ) } - let activeDays = rituals.map { ritualDayIndex(for: $0) }.reduce(0, +) // Streak tracking let current = currentStreak() @@ -305,26 +307,18 @@ final class RitualStore: RitualStoreProviding { title: String(localized: "Completion"), value: "\(completionRate)%", caption: String(localized: "Today's progress"), - explanation: String(localized: "Your completion percentage for today across all rituals. This resets each morning, giving you a fresh start every day."), + explanation: String(localized: "Your completion percentage for today across all rituals. The chart shows your last 7 days—this helps you spot patterns and stay consistent."), symbolName: "chart.bar.fill", - breakdown: nil - ), - InsightCard( - title: String(localized: "Days"), - value: "\(activeDays)", - caption: String(localized: "Days on your journey"), - explanation: String(localized: "The total number of days you've been working on your rituals. This shows your progress through each arc, combining all active rituals."), - symbolName: "calendar", - breakdown: ritualDaysBreakdown - ), - InsightCard( - title: String(localized: "Weekly trend"), - value: "\(weeklyAverage)%", - caption: String(localized: "Last 7 days average"), - explanation: String(localized: "Your average completion rate over the last 7 days. This shows how consistent you've been recently and helps identify patterns."), - symbolName: "chart.line.uptrend.xyaxis", breakdown: trendBreakdown, trendData: trendData + ), + InsightCard( + title: String(localized: "Days Active"), + value: "\(daysActiveCount)", + caption: String(localized: "Days you checked in"), + explanation: String(localized: "The number of days you've completed at least one habit. Each day you check in counts toward your journey."), + symbolName: "calendar", + breakdown: ritualProgressBreakdown ) ] } diff --git a/Andromida/App/Views/Insights/Components/InsightCardView.swift b/Andromida/App/Views/Insights/Components/InsightCardView.swift index b611b5b..8d64184 100644 --- a/Andromida/App/Views/Insights/Components/InsightCardView.swift +++ b/Andromida/App/Views/Insights/Components/InsightCardView.swift @@ -20,12 +20,10 @@ struct InsightCardView: View { private var cardContent: some View { VStack(alignment: .leading, spacing: Design.Spacing.medium) { - HStack(spacing: Design.Spacing.small) { - Image(systemName: card.symbolName) - .foregroundStyle(AppAccent.primary) - .accessibilityHidden(true) + HStack { Text(card.title) .font(.subheadline) + .bold() .foregroundStyle(AppTextColors.secondary) Spacer() @@ -37,49 +35,39 @@ struct InsightCardView: View { .accessibilityHidden(true) } - // Show mini sparkline if trend data is available - if let trendData = card.trendData, !trendData.isEmpty { - HStack(alignment: .bottom, spacing: Design.Spacing.medium) { - VStack(alignment: .leading, spacing: Design.Spacing.xSmall) { - Text(card.value) - .font(.title) - .foregroundStyle(AppTextColors.primary) - .bold() - Text(card.caption) - .font(.caption) - .foregroundStyle(AppTextColors.secondary) - } - - Spacer() - - // Mini sparkline chart - Chart(trendData) { point in - BarMark( - x: .value("Day", point.label), - y: .value("Completion", point.value) - ) - .foregroundStyle( - point.value >= 1.0 ? AppStatus.success : - point.value >= 0.5 ? AppAccent.primary : - AppTextColors.tertiary - ) - .cornerRadius(2) - } - .chartYScale(domain: 0...1) - .chartXAxis(.hidden) - .chartYAxis(.hidden) - .frame(width: 80, height: 40) - .accessibilityHidden(true) - } - } else { - Text(card.value) - .font(.title) - .foregroundStyle(AppTextColors.primary) - .bold() + // Show value prominently + Text(card.value) + .font(.title) + .foregroundStyle(AppTextColors.primary) + .bold() + + // Show caption if present (non-chart cards) + if !card.caption.isEmpty { Text(card.caption) .font(.caption) .foregroundStyle(AppTextColors.secondary) } + + // Show full-width mini bar chart at bottom (Athlytic style) + if let trendData = card.trendData, !trendData.isEmpty { + Chart(trendData) { point in + BarMark( + x: .value("Day", point.label), + y: .value("Completion", point.value) + ) + .foregroundStyle( + point.value >= 1.0 ? AppStatus.success : + point.value >= 0.5 ? AppAccent.primary : + AppTextColors.tertiary + ) + .cornerRadius(2) + } + .chartYScale(domain: 0...1) + .chartXAxis(.hidden) + .chartYAxis(.hidden) + .frame(height: 20) + .accessibilityHidden(true) + } } .padding(Design.Spacing.large) .background(AppSurface.card)