Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2026-01-25 20:20:56 -06:00
parent b444e85b04
commit 5f3f79e6be
3 changed files with 47 additions and 77 deletions

View File

@ -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!" : {

View File

@ -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
)
]
}

View File

@ -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)