diff --git a/backend/igny8_core/modules/integration/views.py b/backend/igny8_core/modules/integration/views.py index a48bad7a..38807261 100644 --- a/backend/igny8_core/modules/integration/views.py +++ b/backend/igny8_core/modules/integration/views.py @@ -598,4 +598,137 @@ class IntegrationViewSet(SiteSectorModelViewSet): 'logs': logs, 'count': len(logs) }, request=request) + + @action(detail=True, methods=['get'], url_path='debug-status') + def debug_status(self, request, pk=None): + """ + Get comprehensive debug status for WordPress integration. + Includes health, recent events, and data validation. + + GET /api/v1/integration/integrations/{id}/debug-status/ + + Query params: + - include_events: Include recent sync events (default: false) + - include_validation: Include data validation matrix (default: false) + - event_limit: Number of events to return (default: 20) + """ + integration = self.get_object() + + include_events = request.query_params.get('include_events', 'false').lower() == 'true' + include_validation = request.query_params.get('include_validation', 'false').lower() == 'true' + event_limit = int(request.query_params.get('event_limit', 20)) + + # Get integration health + health_data = { + 'api_status': 'healthy' if integration.is_active else 'error', + 'api_message': 'Integration is active' if integration.is_active else 'Integration is inactive', + 'last_api_check': integration.updated_at.isoformat() if integration.updated_at else None, + 'plugin_active': integration.is_active, + 'plugin_version': integration.credentials_json.get('plugin_version', 'Unknown') if integration.credentials_json else 'Unknown', + 'debug_mode': integration.credentials_json.get('debug_enabled', False) if integration.credentials_json else False, + 'sync_healthy': integration.last_sync_at is not None, + 'pending_syncs': 0, # TODO: Calculate from actual sync queue + 'last_sync': integration.last_sync_at.isoformat() if integration.last_sync_at else None, + } + + response_data = { + 'health': health_data, + } + + # Include sync events if requested + if include_events: + sync_health_service = SyncHealthService() + logs = sync_health_service.get_sync_logs( + integration.site_id, + integration_id=integration.id, + limit=event_limit + ) + + # Format logs as events + events = [] + for log in logs: + events.append({ + 'type': 'sync' if log.get('success') else 'error', + 'action': log.get('operation', 'sync'), + 'description': log.get('message', 'Sync operation'), + 'timestamp': log.get('timestamp', timezone.now().isoformat()), + 'details': log.get('details', {}), + }) + + response_data['events'] = events + + # Include data validation if requested + if include_validation: + # TODO: Implement actual data validation check + # For now, return placeholder data + response_data['validation'] = [ + { + 'field_name': 'content_title', + 'igny8_value': 'Sample Title', + 'wp_value': 'Sample Title', + 'matches': True, + }, + { + 'field_name': 'content_html', + 'igny8_value': '

Content

', + 'wp_value': '

Content

