Fixes UI iPad + diagnóstico iCloud (build 57)
Feedback del usuario en iPad: - Allocation: gráfica de tarta → semicírculo estilo hemiciclo (parlamento). SemicircleAllocation con arcos dibujados a mano (SemicircleArc Shape), segmentos por cuota en 180°, total/selección en el centro, tap por segmento. - Charts de Analyze (Compare, Period vs Period, Year vs Year) ahora SÍ muestran KPIs en la cabecera de iPad como el resto: Compare (líder/rezagado por modo), Period (retorno final por periodo), YoY (retorno final por año seleccionado). - Momentum & Streaks en el sidebar del Journal (iPad, 320pt): variante compact — tiles en fila sin subtítulos que rompían a 3 líneas, sin bloque de logros. iCloud: - CKErrorPartialFailure (CKErrorDomain code 2) mostraba un mensaje opaco. CoreDataStack.hint(for:) recorre CKPartialErrorsByItemIDKey y clasifica el código dominante → hint accionable (esquema/quota/red). Settings muestra el hint legible sobre el detalle técnico. lastSyncErrorHint publicado. Strings ×7. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
This commit is contained in:
@@ -389,8 +389,42 @@ struct ChartsContainerView: View {
|
||||
ChartStat(label: "Bull case", value: lastPred.confidenceInterval.upper.compactCurrencyString, color: .positiveGreen),
|
||||
ChartStat(label: "Bear case", value: lastPred.confidenceInterval.lower.compactCurrencyString, color: .negativeRed),
|
||||
]
|
||||
case .yearOverYear, .comparison, .simulator, .periodComparison:
|
||||
// These charts carry their own comparison summaries inside the card.
|
||||
case .comparison:
|
||||
let series = viewModel.comparisonData
|
||||
guard !series.isEmpty else { return [] }
|
||||
// Rank sources by their latest value in the current display mode.
|
||||
let ranked = series.compactMap { s -> (name: String, val: Double)? in
|
||||
guard let last = s.points.last?.value else { return nil }
|
||||
return (s.name, last)
|
||||
}.sorted { $0.val > $1.val }
|
||||
let unit = viewModel.comparisonDisplayMode == .absolute ? "" : "%"
|
||||
func fmt(_ v: Double) -> String { unit.isEmpty ? Decimal(v).compactCurrencyString : String(format: "%+.1f%%", v) }
|
||||
var stats: [ChartStat] = [ChartStat(label: "Series", value: "\(series.count)", color: .secondary)]
|
||||
if let best = ranked.first {
|
||||
stats.append(ChartStat(label: "Leader", value: "\(best.name) \(fmt(best.val))", color: .positiveGreen))
|
||||
}
|
||||
if ranked.count > 1, let worst = ranked.last {
|
||||
stats.append(ChartStat(label: "Laggard", value: "\(worst.name) \(fmt(worst.val))", color: .negativeRed))
|
||||
}
|
||||
return stats
|
||||
case .periodComparison:
|
||||
let series = viewModel.periodComparisonData
|
||||
guard !series.isEmpty else { return [] }
|
||||
return series.map { s in
|
||||
let end = s.points.last?.returnPct ?? 0
|
||||
return ChartStat(label: s.label, value: String(format: "%+.1f%%", end),
|
||||
color: end >= 0 ? .positiveGreen : .negativeRed)
|
||||
}
|
||||
case .yearOverYear:
|
||||
let years = viewModel.yearOverYearData.filter { viewModel.yoySelectedYears.contains($0.year) }
|
||||
guard !years.isEmpty else { return [] }
|
||||
return years.map { ys in
|
||||
let end = ys.values.compactMap { $0.isNaN ? nil : $0 }.last ?? 0
|
||||
return ChartStat(label: "\(ys.year)", value: String(format: "%+.1f%%", end),
|
||||
color: end >= 0 ? .positiveGreen : .negativeRed)
|
||||
}
|
||||
case .simulator:
|
||||
// The simulator carries its own live sliders and comparison inline.
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user