diff --git a/backend/igny8_core/modules/integration/views.py b/backend/igny8_core/modules/integration/views.py index 4caa86e3..de5c530d 100644 --- a/backend/igny8_core/modules/integration/views.py +++ b/backend/igny8_core/modules/integration/views.py @@ -237,8 +237,10 @@ class IntegrationViewSet(SiteSectorModelViewSet): issues.append(f"Cannot detect IGNY8 plugin: {str(e)}") # Check 3: Verify API keys MATCH by making authenticated request - # This is the CRITICAL check - WordPress must accept our API key - if health_checks['plugin_installed'] and health_checks['plugin_has_api_key']: + # This is the CRITICAL and AUTHORITATIVE check - WordPress must accept our API key + # We always try this if plugin is installed, regardless of what /status says about has_api_key + # because the /verify-key endpoint is the source of truth + if health_checks['plugin_installed']: try: # Make authenticated request using Site.wp_api_key to dedicated verify endpoint verify_response = http_requests.get( @@ -251,6 +253,8 @@ class IntegrationViewSet(SiteSectorModelViewSet): ) if verify_response.status_code == 200: health_checks['api_key_verified'] = True + # If verify succeeds, plugin definitely has the matching key + health_checks['plugin_has_api_key'] = True elif verify_response.status_code in [401, 403]: issues.append("API key mismatch - WordPress has different key than IGNY8. Please copy the API key from IGNY8 to WordPress plugin settings.") else: diff --git a/backend/igny8_core/modules/integration/webhooks.py b/backend/igny8_core/modules/integration/webhooks.py index c5516df4..2d7f10f0 100644 --- a/backend/igny8_core/modules/integration/webhooks.py +++ b/backend/igny8_core/modules/integration/webhooks.py @@ -156,13 +156,22 @@ def wordpress_status_webhook(request): content.status = igny8_status logger.info(f"[wordpress_status_webhook] Status updated: {old_status} → {content.status}") + # Update site_status based on WordPress status + old_site_status = content.site_status + if post_status == 'publish': + content.site_status = 'published' + content.site_status_updated_at = timezone.now() + elif post_status in ['draft', 'pending', 'trash']: + content.site_status = 'not_published' + content.site_status_updated_at = timezone.now() + # Update WordPress status in metadata if not content.metadata: content.metadata = {} content.metadata['wordpress_status'] = post_status content.metadata['last_wp_sync'] = timezone.now().isoformat() - content.save(update_fields=['external_id', 'external_url', 'status', 'metadata', 'updated_at']) + content.save(update_fields=['external_id', 'external_url', 'status', 'site_status', 'site_status_updated_at', 'metadata', 'updated_at']) logger.info(f"[wordpress_status_webhook] Updated content {content_id}:") logger.info(f" - Status: {old_status} → {content.status}") diff --git a/backend/igny8_core/tasks/wordpress_publishing_new.py b/backend/igny8_core/tasks/wordpress_publishing_new.py index 1232c16f..33833f34 100644 --- a/backend/igny8_core/tasks/wordpress_publishing_new.py +++ b/backend/igny8_core/tasks/wordpress_publishing_new.py @@ -290,12 +290,14 @@ def publish_content_to_wordpress(self, content_id: int, site_integration_id: int content.external_id = str(wp_data.get('post_id')) content.external_url = wp_data.get('post_url') content.status = 'published' + content.site_status = 'published' + content.site_status_updated_at = timezone.now() if not hasattr(content, 'metadata') or content.metadata is None: content.metadata = {} content.metadata['wordpress_status'] = wp_status - content.save(update_fields=['external_id', 'external_url', 'status', 'metadata', 'updated_at']) + content.save(update_fields=['external_id', 'external_url', 'status', 'site_status', 'site_status_updated_at', 'metadata', 'updated_at']) publish_logger.info(f" ✅ Content model updated:") publish_logger.info(f" - External ID: {content.external_id}") @@ -361,12 +363,14 @@ def publish_content_to_wordpress(self, content_id: int, site_integration_id: int content.external_id = str(wp_data.get('post_id')) content.external_url = wp_data.get('post_url') content.status = 'published' + content.site_status = 'published' + content.site_status_updated_at = timezone.now() if not hasattr(content, 'metadata') or content.metadata is None: content.metadata = {} content.metadata['wordpress_status'] = wp_status - content.save(update_fields=['external_id', 'external_url', 'status', 'metadata', 'updated_at']) + content.save(update_fields=['external_id', 'external_url', 'status', 'site_status', 'site_status_updated_at', 'metadata', 'updated_at']) # Log sync event duration_ms = int((time.time() - start_time) * 1000) diff --git a/frontend/src/config/pages/approved.config.tsx b/frontend/src/config/pages/approved.config.tsx index 03c569b7..b408b51e 100644 --- a/frontend/src/config/pages/approved.config.tsx +++ b/frontend/src/config/pages/approved.config.tsx @@ -147,18 +147,63 @@ export function createApprovedPageConfig(params: { date: true, width: '150px', render: (value: string, row: Content) => { - if (!value) { - return Not scheduled; - } - const publishDate = new Date(value); - const now = new Date(); - const isFuture = publishDate > now; + const siteStatus = row.site_status; - return ( - - {formatRelativeDate(value)} - - ); + // For published items: show when it was published + if (siteStatus === 'published') { + // Use site_status_updated_at if available, otherwise updated_at + const publishedAt = row.site_status_updated_at || row.updated_at; + if (publishedAt) { + return ( + + {formatRelativeDate(publishedAt)} + + ); + } + return Published; + } + + // For failed items: show when the failure occurred + if (siteStatus === 'failed') { + const failedAt = row.site_status_updated_at || row.updated_at; + if (failedAt) { + return ( + + {formatRelativeDate(failedAt)} + + ); + } + return Failed; + } + + // For scheduled items: show when it will be published + if (siteStatus === 'scheduled' || siteStatus === 'publishing') { + if (value) { + const publishDate = new Date(value); + const now = new Date(); + const isFuture = publishDate > now; + return ( + + {formatRelativeDate(value)} + + ); + } + return Pending; + } + + // For not_published items: show scheduled date if available + if (value) { + const publishDate = new Date(value); + const now = new Date(); + const isFuture = publishDate > now; + return ( + + {formatRelativeDate(value)} + + ); + } + + return Not scheduled; }, },