Files
igny8/frontend/src/pages/Settings/DebugStatus.tsx
IGNY8 VPS (Salman) 1cbc347cdc be fe fixes
2025-11-26 10:43:51 +00:00

803 lines
31 KiB
TypeScript

import { useState, useEffect, useCallback } from "react";
import PageMeta from "../../components/common/PageMeta";
import ComponentCard from "../../components/common/ComponentCard";
import { API_BASE_URL, fetchAPI } from "../../services/api";
interface HealthCheck {
name: string;
description: string;
status: 'healthy' | 'warning' | 'error' | 'checking';
message?: string;
details?: string;
lastChecked?: string;
}
interface ModuleHealth {
module: string;
description: string;
checks: HealthCheck[];
}
const getStatusColor = (status: string) => {
switch (status) {
case 'healthy': return 'text-green-600 dark:text-green-400';
case 'warning': return 'text-yellow-600 dark:text-yellow-400';
case 'error': return 'text-red-600 dark:text-red-400';
case 'checking': return 'text-blue-600 dark:text-blue-400';
default: return 'text-gray-600 dark:text-gray-400';
}
};
const getStatusBadge = (status: string) => {
switch (status) {
case 'healthy': return 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400';
case 'warning': return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400';
case 'error': return 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400';
case 'checking': return 'bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400';
default: return 'bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-400';
}
};
const getStatusIcon = (status: string) => {
switch (status) {
case 'healthy': return '✓';
case 'warning': return '⚠';
case 'error': return '✗';
case 'checking': return '⟳';
default: return '?';
}
};
export default function DebugStatus() {
const [moduleHealths, setModuleHealths] = useState<ModuleHealth[]>([]);
const [loading, setLoading] = useState(false);
// Helper to get auth token
const getAuthToken = () => {
const token = localStorage.getItem('auth_token') ||
(() => {
try {
const authStorage = localStorage.getItem('auth-storage');
if (authStorage) {
const parsed = JSON.parse(authStorage);
return parsed?.state?.token || '';
}
} catch (e) {
// Ignore parsing errors
}
return '';
})();
return token;
};
// Helper to make authenticated API calls
const apiCall = async (path: string, method: string = 'GET', body?: any) => {
const token = getAuthToken();
const headers: HeadersInit = {
'Content-Type': 'application/json',
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const options: RequestInit = {
method,
headers,
credentials: 'include',
};
if (body && method !== 'GET') {
options.body = JSON.stringify(body);
}
const response = await fetch(`${API_BASE_URL}${path}`, options);
const data = await response.json();
return { response, data };
};
// Check database schema field mappings (the issues we just fixed)
const checkDatabaseSchemaMapping = useCallback(async (): Promise<HealthCheck> => {
try {
// Test Writer Content endpoint (was failing with entity_type error)
const { response: contentResp, data: contentData } = await apiCall('/v1/writer/content/');
if (!contentResp.ok) {
return {
name: 'Database Schema Mapping',
description: 'Checks if model field names map correctly to database columns',
status: 'error',
message: `Writer Content API failed with ${contentResp.status}`,
details: 'Model fields may not be mapped to database columns correctly (e.g., content_type vs entity_type)',
lastChecked: new Date().toISOString(),
};
}
// Check if response has the expected structure with new field names
if (contentData?.results && Array.isArray(contentData.results)) {
// If we can fetch content, schema mapping is working
return {
name: 'Database Schema Mapping',
description: 'Checks if model field names map correctly to database columns',
status: 'healthy',
message: 'All model fields correctly mapped via db_column attributes',
details: `Content API working correctly. Fields like content_type, content_html, content_structure are properly mapped.`,
lastChecked: new Date().toISOString(),
};
}
return {
name: 'Database Schema Mapping',
description: 'Checks if model field names map correctly to database columns',
status: 'warning',
message: 'Content API returned unexpected structure',
details: 'Response format may have changed',
lastChecked: new Date().toISOString(),
};
} catch (error: any) {
return {
name: 'Database Schema Mapping',
description: 'Checks if model field names map correctly to database columns',
status: 'error',
message: error.message || 'Failed to check schema mapping',
details: 'Check if db_column attributes are set correctly in models',
lastChecked: new Date().toISOString(),
};
}
}, []);
// Check Writer module health
const checkWriterModule = useCallback(async (): Promise<HealthCheck[]> => {
const checks: HealthCheck[] = [];
// Check Content endpoint
try {
const { response: contentResp, data: contentData } = await apiCall('/v1/writer/content/');
if (contentResp.ok && contentData?.success !== false) {
checks.push({
name: 'Content List',
description: 'Writer content listing endpoint',
status: 'healthy',
message: `Found ${contentData?.count || 0} content items`,
lastChecked: new Date().toISOString(),
});
} else {
checks.push({
name: 'Content List',
description: 'Writer content listing endpoint',
status: 'error',
message: contentData?.error || `Failed with ${contentResp.status}`,
details: 'Check model field mappings (content_type, content_html, content_structure)',
lastChecked: new Date().toISOString(),
});
}
} catch (error: any) {
checks.push({
name: 'Content List',
description: 'Writer content listing endpoint',
status: 'error',
message: error.message || 'Network error',
lastChecked: new Date().toISOString(),
});
}
// Check Tasks endpoint
try {
const { response: tasksResp, data: tasksData } = await apiCall('/v1/writer/tasks/');
if (tasksResp.ok && tasksData?.success !== false) {
checks.push({
name: 'Tasks List',
description: 'Writer tasks listing endpoint',
status: 'healthy',
message: `Found ${tasksData?.count || 0} tasks`,
lastChecked: new Date().toISOString(),
});
} else {
checks.push({
name: 'Tasks List',
description: 'Writer tasks listing endpoint',
status: 'error',
message: tasksData?.error || `Failed with ${tasksResp.status}`,
details: 'Check model field mappings (content_type, content_structure, taxonomy_term)',
lastChecked: new Date().toISOString(),
});
}
} catch (error: any) {
checks.push({
name: 'Tasks List',
description: 'Writer tasks listing endpoint',
status: 'error',
message: error.message || 'Network error',
lastChecked: new Date().toISOString(),
});
}
// Check Content Validation
try {
// Get first content ID if available
const { data: contentData } = await apiCall('/v1/writer/content/');
const firstContentId = contentData?.results?.[0]?.id;
if (firstContentId) {
const { response: validationResp, data: validationData } = await apiCall(
`/v1/writer/content/${firstContentId}/validation/`
);
if (validationResp.ok && validationData?.success !== false) {
checks.push({
name: 'Content Validation',
description: 'Content validation before publish',
status: 'healthy',
message: 'Validation endpoint working',
lastChecked: new Date().toISOString(),
});
} else {
checks.push({
name: 'Content Validation',
description: 'Content validation before publish',
status: 'error',
message: validationData?.error || `Failed with ${validationResp.status}`,
details: 'Check validation_service.py field references (content_html, content_type)',
lastChecked: new Date().toISOString(),
});
}
} else {
checks.push({
name: 'Content Validation',
description: 'Content validation before publish',
status: 'warning',
message: 'No content available to test validation',
lastChecked: new Date().toISOString(),
});
}
} catch (error: any) {
checks.push({
name: 'Content Validation',
description: 'Content validation before publish',
status: 'error',
message: error.message || 'Network error',
lastChecked: new Date().toISOString(),
});
}
return checks;
}, []);
// Check Planner module health
const checkPlannerModule = useCallback(async (): Promise<HealthCheck[]> => {
const checks: HealthCheck[] = [];
// Check Clusters endpoint
try {
const { response: clustersResp, data: clustersData } = await apiCall('/v1/planner/clusters/');
if (clustersResp.ok && clustersData?.success !== false) {
checks.push({
name: 'Clusters List',
description: 'Planner clusters listing endpoint',
status: 'healthy',
message: `Found ${clustersData?.count || 0} clusters`,
lastChecked: new Date().toISOString(),
});
} else {
checks.push({
name: 'Clusters List',
description: 'Planner clusters listing endpoint',
status: 'error',
message: clustersData?.error || `Failed with ${clustersResp.status}`,
lastChecked: new Date().toISOString(),
});
}
} catch (error: any) {
checks.push({
name: 'Clusters List',
description: 'Planner clusters listing endpoint',
status: 'error',
message: error.message || 'Network error',
lastChecked: new Date().toISOString(),
});
}
// Check Keywords endpoint
try {
const { response: keywordsResp, data: keywordsData } = await apiCall('/v1/planner/keywords/');
if (keywordsResp.ok && keywordsData?.success !== false) {
checks.push({
name: 'Keywords List',
description: 'Planner keywords listing endpoint',
status: 'healthy',
message: `Found ${keywordsData?.count || 0} keywords`,
lastChecked: new Date().toISOString(),
});
} else {
checks.push({
name: 'Keywords List',
description: 'Planner keywords listing endpoint',
status: 'error',
message: keywordsData?.error || `Failed with ${keywordsResp.status}`,
lastChecked: new Date().toISOString(),
});
}
} catch (error: any) {
checks.push({
name: 'Keywords List',
description: 'Planner keywords listing endpoint',
status: 'error',
message: error.message || 'Network error',
lastChecked: new Date().toISOString(),
});
}
// Check Ideas endpoint
try {
const { response: ideasResp, data: ideasData } = await apiCall('/v1/planner/ideas/');
if (ideasResp.ok && ideasData?.success !== false) {
checks.push({
name: 'Ideas List',
description: 'Planner ideas listing endpoint',
status: 'healthy',
message: `Found ${ideasData?.count || 0} ideas`,
lastChecked: new Date().toISOString(),
});
} else {
checks.push({
name: 'Ideas List',
description: 'Planner ideas listing endpoint',
status: 'error',
message: ideasData?.error || `Failed with ${ideasResp.status}`,
lastChecked: new Date().toISOString(),
});
}
} catch (error: any) {
checks.push({
name: 'Ideas List',
description: 'Planner ideas listing endpoint',
status: 'error',
message: error.message || 'Network error',
lastChecked: new Date().toISOString(),
});
}
return checks;
}, []);
// Check Sites module health
const checkSitesModule = useCallback(async (): Promise<HealthCheck[]> => {
const checks: HealthCheck[] = [];
// Check Sites list
try {
const { response: sitesResp, data: sitesData } = await apiCall('/v1/auth/sites/');
if (sitesResp.ok && sitesData?.success !== false) {
checks.push({
name: 'Sites List',
description: 'Sites listing endpoint',
status: 'healthy',
message: `Found ${sitesData?.count || sitesData?.results?.length || 0} sites`,
lastChecked: new Date().toISOString(),
});
} else {
checks.push({
name: 'Sites List',
description: 'Sites listing endpoint',
status: 'error',
message: sitesData?.error || `Failed with ${sitesResp.status}`,
lastChecked: new Date().toISOString(),
});
}
} catch (error: any) {
checks.push({
name: 'Sites List',
description: 'Sites listing endpoint',
status: 'error',
message: error.message || 'Network error',
lastChecked: new Date().toISOString(),
});
}
return checks;
}, []);
// Check Integration module health
const checkIntegrationModule = useCallback(async (): Promise<HealthCheck[]> => {
const checks: HealthCheck[] = [];
// Check Integration content types endpoint
try {
// Get first site ID if available
const { data: sitesData } = await apiCall('/v1/auth/sites/');
const firstSiteId = sitesData?.results?.[0]?.id || sitesData?.[0]?.id;
if (firstSiteId) {
const { response: contentTypesResp, data: contentTypesData } = await apiCall(
`/v1/integration/integrations/${firstSiteId}/content-types/`
);
if (contentTypesResp.ok && contentTypesData?.success !== false) {
checks.push({
name: 'Content Types Sync',
description: 'Integration content types endpoint',
status: 'healthy',
message: 'Content types endpoint working',
lastChecked: new Date().toISOString(),
});
} else {
checks.push({
name: 'Content Types Sync',
description: 'Integration content types endpoint',
status: 'error',
message: contentTypesData?.error || `Failed with ${contentTypesResp.status}`,
details: 'Check integration views field mappings (content_type_map vs entity_type_map)',
lastChecked: new Date().toISOString(),
});
}
} else {
checks.push({
name: 'Content Types Sync',
description: 'Integration content types endpoint',
status: 'warning',
message: 'No sites available to test integration',
lastChecked: new Date().toISOString(),
});
}
} catch (error: any) {
checks.push({
name: 'Content Types Sync',
description: 'Integration content types endpoint',
status: 'error',
message: error.message || 'Network error',
lastChecked: new Date().toISOString(),
});
}
return checks;
}, []);
// Check Content Manager module health (taxonomy relations)
const checkContentManagerModule = useCallback(async (): Promise<HealthCheck[]> => {
const checks: HealthCheck[] = [];
// Check Taxonomy endpoint
try {
const { response: taxonomyResp, data: taxonomyData } = await apiCall('/v1/writer/taxonomy/');
if (taxonomyResp.ok && taxonomyData?.success !== false) {
checks.push({
name: 'Taxonomy System',
description: 'Content taxonomy endpoint',
status: 'healthy',
message: `Found ${taxonomyData?.count || 0} taxonomy items`,
lastChecked: new Date().toISOString(),
});
} else {
checks.push({
name: 'Taxonomy System',
description: 'Content taxonomy endpoint',
status: 'error',
message: taxonomyData?.error || `Failed with ${taxonomyResp.status}`,
details: 'Check ContentTaxonomyRelation through model and field mappings',
lastChecked: new Date().toISOString(),
});
}
} catch (error: any) {
checks.push({
name: 'Taxonomy System',
description: 'Content taxonomy endpoint',
status: 'error',
message: error.message || 'Network error',
lastChecked: new Date().toISOString(),
});
}
return checks;
}, []);
// Run all health checks
const runAllChecks = useCallback(async () => {
setLoading(true);
try {
// Run schema check first
const schemaCheck = await checkDatabaseSchemaMapping();
// Run module checks in parallel
const [writerChecks, plannerChecks, sitesChecks, integrationChecks, contentMgrChecks] = await Promise.all([
checkWriterModule(),
checkPlannerModule(),
checkSitesModule(),
checkIntegrationModule(),
checkContentManagerModule(),
]);
// Build module health results
const moduleHealthResults: ModuleHealth[] = [
{
module: 'Database Schema',
description: 'Critical database field mapping checks',
checks: [schemaCheck],
},
{
module: 'Writer Module',
description: 'Content creation and task management',
checks: writerChecks,
},
{
module: 'Planner Module',
description: 'Keyword clustering and content planning',
checks: plannerChecks,
},
{
module: 'Sites Module',
description: 'Site management and configuration',
checks: sitesChecks,
},
{
module: 'Integration Module',
description: 'External platform sync (WordPress, etc.)',
checks: integrationChecks,
},
{
module: 'Content Manager',
description: 'Taxonomy and content organization',
checks: contentMgrChecks,
},
];
setModuleHealths(moduleHealthResults);
} catch (error) {
console.error('Failed to run health checks:', error);
} finally {
setLoading(false);
}
}, [
checkDatabaseSchemaMapping,
checkWriterModule,
checkPlannerModule,
checkSitesModule,
checkIntegrationModule,
checkContentManagerModule,
]);
// Run checks on mount
useEffect(() => {
runAllChecks();
}, [runAllChecks]);
// Calculate module status
const getModuleStatus = (module: ModuleHealth): 'error' | 'warning' | 'healthy' => {
const statuses = module.checks.map(c => c.status);
if (statuses.some(s => s === 'error')) return 'error';
if (statuses.some(s => s === 'warning')) return 'warning';
return 'healthy';
};
// Calculate overall health
const getOverallHealth = () => {
const allStatuses = moduleHealths.flatMap(m => m.checks.map(c => c.status));
const total = allStatuses.length;
const healthy = allStatuses.filter(s => s === 'healthy').length;
const warning = allStatuses.filter(s => s === 'warning').length;
const error = allStatuses.filter(s => s === 'error').length;
let status: 'error' | 'warning' | 'healthy' = 'healthy';
if (error > 0) status = 'error';
else if (warning > 0) status = 'warning';
return { total, healthy, warning, error, status, percentage: total > 0 ? Math.round((healthy / total) * 100) : 0 };
};
const overallHealth = getOverallHealth();
return (
<>
<PageMeta title="Debug Status - IGNY8" description="Module health checks and diagnostics" />
<div className="space-y-6">
{/* Header */}
<div className="flex justify-between items-center">
<div>
<h1 className="text-2xl font-semibold text-gray-800 dark:text-white/90">Debug Status</h1>
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">
Comprehensive health checks for all modules and recent bug fixes
</p>
</div>
<button
onClick={runAllChecks}
disabled={loading}
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed text-sm"
>
{loading ? 'Running Checks...' : 'Refresh All'}
</button>
</div>
{/* Overall Health Summary */}
<ComponentCard
title={
<div className="flex items-center gap-2">
<span>Overall System Health</span>
<span className={`text-lg ${getStatusColor(overallHealth.status)}`}>
{getStatusIcon(overallHealth.status)}
</span>
</div>
}
desc={
overallHealth.status === 'error'
? `${overallHealth.error} critical issue${overallHealth.error !== 1 ? 's' : ''} detected`
: overallHealth.status === 'warning'
? `${overallHealth.warning} warning${overallHealth.warning !== 1 ? 's' : ''} detected`
: 'All systems operational'
}
>
<div className="space-y-4">
{/* Health Percentage */}
<div className="text-center">
<div className={`text-5xl font-bold ${getStatusColor(overallHealth.status)}`}>
{overallHealth.percentage}%
</div>
<div className="text-sm text-gray-600 dark:text-gray-400 mt-2">
{overallHealth.healthy} of {overallHealth.total} checks passed
</div>
</div>
{/* Health Breakdown */}
<div className="grid grid-cols-3 gap-4 pt-4 border-t border-gray-200 dark:border-gray-700">
<div className="text-center">
<div className={`text-2xl font-semibold ${getStatusColor('healthy')}`}>
{overallHealth.healthy}
</div>
<div className="text-xs text-gray-500 dark:text-gray-500 mt-1">Healthy</div>
</div>
<div className="text-center">
<div className={`text-2xl font-semibold ${getStatusColor('warning')}`}>
{overallHealth.warning}
</div>
<div className="text-xs text-gray-500 dark:text-gray-500 mt-1">Warnings</div>
</div>
<div className="text-center">
<div className={`text-2xl font-semibold ${getStatusColor('error')}`}>
{overallHealth.error}
</div>
<div className="text-xs text-gray-500 dark:text-gray-500 mt-1">Errors</div>
</div>
</div>
</div>
</ComponentCard>
{/* Module Health Cards */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{moduleHealths.map((moduleHealth, index) => {
const moduleStatus = getModuleStatus(moduleHealth);
const healthyCount = moduleHealth.checks.filter(c => c.status === 'healthy').length;
const totalCount = moduleHealth.checks.length;
return (
<ComponentCard
key={index}
title={
<div className="flex items-center gap-2">
<span>{moduleHealth.module}</span>
<span className={`text-xs px-2 py-0.5 rounded ${getStatusBadge(moduleStatus)}`}>
{getStatusIcon(moduleStatus)}
</span>
</div>
}
desc={
moduleStatus === 'error'
? `Issues detected - ${healthyCount}/${totalCount} checks passed`
: moduleStatus === 'warning'
? `Warnings detected - ${healthyCount}/${totalCount} checks passed`
: `All ${totalCount} checks passed`
}
>
<div className="space-y-3">
<p className="text-sm text-gray-600 dark:text-gray-400 mb-4">
{moduleHealth.description}
</p>
{/* Health Checks List */}
{moduleHealth.checks.map((check, checkIndex) => (
<div
key={checkIndex}
className={`p-3 rounded-lg border ${
check.status === 'healthy'
? 'border-green-200 dark:border-green-900/50 bg-green-50 dark:bg-green-900/20'
: check.status === 'warning'
? 'border-yellow-200 dark:border-yellow-900/50 bg-yellow-50 dark:bg-yellow-900/20'
: check.status === 'error'
? 'border-red-200 dark:border-red-900/50 bg-red-50 dark:bg-red-900/20'
: 'border-blue-200 dark:border-blue-900/50 bg-blue-50 dark:bg-blue-900/20'
}`}
>
<div className="flex items-start gap-2">
<span className={`text-lg ${getStatusColor(check.status)} flex-shrink-0`}>
{getStatusIcon(check.status)}
</span>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<span className="font-medium text-gray-800 dark:text-white/90">
{check.name}
</span>
<span className={`text-xs px-2 py-0.5 rounded ${getStatusBadge(check.status)}`}>
{check.status}
</span>
</div>
<p className="text-xs text-gray-500 dark:text-gray-400 mb-1">
{check.description}
</p>
{check.message && (
<p className={`text-sm ${getStatusColor(check.status)} font-medium`}>
{check.message}
</p>
)}
{check.details && (
<p className="text-xs text-gray-600 dark:text-gray-400 mt-1 italic">
💡 {check.details}
</p>
)}
</div>
</div>
</div>
))}
</div>
</ComponentCard>
);
})}
</div>
{/* Help Section */}
<ComponentCard
title="Troubleshooting Guide"
desc="Common issues and solutions"
>
<div className="space-y-4">
<div className="p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg border border-blue-200 dark:border-blue-900/50">
<h3 className="font-medium text-gray-800 dark:text-white/90 mb-2">
Database Schema Mapping Errors
</h3>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-2">
If you see errors about missing fields like <code className="px-1 py-0.5 bg-gray-200 dark:bg-gray-700 rounded text-xs">entity_type</code>,
<code className="px-1 py-0.5 bg-gray-200 dark:bg-gray-700 rounded text-xs ml-1">cluster_role</code>, or
<code className="px-1 py-0.5 bg-gray-200 dark:bg-gray-700 rounded text-xs ml-1">html_content</code>:
</p>
<ul className="text-sm text-gray-600 dark:text-gray-400 list-disc list-inside space-y-1">
<li>Check that model fields have correct <code className="px-1 py-0.5 bg-gray-200 dark:bg-gray-700 rounded text-xs">db_column</code> attributes</li>
<li>Verify database columns exist with <code className="px-1 py-0.5 bg-gray-200 dark:bg-gray-700 rounded text-xs">SELECT column_name FROM information_schema.columns</code></li>
<li>Review <code className="px-1 py-0.5 bg-gray-200 dark:bg-gray-700 rounded text-xs">DATABASE_SCHEMA_FIELD_MAPPING_GUIDE.md</code> in repo root</li>
</ul>
</div>
<div className="p-4 bg-yellow-50 dark:bg-yellow-900/20 rounded-lg border border-yellow-200 dark:border-yellow-900/50">
<h3 className="font-medium text-gray-800 dark:text-white/90 mb-2">
Field Reference Errors in Code
</h3>
<p className="text-sm text-gray-600 dark:text-gray-400 mb-2">
If API endpoints return 500 errors with AttributeError or similar:
</p>
<ul className="text-sm text-gray-600 dark:text-gray-400 list-disc list-inside space-y-1">
<li>Search codebase for old field names: <code className="px-1 py-0.5 bg-gray-200 dark:bg-gray-700 rounded text-xs">entity_type</code>, <code className="px-1 py-0.5 bg-gray-200 dark:bg-gray-700 rounded text-xs">cluster_role</code>, <code className="px-1 py-0.5 bg-gray-200 dark:bg-gray-700 rounded text-xs">html_content</code></li>
<li>Replace with new names: <code className="px-1 py-0.5 bg-gray-200 dark:bg-gray-700 rounded text-xs">content_type</code>, <code className="px-1 py-0.5 bg-gray-200 dark:bg-gray-700 rounded text-xs">content_structure</code>, <code className="px-1 py-0.5 bg-gray-200 dark:bg-gray-700 rounded text-xs">content_html</code></li>
<li>Check views, services, and serializers in writer/planner/integration modules</li>
</ul>
</div>
<div className="p-4 bg-green-50 dark:bg-green-900/20 rounded-lg border border-green-200 dark:border-green-900/50">
<h3 className="font-medium text-gray-800 dark:text-white/90 mb-2">
All Checks Passing?
</h3>
<p className="text-sm text-gray-600 dark:text-gray-400">
Great! Your system is healthy. This page will help you quickly diagnose issues if they appear in the future.
Bookmark this page and check it first when troubleshooting module-specific problems.
</p>
</div>
</div>
</ComponentCard>
</div>
</>
);
}