import React, { useState, useEffect, useCallback, useRef } from "react"; import { AlertTriangle } from "lucide-react"; import PageMeta from "../../components/common/PageMeta"; import ComponentCard from "../../components/common/ComponentCard"; import SiteAndSectorSelector from "../../components/common/SiteAndSectorSelector"; import WordPressIntegrationDebug from "./WordPressIntegrationDebug"; import { API_BASE_URL, fetchAPI } from "../../services/api"; import { useSiteStore } from "../../store/siteStore"; import { useToast } from "../../components/ui/toast/ToastContainer"; interface HealthCheck { name: string; description: string; status: 'healthy' | 'warning' | 'error' | 'checking'; message?: string; details?: string; lastChecked?: string; step?: string; payloadKeys?: string[]; responseCode?: number; } interface ModuleHealth { module: string; description: string; checks: HealthCheck[]; } interface SyncEvent { id: string; timestamp: string; direction: '📤 IGNY8 → WP' | '📥 WP → IGNY8'; trigger: string; contentId?: number; taskId?: number; status: 'success' | 'partial' | 'failed'; steps: SyncEventStep[]; payload?: any; response?: any; } interface SyncEventStep { step: string; file: string; status: 'success' | 'warning' | 'failed' | 'skipped'; details: string; error?: string; duration?: number; } interface DataSyncValidation { field: string; sentByIgny8: boolean; receivedByWP: boolean; storedInWP: boolean; verifiedInWPPost: boolean; error?: string; } interface IntegrationHealth { overall: 'healthy' | 'warning' | 'error'; lastSyncIgny8ToWP?: string; lastSyncWPToIgny8?: string; lastSiteMetadataCheck?: string; wpApiReachable: boolean; wpStatusEndpoint: boolean; wpMetadataEndpoint: boolean; apiKeyValid: boolean; jwtTokenValid: boolean; } const getStatusColor = (status: string) => { switch (status) { case 'healthy': case 'success': return 'text-green-600 dark:text-green-400'; case 'warning': case 'partial': return 'text-yellow-600 dark:text-yellow-400'; case 'error': case 'failed': return 'text-red-600 dark:text-red-400'; case 'checking': return 'text-blue-600 dark:text-blue-400'; case 'skipped': return 'text-gray-600 dark:text-gray-400'; default: return 'text-gray-600 dark:text-gray-400'; } }; const getStatusBadge = (status: string) => { switch (status) { case 'healthy': case 'success': return 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400'; case 'warning': case 'partial': return 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400'; case 'error': case 'failed': 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'; case 'skipped': return 'bg-gray-100 text-gray-800 dark:bg-gray-900/30 dark:text-gray-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': case 'success': return '✓'; case 'warning': case 'partial': return '⚠'; case 'error': case 'failed': return '✗'; case 'checking': return '⟳'; case 'skipped': return '—'; default: return '?'; } }; export default function DebugStatus() { const { activeSite } = useSiteStore(); const toast = useToast(); // Tab navigation state const [activeTab, setActiveTab] = useState<'system-health' | 'wp-integration'>('system-health'); // Data state const [loading, setLoading] = useState(false); const [moduleHealths, setModuleHealths] = useState([]); const [debugEnabled, setDebugEnabled] = useState(false); // Helper to call API endpoints const apiCall = useCallback(async (endpoint: string, options: RequestInit = {}) => { try { console.log(`[DEBUG] Calling API: ${endpoint}`); // fetchAPI returns parsed JSON data directly (not Response object) const data = await fetchAPI(endpoint, options); console.log(`[DEBUG] Success from ${endpoint}:`, data); // Return mock response object with data return { response: { ok: true, status: 200, statusText: 'OK' } as Response, data }; } catch (error: any) { console.error(`[DEBUG] API call failed for ${endpoint}:`, error); // fetchAPI throws errors for non-OK responses - extract error data const errorData = error.response || { detail: error.message?.replace(/^API Error.*?:\s*/, '') || 'Request failed' }; const status = error.status || 500; console.log(`[DEBUG] Error from ${endpoint}:`, { status, errorData }); return { response: { ok: false, status, statusText: error.message } as Response, data: errorData }; } }, []); // Check database schema field mappings (the issues we just fixed) const checkDatabaseSchemaMapping = useCallback(async (): Promise => { if (!activeSite) { return { name: 'Database Schema Mapping', description: 'Checks if model field names map correctly to database columns', status: 'warning', message: 'No site selected', details: 'Please select a site to run health checks', lastChecked: new Date().toISOString(), }; } try { // Test Writer Content endpoint (was failing with entity_type error) const { response: contentResp, data: contentData } = await apiCall(`/v1/writer/content/?site=${activeSite.id}`); if (!contentResp.ok) { const errorMsg = contentData?.detail || contentData?.error || contentData?.message || `HTTP ${contentResp.status}`; return { name: 'Database Schema Mapping', description: 'Checks if model field names map correctly to database columns', status: 'error', message: errorMsg, details: 'Cannot verify schema - API endpoint failed', 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 for ${activeSite.name}. 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(), }; } }, [apiCall, activeSite]); // Check Writer module health const checkWriterModule = useCallback(async (): Promise => { const checks: HealthCheck[] = []; if (!activeSite) { checks.push({ name: 'Content List', description: 'Writer content listing endpoint', status: 'warning', message: 'No site selected', lastChecked: new Date().toISOString(), }); return checks; } // Check Content endpoint try { const { response: contentResp, data: contentData } = await apiCall(`/v1/writer/content/?site=${activeSite.id}`); if (contentResp && contentResp.ok) { checks.push({ name: 'Content List', description: 'Writer content listing endpoint', status: 'healthy', message: `Found ${contentData?.count || contentData?.results?.length || 0} content items for ${activeSite.name}`, lastChecked: new Date().toISOString(), }); } else { const errorMsg = contentData?.detail || contentData?.error || contentData?.message || (contentResp ? `HTTP ${contentResp.status}` : 'Request failed'); checks.push({ name: 'Content List', description: 'Writer content listing endpoint', status: 'error', message: errorMsg, details: 'Check authentication and endpoint availability', 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/?site=${activeSite.id}`); if (tasksResp && tasksResp.ok) { checks.push({ name: 'Tasks List', description: 'Writer tasks listing endpoint', status: 'healthy', message: `Found ${tasksData?.count || tasksData?.results?.length || 0} tasks for ${activeSite.name}`, lastChecked: new Date().toISOString(), }); } else { const errorMsg = tasksData?.detail || tasksData?.error || tasksData?.message || (tasksResp ? `HTTP ${tasksResp.status}` : 'Request failed'); checks.push({ name: 'Tasks List', description: 'Writer tasks listing endpoint', status: 'error', message: errorMsg, details: 'Check authentication and endpoint availability', 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/?site=${activeSite.id}`); 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; }, [apiCall, activeSite]); // Check Planner module health const checkPlannerModule = useCallback(async (): Promise => { const checks: HealthCheck[] = []; if (!activeSite) { checks.push({ name: 'Keyword Clusters', description: 'Planner keyword clustering endpoint', status: 'warning', message: 'No site selected', lastChecked: new Date().toISOString(), }); return checks; } // Check Keyword Clusters endpoint try { const { response: clustersResp, data: clustersData } = await apiCall(`/v1/planner/clusters/?site=${activeSite.id}`); if (clustersResp && clustersResp.ok) { checks.push({ name: 'Clusters List', description: 'Planner clusters listing endpoint', status: 'healthy', message: `Found ${clustersData?.count || clustersData?.results?.length || 0} clusters for ${activeSite.name}`, lastChecked: new Date().toISOString(), }); } else { const errorMsg = clustersData?.detail || clustersData?.error || clustersData?.message || (clustersResp ? `HTTP ${clustersResp.status}` : 'Request failed'); checks.push({ name: 'Clusters List', description: 'Planner clusters listing endpoint', status: 'error', message: errorMsg, 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/?site=${activeSite.id}`); if (keywordsResp.ok && keywordsData?.success !== false) { checks.push({ name: 'Keywords List', description: 'Planner keywords listing endpoint', status: 'healthy', message: `Found ${keywordsData?.count || 0} keywords for ${activeSite.name}`, 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/?site=${activeSite.id}`); if (ideasResp.ok && ideasData?.success !== false) { checks.push({ name: 'Ideas List', description: 'Planner ideas listing endpoint', status: 'healthy', message: `Found ${ideasData?.count || 0} ideas for ${activeSite.name}`, 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; }, [apiCall, activeSite]); // 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 && sitesResp.ok) { checks.push({ name: 'Sites List', description: 'Sites listing endpoint', status: 'healthy', message: `Found ${sitesData?.count || sitesData?.results?.length || sitesData?.length || 0} sites`, lastChecked: new Date().toISOString(), }); } else { const errorMsg = sitesData?.detail || sitesData?.error || sitesData?.message || (sitesResp ? `HTTP ${sitesResp.status}` : 'Request failed'); checks.push({ name: 'Sites List', description: 'Sites listing endpoint', status: 'error', message: errorMsg, 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; }, [apiCall]); // Check Integration module health const checkIntegrationModule = useCallback(async (): Promise => { const checks: HealthCheck[] = []; if (!activeSite) { checks.push({ name: 'Content Types Sync', description: 'Integration content types endpoint', status: 'warning', message: 'No site selected', lastChecked: new Date().toISOString(), }); return checks; } // Check Integration content types endpoint try { // First get the integration for this site const { response: intResp, data: intData } = await apiCall( `/v1/integration/integrations/?site_id=${activeSite.id}` ); if (!intResp.ok || !intData || (Array.isArray(intData) && intData.length === 0) || (intData.results && intData.results.length === 0)) { checks.push({ name: 'Content Types Sync', description: 'Integration content types endpoint', status: 'warning', message: 'No WordPress integration configured', details: 'Add a WordPress integration in Settings > Integrations', lastChecked: new Date().toISOString(), }); } else { // Extract integration ID from response const integration = Array.isArray(intData) ? intData[0] : (intData.results ? intData.results[0] : intData); const integrationId = integration?.id; if (!integrationId) { checks.push({ name: 'Content Types Sync', description: 'Integration content types endpoint', status: 'error', message: 'Invalid integration data', lastChecked: new Date().toISOString(), }); } else { // Now check content types for this integration const { response: contentTypesResp, data: contentTypesData } = await apiCall( `/v1/integration/integrations/${integrationId}/content-types/` ); if (contentTypesResp.ok && contentTypesData?.success !== false) { checks.push({ name: 'Content Types Sync', description: 'Integration content types endpoint', status: 'healthy', message: `Content types synced for ${activeSite.name}`, lastChecked: new Date().toISOString(), }); } else { checks.push({ name: 'Content Types Sync', description: 'Integration content types endpoint', status: 'error', message: contentTypesData?.detail || contentTypesData?.error || `Failed with ${contentTypesResp.status}`, details: 'Check integration views field mappings (content_type_map vs entity_type_map)', 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; }, [apiCall, activeSite]); // 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] = await Promise.all([ checkWriterModule(), checkPlannerModule(), checkSitesModule(), checkIntegrationModule(), ]); // 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, }, ]; setModuleHealths(moduleHealthResults); } catch (error) { console.error('Failed to run health checks:', error); } finally { setLoading(false); } }, [ apiCall, checkDatabaseSchemaMapping, checkWriterModule, checkPlannerModule, checkSitesModule, checkIntegrationModule, ]); // Run checks on mount and when site changes useEffect(() => { if (!debugEnabled || !activeSite) { setModuleHealths([]); return; } runAllChecks(); }, [runAllChecks, debugEnabled, activeSite]); // 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

{/* Site Selector */}
{/* No Site Selected Warning */} {!activeSite && (

No Site Selected

Please select a site above to run health checks and view debug information.

)} {/* Debug Toggle */} {activeSite && (

System Health Debug

Enable debug mode to run health checks for {activeSite.name}

)} {/* Tab Content */} {debugEnabled && activeSite ? ( <> {/* Tab Navigation */}
{/* Tab Content */} {activeTab === 'system-health' ? (
{/* 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 content_type, content_structure, or content_html:

  • Check that model fields match database column names
  • Verify database columns exist with SELECT column_name FROM information_schema.columns
  • All field names now match database (no db_column mappings)

Field Reference Errors in Code

If API endpoints return 500 errors with AttributeError or similar:

  • All field names now standardized: content_type, content_structure, content_html
  • Old names removed: entity_type, site_entity_type, cluster_role, html_content
  • 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.

) : ( // WordPress Integration Debug Tab )} ) : (

{activeSite ? 'Enable debug mode above to view system health checks' : 'Select a site and enable debug mode to view system health checks'}

)} ); }