Browse Source

Merge 33737c18a3 into f4bad6acaf

pull/5731/merge
Aditya Garud 1 week ago
committed by GitHub
parent
commit
8166a2f5f4
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 8
      apps/client/src/app/components/investment-chart/investment-chart.component.ts
  2. 89
      apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts
  3. 6
      apps/client/src/app/pages/portfolio/analysis/analysis-page.html
  4. 8
      libs/ui/src/lib/line-chart/line-chart.component.ts
  5. 160
      test-charts.html

8
apps/client/src/app/components/investment-chart/investment-chart.component.ts

@ -61,6 +61,8 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy {
@Input() isLoading = false;
@Input() locale = getLocale();
@Input() savingsRate = 0;
@Input() xMax: Date;
@Input() xMin: Date;
@ViewChild('chartCanvas') chartCanvas;
@ -241,7 +243,13 @@ export class GfInvestmentChartComponent implements OnChanges, OnDestroy {
grid: {
display: false
},
max: this.xMax?.getTime(),
min: this.xMin?.getTime(),
type: 'time',
bounds: 'data',
ticks: {
source: 'data'
},
time: {
tooltipFormat: getDateFormatString(this.locale),
unit: 'year'

89
apps/client/src/app/pages/portfolio/analysis/analysis-page.component.ts

@ -73,11 +73,15 @@ export class GfAnalysisPageComponent implements OnDestroy, OnInit {
public deviceType: string;
public dividendsByGroup: InvestmentItem[];
public dividendTimelineDataLabel = $localize`Dividend`;
public dividendTimelineXMax: Date;
public dividendTimelineXMin: Date;
public firstOrderDate: Date;
public hasImpersonationId: boolean;
public hasPermissionToReadAiPrompt: boolean;
public investments: InvestmentItem[];
public investmentTimelineDataLabel = $localize`Investment`;
public investmentTimelineXMax: Date;
public investmentTimelineXMin: Date;
public investmentsByGroup: InvestmentItem[];
public isLoadingAnalysisPrompt: boolean;
public isLoadingBenchmarkComparator: boolean;
@ -94,6 +98,10 @@ export class GfAnalysisPageComponent implements OnDestroy, OnInit {
public performanceDataItems: HistoricalDataItem[];
public performanceDataItemsInPercentage: HistoricalDataItem[];
public portfolioEvolutionDataLabel = $localize`Investment`;
public portfolioEvolutionXMax: Date;
public portfolioEvolutionXMin: Date;
public globalXMax: Date;
public globalXMin: Date;
public streaks: PortfolioInvestmentsResponse['streaks'];
public top3: PortfolioPosition[];
public unitCurrentStreak: string;
@ -241,6 +249,8 @@ export class GfAnalysisPageComponent implements OnDestroy, OnInit {
this.isLoadingDividendTimelineChart = false;
this.updateDateRanges();
this.changeDetectorRef.markForCheck();
});
@ -273,6 +283,8 @@ export class GfAnalysisPageComponent implements OnDestroy, OnInit {
this.isLoadingInvestmentTimelineChart = false;
this.updateDateRanges();
this.changeDetectorRef.markForCheck();
});
}
@ -327,6 +339,8 @@ export class GfAnalysisPageComponent implements OnDestroy, OnInit {
this.updateBenchmarkDataItems();
this.updateDateRanges();
this.changeDetectorRef.markForCheck();
});
@ -402,4 +416,79 @@ export class GfAnalysisPageComponent implements OnDestroy, OnInit {
}
}
}
private updateDateRanges() {
// Calculate min and max dates for chart scaling based on filtered data
// This ensures charts are scaled proportionally to the selected time period
const allDates: Date[] = [];
if (this.performanceDataItems && this.performanceDataItems.length > 0) {
allDates.push(
...this.performanceDataItems.map((item) => new Date(item.date))
);
}
if (this.investmentsByGroup && this.investmentsByGroup.length > 0) {
allDates.push(
...this.investmentsByGroup.map((item) => new Date(item.date))
);
}
if (this.dividendsByGroup && this.dividendsByGroup.length > 0) {
allDates.push(
...this.dividendsByGroup.map((item) => new Date(item.date))
);
}
if (allDates.length > 0) {
this.globalXMin = new Date(Math.min(...allDates.map((d) => d.getTime())));
this.globalXMax = new Date(Math.max(...allDates.map((d) => d.getTime())));
} else {
this.globalXMin = undefined;
this.globalXMax = undefined;
}
// Individual ranges for specific charts (fallback if needed)
if (this.performanceDataItems && this.performanceDataItems.length > 0) {
const dates = this.performanceDataItems.map(
(item) => new Date(item.date)
);
this.portfolioEvolutionXMin = new Date(
Math.min(...dates.map((d) => d.getTime()))
);
this.portfolioEvolutionXMax = new Date(
Math.max(...dates.map((d) => d.getTime()))
);
} else {
this.portfolioEvolutionXMin = undefined;
this.portfolioEvolutionXMax = undefined;
}
if (this.investmentsByGroup && this.investmentsByGroup.length > 0) {
const dates = this.investmentsByGroup.map((item) => new Date(item.date));
this.investmentTimelineXMin = new Date(
Math.min(...dates.map((d) => d.getTime()))
);
this.investmentTimelineXMax = new Date(
Math.max(...dates.map((d) => d.getTime()))
);
} else {
this.investmentTimelineXMin = undefined;
this.investmentTimelineXMax = undefined;
}
if (this.dividendsByGroup && this.dividendsByGroup.length > 0) {
const dates = this.dividendsByGroup.map((item) => new Date(item.date));
this.dividendTimelineXMin = new Date(
Math.min(...dates.map((d) => d.getTime()))
);
this.dividendTimelineXMax = new Date(
Math.max(...dates.map((d) => d.getTime()))
);
} else {
this.dividendTimelineXMin = undefined;
this.dividendTimelineXMax = undefined;
}
}
}

