Dashboard: quitar títulos duplicados en cards de gráficas (build 81) #33

showsTitle (default true) en HorizontalPerformanceChart, DrawdownChart,
VolatilityChartView, PredictionChartView y YearOverYearChartView; el
pageContent del Dashboard pasa false porque el header de la card ya nombra
la página. La sección Charts no cambia.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L38583J7AYWCVPevkivscj
This commit is contained in:
alexandrev-tibco
2026-08-06 22:55:27 +02:00
parent cf984dbf09
commit 6e6f4c5489
6 changed files with 50 additions and 27 deletions
+10 -10
View File
@@ -541,7 +541,7 @@
CODE_SIGN_ENTITLEMENTS = PortfolioJournal/PortfolioJournalDebug.entitlements; CODE_SIGN_ENTITLEMENTS = PortfolioJournal/PortfolioJournalDebug.entitlements;
ENABLE_USER_SCRIPT_SANDBOXING = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 80; CURRENT_PROJECT_VERSION = 81;
DEVELOPMENT_ASSET_PATHS = PortfolioJournal/Assets.xcassets; DEVELOPMENT_ASSET_PATHS = PortfolioJournal/Assets.xcassets;
DEVELOPMENT_TEAM = 2825Q76T7H; DEVELOPMENT_TEAM = 2825Q76T7H;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@@ -582,7 +582,7 @@
CODE_SIGN_IDENTITY = "Apple Distribution"; CODE_SIGN_IDENTITY = "Apple Distribution";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
PROVISIONING_PROFILE_SPECIFIER = "porfoliojournal"; PROVISIONING_PROFILE_SPECIFIER = "porfoliojournal";
CURRENT_PROJECT_VERSION = 80; CURRENT_PROJECT_VERSION = 81;
DEVELOPMENT_ASSET_PATHS = PortfolioJournal/Assets.xcassets; DEVELOPMENT_ASSET_PATHS = PortfolioJournal/Assets.xcassets;
DEVELOPMENT_TEAM = 2825Q76T7H; DEVELOPMENT_TEAM = 2825Q76T7H;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@@ -739,7 +739,7 @@
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
CODE_SIGN_ENTITLEMENTS = PortfolioJournalWidgetExtension.entitlements; CODE_SIGN_ENTITLEMENTS = PortfolioJournalWidgetExtension.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 80; CURRENT_PROJECT_VERSION = 81;
DEVELOPMENT_ASSET_PATHS = PortfolioJournalWidget/Assets.xcassets; DEVELOPMENT_ASSET_PATHS = PortfolioJournalWidget/Assets.xcassets;
DEVELOPMENT_TEAM = 2825Q76T7H; DEVELOPMENT_TEAM = 2825Q76T7H;
GENERATE_INFOPLIST_FILE = NO; GENERATE_INFOPLIST_FILE = NO;
@@ -774,7 +774,7 @@
CODE_SIGN_IDENTITY = "Apple Distribution"; CODE_SIGN_IDENTITY = "Apple Distribution";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
PROVISIONING_PROFILE_SPECIFIER = "Portfolio Journalwidget"; PROVISIONING_PROFILE_SPECIFIER = "Portfolio Journalwidget";
CURRENT_PROJECT_VERSION = 80; CURRENT_PROJECT_VERSION = 81;
DEVELOPMENT_ASSET_PATHS = PortfolioJournalWidget/Assets.xcassets; DEVELOPMENT_ASSET_PATHS = PortfolioJournalWidget/Assets.xcassets;
DEVELOPMENT_TEAM = 2825Q76T7H; DEVELOPMENT_TEAM = 2825Q76T7H;
GENERATE_INFOPLIST_FILE = NO; GENERATE_INFOPLIST_FILE = NO;
@@ -805,7 +805,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 80; CURRENT_PROJECT_VERSION = 81;
DEVELOPMENT_TEAM = 2825Q76T7H; DEVELOPMENT_TEAM = 2825Q76T7H;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.6; IPHONEOS_DEPLOYMENT_TARGET = 17.6;
@@ -829,7 +829,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 80; CURRENT_PROJECT_VERSION = 81;
DEVELOPMENT_TEAM = 2825Q76T7H; DEVELOPMENT_TEAM = 2825Q76T7H;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.6; IPHONEOS_DEPLOYMENT_TARGET = 17.6;
@@ -852,7 +852,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 80; CURRENT_PROJECT_VERSION = 81;
DEVELOPMENT_TEAM = 2825Q76T7H; DEVELOPMENT_TEAM = 2825Q76T7H;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.6; IPHONEOS_DEPLOYMENT_TARGET = 17.6;
@@ -875,7 +875,7 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 80; CURRENT_PROJECT_VERSION = 81;
DEVELOPMENT_TEAM = 2825Q76T7H; DEVELOPMENT_TEAM = 2825Q76T7H;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.6; IPHONEOS_DEPLOYMENT_TARGET = 17.6;
@@ -899,7 +899,7 @@
buildSettings = { buildSettings = {
CODE_SIGN_ENTITLEMENTS = PortfolioJournalQuickUpdateExtension.entitlements; CODE_SIGN_ENTITLEMENTS = PortfolioJournalQuickUpdateExtension.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 80; CURRENT_PROJECT_VERSION = 81;
DEVELOPMENT_TEAM = 2825Q76T7H; DEVELOPMENT_TEAM = 2825Q76T7H;
GENERATE_INFOPLIST_FILE = NO; GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = PortfolioJournalQuickUpdate/Info.plist; INFOPLIST_FILE = PortfolioJournalQuickUpdate/Info.plist;
@@ -927,7 +927,7 @@
CODE_SIGN_ENTITLEMENTS = PortfolioJournalQuickUpdateExtension.entitlements; CODE_SIGN_ENTITLEMENTS = PortfolioJournalQuickUpdateExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Distribution"; CODE_SIGN_IDENTITY = "Apple Distribution";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 80; CURRENT_PROJECT_VERSION = 81;
DEVELOPMENT_TEAM = 2825Q76T7H; DEVELOPMENT_TEAM = 2825Q76T7H;
PROVISIONING_PROFILE_SPECIFIER = "PortfolioJournal QuickUpdate AppStore"; PROVISIONING_PROFILE_SPECIFIER = "PortfolioJournal QuickUpdate AppStore";
GENERATE_INFOPLIST_FILE = NO; GENERATE_INFOPLIST_FILE = NO;
@@ -3,6 +3,8 @@ import Charts
struct DrawdownChart: View { struct DrawdownChart: View {
let data: [(date: Date, drawdown: Double)] let data: [(date: Date, drawdown: Double)]
/// Hidden when the host (e.g. a dashboard card) already shows a title.
var showsTitle: Bool = true
@State private var zoom = ChartZoomModel() @State private var zoom = ChartZoomModel()
@State private var selectedDate: Date? @State private var selectedDate: Date?
@Environment(\.horizontalSizeClass) private var labelsSizeClass @Environment(\.horizontalSizeClass) private var labelsSizeClass
@@ -26,8 +28,10 @@ struct DrawdownChart: View {
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 16) { VStack(alignment: .leading, spacing: 16) {
HStack { HStack {
Text("Drawdown Analysis") if showsTitle {
.font(.headline) Text("Drawdown Analysis")
.font(.headline)
}
Spacer() Spacer()
@@ -207,6 +211,8 @@ struct DrawdownStatView: View {
struct VolatilityChartView: View { struct VolatilityChartView: View {
let data: [(date: Date, volatility: Double)] let data: [(date: Date, volatility: Double)]
/// Hidden when the host (e.g. a dashboard card) already shows a title.
var showsTitle: Bool = true
@State private var zoom = ChartZoomModel() @State private var zoom = ChartZoomModel()
@State private var selectedDate: Date? @State private var selectedDate: Date?
@Environment(\.horizontalSizeClass) private var labelsSizeClass @Environment(\.horizontalSizeClass) private var labelsSizeClass
@@ -230,11 +236,13 @@ struct VolatilityChartView: View {
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 16) { VStack(alignment: .leading, spacing: 16) {
HStack { if showsTitle {
Text("Volatility") HStack {
.font(.headline) Text("Volatility")
.font(.headline)
Spacer() Spacer()
}
} }
Text("Measures price variability over time") Text("Measures price variability over time")
@@ -185,6 +185,8 @@ struct PerformanceBarChart: View {
struct HorizontalPerformanceChart: View { struct HorizontalPerformanceChart: View {
let data: [(category: String, cagr: Double, color: String)] let data: [(category: String, cagr: Double, color: String)]
/// Hidden when the host (e.g. a dashboard card) already shows a title.
var showsTitle: Bool = true
var sortedData: [(category: String, cagr: Double, color: String)] { var sortedData: [(category: String, cagr: Double, color: String)] {
data.sorted { $0.cagr > $1.cagr } data.sorted { $0.cagr > $1.cagr }
@@ -196,8 +198,10 @@ struct HorizontalPerformanceChart: View {
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 16) { VStack(alignment: .leading, spacing: 16) {
Text("Performance by Category") if showsTitle {
.font(.headline) Text("Performance by Category")
.font(.headline)
}
ForEach(sortedData, id: \.category) { item in ForEach(sortedData, id: \.category) { item in
VStack(alignment: .leading, spacing: 4) { VStack(alignment: .leading, spacing: 4) {
@@ -4,6 +4,8 @@ import Charts
struct PredictionChartView: View { struct PredictionChartView: View {
let predictions: [Prediction] let predictions: [Prediction]
let historicalData: [(date: Date, value: Decimal)] let historicalData: [(date: Date, value: Decimal)]
/// Hidden when the host (e.g. a dashboard card) already shows a title.
var showsTitle: Bool = true
@State private var selectedDate: Date? @State private var selectedDate: Date?
@Environment(\.horizontalSizeClass) private var labelsSizeClass @Environment(\.horizontalSizeClass) private var labelsSizeClass
@@ -23,8 +25,10 @@ struct PredictionChartView: View {
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 16) { VStack(alignment: .leading, spacing: 16) {
HStack { HStack {
Text(predictions.isEmpty ? "Prediction" : "\(predictions.count)-Month Prediction") if showsTitle {
.font(.headline) Text(predictions.isEmpty ? "Prediction" : "\(predictions.count)-Month Prediction")
.font(.headline)
}
Spacer() Spacer()
@@ -3,6 +3,8 @@ import Charts
struct YearOverYearChartView: View { struct YearOverYearChartView: View {
@ObservedObject var viewModel: ChartsViewModel @ObservedObject var viewModel: ChartsViewModel
/// Hidden when the host (e.g. a dashboard card) already shows a title.
var showsTitle: Bool = true
@State private var selectedMonthIdx: Int? @State private var selectedMonthIdx: Int?
private let monthAbbreviations = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", private let monthAbbreviations = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
@@ -20,8 +22,10 @@ struct YearOverYearChartView: View {
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 16) { VStack(alignment: .leading, spacing: 16) {
Text(String(localized: "chart_yoy_title")) if showsTitle {
.font(.headline) Text(String(localized: "chart_yoy_title"))
.font(.headline)
}
if allYears.isEmpty { if allYears.isEmpty {
Text(String(localized: "chart_yoy_empty")) Text(String(localized: "chart_yoy_empty"))
@@ -177,19 +177,22 @@ struct EvolutionChartCard: View {
.frame(height: 150) .frame(height: 150)
.padding(.vertical, 8) .padding(.vertical, 8)
allocationLegend allocationLegend
// showsTitle: false the card header already names the page;
// the embedded charts' own titles duplicated it.
case .performance: case .performance:
HorizontalPerformanceChart(data: chartsVM.performanceData) HorizontalPerformanceChart(data: chartsVM.performanceData, showsTitle: false)
case .drawdown: case .drawdown:
DrawdownChart(data: chartsVM.drawdownData) DrawdownChart(data: chartsVM.drawdownData, showsTitle: false)
case .volatility: case .volatility:
VolatilityChartView(data: chartsVM.volatilityData) VolatilityChartView(data: chartsVM.volatilityData, showsTitle: false)
case .prediction: case .prediction:
PredictionChartView( PredictionChartView(
predictions: chartsVM.predictionData, predictions: chartsVM.predictionData,
historicalData: chartsVM.evolutionData historicalData: chartsVM.evolutionData,
showsTitle: false
) )
case .yearOverYear: case .yearOverYear:
YearOverYearChartView(viewModel: chartsVM) YearOverYearChartView(viewModel: chartsVM, showsTitle: false)
} }
} }
} }