Fix widget gap: forward-fill months and use reference month date for batch update

BatchUpdateView now accepts a saveDate parameter. When saving from a past-month
check-in, the call site passes referenceDate.endOfMonth so snapshots are dated
within the correct month instead of today. For the current month, Date() is used.

Widget forward-fills missing month buckets (trend and category series) using the
most recent earlier month's value, preventing gaps when a check-in is done in the
following month.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
alexandrev-tibco
2026-02-20 22:51:40 +01:00
parent e3ec0ddb25
commit c94974546f
2 changed files with 22 additions and 4 deletions
@@ -154,7 +154,8 @@ struct MonthlyCheckInView: View {
.sheet(isPresented: $showBatchUpdate) { .sheet(isPresented: $showBatchUpdate) {
viewModel.refresh() viewModel.refresh()
} content: { } content: {
BatchUpdateView(sources: viewModel.sources) let batchSaveDate = referenceDate.isSameMonth(as: Date()) ? Date() : referenceDate.endOfMonth
BatchUpdateView(sources: viewModel.sources, saveDate: batchSaveDate)
} }
.onChange(of: starRating) { _, newValue in .onChange(of: starRating) { _, newValue in
MonthlyCheckInStore.setRating(newValue == 0 ? nil : newValue, for: referenceDate) MonthlyCheckInStore.setRating(newValue == 0 ? nil : newValue, for: referenceDate)
@@ -945,10 +946,16 @@ struct AchievementMilestoneBar: View {
struct BatchUpdateView: View { struct BatchUpdateView: View {
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
let sources: [InvestmentSource] let sources: [InvestmentSource]
let saveDate: Date
@State private var values: [UUID: String] = [:] @State private var values: [UUID: String] = [:]
@State private var contributions: [UUID: String] = [:] @State private var contributions: [UUID: String] = [:]
@State private var savedCount = 0 @State private var savedCount = 0
init(sources: [InvestmentSource], saveDate: Date = Date()) {
self.sources = sources
self.saveDate = saveDate
}
var body: some View { var body: some View {
NavigationStack { NavigationStack {
List { List {
@@ -1080,7 +1087,7 @@ struct BatchUpdateView: View {
repository.createSnapshot( repository.createSnapshot(
for: source, for: source,
date: Date(), date: saveDate,
value: parsed, value: parsed,
contribution: parsedContribution contribution: parsedContribution
) )
+13 -2
View File
@@ -264,7 +264,12 @@ struct InvestmentWidgetProvider: TimelineProvider {
} }
let monthFormatter = DateFormatter() let monthFormatter = DateFormatter()
monthFormatter.dateFormat = "MMM" monthFormatter.dateFormat = "MMM"
trendPoints = months.map { monthlyTotals[$0] ?? .zero } trendPoints = months.map { month in
if let value = monthlyTotals[month] { return value }
// Forward-fill: use the most recent earlier month's value
let previous = sortedMonths.last { $0.0 < month }
return previous?.1 ?? .zero
}
trendLabels = months.map { monthFormatter.string(from: $0) } trendLabels = months.map { monthFormatter.string(from: $0) }
} }
@@ -307,7 +312,13 @@ struct InvestmentWidgetProvider: TimelineProvider {
let categoryEvolution: [CategorySeries] = categoryTotalsData.prefix(4).map { category in let categoryEvolution: [CategorySeries] = categoryTotalsData.prefix(4).map { category in
let monthMap = categoryMonthlyTotals[category.id] ?? [:] let monthMap = categoryMonthlyTotals[category.id] ?? [:]
let points = months.map { monthMap[$0] ?? .zero } let sortedCategoryMonths = monthMap.map { ($0.key, $0.value) }.sorted { $0.0 < $1.0 }
let points = months.map { month -> Decimal in
if let value = monthMap[month] { return value }
// Forward-fill: use the most recent earlier month's value
let previous = sortedCategoryMonths.last { $0.0 < month }
return previous?.1 ?? .zero
}
return CategorySeries( return CategorySeries(
id: category.id, id: category.id,
name: category.name, name: category.name,