6887d05b01
Goal ETA (#6): GoalProjection engine (CAGR mensual robusto de la evolución + aportaciones, proyecta mes a mes → fecha estimada, estado ahead/on-track/behind/ unreachable, sparkline). GoalsView muestra 'Projected: <mes>', chip de ritmo con color, fallback 'Add contributions to reach this'. GoalsViewModel.projection(for:) cacheado. Insights (#7): InsightsEngine rule-based on-device (sin red/AI). Reglas: allocation vs target, riesgo de concentración, mejor/peor categoría, diversificación, racha, YTD, ganancias, forecast, proximidad a milestone. Priorizados por tono, top 4. PortfolioInsight gana detail + tone. Surface en el Dashboard. Localización: 19 claves nuevas en los 7 idiomas. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L2p3gUZRNWW388rWFRjiU7
48 lines
1.1 KiB
Swift
48 lines
1.1 KiB
Swift
import SwiftUI
|
|
|
|
/// Tone / severity of an insight, used to pick an accent color and to
|
|
/// prioritize which insights are surfaced first.
|
|
enum InsightTone {
|
|
case positive
|
|
case neutral
|
|
case attention
|
|
|
|
/// Higher priority insights are shown first when capping the list.
|
|
var priority: Int {
|
|
switch self {
|
|
case .attention: return 2
|
|
case .positive: return 1
|
|
case .neutral: return 0
|
|
}
|
|
}
|
|
}
|
|
|
|
struct PortfolioInsight: Identifiable {
|
|
let id: String
|
|
let systemImage: String
|
|
let title: String
|
|
let value: String
|
|
let accentColor: Color
|
|
/// Optional supporting line shown under the value in the richer card.
|
|
let detail: String?
|
|
let tone: InsightTone
|
|
|
|
init(
|
|
id: String,
|
|
systemImage: String,
|
|
title: String,
|
|
value: String,
|
|
accentColor: Color,
|
|
detail: String? = nil,
|
|
tone: InsightTone = .neutral
|
|
) {
|
|
self.id = id
|
|
self.systemImage = systemImage
|
|
self.title = title
|
|
self.value = value
|
|
self.accentColor = accentColor
|
|
self.detail = detail
|
|
self.tone = tone
|
|
}
|
|
}
|