Fix observer analytics crash: guard Nivo edge cases
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m41s

- Disable enableSlices on ResponsiveLine with single data point (causes
  null reference in Nivo internal slice computation)
- Add null check for slice.points[0] in timeline tooltip
- Guard ResponsivePie from empty data array in diversity metrics
- Add fallback for scoreDistribution.distribution on both
  observer and admin reports pages

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt
2026-02-20 14:09:43 +01:00
parent 57a16d089d
commit 0607d79484
4 changed files with 12 additions and 9 deletions

View File

@@ -532,9 +532,9 @@ function StageAnalytics() {
<Skeleton className="h-[350px]" /> <Skeleton className="h-[350px]" />
) : scoreDistribution ? ( ) : scoreDistribution ? (
<ScoreDistributionChart <ScoreDistributionChart
data={scoreDistribution.distribution} data={scoreDistribution.distribution ?? []}
averageScore={scoreDistribution.averageScore} averageScore={scoreDistribution.averageScore ?? 0}
totalScores={scoreDistribution.totalScores} totalScores={scoreDistribution.totalScores ?? 0}
/> />
) : null} ) : null}

View File

@@ -399,9 +399,9 @@ function AnalyticsTab({ selectedValue }: { selectedValue: string }) {
<Skeleton className="h-[350px]" /> <Skeleton className="h-[350px]" />
) : scoreDistribution ? ( ) : scoreDistribution ? (
<ScoreDistributionChart <ScoreDistributionChart
data={scoreDistribution.distribution} data={scoreDistribution.distribution ?? []}
averageScore={scoreDistribution.averageScore} averageScore={scoreDistribution.averageScore ?? 0}
totalScores={scoreDistribution.totalScores} totalScores={scoreDistribution.totalScores ?? 0}
/> />
) : null} ) : null}

View File

@@ -115,7 +115,7 @@ export function DiversityMetricsChart({ data }: DiversityMetricsProps) {
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div style={{ height: '400px' }}> <div style={{ height: '400px' }}>
<ResponsivePie {nivoPieData.length > 0 ? <ResponsivePie
data={nivoPieData} data={nivoPieData}
theme={nivoTheme} theme={nivoTheme}
colors={[...BRAND_COLORS]} colors={[...BRAND_COLORS]}
@@ -149,7 +149,9 @@ export function DiversityMetricsChart({ data }: DiversityMetricsProps) {
symbolShape: 'circle', symbolShape: 'circle',
}, },
]} ]}
/> /> : (
<p className="text-muted-foreground text-center py-8">No geographic data</p>
)}
</div> </div>
</CardContent> </CardContent>
</Card> </Card>

View File

@@ -63,9 +63,10 @@ export function EvaluationTimelineChart({ data }: EvaluationTimelineProps) {
pointBorderWidth={2} pointBorderWidth={2}
pointBorderColor="#ffffff" pointBorderColor="#ffffff"
useMesh={true} useMesh={true}
enableSlices="x" enableSlices={formattedData.length >= 2 ? 'x' : false}
sliceTooltip={({ slice }) => { sliceTooltip={({ slice }) => {
const point = slice.points[0] const point = slice.points[0]
if (!point) return null
const dataItem = formattedData.find( const dataItem = formattedData.find(
(d) => d.dateFormatted === point.data.xFormatted (d) => d.dateFormatted === point.data.xFormatted
) )