Calm 2.0 Fase 3: Home iPad columna única, Goals con anillos, Journal timeline, Sources polish (build 53)
Home iPad/Mac (feedback del usuario: 'me gusta más el iPhone'): - Fuera el grid de 2 columnas (acoplaba alturas de fila y dejaba huecos entre cards de tamaños distintos) → columna única centrada a 720pt, patrón Things/Day One. Eliminado el drag-drop del grid, columnSpan y el overlay full-screen (~120 líneas); el orden/visibilidad sigue en Customize. Goals: - ProgressRing nuevo (anillo estilo Fitness con porcentaje centrado y color semántico: verde=logrado, ámbar=retrasado, acento=en camino) en GoalRowView y en la card de Goals del Home. GoalProgressBar queda solo en la share card. Journal: - Momentum & Streaks vive ahora en la cabecera del Journal (la historia del hábito es narrativa, no dashboard). - Filas timeline: círculo de mood como ancla visual (tint semántico por mood), mes + estrellas + preview de nota. Sources: - Badge 'Needs update' como chip ámbar visible; valor en tipografía rounded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WoScpmHdVj1aUf4rAp6hbe
This commit is contained in:
@@ -244,62 +244,61 @@ struct GoalRowView: View {
|
||||
}
|
||||
}
|
||||
|
||||
/// Semantic ring color: green when achieved or ahead, orange when behind,
|
||||
/// accent while simply on the way.
|
||||
private var ringTint: Color {
|
||||
if isAchieved { return .appSuccess }
|
||||
if paceStatus?.isBehind == true { return .appWarning }
|
||||
return .appPrimary
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .topTrailing) {
|
||||
Button(action: onEdit) {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
HStack {
|
||||
Text(goal.name)
|
||||
.font(.headline)
|
||||
.foregroundColor(isAchieved ? .appSecondary : .primary)
|
||||
if isAchieved {
|
||||
Text("Achieved")
|
||||
.font(.caption2.weight(.bold))
|
||||
.foregroundColor(.white)
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 4)
|
||||
.background(Color.appSecondary)
|
||||
.clipShape(Capsule())
|
||||
HStack(spacing: 16) {
|
||||
ProgressRing(progress: progress, tint: ringTint)
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
HStack(spacing: 8) {
|
||||
Text(goal.name)
|
||||
.font(.headline)
|
||||
if isAchieved {
|
||||
Text("Achieved")
|
||||
.font(.caption2.weight(.bold))
|
||||
.foregroundColor(.white)
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 3)
|
||||
.background(Color.appSuccess)
|
||||
.clipShape(Capsule())
|
||||
}
|
||||
}
|
||||
|
||||
HStack(spacing: 4) {
|
||||
Text(totalValue.currencyString)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
Text("of \(goal.targetDecimal.currencyString)")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
if let targetDate = goal.targetDate {
|
||||
Text("Target date: \(targetDate.mediumDateString)")
|
||||
.font(.caption)
|
||||
.foregroundColor(targetDateColor)
|
||||
}
|
||||
|
||||
if let paceStatus {
|
||||
Text(paceStatus.statusText)
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundColor(
|
||||
isAchieved ? .appSuccess : (paceStatus.isBehind ? .appWarning : .positiveGreen)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
GoalProgressBar(
|
||||
progress: progress,
|
||||
tint: isAchieved ? .appSuccess : .appSecondary,
|
||||
iconColor: isAchieved ? .appSuccess : .appSecondary
|
||||
)
|
||||
|
||||
HStack {
|
||||
Text(totalValue.currencyString)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
Spacer()
|
||||
Text("of \(goal.targetDecimal.currencyString)")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
if let targetDate = goal.targetDate {
|
||||
Text("Target date: \(targetDate.mediumDateString)")
|
||||
.font(.caption)
|
||||
.foregroundColor(targetDateColor)
|
||||
}
|
||||
|
||||
if let paceStatus {
|
||||
Text(paceStatus.statusText)
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundColor(
|
||||
isAchieved ? .appSuccess : (paceStatus.isBehind ? .negativeRed : .positiveGreen)
|
||||
)
|
||||
}
|
||||
Spacer(minLength: 0)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding(12)
|
||||
.background(isAchieved ? Color.appSuccess.opacity(0.10) : Color.clear)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.stroke(isAchieved ? Color.appSuccess.opacity(0.35) : Color.clear, lineWidth: 1)
|
||||
)
|
||||
.cornerRadius(12)
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user