1.4.1 (build 42): contribución por snapshot, fixes Period vs Period y navegación iPad

- 151: contribución editable por snapshot en cualquier modo (quitado gate detailed)
  con diálogo de propagación al editar: solo este / adelante / atrás / todos
  (SnapshotRepository.propagateContribution, SnapshotFormViewModel.contributionChanged)
- 148: Period vs Period mostraba mal el último mes del period B — la agrupación usaba
  chartMonth(for:) que aplicaba el grace-period del check-in a snapshots históricos;
  ahora agrupa por mes calendario crudo
- 152: iPad Sources no saltaba entre fuentes — añadido .id() al SourceDetailView para
  recrear el StateObject al cambiar de selección
- 146/147: Year vs Year con forecast del año en curso (asterisco de estimado) y KPIs
  arriba / detalle debajo
- 145: card de contribución mensual en SourceDetailView
- Nuevas claves snapshot_contribution_propagate_* en los 7 idiomas

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015qUZrBusG82T37R7PeokqJ
This commit is contained in:
alexandrev-tibco
2026-06-30 16:51:03 +02:00
parent 54dfd8a8e3
commit 7a18dd8360
48 changed files with 2854 additions and 608 deletions
@@ -14,6 +14,10 @@ struct PerformanceBarChart: View {
.font(.caption)
.foregroundColor(.secondary)
if !data.isEmpty {
ChartStatsRow(stats: perfStats)
}
if !data.isEmpty {
Chart(data, id: \.category) { item in
BarMark(
@@ -84,6 +88,19 @@ struct PerformanceBarChart: View {
.cornerRadius(AppConstants.UI.cornerRadius)
.shadow(color: .black.opacity(0.05), radius: 8, y: 2)
}
private var perfStats: [ChartStat] {
guard !data.isEmpty else { return [] }
let values = data.map { $0.cagr }
let best = values.max() ?? 0
let worst = values.min() ?? 0
let avg = values.reduce(0, +) / Double(values.count)
return [
ChartStat(label: "Best", value: String(format: "%.1f%%", best), color: .positiveGreen),
ChartStat(label: "Worst", value: String(format: "%.1f%%", worst), color: .negativeRed),
ChartStat(label: "Average", value: String(format: "%.1f%%", avg), color: .secondary),
]
}
}
// MARK: - Horizontal Bar Version