fix: add null guards to Analytics component preventing white screen

API responses with missing nested fields (sessionStats, agentStats,
qualityStats, averageProcessingTime, averageApiCalls) caused .toFixed()
and .map() to crash on undefined. Added ?? null coalescing throughout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
admin
2026-02-25 11:08:29 -05:00
parent 38a0f0619d
commit 00c156b4fd

View File

@@ -92,8 +92,8 @@ const Analytics: React.FC = () => {
setProcessingStats(stats);
setHealthStatus(health);
} catch (err) {
setError('Failed to load analytics data');
console.error('Analytics loading error:', err);
setError('Failed to load analytics data. Some endpoints may not be available.');
} finally {
setLoading(false);
}
@@ -164,7 +164,7 @@ const Analytics: React.FC = () => {
</div>
{/* System Health Overview */}
{healthStatus && (
{healthStatus?.overall && (
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-lg font-semibold text-gray-900 mb-4">System Health</h2>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
@@ -177,25 +177,25 @@ const Analytics: React.FC = () => {
<span className="text-sm font-medium text-gray-900">Overall Status</span>
</div>
<p className="text-2xl font-bold text-gray-900 mt-2">
{healthStatus.status.charAt(0).toUpperCase() + healthStatus.status.slice(1)}
{healthStatus.status?.charAt(0).toUpperCase() + healthStatus.status?.slice(1)}
</p>
</div>
<div className="bg-gray-50 rounded-lg p-4">
<p className="text-sm font-medium text-gray-500">Success Rate</p>
<p className="text-2xl font-bold text-gray-900">
{(healthStatus.overall.successRate * 100).toFixed(1)}%
{((healthStatus.overall.successRate ?? 0) * 100).toFixed(1)}%
</p>
</div>
<div className="bg-gray-50 rounded-lg p-4">
<p className="text-sm font-medium text-gray-500">Avg Processing Time</p>
<p className="text-2xl font-bold text-gray-900">
{formatTime(healthStatus.overall.averageProcessingTime)}
{formatTime(healthStatus.overall.averageProcessingTime ?? 0)}
</p>
</div>
<div className="bg-gray-50 rounded-lg p-4">
<p className="text-sm font-medium text-gray-500">Active Sessions</p>
<p className="text-2xl font-bold text-gray-900">
{healthStatus.overall.activeSessions}
{healthStatus.overall.activeSessions ?? 0}
</p>
</div>
</div>
@@ -214,7 +214,7 @@ const Analytics: React.FC = () => {
<span className="text-sm text-gray-600">Chunking</span>
<span className="text-sm font-medium">
{processingStats.totalDocuments > 0
? ((processingStats.chunkingSuccess / processingStats.totalDocuments) * 100).toFixed(1)
? (((processingStats.chunkingSuccess ?? 0) / processingStats.totalDocuments) * 100).toFixed(1)
: 0}%
</span>
</div>
@@ -222,7 +222,7 @@ const Analytics: React.FC = () => {
<span className="text-sm text-gray-600">RAG</span>
<span className="text-sm font-medium">
{processingStats.totalDocuments > 0
? ((processingStats.ragSuccess / processingStats.totalDocuments) * 100).toFixed(1)
? (((processingStats.ragSuccess ?? 0) / processingStats.totalDocuments) * 100).toFixed(1)
: 0}%
</span>
</div>
@@ -230,7 +230,7 @@ const Analytics: React.FC = () => {
<span className="text-sm text-gray-600">Agentic RAG</span>
<span className="text-sm font-medium">
{processingStats.totalDocuments > 0
? ((processingStats.agenticRagSuccess / processingStats.totalDocuments) * 100).toFixed(1)
? (((processingStats.agenticRagSuccess ?? 0) / processingStats.totalDocuments) * 100).toFixed(1)
: 0}%
</span>
</div>
@@ -241,15 +241,15 @@ const Analytics: React.FC = () => {
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm text-gray-600">Chunking</span>
<span className="text-sm font-medium">{formatTime(processingStats.averageProcessingTime.chunking)}</span>
<span className="text-sm font-medium">{formatTime(processingStats.averageProcessingTime?.chunking ?? 0)}</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-gray-600">RAG</span>
<span className="text-sm font-medium">{formatTime(processingStats.averageProcessingTime.rag)}</span>
<span className="text-sm font-medium">{formatTime(processingStats.averageProcessingTime?.rag ?? 0)}</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-gray-600">Agentic RAG</span>
<span className="text-sm font-medium">{formatTime(processingStats.averageProcessingTime.agenticRag)}</span>
<span className="text-sm font-medium">{formatTime(processingStats.averageProcessingTime?.agenticRag ?? 0)}</span>
</div>
</div>
</div>
@@ -258,15 +258,15 @@ const Analytics: React.FC = () => {
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm text-gray-600">Chunking</span>
<span className="text-sm font-medium">{processingStats.averageApiCalls.chunking.toFixed(1)}</span>
<span className="text-sm font-medium">{(processingStats.averageApiCalls?.chunking ?? 0).toFixed(1)}</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-gray-600">RAG</span>
<span className="text-sm font-medium">{processingStats.averageApiCalls.rag.toFixed(1)}</span>
<span className="text-sm font-medium">{(processingStats.averageApiCalls?.rag ?? 0).toFixed(1)}</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-gray-600">Agentic RAG</span>
<span className="text-sm font-medium">{processingStats.averageApiCalls.agenticRag.toFixed(1)}</span>
<span className="text-sm font-medium">{(processingStats.averageApiCalls?.agenticRag ?? 0).toFixed(1)}</span>
</div>
</div>
</div>
@@ -291,7 +291,7 @@ const Analytics: React.FC = () => {
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{analyticsData.sessionStats.map((stat, index) => (
{(analyticsData.sessionStats ?? []).map((stat, index) => (
<tr key={index}>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{new Date(stat.date).toLocaleDateString()}
@@ -318,7 +318,7 @@ const Analytics: React.FC = () => {
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-lg font-semibold text-gray-900 mb-4">Agent Performance</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{analyticsData.agentStats.map((agent, index) => (
{(analyticsData.agentStats ?? []).map((agent, index) => (
<div key={index} className="bg-gray-50 rounded-lg p-4">
<h3 className="text-sm font-medium text-gray-900 mb-2">
{agent.agent_name.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}
@@ -342,7 +342,7 @@ const Analytics: React.FC = () => {
</div>
<div className="flex justify-between text-xs">
<span className="text-gray-600">Avg Retries:</span>
<span className="font-medium">{parseFloat(agent.avg_retries).toFixed(1)}</span>
<span className="font-medium">{(parseFloat(agent.avg_retries) || 0).toFixed(1)}</span>
</div>
</div>
</div>
@@ -356,7 +356,7 @@ const Analytics: React.FC = () => {
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-lg font-semibold text-gray-900 mb-4">Quality Metrics</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
{analyticsData.qualityStats.map((metric, index) => (
{(analyticsData.qualityStats ?? []).map((metric, index) => (
<div key={index} className="bg-gray-50 rounded-lg p-4">
<h3 className="text-sm font-medium text-gray-900 mb-2">
{metric.metric_type.charAt(0).toUpperCase() + metric.metric_type.slice(1)}