', + 'matches': True, + }, + ] + + return success_response(response_data, request=request) + + @action(detail=True, methods=['post'], url_path='trigger-debug') + def trigger_debug(self, request, pk=None): + """ + Enable/disable debug mode for WordPress integration. + + POST /api/v1/integration/integrations/{id}/trigger-debug/ + + Body: + { + "debug_enabled": true + } + """ + integration = self.get_object() + debug_enabled = request.data.get('debug_enabled', False) + + # Update credentials with debug flag + credentials = integration.credentials_json or {} + credentials['debug_enabled'] = debug_enabled + integration.credentials_json = credentials + integration.save() + + logger.info( + f"WordPress debug mode {'enabled' if debug_enabled else 'disabled'} " + f"for integration {integration.id} (site: {integration.site.name})" + ) + + return success_response({ + 'debug_enabled': debug_enabled, + 'message': f"Debug mode {'enabled' if debug_enabled else 'disabled'} successfully", + }, request=request) + + @action(detail=True, methods=['post'], url_path='test-connection') + def test_connection_detail(self, request, pk=None): + """ + Test connection to WordPress site. + + POST /api/v1/integration/integrations/{id}/test-connection/ + """ + integration = self.get_object() + + service = IntegrationService() + result = service.test_connection(integration) + + if result.get('success'): + return success_response(result, request=request) + else: + return error_response( + result.get('message', 'Connection test failed'), + result.get('details'), + status.HTTP_400_BAD_REQUEST, + request + ) + diff --git a/backend/igny8_core/modules/writer/admin.py b/backend/igny8_core/modules/writer/admin.py index d9caace6..887af600 100644 --- a/backend/igny8_core/modules/writer/admin.py +++ b/backend/igny8_core/modules/writer/admin.py @@ -91,7 +91,7 @@ class ContentAdmin(SiteSectorAdminMixin, admin.ModelAdmin): search_fields = ['title', 'content_html', 'external_url'] ordering = ['-created_at'] readonly_fields = ['created_at', 'updated_at', 'word_count'] - filter_horizontal = ['taxonomy_terms'] # Add many-to-many widget for taxonomy terms + # Note: taxonomy_terms removed from filter_horizontal because it uses a through model fieldsets = ( ('Basic Info', { @@ -100,10 +100,8 @@ class ContentAdmin(SiteSectorAdminMixin, admin.ModelAdmin): ('Content Classification', { 'fields': ('content_type', 'content_structure', 'source') }), - ('Taxonomy Terms (Tags & Categories)', { - 'fields': ('taxonomy_terms',), - 'description': 'Select tags and categories for this content' - }), + # Note: taxonomy_terms field removed from fieldsets because it uses ContentTaxonomyAssociation through model + # Taxonomy associations can be managed via the inline admin or separately ('Content', { 'fields': ('content_html', 'word_count') }), diff --git a/frontend/src/pages/Settings/DebugStatus.tsx b/frontend/src/pages/Settings/DebugStatus.tsx index 527f329f..53dbcdab 100644 --- a/frontend/src/pages/Settings/DebugStatus.tsx +++ b/frontend/src/pages/Settings/DebugStatus.tsx @@ -1,4 +1,5 @@ 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"; @@ -120,20 +121,60 @@ export default function DebugStatus() { // 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/'); + 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: `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)', + message: errorMsg, + details: 'Cannot verify schema - API endpoint failed', lastChecked: new Date().toISOString(), }; } @@ -146,7 +187,7 @@ export default function DebugStatus() { 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.`, + details: `Content API working correctly for ${activeSite.name}. Fields like content_type, content_html, content_structure are properly mapped.`, lastChecked: new Date().toISOString(), }; } @@ -169,31 +210,43 @@ export default function DebugStatus() { 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/'); + const { response: contentResp, data: contentData } = await apiCall(`/v1/writer/content/?site=${activeSite.id}`); - if (contentResp.ok && contentData?.success !== false) { + if (contentResp && contentResp.ok) { checks.push({ name: 'Content List', description: 'Writer content listing endpoint', status: 'healthy', - message: `Found ${contentData?.count || 0} content items`, + 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: contentData?.error || `Failed with ${contentResp.status}`, - details: 'Check model field mappings (content_type, content_html, content_structure)', + message: errorMsg, + details: 'Check authentication and endpoint availability', lastChecked: new Date().toISOString(), }); } @@ -209,23 +262,24 @@ export default function DebugStatus() { // Check Tasks endpoint try { - const { response: tasksResp, data: tasksData } = await apiCall('/v1/writer/tasks/'); + const { response: tasksResp, data: tasksData } = await apiCall(`/v1/writer/tasks/?site=${activeSite.id}`); - if (tasksResp.ok && tasksData?.success !== false) { + if (tasksResp && tasksResp.ok) { checks.push({ name: 'Tasks List', description: 'Writer tasks listing endpoint', status: 'healthy', - message: `Found ${tasksData?.count || 0} tasks`, + 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: tasksData?.error || `Failed with ${tasksResp.status}`, - details: 'Check model field mappings (content_type, content_structure, taxonomy_term)', + message: errorMsg, + details: 'Check authentication and endpoint availability', lastChecked: new Date().toISOString(), }); } @@ -242,7 +296,7 @@ export default function DebugStatus() { // Check Content Validation try { // Get first content ID if available - const { data: contentData } = await apiCall('/v1/writer/content/'); + const { data: contentData } = await apiCall(`/v1/writer/content/?site=${activeSite.id}`); const firstContentId = contentData?.results?.[0]?.id; if (firstContentId) { @@ -288,30 +342,42 @@ export default function DebugStatus() { } return checks; - }, []); + }, [apiCall, activeSite]); // Check Planner module health const checkPlannerModule = useCallback(async (): Promise => { const checks: HealthCheck[] = []; - // Check Clusters endpoint + 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/'); + const { response: clustersResp, data: clustersData } = await apiCall(`/v1/planner/clusters/?site=${activeSite.id}`); - if (clustersResp.ok && clustersData?.success !== false) { + if (clustersResp && clustersResp.ok) { checks.push({ name: 'Clusters List', description: 'Planner clusters listing endpoint', status: 'healthy', - message: `Found ${clustersData?.count || 0} clusters`, + 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: clustersData?.error || `Failed with ${clustersResp.status}`, + message: errorMsg, lastChecked: new Date().toISOString(), }); } @@ -327,14 +393,14 @@ export default function DebugStatus() { // Check Keywords endpoint try { - const { response: keywordsResp, data: keywordsData } = await apiCall('/v1/planner/keywords/'); + 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`, + message: `Found ${keywordsData?.count || 0} keywords for ${activeSite.name}`, lastChecked: new Date().toISOString(), }); } else { @@ -358,14 +424,14 @@ export default function DebugStatus() { // Check Ideas endpoint try { - const { response: ideasResp, data: ideasData } = await apiCall('/v1/planner/ideas/'); + 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`, + message: `Found ${ideasData?.count || 0} ideas for ${activeSite.name}`, lastChecked: new Date().toISOString(), }); } else { @@ -388,7 +454,7 @@ export default function DebugStatus() { } return checks; - }, []); + }, [apiCall, activeSite]); // Check Sites module health const checkSitesModule = useCallback(async (): Promise => { @@ -398,20 +464,21 @@ export default function DebugStatus() { try { const { response: sitesResp, data: sitesData } = await apiCall('/v1/auth/sites/'); - if (sitesResp.ok && sitesData?.success !== false) { + if (sitesResp && sitesResp.ok) { checks.push({ name: 'Sites List', description: 'Sites listing endpoint', status: 'healthy', - message: `Found ${sitesData?.count || sitesData?.results?.length || 0} sites`, + 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: sitesData?.error || `Failed with ${sitesResp.status}`, + message: errorMsg, lastChecked: new Date().toISOString(), }); } @@ -426,49 +493,77 @@ export default function DebugStatus() { } 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 { - // Get first site ID if available - const { data: sitesData } = await apiCall('/v1/auth/sites/'); - const firstSiteId = sitesData?.results?.[0]?.id || sitesData?.[0]?.id; + // First get the integration for this site + const { response: intResp, data: intData } = await apiCall( + `/v1/integration/integrations/?site_id=${activeSite.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 { + 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 sites available to test integration', + 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({ @@ -481,7 +576,7 @@ export default function DebugStatus() { } return checks; - }, []); + }, [apiCall, activeSite]); // Run all health checks const runAllChecks = useCallback(async () => { @@ -535,6 +630,7 @@ export default function DebugStatus() { setLoading(false); } }, [ + apiCall, checkDatabaseSchemaMapping, checkWriterModule, checkPlannerModule, @@ -542,10 +638,14 @@ export default function DebugStatus() { checkIntegrationModule, ]); - // Run checks on mount + // Run checks on mount and when site changes useEffect(() => { + if (!debugEnabled || !activeSite) { + setModuleHealths([]); + return; + } runAllChecks(); - }, [runAllChecks]); + }, [runAllChecks, debugEnabled, activeSite]); // Calculate module status const getModuleStatus = (module: ModuleHealth): 'error' | 'warning' | 'healthy' => { @@ -594,37 +694,85 @@ export default function DebugStatus() { - {/* Tab Navigation */} -
-
- -
+ {/* 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 */} - {activeTab === 'system-health' ? ( -
+ {debugEnabled && activeSite ? ( + <> + {/* Tab Navigation */} +
+
+ +
+
+ + {/* Tab Content */} + {activeTab === 'system-health' ? ( +
{/* Overall Health Summary */}
-
+
+ ) : ( + // WordPress Integration Debug Tab + + )} + ) : ( - // 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'} +

+
)} diff --git a/frontend/src/pages/Settings/WordPressIntegrationDebug.tsx b/frontend/src/pages/Settings/WordPressIntegrationDebug.tsx index c814ae36..4c8b0c95 100644 --- a/frontend/src/pages/Settings/WordPressIntegrationDebug.tsx +++ b/frontend/src/pages/Settings/WordPressIntegrationDebug.tsx @@ -5,18 +5,15 @@ import { AlertTriangle, Loader2, Activity, - Info, Clock, Globe, RefreshCw, TestTube, - Wrench, - Database + Wrench } from 'lucide-react'; -import SiteAndSectorSelector from '../../components/common/SiteAndSectorSelector'; import { useSiteStore } from '../../store/siteStore'; import { useToast } from '../../components/ui/toast/ToastContainer'; -import { API_BASE_URL } from '../../services/api'; +import { API_BASE_URL, fetchAPI } from '../../services/api'; // Types for WordPress integration debugging interface IntegrationHealth { @@ -51,54 +48,57 @@ export default function WordPressIntegrationDebug() { // State const [debugEnabled, setDebugEnabled] = useState(false); const [loading, setLoading] = useState(false); + const [initializing, setInitializing] = useState(true); const [pollingInterval, setPollingInterval] = useState(null); const [integrationHealth, setIntegrationHealth] = useState(null); const [syncEvents, setSyncEvents] = useState([]); const [dataValidation, setDataValidation] = useState([]); + const [integrationId, setIntegrationId] = useState(null); // Get active site from store const { activeSite } = useSiteStore(); const toast = useToast(); - // 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; - }; + // Check if integration exists for the active site + useEffect(() => { + const checkIntegration = async () => { + if (!activeSite) { + setInitializing(false); + return; + } + + try { + setInitializing(true); + const data = await fetchAPI(`/v1/integration/integrations/?site_id=${activeSite.id}`); + const integrations = data.results || []; + + if (integrations.length > 0) { + const wpIntegration = integrations.find((i: any) => i.platform === 'wordpress'); + if (wpIntegration) { + setIntegrationId(wpIntegration.id); + setDebugEnabled(wpIntegration.credentials_json?.debug_enabled || false); + } + } + } catch (error) { + console.error('[WP-DEBUG] Failed to check integration:', error); + } finally { + setInitializing(false); + } + }; + + checkIntegration(); + }, [activeSite]); // Helper to make authenticated API calls with debug awareness const apiCall = async (path: string, method: string = 'GET', body?: any) => { - // Skip API calls if debug is disabled (unless it's essential) - if (!debugEnabled && !path.includes('/debug-status/')) { - console.log('[WP-DEBUG] Skipping API call - debug disabled:', path); - return { response: null, data: null }; - } - - const token = getAuthToken(); - const headers: HeadersInit = { - 'Content-Type': 'application/json', - }; - - if (token) { - headers['Authorization'] = `Bearer ${token}`; + // Always allow debug-status calls to check if integration exists + if (!integrationId && !path.includes('/integrations/?')) { + console.log('[WP-DEBUG] Skipping API call - no integration configured:', path); + return { response: { ok: false, status: 0 } as Response, data: null }; } const options: RequestInit = { method, - headers, - credentials: 'include', }; if (body && method !== 'GET') { @@ -110,19 +110,19 @@ export default function WordPressIntegrationDebug() { console.log('[WP-DEBUG] API Call:', method, path, body ? { body } : {}); } - const response = await fetch(`${API_BASE_URL}${path}`, options); - const data = await response.json(); + const data = await fetchAPI(path, options); if (debugEnabled) { - console.log('[WP-DEBUG] API Response:', response.status, data); + console.log('[WP-DEBUG] API Response:', data); } - return { response, data }; - } catch (error) { + return { response: { ok: true, status: 200 } as Response, data }; + } catch (error: any) { if (debugEnabled) { console.error('[WP-DEBUG] API Error:', error); } - throw error; + const errorData = error.response || { detail: error.message || 'Request failed' }; + return { response: { ok: false, status: error.status || 500 } as Response, data: errorData }; } }; @@ -133,6 +133,11 @@ export default function WordPressIntegrationDebug() { return; } + if (!integrationId) { + toast.error('No WordPress integration found for this site'); + return; + } + try { setLoading(true); @@ -147,11 +152,12 @@ export default function WordPressIntegrationDebug() { startPolling(); // Trigger debug mode on WordPress plugin if integrated - if (activeSite.id) { - await apiCall(`/integration/integrations/${activeSite.id}/trigger-debug/`, 'POST', { - debug_enabled: true - }); - } + await apiCall(`/v1/integration/integrations/${integrationId}/trigger-debug/`, 'POST', { + debug_enabled: true + }); + + // Load initial debug data + await loadDebugData(); } else { console.log('[WP-DEBUG] 🔒 WordPress Debug mode DISABLED'); toast.info('WordPress debug mode disabled - reduced logging'); @@ -160,11 +166,9 @@ export default function WordPressIntegrationDebug() { stopPolling(); // Disable debug on WordPress - if (activeSite.id) { - await apiCall(`/integration/integrations/${activeSite.id}/trigger-debug/`, 'POST', { - debug_enabled: false - }); - } + await apiCall(`/v1/integration/integrations/${integrationId}/trigger-debug/`, 'POST', { + debug_enabled: false + }); } } catch (error) { console.error('[WP-DEBUG] Failed to toggle debug mode:', error); @@ -200,7 +204,10 @@ export default function WordPressIntegrationDebug() { // Load all debug data const loadDebugData = useCallback(async () => { - if (!activeSite || !debugEnabled) return; + if (!activeSite || !integrationId) { + console.log('[WP-DEBUG] Skipping load - no site or integration'); + return; + } try { setLoading(true); @@ -209,32 +216,30 @@ export default function WordPressIntegrationDebug() { // Load integration health await loadIntegrationHealth(); - // Load sync events - await loadSyncEvents(); - - // Load validation data - await loadDataValidation(); + // Load sync events if debug is enabled + if (debugEnabled) { + await loadSyncEvents(); + await loadDataValidation(); + } console.log('[WP-DEBUG] ✅ WordPress debug data loaded successfully'); } catch (error) { console.error('[WP-DEBUG] ❌ Failed to load WordPress debug data:', error); - if (debugEnabled) { - toast.error('Failed to load WordPress debug data'); - } + // Don't show toast error on initial load } finally { setLoading(false); } - }, [activeSite, debugEnabled]); + }, [activeSite, integrationId, debugEnabled]); // Load integration health const loadIntegrationHealth = async () => { - if (!activeSite) return; + if (!integrationId) return; try { - const { data } = await apiCall(`/integration/integrations/${activeSite.id}/debug-status/`); + const { data } = await apiCall(`/v1/integration/integrations/${integrationId}/debug-status/`); - if (data?.success) { - setIntegrationHealth(data.data.health); + if (data?.health) { + setIntegrationHealth(data.health); console.log('[WP-DEBUG] 💚 Integration health loaded'); } } catch (error) { @@ -244,14 +249,14 @@ export default function WordPressIntegrationDebug() { // Load sync events const loadSyncEvents = async () => { - if (!activeSite) return; + if (!integrationId) return; try { - const { data } = await apiCall(`/integration/integrations/${activeSite.id}/debug-status/?include_events=true`); + const { data } = await apiCall(`/v1/integration/integrations/${integrationId}/debug-status/?include_events=true`); - if (data?.success && data.data?.events) { - setSyncEvents(data.data.events); - console.log('[WP-DEBUG] 📜 Loaded', data.data.events.length, 'WordPress sync events'); + if (data?.events) { + setSyncEvents(data.events); + console.log('[WP-DEBUG] 📜 Loaded', data.events.length, 'WordPress sync events'); } } catch (error) { console.error('[WP-DEBUG] Failed to load sync events:', error); @@ -260,13 +265,13 @@ export default function WordPressIntegrationDebug() { // Load data validation matrix const loadDataValidation = async () => { - if (!activeSite) return; + if (!integrationId) return; try { - const { data } = await apiCall(`/integration/integrations/${activeSite.id}/debug-status/?include_validation=true`); + const { data } = await apiCall(`/v1/integration/integrations/${integrationId}/debug-status/?include_validation=true`); - if (data?.success && data.data?.validation) { - setDataValidation(data.data.validation); + if (data?.validation) { + setDataValidation(data.validation); console.log('[WP-DEBUG] 🧩 Loaded WordPress data validation matrix'); } } catch (error) { @@ -276,19 +281,22 @@ export default function WordPressIntegrationDebug() { // Interactive tools const testConnection = async () => { - if (!activeSite) return; + if (!integrationId) { + toast.error('No WordPress integration found'); + return; + } try { setLoading(true); console.log('[WP-DEBUG] 🔧 Testing connection to WordPress...'); - const { data } = await apiCall(`/integration/integrations/${activeSite.id}/test-connection/`, 'POST'); + const { data } = await apiCall(`/v1/integration/integrations/${integrationId}/test-connection/`, 'POST'); if (data?.success) { toast.success('WordPress connection test successful'); await loadIntegrationHealth(); // Refresh health data } else { - toast.error('WordPress connection test failed: ' + data?.message); + toast.error('WordPress connection test failed: ' + (data?.message || 'Unknown error')); } } catch (error) { console.error('[WP-DEBUG] Connection test error:', error); @@ -298,6 +306,69 @@ export default function WordPressIntegrationDebug() { } }; + // Load initial data when component mounts or integration changes + useEffect(() => { + if (!initializing && integrationId) { + loadDebugData(); + } + }, [integrationId, initializing]); + + // Start/stop polling based on debug state + useEffect(() => { + if (debugEnabled && integrationId) { + startPolling(); + } else { + stopPolling(); + } + + return () => stopPolling(); + }, [debugEnabled, integrationId]); + + // Effects - Load initial data when site changes or integration found + useEffect(() => { + if (activeSite && integrationId && !initializing) { + loadDebugData(); + // Auto-start polling for real-time updates if debug enabled + if (debugEnabled) { + startPolling(); + } + } + + return () => { + stopPolling(); + }; + }, [activeSite, integrationId, initializing]); + + // Show loading state while initializing + if (initializing) { + return ( +
+ +

Loading WordPress integration...

+
+ ); + } + + // Show message if no integration is configured + if (!integrationId) { + return ( +
+
+ +

+ No WordPress Integration Found +

+

+ This site doesn't have a WordPress integration configured yet. +

+

+ Please configure a WordPress integration in the Settings → Integration page first. +

+
+
+ ); + } + const resyncSiteMetadata = async () => { if (!activeSite) return; @@ -333,9 +404,9 @@ export default function WordPressIntegrationDebug() { const { data } = await apiCall(`/integration/integrations/${activeSite.id}/validate-content/${id}/`); - if (data?.success) { + if (data) { toast.success('WordPress post validation completed'); - setDataValidation(data.data.validation || []); + setDataValidation(data.validation || []); } else { toast.error('WordPress validation failed: ' + data?.message); } @@ -347,100 +418,40 @@ export default function WordPressIntegrationDebug() { } }; - // Effects - useEffect(() => { - // Load initial data when site changes - if (activeSite && debugEnabled) { - loadDebugData(); - } - }, [activeSite, debugEnabled, loadDebugData]); - - useEffect(() => { - // Cleanup polling on unmount - return () => { - stopPolling(); - }; - }, []); - return (
- {/* Debug Header with Site Selector and Toggle */} -
-
-
-

- IGNY8 ↔ WordPress Integration Debug -

-

- Real-time monitoring of WordPress bridge plugin and content synchronization + {/* No WordPress Integration Found */} + {initializing ? ( +

+ +

Checking for WordPress integration...

+
+ ) : !integrationId && activeSite ? ( +
+
+ +
+

No WordPress Integration Found

+

+ This site doesn't have a WordPress integration configured yet. +

+

+ Please configure a WordPress integration in Settings → Integration page first. +

+
+
+
+ ) : !activeSite ? ( +
+
+ +

No Site Selected

+

+ Please select a site to view WordPress integration debug data.

- - {/* Debug Toggle */} -
- - - {debugEnabled && pollingInterval && ( -
- - Live -
- )} -
- - {/* Site Selector */} -
- -
- - {/* Debug Status Info */} - {debugEnabled ? ( -
-
- -
-

WordPress Debug Mode Active

-

- Real-time monitoring enabled for {activeSite?.name || 'selected site'}. - WordPress bridge plugin logs are being captured and will auto-disable after 24h. -

-
-
-
- ) : ( -
-
- -
-

WordPress Debug Mode Disabled

-

- Enable debug mode above to start monitoring WordPress integration events and detailed sync logs. -

-
-
-
- )} -
- - {debugEnabled && activeSite ? ( + ) : ( <> {/* Integration Health Summary */}
@@ -638,32 +649,6 @@ export default function WordPressIntegrationDebug() {
)} - ) : debugEnabled && !activeSite ? ( -
-
- -

No Site Selected

-

- Please select a site above to enable WordPress integration debug monitoring. -

-

- Only sites with WordPress integrations will show debug data. -

-
-
- ) : ( -
-
- -

WordPress Debug Disabled

-

- Enable the WordPress debug toggle above to start monitoring integration events. -

-

- This will track content sync, plugin health, and API calls in real-time. -

-
-
)}
);