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([]); 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 => { 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 => { 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 => { 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 => { 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 => { 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 => { 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 ( <>
{/* Header */}

Debug Status

Comprehensive health checks for all modules and recent bug fixes

{/* Overall Health Summary */} Overall System Health {getStatusIcon(overallHealth.status)}
} 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' } >
{/* Health Percentage */}
{overallHealth.percentage}%
{overallHealth.healthy} of {overallHealth.total} checks passed
{/* Health Breakdown */}
{overallHealth.healthy}
Healthy
{overallHealth.warning}
Warnings
{overallHealth.error}
Errors
{/* Module Health Cards */}
{moduleHealths.map((moduleHealth, index) => { const moduleStatus = getModuleStatus(moduleHealth); const healthyCount = moduleHealth.checks.filter(c => c.status === 'healthy').length; const totalCount = moduleHealth.checks.length; return ( {moduleHealth.module} {getStatusIcon(moduleStatus)}
} desc={ moduleStatus === 'error' ? `Issues detected - ${healthyCount}/${totalCount} checks passed` : moduleStatus === 'warning' ? `Warnings detected - ${healthyCount}/${totalCount} checks passed` : `All ${totalCount} checks passed` } >

{moduleHealth.description}

{/* Health Checks List */} {moduleHealth.checks.map((check, checkIndex) => (
{getStatusIcon(check.status)}
{check.name} {check.status}

{check.description}

{check.message && (

{check.message}

)} {check.details && (

💡 {check.details}

)}
))}
); })} {/* Help Section */}

Database Schema Mapping Errors

If you see errors about missing fields like entity_type, cluster_role, or html_content:

  • Check that model fields have correct db_column attributes
  • Verify database columns exist with SELECT column_name FROM information_schema.columns
  • Review DATABASE_SCHEMA_FIELD_MAPPING_GUIDE.md in repo root

Field Reference Errors in Code

If API endpoints return 500 errors with AttributeError or similar:

  • Search codebase for old field names: entity_type, cluster_role, html_content
  • Replace with new names: content_type, content_structure, content_html
  • Check views, services, and serializers in writer/planner/integration modules

All Checks Passing?

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.

); }