Feedback iPad: allocation grande + %, Home 2 columnas, YoY KPIs, achievements fix, iCloud calmado (build 58)

Allocation:
- Semicírculo responsive que llena su mitad de la fila (grande en iPad, no
  minúsculo en una esquina) + porcentaje dentro de cada segmento >=7%.
  SemicircleArc acepta center explícito; labels en el ángulo medio del arco.

Home iPad:
- Ya no columna única estrecha a 720. Hero + check-in a ancho completo, resto
  de cards en DOS columnas masonry independientes (sin acoplar alturas → sin
  gaps del grid). Cap 1100pt.

Year vs Year KPIs:
- Retorno del año actual + diferencia (pp) vs año anterior + mejor/peor año
  histórico. Usa forecastEndValue para el año en curso.

Fixes:
- 'View all achievements' en Journal no hacía nada: NavigationLink inline
  (frágil según contexto de navegación) → sheet fiable en cualquier sitio.
- iCloud: CKErrorPartialFailure tras una sync que funciona ya NO se pinta en
  rojo alarmante — nota calmada 'Sincronizando con iCloud' + detalle técnico
  en disclosure. Rojo solo si nunca ha sincronizado y es fallo total.
  syncErrorIsCritical + strings ×7.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
This commit is contained in:
alexandrev-tibco
2026-07-10 14:48:06 +02:00
parent 18f2609d87
commit 1abffdb7bb
13 changed files with 221 additions and 48 deletions
@@ -425,22 +425,42 @@ struct SettingsView: View {
}
}
// CloudKit error (if any)
// CloudKit status: only alarm (red) when sync is genuinely
// broken. A partial failure while data is syncing is shown as
// a calm "still syncing" note, not a scary error.
if let error = cloudStack.lastSyncError {
let critical = cloudStack.syncErrorIsCritical
VStack(alignment: .leading, spacing: 4) {
Label("CloudKit error", systemImage: "exclamationmark.icloud")
.font(.caption.weight(.semibold))
.foregroundColor(.red)
if let hint = cloudStack.lastSyncErrorHint {
Label(
critical
? String(localized: "icloud_status_error")
: String(localized: "icloud_status_syncing"),
systemImage: critical ? "exclamationmark.icloud" : "arrow.triangle.2.circlepath.icloud"
)
.font(.caption.weight(.semibold))
.foregroundColor(critical ? .red : .secondary)
if critical, let hint = cloudStack.lastSyncErrorHint {
Text(hint)
.font(.caption)
.foregroundColor(.primary)
}
Text(error)
.font(.caption2)
.foregroundColor(.secondary)
.lineLimit(6)
.textSelection(.enabled)
// Technical detail is opt-in behind a disclosure, so it
// never dominates the screen for a benign partial sync.
DisclosureGroup(String(localized: "icloud_show_details")) {
if let hint = cloudStack.lastSyncErrorHint, !critical {
Text(hint)
.font(.caption2)
.foregroundColor(.secondary)
}
Text(error)
.font(.caption2)
.foregroundColor(.secondary)
.textSelection(.enabled)
}
.font(.caption2)
.tint(.secondary)
}
}