6
apps/client/src/app/pages/portfolio/analysis/analysis-page.html

@ -354,6 +354,8 @@
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
[isLoading]="isLoadingInvestmentChart"
[locale]="user?.settings?.locale"
[xMax]="globalXMax"
[xMin]="globalXMin"
/>
</div>
</div>
@ -411,6 +413,8 @@
[isLoading]="isLoadingInvestmentTimelineChart"
[locale]="user?.settings?.locale"
[savingsRate]="savingsRate"
[xMax]="globalXMax"
[xMin]="globalXMin"
/>
</div>
</div>
@ -445,6 +449,8 @@
[isInPercent]="hasImpersonationId || user.settings.isRestrictedView"
[isLoading]="isLoadingDividendTimelineChart"
[locale]="user?.settings?.locale"
[xMax]="globalXMax"
[xMin]="globalXMin"
/>
</div>
</div>

8
libs/ui/src/lib/line-chart/line-chart.component.ts

@ -66,6 +66,8 @@ export class GfLineChartComponent
@Input() yMaxLabel: string;
@Input() yMin: number;
@Input() yMinLabel: string;
@Input() xMax: Date;
@Input() xMin: Date;
@ViewChild('chartCanvas') chartCanvas;
@ -218,6 +220,12 @@ export class GfLineChartComponent
grid: {
display: false
},
max: this.xMax?.getTime(),
min: this.xMin?.getTime(),
bounds: 'data',
ticks: {
source: 'data'
},
time: {
tooltipFormat: getDateFormatString(this.locale),
unit: 'year'

160
test-charts.html

@ -0,0 +1,160 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>Chart UI Test - Ghostfolio #3998</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@3.0.0/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
.chart-container {
width: 100%;
max-width: 800px;
margin: 20px 0;
}
canvas {
max-height: 400px;
}
</style>
</head>
<body>
<h1>Mock Frontend: Chart Visual Comparability Test (#3998)</h1>
<p>
This is a simple test page to verify chart scaling with mocked data. No
server needed.
</p>
<!-- Portfolio Performance Chart (Line Chart) -->
<h2>Portfolio Performance Chart</h2>
<div class="chart-container">
<canvas id="portfolioChart"></canvas>
</div>
<!-- Investment Timeline Chart (Bar/Line Combo) -->
<h2>Investment Timeline Chart</h2>
<div class="chart-container">
<canvas id="investmentChart"></canvas>
</div>
<script>
// Mock data for Portfolio Performance (Line Chart)
const portfolioData = {
labels: [
'2023-01-01',
'2023-02-01',
'2023-03-01',
'2023-04-01',
'2023-05-01'
],
datasets: [
{
label: 'Portfolio Value',
data: [
{ x: new Date('2023-01-01').getTime(), y: 10000 },
{ x: new Date('2023-02-01').getTime(), y: 11000 },
{ x: new Date('2023-03-01').getTime(), y: 10500 },
{ x: new Date('2023-04-01').getTime(), y: 12000 },
{ x: new Date('2023-05-01').getTime(), y: 12500 }
],
borderColor: 'rgb(75, 192, 192)',
backgroundColor: 'rgba(75, 192, 192, 0.2)',
fill: true,
tension: 0.1
}
]
};
// Mock data for Investment Timeline (Combo Chart)
const investmentData = {
labels: ['2023-01-01', '2023-02-01', '2023-03-01'],
datasets: [
{
label: 'Investments',
type: 'bar',
data: [
{ x: new Date('2023-01-01').getTime(), y: 5000 },
{ x: new Date('2023-02-01').getTime(), y: 3000 },
{ x: new Date('2023-03-01').getTime(), y: 7000 }
],
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgba(255, 99, 132, 1)',
borderWidth: 1
},
{
label: 'Total Value',
type: 'line',
data: [
{ x: new Date('2023-01-01').getTime(), y: 15000 },
{ x: new Date('2023-02-01').getTime(), y: 16000 },
{ x: new Date('2023-03-01').getTime(), y: 18000 }
],
borderColor: 'rgb(54, 162, 235)',
backgroundColor: 'rgba(54, 162, 235, 0.2)',
fill: false,
tension: 0.1
}
]
};
// Config for Portfolio Chart (Line Chart)
const portfolioConfig = {
type: 'line',
data: portfolioData,
options: {
responsive: true,
scales: {
x: {
type: 'time',
time: {
unit: 'month'
},
min: new Date('2023-01-01').getTime(), // Simulate xMin
max: new Date('2023-05-01').getTime() // Simulate xMax
},
y: {
beginAtZero: true
}
}
}
};
// Config for Investment Chart (Combo Chart)
const investmentConfig = {
data: investmentData,
options: {
responsive: true,
scales: {
x: {
type: 'time',
time: {
unit: 'month'
},
min: new Date('2023-01-01').getTime(), // Simulate xMin
max: new Date('2023-03-01').getTime() // Simulate xMax
},
y: {
beginAtZero: true
}
}
}
};
// Render Charts
const portfolioCtx = document
.getElementById('portfolioChart')
.getContext('2d');
new Chart(portfolioCtx, portfolioConfig);
const investmentCtx = document
.getElementById('investmentChart')
.getContext('2d');
new Chart(investmentCtx, investmentConfig);
console.log('Charts loaded successfully!');
</script>
</body>
</html>
Loading…
Cancel
Save