Files
igny8/igny8-ai-seo-wp-plugin/debug/module-debug.php
2025-11-11 21:16:37 +05:00

1624 lines
64 KiB
PHP

<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : module-debug.php
* @location : /debug/module-debug.php
* @type : Debug Tool
* @scope : Global
* @allowed : Module debugging, development tools
* @reusability : Single Use
* @notes : Module debug widgets and development tools
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Dev-only access guard
if (!defined('IGNY8_DEV') || IGNY8_DEV !== true) {
return;
}
// Only run if WordPress debugging is enabled
if (!WP_DEBUG) {
return;
}
// Evidence functions removed - no longer needed
// Always render module debug content, but control visibility via JavaScript
/**
* Get current module and submodule information
* Centralized function to ensure consistent module detection
*/
function igny8_get_current_module_info() {
// Try to get from global first (set by routing)
if (isset($GLOBALS['igny8_current_module'])) {
$current_module = $GLOBALS['igny8_current_module'];
} else {
// Fallback to URL parsing
$current_page = $_GET['page'] ?? '';
$current_module = 'planner'; // Default fallback
if (!empty($current_page) && strpos($current_page, 'igny8-') === 0) {
$current_module = str_replace('igny8-', '', $current_page);
}
}
// Get submodule from URL or global
$current_submodule = $_GET['sm'] ?? $GLOBALS['current_submodule'] ?? '';
// Build table_id - don't use hardcoded fallback
if (!empty($current_submodule)) {
$table_id = $current_module . '_' . $current_submodule;
} else {
// If no submodule detected, don't assume 'keywords' - return empty
$table_id = $current_module;
}
return [
'module' => $current_module,
'submodule' => $current_submodule,
'table_id' => $table_id,
'page' => $_GET['page'] ?? ''
];
}
/**
* Track AI Logs State
*/
function igny8_track_ai_logs_state($module_info) {
// Check if AI functions are available
if (!function_exists('igny8_get_ai_setting')) {
return [
'status' => 'error',
'message' => 'AI system not available',
'details' => 'igny8_get_ai_setting function missing'
];
}
// Check AI mode
$ai_mode = igny8_get_ai_setting('planner_mode', 'manual');
$ai_enabled = $ai_mode === 'ai';
// Get recent AI logs (last 10 events)
$ai_logs = get_option('igny8_ai_logs', []);
$recent_logs = array_slice($ai_logs, 0, 10);
$log_count = count($recent_logs);
$error_count = 0;
$success_count = 0;
foreach ($recent_logs as $log) {
if ($log['status'] === 'error') {
$error_count++;
} elseif ($log['status'] === 'success') {
$success_count++;
}
}
$details = [
'AI Mode: ' . ($ai_enabled ? '✅ Enabled' : '❌ Disabled'),
'Recent Events: ' . $log_count,
'Success: ' . $success_count . ' | Errors: ' . $error_count,
'Module: ' . $module_info['module'],
'Submodule: ' . ($module_info['submodule'] ?: 'NONE')
];
if ($ai_enabled && $log_count > 0) {
$status = $error_count === 0 ? 'success' : ($error_count < $success_count ? 'warning' : 'error');
$message = "AI Logs: {$log_count} events ({$success_count} success, {$error_count} errors)";
} elseif ($ai_enabled) {
$status = 'success';
$message = 'AI Logs: Ready (no events yet)';
} else {
$status = 'warning';
$message = 'AI Logs: AI mode disabled';
}
return [
'status' => $status,
'message' => $message,
'details' => implode('<br>', $details)
];
}
/**
* Track Database Validation State
*/
function igny8_track_database_validation_state() {
$module_info = igny8_get_current_module_info();
$table_id = $module_info['table_id'];
// Get actual database table name and fields
global $wpdb;
$table_name = igny8_get_table_name($table_id);
if (!$table_name) {
return [
'status' => 'error',
'message' => 'Table not found',
'details' => 'Table ID: ' . $table_id . ' - No table name mapping found'
];
}
// Get actual database fields
$db_fields = [];
$table_exists = $wpdb->get_var("SHOW TABLES LIKE '{$table_name}'") !== null;
if ($table_exists) {
$columns = $wpdb->get_results("DESCRIBE {$table_name}");
foreach ($columns as $column) {
$db_fields[] = $column->Field;
}
} else {
return [
'status' => 'error',
'message' => 'Database table missing',
'details' => 'Table: ' . $table_name . ' does not exist in database'
];
}
// Validate table config fields
$table_errors = igny8_validate_table_config_fields($table_id, $db_fields);
$filter_errors = igny8_validate_filter_config_fields($table_id, $db_fields);
$form_errors = igny8_validate_form_config_fields($table_id, $db_fields);
$total_errors = count($table_errors) + count($filter_errors) + count($form_errors);
if ($total_errors === 0) {
return [
'status' => 'success',
'message' => 'Database validation passed',
'details' => 'Table: ✅<br>Filters: ✅<br>Forms: ✅<br>Automation: ✅'
];
} else {
$error_details = [];
// Format table errors
if (!empty($table_errors)) {
// Filter out config errors and show only field names
$field_errors = array_filter($table_errors, function($error) {
return !strpos($error, 'config') && !strpos($error, 'missing');
});
if (!empty($field_errors)) {
$error_details[] = 'Table:<br>' . implode(', ', $field_errors) . ' mismatch with db, doesn\'t exist';
}
}
// Format filter errors
if (!empty($filter_errors)) {
// Filter out config errors and show only field names
$field_errors = array_filter($filter_errors, function($error) {
return !strpos($error, 'config') && !strpos($error, 'missing');
});
if (!empty($field_errors)) {
$error_details[] = 'Filters:<br>' . implode(', ', $field_errors) . ' mismatch with db, doesn\'t exist';
}
}
// Format form errors
if (!empty($form_errors)) {
// Filter out config errors and show only field names
$field_errors = array_filter($form_errors, function($error) {
return !strpos($error, 'config') && !strpos($error, 'missing');
});
if (!empty($field_errors)) {
$error_details[] = 'Forms:<br>' . implode(', ', $field_errors) . ' mismatch with db, doesn\'t exist';
}
}
// If no field errors, show config errors
if (empty($error_details)) {
if (!empty($table_errors)) {
$error_details[] = 'Table:<br>' . implode(', ', $table_errors);
}
if (!empty($filter_errors)) {
$error_details[] = 'Filters:<br>' . implode(', ', $filter_errors);
}
if (!empty($form_errors)) {
$error_details[] = 'Forms:<br>' . implode(', ', $form_errors);
}
}
return [
'status' => 'error',
'message' => 'Database validation failed',
'details' => implode('<br><br>', $error_details)
];
}
}
/**
* Validate table config fields against database schema
*/
function igny8_validate_table_config_fields($table_id, $db_fields) {
$errors = [];
try {
// Load config file directly
$config_path = plugin_dir_path(__FILE__) . '../modules/config/tables-config.php';
if (!file_exists($config_path)) {
return ['tables_config_file_missing'];
}
// Define constant to bypass access control
if (!defined('IGNY8_INCLUDE_CONFIG')) {
define('IGNY8_INCLUDE_CONFIG', true);
}
$tables_config = require $config_path;
if (!isset($tables_config[$table_id])) {
return ['table_config_not_found'];
}
$table_config = $tables_config[$table_id];
// Check columns
if (isset($table_config['columns'])) {
foreach ($table_config['columns'] as $column_name => $column_config) {
// For display columns, check the source_field instead of the column name
$field_to_check = $column_name;
if (isset($column_config['source_field'])) {
$field_to_check = $column_config['source_field'];
}
if (!in_array($field_to_check, $db_fields)) {
$errors[] = $column_name;
}
}
}
// Check humanize_columns
if (isset($table_config['humanize_columns'])) {
foreach ($table_config['humanize_columns'] as $column_name) {
if (!in_array($column_name, $db_fields)) {
$errors[] = $column_name;
}
}
}
} catch (Exception $e) {
$errors[] = 'config_load_error: ' . $e->getMessage();
}
return $errors;
}
/**
* Validate filter config fields against database schema
*/
function igny8_validate_filter_config_fields($table_id, $db_fields) {
$errors = [];
try {
// Load config file directly
$config_path = plugin_dir_path(__FILE__) . '../modules/config/filters-config.php';
if (!file_exists($config_path)) {
return ['filters_config_file_missing'];
}
// Define constant to bypass access control
if (!defined('IGNY8_INCLUDE_CONFIG')) {
define('IGNY8_INCLUDE_CONFIG', true);
}
$filters_config = require $config_path;
if (!isset($filters_config[$table_id])) {
return []; // No filters config is OK
}
$filter_config = $filters_config[$table_id];
foreach ($filter_config as $filter_name => $filter_data) {
if (isset($filter_data['field']) && !in_array($filter_data['field'], $db_fields)) {
$errors[] = $filter_data['field'];
}
}
} catch (Exception $e) {
$errors[] = 'config_load_error: ' . $e->getMessage();
}
return $errors;
}
/**
* Validate form config fields against database schema
*/
function igny8_validate_form_config_fields($table_id, $db_fields) {
$errors = [];
try {
// Load config file directly
$config_path = plugin_dir_path(__FILE__) . '../modules/config/forms-config.php';
if (!file_exists($config_path)) {
return ['forms_config_file_missing'];
}
// Define constant to bypass access control
if (!defined('IGNY8_INCLUDE_CONFIG')) {
define('IGNY8_INCLUDE_CONFIG', true);
}
$forms_config = require $config_path;
if (!isset($forms_config[$table_id])) {
return []; // No form config is OK
}
$form_config = $forms_config[$table_id];
if (!$form_config || !isset($form_config['fields'])) {
return []; // No form config is OK
}
foreach ($form_config['fields'] as $field) {
if (isset($field['name']) && !in_array($field['name'], $db_fields)) {
$errors[] = $field['name'];
}
}
} catch (Exception $e) {
$errors[] = 'config_load_error: ' . $e->getMessage();
}
return $errors;
}
function igny8_test_column_type_validation($config_data, $table_id) {
if (!isset($config_data[$table_id]['columns'])) {
return ['passed' => false, 'error' => 'no columns'];
}
$columns = $config_data[$table_id]['columns'];
foreach ($columns as $key => $column) {
if (!isset($column['type']) || !in_array($column['type'], ['text', 'number', 'date', 'select', 'textarea', 'enum'])) {
return ['passed' => false, 'error' => 'invalid type: ' . $key];
}
}
return ['passed' => true, 'error' => ''];
}
function igny8_test_filter_field_validation($config_data, $table_id) {
if (!isset($config_data[$table_id])) {
return ['passed' => false, 'error' => 'no filters'];
}
$filters = $config_data[$table_id];
foreach ($filters as $key => $filter) {
if (!isset($filter['field']) || empty($filter['field'])) {
return ['passed' => false, 'error' => 'missing field: ' . $key];
}
}
return ['passed' => true, 'error' => ''];
}
function igny8_test_field_validation_check($config_data, $table_id) {
if (!function_exists('igny8_get_form_config')) {
return ['passed' => false, 'error' => 'function missing'];
}
$form_config = igny8_get_form_config($table_id);
if (!$form_config || !isset($form_config['fields'])) {
return ['passed' => false, 'error' => 'no form fields'];
}
foreach ($form_config['fields'] as $field) {
if (!isset($field['name']) || !isset($field['type'])) {
return ['passed' => false, 'error' => 'invalid field structure'];
}
// Test foreign key relationships
if (isset($field['type']) && $field['type'] === 'select' && (isset($field['options']) || isset($field['source']))) {
$fk_result = igny8_test_foreign_key_relationship($field, $table_id);
if (!$fk_result['passed']) {
return ['passed' => false, 'error' => 'FK issue: ' . $field['name'] . ' - ' . $fk_result['error']];
}
}
}
return ['passed' => true, 'error' => ''];
}
function igny8_test_foreign_key_relationship($field, $table_id) {
// Check if this is a foreign key field (like cluster_id, category_id, etc.)
$field_name = $field['name'];
// Common foreign key patterns
$fk_patterns = ['cluster_id', 'category_id', 'parent_id', 'user_id', 'group_id'];
$is_foreign_key = false;
foreach ($fk_patterns as $pattern) {
if (strpos($field_name, $pattern) !== false) {
$is_foreign_key = true;
break;
}
}
if (!$is_foreign_key) {
return ['passed' => true, 'error' => '']; // Not a foreign key, skip test
}
// CRITICAL ISSUE: Check if field uses 'source' with custom select rendering
if (isset($field['source']) && $field['type'] === 'select') {
// This is the cluster_id issue - custom select button vs form submission mismatch
return ['passed' => false, 'error' => 'custom select rendering - form submission mismatch'];
}
return ['passed' => true, 'error' => ''];
}
function igny8_test_query_syntax_validation($config_data, $table_id) {
if (!isset($config_data[$table_id])) {
return ['passed' => false, 'error' => 'no kpi config'];
}
$kpis = $config_data[$table_id];
foreach ($kpis as $key => $kpi) {
if (!isset($kpi['query']) || empty($kpi['query'])) {
return ['passed' => false, 'error' => 'missing query: ' . $key];
}
// Check for basic SQL syntax
if (!preg_match('/SELECT.*FROM.*\{table_name\}/i', $kpi['query'])) {
return ['passed' => false, 'error' => 'invalid query syntax: ' . $key];
}
}
return ['passed' => true, 'error' => ''];
}
/**
* Track Routing State
*/
function igny8_track_routing_state() {
$module_info = igny8_get_current_module_info();
$current_page = $_GET['page'] ?? '';
$current_submodule = $_GET['sm'] ?? '';
$routing_ok = !empty($current_page) && strpos($current_page, 'igny8-') === 0;
$submodule_ok = !empty($current_submodule);
return [
'status' => $routing_ok ? 'success' : 'error',
'message' => "Routing: {$current_page}" . ($submodule_ok ? "{$current_submodule}" : ""),
'details' => "Table ID: {$module_info['table_id']}"
];
}
/**
* Track Initial Render State
*/
function igny8_track_initial_render_state() {
$module_info = igny8_get_current_module_info();
$table_id = $module_info['table_id'];
$render_functions = [
'igny8_render_filters' => 'Filters',
'igny8_render_table_actions' => 'Actions',
'igny8_render_pagination' => 'Pagination'
];
$working_functions = 0;
$total_functions = count($render_functions);
$details = [];
foreach ($render_functions as $function => $name) {
if (function_exists($function)) {
$working_functions++;
$details[] = "{$name}: ✅";
} else {
$details[] = "{$name}: ❌";
}
}
// Test form rendering system
$form_tests = igny8_test_form_rendering_system($table_id);
$details[] = "Form Rendering: " . ($form_tests['passed'] ? '✅' : '❌ ' . $form_tests['error']);
if ($form_tests['passed']) {
$working_functions++;
}
$total_functions++;
return [
'status' => $working_functions === $total_functions ? 'success' : 'warning',
'message' => "Render functions: {$working_functions}/{$total_functions}",
'details' => implode('<br>', $details)
];
}
/**
* Test form rendering system
*/
function igny8_test_form_rendering_system($table_id) {
// Test 1: Form config function exists
if (!function_exists('igny8_get_form_config')) {
return ['passed' => false, 'error' => 'igny8_get_form_config missing'];
}
// Test 2: Form rendering function exists
if (!function_exists('igny8_render_inline_form_row')) {
return ['passed' => false, 'error' => 'igny8_render_inline_form_row missing'];
}
// Test 3: Form field rendering functions exist
$field_functions = [
'igny8_render_form_field',
'igny8_render_text_field',
'igny8_render_number_field',
'igny8_render_select_field',
'igny8_render_textarea_field'
];
foreach ($field_functions as $func) {
if (!function_exists($func)) {
return ['passed' => false, 'error' => "{$func} missing"];
}
}
// Test 4: Form config loads for current table
try {
$form_config = igny8_get_form_config($table_id);
if (!$form_config) {
return ['passed' => false, 'error' => 'No form config for table'];
}
if (empty($form_config['fields'])) {
return ['passed' => false, 'error' => 'Empty form fields'];
}
// Test 5: Form can render without errors
$test_output = igny8_render_inline_form_row($table_id, 'add', []);
if (empty($test_output) || strpos($test_output, 'Form not configured') !== false) {
return ['passed' => false, 'error' => 'Form render failed'];
}
} catch (Exception $e) {
return ['passed' => false, 'error' => 'Form config error: ' . $e->getMessage()];
}
return ['passed' => true, 'error' => ''];
}
/**
* Track Table Rendering State
*/
function igny8_track_table_rendering_state() {
$module_info = igny8_get_current_module_info();
$table_id = $module_info['table_id'];
// Test 1: Table render function exists
if (!function_exists('igny8_render_table')) {
return [
'status' => 'error',
'message' => 'Table render function missing',
'details' => 'igny8_render_table function not found'
];
}
// Test 2: AJAX table loading functions exist
$ajax_functions = [
'igny8_get_table_data' => 'Main table data loader',
'igny8_ajax_load_table_data' => 'Submodule table loader'
];
$working_ajax = 0;
$total_ajax = count($ajax_functions);
$ajax_details = [];
foreach ($ajax_functions as $func => $name) {
if (function_exists($func)) {
$working_ajax++;
$ajax_details[] = "{$name}: ✅";
} else {
$ajax_details[] = "{$name}: ❌";
}
}
// Test 3: Check if table has actually loaded data
$table_data_status = igny8_check_table_data_loaded($table_id);
// Test 4: AJAX states tracking (only meaningful after AJAX has run)
$ajax_states = igny8_track_ajax_table_states();
$total_tests = 4;
$passed_tests = ($working_ajax === $total_ajax ? 1 : 0) +
($table_data_status['passed'] ? 1 : 0) +
($ajax_states['passed'] ? 1 : 0) + 1; // +1 for function existence
$details = [
'Function exists: ✅',
'AJAX functions: ' . $working_ajax . '/' . $total_ajax,
implode('<br>', $ajax_details),
'Table data loaded: ' . ($table_data_status['passed'] ? '✅' : '⏳ ' . $table_data_status['message']),
'AJAX states: ' . ($ajax_states['passed'] ? '✅' : '⏳ ' . $ajax_states['error']),
$ajax_states['details']
];
return [
'status' => $passed_tests === $total_tests ? 'success' : ($passed_tests > 2 ? 'warning' : 'error'),
'message' => "Table rendering: {$passed_tests}/{$total_tests} tests passed",
'details' => implode('<br>', $details)
];
}
/**
* Check if table has actually loaded data
*/
function igny8_check_table_data_loaded($table_id) {
// Check if there's recent AJAX response data
if (isset($GLOBALS['igny8_last_ajax_response'])) {
$response = $GLOBALS['igny8_last_ajax_response'];
$time_diff = time() - $response['timestamp'];
// If response is recent (within 30 seconds) and has data
if ($time_diff < 30 && !empty($response['data']['items'])) {
return [
'passed' => true,
'message' => 'Data loaded (' . count($response['data']['items']) . ' rows)'
];
}
}
// Check if AJAX states indicate successful loading
if (isset($GLOBALS['igny8_debug_states']['TABLE_AJAX_RESPONSE_OK'])) {
$state = $GLOBALS['igny8_debug_states']['TABLE_AJAX_RESPONSE_OK'];
if ($state['status'] === true) {
return [
'passed' => true,
'message' => 'AJAX response successful'
];
}
}
// If no data loaded yet
return [
'passed' => false,
'message' => 'Waiting for AJAX data load'
];
}
/**
* Debug state helper function - stores debug states in globals
*/
function igny8_debug_state($stage, $ok, $msg) {
if (!isset($GLOBALS['igny8_debug_states'])) {
$GLOBALS['igny8_debug_states'] = [];
}
$GLOBALS['igny8_debug_states'][$stage] = [
'status' => $ok,
'message' => $msg,
'timestamp' => time()
];
}
/**
* Track AJAX table states
*/
function igny8_track_ajax_table_states() {
// The 4 AJAX states that indicate successful table loading
$ajax_states = [
'AJAX_NONCE_VALIDATED' => false,
'USER_CAPABILITY_OK' => false,
'TABLE_AJAX_RESPONSE_OK' => false,
'TABLE_AJAX_REQUEST_SENT' => false
];
$details = [];
$passed_states = 0;
// Check each AJAX state
foreach ($ajax_states as $state_name => $state_value) {
// Check if this state has been set by AJAX functions
if (isset($GLOBALS['igny8_debug_states'][$state_name])) {
$state_data = $GLOBALS['igny8_debug_states'][$state_name];
if ($state_data['status'] === true) {
$passed_states++;
$details[] = "{$state_name}: ✅";
} else {
$details[] = "{$state_name}: ❌ " . $state_data['message'];
}
} else {
$details[] = "{$state_name}: ⏳ Not triggered yet";
}
}
// Check if AJAX tracking system is available
if (!function_exists('igny8_debug_state')) {
return [
'passed' => false,
'error' => 'AJAX tracking system not available',
'details' => implode('<br>', $details)
];
}
// If no states have been triggered yet (page just loaded)
if ($passed_states === 0 && !isset($GLOBALS['igny8_debug_states'])) {
return [
'passed' => true,
'error' => 'AJAX tracking ready (no requests yet)',
'details' => implode('<br>', $details)
];
}
return [
'passed' => $passed_states === 4,
'error' => $passed_states === 4 ? '' : "Only {$passed_states}/4 states passed",
'details' => implode('<br>', $details)
];
}
/**
* Track Filters State
*/
function igny8_track_filters_state() {
$module_info = igny8_get_current_module_info();
$table_id = $module_info['table_id'];
// Test 1: Filter render function exists
if (!function_exists('igny8_render_filters')) {
return [
'status' => 'error',
'message' => 'Filter render function missing',
'details' => 'igny8_render_filters function not found'
];
}
// Test 2: Filter config loads for current table (direct method)
try {
// Load filters config directly like igny8_render_filters() does
$filters_config_path = plugin_dir_path(__FILE__) . '../modules/config/filters-config.php';
if (!file_exists($filters_config_path)) {
return [
'status' => 'error',
'message' => 'Filter config file missing',
'details' => 'filters-config.php not found at: ' . $filters_config_path
];
}
$filters_config = require $filters_config_path;
$filter_config = $filters_config[$table_id] ?? [];
if (empty($filter_config)) {
return [
'status' => 'warning',
'message' => 'No filter config for table',
'details' => 'Filter config not found for: ' . $table_id
];
}
$filter_count = count($filter_config);
} catch (Exception $e) {
return [
'status' => 'error',
'message' => 'Filter config error',
'details' => 'Error loading filter config: ' . $e->getMessage()
];
}
// Test 4: Filter can render without errors
try {
$test_output = igny8_render_filters($table_id);
if (empty($test_output) || strpos($test_output, 'Filter not configured') !== false) {
$render_test = '❌ Filter render failed';
} else {
$render_test = '✅ Filter renders OK';
}
} catch (Exception $e) {
$render_test = '❌ Render error: ' . $e->getMessage();
}
$details = [
'Render function: ✅',
'Config file: ✅',
'Filter config: ✅ (' . $filter_count . ' filters)',
'Render test: ' . $render_test
];
return [
'status' => 'success',
'message' => "Filters: {$filter_count} filters configured",
'details' => implode('<br>', $details)
];
}
/**
* Track Forms State
*/
function igny8_track_forms_state() {
$module_info = igny8_get_current_module_info();
$table_id = $module_info['table_id'];
// Test 1: Form render function exists
if (!function_exists('igny8_render_inline_form_row')) {
return [
'status' => 'error',
'message' => 'Form render function missing',
'details' => 'igny8_render_inline_form_row function not found'
];
}
// Test 2: Form config function exists
if (!function_exists('igny8_get_form_config')) {
return [
'status' => 'error',
'message' => 'Form config function missing',
'details' => 'igny8_get_form_config function not found'
];
}
// Test 3: Form config loads for current table
try {
$form_config = igny8_get_form_config($table_id);
if (!$form_config) {
return [
'status' => 'warning',
'message' => 'No form config for table',
'details' => 'Form config not found for: ' . $table_id
];
}
if (empty($form_config['fields'])) {
return [
'status' => 'warning',
'message' => 'Empty form fields',
'details' => 'Form config exists but has no fields'
];
}
$field_count = count($form_config['fields']);
} catch (Exception $e) {
return [
'status' => 'error',
'message' => 'Form config error',
'details' => 'Error loading form config: ' . $e->getMessage()
];
}
// Test 4: Form can render without errors
try {
$test_output = igny8_render_inline_form_row($table_id, 'add', []);
if (empty($test_output) || strpos($test_output, 'Form not configured') !== false) {
$render_test = '❌ Form render failed';
} else {
$render_test = '✅ Form renders OK';
}
} catch (Exception $e) {
$render_test = '❌ Render error: ' . $e->getMessage();
}
$details = [
'Function exists: ✅',
'Config function: ✅',
'Form config: ✅ (' . $field_count . ' fields)',
'Render test: ' . $render_test
];
return [
'status' => 'success',
'message' => "Forms: {$field_count} fields configured",
'details' => implode('<br>', $details)
];
}
/**
* Track Automation Validation State - Submodule Specific
*/
function igny8_track_automation_validation_state() {
$module_info = igny8_get_current_module_info();
$current_submodule = $module_info['submodule'];
$automation_errors = [];
$automation_functions = [];
$working_functions = 0;
try {
// Define submodule-specific automation functions
switch ($current_submodule) {
case 'keywords':
$automation_functions = [
'igny8_update_cluster_metrics' => 'Cluster metrics update',
'igny8_handle_keyword_cluster_update' => 'Keyword cluster updates',
'igny8_bulk_delete_keywords' => 'Bulk keyword deletion',
'igny8_bulk_map_keywords' => 'Bulk keyword mapping',
'igny8_bulk_unmap_keywords' => 'Bulk keyword unmapping',
'igny8_ajax_ai_cluster_keywords' => 'AI cluster creation',
'igny8_workflow_triggers' => 'Workflow triggers',
'igny8_ajax_import_keywords' => 'Keyword import automation'
];
break;
case 'clusters':
$automation_functions = [
'igny8_update_cluster_metrics' => 'Cluster metrics update',
'igny8_auto_create_cluster_term' => 'Cluster taxonomy creation',
'igny8_auto_update_cluster_term' => 'Cluster taxonomy updates',
'igny8_handle_content_cluster_association' => 'Content cluster associations',
'igny8_ajax_ai_generate_ideas' => 'AI idea generation',
'igny8_workflow_triggers' => 'Workflow triggers'
];
break;
case 'ideas':
$automation_functions = [
'igny8_update_idea_metrics' => 'Idea metrics update',
'igny8_create_task_from_idea' => 'Task creation from ideas',
'igny8_workflow_triggers' => 'Workflow triggers',
'igny8_write_log' => 'Automation logging',
'igny8_ajax_ai_generate_ideas' => 'AI idea generation',
'igny8_ajax_ai_generate_content' => 'AI content generation'
];
break;
default:
// Fallback to basic automation functions
$automation_functions = [
'igny8_update_cluster_metrics' => 'Cluster metrics update',
'igny8_handle_keyword_cluster_update' => 'Keyword cluster updates',
'igny8_workflow_triggers' => 'Workflow triggers'
];
}
// Check if each automation function exists
$function_details = [];
foreach ($automation_functions as $function => $description) {
if (function_exists($function)) {
$working_functions++;
$function_details[] = "{$description}: ✅";
} else {
// Check if this is a known missing function
if (strpos($description, 'MISSING') !== false) {
$automation_errors[] = "{$description}: ❌ (Function never implemented - referenced in docs but missing from code)";
$function_details[] = "{$description}: ❌ (NOT IMPLEMENTED)";
} else {
$automation_errors[] = "{$description}: ❌ ({$function} missing)";
$function_details[] = "{$description}: ❌";
}
}
}
$total_functions = count($automation_functions);
} catch (Exception $e) {
$automation_errors[] = 'Automation check error: ' . $e->getMessage();
}
if (empty($automation_errors)) {
return [
'status' => 'success',
'message' => "Automation: {$working_functions}/{$total_functions} functions active",
'details' => implode('<br>', $function_details)
];
} else {
return [
'status' => $working_functions > ($total_functions / 2) ? 'warning' : 'error',
'message' => "Automation: {$working_functions}/{$total_functions} functions active",
'details' => implode('<br>', $function_details) . '<br><br>Issues:<br>' . implode('<br>', $automation_errors)
];
}
}
/**
* Track Database Pre-Fetch State
*/
function igny8_track_db_prefetch_state() {
global $wpdb;
$module_info = igny8_get_current_module_info();
// Get table name
if (function_exists('igny8_get_table_name')) {
$table_name = igny8_get_table_name($module_info['table_id']);
} else {
$table_name = $wpdb->prefix . 'igny8_' . str_replace('_', '', $module_info['table_id']);
}
// Check if table exists
$table_exists = $wpdb->get_var("SHOW TABLES LIKE '{$table_name}'") !== null;
if ($table_exists) {
$row_count = $wpdb->get_var("SELECT COUNT(*) FROM {$table_name}");
return [
'status' => 'success',
'message' => "Table: {$table_name} ({$row_count} rows)",
'details' => "Database connection: ✅<br>Table exists: ✅"
];
} else {
return [
'status' => 'error',
'message' => "Table not found: {$table_name}",
'details' => "Database connection: " . (!empty($wpdb->dbh) ? '✅' : '❌') . "<br>Table exists: ❌"
];
}
}
/**
* Track Frontend Initialization State
*/
function igny8_track_frontend_init_state() {
// Check if core.js is enqueued
$core_js_enqueued = wp_script_is('igny8-admin-js', 'enqueued');
// Check if required DOM elements exist (this will be checked by JavaScript)
return [
'status' => $core_js_enqueued ? 'success' : 'warning',
'message' => "Frontend JS: " . ($core_js_enqueued ? 'Enqueued' : 'Not enqueued'),
'details' => "Core.js loaded: " . ($core_js_enqueued ? '✅' : '❌') . "<br>DOM elements: Checked by JS"
];
}
// Get module debug content using consolidated evidence system
function igny8_get_module_debug_content() {
// Get current module information
$module_info = igny8_get_current_module_info();
$module_name = ucfirst($module_info['module']);
$current_submodule = $module_info['submodule'];
// Track debug states - split into component states and automation states
$component_states = [
'database' => igny8_track_database_validation_state(),
'table' => igny8_track_initial_render_state(),
'filters' => igny8_track_filters_state(),
'forms' => igny8_track_forms_state()
];
$automation_states = [
'automation' => igny8_track_automation_validation_state(),
'ai_logs' => igny8_track_ai_logs_state($module_info)
];
ob_start();
?>
<?php
$debug_enabled = get_option('igny8_debug_enabled', false);
?>
<div class="igny8-card igny8-mb-20" id="igny8-module-debug-container" style="display: <?php echo $debug_enabled ? 'block' : 'none'; ?>;">
<div class="igny8-card-header">
<div class="igny8-card-title">
<span style="font-size: 20px; margin-right: 8px;">🔍</span>
Module Debug: <?php echo esc_html($module_name); ?>
</div>
<button class="igny8-btn igny8-btn-secondary" id="toggle-module-debug-btn">Toggle Debug</button>
</div>
<div class="igny8-card-body" id="module-debug-body">
<!-- Module Information -->
<div class="igny8-flex" style="justify-content: space-between; align-items: center; margin-bottom: 20px;">
<div>
<h4>Module Information</h4>
<div class="igny8-flex" style="background: #f0f0f0; padding: 10px; border-radius: 4px; margin-top: 10px; font-family: monospace; font-size: 12px;">
<strong>Module:</strong> <?php echo esc_html($module_info['module']); ?><br>
<strong>Submodule:</strong> <?php echo esc_html($module_info['submodule'] ?: 'NONE'); ?><br>
<strong>Table ID:</strong> <?php echo esc_html($module_info['table_id']); ?><br>
<strong>Page:</strong> <?php echo esc_html($module_info['page']); ?>
</div>
</div>
</div>
<!-- Component States -->
<div class="igny8-debug-section">
<h4>Submodule Components Render</h4>
<div class="igny8-grid-4">
<?php foreach ($component_states as $state_key => $state_data): ?>
<div class="igny8-debug-item" style="padding: 10px; border: 1px solid #ddd; border-radius: 4px; background: <?php echo $state_data['status'] === 'success' ? '#d4edda' : ($state_data['status'] === 'warning' ? '#fff3cd' : '#f8d7da'); ?>;">
<div style="font-weight: bold; margin-bottom: 5px;">
<?php echo ucwords(str_replace('_', ' ', $state_key)); ?>
<span style="color: <?php echo $state_data['status'] === 'success' ? 'green' : ($state_data['status'] === 'warning' ? 'orange' : 'red'); ?>;">
<?php echo $state_data['status'] === 'success' ? '✅' : ($state_data['status'] === 'warning' ? '⚠️' : '❌'); ?>
</span>
</div>
<div style="font-size: 12px; margin-bottom: 3px;">
<?php echo esc_html($state_data['message']); ?>
</div>
<div style="font-size: 11px; color: #666;">
<?php echo $state_data['details']; ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- Automation States -->
<div class="igny8-debug-section" style="margin-top: 20px;">
<h4>Automation & AI Systems</h4>
<div class="igny8-grid-5">
<?php foreach ($automation_states as $state_key => $state_data): ?>
<?php if ($state_key === 'ai_logs') continue; // Skip AI logs, will be displayed separately below ?>
<div class="igny8-debug-item" style="padding: 10px; border: 1px solid #ddd; border-radius: 4px; background: <?php echo $state_data['status'] === 'success' ? '#d4edda' : ($state_data['status'] === 'warning' ? '#fff3cd' : '#f8d7da'); ?>;">
<div style="font-weight: bold; margin-bottom: 5px;">
<?php echo ucwords(str_replace('_', ' ', $state_key)); ?>
<span style="color: <?php echo $state_data['status'] === 'success' ? 'green' : ($state_data['status'] === 'warning' ? 'orange' : 'red'); ?>;">
<?php echo $state_data['status'] === 'success' ? '✅' : ($state_data['status'] === 'warning' ? '⚠️' : '❌'); ?>
</span>
</div>
<div style="font-size: 12px; margin-bottom: 3px;">
<?php echo esc_html($state_data['message']); ?>
</div>
<div style="font-size: 11px; color: #666;">
<?php echo $state_data['details']; ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
</div>
<!-- AI Logs Section (Standalone, below debug card) -->
<div class="igny8-card igny8-mb-20" id="igny8-ai-logs-container" style="display: <?php echo $debug_enabled ? 'block' : 'none'; ?>;">
<div class="igny8-card-header">
<div class="igny8-card-title">
<span style="font-size: 20px; margin-right: 8px;">🤖</span>
AI Logs & Events
</div>
<div style="display: flex; gap: 8px;">
<button id="refresh-ai-logs" class="igny8-btn igny8-btn-secondary igny8-btn-sm">
<span class="dashicons dashicons-update"></span> Refresh
</button>
<button id="clear-ai-logs" class="igny8-btn igny8-btn-outline igny8-btn-sm">
<span class="dashicons dashicons-trash"></span> Clear
</button>
</div>
</div>
<div class="igny8-card-body" style="padding: 20px;">
<div style="display: flex; gap: 10px; margin-bottom: 15px; font-size: 13px;">
<div style="flex: 1; padding: 10px; background: #f8f9fa; border-radius: 4px;">
<strong>Status:</strong> <span id="ai-logs-message">Loading...</span>
</div>
<div style="flex: 2; padding: 10px; background: #f8f9fa; border-radius: 4px;">
<span id="ai-logs-details">Initializing...</span>
</div>
</div>
<!-- AI Events Container with persistent log boxes -->
<div id="ai-logs-events" style="max-height: 400px; overflow-y: auto; padding: 10px; background: #fff; border: 1px solid #ddd; border-radius: 4px;">
<div style="text-align: center; color: #666; padding: 20px;">
<span class="dashicons dashicons-update" style="animation: spin 2s linear infinite; font-size: 24px;"></span>
<br><br>Loading AI events...
</div>
</div>
</div>
</div>
<!-- Image Generation Logs Section (only on drafts page) -->
<?php if ($current_submodule === 'drafts'): ?>
<div class="igny8-card igny8-mb-20" id="igny8-image-gen-logs-container" style="display: <?php echo $debug_enabled ? 'block' : 'none'; ?>;">
<div class="igny8-card-header">
<div class="igny8-card-title">
<span style="font-size: 20px; margin-right: 8px;">🎨</span>
Image Generation Logs
</div>
<div style="display: flex; gap: 8px;">
<button id="refresh-image-gen" class="igny8-btn igny8-btn-secondary igny8-btn-sm">
<span class="dashicons dashicons-update"></span> Refresh
</button>
<button id="clear-image-gen" class="igny8-btn igny8-btn-outline igny8-btn-sm">
<span class="dashicons dashicons-trash"></span> Clear
</button>
</div>
</div>
<div class="igny8-card-body" style="padding: 20px;">
<div style="display: flex; gap: 10px; margin-bottom: 15px; font-size: 13px;">
<div style="flex: 1; padding: 10px; background: #f8f9fa; border-radius: 4px;">
<strong>Status:</strong> <span id="image-gen-message">Ready for image generation</span>
</div>
<div style="flex: 2; padding: 10px; background: #f8f9fa; border-radius: 4px;">
<span id="image-gen-details">Waiting for image generation process</span>
</div>
</div>
<!-- Image Generation Events Container with persistent log boxes -->
<div id="image-gen-events" style="max-height: 400px; overflow-y: auto; padding: 10px; background: #fff; border: 1px solid #ddd; border-radius: 4px;">
<div style="text-align: center; color: #666; padding: 20px;">
<span class="dashicons dashicons-format-image" style="font-size: 24px;"></span>
<br><br>No image generation events yet
</div>
</div>
</div>
</div>
<?php endif; ?>
<script>
// Module Debug Toggle Functionality with localStorage
document.addEventListener('DOMContentLoaded', function() {
const toggleBtn = document.getElementById('toggle-module-debug-btn');
const debugBody = document.getElementById('module-debug-body');
const aiLogsContainer = document.getElementById('igny8-ai-logs-container');
const imageGenContainer = document.getElementById('igny8-image-gen-logs-container');
// Load saved state from localStorage
const debugState = localStorage.getItem('igny8_module_debug_state');
if (debugState === 'collapsed') {
debugBody.style.display = 'none';
}
// Toggle functionality
if (toggleBtn && debugBody) {
toggleBtn.addEventListener('click', function() {
if (debugBody.style.display === 'none') {
debugBody.style.display = 'block';
localStorage.setItem('igny8_module_debug_state', 'expanded');
} else {
debugBody.style.display = 'none';
localStorage.setItem('igny8_module_debug_state', 'collapsed');
}
});
}
// Real-time AI Logs functionality
const aiLogsMessage = document.getElementById('ai-logs-message');
const aiLogsDetails = document.getElementById('ai-logs-details');
const aiLogsEvents = document.getElementById('ai-logs-events');
const refreshBtn = document.getElementById('refresh-ai-logs');
const clearBtn = document.getElementById('clear-ai-logs');
if (!aiLogsEvents) return;
let refreshInterval;
// Load AI logs and stats
function loadAILogs() {
const ajaxUrl = window.ajaxurl || (window.IGNY8_PAGE?.ajaxUrl) || '/wp-admin/admin-ajax.php';
fetch(ajaxUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'action=igny8_get_ai_logs&nonce=' + (window.IGNY8_PAGE?.nonce || '')
})
.then(response => response.json())
.then(data => {
if (data.success) {
updateAIStats(data.data);
displayAIEvents(data.data);
} else {
console.error('Error loading AI logs:', data.data);
}
})
.catch(error => {
console.error('Error loading AI logs:', error);
});
}
// Expose refresh function globally for other scripts
window.refreshAILogs = loadAILogs;
// Update module context dynamically based on current URL
function updateModuleContext() {
const urlParams = new URLSearchParams(window.location.search);
const page = urlParams.get('page') || '';
const submodule = urlParams.get('sm') || '';
// Extract module from page parameter
let module = 'planner'; // default
if (page && page.startsWith('igny8-')) {
module = page.replace('igny8-', '');
}
// Update the module context display
if (aiLogsDetails) {
const details = aiLogsDetails.innerHTML;
const updatedDetails = details.replace(
/Module: [^<]*/g,
`Module: ${module}`
).replace(
/Submodule: [^<]*/g,
`Submodule: ${submodule || 'NONE'}`
);
aiLogsDetails.innerHTML = updatedDetails;
}
}
// Update AI stats in real-time
function updateAIStats(logs) {
const logCount = logs.length;
let errorCount = 0;
let successCount = 0;
let infoCount = 0;
logs.forEach(log => {
if (log.status === 'error') errorCount++;
else if (log.status === 'success') successCount++;
else infoCount++;
});
// Update module context dynamically
updateModuleContext();
// Update message
if (aiLogsMessage) {
if (logCount > 0) {
aiLogsMessage.textContent = `AI Logs: ${logCount} events (${successCount} success, ${errorCount} errors)`;
} else {
aiLogsMessage.textContent = 'AI Logs: Ready (no events yet)';
}
}
// Update details
if (aiLogsDetails) {
const aiMode = 'AI Mode: ✅ Enabled'; // This could be dynamic
const events = `Recent Events: ${logCount}`;
const stats = `Success: ${successCount} | Errors: ${errorCount}`;
const module = `Module: planner`; // This could be dynamic
const submodule = `Submodule: keywords`; // This could be dynamic
aiLogsDetails.innerHTML = `${aiMode}<br>${events}<br>${stats}<br>${module}<br>${submodule}`;
}
}
// Display AI events in real-time
function displayAIEvents(logs) {
if (!logs || logs.length === 0) {
aiLogsEvents.innerHTML = '<div style="text-align: center; color: #666; padding: 10px;">No AI events logged yet</div>';
return;
}
// Get current page context
const urlParams = new URLSearchParams(window.location.search);
const currentPage = urlParams.get('page') || '';
const currentSubmodule = urlParams.get('sm') || '';
// Filter logs based on current page
let filteredLogs = logs;
// On writer tasks page, only show content_generation logs
if (currentPage === 'igny8-writer' && currentSubmodule === 'tasks') {
filteredLogs = logs.filter(log => log.action === 'content_generation');
}
// On writer drafts page, only show image generation logs (handled by separate card)
else if (currentPage === 'igny8-writer' && currentSubmodule === 'drafts') {
filteredLogs = logs.filter(log => log.action !== 'content_generation');
}
if (filteredLogs.length === 0) {
aiLogsEvents.innerHTML = '<div style="text-align: center; color: #666; padding: 10px;">No relevant AI events for this page</div>';
return;
}
let html = '';
filteredLogs.slice(0, 20).forEach((log, index) => {
const timestamp = new Date(log.timestamp).toLocaleTimeString();
const statusColor = log.status === 'success' ? '#28a745' : log.status === 'error' ? '#dc3545' : '#ffc107';
const statusBg = log.status === 'success' ? '#d4edda' : log.status === 'error' ? '#f8d7da' : '#fff3cd';
const statusIcon = log.status === 'success' ? '✓' : log.status === 'error' ? '✗' : 'ⓘ';
html += `
<div style="background: ${statusBg}; border-left: 4px solid ${statusColor}; padding: 12px; margin-bottom: 10px; border-radius: 4px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 6px;">
<span style="color: ${statusColor}; font-weight: bold; font-size: 16px;">${statusIcon}</span>
<strong style="font-size: 13px; flex: 1;">${log.event}</strong>
<span style="font-size: 11px; color: #666;">${timestamp}</span>
</div>
<div style="font-size: 11px; color: #666; margin-left: 24px; margin-bottom: 4px;">
<strong>Module:</strong> ${log.module} | <strong>Action:</strong> ${log.action}
</div>
${log.message ? `<div style="font-size: 12px; color: #333; margin-left: 24px; padding: 6px; background: rgba(255,255,255,0.5); border-radius: 3px;">${log.message}</div>` : ''}
</div>
`;
});
aiLogsEvents.innerHTML = html;
}
// Event listeners
if (refreshBtn) {
refreshBtn.addEventListener('click', function() {
loadAILogs();
});
}
if (clearBtn) {
clearBtn.addEventListener('click', function() {
if (confirm('Are you sure you want to clear all AI logs?')) {
const ajaxUrl = window.ajaxurl || (window.IGNY8_PAGE?.ajaxUrl) || '/wp-admin/admin-ajax.php';
fetch(ajaxUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'action=igny8_clear_ai_logs&nonce=' + (window.IGNY8_PAGE?.nonce || '')
})
.then(response => response.json())
.then(data => {
if (data.success) {
loadAILogs();
}
})
.catch(error => console.error('Error clearing AI logs:', error));
}
});
}
// Initial load
updateModuleContext(); // Update context first
loadAILogs();
// Auto-refresh every 5 seconds for real-time updates
refreshInterval = setInterval(loadAILogs, 5000);
// Clean up interval when page unloads
window.addEventListener('beforeunload', function() {
if (refreshInterval) {
clearInterval(refreshInterval);
}
});
});
// Image Generation Debug functionality (only on drafts page)
document.addEventListener('DOMContentLoaded', function() {
const imageGenMessage = document.getElementById('image-gen-message');
const imageGenDetails = document.getElementById('image-gen-details');
const imageGenEvents = document.getElementById('image-gen-events');
const refreshImageGenBtn = document.getElementById('refresh-image-gen');
const clearImageGenBtn = document.getElementById('clear-image-gen');
// Only initialize if we're on drafts page (image-gen-events element exists)
if (!imageGenEvents) return;
let imageGenRefreshInterval;
// Load image generation events
function loadImageGenEvents() {
// For now, just show placeholder - will be implemented later
if (imageGenMessage) {
imageGenMessage.textContent = 'Image generation debug ready';
}
if (imageGenDetails) {
imageGenDetails.textContent = 'Status: Waiting for image generation process';
}
}
// Add image generation debug log function (exposed globally)
window.addImageGenDebugLog = function(level, message, data = null) {
const timestamp = new Date().toLocaleTimeString();
const statusColor = level === 'SUCCESS' ? '#28a745' : level === 'ERROR' ? '#dc3545' : '#0073aa';
const statusIcon = level === 'SUCCESS' ? '✓' : level === 'ERROR' ? '✗' : 'ⓘ';
// Build data display
let dataDisplay = '';
if (data) {
if (data.postTitle) {
dataDisplay = `Post: ${data.postTitle} (ID: ${data.postId})`;
} else if (data.attachmentId) {
dataDisplay = `Attachment ID: ${data.attachmentId}, Provider: ${data.provider || 'N/A'}`;
} else if (data.queuePosition) {
dataDisplay = `Queue Position: ${data.queuePosition}`;
if (data.type) dataDisplay += `, Type: ${data.type}`;
if (data.device && data.device !== 'N/A') dataDisplay += `, Device: ${data.device}`;
if (data.index) dataDisplay += `, Index: ${data.index}`;
if (data.postId) dataDisplay += `, Task ID: ${data.postId}`;
} else if (data.postIds) {
dataDisplay = `Task IDs: ${Array.isArray(data.postIds) ? data.postIds.join(', ') : data.postIds}`;
} else if (data.taskIds) {
dataDisplay = `Task IDs: ${Array.isArray(data.taskIds) ? data.taskIds.join(', ') : data.taskIds}`;
} else if (data.mapping) {
const mappings = Object.entries(data.mapping).map(([taskId, postId]) => `Task ${taskId} → Post ${postId}`);
dataDisplay = mappings.join(', ');
} else if (data.desktop !== undefined) {
dataDisplay = `Desktop: ${data.desktop ? 'Yes' : 'No'}, Mobile: ${data.mobile ? 'Yes' : 'No'}, Max: ${data.maxImages}`;
} else if (data.status) {
dataDisplay = `Status: ${data.status}`;
} else if (data.error) {
dataDisplay = `Error: ${data.error}`;
} else if (data.total) {
dataDisplay = `Total Images: ${data.total}`;
} else if (data.postId) {
dataDisplay = `Task ID: ${data.postId}`;
}
}
const statusBg = level === 'SUCCESS' ? '#d4edda' : level === 'ERROR' ? '#f8d7da' : '#e7f3ff';
const eventHtml = `
<div style="background: ${statusBg}; border-left: 4px solid ${statusColor}; padding: 12px; margin-bottom: 10px; border-radius: 4px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 6px;">
<span style="color: ${statusColor}; font-weight: bold; font-size: 16px;">${statusIcon}</span>
<strong style="font-size: 13px; flex: 1;">${message}</strong>
<span style="font-size: 11px; color: #666;">${timestamp}</span>
</div>
<div style="font-size: 11px; color: #666; margin-left: 24px; margin-bottom: 4px;">
<strong>Type:</strong> Image Generation | <strong>Level:</strong> ${level}
</div>
${dataDisplay ? `<div style="font-size: 12px; color: #333; margin-left: 24px; padding: 6px; background: rgba(255,255,255,0.5); border-radius: 3px;">${dataDisplay}</div>` : ''}
</div>
`;
// Add to top of events container (prepend new logs)
const currentContent = imageGenEvents.innerHTML;
if (currentContent.includes('No image generation events yet')) {
imageGenEvents.innerHTML = eventHtml;
} else {
imageGenEvents.innerHTML = eventHtml + currentContent;
}
// Update message
if (imageGenMessage) {
imageGenMessage.textContent = 'Image generation in progress';
}
if (imageGenDetails) {
imageGenDetails.textContent = 'Status: Processing image generation events';
}
};
// Display image generation events
function displayImageGenEvents(events) {
if (!events || events.length === 0) {
imageGenEvents.innerHTML = '<div style="text-align: center; color: #666; padding: 10px;"><span class="dashicons dashicons-format-image" style="font-size: 16px;"></span><br>No image generation events yet</div>';
return;
}
let html = '';
events.slice(0, 10).forEach(event => {
const timestamp = new Date(event.timestamp).toLocaleTimeString();
const statusColor = event.status === 'success' ? '#28a745' : event.status === 'error' ? '#dc3545' : '#ffc107';
const statusIcon = event.status === 'success' ? '✓' : event.status === 'error' ? '✗' : 'ⓘ';
html += `
<div style="border-bottom: 1px solid #ddd; padding: 4px 0; margin-bottom: 4px;">
<div style="display: flex; align-items: center; gap: 6px; margin-bottom: 2px;">
<span style="color: ${statusColor}; font-weight: bold;">${statusIcon}</span>
<strong style="font-size: 11px;">${event.event}</strong>
<span style="font-size: 9px; color: #666; margin-left: auto;">${timestamp}</span>
</div>
<div style="font-size: 9px; color: #666; margin-left: 16px;">
${event.module} | ${event.action}
</div>
${event.message ? `<div style="font-size: 9px; color: #333; margin-left: 16px; margin-top: 2px;">${event.message}</div>` : ''}
</div>
`;
});
imageGenEvents.innerHTML = html;
}
// Event listeners
if (refreshImageGenBtn) {
refreshImageGenBtn.addEventListener('click', function() {
loadImageGenEvents();
});
}
if (clearImageGenBtn) {
clearImageGenBtn.addEventListener('click', function() {
if (confirm('Are you sure you want to clear all image generation events?')) {
imageGenEvents.innerHTML = '<div style="text-align: center; color: #666; padding: 10px;"><span class="dashicons dashicons-format-image" style="font-size: 16px;"></span><br>No image generation events yet</div>';
}
});
}
// Initial load
loadImageGenEvents();
// Auto-refresh every 3 seconds for image generation events
imageGenRefreshInterval = setInterval(loadImageGenEvents, 3000);
// Clean up interval when page unloads
window.addEventListener('beforeunload', function() {
if (imageGenRefreshInterval) {
clearInterval(imageGenRefreshInterval);
}
});
});
</script>
<?php
return ob_get_clean();
}
// Keep the old function for backward compatibility
function igny8_render_module_debug_widgets() {
echo igny8_get_module_debug_content();
}