1.5.0: aviso de huecos + resumen mensual compartible + import con vista previa

Feature 1 — Aviso de huecos: SnapshotGapDetector detecta huecos internos por
source (meses sin datos entre dos snapshots). Banner desechable en Dashboard +
DataGapsSheet para rellenar (abre AddSnapshotView con el mes que falta). Complementa
la interpolación de gráficas: la línea se ve suave pero el usuario sabe que faltan
datos reales.

Feature 2 — Resumen mensual compartible: MonthlySummaryShareView (tarjeta con marca:
valor, variación desde el check-in, racha, mood/rating) renderizada con ImageRenderer
y compartida vía ShareService. Botón en la fila "check-in done" del Dashboard.

Feature 3 — Import con vista previa: ImportPreview + previewImportAsync (dry-run
READ-ONLY, no muta nada) cuenta qué se creará/actualizará usando los mismos checks
de existencia que applyImport. ImportDataView muestra la confirmación antes de
importar. El path real de import (applyImport/importDataAsync) NO se toca.

Localización en los 7 idiomas.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L2p3gUZRNWW388rWFRjiU7
This commit is contained in:
alexandrev-tibco
2026-07-24 09:36:50 +02:00
parent 8d7cfc098b
commit 84d9240ee6
17 changed files with 1096 additions and 13 deletions
@@ -62,6 +62,56 @@ class ShareService {
}
}
struct MonthlySummaryShareData {
let monthLabel: String
let totalValue: String
let changeAmount: String
let changePercentage: String
let isPositive: Bool
let streak: Int
let mood: MonthlyCheckInMood?
let rating: Int?
}
@MainActor
func shareMonthlySummary(_ data: MonthlySummaryShareData) {
FirebaseService.shared.logShare(type: "monthly_summary")
let appName = Self.appDisplayName
var lines = [
"\(data.monthLabel) — Monthly Summary",
"Portfolio Value: \(data.totalValue)",
"\(data.changeAmount) (\(data.changePercentage)) since last check-in"
]
if data.streak >= 2 {
lines.append("Streak: \(data.streak) months")
}
if let mood = data.mood {
lines.append("Mood: \(mood.title)")
}
lines.append("")
lines.append("Tracked with \(appName)")
let fallbackText = lines.joined(separator: "\n")
shareCard(
cardTitle: "\(data.monthLabel) — Monthly Summary",
fallbackText: fallbackText
) {
MonthlySummaryShareView(
monthLabel: data.monthLabel,
totalValue: data.totalValue,
changeAmount: data.changeAmount,
changePercentage: data.changePercentage,
isPositive: data.isPositive,
streak: data.streak,
mood: data.mood,
rating: data.rating,
appName: appName,
qrCodeImage: GoalShareService.generateQRCode(for: GoalShareService.appStoreURL, size: 200)
)
}
}
@MainActor
func sharePortfolioValue(
totalValue: String,