fix: Replace unsafe .toFixed() calls with safeFixed() in analytics views

- AnalyticsView: avg_players, uptime_percentage
- WipeAnalyticsView: success_rate, population curve, durations, CSV export
- PlayerRetentionView: retention percentages, session duration, tooltip
- MapAnalyticsView: rotation effectiveness, performance metrics, table

All analytics views now use safe formatter utilities with optional chaining
to prevent null/undefined runtime errors when displaying numeric data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell
2026-02-15 21:56:04 -05:00
parent daa9c3035f
commit 26e717ac96
18 changed files with 111 additions and 53 deletions

View File

@@ -4,6 +4,7 @@ import { Users, TrendingUp, Clock, Download, BarChart3 } from 'lucide-vue-next'
import * as echarts from 'echarts'
import type { ECharts } from 'echarts'
import { useApi } from '@/composables/useApi'
import { safeFixed } from '@/utils/formatters'
const api = useApi()
@@ -88,7 +89,7 @@ const renderCharts = () => {
formatter: (params: any) => {
let tooltip = `<strong>${params[0].axisValue}</strong><br/>`
params.forEach((param: any) => {
tooltip += `${param.marker} ${param.seriesName}: ${param.value.toFixed(1)}%<br/>`
tooltip += `${param.marker} ${param.seriesName}: ${safeFixed(param.value, 1)}%<br/>`
})
return tooltip
}
@@ -236,7 +237,7 @@ onMounted(() => {
<p class="text-sm text-neutral-400">Avg Session</p>
</div>
<p class="text-2xl font-bold text-neutral-100">
{{ retentionData.summary.avg_session_duration_minutes.toFixed(0) }}m
{{ safeFixed(retentionData?.summary?.avg_session_duration_minutes, 0) }}m
</p>
<p class="text-xs text-neutral-600 mt-1">Duration</p>
</div>
@@ -308,15 +309,15 @@ onMounted(() => {
</td>
<td class="py-3 px-3 text-right">
<span class="text-neutral-100 font-medium">{{ wipe.returned_24h }}</span>
<span class="text-neutral-500 text-xs ml-1">({{ wipe.retention_24h_percent.toFixed(1) }}%)</span>
<span class="text-neutral-500 text-xs ml-1">({{ safeFixed(wipe.retention_24h_percent, 1) }}%)</span>
</td>
<td class="py-3 px-3 text-right">
<span class="text-neutral-100 font-medium">{{ wipe.returned_48h }}</span>
<span class="text-neutral-500 text-xs ml-1">({{ wipe.retention_48h_percent.toFixed(1) }}%)</span>
<span class="text-neutral-500 text-xs ml-1">({{ safeFixed(wipe.retention_48h_percent, 1) }}%)</span>
</td>
<td class="py-3 px-3 text-right">
<span class="text-neutral-100 font-medium">{{ wipe.returned_72h }}</span>
<span class="text-neutral-500 text-xs ml-1">({{ wipe.retention_72h_percent.toFixed(1) }}%)</span>
<span class="text-neutral-500 text-xs ml-1">({{ safeFixed(wipe.retention_72h_percent, 1) }}%)</span>
</td>
</tr>
</tbody>