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