diff --git a/booklore-ui/src/app/features/stats/component/user-stats/charts/completion-race-chart/completion-race-chart.component.html b/booklore-ui/src/app/features/stats/component/user-stats/charts/completion-race-chart/completion-race-chart.component.html index 1a87944aa..496e6d707 100644 --- a/booklore-ui/src/app/features/stats/component/user-stats/charts/completion-race-chart/completion-race-chart.component.html +++ b/booklore-ui/src/app/features/stats/component/user-stats/charts/completion-race-chart/completion-race-chart.component.html @@ -35,16 +35,26 @@ {{ totalBooks }} Books +
+ {{ totalSessions }} + Sessions +
{{ avgDaysToFinish }}d Avg Days
+
+ {{ medianDaysToFinish }}d + Median Days +
- {{ fastestCompletion }} + {{ fastestDays }} + {{ fastestTitle }} Fastest
- {{ slowestCompletion }} + {{ slowestDays }} + {{ slowestTitle }} Slowest
diff --git a/booklore-ui/src/app/features/stats/component/user-stats/charts/completion-race-chart/completion-race-chart.component.scss b/booklore-ui/src/app/features/stats/component/user-stats/charts/completion-race-chart/completion-race-chart.component.scss index 0a56e891e..41bd8e5de 100644 --- a/booklore-ui/src/app/features/stats/component/user-stats/charts/completion-race-chart/completion-race-chart.component.scss +++ b/booklore-ui/src/app/features/stats/component/user-stats/charts/completion-race-chart/completion-race-chart.component.scss @@ -97,12 +97,15 @@ color: #4caf50; } - .stat-value-sm { - font-size: 0.85rem; - font-weight: 500; - color: var(--text-color, #ffffff); + .stat-subtitle { + font-size: 0.75rem; + color: var(--text-secondary-color); text-align: center; line-height: 1.3; + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .stat-label { @@ -113,11 +116,11 @@ margin-top: 0.25rem; } - &.fastest .stat-value-sm { + &.fastest .stat-value { color: #4caf50; } - &.slowest .stat-value-sm { + &.slowest .stat-value { color: #ff9800; } } diff --git a/booklore-ui/src/app/features/stats/component/user-stats/charts/completion-race-chart/completion-race-chart.component.ts b/booklore-ui/src/app/features/stats/component/user-stats/charts/completion-race-chart/completion-race-chart.component.ts index 22c9dd646..21786eb82 100644 --- a/booklore-ui/src/app/features/stats/component/user-stats/charts/completion-race-chart/completion-race-chart.component.ts +++ b/booklore-ui/src/app/features/stats/component/user-stats/charts/completion-race-chart/completion-race-chart.component.ts @@ -38,9 +38,13 @@ export class CompletionRaceChartComponent implements OnInit, OnDestroy { public chartOptions: ChartConfiguration<'line'>['options']; public totalBooks = 0; - public fastestCompletion = ''; - public slowestCompletion = ''; public avgDaysToFinish = 0; + public medianDaysToFinish = 0; + public totalSessions = 0; + public fastestDays = ''; + public fastestTitle = ''; + public slowestDays = ''; + public slowestTitle = ''; private readonly userStatsService = inject(UserStatsService); private readonly destroy$ = new Subject(); @@ -189,16 +193,26 @@ export class CompletionRaceChartComponent implements OnInit, OnDestroy { this.totalBooks = races.length; if (races.length > 0) { - const days = races.map(r => r.totalDays); + const days = races.map(r => r.totalDays).sort((a, b) => a - b); const fastest = races.reduce((a, b) => a.totalDays <= b.totalDays ? a : b); const slowest = races.reduce((a, b) => a.totalDays >= b.totalDays ? a : b); - this.fastestCompletion = `${fastest.bookTitle.substring(0, 25)}${fastest.bookTitle.length > 25 ? '...' : ''} (${fastest.totalDays}d)`; - this.slowestCompletion = `${slowest.bookTitle.substring(0, 25)}${slowest.bookTitle.length > 25 ? '...' : ''} (${slowest.totalDays}d)`; this.avgDaysToFinish = Math.round(days.reduce((a, b) => a + b, 0) / days.length); + this.medianDaysToFinish = days.length % 2 === 0 + ? Math.round((days[days.length / 2 - 1] + days[days.length / 2]) / 2) + : days[Math.floor(days.length / 2)]; + this.totalSessions = races.reduce((sum, r) => sum + r.sessions.length, 0); + this.fastestDays = `${fastest.totalDays}d`; + this.fastestTitle = fastest.bookTitle.length > 25 ? fastest.bookTitle.substring(0, 25) + '...' : fastest.bookTitle; + this.slowestDays = `${slowest.totalDays}d`; + this.slowestTitle = slowest.bookTitle.length > 25 ? slowest.bookTitle.substring(0, 25) + '...' : slowest.bookTitle; } else { - this.fastestCompletion = '-'; - this.slowestCompletion = '-'; this.avgDaysToFinish = 0; + this.medianDaysToFinish = 0; + this.totalSessions = 0; + this.fastestDays = '-'; + this.fastestTitle = ''; + this.slowestDays = '-'; + this.slowestTitle = ''; } const datasets = races.map((race, i) => { diff --git a/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-clock-chart/reading-clock-chart.component.html b/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-clock-chart/reading-clock-chart.component.html index 83f61dfff..cdf8c69f6 100644 --- a/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-clock-chart/reading-clock-chart.component.html +++ b/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-clock-chart/reading-clock-chart.component.html @@ -25,7 +25,7 @@ Total Read
- {{ readerType }} + {{ readerType }} Reader Type
diff --git a/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-clock-chart/reading-clock-chart.component.scss b/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-clock-chart/reading-clock-chart.component.scss index 29864612f..c402b20a4 100644 --- a/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-clock-chart/reading-clock-chart.component.scss +++ b/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-clock-chart/reading-clock-chart.component.scss @@ -58,12 +58,6 @@ color: var(--text-color, #ffffff); } - .stat-value-sm { - font-size: 1rem; - font-weight: 500; - color: var(--text-color, #ffffff); - } - .stat-label { font-size: 0.75rem; color: var(--text-secondary-color); @@ -80,7 +74,7 @@ color: #42a5f5; } - &.type .stat-value-sm { + &.type .stat-value { color: #9c27b0; } } @@ -89,7 +83,8 @@ .chart-wrapper { position: relative; height: 350px; - width: 100%; + aspect-ratio: 1; + margin: 0 auto; } .no-data-message { diff --git a/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-survival-chart/reading-survival-chart.component.html b/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-survival-chart/reading-survival-chart.component.html index ed2628325..b69502add 100644 --- a/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-survival-chart/reading-survival-chart.component.html +++ b/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-survival-chart/reading-survival-chart.component.html @@ -25,11 +25,12 @@ Completion Rate
- {{ medianDropout }} + {{ medianDropout }} Median Dropout
- {{ dangerZone }} + {{ dangerZoneRange }} + {{ dangerZoneDrop }} Danger Zone
diff --git a/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-survival-chart/reading-survival-chart.component.scss b/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-survival-chart/reading-survival-chart.component.scss index 46b91230d..bfd8c09ee 100644 --- a/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-survival-chart/reading-survival-chart.component.scss +++ b/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-survival-chart/reading-survival-chart.component.scss @@ -58,10 +58,9 @@ color: #e91e63; } - .stat-value-sm { - font-size: 0.85rem; - font-weight: 500; - color: var(--text-color, #ffffff); + .stat-subtitle { + font-size: 0.75rem; + color: var(--text-secondary-color); text-align: center; } @@ -73,7 +72,11 @@ margin-top: 0.25rem; } - &.danger .stat-value-sm { + &.danger .stat-value { + color: #ff5722; + } + + &.danger .stat-subtitle { color: #ff5722; } } diff --git a/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-survival-chart/reading-survival-chart.component.ts b/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-survival-chart/reading-survival-chart.component.ts index a640a83c2..29afad393 100644 --- a/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-survival-chart/reading-survival-chart.component.ts +++ b/booklore-ui/src/app/features/stats/component/user-stats/charts/reading-survival-chart/reading-survival-chart.component.ts @@ -28,7 +28,8 @@ export class ReadingSurvivalChartComponent implements OnInit, OnDestroy { public totalStarted = 0; public completionRate = 0; public medianDropout = ''; - public dangerZone = ''; + public dangerZoneRange = ''; + public dangerZoneDrop = ''; public readonly chartOptions: ChartConfiguration<'line'>['options'] = { responsive: true, @@ -156,7 +157,8 @@ export class ReadingSurvivalChartComponent implements OnInit, OnDestroy { dangerIdx = i; } } - this.dangerZone = `${THRESHOLDS[dangerIdx - 1]}-${THRESHOLDS[dangerIdx]}% (-${maxDrop.toFixed(0)}%)`; + this.dangerZoneRange = `${THRESHOLDS[dangerIdx - 1]}-${THRESHOLDS[dangerIdx]}%`; + this.dangerZoneDrop = `-${maxDrop.toFixed(0)}%`; const labels = THRESHOLDS.map(t => `${t}%`); this.chartDataSubject.next({