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:
@@ -3,6 +3,8 @@ import Charts
|
||||
|
||||
struct AllocationPieChart: View {
|
||||
let data: [(category: String, value: Decimal, color: String)]
|
||||
var title: String = "Asset Allocation"
|
||||
var showsTargetsComparison: Bool = true
|
||||
|
||||
@State private var selectedSlice: String?
|
||||
|
||||
@@ -12,7 +14,7 @@ struct AllocationPieChart: View {
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
Text("Asset Allocation")
|
||||
Text(title)
|
||||
.font(.headline)
|
||||
|
||||
if !data.isEmpty {
|
||||
@@ -98,7 +100,9 @@ struct AllocationPieChart: View {
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
AllocationTargetsComparisonChart(data: data)
|
||||
if showsTargetsComparison {
|
||||
AllocationTargetsComparisonChart(data: data)
|
||||
}
|
||||
} else {
|
||||
Text("No allocation data available")
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
@@ -23,7 +23,6 @@ struct ChartsContainerView: View {
|
||||
|
||||
// Time Range Selector
|
||||
if viewModel.selectedChartType != .allocation &&
|
||||
viewModel.selectedChartType != .performance &&
|
||||
viewModel.selectedChartType != .riskReturn {
|
||||
timeRangeSelector
|
||||
}
|
||||
@@ -34,6 +33,17 @@ struct ChartsContainerView: View {
|
||||
categoryFilter
|
||||
}
|
||||
|
||||
// Source Filter
|
||||
if viewModel.selectedChartType == .evolution {
|
||||
sourceFilter
|
||||
}
|
||||
|
||||
// Breakdown Selector
|
||||
if viewModel.selectedChartType == .allocation ||
|
||||
viewModel.selectedChartType == .performance {
|
||||
breakdownSelector
|
||||
}
|
||||
|
||||
// Chart Content
|
||||
chartContent
|
||||
}
|
||||
@@ -101,7 +111,7 @@ struct ChartsContainerView: View {
|
||||
|
||||
private var timeRangeSelector: some View {
|
||||
HStack(spacing: 8) {
|
||||
ForEach(ChartsViewModel.TimeRange.allCases) { range in
|
||||
ForEach(viewModel.availableTimeRanges(for: viewModel.selectedChartType)) { range in
|
||||
Button {
|
||||
viewModel.selectedTimeRange = range
|
||||
} label: {
|
||||
@@ -158,6 +168,7 @@ struct ChartsContainerView: View {
|
||||
ForEach(availableCategories) { category in
|
||||
Button {
|
||||
viewModel.selectedCategory = category
|
||||
viewModel.selectedSource = nil
|
||||
} label: {
|
||||
HStack(spacing: 4) {
|
||||
Circle()
|
||||
@@ -186,6 +197,91 @@ struct ChartsContainerView: View {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Source Filter
|
||||
|
||||
@ViewBuilder
|
||||
private var sourceFilter: some View {
|
||||
let availableSources = viewModel.availableSources(for: viewModel.selectedChartType)
|
||||
if !availableSources.isEmpty {
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack(spacing: 8) {
|
||||
if availableSources.count > 1 {
|
||||
Button {
|
||||
viewModel.selectedSource = nil
|
||||
} label: {
|
||||
Text("All Sources")
|
||||
.font(.caption.weight(.medium))
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 6)
|
||||
.background(
|
||||
viewModel.selectedSource == nil
|
||||
? Color.appPrimary
|
||||
: Color.gray.opacity(0.1)
|
||||
)
|
||||
.foregroundColor(
|
||||
viewModel.selectedSource == nil
|
||||
? .white
|
||||
: .primary
|
||||
)
|
||||
.cornerRadius(16)
|
||||
}
|
||||
}
|
||||
|
||||
ForEach(availableSources, id: \.id) { source in
|
||||
Button {
|
||||
viewModel.selectedSource = source
|
||||
viewModel.selectedCategory = nil
|
||||
} label: {
|
||||
Text(source.name)
|
||||
.font(.caption.weight(.medium))
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 6)
|
||||
.background(
|
||||
viewModel.selectedSource?.id == source.id
|
||||
? Color.appPrimary
|
||||
: Color.gray.opacity(0.1)
|
||||
)
|
||||
.foregroundColor(
|
||||
viewModel.selectedSource?.id == source.id
|
||||
? .white
|
||||
: .primary
|
||||
)
|
||||
.cornerRadius(16)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Breakdown Selector
|
||||
|
||||
private var breakdownSelector: some View {
|
||||
HStack(spacing: 8) {
|
||||
ForEach(ChartsViewModel.BreakdownMode.allCases) { mode in
|
||||
Button {
|
||||
viewModel.selectedBreakdown = mode
|
||||
} label: {
|
||||
Text(mode.rawValue)
|
||||
.font(.subheadline.weight(.medium))
|
||||
.padding(.horizontal, 14)
|
||||
.padding(.vertical, 8)
|
||||
.background(
|
||||
viewModel.selectedBreakdown == mode
|
||||
? Color.appPrimary
|
||||
: Color.gray.opacity(0.1)
|
||||
)
|
||||
.foregroundColor(
|
||||
viewModel.selectedBreakdown == mode
|
||||
? .white
|
||||
: .primary
|
||||
)
|
||||
.cornerRadius(18)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Chart Content
|
||||
|
||||
@ViewBuilder
|
||||
@@ -204,9 +300,16 @@ struct ChartsContainerView: View {
|
||||
goals: goalsViewModel.goals
|
||||
)
|
||||
case .allocation:
|
||||
AllocationPieChart(data: viewModel.allocationData)
|
||||
AllocationPieChart(
|
||||
data: viewModel.allocationData,
|
||||
title: viewModel.selectedBreakdown == .source ? "Allocation by Source" : "Asset Allocation",
|
||||
showsTargetsComparison: ChartsViewModel.supportsAllocationTargets(for: viewModel.selectedBreakdown)
|
||||
)
|
||||
case .performance:
|
||||
PerformanceBarChart(data: viewModel.performanceData)
|
||||
PerformanceBarChart(
|
||||
data: viewModel.performanceData,
|
||||
title: viewModel.selectedBreakdown == .source ? "Performance by Source" : "Performance by Category"
|
||||
)
|
||||
case .contributions:
|
||||
ContributionsChartView(data: viewModel.contributionsData)
|
||||
case .rollingReturn:
|
||||
|
||||
@@ -3,10 +3,11 @@ import Charts
|
||||
|
||||
struct PerformanceBarChart: View {
|
||||
let data: [(category: String, cagr: Double, color: String)]
|
||||
var title: String = "Performance by Category"
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
Text("Performance by Category")
|
||||
Text(title)
|
||||
.font(.headline)
|
||||
|
||||
Text("Compound Annual Growth Rate (CAGR)")
|
||||
|
||||
Reference in New Issue
Block a user