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:
alexandrev-tibco
2026-02-11 23:37:11 +01:00
parent daaca95913
commit 4761e2e5c8
19 changed files with 1118 additions and 90 deletions
@@ -160,7 +160,18 @@ struct DashboardView: View {
yearChange: viewModel.portfolioSummary.formattedYearChange,
sinceInceptionChange: viewModel.portfolioSummary.formattedAllTimeReturn,
isYearPositive: viewModel.isYearChangePositive,
isSinceInceptionPositive: viewModel.portfolioSummary.allTimeReturn >= 0
isSinceInceptionPositive: viewModel.portfolioSummary.allTimeReturn >= 0,
onShareTap: {
ShareService.shared.sharePortfolioValue(
totalValue: viewModel.portfolioSummary.formattedTotalValue,
changeText: calmModeEnabled
? "\(viewModel.latestPortfolioChange.formattedAbsolute) (\(viewModel.latestPortfolioChange.formattedPercentage))"
: viewModel.portfolioSummary.formattedDayChange,
changeLabel: calmModeEnabled ? "since last check-in" : "today",
yearChange: viewModel.portfolioSummary.formattedYearChange,
sinceInceptionChange: viewModel.portfolioSummary.formattedAllTimeReturn
)
}
)
}
case .monthlyCheckIn:
@@ -210,11 +221,12 @@ struct DashboardView: View {
CategoryBreakdownCard(categories: viewModel.topCategories)
}
case .goals:
let homeGoals = goalsViewModel.goals.filter { !GoalsViewModel.isAchieved(progress: goalsViewModel.progress(for: $0)) }
if config.isCollapsed {
CompactCard(title: "Goals", subtitle: "\(goalsViewModel.goals.count) active")
CompactCard(title: "Goals", subtitle: "\(homeGoals.count) active")
} else {
GoalsSummaryCard(
goals: goalsViewModel.goals,
goals: homeGoals,
progressProvider: goalsViewModel.progress(for:),
currentValueProvider: goalsViewModel.totalValue(for:),
paceStatusProvider: goalsViewModel.paceStatus(for:),
@@ -266,12 +278,24 @@ struct TotalValueCard: View {
var sinceInceptionChange: String?
var isYearPositive: Bool = true
var isSinceInceptionPositive: Bool = true
var onShareTap: (() -> Void)?
var body: some View {
VStack(spacing: 8) {
Text("Total Portfolio Value")
.font(.subheadline)
.foregroundColor(.white.opacity(0.85))
ZStack(alignment: .trailing) {
Text("Total Portfolio Value")
.font(.headline.weight(.semibold))
.foregroundColor(.white.opacity(0.85))
if let onShareTap {
Button(action: onShareTap) {
Image(systemName: "square.and.arrow.up")
.font(.subheadline.weight(.semibold))
.foregroundColor(.white.opacity(0.9))
}
.padding(.trailing, 8)
}
}
Text(totalValue)
.font(.system(size: 42, weight: .bold, design: .rounded))
@@ -423,13 +447,6 @@ struct MonthlyCheckInCard: View {
Spacer()
NavigationLink {
AchievementsView(referenceDate: Date())
} label: {
Image(systemName: "trophy.fill")
}
.font(.subheadline.weight(.semibold))
if let reminderDate {
Button {
let title = String(
@@ -1053,6 +1070,12 @@ struct GoalsSummaryCard: View {
ForEach(goals.prefix(2)) { goal in
let currentValue = currentValueProvider(goal)
let paceStatus = paceStatusProvider(goal)
let isAchieved = GoalsViewModel.isAchieved(progress: progressProvider(goal))
let targetUrgency = GoalsViewModel.urgencyLevel(
targetDate: goal.targetDate,
isBehind: paceStatus?.isBehind ?? false,
isAchieved: isAchieved
)
VStack(alignment: .leading, spacing: 6) {
HStack {
Text(goal.name)
@@ -1083,6 +1106,12 @@ struct GoalsSummaryCard: View {
.foregroundColor(.secondary)
}
if let targetDate = goal.targetDate {
Text("Target: \(targetDate.mediumDateString)")
.font(.caption2.weight(.semibold))
.foregroundColor(targetUrgency == .critical ? .negativeRed : (targetUrgency == .warning ? .appWarning : .secondary))
}
if let etaText = etaProvider(goal) {
Text(etaText)
.font(.caption2)