1.1.0 feature work: Monthly Check-in, Charts, Goals, Share, Reviews
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ struct GoalsView: View {
|
||||
@StateObject private var viewModel = GoalsViewModel()
|
||||
@State private var showingAddGoal = false
|
||||
@State private var editingGoal: Goal?
|
||||
@State private var showAchievedGoals = false
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
@@ -12,11 +13,15 @@ struct GoalsView: View {
|
||||
AppBackground()
|
||||
|
||||
List {
|
||||
if viewModel.goals.isEmpty {
|
||||
emptyState
|
||||
if filteredGoals.isEmpty {
|
||||
if viewModel.goals.isEmpty {
|
||||
emptyState
|
||||
} else {
|
||||
hiddenAchievedState
|
||||
}
|
||||
} else {
|
||||
Section {
|
||||
ForEach(viewModel.goals) { goal in
|
||||
ForEach(filteredGoals) { goal in
|
||||
GoalRowView(
|
||||
goal: goal,
|
||||
progress: viewModel.progress(for: goal),
|
||||
@@ -48,6 +53,12 @@ struct GoalsView: View {
|
||||
}
|
||||
.navigationTitle("Goals")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button(showAchievedGoals ? "Hide Achieved" : "Show Achieved") {
|
||||
showAchievedGoals.toggle()
|
||||
}
|
||||
.font(.caption.weight(.semibold))
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button {
|
||||
showingAddGoal = true
|
||||
@@ -78,6 +89,11 @@ struct GoalsView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private var filteredGoals: [Goal] {
|
||||
guard !showAchievedGoals else { return viewModel.goals }
|
||||
return viewModel.goals.filter { !viewModel.isAchieved($0) }
|
||||
}
|
||||
|
||||
private var emptyState: some View {
|
||||
VStack(spacing: 16) {
|
||||
Image(systemName: "target")
|
||||
@@ -93,6 +109,22 @@ struct GoalsView: View {
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 32)
|
||||
}
|
||||
|
||||
private var hiddenAchievedState: some View {
|
||||
VStack(spacing: 12) {
|
||||
Image(systemName: "party.popper")
|
||||
.font(.system(size: 40))
|
||||
.foregroundColor(.appSecondary)
|
||||
Text("All visible goals are achieved")
|
||||
.font(.headline)
|
||||
Text("Tap \"Show Achieved\" to review completed goals.")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical, 32)
|
||||
}
|
||||
}
|
||||
|
||||
struct GoalRowView: View {
|
||||
@@ -105,14 +137,53 @@ struct GoalRowView: View {
|
||||
|
||||
@State private var showingShareOptions = false
|
||||
|
||||
private var isAchieved: Bool {
|
||||
GoalsViewModel.isAchieved(progress: progress)
|
||||
}
|
||||
|
||||
private var targetUrgency: GoalUrgencyLevel {
|
||||
GoalsViewModel.urgencyLevel(
|
||||
targetDate: goal.targetDate,
|
||||
isBehind: paceStatus?.isBehind ?? false,
|
||||
isAchieved: isAchieved
|
||||
)
|
||||
}
|
||||
|
||||
private var targetDateColor: Color {
|
||||
switch targetUrgency {
|
||||
case .normal:
|
||||
return .secondary
|
||||
case .warning:
|
||||
return .appWarning
|
||||
case .critical:
|
||||
return .negativeRed
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .topTrailing) {
|
||||
Button(action: onEdit) {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
Text(goal.name)
|
||||
.font(.headline)
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
GoalProgressBar(progress: progress, tint: .appSecondary, iconColor: .appSecondary)
|
||||
GoalProgressBar(
|
||||
progress: progress,
|
||||
tint: isAchieved ? .appSuccess : .appSecondary,
|
||||
iconColor: isAchieved ? .appSuccess : .appSecondary
|
||||
)
|
||||
|
||||
HStack {
|
||||
Text(totalValue.currencyString)
|
||||
@@ -126,16 +197,25 @@ struct GoalRowView: View {
|
||||
if let targetDate = goal.targetDate {
|
||||
Text("Target date: \(targetDate.mediumDateString)")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.foregroundColor(targetDateColor)
|
||||
}
|
||||
|
||||
if let paceStatus {
|
||||
Text(paceStatus.statusText)
|
||||
.font(.caption.weight(.semibold))
|
||||
.foregroundColor(paceStatus.isBehind ? .negativeRed : .positiveGreen)
|
||||
.foregroundColor(
|
||||
isAchieved ? .appSuccess : (paceStatus.isBehind ? .negativeRed : .positiveGreen)
|
||||
)
|
||||
}
|
||||
}
|
||||
.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)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user