Initial commit: igny8 project

This commit is contained in:
igny8
2025-11-09 10:27:02 +00:00
commit 60b8188111
27265 changed files with 4360521 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
<?php
/**
* ==============================
* 📁 Folder Scope Declaration
* ==============================
* Folder: /modules/
* Purpose: Admin UI and logic for core modules (Planner, Writer, etc.)
* Rules:
* - Must not contain reusable code
* - Must use components for forms/tables
* - No AI logic allowed here
* - Each module must be independent
* - No cross-module dependencies allowed
*/

View File

@@ -0,0 +1,44 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : analytics.php
* @location : /modules/analytics/analytics.php
* @type : Admin Page
* @scope : Module Only
* @allowed : Analytics reporting, performance metrics, data visualization
* @reusability : Single Use
* @notes : Analytics reporting page for analytics module
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Handle URL parameters for subpages
$subpage = $_GET['sp'] ?? 'analytics';
// Start output buffering
ob_start();
switch ($subpage) {
case 'analytics':
default:
// Analytics content
?>
<div class="igny8-analytics-page">
<h3>Analytics & Reporting</h3>
<p>Performance analytics and reporting functionality coming soon...</p>
</div>
<?php
break;
}
// Capture page content
$igny8_page_content = ob_get_clean();
// Include global layout
include plugin_dir_path(__FILE__) . '../../core/global-layout.php';
?>

View File

@@ -0,0 +1,14 @@
<?php
/**
* ==============================
* 📁 Folder Scope Declaration
* ==============================
* Folder: /components/
* Purpose: UI/UX templates (forms, modals, tables)
* Rules:
* - Can be reused globally across all modules
* - Contains only UI template components
* - No business logic allowed
* - Must be configuration-driven
* - Pure presentation layer only
*/

View File

@@ -0,0 +1,111 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : actions-tpl.php
* @location : /modules/components/actions-tpl.php
* @type : Component
* @scope : Cross-Module
* @allowed : Action rendering, bulk operations
* @reusability : Shared
* @notes : Dynamic action component for all modules
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Render table actions function
function igny8_render_table_actions($table_id, $actions = []) {
// Set default actions if none provided
$actions = $actions ?: ['export_selected', 'delete_selected', 'export_all', 'import', 'add_new'];
// Check if AI mode is enabled
$ai_mode = igny8_get_ai_setting('planner_mode', 'manual') === 'ai';
// Start output buffering to capture HTML
ob_start();
?>
<!-- Table Actions -->
<div class="igny8-table-actions" data-table="<?php echo esc_attr($table_id); ?>">
<div class="left-actions">
<span id="<?php echo esc_attr($table_id); ?>_count" class="igny8-count-hidden">0 selected</span>
<?php if (in_array('export_selected', $actions)): ?>
<button id="<?php echo esc_attr($table_id); ?>_export_btn" class="igny8-btn igny8-btn-success" disabled>Export Selected</button>
<?php endif; ?>
<?php if (in_array('delete_selected', $actions)): ?>
<button id="<?php echo esc_attr($table_id); ?>_delete_btn" class="igny8-btn igny8-btn-danger" disabled>Delete Selected</button>
<?php endif; ?>
<!-- Publish Selected Button (for Drafts) -->
<?php if ($table_id === 'writer_drafts'): ?>
<button id="<?php echo esc_attr($table_id); ?>_generate_images_btn" class="igny8-btn igny8-btn-accent" disabled>
<span class="dashicons dashicons-format-image"></span> Generate Images
</button>
<button id="<?php echo esc_attr($table_id); ?>_publish_btn" class="igny8-btn igny8-btn-primary" disabled>
<span class="dashicons dashicons-yes-alt"></span> Publish Selected
</button>
<?php endif; ?>
<!-- AI Action Buttons (only visible in AI mode) -->
<?php if ($ai_mode): ?>
<?php if ($table_id === 'planner_keywords' && igny8_get_ai_setting('clustering', 'enabled') === 'enabled'): ?>
<button id="<?php echo esc_attr($table_id); ?>_ai_cluster_btn" class="igny8-btn igny8-btn-accent" disabled>
<span class="dashicons dashicons-admin-generic"></span> Auto Cluster
</button>
<?php endif; ?>
<?php if ($table_id === 'planner_clusters' && igny8_get_ai_setting('ideas', 'enabled') === 'enabled'): ?>
<button id="<?php echo esc_attr($table_id); ?>_ai_ideas_btn" class="igny8-btn igny8-btn-accent" disabled>
<span class="dashicons dashicons-lightbulb"></span> Generate Ideas
</button>
<?php endif; ?>
<?php endif; ?>
<!-- Queue to Writer Button (for Ideas) -->
<?php if ($table_id === 'planner_ideas'): ?>
<button id="<?php echo esc_attr($table_id); ?>_queue_writer_btn" class="igny8-btn igny8-btn-primary" disabled>
<span class="dashicons dashicons-edit"></span> Queue to Writer
</button>
<?php endif; ?>
<?php if ($table_id === 'writer_tasks' && igny8_get_ai_setting('writer_mode', 'manual') === 'ai' && igny8_get_ai_setting('content_generation', 'enabled') === 'enabled'): ?>
<button id="<?php echo esc_attr($table_id); ?>_generate_content_btn" class="igny8-btn igny8-btn-success" disabled>
<span class="dashicons dashicons-admin-generic"></span> Generate Content
</button>
<?php endif; ?>
</div>
<div class="right-actions">
<div class="igny8-ml-auto igny8-flex igny8-flex-gap-10 igny8-align-center">
<?php if (in_array('export_all', $actions)): ?>
<button id="<?php echo esc_attr($table_id); ?>_export_all_btn" class="igny8-btn igny8-btn-outline" onclick="igny8ShowExportModal('<?php echo esc_attr($table_id); ?>')">Export All</button>
<?php endif; ?>
<?php if (in_array('import', $actions)): ?>
<button id="<?php echo esc_attr($table_id); ?>_import_btn" class="igny8-btn igny8-btn-secondary">Import</button>
<?php endif; ?>
<?php if (in_array('add_new', $actions)): ?>
<button
class="igny8-btn igny8-btn-primary"
data-action="addRow"
data-table-id="<?php echo esc_attr($table_id); ?>"
>
Add New
</button>
<?php endif; ?>
</div>
</div>
</div>
<!-- Global notification system is handled by core.js -->
<?php
return ob_get_clean();
}
// Set default values
$table_id = $table_id ?? 'data_table';
$actions = $actions ?? ['export_selected', 'delete_selected', 'export_all', 'import', 'add_new'];
?>

View File

@@ -0,0 +1,77 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : export-modal-tpl.php
* @location : /modules/components/export-modal-tpl.php
* @type : Component
* @scope : Cross-Module
* @allowed : Modal rendering, export functionality
* @reusability : Shared
* @notes : Dynamic export modal component for all modules
*/
// Prevent direct access
if (!defined('ABSPATH') && !defined('IGNY8_INCLUDE_TEMPLATE')) {
exit;
}
// Get configuration for this table
$config = igny8_get_import_export_config($table_id);
if (!$config) {
return;
}
$singular = $config['singular'];
$plural = $config['plural'];
$is_selected = !empty($selected_ids);
$mode_text = $is_selected ? "Selected {$plural}" : "All {$plural}";
?>
<div id="igny8-import-export-modal" class="igny8-modal" data-table-id="<?php echo esc_attr($table_id); ?>">
<div class="igny8-modal-content">
<div class="igny8-modal-header">
<h3>Export <?php echo esc_html($mode_text); ?></h3>
<button class="igny8-btn-close" onclick="igny8CloseImportExportModal()">&times;</button>
</div>
<div class="igny8-modal-body">
<p>Export <?php echo esc_html(strtolower($mode_text)); ?> to CSV format.</p>
<!-- Export Options -->
<div class="igny8-form-group">
<h4>Export Options</h4>
<div class="igny8-checkbox-group">
<label class="igny8-checkbox-label">
<input type="checkbox" id="include-metrics" name="include_metrics" checked>
<span class="igny8-checkbox-text">Include Metrics</span>
</label>
<label class="igny8-checkbox-label">
<input type="checkbox" id="include-relationships" name="include_relationships">
<span class="igny8-checkbox-text">Include Relationships</span>
</label>
<label class="igny8-checkbox-label">
<input type="checkbox" id="include-timestamps" name="include_timestamps">
<span class="igny8-checkbox-text">Include Timestamps</span>
</label>
</div>
</div>
<!-- Hidden form data -->
<form id="igny8-modal-export-form" style="display: none;">
<input type="hidden" name="action" value="<?php echo $is_selected ? 'igny8_export_selected' : 'igny8_run_export'; ?>">
<input type="hidden" name="nonce" value="">
<input type="hidden" name="export_type" value="<?php echo esc_attr($config['type']); ?>">
<?php if ($is_selected): ?>
<input type="hidden" name="selected_ids" value="<?php echo esc_attr(json_encode($selected_ids)); ?>">
<?php endif; ?>
</form>
</div>
<div class="igny8-modal-footer">
<button type="button" class="igny8-btn igny8-btn-secondary" onclick="igny8CloseImportExportModal()">Cancel</button>
<button type="button" class="igny8-btn igny8-btn-primary" onclick="igny8SubmitExportForm()">
<span class="dashicons dashicons-download"></span> Export <?php echo esc_html($mode_text); ?>
</button>
</div>
</div>
</div>

View File

@@ -0,0 +1,136 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : filters-tpl.php
* @location : /modules/components/filters-tpl.php
* @type : Component
* @scope : Cross-Module
* @allowed : Filter rendering, search functionality
* @reusability : Shared
* @notes : Dynamic filter component for all modules
*/
/*
* <?php igny8_render_filters('planner_clusters'); ?>
* <?php igny8_render_filters('writer_drafts'); ?>
*
* @package Igny8Compact
* @since 1.0.0
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Render filters function
function igny8_render_filters($table_id) {
// Load filters configuration
$filters_config = require plugin_dir_path(__FILE__) . '../config/filters-config.php';
$filters = $filters_config[$table_id] ?? [];
// Load table configuration to get humanize_columns
$tables_config = require plugin_dir_path(__FILE__) . '../config/tables-config.php';
$GLOBALS['igny8_tables_config'] = $tables_config;
$table_config = igny8_get_dynamic_table_config($table_id);
$humanize_columns = $table_config['humanize_columns'] ?? [];
// Set variables for component
$module = explode('_', $table_id)[0];
$tab = explode('_', $table_id)[1] ?? '';
// Debug: Log filters array for verification
// Start output buffering to capture HTML
ob_start();
?>
<!-- Filters HTML -->
<div class="igny8-filters igny8-mb-20" data-table="<?php echo esc_attr($table_id); ?>">
<div class="igny8-filter-bar">
<?php foreach ($filters as $filter_key => $filter_config): ?>
<div class="igny8-filter-group">
<?php if ($filter_config['type'] === 'search'): ?>
<!-- Search Input -->
<input type="text"
class="igny8-search-input igny8-input-md"
id="<?php echo esc_attr($table_id); ?>_filter_<?php echo esc_attr($filter_key); ?>"
placeholder="<?php echo esc_attr($filter_config['placeholder'] ?? 'Search...'); ?>"
data-filter="<?php echo esc_attr($filter_key); ?>">
<?php elseif ($filter_config['type'] === 'select'): ?>
<!-- Dropdown Filter -->
<div class="select" data-filter="<?php echo esc_attr($filter_key); ?>">
<button class="select-btn" id="<?php echo esc_attr($table_id); ?>_filter_<?php echo esc_attr($filter_key); ?>_btn" data-value="">
<span class="select-text"><?php echo esc_html($filter_config['label'] ?? (in_array($filter_config['field'] ?? $filter_key, $humanize_columns) ? igny8_humanize_label($filter_config['field'] ?? $filter_key) : ucfirst($filter_key))); ?></span>
<span class="dd-arrow">▼</span>
</button>
<div class="select-list" id="<?php echo esc_attr($table_id); ?>_filter_<?php echo esc_attr($filter_key); ?>_list">
<div class="select-item" data-value="">All <?php echo esc_html($filter_config['label'] ?? (in_array($filter_config['field'] ?? $filter_key, $humanize_columns) ? igny8_humanize_label($filter_config['field'] ?? $filter_key) : ucfirst($filter_key))); ?></div>
<?php if (isset($filter_config['options'])): ?>
<?php if (is_array($filter_config['options'])): ?>
<?php foreach ($filter_config['options'] as $value => $label): ?>
<div class="select-item" data-value="<?php echo esc_attr($value); ?>"><?php echo esc_html($label); ?></div>
<?php endforeach; ?>
<?php elseif ($filter_config['options'] === 'dynamic_clusters'): ?>
<?php
// Load cluster options dynamically
$cluster_options = igny8_get_cluster_options();
if ($cluster_options) {
foreach ($cluster_options as $option) {
if (!empty($option['value'])) { // Skip "No Cluster" option
echo '<div class="select-item" data-value="' . esc_attr($option['value']) . '">' . esc_html($option['label']) . '</div>';
}
}
}
?>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
<?php elseif ($filter_config['type'] === 'range'): ?>
<!-- Numeric Range Filter -->
<div class="select" data-filter="<?php echo esc_attr($filter_key); ?>">
<button class="select-btn" id="<?php echo esc_attr($table_id); ?>_filter_<?php echo esc_attr($filter_key); ?>_btn" data-value="">
<span class="select-text"><?php echo esc_html($filter_config['label'] ?? (in_array($filter_config['field'] ?? $filter_key, $humanize_columns) ? igny8_humanize_label($filter_config['field'] ?? $filter_key) : ucfirst($filter_key))); ?></span>
<span>▼</span>
</button>
<div class="select-list igny8-dropdown-panel" id="<?php echo esc_attr($table_id); ?>_filter_<?php echo esc_attr($filter_key); ?>_list">
<div style="margin-bottom: 10px;">
<label class="igny8-text-sm igny8-mb-5 igny8-text-muted">Min <?php echo esc_html($filter_config['label'] ?? (in_array($filter_config['field'] ?? $filter_key, $humanize_columns) ? igny8_humanize_label($filter_config['field'] ?? $filter_key) : ucfirst($filter_key))); ?>:</label>
<input type="number" id="<?php echo esc_attr($table_id); ?>_filter_<?php echo esc_attr($filter_key); ?>_min" placeholder="0" class="igny8-input-sm">
</div>
<div style="margin-bottom: 10px;">
<label class="igny8-text-sm igny8-mb-5 igny8-text-muted">Max <?php echo esc_html($filter_config['label'] ?? (in_array($filter_config['field'] ?? $filter_key, $humanize_columns) ? igny8_humanize_label($filter_config['field'] ?? $filter_key) : ucfirst($filter_key))); ?>:</label>
<input type="number" id="<?php echo esc_attr($table_id); ?>_filter_<?php echo esc_attr($filter_key); ?>_max" placeholder="10000" class="igny8-input-sm">
</div>
<div class="igny8-flex igny8-flex-gap-10">
<button type="button" id="<?php echo esc_attr($table_id); ?>_filter_<?php echo esc_attr($filter_key); ?>_ok" class="igny8-btn igny8-btn-primary igny8-flex igny8-p-5 igny8-text-xs">Ok</button>
<button type="button" id="<?php echo esc_attr($table_id); ?>_filter_<?php echo esc_attr($filter_key); ?>_clear" class="igny8-btn igny8-btn-secondary igny8-flex igny8-p-5 igny8-text-xs">Clear</button>
</div>
</div>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
<div class="igny8-filter-actions">
<button class="igny8-btn igny8-btn-primary" id="<?php echo esc_attr($table_id); ?>_filter_apply_btn">Apply Filters</button>
<button class="igny8-btn igny8-btn-secondary" id="<?php echo esc_attr($table_id); ?>_filter_reset_btn">Reset</button>
</div>
</div>
</div>
<?php
return ob_get_clean();
}
// Set default values
$table_id = $table_id ?? 'data_table';
$filters = $filters ?? [];
$module = $module ?? '';
$tab = $tab ?? '';
// Debug state: Filter HTML rendered
if (function_exists('igny8_debug_state')) {
igny8_debug_state('FILTER_HTML_RENDERED', true, 'Filter HTML rendered for ' . $table_id);
}
?>

View File

@@ -0,0 +1,176 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : forms-tpl.php
* @location : /modules/components/forms-tpl.php
* @type : Component
* @scope : Cross-Module
* @allowed : Form rendering, validation, data processing
* @reusability : Shared
* @notes : Dynamic form component for all modules
*/
if (!defined('ABSPATH')) {
exit;
}
function igny8_render_inline_form_row($table_id, $mode = 'add', $record_data = []) {
$form_config = igny8_get_form_config($table_id);
if (!$form_config) {
return '<tr><td colspan="100%">Form not configured for table: ' . esc_html($table_id) . '</td></tr>';
}
$row_id_attr = ($mode === 'edit' && !empty($record_data['id']))
? ' data-id="' . esc_attr($record_data['id']) . '"'
: '';
ob_start(); ?>
<tr class="igny8-inline-form-row" data-mode="<?php echo esc_attr($mode); ?>"<?php echo $row_id_attr; ?>>
<td><input type="checkbox" disabled></td>
<?php foreach ($form_config['fields'] as $field): ?>
<td>
<?php echo igny8_render_form_field($field, $record_data, $mode); ?>
</td>
<?php endforeach; ?>
<td class="igny8-align-center igny8-actions">
<button
type="button"
class="igny8-btn igny8-btn-success igny8-btn-sm igny8-form-save"
data-table-id="<?php echo esc_attr($table_id); ?>"
data-nonce="<?php echo esc_attr(wp_create_nonce('igny8_ajax_nonce')); ?>"
title="Save"
>
<svg width="20" height="20" viewBox="0 0 26 26" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
</svg>
</button>
<button
type="button"
class="igny8-btn igny8-btn-secondary igny8-btn-sm igny8-form-cancel"
title="Cancel"
>
<svg width="20" height="20" viewBox="0 0 26 26" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="5" y1="5" x2="20" y2="20"></line>
<line x1="5" y1="20" x2="20" y2="5"></line>
</svg>
</button>
</td>
</tr>
<style>
tr.igny8-inline-form-row {
animation: slideInForm 0.3s ease-out;
}
@keyframes slideInForm {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
tr.igny8-inline-form-row td {
transition: all 0.2s ease;
}
tr.igny8-inline-form-row:hover td {
background-color: rgba(0, 123, 255, 0.05);
}
</style>
<?php
return ob_get_clean();
}
function igny8_render_form_field($field, $record_data = [], $mode = 'add') {
$field_name = $field['name'];
$field_type = $field['type'];
$field_label = $field['label'] ?? ucfirst($field_name);
$field_value = isset($record_data[$field_name]) ? $record_data[$field_name] : ($field['default'] ?? '');
$is_required = !empty($field['required']);
switch ($field_type) {
case 'number':
return igny8_render_number_field($field_name, $field_label, $field_value, $is_required, $field['step'] ?? '');
case 'select':
return igny8_render_select_field($field_name, $field_label, $field_value, $field, $is_required);
case 'textarea':
return igny8_render_textarea_field($field_name, $field_label, $field_value, $is_required);
default:
return igny8_render_text_field($field_name, $field_label, $field_value, $is_required);
}
}
function igny8_render_text_field($name, $label, $value, $required = false) {
ob_start();
?>
<input type="text" name="<?php echo esc_attr($name); ?>" placeholder="<?php echo esc_attr($label); ?>"
class="igny8-input-sm" value="<?php echo esc_attr($value); ?>"<?php echo $required ? ' required' : ''; ?> />
<?php
return ob_get_clean();
}
function igny8_render_number_field($name, $label, $value, $required = false, $step = '') {
ob_start();
?>
<input type="number" name="<?php echo esc_attr($name); ?>" placeholder="<?php echo esc_attr($label); ?>"
class="igny8-input-sm" value="<?php echo esc_attr($value); ?>"<?php
echo $step ? ' step="' . esc_attr($step) . '"' : '';
echo $required ? ' required' : ''; ?> />
<?php
return ob_get_clean();
}
function igny8_render_select_field($name, $label, $value, $field_config, $required = false) {
$display_text = 'Select ' . esc_html($label);
$options = [];
// Get options
if (isset($field_config['source']) && function_exists($field_config['source'])) {
try {
$dynamic_options = call_user_func($field_config['source']);
foreach ($dynamic_options as $option) {
$val = $option['value'] ?? $option;
$lbl = $option['label'] ?? $option;
$options[$val] = $lbl;
if ($value == $val) $display_text = esc_html($lbl);
}
} catch (Exception $e) {
$options = [];
}
} else {
$options = $field_config['options'] ?? [];
foreach ($options as $val => $lbl) {
if ($value == $val) $display_text = esc_html($lbl);
}
}
ob_start();
?>
<div class="select">
<button type="button" class="select-btn" name="<?php echo esc_attr($name); ?>" data-value="<?php echo esc_attr($value); ?>">
<span class="select-text"><?php echo $display_text; ?></span>
<span>▼</span>
</button>
<div class="select-list" style="max-height:200px;overflow-y:auto;">
<div class="select-item" data-value="">Select <?php echo esc_html($label); ?></div>
<?php foreach ($options as $val => $lbl): ?>
<div class="select-item" data-value="<?php echo esc_attr($val); ?>"><?php echo esc_html($lbl); ?></div>
<?php endforeach; ?>
</div>
</div>
<?php
return ob_get_clean();
}
function igny8_render_textarea_field($name, $label, $value, $required = false) {
ob_start();
?>
<textarea name="<?php echo esc_attr($name); ?>" placeholder="<?php echo esc_attr($label); ?>"
class="igny8-input-sm" rows="3"<?php echo $required ? ' required' : ''; ?>><?php echo esc_textarea($value); ?></textarea>
<?php
return ob_get_clean();
}
?>

View File

@@ -0,0 +1,67 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : import-modal-tpl.php
* @location : /modules/components/import-modal-tpl.php
* @type : Component
* @scope : Cross-Module
* @allowed : Modal rendering, import functionality
* @reusability : Shared
* @notes : Dynamic import modal component for all modules
*/
// Prevent direct access
if (!defined('ABSPATH') && !defined('IGNY8_INCLUDE_TEMPLATE')) {
exit;
}
// Get configuration for this table
$config = igny8_get_import_export_config($table_id);
if (!$config) {
return;
}
$singular = $config['singular'];
$plural = $config['plural'];
?>
<div id="igny8-import-export-modal" class="igny8-modal" data-table-id="<?php echo esc_attr($table_id); ?>">
<div class="igny8-modal-content">
<div class="igny8-modal-header">
<h3>Import <?php echo esc_html($plural); ?></h3>
<button class="igny8-btn-close" onclick="igny8CloseImportExportModal()">&times;</button>
</div>
<div class="igny8-modal-body">
<p>Import <?php echo esc_html(strtolower($plural)); ?> from a CSV file. Use the template below for proper format.</p>
<!-- Template Download -->
<div class="igny8-mb-15">
<button type="button" class="igny8-btn igny8-btn-outline" onclick="igny8DownloadTemplate('<?php echo esc_attr($table_id); ?>')">
<span class="dashicons dashicons-download"></span> Download <?php echo esc_html($singular); ?> Template
</button>
</div>
<!-- Import Form -->
<form id="igny8-modal-import-form" enctype="multipart/form-data">
<input type="hidden" name="action" value="igny8_run_import">
<input type="hidden" name="nonce" value="">
<input type="hidden" name="import_type" value="<?php echo esc_attr($config['type']); ?>">
<div class="igny8-form-group">
<label for="import-file">Select CSV File</label>
<input type="file" id="import-file" name="import_file" accept=".csv" required>
<p class="description">Upload a CSV file with your <?php echo esc_html(strtolower($plural)); ?>. Use the template above for proper format.</p>
</div>
</form>
</div>
<div class="igny8-modal-footer">
<button type="button" class="igny8-btn igny8-btn-secondary" onclick="igny8CloseImportExportModal()">Cancel</button>
<button type="button" class="igny8-btn igny8-btn-success" onclick="igny8SubmitImportForm()">
<span class="dashicons dashicons-upload"></span> Import <?php echo esc_html($plural); ?>
</button>
</div>
</div>
</div>

View File

@@ -0,0 +1,140 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : kpi-tpl.php
* @location : /modules/components/kpi-tpl.php
* @type : Component
* @scope : Cross-Module
* @allowed : KPI rendering, metrics display
* @reusability : Shared
* @notes : Dynamic KPI component for all modules
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
/**
* Get KPI data for a specific submodule
*
* @param string $table_id The table ID (e.g., 'planner_keywords')
* @param array $kpi_config The KPI configuration array
* @return array KPI data array
*/
function igny8_get_kpi_data($table_id, $kpi_config) {
global $wpdb;
$kpi_data = [];
// Get table name from table_id
$table_name = igny8_get_table_name($table_id);
if (empty($table_name) || empty($kpi_config)) {
return $kpi_data;
}
// Execute each KPI query
foreach ($kpi_config as $kpi_key => $kpi_info) {
if (!isset($kpi_info['query'])) {
continue;
}
// Replace placeholders with actual values
$query = str_replace('{table_name}', $table_name, $kpi_info['query']);
$query = str_replace('{prefix}', $wpdb->prefix, $query);
// Execute query safely
$result = $wpdb->get_var($query);
// Store result (handle null/empty results)
$kpi_data[$kpi_key] = $result !== null ? (int) $result : 0;
}
return $kpi_data;
}
/**
* Get actual table name from table ID
*
* @param string $table_id The table ID (e.g., 'planner_keywords')
* @return string The actual table name (e.g., 'wp_igny8_keywords')
*/
function igny8_get_table_name($table_id) {
global $wpdb;
// Map table IDs to actual table names
$table_mapping = [
'planner_home' => $wpdb->prefix . 'igny8_keywords', // Uses keywords table as base for home metrics
'planner_keywords' => $wpdb->prefix . 'igny8_keywords',
'planner_clusters' => $wpdb->prefix . 'igny8_clusters',
'planner_ideas' => $wpdb->prefix . 'igny8_content_ideas',
'writer_home' => $wpdb->prefix . 'igny8_content_ideas', // Uses ideas table as base for home metrics
'writer_drafts' => $wpdb->prefix . 'igny8_tasks',
'writer_published' => $wpdb->prefix . 'igny8_tasks',
'writer_templates' => $wpdb->prefix . 'igny8_prompts',
'writer_tasks' => $wpdb->prefix . 'igny8_tasks',
'optimizer_audits' => $wpdb->prefix . 'igny8_logs',
'optimizer_suggestions' => $wpdb->prefix . 'igny8_suggestions',
'linker_backlinks' => $wpdb->prefix . 'igny8_backlinks',
'linker_campaigns' => $wpdb->prefix . 'igny8_campaigns',
'linker_links' => $wpdb->prefix . 'igny8_links',
'personalize_rewrites' => $wpdb->prefix . 'igny8_variations',
'personalize_tones' => $wpdb->prefix . 'igny8_sites',
'personalize_data' => $wpdb->prefix . 'igny8_personalization',
'personalize_variations' => $wpdb->prefix . 'igny8_variations',
'personalize_records' => $wpdb->prefix . 'igny8_personalization',
'thinker_prompts' => '' // No table needed for prompts submodule
];
$table_name = $table_mapping[$table_id] ?? '';
// Throw error if unknown table ID (except for special cases that don't need tables)
if (empty($table_name) && !in_array($table_id, ['thinker_prompts'])) {
throw new InvalidArgumentException("Unknown table ID: {$table_id}");
}
return $table_name;
}
/**
* Check if a table exists in the database
*
* @param string $table_name The table name to check
* @return bool True if table exists, false otherwise
*/
function igny8_table_exists($table_name) {
global $wpdb;
$result = $wpdb->get_var($wpdb->prepare(
"SHOW TABLES LIKE %s",
$table_name
));
return $result === $table_name;
}
/**
* Get KPI data with fallback for missing tables
*
* @param string $table_id The table ID
* @param array $kpi_config The KPI configuration
* @return array KPI data array with fallback values
*/
function igny8_get_kpi_data_safe($table_id, $kpi_config) {
$table_name = igny8_get_table_name($table_id);
// If table doesn't exist, return empty data
if (!igny8_table_exists($table_name)) {
$fallback_data = [];
foreach ($kpi_config as $kpi_key => $kpi_info) {
$fallback_data[$kpi_key] = 0;
}
return $fallback_data;
}
// Get real data
return igny8_get_kpi_data($table_id, $kpi_config);
}

View File

@@ -0,0 +1,136 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : pagination-tpl.php
* @location : /modules/components/pagination-tpl.php
* @type : Component
* @scope : Cross-Module
* @allowed : Pagination rendering, navigation controls
* @reusability : Shared
* @notes : Dynamic pagination component for all modules
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Render pagination function
function igny8_render_pagination($table_id, $pagination = []) {
// Set variables for component
$module = explode('_', $table_id)[0];
$tab = explode('_', $table_id)[1] ?? '';
// Set default pagination values
$pagination = array_merge([
'current_page' => 1,
'total_pages' => 1,
'per_page' => 10,
'total_items' => 0
], $pagination);
// Start output buffering to capture HTML
ob_start();
?>
<!-- Pagination -->
<div class="igny8-pagination igny8-mt-20"
data-table="<?php echo esc_attr($table_id); ?>"
data-current-page="<?php echo esc_attr($pagination['current_page']); ?>"
data-per-page="<?php echo esc_attr($pagination['per_page']); ?>"
data-total-items="<?php echo esc_attr($pagination['total_items']); ?>"
data-module="<?php echo esc_attr($module); ?>"
data-tab="<?php echo esc_attr($tab); ?>">
<button class="igny8-btn igny8-btn-sm igny8-btn-outline igny8-page-btn"
data-page="<?php echo max(1, $pagination['current_page'] - 1); ?>"
<?php echo $pagination['current_page'] <= 1 ? 'disabled' : ''; ?>>
Previous
</button>
<?php if ($pagination['total_items'] > 20): ?>
<?php
$current = $pagination['current_page'];
$total = $pagination['total_pages'];
?>
<?php // Always show first 2 pages ?>
<?php for ($i = 1; $i <= min(2, $total); $i++): ?>
<button class="igny8-btn igny8-btn-sm igny8-page-btn <?php echo $i === $current ? 'igny8-btn-primary' : 'igny8-btn-outline'; ?>"
data-page="<?php echo $i; ?>">
<?php echo $i; ?>
</button>
<?php endfor; ?>
<?php // Add ellipsis if there's a gap ?>
<?php if ($total > 4 && $current > 3): ?>
<span style="margin: 0 8px; color: #666;">...</span>
<?php endif; ?>
<?php // Show current page if it's not in first 2 or last 2 ?>
<?php if ($current > 2 && $current < $total - 1): ?>
<button class="igny8-btn igny8-btn-sm igny8-page-btn igny8-btn-primary"
data-page="<?php echo $current; ?>">
<?php echo $current; ?>
</button>
<?php endif; ?>
<?php // Add ellipsis before last 2 if there's a gap ?>
<?php if ($total > 4 && $current < $total - 2): ?>
<span style="margin: 0 8px; color: #666;">...</span>
<?php endif; ?>
<?php // Always show last 2 pages (if different from first 2) ?>
<?php for ($i = max(3, $total - 1); $i <= $total; $i++): ?>
<?php if ($i > 2): // Don't duplicate first 2 pages ?>
<button class="igny8-btn igny8-btn-sm igny8-page-btn <?php echo $i === $current ? 'igny8-btn-primary' : 'igny8-btn-outline'; ?>"
data-page="<?php echo $i; ?>">
<?php echo $i; ?>
</button>
<?php endif; ?>
<?php endfor; ?>
<?php endif; ?>
<button class="igny8-btn igny8-btn-sm igny8-btn-outline igny8-page-btn"
data-page="<?php echo min($pagination['total_pages'], $pagination['current_page'] + 1); ?>"
<?php echo $pagination['current_page'] >= $pagination['total_pages'] ? 'disabled' : ''; ?>>
Next
</button>
<!-- Records per page selector -->
<div class="igny8-per-page-selector" style="margin-left: 20px; display: inline-flex; align-items: center; gap: 8px;">
<label for="<?php echo esc_attr($table_id); ?>_per_page" style="font-size: 12px; color: #666;">Show:</label>
<select id="<?php echo esc_attr($table_id); ?>_per_page" class="igny8-per-page-select" data-table="<?php echo esc_attr($table_id); ?>" style="padding: 4px 8px; font-size: 12px; border: 1px solid #ddd; border-radius: 4px;">
<option value="10" <?php selected($pagination['per_page'], 10); ?>>10</option>
<option value="20" <?php selected($pagination['per_page'], 20); ?>>20</option>
<option value="50" <?php selected($pagination['per_page'], 50); ?>>50</option>
<option value="100" <?php selected($pagination['per_page'], 100); ?>>100</option>
</select>
<span style="font-size: 12px; color: #666;">per page</span>
</div>
<span style="margin-left: 12px; font-size: 12px; color: #666;">
Showing <?php echo (($pagination['current_page'] - 1) * $pagination['per_page']) + 1; ?>-<?php echo min($pagination['current_page'] * $pagination['per_page'], $pagination['total_items']); ?> of <?php echo $pagination['total_items']; ?> items
</span>
</div>
<?php
return ob_get_clean();
}
// Set default values
$table_id = $table_id ?? 'data_table';
$pagination = $pagination ?? [
'current_page' => 1,
'total_pages' => 1,
'per_page' => 10,
'total_items' => 0
];
$module = $module ?? '';
$tab = $tab ?? '';
// Debug state: Pagination HTML rendered
if (function_exists('igny8_debug_state')) {
igny8_debug_state('PAGINATION_HTML_RENDERED', true, 'Pagination HTML rendered for ' . $table_id);
}
?>

View File

@@ -0,0 +1,870 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : table-tpl.php
* @location : /modules/components/table-tpl.php
* @type : Component
* @scope : Cross-Module
* @allowed : Table rendering, data formatting, display functions
* @reusability : Shared
* @notes : Dynamic table component with formatting functions for all modules
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
/**
* Check if a column should have proper case transformation applied
*
* @param string $column_name The column name
* @param array $column_config The column configuration
* @return bool Whether to apply proper case transformation
*/
function igny8_should_apply_proper_case($column_name, $column_config) {
// Only apply to enum fields
$column_type = $column_config['type'] ?? 'text';
return $column_type === 'enum';
}
/**
* Format enum field values with proper case display
*
* @param string $value The database value
* @param string $field_name The field name
* @return string The formatted display value
*/
function igny8_format_enum_field($value, $field_name) {
if (empty($value)) {
return $value;
}
// Handle specific field mappings for consistent display
$field_mappings = [
'status' => [
'queued' => 'Queued',
'in_progress' => 'In Progress',
'draft' => 'Draft',
'review' => 'Review',
'published' => 'Published'
],
'priority' => [
'low' => 'Low',
'medium' => 'Medium',
'high' => 'High',
'urgent' => 'Urgent'
],
'content_type' => [
'blog_post' => 'Blog Post',
'landing_page' => 'Landing Page',
'product_page' => 'Product Page',
'guide_tutorial' => 'Guide Tutorial',
'news_article' => 'News Article',
'review' => 'Review',
'comparison' => 'Comparison',
'email' => 'Email',
'social_media' => 'Social Media'
]
];
// Check for specific field mapping first
if (isset($field_mappings[$field_name][$value])) {
return $field_mappings[$field_name][$value];
}
// Fallback: convert snake_case to Title Case
return ucwords(str_replace('_', ' ', $value));
}
/**
* Get proper case for enum values
*
* @param string $value The snake_case value
* @return string The proper case value
*/
function igny8_get_proper_case_enum($value) {
$enum_mapping = [
// Content Type values
'blog_post' => 'Blog Post',
'landing_page' => 'Landing Page',
'product_page' => 'Product Page',
'guide_tutorial' => 'Guide Tutorial',
'news_article' => 'News Article',
'review' => 'Review',
'comparison' => 'Comparison',
'page' => 'Page',
'product' => 'Product',
'product_description' => 'Product Description',
'email' => 'Email',
'social_media' => 'Social Media',
// Status values
'unmapped' => 'Unmapped',
'mapped' => 'Mapped',
'queued' => 'Queued',
'published' => 'Published',
'active' => 'Active',
'inactive' => 'Inactive',
'archived' => 'Archived',
'draft' => 'Draft',
'in_progress' => 'In Progress',
'completed' => 'Completed',
'cancelled' => 'Cancelled',
'pending' => 'Pending',
'failed' => 'Failed',
'lost' => 'Lost',
'approved' => 'Approved',
'rejected' => 'Rejected',
'needs_revision' => 'Needs Revision',
'planning' => 'Planning',
'paused' => 'Paused',
'review' => 'Review',
// Intent values
'informational' => 'Informational',
'navigational' => 'Navigational',
'transactional' => 'Transactional',
'commercial' => 'Commercial',
// Link type values
'dofollow' => 'Dofollow',
'nofollow' => 'Nofollow',
'sponsored' => 'Sponsored',
'ugc' => 'UGC',
// Coverage status values
'fully_mapped' => 'Fully Mapped',
'partially_mapped' => 'Partially Mapped',
'not_mapped' => 'Not Mapped',
// Suggestion type values
'title_optimization' => 'Title Optimization',
'meta_description' => 'Meta Description',
'heading_structure' => 'Heading Structure',
'content_improvement' => 'Content Improvement',
'internal_linking' => 'Internal Linking',
// Tone values
'professional' => 'Professional',
'casual' => 'Casual',
'friendly' => 'Friendly',
'authoritative' => 'Authoritative',
'conversational' => 'Conversational',
// Category values
'business' => 'Business',
'creative' => 'Creative',
'technical' => 'Technical',
'marketing' => 'Marketing',
'educational' => 'Educational'
];
return $enum_mapping[$value] ?? ucwords(str_replace('_', ' ', $value));
}
/**
* Apply proper case transformation to a value
*
* @param string $value The value to transform
* @param string $column_name The column name for special handling
* @return string The transformed value
*/
function igny8_apply_proper_case($value, $column_name) {
// Apply global proper case transformation to all enum fields
return igny8_get_proper_case_enum($value);
}
/**
* Format date for created_at column (tasks table)
* Shows hours/days ago if less than 30 days, month/day if greater
*/
function igny8_format_created_date($date_string) {
if (empty($date_string)) {
return 'Never';
}
try {
// Use WordPress timezone
$wp_timezone = wp_timezone();
$date = new DateTime($date_string, $wp_timezone);
$now = new DateTime('now', $wp_timezone);
$diff = $now->diff($date);
// Calculate total days difference
$total_days = $diff->days;
// If less than 30 days, show relative time
if ($total_days < 30) {
if ($total_days == 0) {
if ($diff->h > 0) {
$result = $diff->h . ' hour' . ($diff->h > 1 ? 's' : '') . ' ago';
} elseif ($diff->i > 0) {
$result = $diff->i . ' minute' . ($diff->i > 1 ? 's' : '') . ' ago';
} else {
$result = 'Just now';
}
} else {
$result = $total_days . ' day' . ($total_days > 1 ? 's' : '') . ' ago';
}
} else {
// If 30+ days, show month and day
$result = $date->format('M j');
}
return $result;
} catch (Exception $e) {
// Fallback to original date if parsing fails
$fallback = wp_date('M j', strtotime($date_string));
return $fallback;
}
}
/**
* Format date for updated_at column (drafts/published tables)
* Shows hours/days ago if less than 30 days, month/day if greater
*/
function igny8_format_updated_date($date_string) {
if (empty($date_string)) {
return 'Never';
}
try {
// Use WordPress timezone
$wp_timezone = wp_timezone();
$date = new DateTime($date_string, $wp_timezone);
$now = new DateTime('now', $wp_timezone);
$diff = $now->diff($date);
// Calculate total days difference
$total_days = $diff->days;
// If less than 30 days, show relative time
if ($total_days < 30) {
if ($total_days == 0) {
if ($diff->h > 0) {
return $diff->h . ' hour' . ($diff->h > 1 ? 's' : '') . ' ago';
} elseif ($diff->i > 0) {
return $diff->i . ' minute' . ($diff->i > 1 ? 's' : '') . ' ago';
} else {
return 'Just now';
}
} else {
return $total_days . ' day' . ($total_days > 1 ? 's' : '') . ' ago';
}
} else {
// If 30+ days, show month and day
return $date->format('M j');
}
} catch (Exception $e) {
// Fallback to original date if parsing fails
return wp_date('M j', strtotime($date_string));
}
}
/**
* Display image prompts in a formatted way for table display
*
* @param string $image_prompts JSON string of image prompts
* @return string Formatted display string
*/
function igny8_display_image_prompts($image_prompts) {
if (empty($image_prompts)) {
return '<span class="text-muted">No prompts</span>';
}
// Try to decode JSON
$prompts = json_decode($image_prompts, true);
if (!$prompts || !is_array($prompts)) {
return '<span class="text-muted">Invalid format</span>';
}
$output = '<div class="image-prompts-display">';
$count = 0;
foreach ($prompts as $key => $prompt) {
if (!empty($prompt)) {
$count++;
$label = ucfirst(str_replace('_', ' ', $key));
$truncated = strlen($prompt) > 50 ? substr($prompt, 0, 50) . '...' : $prompt;
$output .= '<div class="prompt-item mb-1">';
$output .= '<strong>' . esc_html($label) . ':</strong> ';
$output .= '<span title="' . esc_attr($prompt) . '">' . esc_html($truncated) . '</span>';
$output .= '</div>';
}
}
if ($count === 0) {
$output = '<span class="text-muted">No prompts</span>';
} else {
$output .= '</div>';
}
return $output;
}
/**
* Format structured description for display
*/
function igny8_format_structured_description_for_display($structured_description) {
if (!is_array($structured_description)) {
return 'No structured outline available';
}
$formatted = "<div class='igny8-structured-description'>";
$formatted .= "<h4>Content Outline</h4>";
// Handle introduction section with hook
if (!empty($structured_description['introduction'])) {
$formatted .= "<div class='description-intro'>";
// Add hook if it exists
if (!empty($structured_description['introduction']['hook'])) {
$formatted .= "<div class='intro-hook'><strong>Hook:</strong> " . esc_html($structured_description['introduction']['hook']) . "</div>";
}
// Add paragraphs if they exist
if (!empty($structured_description['introduction']['paragraphs']) && is_array($structured_description['introduction']['paragraphs'])) {
$paragraph_count = 1;
foreach ($structured_description['introduction']['paragraphs'] as $paragraph) {
if (!empty($paragraph['details'])) {
$formatted .= "<div class='intro-paragraph'><strong>Intro Paragraph " . $paragraph_count . ":</strong> " . esc_html($paragraph['details']) . "</div>";
$paragraph_count++;
}
}
}
$formatted .= "</div>";
}
// Handle H2 sections if they exist
if (!empty($structured_description['H2']) && is_array($structured_description['H2'])) {
foreach ($structured_description['H2'] as $h2_section) {
$formatted .= "<div class='igny8-h2-section'>";
$formatted .= "<h5>" . esc_html($h2_section['heading']) . "</h5>";
if (!empty($h2_section['subsections'])) {
$formatted .= "<ul class='igny8-subsections'>";
foreach ($h2_section['subsections'] as $h3_section) {
$formatted .= "<li>";
$formatted .= "<strong>" . esc_html($h3_section['subheading']) . "</strong>";
$formatted .= " <span class='igny8-content-type'>(" . esc_html($h3_section['content_type']) . ")</span>";
$formatted .= "<br><small>" . esc_html($h3_section['details']) . "</small>";
$formatted .= "</li>";
}
$formatted .= "</ul>";
}
$formatted .= "</div>";
}
}
$formatted .= "</div>";
return $formatted;
}
/**
* Fetch real table data from database
* Phase-2: Real Data Loading from Config
*
* @param string $tableId The table identifier
* @param array $filters Optional filters to apply
* @param int $page Page number for pagination
* @return array Real data structure
*/
function igny8_fetch_table_data($tableId, $filters = [], $page = 1, $per_page = null) {
global $wpdb;
// Sanitize all inputs
$tableId = sanitize_text_field($tableId);
$page = intval($page);
$per_page = $per_page ? intval($per_page) : get_option('igny8_records_per_page', 20);
// Sanitize filters array
if (is_array($filters)) {
$filters = array_map('sanitize_text_field', $filters);
} else {
$filters = [];
}
// Get table configuration to apply default filters
$tables_config = require plugin_dir_path(__FILE__) . '../config/tables-config.php';
$GLOBALS['igny8_tables_config'] = $tables_config;
$table_config = igny8_get_dynamic_table_config($tableId);
// Apply default filters - merge with existing filters
if (isset($table_config['default_filter'])) {
$default_filters = $table_config['default_filter'];
foreach ($default_filters as $key => $value) {
// Only apply default filter if not already set by user
if (!isset($filters[$key]) || empty($filters[$key])) {
$filters[$key] = $value;
}
}
}
// Force completed status filter for writer_published table
if ($tableId === 'writer_published') {
$filters['status'] = ['completed'];
}
// Get table name from table ID
$table_name = igny8_get_table_name($tableId);
// Check if table exists
if (!igny8_table_exists($table_name)) {
// Return empty data if table doesn't exist
return [
'rows' => [],
'pagination' => [
'page' => $page,
'total' => 0,
'per_page' => $per_page,
'total_pages' => 0
]
];
}
// Build WHERE clause for filters
$where_conditions = [];
$where_values = [];
if (!empty($filters)) {
foreach ($filters as $key => $value) {
if (!empty($value)) {
// Check if this is a range filter (min/max)
if (strpos($key, '-min') !== false) {
$field_name = str_replace('-min', '', $key);
$where_conditions[] = "`{$field_name}` >= %d";
$where_values[] = intval($value);
} elseif (strpos($key, '-max') !== false) {
$field_name = str_replace('-max', '', $key);
$where_conditions[] = "`{$field_name}` <= %d";
$where_values[] = intval($value);
} elseif (in_array($key, ['status', 'intent'])) {
// For dropdown filters (status, intent), handle both single values and arrays
if (is_array($value)) {
// For array values (like ['draft'] from default filters)
$placeholders = implode(',', array_fill(0, count($value), '%s'));
$where_conditions[] = "`{$key}` IN ({$placeholders})";
$where_values = array_merge($where_values, $value);
} else {
// For single values
$where_conditions[] = "`{$key}` = %s";
$where_values[] = $value;
}
} elseif ($key === 'difficulty') {
// For difficulty, convert text label to numeric range
$difficulty_range = igny8_get_difficulty_numeric_range($value);
if ($difficulty_range) {
$where_conditions[] = "`{$key}` >= %d AND `{$key}` <= %d";
$where_values[] = $difficulty_range['min'];
$where_values[] = $difficulty_range['max'];
}
} else {
// For keyword search, use LIKE
$where_conditions[] = "`{$key}` LIKE %s";
$where_values[] = '%' . $wpdb->esc_like($value) . '%';
}
}
}
}
$where_clause = '';
if (!empty($where_conditions)) {
$where_clause = 'WHERE ' . implode(' AND ', $where_conditions);
}
// Build JOIN queries from column configurations
$join_queries = [];
foreach ($config_columns as $col_key => $col_config) {
if (isset($col_config['join_query'])) {
$join_query = str_replace(['{prefix}', '{table_name}'], [$wpdb->prefix, $table_name], $col_config['join_query']);
if (!in_array($join_query, $join_queries)) {
$join_queries[] = $join_query;
}
}
}
// Get total count for pagination (with JOINs)
$join_clause = '';
if (!empty($join_queries)) {
$join_clause = implode(' ', $join_queries);
}
$count_query = "SELECT COUNT(*) FROM `{$table_name}` {$join_clause} {$where_clause}";
if (!empty($where_values)) {
$total_count = $wpdb->get_var($wpdb->prepare($count_query, $where_values));
} else {
$total_count = $wpdb->get_var($count_query);
}
// Calculate pagination
$total_pages = ceil($total_count / $per_page);
$offset = ($page - 1) * $per_page;
// Build main query with JOINs and proper field selection
$select_fields = [];
// Add base table fields
$select_fields[] = "{$table_name}.*";
// Process column configurations to build select fields
foreach ($config_columns as $col_key => $col_config) {
if (isset($col_config['select_field'])) {
$select_fields[] = $col_config['select_field'];
}
}
// Build final query with JOINs
$select_clause = implode(', ', $select_fields);
$query = "SELECT {$select_clause} FROM `{$table_name}` {$join_clause} {$where_clause} ORDER BY {$table_name}.id DESC LIMIT %d OFFSET %d";
$query_values = array_merge($where_values, [$per_page, $offset]);
// Execute query
if (!empty($where_values)) {
$final_query = $wpdb->prepare($query, $query_values);
$results = $wpdb->get_results($final_query, ARRAY_A);
} else {
$final_query = $wpdb->prepare($query, $per_page, $offset);
$results = $wpdb->get_results($final_query, ARRAY_A);
}
// Format results for frontend - return complete table body HTML using PHP templating
$table_body_html = '';
// Load table configuration once (needed for both results and empty state)
$tables_config = require plugin_dir_path(__FILE__) . '../config/tables-config.php';
$GLOBALS['igny8_tables_config'] = $tables_config;
$table_config = igny8_get_dynamic_table_config($tableId);
$config_columns = $table_config['columns'] ?? [];
// Get column keys from humanize_columns, including date columns
$column_keys = $table_config['humanize_columns'] ?? array_keys($config_columns);
if ($results) {
foreach ($results as $row) {
$id = $row['id'] ?? 0;
// Use PHP templating instead of string concatenation
ob_start();
?>
<tr data-id="<?php echo esc_attr($id); ?>">
<td><input type="checkbox" class="igny8-checkbox" value="<?php echo esc_attr($id); ?>"></td>
<?php
// If no config found, fallback to all available columns
if (empty($column_keys)) {
$column_keys = array_keys($row);
}
foreach ($column_keys as $col) {
// Check if this is a calculated field
$column_config = $config_columns[$col] ?? [];
if (isset($column_config['calculated']) && $column_config['calculated'] === true) {
// Execute calculation query for this row
$calculation_query = $column_config['calculation_query'] ?? '';
if (!empty($calculation_query)) {
// Replace placeholders in the query
$query = str_replace(['{prefix}', '{table_name}'], [$wpdb->prefix, $table_name], $calculation_query);
$query = str_replace('{table_name}.id', $row['id'], $query);
$value = $wpdb->get_var($query) ?? 0;
} else {
$value = 0;
}
} elseif ($col === 'cluster_id' || $col === 'keyword_cluster_id') {
// Check if this column has a display_field from JOIN query
$display_field = $column_config['display_field'] ?? null;
if ($display_field && isset($row[$display_field])) {
$value = $row[$display_field];
} else {
// Fallback to fetching cluster name by ID
$cluster_id = isset($row[$col]) ? $row[$col] : '';
$value = igny8_get_cluster_term_name($cluster_id);
}
} elseif ($col === 'source') {
// Handle source column - use actual field value
$value = isset($row[$col]) ? $row[$col] : '';
} elseif ($col === 'sector_id') {
$sector_id = isset($row[$col]) ? $row[$col] : '';
$value = igny8_get_sector_name($sector_id);
} elseif ($col === 'difficulty' || $col === 'avg_difficulty') {
$difficulty = isset($row[$col]) ? $row[$col] : '';
$value = igny8_get_difficulty_range_name($difficulty);
} elseif (in_array($col, ['created_at', 'created_date', 'last_audit', 'discovered_date', 'start_date'])) {
// Format created/audit/discovered/start date columns for all tables
$date_value = isset($row[$col]) ? $row[$col] : '';
$value = igny8_format_created_date($date_value);
} elseif (in_array($col, ['updated_at', 'updated_date', 'next_audit', 'end_date'])) {
// Format updated/next audit/end date columns for all tables
$date_value = isset($row[$col]) ? $row[$col] : '';
$value = igny8_format_updated_date($date_value);
} else {
$value = isset($row[$col]) ? $row[$col] : '';
// Apply proper case transformation to eligible columns
if (!empty($value) && igny8_should_apply_proper_case($col, $column_config)) {
$value = igny8_apply_proper_case($value, $col);
}
}
// Special handling for idea_title column in planner_ideas table
if ($col === 'idea_title' && $tableId === 'planner_ideas') {
$description = isset($row['idea_description']) ? $row['idea_description'] : '';
$image_prompts = isset($row['image_prompts']) ? $row['image_prompts'] : '';
// Format description for display (handle JSON vs plain text)
$display_description = '';
if (!empty($description)) {
$decoded = json_decode($description, true);
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
// Format structured description for display
$display_description = igny8_format_structured_description_for_display($decoded);
} else {
// Use as plain text
$display_description = $description;
}
}
?>
<td>
<div class="igny8-title-with-badge">
<span class="igny8-title-text"><?php echo esc_html($value); ?></span>
<div class="igny8-title-actions">
<?php if (!empty($display_description)): ?>
<button class="igny8-menu-toggle igny8-description-toggle"
data-row-id="<?php echo esc_attr($id); ?>"
data-description="<?php echo esc_attr($display_description); ?>"
title="View description">
<span class="igny8-hamburger">
<span></span>
<span></span>
<span></span>
</span>
</button>
<?php endif; ?>
<?php if (!empty($image_prompts)): ?>
<button class="igny8-menu-toggle igny8-image-prompts-toggle"
data-row-id="<?php echo esc_attr($id); ?>"
data-image-prompts="<?php echo esc_attr($image_prompts); ?>"
title="View image prompts">
<span class="igny8-image-icon dashicons dashicons-format-image"></span>
</button>
<?php endif; ?>
</div>
</div>
</td>
<?php
}
// Special handling for title column in writer_tasks table
elseif ($col === 'title' && $tableId === 'writer_tasks') {
$description = isset($row['description']) ? $row['description'] : '';
// Format description for display (handle JSON vs plain text)
$display_description = '';
if (!empty($description)) {
$decoded = json_decode($description, true);
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
// Format structured description for display
$display_description = igny8_format_structured_description_for_display($decoded);
} else {
// Use as plain text
$display_description = $description;
}
}
?>
<td>
<div class="igny8-title-with-badge">
<span class="igny8-title-text"><?php echo esc_html($value); ?></span>
<div class="igny8-title-actions">
<?php if (!empty($display_description)): ?>
<button class="igny8-menu-toggle igny8-description-toggle"
data-row-id="<?php echo esc_attr($id); ?>"
data-description="<?php echo esc_attr($display_description); ?>"
title="View description">
<span class="igny8-hamburger">
<span></span>
<span></span>
<span></span>
</span>
</button>
<?php endif; ?>
</div>
</div>
</td>
<?php
}
// Special handling for status column in planner_ideas table
elseif ($col === 'status' && $tableId === 'planner_ideas') {
$source = isset($row['source']) ? $row['source'] : '';
?>
<td>
<div class="igny8-status-with-badge">
<span class="igny8-status-text"><?php echo esc_html($value); ?></span>
<?php if ($source === 'Manual'): ?>
<span class="igny8-badge igny8-badge-dark-red">Manual</span>
<?php endif; ?>
</div>
</td>
<?php
} else {
?>
<td><?php echo esc_html($value); ?></td>
<?php
}
}
?>
<td class="igny8-align-center igny8-actions">
<button
class="igny8-icon-only igny8-icon-edit"
data-action="editRow"
data-row-id="<?php echo esc_attr($id); ?>"
data-table-id="<?php echo esc_attr($tableId); ?>"
title="Edit"
>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
<path d="m18.5 2.5 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
</svg>
</button>
<button class="igny8-icon-only igny8-icon-delete" data-action="deleteRow" data-row-id="<?php echo esc_attr($id); ?>" title="Delete">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="3,6 5,6 21,6"></polyline>
<path d="m19,6v14a2,2 0 0,1 -2,2H7a2,2 0 0,1 -2,-2V6m3,0V4a2,2 0 0,1 2,-2h4a2,2 0 0,1 2,2v2"></path>
<line x1="10" y1="11" x2="10" y2="17"></line>
<line x1="14" y1="11" x2="14" y2="17"></line>
</svg>
</button>
</td>
</tr>
<?php
$table_body_html .= ob_get_clean();
}
} else {
// No records found - add empty state
ob_start();
?>
<tr>
<td colspan="<?php echo count($column_keys) + 2; ?>" class="igny8-align-center igny8-text-muted">
No records found
</td>
</tr>
<?php
$table_body_html = ob_get_clean();
}
$pagination_data = [
'current_page' => $page,
'total_items' => intval($total_count),
'per_page' => $per_page,
'total_pages' => $total_pages
];
return [
'table_body_html' => $table_body_html,
'pagination' => $pagination_data
];
}
// Render table function
function igny8_render_table($table_id, $columns = []) {
// Load table configuration with AI-specific modifications
$tables_config = require plugin_dir_path(__FILE__) . '../config/tables-config.php';
$GLOBALS['igny8_tables_config'] = $tables_config;
$table_config = igny8_get_dynamic_table_config($table_id);
// Set variables for component
$module = explode('_', $table_id)[0];
$tab = explode('_', $table_id)[1] ?? '';
// Use config columns if provided, otherwise use passed columns
$config_columns = $table_config['columns'] ?? [];
// Convert associative array to indexed array with 'key' field, including date columns
if (!empty($config_columns)) {
$columns = [];
foreach ($config_columns as $key => $column_config) {
// Check if this column should be humanized
$humanize_columns = $table_config['humanize_columns'] ?? [];
$should_humanize = in_array($key, $humanize_columns);
$columns[] = [
'key' => $key,
'label' => $column_config['label'] ?? ($should_humanize ? igny8_humanize_label($key) : $key),
'sortable' => $column_config['sortable'] ?? false,
'type' => $column_config['type'] ?? 'text'
];
}
}
// Start output buffering to capture HTML
ob_start();
?>
<!-- Data Table -->
<div class="igny8-table" data-table="<?php echo esc_attr($table_id); ?>">
<table id="<?php echo esc_attr($table_id); ?>" class="igny8-table">
<thead>
<tr>
<th>
<input type="checkbox" id="<?php echo esc_attr($table_id); ?>_select_all" class="igny8-checkbox">
</th>
<?php
// Render column headers dynamically based on table configuration
if (!empty($columns)) {
foreach ($columns as $column) {
$key = $column['key'];
$label = $column['label'];
$sortable = $column['sortable'] ?? false;
$sort_class = $sortable ? 'sortable' : '';
$sort_indicator = $sortable ? ' <span class="sort-indicator">↕</span>' : '';
?>
<th class="<?php echo esc_attr($sort_class); ?>" data-column="<?php echo esc_attr($key); ?>">
<?php echo esc_html($label); ?><?php echo $sort_indicator; ?>
</th>
<?php
}
} else {
// Fallback headers if no config
?>
<th class="sortable" data-column="keyword">Keyword <span class="sort-indicator">↕</span></th>
<th class="sortable" data-column="volume">Volume <span class="sort-indicator">↕</span></th>
<th class="sortable" data-column="kd">KD <span class="sort-indicator">↕</span></th>
<th class="sortable" data-column="cpc">CPC <span class="sort-indicator">↕</span></th>
<th class="sortable" data-column="intent">Intent <span class="sort-indicator">↕</span></th>
<th class="sortable" data-column="status">Status <span class="sort-indicator">↕</span></th>
<th class="sortable" data-column="cluster">Cluster <span class="sort-indicator">↕</span></th>
<?php
}
?>
<th>Actions</th>
</tr>
</thead>
<tbody id="table-<?php echo esc_attr($table_id); ?>-body">
<!-- Table data will be loaded dynamically via JavaScript -->
</tbody>
</table>
</div>
<?php
return ob_get_clean();
}
// Set default values
$table_id = $table_id ?? 'data_table';
$columns = $columns ?? [
['key' => 'content', 'label' => 'Content', 'sortable' => true],
['key' => 'status', 'label' => 'Status', 'sortable' => true],
['key' => 'date', 'label' => 'Date', 'sortable' => true]
];
$module = $module ?? '';
$tab = $tab ?? '';
// Debug state: Table HTML rendered
if (function_exists('igny8_debug_state')) {
igny8_debug_state('TABLE_HTML_RENDERED', true, 'Table HTML rendered for ' . $table_id);
}
?>

View File

@@ -0,0 +1,14 @@
<?php
/**
* ==============================
* 📁 Folder Scope Declaration
* ==============================
* Folder: /config/
* Purpose: Central config arrays (filters, tables, prompts)
* Rules:
* - Can be reused globally across all modules
* - Contains only configuration arrays
* - No executable code allowed
* - Must be pure data structures
* - Used by components and modules
*/

View File

@@ -0,0 +1,586 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : filters-config.php
* @location : /modules/config/filters-config.php
* @type : Config Array
* @scope : Global
* @allowed : Filter definitions, search configurations
* @reusability : Globally Reusable
* @notes : Central filter configuration for all modules
*/
// Prevent direct access only if not being included
if (!defined('ABSPATH') && !defined('IGNY8_INCLUDE_CONFIG')) {
exit;
}
return [
// Keywords Filters
'planner_keywords' => [
'keyword' => [
'type' => 'search',
'placeholder' => 'Search keywords...',
'field' => 'keyword'
],
'intent' => [
'type' => 'select',
'label' => 'Intent',
'field' => 'intent',
'options' => [
'informational' => 'Informational',
'navigational' => 'Navigational',
'transactional' => 'Transactional',
'commercial' => 'Commercial'
]
],
'status' => [
'type' => 'select',
'label' => 'Status',
'field' => 'status',
'options' => [
'unmapped' => 'Unmapped',
'mapped' => 'Mapped',
'queued' => 'Queued',
'published' => 'Published'
]
],
'difficulty' => [
'type' => 'select',
'label' => 'Difficulty',
'field' => 'difficulty',
'options' => [
'Very Easy' => 'Very Easy',
'Easy' => 'Easy',
'Medium' => 'Medium',
'Hard' => 'Hard',
'Very Hard' => 'Very Hard'
]
],
'search_volume' => [
'type' => 'range',
'label' => 'Volume',
'field' => 'search_volume',
'min' => 0,
'max' => 100000
]
],
// Clusters Filters
'planner_clusters' => [
'cluster_name' => [
'type' => 'search',
'placeholder' => 'Search clusters...',
'field' => 'cluster_name'
],
'sector_id' => [
'type' => 'select',
'label' => 'Sector',
'field' => 'sector_id',
'options' => 'dynamic_sectors' // Will be loaded via AJAX
],
'status' => [
'type' => 'select',
'label' => 'Status',
'field' => 'status',
'options' => [
'active' => 'Active',
'inactive' => 'Inactive',
'archived' => 'Archived'
]
],
'keyword_count' => [
'type' => 'range',
'label' => 'Keywords Count',
'field' => 'keyword_count',
'min' => 0,
'max' => 1000
]
],
// Ideas Filters
'planner_ideas' => [
'idea_title' => [
'type' => 'search',
'placeholder' => 'Search ideas...',
'field' => 'idea_title'
],
'content_structure' => [
'type' => 'select',
'label' => 'Content Structure',
'field' => 'content_structure',
'options' => [
'cluster_hub' => 'Cluster Hub',
'landing_page' => 'Landing Page',
'guide_tutorial' => 'Guide Tutorial',
'how_to' => 'How To',
'comparison' => 'Comparison',
'review' => 'Review',
'top_listicle' => 'Top Listicle',
'question' => 'Question',
'product_description' => 'Product Description',
'service_page' => 'Service Page',
'home_page' => 'Home Page'
]
],
'source' => [
'type' => 'select',
'label' => 'Source',
'field' => 'source',
'options' => [
'AI' => 'AI',
'Manual' => 'Manual'
]
],
'status' => [
'type' => 'select',
'label' => 'Status',
'field' => 'status',
'options' => [
'new' => 'New',
'scheduled' => 'Scheduled',
'published' => 'Published'
]
],
'keyword_cluster_id' => [
'type' => 'select',
'label' => 'Cluster',
'field' => 'keyword_cluster_id',
'options' => 'dynamic_clusters'
],
'estimated_word_count' => [
'type' => 'range',
'label' => 'Word Count',
'field' => 'estimated_word_count',
'min' => 0,
'max' => 5000
]
],
// Writer Tasks Filters (Content Queue / Tasks)
'writer_tasks' => [
'title' => [
'type' => 'search',
'placeholder' => 'Search tasks...',
'field' => 'title'
],
'keywords' => [
'type' => 'search',
'placeholder' => 'Search keywords...',
'field' => 'keywords'
],
'cluster_id' => [
'type' => 'select',
'label' => 'Cluster Name',
'field' => 'cluster_id',
'options' => 'dynamic_clusters'
],
'status' => [
'type' => 'select',
'label' => 'Status',
'field' => 'status',
'options' => [
'queued' => 'Queued',
'in_progress' => 'In Progress',
'completed' => 'Completed',
'cancelled' => 'Cancelled',
'draft' => 'Draft',
'review' => 'Review',
'published' => 'Published'
]
],
'content_type' => [
'type' => 'select',
'label' => 'Content Type',
'field' => 'content_type',
'options' => [
'blog_post' => 'Blog Post',
'landing_page' => 'Landing Page',
'product_page' => 'Product Page',
'guide_tutorial' => 'Guide Tutorial',
'news_article' => 'News Article',
'review' => 'Review',
'comparison' => 'Comparison',
'email' => 'Email',
'social_media' => 'Social Media'
]
],
'created_at' => [
'type' => 'date_range',
'label' => 'Queued Date Range',
'field' => 'created_at'
]
],
// Writer Drafts Filters (Content Generated)
'writer_drafts' => [
'title' => [
'type' => 'search',
'placeholder' => 'Search drafts...',
'field' => 'title'
],
'status' => [
'type' => 'select',
'label' => 'Status',
'field' => 'status',
'options' => [
'draft' => 'Draft',
'review' => 'Review'
]
],
'content_type' => [
'type' => 'select',
'label' => 'Content Type',
'field' => 'content_type',
'options' => [
'blog_post' => 'Blog Post',
'landing_page' => 'Landing Page',
'product_page' => 'Product Page',
'guide_tutorial' => 'Guide Tutorial',
'news_article' => 'News Article',
'review' => 'Review',
'comparison' => 'Comparison',
'email' => 'Email',
'social_media' => 'Social Media'
]
],
'cluster_id' => [
'type' => 'select',
'label' => 'Cluster',
'field' => 'cluster_id',
'options' => 'dynamic_clusters'
],
'meta_status' => [
'label' => 'Meta Status',
'type' => 'select',
'options' => [
'all' => 'All',
'complete' => 'Meta Present',
'missing' => 'Meta Missing'
]
],
'keywords' => [
'label' => 'Keywords',
'type' => 'text',
'searchable' => true
]
],
// Writer Published Filters (Live Content)
'writer_published' => [
'title' => [
'type' => 'search',
'placeholder' => 'Search published content...',
'field' => 'title'
],
'status' => [
'type' => 'select',
'label' => 'Status',
'field' => 'status',
'options' => [
'published' => 'Published'
]
],
'content_type' => [
'type' => 'select',
'label' => 'Content Type',
'field' => 'content_type',
'options' => [
'blog_post' => 'Blog Post',
'landing_page' => 'Landing Page',
'product_page' => 'Product Page',
'guide_tutorial' => 'Guide Tutorial',
'news_article' => 'News Article',
'review' => 'Review',
'comparison' => 'Comparison',
'email' => 'Email',
'social_media' => 'Social Media'
]
],
'cluster_id' => [
'type' => 'select',
'label' => 'Cluster',
'field' => 'cluster_id',
'options' => 'dynamic_clusters'
],
'meta_status' => [
'label' => 'Meta Status',
'type' => 'select',
'options' => [
'all' => 'All',
'complete' => 'Meta Present',
'missing' => 'Meta Missing'
]
],
'keywords' => [
'label' => 'Keywords',
'type' => 'text',
'searchable' => true
],
'created_at' => [
'type' => 'date_range',
'label' => 'Date Range',
'field' => 'created_at'
]
],
// Optimizer Audits Filters
'optimizer_audits' => [
'page_url' => [
'type' => 'search',
'placeholder' => 'Search pages...',
'field' => 'page_url'
],
'audit_status' => [
'type' => 'select',
'label' => 'Audit Status',
'field' => 'audit_status',
'options' => [
'pending' => 'Pending',
'in_progress' => 'In Progress',
'completed' => 'Completed',
'failed' => 'Failed'
]
],
'score_range' => [
'type' => 'range',
'label' => 'SEO Score',
'field' => 'seo_score',
'min' => 0,
'max' => 100
],
'last_audit' => [
'type' => 'date_range',
'label' => 'Last Audit',
'field' => 'last_audit_date'
]
],
// Linker Backlinks Filters
'linker_backlinks' => [
'target_url' => [
'type' => 'search',
'placeholder' => 'Search target URLs...',
'field' => 'target_url'
],
'source_domain' => [
'type' => 'search',
'placeholder' => 'Search source domains...',
'field' => 'source_domain'
],
'link_type' => [
'type' => 'select',
'label' => 'Link Type',
'field' => 'link_type',
'options' => [
'dofollow' => 'DoFollow',
'nofollow' => 'NoFollow',
'sponsored' => 'Sponsored',
'ugc' => 'UGC'
]
],
'status' => [
'type' => 'select',
'label' => 'Status',
'field' => 'status',
'options' => [
'active' => 'Active',
'lost' => 'Lost',
'pending' => 'Pending'
]
],
'domain_authority' => [
'type' => 'range',
'label' => 'Domain Authority',
'field' => 'domain_authority',
'min' => 0,
'max' => 100
]
],
// Writer Templates Filters
'writer_templates' => [
'prompt_name' => [
'type' => 'search',
'placeholder' => 'Search templates...',
'field' => 'prompt_name'
],
'prompt_type' => [
'type' => 'select',
'label' => 'Category',
'field' => 'prompt_type',
'options' => [
'content' => 'Blog',
'optimization' => 'Review',
'generation' => 'Product',
'custom' => 'Custom'
]
],
'is_active' => [
'type' => 'select',
'label' => 'Status',
'field' => 'is_active',
'options' => [
'1' => 'Active',
'0' => 'Draft'
]
],
'created_at' => [
'type' => 'date_range',
'label' => 'Created Date',
'field' => 'created_at'
]
],
// Optimizer Suggestions Filters
'optimizer_suggestions' => [
'page_url' => [
'type' => 'search',
'placeholder' => 'Search pages...',
'field' => 'page_url'
],
'suggestion_type' => [
'type' => 'select',
'label' => 'Suggestion Type',
'field' => 'suggestion_type',
'options' => [
'title_optimization' => 'Title Optimization',
'meta_description' => 'Meta Description',
'heading_structure' => 'Heading Structure',
'content_improvement' => 'Content Improvement',
'internal_linking' => 'Internal Linking'
]
],
'priority' => [
'type' => 'select',
'label' => 'Priority',
'field' => 'priority',
'options' => [
'high' => 'High',
'medium' => 'Medium',
'low' => 'Low'
]
],
'status' => [
'type' => 'select',
'label' => 'Status',
'field' => 'status',
'options' => [
'pending' => 'Pending',
'in_progress' => 'In Progress',
'completed' => 'Completed',
'dismissed' => 'Dismissed'
]
],
'impact_score' => [
'type' => 'range',
'label' => 'Impact Score',
'field' => 'impact_score',
'min' => 0,
'max' => 100
]
],
// Linker Campaigns Filters
'linker_campaigns' => [
'campaign_name' => [
'type' => 'search',
'placeholder' => 'Search campaigns...',
'field' => 'campaign_name'
],
'target_url' => [
'type' => 'search',
'placeholder' => 'Search target URLs...',
'field' => 'target_url'
],
'status' => [
'type' => 'select',
'label' => 'Status',
'field' => 'status',
'options' => [
'planning' => 'Planning',
'active' => 'Active',
'paused' => 'Paused',
'completed' => 'Completed',
'cancelled' => 'Cancelled'
]
],
'completion_percentage' => [
'type' => 'range',
'label' => 'Completion %',
'field' => 'completion_percentage',
'min' => 0,
'max' => 100
],
'start_date' => [
'type' => 'date_range',
'label' => 'Start Date',
'field' => 'start_date'
]
],
// Personalize Rewrites Filters
'personalize_rewrites' => [
'personalized_content' => [
'type' => 'search',
'placeholder' => 'Search personalized content...',
'field' => 'personalized_content'
],
'field_inputs' => [
'type' => 'search',
'placeholder' => 'Search field inputs...',
'field' => 'field_inputs'
],
'post_id' => [
'type' => 'search',
'placeholder' => 'Search by post ID...',
'field' => 'post_id'
],
'created_at' => [
'type' => 'date_range',
'label' => 'Created Date',
'field' => 'created_at'
]
],
// Personalize Tones Filters
'personalize_tones' => [
'tone_name' => [
'type' => 'search',
'placeholder' => 'Search tones...',
'field' => 'tone_name'
],
'category' => [
'type' => 'select',
'label' => 'Category',
'field' => 'category',
'options' => [
'business' => 'Business',
'creative' => 'Creative',
'technical' => 'Technical',
'marketing' => 'Marketing',
'educational' => 'Educational'
]
],
'status' => [
'type' => 'select',
'label' => 'Status',
'field' => 'status',
'options' => [
'active' => 'Active',
'inactive' => 'Inactive',
'draft' => 'Draft'
]
],
'usage_count' => [
'type' => 'range',
'label' => 'Usage Count',
'field' => 'usage_count',
'min' => 0,
'max' => 1000
]
]
];

View File

@@ -0,0 +1,638 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : forms-config.php
* @location : /modules/config/forms-config.php
* @type : Config Array
* @scope : Global
* @allowed : Form definitions, validation rules, field configurations
* @reusability : Globally Reusable
* @notes : Central form configuration for all modules
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
/**
* Get form configuration for a specific table
*
* @param string $table_id Table ID (e.g., 'planner_keywords')
* @return array|null Form configuration or null if not found
*/
function igny8_get_form_config($table_id) {
$form_configs = igny8_get_all_form_configs();
$GLOBALS['igny8_forms_config'] = $form_configs;
return igny8_get_dynamic_form_config($table_id);
}
/**
* Get all form configurations
*
* @return array All form configurations
*/
function igny8_get_all_form_configs() {
return [
'planner_keywords' => [
'fields' => [
[
'name' => 'keyword',
'type' => 'text',
'label' => 'Keyword',
'required' => true
],
[
'name' => 'search_volume',
'type' => 'number',
'label' => 'Search Volume',
'required' => false
],
[
'name' => 'difficulty',
'type' => 'select',
'label' => 'Difficulty',
'options' => [
'Very Easy' => 'Very Easy',
'Easy' => 'Easy',
'Medium' => 'Medium',
'Hard' => 'Hard',
'Very Hard' => 'Very Hard'
],
'required' => false
],
[
'name' => 'cpc',
'type' => 'number',
'label' => 'CPC',
'required' => false,
'step' => 0.01
],
[
'name' => 'intent',
'type' => 'select',
'label' => 'Intent',
'options' => [
'informational' => 'Informational',
'navigational' => 'Navigational',
'transactional' => 'Transactional',
'commercial' => 'Commercial'
],
'required' => false
],
[
'name' => 'status',
'type' => 'select',
'label' => 'Status',
'options' => [
'unmapped' => 'Unmapped',
'mapped' => 'Mapped',
'queued' => 'Queued',
'published' => 'Published'
],
'required' => true,
'default' => 'unmapped'
],
[
'name' => 'cluster_id',
'type' => 'select',
'label' => 'Cluster',
'source' => 'igny8_get_cluster_options',
'required' => false
]
],
'title' => 'Keyword',
'submit_text' => 'Save Keyword'
],
'planner_clusters' => [
'fields' => [
[
'name' => 'cluster_name',
'type' => 'text',
'label' => 'Cluster Name',
'required' => true
],
[
'name' => 'sector_id',
'type' => 'select',
'label' => 'Sector',
'source' => 'igny8_get_sector_options',
'required' => false
],
[
'name' => 'status',
'type' => 'select',
'label' => 'Status',
'options' => [
'active' => 'Active',
'inactive' => 'Inactive',
'archived' => 'Archived'
],
'required' => true,
'default' => 'active'
],
],
'title' => 'Cluster',
'submit_text' => 'Save Cluster'
],
'planner_ideas' => [
'fields' => [
[
'name' => 'idea_title',
'type' => 'text',
'label' => 'Idea Title',
'required' => true
],
[
'name' => 'idea_description',
'type' => 'textarea',
'label' => 'Description',
'required' => false
],
[
'name' => 'content_structure',
'type' => 'select',
'label' => 'Content Structure',
'options' => [
'cluster_hub' => 'Cluster Hub',
'landing_page' => 'Landing Page',
'guide_tutorial' => 'Guide Tutorial',
'how_to' => 'How To',
'comparison' => 'Comparison',
'review' => 'Review',
'top_listicle' => 'Top Listicle',
'question' => 'FAQ',
'product_description' => 'Product Description',
'service_page' => 'Service Page',
'home_page' => 'Home Page'
],
'required' => true,
'default' => 'cluster_hub'
],
[
'name' => 'content_type',
'type' => 'select',
'label' => 'Content Type',
'options' => [
'post' => 'Post',
'product' => 'Product',
'page' => 'Page',
'CPT' => 'Custom Post Type'
],
'required' => true,
'default' => 'post'
],
[
'name' => 'target_keywords',
'type' => 'textarea',
'label' => 'Target Keywords (comma-separated)',
'required' => false,
'placeholder' => 'Enter target keywords for this idea, separated by commas...',
'rows' => 3
],
[
'name' => 'image_prompts',
'type' => 'textarea',
'label' => 'Image Prompts (JSON)',
'required' => false,
'placeholder' => 'Enter image prompts as JSON...',
'rows' => 4,
'help_text' => 'JSON format: {"featured_image": "prompt", "in_article_image_1": "prompt", "in_article_image_2": "prompt"}'
],
[
'name' => 'keyword_cluster_id',
'type' => 'select',
'label' => 'Cluster',
'source' => 'igny8_get_cluster_options',
'required' => false
],
[
'name' => 'source',
'type' => 'select',
'label' => 'Source',
'options' => [
'AI' => 'AI',
'Manual' => 'Manual'
],
'required' => true,
'default' => 'AI'
],
[
'name' => 'status',
'type' => 'select',
'label' => 'Status',
'options' => [
'new' => 'New',
'scheduled' => 'Scheduled',
'published' => 'Published'
],
'required' => true,
'default' => 'new'
],
[
'name' => 'estimated_word_count',
'type' => 'number',
'label' => 'Estimated Words',
'required' => false
]
],
'title' => 'Content Idea',
'submit_text' => 'Save Idea'
],
'writer_tasks' => [
'fields' => [
[
'name' => 'title',
'type' => 'text',
'label' => 'Task Title',
'required' => true
],
[
'name' => 'cluster_id',
'type' => 'select',
'label' => 'Cluster Name',
'source' => 'igny8_get_cluster_options',
'required' => false
],
[
'name' => 'keywords',
'type' => 'text',
'label' => 'Keywords',
'required' => false
],
[
'name' => 'word_count',
'type' => 'number',
'label' => 'Word Count',
'required' => false
],
[
'name' => 'status',
'type' => 'select',
'label' => 'Status',
'options' => [
'queued' => 'Queued',
'in_progress' => 'In Progress',
'completed' => 'Completed',
'cancelled' => 'Cancelled',
'draft' => 'Draft',
'review' => 'Review',
'published' => 'Published'
],
'required' => true,
'default' => 'queued'
],
[
'name' => 'content_structure',
'type' => 'select',
'label' => 'Content Structure',
'options' => [
'cluster_hub' => 'Cluster Hub',
'landing_page' => 'Landing Page',
'guide_tutorial' => 'Guide Tutorial',
'how_to' => 'How To',
'comparison' => 'Comparison',
'review' => 'Review',
'top_listicle' => 'Top Listicle',
'question' => 'FAQ',
'product_description' => 'Product Description',
'service_page' => 'Service Page',
'home_page' => 'Home Page'
],
'required' => true,
'default' => 'cluster_hub'
],
[
'name' => 'content_type',
'type' => 'select',
'label' => 'Content Type',
'options' => [
'post' => 'Post',
'product' => 'Product',
'page' => 'Page',
'CPT' => 'Custom Post Type'
],
'required' => true,
'default' => 'post'
],
[
'name' => 'created_at',
'type' => 'text',
'label' => 'Created',
'required' => false,
'readonly' => true
]
],
'title' => 'Queue Task',
'submit_text' => 'Add to Queue'
],
'writer_drafts' => [
'fields' => [
[
'name' => 'title',
'type' => 'text',
'label' => 'Title',
'required' => true
],
[
'name' => 'status',
'type' => 'select',
'label' => 'Status',
'options' => [
'draft' => 'Draft'
],
'required' => true,
'default' => 'draft'
],
[
'name' => 'cluster_id',
'type' => 'select',
'label' => 'Cluster',
'source' => 'igny8_get_cluster_options',
'required' => false
],
[
'name' => 'content_structure',
'type' => 'select',
'label' => 'Content Structure',
'options' => [
'cluster_hub' => 'Cluster Hub',
'landing_page' => 'Landing Page',
'guide_tutorial' => 'Guide Tutorial',
'how_to' => 'How To',
'comparison' => 'Comparison',
'review' => 'Review',
'top_listicle' => 'Top Listicle',
'question' => 'FAQ',
'product_description' => 'Product Description',
'service_page' => 'Service Page',
'home_page' => 'Home Page'
],
'required' => false,
'default' => 'cluster_hub'
],
[
'name' => 'content_type',
'type' => 'select',
'label' => 'Content Type',
'options' => [
'post' => 'Post',
'product' => 'Product',
'page' => 'Page',
'CPT' => 'Custom Post Type'
],
'required' => false,
'default' => 'post'
],
[
'name' => 'meta_title',
'label' => 'Meta Title',
'type' => 'text',
'placeholder' => 'Enter SEO title...',
'maxlength' => 60
],
[
'name' => 'meta_description',
'label' => 'Meta Description',
'type' => 'textarea',
'placeholder' => 'Enter meta description...',
'maxlength' => 160
],
[
'name' => 'keywords',
'label' => 'Primary Keywords',
'type' => 'text',
'placeholder' => 'e.g., duvet covers, king size'
],
[
'name' => 'word_count',
'label' => 'Word Count',
'type' => 'number',
'readonly' => true
],
[
'name' => 'updated_at',
'type' => 'text',
'label' => 'Updated',
'required' => false,
'readonly' => true
]
],
'title' => 'Content Draft',
'submit_text' => 'Save Draft'
],
'writer_published' => [
'fields' => [
[
'name' => 'title',
'type' => 'text',
'label' => 'Title',
'required' => true
],
[
'name' => 'status',
'type' => 'select',
'label' => 'Status',
'options' => [
'published' => 'Published'
],
'required' => true,
'default' => 'published'
],
[
'name' => 'cluster_id',
'type' => 'select',
'label' => 'Cluster',
'source' => 'igny8_get_cluster_options',
'required' => false
],
[
'name' => 'content_structure',
'type' => 'select',
'label' => 'Content Structure',
'options' => [
'cluster_hub' => 'Cluster Hub',
'landing_page' => 'Landing Page',
'guide_tutorial' => 'Guide Tutorial',
'how_to' => 'How To',
'comparison' => 'Comparison',
'review' => 'Review',
'top_listicle' => 'Top Listicle',
'question' => 'FAQ',
'product_description' => 'Product Description',
'service_page' => 'Service Page',
'home_page' => 'Home Page'
],
'required' => false,
'default' => 'cluster_hub'
],
[
'name' => 'content_type',
'type' => 'select',
'label' => 'Content Type',
'options' => [
'post' => 'Post',
'product' => 'Product',
'page' => 'Page',
'CPT' => 'Custom Post Type'
],
'required' => false,
'default' => 'post'
],
[
'name' => 'meta_title',
'label' => 'Meta Title',
'type' => 'text',
'placeholder' => 'Enter SEO title...',
'maxlength' => 60
],
[
'name' => 'meta_description',
'label' => 'Meta Description',
'type' => 'textarea',
'placeholder' => 'Enter meta description...',
'maxlength' => 160
],
[
'name' => 'keywords',
'label' => 'Primary Keywords',
'type' => 'text',
'placeholder' => 'e.g., duvet covers, king size'
],
[
'name' => 'word_count',
'label' => 'Word Count',
'type' => 'number',
'readonly' => true
],
[
'name' => 'updated_at',
'type' => 'text',
'label' => 'Updated',
'required' => false,
'readonly' => true
]
],
'title' => 'Published Content',
'submit_text' => 'Save Published Content'
],
'writer_templates' => [
'fields' => [
[
'name' => 'prompt_name',
'type' => 'text',
'label' => 'Template Name',
'required' => true,
'placeholder' => 'Enter template name...'
],
[
'name' => 'prompt_type',
'type' => 'select',
'label' => 'Category',
'options' => [
'content' => 'Blog',
'optimization' => 'Review',
'generation' => 'Product',
'custom' => 'Custom'
],
'required' => true,
'default' => 'content'
],
[
'name' => 'is_active',
'type' => 'select',
'label' => 'Status',
'options' => [
'1' => 'Active',
'0' => 'Draft'
],
'required' => true,
'default' => '1'
],
[
'name' => 'prompt_text',
'type' => 'textarea',
'label' => 'Prompt Body',
'required' => true,
'rows' => 10,
'placeholder' => 'Enter the prompt template...'
],
[
'name' => 'variables',
'type' => 'textarea',
'label' => 'Variables (JSON)',
'required' => false,
'rows' => 5,
'placeholder' => '{"label": "Custom Label", "description": "Template description"}'
]
],
'title' => 'Content Template',
'submit_text' => 'Save Template'
],
'personalize_data' => [
'fields' => [
[
'name' => 'post_id',
'type' => 'number',
'label' => 'Post ID',
'required' => true
],
[
'name' => 'data_type',
'type' => 'text',
'label' => 'Data Type',
'required' => true
],
[
'name' => 'data',
'type' => 'textarea',
'label' => 'Data (JSON)',
'required' => true,
'rows' => 10
]
],
'title' => 'Personalization Data',
'submit_text' => 'Save Data'
],
'personalize_variations' => [
'fields' => [
[
'name' => 'post_id',
'type' => 'number',
'label' => 'Post ID',
'required' => true
],
[
'name' => 'fields_hash',
'type' => 'text',
'label' => 'Fields Hash',
'required' => true
],
[
'name' => 'fields_json',
'type' => 'textarea',
'label' => 'Fields JSON',
'required' => true,
'rows' => 5
],
[
'name' => 'content',
'type' => 'textarea',
'label' => 'Content',
'required' => true,
'rows' => 15
]
],
'title' => 'Content Variation',
'submit_text' => 'Save Variation'
]
];
}

View File

@@ -0,0 +1,150 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : import-export-config.php
* @location : /modules/config/import-export-config.php
* @type : Config Array
* @scope : Global
* @allowed : Import/export definitions, file handling configurations
* @reusability : Globally Reusable
* @notes : Central import/export configuration for all modules
*/
// Prevent direct access
if (!defined('ABSPATH') && !defined('IGNY8_INCLUDE_CONFIG')) {
exit;
}
return [
// PLANNER MODULE (4 submodules)
'planner_keywords' => [
'type' => 'keywords',
'singular' => 'Keyword',
'plural' => 'Keywords',
'template_file' => 'igny8_keywords_template.csv',
'columns' => ['keyword', 'search_volume', 'difficulty', 'cpc', 'intent', 'status', 'sector_id', 'cluster_id'],
'required_fields' => ['keyword']
],
'planner_clusters' => [
'type' => 'clusters',
'singular' => 'Cluster',
'plural' => 'Clusters',
'template_file' => 'igny8_clusters_template.csv',
'columns' => ['cluster_name', 'sector_id', 'status', 'keyword_count', 'total_volume', 'avg_difficulty', 'mapped_pages_count'],
'required_fields' => ['cluster_name']
],
'planner_ideas' => [
'type' => 'ideas',
'singular' => 'Idea',
'plural' => 'Ideas',
'template_file' => 'igny8_ideas_template.csv',
'columns' => ['idea_title', 'idea_description', 'content_structure', 'content_type', 'keyword_cluster_id', 'target_keywords', 'status', 'estimated_word_count'],
'required_fields' => ['idea_title']
],
// WRITER MODULE (3 submodules)
'writer_tasks' => [
'type' => 'tasks',
'singular' => 'Task',
'plural' => 'Tasks',
'template_file' => 'igny8_tasks_template.csv',
'columns' => ['title', 'description', 'content_type', 'cluster_id', 'priority', 'status', 'keywords', 'schedule_at'],
'required_fields' => ['title']
],
'writer_drafts' => [
'type' => 'tasks',
'singular' => 'Draft',
'plural' => 'Drafts',
'template_file' => 'igny8_tasks_template.csv',
'columns' => ['title', 'description', 'content_type', 'cluster_id', 'status', 'assigned_post_id'],
'required_fields' => ['title']
],
'writer_published' => [
'type' => 'tasks',
'singular' => 'Published Content',
'plural' => 'Published Content',
'template_file' => 'igny8_tasks_template.csv',
'columns' => ['title', 'description', 'content_type', 'cluster_id', 'status', 'assigned_post_id'],
'required_fields' => ['title']
],
'writer_templates' => [
'type' => 'templates',
'singular' => 'Template',
'plural' => 'Templates',
'template_file' => 'igny8_templates_template.csv',
'columns' => ['template_name', 'prompt_type', 'system_prompt', 'user_prompt', 'is_active'],
'required_fields' => ['template_name']
],
// OPTIMIZER MODULE (2 submodules)
'optimizer_audits' => [
'type' => 'audits',
'singular' => 'Audit',
'plural' => 'Audits',
'template_file' => 'igny8_audits_template.csv',
'columns' => ['page_id', 'audit_status', 'seo_score', 'issues_found', 'recommendations'],
'required_fields' => ['page_id']
],
'optimizer_suggestions' => [
'type' => 'suggestions',
'singular' => 'Suggestion',
'plural' => 'Suggestions',
'template_file' => 'igny8_suggestions_template.csv',
'columns' => ['audit_id', 'suggestion_type', 'priority', 'status', 'impact_level'],
'required_fields' => ['audit_id']
],
// LINKER MODULE (2 submodules)
'linker_backlinks' => [
'type' => 'backlinks',
'singular' => 'Backlink',
'plural' => 'Backlinks',
'template_file' => 'igny8_backlinks_template.csv',
'columns' => ['source_url', 'target_url', 'anchor_text', 'domain_authority', 'link_type', 'status'],
'required_fields' => ['source_url', 'target_url']
],
'linker_campaigns' => [
'type' => 'campaigns',
'singular' => 'Campaign',
'plural' => 'Campaigns',
'template_file' => 'igny8_campaigns_template.csv',
'columns' => ['campaign_name', 'target_url', 'status', 'backlink_count', 'live_links_count'],
'required_fields' => ['campaign_name']
],
// PERSONALIZE MODULE (4 submodules)
'personalize_rewrites' => [
'type' => 'rewrites',
'singular' => 'Rewrite',
'plural' => 'Rewrites',
'template_file' => 'igny8_rewrites_template.csv',
'columns' => ['post_id', 'tone_id', 'variation_content', 'created_at'],
'required_fields' => ['post_id']
],
'personalize_tones' => [
'type' => 'tones',
'singular' => 'Tone',
'plural' => 'Tones',
'template_file' => 'igny8_tones_template.csv',
'columns' => ['tone_name', 'tone_type', 'description', 'status', 'usage_count'],
'required_fields' => ['tone_name']
],
'personalize_data' => [
'type' => 'personalization_data',
'singular' => 'Data Entry',
'plural' => 'Data Entries',
'template_file' => 'igny8_personalization_data_template.csv',
'columns' => ['data_key', 'data_value', 'data_type', 'created_at'],
'required_fields' => ['data_key']
],
'personalize_variations' => [
'type' => 'variations',
'singular' => 'Variation',
'plural' => 'Variations',
'template_file' => 'igny8_variations_template.csv',
'columns' => ['post_id', 'field_name', 'variation_content', 'tone_id'],
'required_fields' => ['post_id', 'field_name']
]
];

View File

@@ -0,0 +1,581 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : kpi-config.php
* @location : /modules/config/kpi-config.php
* @type : Config Array
* @scope : Global
* @allowed : KPI definitions, metrics configurations
* @reusability : Globally Reusable
* @notes : Central KPI configuration for all modules
*/
// Prevent direct access only if not being included
if (!defined('ABSPATH') && !defined('IGNY8_INCLUDE_CONFIG')) {
exit;
}
return [
// Keywords KPIs
'planner_keywords' => [
'total_keywords' => [
'label' => 'Total Keywords',
'query' => 'SELECT COUNT(*) as count FROM {table_name}',
'color' => 'blue'
],
'mapped_keywords' => [
'label' => 'Mapped',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE status = "mapped"',
'color' => 'green'
],
'unmapped_keywords' => [
'label' => 'Unmapped',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE status = "unmapped"',
'color' => 'amber'
],
'total_volume' => [
'label' => 'Total Volume',
'query' => 'SELECT SUM(search_volume) as count FROM {table_name}',
'color' => 'purple'
],
'avg_difficulty' => [
'label' => 'Avg Difficulty',
'query' => 'SELECT ROUND(AVG(difficulty)) as count FROM {table_name}',
'color' => 'blue'
],
'high_volume_keywords' => [
'label' => 'High Volume (>1K)',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE search_volume > 1000',
'color' => 'green'
]
],
// Clusters KPIs
'planner_clusters' => [
'total_clusters' => [
'label' => 'Clusters',
'query' => 'SELECT COUNT(*) as count FROM {table_name}',
'color' => ''
],
'total_volume' => [
'label' => 'Volume',
'query' => 'SELECT SUM(total_volume) as count FROM {table_name}',
'color' => 'green'
],
'total_keywords' => [
'label' => 'Keywords',
'query' => 'SELECT SUM(keyword_count) as count FROM {table_name}',
'color' => 'amber'
],
'mapped_clusters' => [
'label' => 'Mapped',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE mapped_pages_count > 0',
'color' => 'purple'
],
'avg_keywords_per_cluster' => [
'label' => 'Avg Keywords/Cluster',
'query' => 'SELECT ROUND(AVG(keyword_count)) as count FROM {table_name}',
'color' => 'blue'
],
'high_volume_clusters' => [
'label' => 'High Volume Clusters',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE total_volume > 10000',
'color' => 'green'
]
],
// Ideas KPIs
'planner_ideas' => [
'total_ideas' => [
'label' => 'Ideas',
'query' => 'SELECT COUNT(*) as count FROM {table_name}',
'color' => ''
],
'new_ideas' => [
'label' => 'New',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE status = "new"',
'color' => 'green'
],
'scheduled_ideas' => [
'label' => 'Scheduled',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE status = "scheduled"',
'color' => 'amber'
],
'published_ideas' => [
'label' => 'Published',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE status = "published"',
'color' => 'purple'
],
'ai_generated' => [
'label' => 'AI Generated',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE source = "AI"',
'color' => 'blue'
],
'recent_ideas' => [
'label' => 'This Week',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)',
'color' => 'green'
]
],
// Planner Home KPIs (Main Dashboard)
'planner_home' => [
'total_keywords' => [
'label' => 'Keywords',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_keywords',
'color' => ''
],
'total_volume' => [
'label' => 'Volume',
'query' => 'SELECT SUM(search_volume) as count FROM {prefix}igny8_keywords',
'color' => 'green'
],
'total_clusters' => [
'label' => 'Clusters',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_clusters',
'color' => 'amber'
],
'total_ideas' => [
'label' => 'Ideas',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_content_ideas',
'color' => 'purple'
],
'high_volume_keywords' => [
'label' => 'High Vol (>1K)',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_keywords WHERE search_volume > 1000',
'color' => 'blue'
],
'recent_ideas' => [
'label' => 'This Week',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_content_ideas WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)',
'color' => 'teal'
],
'mapped_keywords' => [
'label' => 'Mapped Keywords',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_keywords WHERE status = "mapped"',
'color' => 'green'
],
'unmapped_keywords' => [
'label' => 'Unmapped Keywords',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_keywords WHERE status = "unmapped"',
'color' => 'amber'
],
'clusters_with_ideas' => [
'label' => 'Clusters With Ideas',
'query' => 'SELECT COUNT(DISTINCT keyword_cluster_id) as count FROM {prefix}igny8_content_ideas WHERE keyword_cluster_id IS NOT NULL',
'color' => 'green'
],
'queued_ideas' => [
'label' => 'Queued Ideas',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_content_ideas WHERE status = "scheduled"',
'color' => 'amber'
]
],
// Writer Home KPIs (Main Dashboard)
'writer_home' => [
'queued_tasks' => [
'label' => 'Queued Tasks',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_tasks WHERE status IN ("queued", "in_progress")',
'color' => 'blue'
],
'draft_tasks' => [
'label' => 'Drafts',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_tasks WHERE status IN ("draft", "review")',
'color' => 'amber'
],
'published_tasks' => [
'label' => 'Published',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_tasks WHERE status = "published"',
'color' => 'green'
],
'total_tasks' => [
'label' => 'Total Tasks',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_tasks',
'color' => ''
]
],
// Writer Tasks KPIs
'writer_tasks' => [
'total_ideas' => [
'label' => 'Ideas',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_content_ideas',
'color' => ''
],
'content_scheduled' => [
'label' => 'Content Scheduled',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_tasks WHERE status IN ("queued", "in_progress")',
'color' => 'green'
],
'written' => [
'label' => 'Written',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_tasks WHERE status IN ("draft", "review")',
'color' => 'amber'
],
'published' => [
'label' => 'Published',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_tasks WHERE status = "published"',
'color' => 'purple'
]
],
// Writer Drafts KPIs
'writer_drafts' => [
'total_ideas' => [
'label' => 'Ideas',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_content_ideas',
'color' => ''
],
'content_scheduled' => [
'label' => 'Content Scheduled',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_tasks WHERE status IN ("queued", "in_progress")',
'color' => 'green'
],
'written' => [
'label' => 'Written',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_tasks WHERE status IN ("draft", "review")',
'color' => 'amber'
],
'published' => [
'label' => 'Published',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_tasks WHERE status = "published"',
'color' => 'purple'
]
],
// Writer Published KPIs
'writer_published' => [
'total_ideas' => [
'label' => 'Ideas',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_content_ideas',
'color' => ''
],
'content_scheduled' => [
'label' => 'Content Scheduled',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_tasks WHERE status IN ("queued", "in_progress")',
'color' => 'green'
],
'written' => [
'label' => 'Written',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_tasks WHERE status IN ("draft", "review")',
'color' => 'amber'
],
'published' => [
'label' => 'Published',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_tasks WHERE status = "published"',
'color' => 'purple'
]
],
// Optimizer Audits KPIs
'optimizer_audits' => [
'total_audits' => [
'label' => 'Total Audits',
'query' => 'SELECT COUNT(*) as count FROM {table_name}',
'color' => 'blue'
],
'completed_audits' => [
'label' => 'Completed',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE audit_status = "completed"',
'color' => 'green'
],
'pending_audits' => [
'label' => 'Pending',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE audit_status = "pending"',
'color' => 'amber'
],
'avg_seo_score' => [
'label' => 'Avg SEO Score',
'query' => 'SELECT ROUND(AVG(seo_score)) as count FROM {table_name} WHERE audit_status = "completed"',
'color' => 'purple'
],
'total_issues' => [
'label' => 'Total Issues',
'query' => 'SELECT SUM(issues_found) as count FROM {table_name} WHERE audit_status = "completed"',
'color' => 'blue'
],
'recent_audits' => [
'label' => 'This Week',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)',
'color' => 'green'
]
],
// Writer Templates KPIs
'writer_templates' => [
'total_templates' => [
'label' => 'Total Templates',
'query' => 'SELECT COUNT(*) as count FROM {table_name}',
'color' => 'blue'
],
'active_templates' => [
'label' => 'Active',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE is_active = 1',
'color' => 'green'
],
'draft_templates' => [
'label' => 'Draft',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE is_active = 0',
'color' => 'gray'
],
'content_templates' => [
'label' => 'Blog Templates',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE prompt_type = "content"',
'color' => 'blue'
],
'product_templates' => [
'label' => 'Product Templates',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE prompt_type = "generation"',
'color' => 'green'
],
'popular_templates' => [
'label' => 'Popular (>10 uses)',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE usage_count > 10',
'color' => 'purple'
]
],
// Optimizer Suggestions KPIs
'optimizer_suggestions' => [
'total_suggestions' => [
'label' => 'Total Suggestions',
'query' => 'SELECT COUNT(*) as count FROM {table_name}',
'color' => 'blue'
],
'implemented' => [
'label' => 'Implemented',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE status = "implemented"',
'color' => 'green'
],
'pending' => [
'label' => 'Pending',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE status = "pending"',
'color' => 'amber'
],
'high_impact' => [
'label' => 'High Impact',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE impact_level = "high"',
'color' => 'purple'
],
'avg_improvement' => [
'label' => 'Avg Improvement',
'query' => 'SELECT ROUND(AVG(improvement_score)) as count FROM {table_name} WHERE status = "implemented"',
'color' => 'blue'
],
'recent_suggestions' => [
'label' => 'This Week',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)',
'color' => 'green'
]
],
// Linker Backlinks KPIs
'linker_backlinks' => [
'total_backlinks' => [
'label' => 'Total Backlinks',
'query' => 'SELECT COUNT(*) as count FROM {table_name}',
'color' => 'blue'
],
'active_backlinks' => [
'label' => 'Active',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE status = "active"',
'color' => 'green'
],
'lost_backlinks' => [
'label' => 'Lost',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE status = "lost"',
'color' => 'amber'
],
'avg_domain_authority' => [
'label' => 'Avg DA',
'query' => 'SELECT ROUND(AVG(domain_authority)) as count FROM {table_name} WHERE status = "active"',
'color' => 'purple'
],
'dofollow_links' => [
'label' => 'DoFollow Links',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE link_type = "dofollow" AND status = "active"',
'color' => 'blue'
],
'recent_backlinks' => [
'label' => 'This Week',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)',
'color' => 'green'
]
],
// Linker Campaigns KPIs
'linker_campaigns' => [
'total_campaigns' => [
'label' => 'Total Campaigns',
'query' => 'SELECT COUNT(*) as count FROM {table_name}',
'color' => 'blue'
],
'active_campaigns' => [
'label' => 'Active',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE status = "active"',
'color' => 'green'
],
'completed_campaigns' => [
'label' => 'Completed',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE status = "completed"',
'color' => 'amber'
],
'avg_links_per_campaign' => [
'label' => 'Avg Links/Campaign',
'query' => 'SELECT ROUND(AVG(links_acquired)) as count FROM {table_name}',
'color' => 'purple'
],
'success_rate' => [
'label' => 'Success Rate %',
'query' => 'SELECT ROUND((COUNT(CASE WHEN status = "completed" THEN 1 END) * 100.0 / COUNT(*))) as count FROM {table_name}',
'color' => 'blue'
],
'recent_campaigns' => [
'label' => 'This Week',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)',
'color' => 'green'
]
],
// Personalize Rewrites KPIs
'personalize_rewrites' => [
'total_rewrites' => [
'label' => 'Total Variations',
'query' => 'SELECT COUNT(*) as count FROM {table_name}',
'color' => 'blue'
],
'this_month_rewrites' => [
'label' => 'This Month',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE created_at >= DATE_FORMAT(NOW(), "%Y-%m-01")',
'color' => 'amber'
],
'total_ai_sessions' => [
'label' => 'AI Sessions',
'query' => 'SELECT COUNT(*) as count FROM {prefix}igny8_logs WHERE log_type = "field_detection" OR log_type = "content_generation"',
'color' => 'green'
],
'avg_sessions_per_rewrite' => [
'label' => 'Avg Sessions/Rewrite',
'query' => 'SELECT ROUND((SELECT COUNT(*) FROM {prefix}igny8_logs WHERE log_type = "field_detection" OR log_type = "content_generation") / GREATEST(COUNT(*), 1), 1) as count FROM {table_name}',
'color' => 'purple'
],
'recent_rewrites' => [
'label' => 'This Week',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)',
'color' => 'green'
],
'successful_rewrites' => [
'label' => 'Successful',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE status = "completed"',
'color' => 'blue'
]
],
// Personalize Tones KPIs
'personalize_tones' => [
'total_tones' => [
'label' => 'Total Tones',
'query' => 'SELECT COUNT(*) as count FROM {table_name}',
'color' => 'blue'
],
'active_tones' => [
'label' => 'Active',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE status = "active"',
'color' => 'green'
],
'custom_tones' => [
'label' => 'Custom',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE tone_type = "custom"',
'color' => 'amber'
],
'avg_usage_frequency' => [
'label' => 'Avg Usage',
'query' => 'SELECT ROUND(AVG(usage_count)) as count FROM {table_name}',
'color' => 'purple'
],
'popular_tones' => [
'label' => 'Popular (>50 uses)',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE usage_count > 50',
'color' => 'blue'
],
'recent_tones' => [
'label' => 'This Week',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)',
'color' => 'green'
]
],
// Personalization Data KPIs
'personalize_data' => [
'total_data_entries' => [
'label' => 'Total Data Entries',
'query' => 'SELECT COUNT(*) as count FROM {table_name}',
'color' => 'blue'
],
'personalization_data' => [
'label' => 'Personalization Data',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE data_type = "personalization"',
'color' => 'green'
],
'field_data' => [
'label' => 'Field Data',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE data_type = "fields"',
'color' => 'amber'
],
'recent_entries' => [
'label' => 'Recent (7 days)',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)',
'color' => 'purple'
],
'active_entries' => [
'label' => 'Active',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE status = "active"',
'color' => 'green'
],
'avg_usage_per_entry' => [
'label' => 'Avg Usage/Entry',
'query' => 'SELECT ROUND(AVG(usage_count)) as count FROM {table_name}',
'color' => 'blue'
]
],
// Personalization Variations KPIs
'personalize_variations' => [
'total_variations' => [
'label' => 'Total Variations',
'query' => 'SELECT COUNT(*) as count FROM {table_name}',
'color' => 'blue'
],
'unique_posts' => [
'label' => 'Unique Posts',
'query' => 'SELECT COUNT(DISTINCT post_id) as count FROM {table_name}',
'color' => 'green'
],
'recent_variations' => [
'label' => 'Recent (7 days)',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)',
'color' => 'amber'
],
'avg_variations_per_post' => [
'label' => 'Avg Variations/Post',
'query' => 'SELECT ROUND(COUNT(*) / COUNT(DISTINCT post_id), 2) as count FROM {table_name}',
'color' => 'purple'
],
'published_variations' => [
'label' => 'Published',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE status = "published"',
'color' => 'green'
],
'high_performing_variations' => [
'label' => 'High Performing',
'query' => 'SELECT COUNT(*) as count FROM {table_name} WHERE performance_score > 80',
'color' => 'blue'
]
]
];

View File

@@ -0,0 +1,989 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : tables-config.php
* @location : /modules/config/tables-config.php
* @type : Config Array
* @scope : Global
* @allowed : Table definitions, column structures, configuration data
* @reusability : Globally Reusable
* @notes : Central table configuration for all modules
*/
// Prevent direct access only if not being included
if (!defined('ABSPATH') && !defined('IGNY8_INCLUDE_CONFIG')) {
exit;
}
return [
// Keywords Table
'planner_keywords' => [
'table' => 'igny8_keywords',
'title' => 'Keywords Management',
'humanize_columns' => ['keyword', 'search_volume', 'difficulty', 'cpc', 'intent', 'status', 'cluster_id'],
'columns' => [
'keyword' => [
'label' => 'Keyword',
'type' => 'text',
'sortable' => true,
'searchable' => true
],
'search_volume' => [
'label' => 'Volume',
'type' => 'number',
'sortable' => true,
'format' => 'number'
],
'difficulty' => [
'label' => 'Difficulty',
'type' => 'number',
'sortable' => true,
'format' => 'difficulty_label'
],
'cpc' => [
'label' => 'CPC',
'type' => 'number',
'sortable' => true,
'decimal' => true
],
'intent' => [
'label' => 'Intent',
'type' => 'enum',
'options' => ['informational', 'navigational', 'transactional', 'commercial'],
'sortable' => true
],
'status' => [
'label' => 'Status',
'type' => 'enum',
'options' => ['unmapped', 'mapped', 'queued', 'published'],
'sortable' => true
],
'cluster_id' => [
'label' => 'Cluster',
'type' => 'lookup',
'source_field' => 'cluster_id',
'display_field' => 'cluster_name',
'sortable' => true,
'join_query' => 'LEFT JOIN {prefix}igny8_clusters c ON {table_name}.cluster_id = c.id',
'select_field' => 'c.cluster_name as cluster_name'
]
],
'pagination' => ['per_page' => 10, 'enabled' => true],
'search_field' => 'keyword',
'search_placeholder' => 'Search keywords...',
'actions' => ['delete_selected', 'export_selected', 'import', 'add_new'],
'bulk_actions' => ['delete', 'map'],
'row_actions' => ['edit', 'delete']
],
// Clusters Table
'planner_clusters' => [
'table' => 'igny8_clusters',
'title' => 'Clusters Management',
'humanize_columns' => ['cluster_name', 'sector_id', 'status', 'keyword_count', 'total_volume', 'avg_difficulty', 'mapped_pages_count', 'created_at'],
'columns' => [
'cluster_name' => [
'label' => 'Cluster Name',
'type' => 'text',
'sortable' => true,
'searchable' => true
],
'sector_id' => [
'label' => 'Sectors',
'type' => 'text',
'source_field' => 'sector_id',
'display_field' => 'sector_name',
'sortable' => true,
'join_query' => 'LEFT JOIN {prefix}igny8_sectors s ON {table_name}.sector_id = s.id',
'select_field' => 's.sector_name as sector_name'
],
'status' => [
'label' => 'Status',
'type' => 'enum',
'options' => ['active', 'inactive', 'archived'],
'sortable' => true
],
'keyword_count' => [
'label' => 'Keywords',
'type' => 'number',
'sortable' => true,
'calculated' => false
//'calculation_query' => 'SELECT COUNT(k.id) FROM {prefix}igny8_keywords k WHERE k.cluster_id = {table_name}.id'
],
'total_volume' => [
'label' => 'Total Volume',
'type' => 'number',
'sortable' => true,
'format' => 'number'
],
'avg_difficulty' => [
'label' => 'Avg KD',
'type' => 'number',
'sortable' => true,
'format' => 'difficulty_label'
],
'mapped_pages_count' => [
'label' => 'Mapped Pages',
'type' => 'number',
'sortable' => true,
'calculated' => false
],
'created_at' => [
'label' => 'Created',
'type' => 'date',
'sortable' => true,
'format' => 'date'
]
],
'pagination' => ['per_page' => 10, 'enabled' => true],
'search_field' => 'cluster_name',
'search_placeholder' => 'Search clusters...',
'actions' => ['delete_selected', 'export_selected', 'import', 'add_new'],
'bulk_actions' => ['delete', 'activate', 'deactivate'],
'row_actions' => ['edit', 'delete', 'view_keywords']
],
// Ideas Table
'planner_ideas' => [
'table' => 'igny8_content_ideas',
'title' => 'Content Ideas Management',
'humanize_columns' => ['idea_title', 'content_structure', 'content_type', 'target_keywords', 'keyword_cluster_id', 'status', 'estimated_word_count', 'created_at'],
'columns' => [
'idea_title' => [
'label' => 'Title',
'type' => 'text',
'sortable' => true,
'searchable' => true
],
'content_structure' => [
'label' => 'Structure',
'type' => 'enum',
'options' => ['cluster_hub', 'landing_page', 'guide_tutorial', 'how_to', 'comparison', 'review', 'top_listicle', 'question', 'product_description', 'service_page', 'home_page'],
'sortable' => true
],
'content_type' => [
'label' => 'Type',
'type' => 'enum',
'options' => ['post', 'product', 'page', 'CPT'],
'sortable' => true
],
'target_keywords' => [
'label' => 'Target Keywords',
'type' => 'text',
'sortable' => true,
'searchable' => true
],
'keyword_cluster_id' => [
'label' => 'Cluster',
'type' => 'lookup',
'source_field' => 'keyword_cluster_id',
'display_field' => 'cluster_name',
'sortable' => true,
'join_query' => 'LEFT JOIN {prefix}igny8_clusters c ON {table_name}.keyword_cluster_id = c.id',
'select_field' => 'c.cluster_name as cluster_name'
],
'status' => [
'label' => 'Status',
'type' => 'enum',
'options' => ['new', 'scheduled', 'published'],
'sortable' => true
],
'estimated_word_count' => [
'label' => 'Words',
'type' => 'number',
'sortable' => true
],
'created_at' => [
'label' => 'Created',
'type' => 'date',
'sortable' => true,
'format' => 'date'
]
],
'pagination' => ['per_page' => 10, 'enabled' => true],
'search_field' => 'idea_title',
'search_placeholder' => 'Search ideas...',
'actions' => ['delete_selected', 'export_selected', 'import', 'add_new'],
'bulk_actions' => ['delete', 'change_status', 'bulk_queue_to_writer'],
'row_actions' => ['edit', 'delete', 'create_draft', 'queue_to_writer']
],
// Writer Tasks Table (Content Queue / Tasks)
'writer_tasks' => [
'table' => 'igny8_tasks',
'title' => 'Content Queue / Tasks',
'humanize_columns' => ['title', 'cluster_id', 'keywords', 'word_count', 'status', 'content_structure', 'content_type', 'created_at'],
'default_filter' => [
'status' => ['queued', 'in_progress']
],
'columns' => [
'title' => [
'label' => 'Task Title',
'type' => 'text',
'sortable' => true,
'searchable' => true
],
'cluster_id' => [
'label' => 'Cluster Name',
'type' => 'lookup',
'source_field' => 'cluster_id',
'display_field' => 'cluster_name',
'sortable' => true,
'join_query' => 'LEFT JOIN {prefix}igny8_clusters c ON {table_name}.cluster_id = c.id',
'select_field' => 'c.cluster_name as cluster_name'
],
'keywords' => [
'label' => 'Keywords',
'type' => 'text',
'sortable' => true,
'searchable' => true
],
'word_count' => [
'label' => 'Word Count',
'type' => 'number',
'sortable' => true,
'format' => 'number'
],
'status' => [
'label' => 'Status',
'type' => 'enum',
'sortable' => true,
'options' => ['queued', 'in_progress', 'completed', 'cancelled', 'draft', 'review', 'published']
],
'content_structure' => [
'label' => 'Structure',
'type' => 'enum',
'sortable' => true,
'options' => ['cluster_hub', 'landing_page', 'guide_tutorial', 'how_to', 'comparison', 'review', 'top_listicle', 'question', 'product_description', 'service_page', 'home_page']
],
'content_type' => [
'label' => 'Type',
'type' => 'enum',
'sortable' => true,
'options' => ['post', 'product', 'page', 'CPT']
],
'created_at' => [
'label' => 'Created',
'type' => 'datetime',
'sortable' => true,
'format' => 'time_ago_created'
]
],
'pagination' => ['per_page' => 20, 'enabled' => true],
'search_field' => 'title',
'search_placeholder' => 'Search tasks...',
'filters' => [
'status' => [
'label' => 'Status',
'type' => 'select',
'options' => ['queued', 'in_progress']
],
'priority' => [
'label' => 'Priority',
'type' => 'select',
'options' => ['urgent', 'high', 'medium', 'low']
],
'content_structure' => [
'label' => 'Content Structure',
'type' => 'select',
'options' => ['cluster_hub', 'landing_page', 'guide_tutorial', 'how_to', 'comparison', 'review', 'top_listicle', 'question', 'product_description', 'service_page', 'home_page']
],
'content_type' => [
'label' => 'Content Type',
'type' => 'select',
'options' => ['post', 'product', 'page', 'CPT']
],
'cluster_id' => [
'label' => 'Cluster',
'type' => 'select',
'options' => 'dynamic_clusters'
]
],
'actions' => ['delete_selected', 'export_selected', 'add_new'],
'bulk_actions' => ['delete', 'mark_in_progress', 'move_to_drafts'],
'row_actions' => ['edit', 'delete']
],
// Writer Drafts Table (Content Generated)
'writer_drafts' => [
'table' => 'igny8_tasks',
'title' => 'Content Generated',
'humanize_columns' => ['title', 'cluster_id', 'status', 'content_structure', 'content_type', 'meta_title', 'meta_description', 'keywords', 'word_count', 'updated_at'],
'default_filter' => [
'status' => ['draft', 'review']
],
'columns' => [
'title' => [
'label' => 'Title',
'type' => 'text',
'sortable' => true,
'searchable' => true
],
'cluster_id' => [
'label' => 'Cluster',
'type' => 'lookup',
'source_field' => 'cluster_id',
'display_field' => 'cluster_name',
'sortable' => true,
'join_query' => 'LEFT JOIN {prefix}igny8_clusters c ON {table_name}.cluster_id = c.id',
'select_field' => 'c.cluster_name as cluster_name'
],
'status' => [
'label' => 'Status',
'type' => 'enum',
'sortable' => true,
'options' => ['draft', 'review']
],
'content_structure' => [
'label' => 'Structure',
'type' => 'enum',
'sortable' => true,
'options' => ['cluster_hub', 'landing_page', 'guide_tutorial', 'how_to', 'comparison', 'review', 'top_listicle', 'question', 'product_description', 'service_page', 'home_page']
],
'content_type' => [
'label' => 'Type',
'type' => 'enum',
'sortable' => true,
'options' => ['post', 'product', 'page', 'CPT']
],
'meta_title' => [
'label' => 'Meta Title',
'type' => 'text',
'sortable' => true,
'searchable' => true
],
'meta_description' => [
'label' => 'Meta Description',
'type' => 'text',
'sortable' => true,
'searchable' => true
],
'keywords' => [
'label' => 'Keywords',
'type' => 'text',
'sortable' => true,
'searchable' => true
],
'word_count' => [
'label' => 'Word Count',
'type' => 'number',
'sortable' => true
],
'updated_at' => [
'label' => 'Updated',
'type' => 'datetime',
'sortable' => true,
'format' => 'time_ago_updated'
]
],
'pagination' => ['per_page' => 20, 'enabled' => true],
'search_field' => 'title',
'search_placeholder' => 'Search drafts...',
'filters' => [
'status' => [
'label' => 'Status',
'type' => 'select',
'options' => ['draft', 'review']
],
'content_structure' => [
'label' => 'Content Structure',
'type' => 'select',
'options' => ['cluster_hub', 'landing_page', 'guide_tutorial', 'how_to', 'comparison', 'review', 'top_listicle', 'question', 'product_description', 'service_page', 'home_page']
],
'content_type' => [
'label' => 'Content Type',
'type' => 'select',
'options' => ['post', 'product', 'page', 'CPT']
],
'cluster_id' => [
'label' => 'Cluster',
'type' => 'select',
'options' => 'dynamic_clusters'
]
],
'actions' => ['delete_selected', 'publish_selected', 'export_selected', 'add_new'],
'bulk_actions' => ['delete', 'move_to_queue', 'publish'],
'row_actions' => ['edit', 'publish', 'delete']
],
// Writer Published Table (Live Content)
'writer_published' => [
'table' => 'igny8_tasks',
'title' => 'Live Content',
'humanize_columns' => ['title', 'status', 'cluster_id', 'content_structure', 'content_type', 'meta_title', 'meta_description', 'keywords', 'word_count', 'updated_at'],
'default_filter' => [
'status' => ['published']
],
'columns' => [
'title' => [
'label' => 'Title',
'type' => 'text',
'sortable' => true,
'searchable' => true
],
'status' => [
'label' => 'Status',
'type' => 'enum',
'sortable' => true,
'options' => ['published']
],
'cluster_id' => [
'label' => 'Cluster',
'type' => 'lookup',
'source_field' => 'cluster_id',
'display_field' => 'cluster_name',
'sortable' => true,
'join_query' => 'LEFT JOIN {prefix}igny8_clusters c ON {table_name}.cluster_id = c.id',
'select_field' => 'c.cluster_name as cluster_name'
],
'content_structure' => [
'label' => 'Structure',
'type' => 'enum',
'sortable' => true,
'options' => ['cluster_hub', 'landing_page', 'guide_tutorial', 'how_to', 'comparison', 'review', 'top_listicle', 'question', 'product_description', 'service_page', 'home_page']
],
'content_type' => [
'label' => 'Type',
'type' => 'enum',
'sortable' => true,
'options' => ['post', 'product', 'page', 'CPT']
],
'meta_title' => [
'label' => 'Meta Title',
'type' => 'text',
'source_meta' => '_igny8_meta_title'
],
'meta_description' => [
'label' => 'Meta Description',
'type' => 'text',
'source_meta' => '_igny8_meta_description'
],
'keywords' => [
'label' => 'Keywords',
'type' => 'text',
'source_meta' => '_igny8_primary_keywords'
],
'word_count' => [
'label' => 'Word Count',
'type' => 'number',
'source_meta' => '_igny8_word_count',
'sortable' => true
],
'updated_at' => [
'label' => 'Updated',
'type' => 'datetime',
'sortable' => true,
'format' => 'time_ago_updated'
]
],
'pagination' => ['per_page' => 20, 'enabled' => true],
'search_field' => 'title',
'search_placeholder' => 'Search published content...',
'filters' => [
'status' => [
'label' => 'Status',
'type' => 'select',
'options' => ['published']
],
'content_structure' => [
'label' => 'Content Structure',
'type' => 'select',
'options' => ['cluster_hub', 'landing_page', 'guide_tutorial', 'how_to', 'comparison', 'review', 'top_listicle', 'question', 'product_description', 'service_page', 'home_page']
],
'content_type' => [
'label' => 'Content Type',
'type' => 'select',
'options' => ['post', 'product', 'page', 'CPT']
],
'cluster_id' => [
'label' => 'Cluster',
'type' => 'select',
'options' => 'dynamic_clusters'
],
'created_at' => [
'label' => 'Date Range',
'type' => 'date_range',
'field' => 'created_at'
]
],
'actions' => ['delete_selected', 'export_selected', 'add_new'],
'bulk_actions' => ['delete', 'move_to_draft', 'unpublish'],
'row_actions' => ['edit', 'unpublish', 'delete']
],
// Optimizer Audits Table
'optimizer_audits' => [
'table' => 'igny8_audits',
'title' => 'SEO Audits Management',
'columns' => [
'page_url' => [
'label' => 'Page URL',
'type' => 'text',
'sortable' => true,
'searchable' => true,
'format' => 'url'
],
'seo_score' => [
'label' => 'SEO Score',
'type' => 'number',
'sortable' => true,
'format' => 'score'
],
'audit_status' => [
'label' => 'Status',
'type' => 'enum',
'options' => ['pending', 'in_progress', 'completed', 'failed'],
'sortable' => true
],
'issues_found' => [
'label' => 'Issues',
'type' => 'number',
'sortable' => true
],
'last_audit' => [
'label' => 'Last Audit',
'type' => 'date',
'sortable' => true,
'format' => 'datetime'
],
'next_audit' => [
'label' => 'Next Audit',
'type' => 'date',
'sortable' => true,
'format' => 'date'
]
],
'pagination' => ['per_page' => 10, 'enabled' => true],
'search_field' => 'page_url',
'search_placeholder' => 'Search pages...',
'actions' => ['delete_selected', 'export_selected', 'bulk_audit'],
'bulk_actions' => ['run_audit', 'schedule_audit'],
'row_actions' => ['view_details', 'run_audit', 'delete']
],
// Linker Backlinks Table
'linker_backlinks' => [
'table' => 'igny8_backlinks',
'title' => 'Backlinks Management',
'columns' => [
'target_url' => [
'label' => 'Target URL',
'type' => 'text',
'sortable' => true,
'searchable' => true,
'format' => 'url'
],
'source_domain' => [
'label' => 'Source Domain',
'type' => 'text',
'sortable' => true,
'searchable' => true
],
'link_type' => [
'label' => 'Link Type',
'type' => 'enum',
'options' => ['dofollow', 'nofollow', 'sponsored', 'ugc'],
'sortable' => true
],
'status' => [
'label' => 'Status',
'type' => 'enum',
'options' => ['active', 'lost', 'pending'],
'sortable' => true
],
'domain_authority' => [
'label' => 'DA',
'type' => 'number',
'sortable' => true
],
'anchor_text' => [
'label' => 'Anchor Text',
'type' => 'text',
'sortable' => true
],
'discovered_date' => [
'label' => 'Discovered',
'type' => 'date',
'sortable' => true,
'format' => 'date'
]
],
'pagination' => ['per_page' => 10, 'enabled' => true],
'search_field' => 'target_url',
'search_placeholder' => 'Search backlinks...',
'actions' => ['delete_selected', 'export_selected', 'recheck_links'],
'bulk_actions' => ['recheck', 'mark_lost'],
'row_actions' => ['edit', 'delete', 'recheck', 'view_source']
],
// Writer Templates Table (Prompts)
'writer_templates' => [
'table' => 'igny8_prompts',
'title' => 'Content Templates Management',
'columns' => [
'prompt_name' => [
'label' => 'Template Name',
'type' => 'text',
'sortable' => true,
'searchable' => true,
'source_field' => 'prompt_name',
'editable' => true
],
'category' => [
'label' => 'Category',
'type' => 'text',
'sortable' => true,
'source_field' => 'prompt_type',
'options' => [
'content' => 'Blog',
'optimization' => 'Review',
'generation' => 'Product',
'custom' => 'Custom'
]
],
'status' => [
'label' => 'Status',
'type' => 'badge',
'sortable' => true,
'source_field' => 'is_active',
'options' => [
'1' => ['label' => 'Active', 'color' => 'green'],
'0' => ['label' => 'Draft', 'color' => 'gray']
]
],
'label' => [
'label' => 'Label',
'type' => 'text',
'sortable' => true,
'source_field' => 'variables',
'format' => 'json_extract',
'json_path' => '$.label'
],
'prompt_text' => [
'label' => 'Prompt Body',
'type' => 'truncated_text',
'sortable' => false,
'source_field' => 'prompt_text',
'truncate_length' => 100,
'tooltip' => true
],
'created_at' => [
'label' => 'Created',
'type' => 'datetime',
'sortable' => true,
'format' => 'datetime'
]
],
'pagination' => ['per_page' => 15, 'enabled' => true],
'search_field' => 'prompt_name',
'search_placeholder' => 'Search templates...',
'actions' => ['delete_selected', 'export_selected', 'import', 'add_new'],
'bulk_actions' => ['delete', 'activate', 'deactivate'],
'row_actions' => ['edit', 'duplicate', 'delete']
],
// Optimizer Suggestions Table
'optimizer_suggestions' => [
'table' => 'igny8_suggestions',
'title' => 'SEO Suggestions Management',
'columns' => [
'page_url' => [
'label' => 'Page URL',
'type' => 'text',
'sortable' => true,
'searchable' => true,
'format' => 'url'
],
'suggestion_type' => [
'label' => 'Type',
'type' => 'enum',
'options' => ['title_optimization', 'meta_description', 'heading_structure', 'content_improvement', 'internal_linking'],
'sortable' => true
],
'priority' => [
'label' => 'Priority',
'type' => 'enum',
'options' => ['high', 'medium', 'low'],
'sortable' => true
],
'status' => [
'label' => 'Status',
'type' => 'enum',
'options' => ['pending', 'in_progress', 'completed', 'dismissed'],
'sortable' => true
],
'impact_score' => [
'label' => 'Impact Score',
'type' => 'number',
'sortable' => true,
'format' => 'score'
],
'created_date' => [
'label' => 'Created',
'type' => 'date',
'sortable' => true,
'format' => 'date'
]
],
'pagination' => ['per_page' => 10, 'enabled' => true],
'search_field' => 'page_url',
'search_placeholder' => 'Search pages...',
'actions' => ['delete_selected', 'export_selected', 'bulk_apply'],
'bulk_actions' => ['apply', 'dismiss', 'change_priority'],
'row_actions' => ['view_details', 'apply', 'dismiss']
],
// Linker Campaigns Table
'linker_campaigns' => [
'table' => 'igny8_campaigns',
'title' => 'Link Building Campaigns',
'columns' => [
'campaign_name' => [
'label' => 'Campaign Name',
'type' => 'text',
'sortable' => true,
'searchable' => true
],
'target_url' => [
'label' => 'Target URL',
'type' => 'text',
'sortable' => true,
'searchable' => true,
'format' => 'url'
],
'status' => [
'label' => 'Status',
'type' => 'enum',
'options' => ['planning', 'active', 'paused', 'completed', 'cancelled'],
'sortable' => true
],
'links_acquired' => [
'label' => 'Links Acquired',
'type' => 'number',
'sortable' => true
],
'target_links' => [
'label' => 'Target Links',
'type' => 'number',
'sortable' => true
],
'completion_percentage' => [
'label' => 'Completion %',
'type' => 'number',
'sortable' => true,
'format' => 'percentage'
],
'start_date' => [
'label' => 'Start Date',
'type' => 'date',
'sortable' => true,
'format' => 'date'
],
'end_date' => [
'label' => 'End Date',
'type' => 'date',
'sortable' => true,
'format' => 'date'
]
],
'pagination' => ['per_page' => 10, 'enabled' => true],
'search_field' => 'campaign_name',
'search_placeholder' => 'Search campaigns...',
'actions' => ['delete_selected', 'export_selected', 'import', 'add_new'],
'bulk_actions' => ['delete', 'activate', 'pause', 'complete'],
'row_actions' => ['edit', 'delete', 'view_progress', 'duplicate']
],
// Personalize Rewrites Table
'personalize_rewrites' => [
'table' => 'igny8_variations',
'title' => 'Content Variations Management',
'humanize_columns' => ['post_id', 'field_inputs', 'personalized_content', 'fields_hash', 'created_at'],
'columns' => [
'post_id' => [
'label' => 'Post',
'type' => 'text',
'sortable' => true,
'searchable' => true,
'join_query' => 'LEFT JOIN {prefix}posts p ON {table_name}.post_id = p.ID',
'select_field' => 'p.post_title as post_title',
'display_field' => 'post_title'
],
'field_inputs' => [
'label' => 'Field Inputs',
'type' => 'text',
'sortable' => false,
'searchable' => true,
'truncate' => 100
],
'personalized_content' => [
'label' => 'Personalized Content',
'type' => 'text',
'sortable' => false,
'searchable' => true,
'truncate' => 150
],
'fields_hash' => [
'label' => 'Fields Hash',
'type' => 'text',
'sortable' => true,
'searchable' => true,
'truncate' => 20
],
'created_at' => [
'label' => 'Created',
'type' => 'date',
'sortable' => true,
'format' => 'datetime'
]
],
'pagination' => ['per_page' => 20, 'enabled' => true],
'search_field' => 'personalized_content',
'search_placeholder' => 'Search personalized content...',
'actions' => ['delete_selected', 'export_selected', 'bulk_delete'],
'bulk_actions' => ['delete'],
'row_actions' => ['edit', 'delete', 'preview']
],
// Personalize Tones Table
'personalize_tones' => [
'table' => 'igny8_tones',
'title' => 'Tone Management',
'columns' => [
'tone_name' => [
'label' => 'Tone Name',
'type' => 'text',
'sortable' => true,
'searchable' => true
],
'description' => [
'label' => 'Description',
'type' => 'text',
'sortable' => false,
'searchable' => true,
'truncate' => 150
],
'category' => [
'label' => 'Category',
'type' => 'enum',
'options' => ['business', 'creative', 'technical', 'marketing', 'educational'],
'sortable' => true
],
'status' => [
'label' => 'Status',
'type' => 'enum',
'options' => ['active', 'inactive', 'draft'],
'sortable' => true
],
'usage_count' => [
'label' => 'Usage Count',
'type' => 'number',
'sortable' => true
],
'created_date' => [
'label' => 'Created',
'type' => 'date',
'sortable' => true,
'format' => 'date'
]
],
'pagination' => ['per_page' => 10, 'enabled' => true],
'search_field' => 'tone_name',
'search_placeholder' => 'Search tones...',
'actions' => ['delete_selected', 'export_selected', 'import', 'add_new'],
'bulk_actions' => ['delete', 'activate', 'deactivate'],
'row_actions' => ['edit', 'delete', 'duplicate', 'preview']
],
// Personalization Data Table
'personalize_data' => [
'table' => 'igny8_data',
'title' => 'Personalization Data',
'humanize_columns' => ['post_id', 'data_type', 'data', 'created_at'],
'columns' => [
'post_id' => [
'label' => 'Post ID',
'type' => 'number',
'sortable' => true,
'display_field' => 'post_title',
'join' => [
'table' => 'posts',
'on' => 'igny8_data.post_id = posts.ID',
'type' => 'LEFT'
]
],
'data_type' => [
'label' => 'Data Type',
'type' => 'text',
'sortable' => true,
'searchable' => true
],
'data' => [
'label' => 'Data',
'type' => 'json',
'sortable' => false,
'format' => 'json_preview'
],
'created_at' => [
'label' => 'Created',
'type' => 'date',
'sortable' => true,
'format' => 'datetime'
]
],
'pagination' => ['per_page' => 20, 'enabled' => true],
'search_field' => 'data_type',
'search_placeholder' => 'Search data types...',
'actions' => ['delete_selected', 'export_selected'],
'bulk_actions' => ['delete'],
'row_actions' => ['view', 'delete']
],
// Personalization Variations Table
'personalize_variations' => [
'table' => 'igny8_variations',
'title' => 'Content Variations',
'humanize_columns' => ['post_id', 'fields_hash', 'content', 'created_at'],
'columns' => [
'post_id' => [
'label' => 'Post ID',
'type' => 'number',
'sortable' => true,
'display_field' => 'post_title',
'join' => [
'table' => 'posts',
'on' => 'igny8_variations.post_id = posts.ID',
'type' => 'LEFT'
]
],
'fields_hash' => [
'label' => 'Fields Hash',
'type' => 'text',
'sortable' => true,
'format' => 'hash_preview'
],
'content' => [
'label' => 'Content',
'type' => 'text',
'sortable' => false,
'format' => 'content_preview'
],
'created_at' => [
'label' => 'Created',
'type' => 'date',
'sortable' => true,
'format' => 'datetime'
]
],
'pagination' => ['per_page' => 20, 'enabled' => true],
'search_field' => 'fields_hash',
'search_placeholder' => 'Search variations...',
'actions' => ['delete_selected', 'export_selected'],
'bulk_actions' => ['delete'],
'row_actions' => ['view', 'edit', 'delete']
]
];

View File

@@ -0,0 +1,841 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : docs.php
* @location : /modules/help/docs.php
* @type : Admin Page
* @scope : Module Only
* @allowed : Technical documentation, API reference, architecture overview
* @reusability : Single Use
* @notes : Technical documentation page for help module
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Start output buffering
ob_start();
?>
<!-- System Overview Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Igny8 AI SEO - Complete Technical Snapshot</h3>
<p class="igny8-card-subtitle">Comprehensive AI-powered SEO operating system for WordPress</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-site igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-module-overview">
<div class="igny8-module-section">
<h4>System Architecture</h4>
<p>Igny8 is a sophisticated WordPress plugin that combines AI automation with configuration-driven architecture to deliver enterprise-level SEO functionality.</p>
<ul>
<li><strong>Modular Design:</strong> Independent modules with clear interfaces</li>
<li><strong>AI-Centric:</strong> OpenAI integration for intelligent automation</li>
<li><strong>Configuration-Driven:</strong> All UI components render from configuration files</li>
<li><strong>Automation-First:</strong> CRON-based workflows for hands-off operation</li>
</ul>
</div>
<div class="igny8-module-section">
<h4>Core Modules</h4>
<p>Eight main modules provide comprehensive SEO functionality across the entire content lifecycle.</p>
<ul>
<li><strong>Planner:</strong> Keyword research, clustering, and content planning</li>
<li><strong>Writer:</strong> AI-powered content generation and task management</li>
<li><strong>Analytics:</strong> Performance tracking and SEO analytics</li>
<li><strong>Schedules:</strong> Automated task scheduling and CRON management</li>
</ul>
</div>
<div class="igny8-module-section">
<h4>Technical Stack</h4>
<p>Built on WordPress with advanced AI integration and modern web technologies.</p>
<ul>
<li><strong>Backend:</strong> PHP 7.4+, WordPress 5.0+, MySQL 5.7+</li>
<li><strong>AI Integration:</strong> OpenAI GPT-4, GPT-3.5-turbo</li>
<li><strong>Frontend:</strong> Vanilla JavaScript, CSS3, HTML5</li>
<li><strong>Database:</strong> 15 custom tables, WordPress integration</li>
</ul>
</div>
</div>
</div>
</div>
<!-- File Structure Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Complete File Structure</h3>
<p class="igny8-card-subtitle">Organized file tree with detailed descriptions</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-media-code igny8-dashboard-icon-lg igny8-dashboard-icon-purple"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-file-tree">
<pre><code>igny8-ai-seo/
├── igny8.php # Main plugin bootstrap and initialization
├── install.php # Database setup and plugin activation
├── uninstall.php # Plugin cleanup and data removal
├── igny8-wp-load-handler.php # CRON endpoint handler
├── CHANGELOG.md # Version history and changes
├── ai/ # AI Integration System (5 files)
│ ├── integration.php # API key setup and connection management
│ ├── modules-ai.php # Common AI interface for modules
│ ├── model-rates-config.php # AI model pricing and rate limits
│ ├── openai-api.php # OpenAI API integration and AI functions
│ └── prompts-library.php # AI prompts library and templates
├── assets/ # Frontend Assets
│ ├── css/
│ │ └── core.css # Main stylesheet (2000+ lines)
│ ├── js/
│ │ └── core.js # Main JavaScript (1000+ lines)
│ ├── templates/ # CSV templates for import/export
│ │ ├── igny8_clusters_template.csv
│ │ ├── igny8_ideas_template.csv
│ │ └── igny8_keywords_template.csv
│ └── ai-images/ # AI-generated images
├── core/ # Core System Files
│ ├── admin/ # Admin Interface System (7 files)
│ │ ├── ajax.php # Centralized AJAX endpoint management
│ │ ├── global-helpers.php # Global utility functions (50+ helpers)
│ │ ├── init.php # Admin initialization and settings registration
│ │ ├── menu.php # WordPress admin menu registration
│ │ ├── meta-boxes.php # WordPress meta boxes integration
│ │ ├── module-manager-class.php # Module management system
│ │ └── routing.php # Admin page routing and content rendering
│ ├── cron/ # CRON System (2 files)
│ │ ├── igny8-cron-handlers.php # CRON task handlers
│ │ └── igny8-cron-master-dispatcher.php # CRON master dispatcher
│ ├── db/ # Database System (2 files)
│ │ ├── db.php # Database operations, schema, and utilities
│ │ └── db-migration.php # Version-based migration system
│ ├── pages/ # Admin Page Templates (organized by module)
│ │ ├── analytics/ # Analytics module pages (2 files)
│ │ │ ├── analytics.php # Analytics and reporting interface
│ │ │ └── status.php # System status and health monitoring
│ │ ├── cron/ # CRON management pages
│ │ ├── help/ # Help and documentation pages (2 files)
│ │ │ ├── docs.php # Technical documentation page
│ │ │ └── help.php # User guide and support page
│ │ ├── settings/ # Settings module pages (4 files)
│ │ │ ├── general-settings.php # General plugin settings interface
│ │ │ ├── import-export.php # Data import/export interface
│ │ │ ├── integration.php # API integration settings interface
│ │ │ └── schedules.php # Scheduling and automation interface
│ │ └── thinker/ # Thinker module pages (5 files)
│ │ ├── image-testing.php # Image testing interface
│ │ ├── main.php # Thinker main interface
│ │ ├── profile.php # Thinker profile interface
│ │ ├── prompts.php # Prompts management interface
│ │ └── strategies.php # Strategies interface
│ └── global-layout.php # Master UI layout template
├── debug/ # Debug & Monitoring System (5 files)
│ ├── debug.php # Debug functionality (redirected to status)
│ ├── module-debug.php # Module-specific debugging utilities
│ ├── monitor-helpers.php # Monitoring helper functions
│ ├── system-testing.php # System testing utilities
│ └── temp-function-testing.php # Function testing utilities
├── docs/ # Documentation System (8 files)
│ ├── HOW_TO_ADD_COLUMN.md # Database column addition guide
│ ├── IGNY8_SNAPSHOT_V0.1.md # Complete plugin snapshot
│ ├── MASTER_ARCHITECTURE.md # Master architecture documentation
│ ├── how-tos/ # How-to guides (5 files)
│ │ ├── 01-adding-new-pages-and-modules.md
│ │ ├── 02-adding-new-modules-to-module-manager.md
│ │ ├── 03-auto-clustering-system.md
│ │ ├── cron-management.md
│ │ └── HOW_TO_ADD_COLUMN.md
│ └── parts/ # Architecture parts (2 files)
│ ├── AI_INTEGRATION_ARCHITECTURE.md
│ └── AUTOMATION_FLOWS.md
├── flows/ # Automation & Workflow System (3 files)
│ ├── sync-ajax.php # Automation-specific AJAX handlers
│ ├── sync-functions.php # Core automation logic and workflow functions
│ └── sync-hooks.php # Workflow hook definitions and registration
└── modules/ # Module System
├── components/ # Reusable UI Components (8 files)
│ ├── actions-tpl.php # Action buttons template
│ ├── export-modal-tpl.php # Export modal template
│ ├── filters-tpl.php # Filter controls template
│ ├── forms-tpl.php # Form rendering template
│ ├── import-modal-tpl.php # Import modal template
│ ├── kpi-tpl.php # KPI display template
│ ├── pagination-tpl.php # Pagination controls template
│ └── table-tpl.php # Data table template
├── config/ # Configuration Files (5 files)
│ ├── filters-config.php # Filter configuration definitions
│ ├── forms-config.php # Form configuration definitions
│ ├── import-export-config.php # Import/export configuration
│ ├── kpi-config.php # KPI configuration definitions
│ └── tables-config.php # Table configuration definitions
└── modules-pages/ # Module Page Interfaces
├── linker.php # Linker module interface
├── optimizer.php # Optimizer module interface
├── planner.php # Planner module interface
├── writer.php # Writer module interface
└── personalize/ # Personalization Module (7 files)
├── content-generation.php # Content generation interface
├── front-end.php # Frontend personalization
├── personalize.ajax # Personalization AJAX handlers
├── personalize.js # Personalization JavaScript
├── personalize.php # Personalize module main interface
├── rewrites.php # Content rewriting interface
└── Settings.php # Personalization settings</code></pre>
</div>
</div>
</div>
<!-- Database Schema Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Database Architecture</h3>
<p class="igny8-card-subtitle">15 custom tables with comprehensive relationships</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-database igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-module-overview">
<div class="igny8-module-section">
<h4>Core Data Tables</h4>
<ul>
<li><strong>igny8_keywords</strong> - Keyword research data with metrics</li>
<li><strong>igny8_clusters</strong> - Content topic groupings with stored metrics</li>
<li><strong>igny8_content_ideas</strong> - AI-generated content concepts</li>
<li><strong>igny8_tasks</strong> - Writer workflow management</li>
<li><strong>igny8_variations</strong> - Personalization content cache</li>
</ul>
</div>
<div class="igny8-module-section">
<h4>Analytics & Tracking</h4>
<ul>
<li><strong>igny8_logs</strong> - System audit trail and AI event logging</li>
<li><strong>igny8_ai_queue</strong> - AI processing queue with retry logic</li>
<li><strong>igny8_campaigns</strong> - Link building campaign management</li>
<li><strong>igny8_backlinks</strong> - Backlink monitoring and tracking</li>
</ul>
</div>
<div class="igny8-module-section">
<h4>WordPress Integration</h4>
<ul>
<li><strong>wp_options</strong> - Plugin settings (38+ options)</li>
<li><strong>wp_posts</strong> - Generated WordPress content</li>
<li><strong>wp_postmeta</strong> - Custom post meta fields (6 fields)</li>
<li><strong>wp_terms</strong> - Custom taxonomies (sectors, clusters)</li>
</ul>
</div>
</div>
<div class="igny8-data-flow">
<h4>Data Flow Architecture</h4>
<pre><code>Keywords → Clusters → Ideas → Tasks → WordPress Posts
↓ ↓ ↓ ↓
Mapping → Posts ← Variations (Personalization)
Campaigns → Sites → Backlinks</code></pre>
</div>
</div>
</div>
<!-- AI Integration Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>AI Integration System</h3>
<p class="igny8-card-subtitle">OpenAI integration with cost tracking and automation</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-robot igny8-dashboard-icon-lg igny8-dashboard-icon-orange"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-module-overview">
<div class="igny8-module-section">
<h4>AI Functions</h4>
<ul>
<li><strong>Content Generation:</strong> Blog posts, landing pages, product descriptions</li>
<li><strong>Keyword Analysis:</strong> Intent detection, difficulty scoring, clustering</li>
<li><strong>SEO Optimization:</strong> Meta descriptions, title optimization</li>
<li><strong>Personalization:</strong> Audience-specific content variations</li>
</ul>
</div>
<div class="igny8-module-section">
<h4>Model Configuration</h4>
<ul>
<li><strong>GPT-4:</strong> Primary model for complex tasks</li>
<li><strong>GPT-3.5-turbo:</strong> Fallback for cost optimization</li>
<li><strong>Rate Limiting:</strong> Automatic retry with exponential backoff</li>
<li><strong>Cost Tracking:</strong> Daily budget limits and usage monitoring</li>
</ul>
</div>
<div class="igny8-module-section">
<h4>AI Queue System</h4>
<ul>
<li><strong>Queue Processing:</strong> Background AI task processing</li>
<li><strong>Retry Logic:</strong> Automatic retry for failed requests</li>
<li><strong>Priority System:</strong> Task prioritization for efficient processing</li>
<li><strong>Error Handling:</strong> Comprehensive error logging and recovery</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Automation Workflows Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Automation Workflows</h3>
<p class="igny8-card-subtitle">Event-driven automation with CRON scheduling</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-update igny8-dashboard-icon-lg igny8-dashboard-icon-purple"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-workflow-examples">
<div class="igny8-workflow">
<h4>Keyword Processing Workflow</h4>
<pre><code>// When keywords are imported/updated
igny8_handle_keyword_cluster_update($keyword_id) {
// Update cluster metrics
igny8_update_cluster_metrics($cluster_id);
// Trigger AI clustering if enabled
if (ai_enabled) {
igny8_ajax_ai_cluster_keywords($keyword_ids);
}
}</code></pre>
</div>
<div class="igny8-workflow">
<h4>Content Generation Workflow</h4>
<pre><code>// When content ideas are created
igny8_create_task_from_idea($idea_id) {
// Create writer task
$task_id = create_task($idea_data);
// Generate content if AI enabled
if (ai_enabled) {
igny8_ajax_ai_generate_content($task_id);
}
// Update metrics
igny8_update_idea_metrics($idea_id);
}</code></pre>
</div>
<div class="igny8-workflow">
<h4>Cluster Management Workflow</h4>
<pre><code>// When clusters are created/updated
igny8_auto_create_cluster_term($cluster_id) {
// Create WordPress taxonomy term
$term_id = wp_insert_term($cluster_name, 'clusters');
// Link cluster to term
update_cluster_term_id($cluster_id, $term_id);
// Update metrics
igny8_update_cluster_metrics($cluster_id);
}</code></pre>
</div>
</div>
</div>
</div>
<!-- Configuration System Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Configuration System</h3>
<p class="igny8-card-subtitle">Configuration-driven UI with reusable components</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-tools igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-module-overview">
<div class="igny8-module-section">
<h4>Table Configuration</h4>
<p>Dynamic table rendering with sorting, filtering, and pagination based on configuration files.</p>
<ul>
<li><strong>Column Definitions:</strong> Field types, labels, and display options</li>
<li><strong>Sorting & Filtering:</strong> Configurable sort and filter options</li>
<li><strong>Actions:</strong> Bulk operations and individual record actions</li>
<li><strong>Pagination:</strong> Configurable page sizes and navigation</li>
</ul>
</div>
<div class="igny8-module-section">
<h4>Form Configuration</h4>
<p>Dynamic form generation with validation and field types based on configuration.</p>
<ul>
<li><strong>Field Types:</strong> Text, number, select, textarea, date, etc.</li>
<li><strong>Validation:</strong> Required fields, data types, and custom validation</li>
<li><strong>Lookup Fields:</strong> Foreign key relationships and dropdown options</li>
<li><strong>Conditional Logic:</strong> Show/hide fields based on other field values</li>
</ul>
</div>
<div class="igny8-module-section">
<h4>KPI Configuration</h4>
<p>Dynamic metrics display with charts and trend indicators based on configuration.</p>
<ul>
<li><strong>Metric Types:</strong> Count, sum, average, percentage calculations</li>
<li><strong>Visualization:</strong> Charts, graphs, and trend indicators</li>
<li><strong>Filtering:</strong> Date ranges and conditional filtering</li>
<li><strong>Real-time Updates:</strong> Live data updates and caching</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Security & Performance Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Security & Performance</h3>
<p class="igny8-card-subtitle">Enterprise-level security and optimization</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-shield igny8-dashboard-icon-lg igny8-dashboard-icon-red"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-module-overview">
<div class="igny8-module-section">
<h4>Security Measures</h4>
<ul>
<li><strong>Nonce Verification:</strong> All AJAX requests protected with WordPress nonces</li>
<li><strong>Capability Checks:</strong> User permission validation for all operations</li>
<li><strong>Data Sanitization:</strong> All input data sanitized and validated</li>
<li><strong>SQL Injection Protection:</strong> Prepared statements for all database queries</li>
</ul>
</div>
<div class="igny8-module-section">
<h4>Performance Optimizations</h4>
<ul>
<li><strong>Conditional Loading:</strong> Admin assets only loaded when needed</li>
<li><strong>Database Indexing:</strong> Optimized indexes on frequently queried fields</li>
<li><strong>Caching:</strong> WordPress transients for expensive operations</li>
<li><strong>Lazy Loading:</strong> AJAX-based data loading for large datasets</li>
</ul>
</div>
<div class="igny8-module-section">
<h4>Monitoring & Debugging</h4>
<ul>
<li><strong>Real-time Monitoring:</strong> Live system health monitoring</li>
<li><strong>Module Debug:</strong> Individual module performance tracking</li>
<li><strong>Error Logging:</strong> Comprehensive error tracking and reporting</li>
<li><strong>Performance Metrics:</strong> Response times and resource usage</li>
</ul>
</div>
</div>
</div>
</div>
<!-- API Reference Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>API Reference</h3>
<p class="igny8-card-subtitle">Complete function and endpoint documentation</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-book igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-api-reference">
<div class="igny8-api-section">
<h4>Core Functions</h4>
<div class="igny8-grid-2">
<div>
<h5>Database Functions</h5>
<ul>
<li><strong>igny8_create_all_tables()</strong> - Create all database tables</li>
<li><strong>igny8_register_taxonomies()</strong> - Register custom taxonomies</li>
<li><strong>igny8_register_post_meta()</strong> - Register custom post meta</li>
<li><strong>igny8_install_database()</strong> - Complete plugin installation</li>
</ul>
</div>
<div>
<h5>Admin Functions</h5>
<ul>
<li><strong>igny8_get_cluster_options()</strong> - Get cluster dropdown options</li>
<li><strong>igny8_get_sector_options()</strong> - Get sector dropdown options</li>
<li><strong>igny8_render_table()</strong> - Render dynamic tables</li>
<li><strong>igny8_render_filters()</strong> - Render filter controls</li>
</ul>
</div>
</div>
</div>
<div class="igny8-api-section">
<h4>AI Functions</h4>
<div class="igny8-grid-2">
<div>
<h5>Content Generation</h5>
<ul>
<li><strong>igny8_generate_blog_post()</strong> - Generate blog post content</li>
<li><strong>igny8_generate_landing_page()</strong> - Generate landing page content</li>
<li><strong>igny8_generate_product_description()</strong> - Generate product content</li>
<li><strong>igny8_generate_seo_meta()</strong> - Generate SEO meta data</li>
</ul>
</div>
<div>
<h5>AI Analysis</h5>
<ul>
<li><strong>igny8_ai_analyze_keywords()</strong> - Analyze keywords using AI</li>
<li><strong>igny8_ai_cluster_keywords()</strong> - Cluster keywords using AI</li>
<li><strong>igny8_ai_generate_ideas()</strong> - Generate content ideas</li>
<li><strong>igny8_ai_optimize_content()</strong> - Optimize existing content</li>
</ul>
</div>
</div>
</div>
<div class="igny8-api-section">
<h4>Automation Functions</h4>
<div class="igny8-grid-2">
<div>
<h5>Workflow Functions</h5>
<ul>
<li><strong>igny8_update_cluster_metrics()</strong> - Update cluster metrics</li>
<li><strong>igny8_update_idea_metrics()</strong> - Update idea metrics</li>
<li><strong>igny8_workflow_triggers()</strong> - Trigger workflow automation</li>
<li><strong>igny8_bulk_delete_keywords()</strong> - Bulk delete keywords</li>
</ul>
</div>
<div>
<h5>AJAX Endpoints</h5>
<ul>
<li><strong>wp_ajax_igny8_get_table_data</strong> - Get table data</li>
<li><strong>wp_ajax_igny8_save_record</strong> - Save/update record</li>
<li><strong>wp_ajax_igny8_ai_generate_content</strong> - AI content generation</li>
<li><strong>wp_ajax_igny8_bulk_action</strong> - Perform bulk actions</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Development Workflow Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Development Workflow</h3>
<p class="igny8-card-subtitle">Guidelines for extending and maintaining the plugin</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-editor-code igny8-dashboard-icon-lg igny8-dashboard-icon-purple"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-module-overview">
<div class="igny8-module-section">
<h4>Adding New Modules</h4>
<ol>
<li>Create module page in <code>modules/modules-pages/</code></li>
<li>Update module manager in <code>core/admin/module-manager-class.php</code></li>
<li>Add table, form, and filter configurations</li>
<li>Register routes in <code>core/admin/routing.php</code></li>
<li>Add menu items in <code>core/admin/menu.php</code></li>
</ol>
</div>
<div class="igny8-module-section">
<h4>Adding New Tables</h4>
<ol>
<li>Add database schema to <code>core/db/db.php</code></li>
<li>Create migration in <code>core/db/db-migration.php</code></li>
<li>Add table configuration to <code>modules/config/tables-config.php</code></li>
<li>Add form configuration to <code>modules/config/forms-config.php</code></li>
<li>Add filter configuration to <code>modules/config/filters-config.php</code></li>
</ol>
</div>
<div class="igny8-module-section">
<h4>Adding AI Features</h4>
<ol>
<li>Add prompt template to <code>ai/prompts-library.php</code></li>
<li>Add AI handler to <code>ai/modules-ai.php</code></li>
<li>Add queue processing to <code>flows/sync-functions.php</code></li>
<li>Add AJAX endpoint to <code>flows/sync-ajax.php</code></li>
</ol>
</div>
</div>
</div>
</div>
<style>
.igny8-help-page {
max-width: 1200px;
margin: 0 auto;
}
.igny8-standard-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.igny8-card-header-content {
display: flex;
align-items: center;
justify-content: space-between;
}
.igny8-card-title-text h3 {
margin: 0 0 8px 0;
font-size: 24px;
font-weight: 600;
}
.igny8-card-subtitle {
margin: 0;
font-size: 16px;
opacity: 0.9;
}
.igny8-dashboard-icon-lg {
font-size: 32px;
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
}
.igny8-dashboard-icon-blue { color: #3b82f6; }
.igny8-dashboard-icon-green { color: #10b981; }
.igny8-dashboard-icon-purple { color: #8b5cf6; }
.igny8-dashboard-icon-orange { color: #f59e0b; }
.igny8-dashboard-icon-red { color: #ef4444; }
.igny8-module-overview {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
}
.igny8-module-section {
background: #f8fafc;
padding: 20px;
border-radius: 8px;
border-left: 4px solid #3b82f6;
}
.igny8-module-section h4 {
margin: 0 0 12px 0;
color: #1f2937;
font-size: 18px;
}
.igny8-module-section p {
margin: 0 0 15px 0;
color: #6b7280;
line-height: 1.6;
}
.igny8-module-section ul {
margin: 0;
padding-left: 20px;
}
.igny8-module-section li {
margin-bottom: 8px;
color: #374151;
line-height: 1.5;
}
.igny8-file-tree {
background: #1f2937;
color: #f9fafb;
padding: 20px;
border-radius: 8px;
overflow-x: auto;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 13px;
line-height: 1.5;
}
.igny8-file-tree pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
.igny8-data-flow {
margin-top: 20px;
padding: 20px;
background: #f8fafc;
border-radius: 8px;
border-left: 4px solid #10b981;
}
.igny8-data-flow pre {
background: #1f2937;
color: #f9fafb;
padding: 15px;
border-radius: 6px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 13px;
overflow-x: auto;
}
.igny8-workflow-examples {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 20px;
}
.igny8-workflow {
background: #f8fafc;
padding: 20px;
border-radius: 8px;
border-left: 4px solid #8b5cf6;
}
.igny8-workflow h4 {
margin: 0 0 15px 0;
color: #1f2937;
font-size: 16px;
}
.igny8-workflow pre {
background: #1f2937;
color: #f9fafb;
padding: 15px;
border-radius: 6px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 12px;
overflow-x: auto;
margin: 0;
}
.igny8-api-reference {
display: flex;
flex-direction: column;
gap: 30px;
}
.igny8-api-section {
background: #f8fafc;
padding: 20px;
border-radius: 8px;
border-left: 4px solid #3b82f6;
}
.igny8-api-section h4 {
margin: 0 0 20px 0;
color: #1f2937;
font-size: 18px;
}
.igny8-api-section h5 {
margin: 0 0 10px 0;
color: #374151;
font-size: 14px;
font-weight: 600;
}
.igny8-api-section ul {
margin: 0;
padding-left: 20px;
}
.igny8-api-section li {
margin-bottom: 6px;
color: #4b5563;
line-height: 1.4;
font-size: 13px;
}
.igny8-api-section code {
background: #e5e7eb;
padding: 2px 6px;
border-radius: 3px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 12px;
}
@media (max-width: 768px) {
.igny8-card-header-content {
flex-direction: column;
text-align: center;
gap: 15px;
}
.igny8-module-overview,
.igny8-workflow-examples {
grid-template-columns: 1fr;
}
.igny8-file-tree {
font-size: 11px;
}
}
</style>

View File

@@ -0,0 +1,122 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : function-testing.php
* @location : /modules/help/function-testing.php
* @type : Debug Tool
* @scope : Module Only
* @allowed : Function testing, development tools, debugging functions
* @reusability : Single Use
* @notes : Function testing interface for help module (dev-only)
*/
// Note: AJAX handlers are now registered in core/admin/ajax.php
// This file only contains the HTML interface for testing
?>
<div class="wrap">
<h1>Test Page</h1>
<p>This is a test page</p>
<h2>AJAX Text Input Test</h2>
<form id="ajax-text-form">
<input type="hidden" id="ajax_text_nonce" name="ajax_text_nonce" value="<?php echo wp_create_nonce('ajax_text_action'); ?>" />
<table class="form-table">
<tr>
<th scope="row">
<label for="ajax_text_input">Test Text Input</label>
</th>
<td>
<input type="text" id="ajax_text_input" name="ajax_text_input" value="<?php echo esc_attr(get_option('igny8_ajax_test_text', '')); ?>" class="regular-text" placeholder="Enter some text to save via AJAX" />
<p class="description">This text will be saved using AJAX without page reload</p>
</td>
</tr>
</table>
<button type="button" id="ajax-save-btn" class="button button-primary">Save Text via AJAX</button>
<button type="button" id="ajax-test-btn" class="button button-secondary">Test AJAX</button>
<span id="ajax-status" style="margin-left: 10px;"></span>
</form>
<hr>
<script type="text/javascript">
jQuery(document).ready(function($){
// Test AJAX button
$('#ajax-test-btn').click(function(e){
e.preventDefault();
$('#ajax-status').html('<span style="color: blue;">Testing AJAX...</span>');
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'igny8_test_ajax'
},
success: function(response){
if (response.success) {
$('#ajax-status').html('<span style="color: green;">✓ ' + response.data + '</span>');
} else {
$('#ajax-status').html('<span style="color: red;">✗ Test failed: ' + response.data + '</span>');
}
},
error: function(){
$('#ajax-status').html('<span style="color: red;">✗ AJAX connection failed</span>');
}
});
});
// Save AJAX button
$('#ajax-save-btn').click(function(e){
e.preventDefault();
var textValue = $('#ajax_text_input').val().trim();
if (!textValue) {
$('#ajax-status').html('<span style="color: red;">Please enter some text</span>');
return;
}
// Show loading state
$('#ajax-status').html('<span style="color: blue;">Saving...</span>');
$('#ajax-save-btn').prop('disabled', true);
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'igny8_save_ajax_text',
ajax_text_input: textValue,
ajax_text_nonce: $('#ajax_text_nonce').val()
},
success: function(response){
if (response.success) {
$('#ajax-status').html('<span style="color: green;">✓ ' + response.data + '</span>');
// Update the input field to show the saved value
$('#ajax_text_input').val(textValue);
} else {
$('#ajax-status').html('<span style="color: red;">✗ Error: ' + response.data + '</span>');
}
},
error: function(xhr, status, error){
$('#ajax-status').html('<span style="color: red;">✗ AJAX Error: ' + error + '</span>');
},
complete: function(){
$('#ajax-save-btn').prop('disabled', false);
}
});
});
});
</script>
</div>
<?php
// Note: This file is included by help.php, so no need to capture content or include layout
// The content will be captured by the parent help.php file
?>

View File

@@ -0,0 +1,834 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : help.php
* @location : /modules/help/help.php
* @type : Admin Page
* @scope : Module Only
* @allowed : Help content, documentation, user guides, subpage routing
* @reusability : Single Use
* @notes : Main help page with subpage routing for help module
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Handle URL parameters for subpages
$subpage = $_GET['sp'] ?? 'help';
$GLOBALS['current_subpage'] = $subpage;
$GLOBALS['current_module'] = 'help';
// Start output buffering
ob_start();
switch ($subpage) {
case 'docs':
include plugin_dir_path(__FILE__) . 'docs.php';
break;
case 'system-testing':
include plugin_dir_path(__FILE__) . 'system-testing.php';
break;
case 'function-testing':
include plugin_dir_path(__FILE__) . 'function-testing.php';
break;
case 'help':
default:
// Main help content (existing content below)
?>
<div class="igny8-help-page">
<!-- Welcome Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Welcome to Igny8 AI SEO</h3>
<p class="igny8-card-subtitle">Your complete AI-powered SEO solution for WordPress</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-site igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<p>Igny8 is a comprehensive AI-powered SEO plugin that helps you research keywords, plan content, and optimize your website for search engines using artificial intelligence. Transform your content strategy with intelligent automation and AI-driven insights.</p>
<div class="igny8-feature-highlights">
<div class="igny8-feature-item">
<span class="dashicons dashicons-search"></span>
<strong>Smart Keyword Research</strong> - AI-powered keyword analysis and clustering
</div>
<div class="igny8-feature-item">
<span class="dashicons dashicons-edit"></span>
<strong>Content Generation</strong> - Create high-quality content with AI assistance
</div>
<div class="igny8-feature-item">
<span class="dashicons dashicons-chart-line"></span>
<strong>Performance Tracking</strong> - Monitor your SEO progress and results
</div>
</div>
</div>
</div>
<!-- Getting Started Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Getting Started</h3>
<p class="igny8-card-subtitle">Set up your AI-powered SEO workflow in minutes</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-arrow-right-alt igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-module-overview">
<div class="igny8-module-section">
<div class="igny8-step-header">
<span class="igny8-step-number">1</span>
<h4>Configure AI Integration</h4>
</div>
<p>Go to <strong>Settings > AI Integration</strong> and enter your OpenAI API key. Choose your preferred AI model (GPT-4 recommended for best results).</p>
<ul>
<li><strong>API Key Setup:</strong> Enter your OpenAI API key for AI functionality</li>
<li><strong>Model Selection:</strong> Choose between GPT-4, GPT-3.5-turbo, or other available models</li>
<li><strong>Cost Management:</strong> Set daily limits to control API usage costs</li>
<li><strong>Testing:</strong> Test your AI integration to ensure everything works properly</li>
</ul>
<div class="igny8-step-tip">
<strong>💡 Tip:</strong> You can get an OpenAI API key from platform.openai.com
</div>
</div>
<div class="igny8-module-section">
<div class="igny8-step-header">
<span class="igny8-step-number">2</span>
<h4>Import Your Keywords</h4>
</div>
<p>Navigate to <strong>Planner > Keywords</strong> and import your keyword list or add keywords manually. Set search volume, difficulty, and intent for each keyword.</p>
<ul>
<li><strong>Bulk Import:</strong> Upload CSV files with keyword data</li>
<li><strong>Manual Entry:</strong> Add keywords one by one with detailed metrics</li>
<li><strong>Data Enrichment:</strong> Set search volume, difficulty, and CPC data</li>
<li><strong>Intent Classification:</strong> Categorize keywords by user intent</li>
</ul>
<div class="igny8-step-tip">
<strong>💡 Tip:</strong> Use the bulk import feature to add multiple keywords at once
</div>
</div>
<div class="igny8-module-section">
<div class="igny8-step-header">
<span class="igny8-step-number">3</span>
<h4>Create Content Clusters</h4>
</div>
<p>Go to <strong>Planner > Clusters</strong> and group related keywords into content topics. Use AI clustering to automatically organize your keywords.</p>
<ul>
<li><strong>AI Clustering:</strong> Automatically group related keywords using AI</li>
<li><strong>Manual Organization:</strong> Create custom clusters for specific topics</li>
<li><strong>Cluster Metrics:</strong> Track keyword count, volume, and difficulty</li>
<li><strong>Content Mapping:</strong> Link clusters to published content</li>
</ul>
<div class="igny8-step-tip">
<strong>💡 Tip:</strong> Let AI suggest cluster groupings for faster organization
</div>
</div>
<div class="igny8-module-section">
<div class="igny8-step-header">
<span class="igny8-step-number">4</span>
<h4>Generate Content Ideas</h4>
</div>
<p>Visit <strong>Planner > Ideas</strong> to generate AI-powered content ideas based on your clusters. Refine and prepare ideas for content creation.</p>
<ul>
<li><strong>AI Generation:</strong> Create content ideas using artificial intelligence</li>
<li><strong>Keyword Integration:</strong> Ideas include target keywords and topics</li>
<li><strong>Content Types:</strong> Generate ideas for blog posts, guides, and more</li>
<li><strong>Idea Management:</strong> Organize and prioritize content ideas</li>
</ul>
<div class="igny8-step-tip">
<strong>💡 Tip:</strong> Generate multiple ideas per cluster for content variety
</div>
</div>
<div class="igny8-module-section">
<div class="igny8-step-header">
<span class="igny8-step-number">5</span>
<h4>Create and Publish Content</h4>
</div>
<p>Go to <strong>Writer > Tasks</strong> to create content tasks from your ideas. Use AI to generate content or write manually, then publish directly to your site.</p>
<ul>
<li><strong>Task Creation:</strong> Convert ideas into actionable content tasks</li>
<li><strong>AI Content Generation:</strong> Generate high-quality content using AI</li>
<li><strong>Content Review:</strong> Edit and refine content before publishing</li>
<li><strong>Direct Publishing:</strong> Publish content directly to your WordPress site</li>
</ul>
<div class="igny8-step-tip">
<strong>💡 Tip:</strong> Review AI-generated content before publishing to ensure quality
</div>
</div>
</div>
</div>
</div>
<!-- Planner Module Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Planner Module</h3>
<p class="igny8-card-subtitle">Research keywords, create clusters, and generate content ideas</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-search igny8-dashboard-icon-lg igny8-dashboard-icon-purple"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-module-overview">
<div class="igny8-module-section">
<h4>Keywords Management</h4>
<p>Research and organize keywords by search volume, difficulty, and intent. Import keywords from various sources or add them manually.</p>
<ul>
<li><strong>Import Keywords:</strong> Upload CSV files or paste keyword lists</li>
<li><strong>Set Metrics:</strong> Add search volume, difficulty, and CPC data</li>
<li><strong>Intent Classification:</strong> Categorize keywords by user intent</li>
<li><strong>Status Tracking:</strong> Monitor keyword mapping and usage</li>
</ul>
</div>
<div class="igny8-module-section">
<h4>Content Clusters</h4>
<p>Group related keywords into content topics for better content planning and SEO strategy.</p>
<ul>
<li><strong>AI Clustering:</strong> Automatically group related keywords</li>
<li><strong>Manual Organization:</strong> Create custom clusters for specific topics</li>
<li><strong>Cluster Metrics:</strong> Track keyword count, volume, and difficulty</li>
<li><strong>Content Mapping:</strong> Link clusters to published content</li>
</ul>
</div>
<div class="igny8-module-section">
<h4>Content Ideas</h4>
<p>Generate AI-powered content ideas based on your keyword clusters and research.</p>
<ul>
<li><strong>AI Generation:</strong> Create content ideas using artificial intelligence</li>
<li><strong>Keyword Integration:</strong> Ideas include target keywords and topics</li>
<li><strong>Content Types:</strong> Generate ideas for blog posts, guides, and more</li>
<li><strong>Idea Management:</strong> Organize and prioritize content ideas</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Writer Module Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Writer Module</h3>
<p class="igny8-card-subtitle">Create, manage, and publish content with AI assistance</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-edit igny8-dashboard-icon-lg igny8-dashboard-icon-orange"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-module-overview">
<div class="igny8-module-section">
<h4>Content Tasks</h4>
<p>Create and manage content writing tasks with detailed specifications and deadlines.</p>
<ul>
<li><strong>Task Creation:</strong> Convert ideas into actionable content tasks</li>
<li><strong>Priority Setting:</strong> Organize tasks by importance and urgency</li>
<li><strong>Deadline Management:</strong> Set and track content deadlines</li>
<li><strong>Progress Tracking:</strong> Monitor task completion status</li>
</ul>
</div>
<div class="igny8-module-section">
<h4>AI Content Generation</h4>
<p>Generate high-quality content using AI based on your research and specifications.</p>
<ul>
<li><strong>Blog Posts:</strong> Create complete blog post content</li>
<li><strong>Landing Pages:</strong> Generate optimized landing page copy</li>
<li><strong>Product Descriptions:</strong> Write compelling product content</li>
<li><strong>SEO Meta:</strong> Generate titles, descriptions, and meta tags</li>
</ul>
</div>
<div class="igny8-module-section">
<h4>Content Workflow</h4>
<p>Track content from idea to publication with automated workflows and status updates.</p>
<ul>
<li><strong>Draft Management:</strong> Create and manage content drafts</li>
<li><strong>Review Process:</strong> Track content review and approval</li>
<li><strong>Publishing:</strong> Publish content directly to your WordPress site</li>
<li><strong>Status Updates:</strong> Automatic status updates throughout the workflow</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Analytics Module Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Analytics Module</h3>
<p class="igny8-card-subtitle">Track performance and monitor your SEO progress</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-chart-line igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-module-overview">
<div class="igny8-module-section">
<h4>Performance Metrics</h4>
<p>Monitor key SEO metrics and track your content performance over time.</p>
<ul>
<li><strong>Keyword Rankings:</strong> Track keyword position changes</li>
<li><strong>Content Performance:</strong> Monitor page views and engagement</li>
<li><strong>SEO Scores:</strong> Track overall SEO improvement</li>
<li><strong>Traffic Analysis:</strong> Monitor organic traffic growth</li>
</ul>
</div>
<div class="igny8-module-section">
<h4>Content Analytics</h4>
<p>Analyze your content performance and identify optimization opportunities.</p>
<ul>
<li><strong>Top Performing Content:</strong> Identify your best-performing pages</li>
<li><strong>Content Gaps:</strong> Find opportunities for new content</li>
<li><strong>Keyword Performance:</strong> Track which keywords drive traffic</li>
<li><strong>Conversion Tracking:</strong> Monitor content conversion rates</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Schedules Module Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Schedules Module</h3>
<p class="igny8-card-subtitle">Automate your SEO tasks with intelligent scheduling</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-clock igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-module-overview">
<div class="igny8-module-section">
<h4>Automated Tasks</h4>
<p>Set up automated tasks to run keyword research, content generation, and optimization tasks.</p>
<ul>
<li><strong>Keyword Research:</strong> Automatically discover new keywords</li>
<li><strong>Content Generation:</strong> Schedule AI content creation</li>
<li><strong>SEO Audits:</strong> Regular automated SEO analysis</li>
<li><strong>Performance Reports:</strong> Scheduled performance reports</li>
</ul>
</div>
<div class="igny8-module-section">
<h4>Workflow Automation</h4>
<p>Create automated workflows that trigger based on specific conditions and schedules.</p>
<ul>
<li><strong>Trigger Conditions:</strong> Set up conditions for task execution</li>
<li><strong>Schedule Management:</strong> Configure when tasks should run</li>
<li><strong>Notification System:</strong> Get alerts when tasks complete</li>
<li><strong>Error Handling:</strong> Automatic retry and error management</li>
</ul>
</div>
</div>
</div>
</div>
<!-- AI Features Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>AI-Powered Features</h3>
<p class="igny8-card-subtitle">Leverage artificial intelligence for smarter SEO</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-robot igny8-dashboard-icon-lg igny8-dashboard-icon-purple"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-ai-features">
<div class="igny8-ai-feature">
<h4>Intelligent Keyword Analysis</h4>
<p>AI analyzes your keywords to determine search intent, difficulty, and optimization opportunities.</p>
<ul>
<li>Automatic intent classification (informational, commercial, navigational)</li>
<li>AI-powered difficulty scoring</li>
<li>Keyword clustering and grouping</li>
<li>Competition analysis and insights</li>
</ul>
</div>
<div class="igny8-ai-feature">
<h4>Smart Content Generation</h4>
<p>Generate high-quality, SEO-optimized content using advanced AI models.</p>
<ul>
<li>Context-aware content creation</li>
<li>SEO optimization built-in</li>
<li>Multiple content formats (blog posts, landing pages, product descriptions)</li>
<li>Automatic keyword integration</li>
</ul>
</div>
<div class="igny8-ai-feature">
<h4>Automated Content Optimization</h4>
<p>AI continuously optimizes your content for better search engine performance.</p>
<ul>
<li>Automatic SEO scoring and suggestions</li>
<li>Content improvement recommendations</li>
<li>Keyword density optimization</li>
<li>Readability enhancement</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Best Practices Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Best Practices</h3>
<p class="igny8-card-subtitle">Get the most out of your AI-powered SEO workflow</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-star-filled igny8-dashboard-icon-lg igny8-dashboard-icon-yellow"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-best-practices">
<div class="igny8-practice-category">
<h4>Content Strategy</h4>
<ul>
<li><strong>Plan Before You Write:</strong> Use the Planner module to research keywords and create clusters before writing</li>
<li><strong>Quality Over Quantity:</strong> Focus on creating high-quality, comprehensive content rather than many short posts</li>
<li><strong>Regular Content Updates:</strong> Keep your content fresh and updated to maintain search rankings</li>
<li><strong>User Intent Focus:</strong> Always consider what users are looking for when creating content</li>
</ul>
</div>
<div class="igny8-practice-category">
<h4>AI Usage</h4>
<ul>
<li><strong>Review AI Content:</strong> Always review and edit AI-generated content before publishing</li>
<li><strong>Use AI as a Starting Point:</strong> Let AI generate ideas and drafts, then add your unique perspective</li>
<li><strong>Monitor API Usage:</strong> Keep track of your OpenAI API usage to manage costs</li>
<li><strong>Test Different Prompts:</strong> Experiment with different AI prompts to get better results</li>
</ul>
</div>
<div class="igny8-practice-category">
<h4>SEO Optimization</h4>
<ul>
<li><strong>Keyword Research First:</strong> Always start with thorough keyword research</li>
<li><strong>Monitor Performance:</strong> Regularly check your analytics to see what's working</li>
<li><strong>Optimize for Users:</strong> Write for humans first, search engines second</li>
<li><strong>Build Authority:</strong> Focus on creating content that establishes your expertise</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Troubleshooting Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Troubleshooting</h3>
<p class="igny8-card-subtitle">Common issues and solutions</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-sos igny8-dashboard-icon-lg igny8-dashboard-icon-red"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-troubleshooting">
<div class="igny8-issue-category">
<h4>AI Integration Issues</h4>
<div class="igny8-issue">
<strong>Problem:</strong> AI features not working
<br><strong>Solution:</strong> Check your OpenAI API key in Settings > AI Integration. Ensure you have sufficient API credits and a stable internet connection.
</div>
<div class="igny8-issue">
<strong>Problem:</strong> Slow AI responses
<br><strong>Solution:</strong> Try switching to a faster model like GPT-3.5-turbo or check your internet connection speed.
</div>
</div>
<div class="igny8-issue-category">
<h4>Performance Issues</h4>
<div class="igny8-issue">
<strong>Problem:</strong> Slow page loading
<br><strong>Solution:</strong> Reduce the number of records per page in table settings, clear your browser cache, or check for plugin conflicts.
</div>
<div class="igny8-issue">
<strong>Problem:</strong> Missing data
<br><strong>Solution:</strong> Check that database tables are created correctly by visiting Settings > Status and running a system check.
</div>
</div>
<div class="igny8-issue-category">
<h4>Content Issues</h4>
<div class="igny8-issue">
<strong>Problem:</strong> AI content quality issues
<br><strong>Solution:</strong> Try different prompts, provide more specific instructions, or use the content as a starting point for manual editing.
</div>
<div class="igny8-issue">
<strong>Problem:</strong> Keywords not being used properly
<br><strong>Solution:</strong> Ensure keywords are properly imported and mapped to clusters before generating content.
</div>
</div>
</div>
</div>
</div>
<!-- Support Section -->
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Support & Resources</h3>
<p class="igny8-card-subtitle">Get help when you need it</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-info igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-support-resources">
<div class="igny8-support-item">
<h4>System Status</h4>
<p>Check your system health and configuration at <strong>Settings > Status</strong>. This page shows database status, AI integration, and system performance.</p>
</div>
<div class="igny8-support-item">
<h4>Debug Information</h4>
<p>Use the built-in debug tools to monitor real-time system status and identify any issues with your setup.</p>
</div>
<div class="igny8-support-item">
<h4>Regular Backups</h4>
<p>Always backup your WordPress site before making major changes or updates to ensure you can restore if needed.</p>
</div>
<div class="igny8-support-item">
<h4>API Monitoring</h4>
<p>Keep track of your OpenAI API usage to manage costs and ensure you don't exceed your limits.</p>
</div>
</div>
</div>
</div>
</div>
<?php
break;
}
?>
<style>
.igny8-help-page {
max-width: 1200px;
margin: 0 auto;
}
.igny8-standard-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.igny8-card-header-content {
display: flex;
align-items: center;
justify-content: space-between;
}
.igny8-card-title-text h3 {
margin: 0 0 8px 0;
font-size: 24px;
font-weight: 600;
}
.igny8-card-subtitle {
margin: 0;
font-size: 16px;
opacity: 0.9;
}
.igny8-dashboard-icon-lg {
font-size: 32px;
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.2);
border-radius: 50%;
}
.igny8-dashboard-icon-blue { color: #3b82f6; }
.igny8-dashboard-icon-green { color: #10b981; }
.igny8-dashboard-icon-purple { color: #8b5cf6; }
.igny8-dashboard-icon-orange { color: #f59e0b; }
.igny8-dashboard-icon-yellow { color: #eab308; }
.igny8-dashboard-icon-red { color: #ef4444; }
.igny8-feature-highlights {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-top: 20px;
}
.igny8-feature-item {
display: flex;
align-items: center;
gap: 12px;
padding: 15px;
background: #f8fafc;
border-radius: 6px;
border-left: 4px solid #3b82f6;
}
.igny8-step-header {
display: flex;
align-items: center;
gap: 15px;
margin-bottom: 15px;
}
.igny8-step-header .igny8-step-number {
background: #3b82f6;
color: white;
width: 35px;
height: 35px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 16px;
flex-shrink: 0;
}
.igny8-step-header h4 {
margin: 0;
color: #1f2937;
font-size: 18px;
}
.igny8-step-tip {
background: #fef3c7;
border: 1px solid #f59e0b;
border-radius: 6px;
padding: 12px;
margin-top: 12px;
}
.igny8-module-overview {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
}
.igny8-module-section {
background: #f8fafc;
padding: 20px;
border-radius: 8px;
border-left: 4px solid #3b82f6;
}
.igny8-module-section h4 {
margin: 0 0 12px 0;
color: #1f2937;
font-size: 18px;
}
.igny8-module-section p {
margin: 0 0 15px 0;
color: #6b7280;
line-height: 1.6;
}
.igny8-module-section ul {
margin: 0;
padding-left: 20px;
}
.igny8-module-section li {
margin-bottom: 8px;
color: #374151;
line-height: 1.5;
}
.igny8-ai-features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 30px;
}
.igny8-ai-feature {
background: #f8fafc;
padding: 25px;
border-radius: 8px;
border-left: 4px solid #8b5cf6;
}
.igny8-ai-feature h4 {
margin: 0 0 12px 0;
color: #1f2937;
font-size: 18px;
}
.igny8-ai-feature p {
margin: 0 0 15px 0;
color: #6b7280;
line-height: 1.6;
}
.igny8-best-practices {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
}
.igny8-practice-category {
background: #f8fafc;
padding: 20px;
border-radius: 8px;
border-left: 4px solid #10b981;
}
.igny8-practice-category h4 {
margin: 0 0 15px 0;
color: #1f2937;
font-size: 18px;
}
.igny8-troubleshooting {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 30px;
}
.igny8-issue-category {
background: #f8fafc;
padding: 20px;
border-radius: 8px;
border-left: 4px solid #ef4444;
}
.igny8-issue-category h4 {
margin: 0 0 15px 0;
color: #1f2937;
font-size: 18px;
}
.igny8-issue {
background: white;
padding: 15px;
border-radius: 6px;
margin-bottom: 15px;
border-left: 3px solid #f59e0b;
}
.igny8-support-resources {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
.igny8-support-item {
background: #f8fafc;
padding: 20px;
border-radius: 8px;
border-left: 4px solid #3b82f6;
}
.igny8-support-item h4 {
margin: 0 0 10px 0;
color: #1f2937;
font-size: 16px;
}
.igny8-support-item p {
margin: 0;
color: #6b7280;
line-height: 1.6;
}
@media (max-width: 768px) {
.igny8-card-header-content {
flex-direction: column;
text-align: center;
gap: 15px;
}
.igny8-step-header {
flex-direction: column;
text-align: center;
gap: 10px;
}
.igny8-module-overview,
.igny8-ai-features,
.igny8-best-practices,
.igny8-troubleshooting {
grid-template-columns: 1fr;
}
}
</style>
<?php
// Capture page content
$igny8_page_content = ob_get_clean();
// Include global layout
include plugin_dir_path(__FILE__) . '../../core/global-layout.php';
?>

View File

@@ -0,0 +1,329 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : system-testing.php
* @location : /modules/help/system-testing.php
* @type : Admin Page
* @scope : Module Only
* @allowed : System testing, functionality verification, debugging tools
* @reusability : Single Use
* @notes : System testing interface for help module
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Dev-only access guard
if (!defined('IGNY8_DEV') || IGNY8_DEV !== true) {
return;
}
?>
<div class="notice notice-info">
<p><strong>Testing Interface:</strong> Basic functionality verification for taxonomy, schema, and UI components.</p>
</div>
<div class="igny8-test-container" style="display: flex; gap: 20px; margin-top: 20px;">
<!-- Schema Tests -->
<div class="igny8-test-section" style="flex: 1; border: 1px solid #ddd; padding: 20px; border-radius: 5px;">
<h2>Database Schema Tests</h2>
<div class="test-buttons">
<button class="button button-primary" onclick="testDatabaseConnection()">Test DB Connection</button>
<button class="button" onclick="testTableExists()">Check Tables</button>
<button class="button" onclick="testSchemaIntegrity()">Schema Integrity</button>
</div>
<div id="schema-results" class="test-results" style="margin-top: 15px; padding: 10px; background: #f9f9f9; border-radius: 3px; min-height: 100px;">
<p><em>Click a test button to see results...</em></p>
</div>
</div>
<!-- Taxonomy Tests -->
<div class="igny8-test-section" style="flex: 1; border: 1px solid #ddd; padding: 20px; border-radius: 5px;">
<h2>Taxonomy Tests</h2>
<div class="test-buttons">
<button class="button button-primary" onclick="testTaxonomyRegistration()">Test Taxonomy</button>
<button class="button" onclick="testCreateTerm()">Create Test Term</button>
<button class="button" onclick="testTermQueries()">Query Terms</button>
</div>
<div id="taxonomy-results" class="test-results" style="margin-top: 15px; padding: 10px; background: #f9f9f9; border-radius: 3px; min-height: 100px;">
<p><em>Click a test button to see results...</em></p>
</div>
</div>
<!-- AJAX Tests -->
<div class="igny8-test-section" style="flex: 1; border: 1px solid #ddd; padding: 20px; border-radius: 5px;">
<h2>AJAX & API Tests</h2>
<div class="test-buttons">
<button class="button button-primary" onclick="testAjaxConnection()">Test AJAX</button>
<button class="button" onclick="testTableData()">Load Table Data</button>
<button class="button" onclick="testWorkflowTriggers()">Test Workflows</button>
</div>
<div id="ajax-results" class="test-results" style="margin-top: 15px; padding: 10px; background: #f9f9f9; border-radius: 3px; min-height: 100px;">
<p><em>Click a test button to see results...</em></p>
</div>
</div>
</div>
<!-- Quick Record Test -->
<div style="margin-top: 30px; border: 1px solid #ddd; padding: 20px; border-radius: 5px;">
<h2>Quick Record Test</h2>
<p>Test basic record operations:</p>
<div style="display: flex; gap: 10px; align-items: center; margin-top: 10px;">
<input type="text" id="test-record-name" placeholder="Test record name" value="Test Record <?php echo date('Y-m-d H:i:s'); ?>" style="flex: 1;">
<button class="button button-primary" onclick="createTestRecord()">Create Test Record</button>
<button class="button" onclick="listTestRecords()">List Records</button>
<button class="button" onclick="clearTestRecords()" style="color: #d63638;">Clear All</button>
</div>
<div id="record-results" class="test-results" style="margin-top: 15px; padding: 10px; background: #f9f9f9; border-radius: 3px; min-height: 50px;">
<p><em>Create or list records to see results...</em></p>
</div>
</div>
<script>
// Test functions
function testDatabaseConnection() {
updateResults('schema-results', 'Testing database connection...', 'info');
fetch(ajaxurl, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=igny8_test_db_connection&nonce=' + igny8_ajax.nonce
})
.then(response => response.json())
.then(data => {
updateResults('schema-results', JSON.stringify(data, null, 2), data.success ? 'success' : 'error');
})
.catch(error => {
updateResults('schema-results', 'Error: ' + error.message, 'error');
});
}
function testTableExists() {
updateResults('schema-results', 'Checking table existence...', 'info');
fetch(ajaxurl, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=igny8_test_table_exists&nonce=' + igny8_ajax.nonce
})
.then(response => response.json())
.then(data => {
updateResults('schema-results', JSON.stringify(data, null, 2), data.success ? 'success' : 'error');
})
.catch(error => {
updateResults('schema-results', 'Error: ' + error.message, 'error');
});
}
function testSchemaIntegrity() {
updateResults('schema-results', 'Testing schema integrity...', 'info');
fetch(ajaxurl, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=igny8_test_schema_integrity&nonce=' + igny8_ajax.nonce
})
.then(response => response.json())
.then(data => {
updateResults('schema-results', JSON.stringify(data, null, 2), data.success ? 'success' : 'error');
})
.catch(error => {
updateResults('schema-results', 'Error: ' + error.message, 'error');
});
}
function testTaxonomyRegistration() {
updateResults('taxonomy-results', 'Testing taxonomy registration...', 'info');
fetch(ajaxurl, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=igny8_test_taxonomy&nonce=' + igny8_ajax.nonce
})
.then(response => response.json())
.then(data => {
updateResults('taxonomy-results', JSON.stringify(data, null, 2), data.success ? 'success' : 'error');
})
.catch(error => {
updateResults('taxonomy-results', 'Error: ' + error.message, 'error');
});
}
function testCreateTerm() {
updateResults('taxonomy-results', 'Creating test term...', 'info');
fetch(ajaxurl, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=igny8_test_create_term&nonce=' + igny8_ajax.nonce
})
.then(response => response.json())
.then(data => {
updateResults('taxonomy-results', JSON.stringify(data, null, 2), data.success ? 'success' : 'error');
})
.catch(error => {
updateResults('taxonomy-results', 'Error: ' + error.message, 'error');
});
}
function testTermQueries() {
updateResults('taxonomy-results', 'Querying terms...', 'info');
fetch(ajaxurl, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=igny8_test_term_queries&nonce=' + igny8_ajax.nonce
})
.then(response => response.json())
.then(data => {
updateResults('taxonomy-results', JSON.stringify(data, null, 2), data.success ? 'success' : 'error');
})
.catch(error => {
updateResults('taxonomy-results', 'Error: ' + error.message, 'error');
});
}
function testAjaxConnection() {
updateResults('ajax-results', 'Testing AJAX connection...', 'info');
fetch(ajaxurl, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=igny8_test_ajax&nonce=' + igny8_ajax.nonce
})
.then(response => response.json())
.then(data => {
updateResults('ajax-results', JSON.stringify(data, null, 2), data.success ? 'success' : 'error');
})
.catch(error => {
updateResults('ajax-results', 'Error: ' + error.message, 'error');
});
}
function testTableData() {
updateResults('ajax-results', 'Testing table data loading...', 'info');
fetch(ajaxurl, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=igny8_get_table_data&nonce=' + igny8_ajax.nonce + '&table=planner_keywords&page=1&per_page=5'
})
.then(response => response.json())
.then(data => {
updateResults('ajax-results', JSON.stringify(data, null, 2), data.success ? 'success' : 'error');
})
.catch(error => {
updateResults('ajax-results', 'Error: ' + error.message, 'error');
});
}
function testWorkflowTriggers() {
updateResults('ajax-results', 'Testing workflow triggers...', 'info');
fetch(ajaxurl, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=igny8_test_workflows&nonce=' + igny8_ajax.nonce
})
.then(response => response.json())
.then(data => {
updateResults('ajax-results', JSON.stringify(data, null, 2), data.success ? 'success' : 'error');
})
.catch(error => {
updateResults('ajax-results', 'Error: ' + error.message, 'error');
});
}
function createTestRecord() {
const name = document.getElementById('test-record-name').value;
if (!name.trim()) {
updateResults('record-results', 'Please enter a record name', 'error');
return;
}
updateResults('record-results', 'Creating test record...', 'info');
fetch(ajaxurl, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=igny8_test_create_record&nonce=' + igny8_ajax.nonce + '&name=' + encodeURIComponent(name)
})
.then(response => response.json())
.then(data => {
updateResults('record-results', JSON.stringify(data, null, 2), data.success ? 'success' : 'error');
})
.catch(error => {
updateResults('record-results', 'Error: ' + error.message, 'error');
});
}
function listTestRecords() {
updateResults('record-results', 'Listing test records...', 'info');
fetch(ajaxurl, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=igny8_test_list_records&nonce=' + igny8_ajax.nonce
})
.then(response => response.json())
.then(data => {
updateResults('record-results', JSON.stringify(data, null, 2), data.success ? 'success' : 'error');
})
.catch(error => {
updateResults('record-results', 'Error: ' + error.message, 'error');
});
}
function clearTestRecords() {
if (!confirm('Are you sure you want to clear all test records?')) return;
updateResults('record-results', 'Clearing test records...', 'info');
fetch(ajaxurl, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: 'action=igny8_test_clear_records&nonce=' + igny8_ajax.nonce
})
.then(response => response.json())
.then(data => {
updateResults('record-results', JSON.stringify(data, null, 2), data.success ? 'success' : 'error');
})
.catch(error => {
updateResults('record-results', 'Error: ' + error.message, 'error');
});
}
function updateResults(elementId, content, type) {
const element = document.getElementById(elementId);
const timestamp = new Date().toLocaleTimeString();
const prefix = type === 'success' ? '✅' : type === 'error' ? '❌' : '';
element.innerHTML = `<div style="margin-bottom: 10px;"><strong>${prefix} [${timestamp}]</strong></div><pre style="white-space: pre-wrap; font-size: 12px;">${content}</pre>`;
if (type === 'success') {
element.style.borderLeft = '4px solid #00a32a';
} else if (type === 'error') {
element.style.borderLeft = '4px solid #d63638';
} else {
element.style.borderLeft = '4px solid #0073aa';
}
}
</script>
<?php
// Note: This file is included by help.php, so no need to capture content or include layout
// The content will be captured by the parent help.php file
?>

View File

@@ -0,0 +1,231 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : home.php
* @location : modules/
* @type : Home Page Template
* @scope : Global
* @allowed : Home page content only, no routing or business logic
* @reusability : Global
* @notes : Home page dashboard content
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Get workflow data
$workflow_data = igny8_get_system_workflow_data();
// Start output buffering
ob_start();
?>
<div class="igny8-module-home">
<!-- System-Wide AI Workflow Guide -->
<div class="igny8-system-workflow">
<div class="igny8-system-workflow-header">
<span class="dashicons dashicons-networking"></span>
<div>
<h2>Complete AI Content Workflow</h2>
<p class="igny8-system-workflow-subtitle">Track your progress through the entire content creation and optimization pipeline</p>
</div>
</div>
<div class="igny8-system-steps-container">
<!-- Step 1: Add Keywords -->
<div class="igny8-system-step <?php echo $workflow_data['keywords']['status']; ?> <?php echo !$workflow_data['keywords']['module_enabled'] ? 'disabled' : ''; ?>"
<?php if ($workflow_data['keywords']['url']): ?>onclick="window.location.href='<?php echo admin_url($workflow_data['keywords']['url']); ?>'"<?php endif; ?>>
<div class="igny8-system-step-number">1</div>
<div class="igny8-system-step-title">Add Keywords</div>
<div class="igny8-system-step-status">
<span class="igny8-system-step-status-icon"><?php echo $workflow_data['keywords']['status'] === 'completed' ? '✅' : '⚠'; ?></span>
<span class="igny8-system-step-status-text"><?php echo $workflow_data['keywords']['status'] === 'completed' ? 'Completed' : 'Missing'; ?></span>
</div>
<div class="igny8-system-step-data">
<?php if ($workflow_data['keywords']['count'] > 0): ?>
<?php echo $workflow_data['keywords']['count']; ?> keywords added
<?php else: ?>
No keywords yet
<?php endif; ?>
</div>
<?php if ($workflow_data['keywords']['status'] === 'missing' && $workflow_data['keywords']['module_enabled']): ?>
<div class="igny8-system-step-action">
<a href="<?php echo admin_url($workflow_data['keywords']['url']); ?>" class="igny8-btn igny8-btn-primary">Go to Planner</a>
</div>
<?php endif; ?>
<div class="igny8-system-step-connector"></div>
</div>
<!-- Step 2: Select Sector -->
<div class="igny8-system-step <?php echo $workflow_data['sector']['status']; ?> <?php echo !$workflow_data['sector']['module_enabled'] ? 'disabled' : ''; ?>"
<?php if ($workflow_data['sector']['url']): ?>onclick="window.location.href='<?php echo admin_url($workflow_data['sector']['url']); ?>'"<?php endif; ?>>
<div class="igny8-system-step-number">2</div>
<div class="igny8-system-step-title">Select Sector</div>
<div class="igny8-system-step-status">
<span class="igny8-system-step-status-icon"><?php echo $workflow_data['sector']['status'] === 'completed' ? '✅' : '⚠'; ?></span>
<span class="igny8-system-step-status-text"><?php echo $workflow_data['sector']['status'] === 'completed' ? 'Selected' : 'Required'; ?></span>
</div>
<div class="igny8-system-step-data">
<?php if ($workflow_data['sector']['selected']): ?>
Sector configured
<?php else: ?>
Required for AI workflows
<?php endif; ?>
</div>
<?php if ($workflow_data['sector']['status'] === 'missing' && $workflow_data['sector']['module_enabled']): ?>
<div class="igny8-system-step-action">
<a href="<?php echo admin_url($workflow_data['sector']['url']); ?>" class="igny8-btn igny8-btn-primary">Configure</a>
</div>
<?php endif; ?>
<div class="igny8-system-step-connector"></div>
</div>
<!-- Step 3: Auto Cluster -->
<div class="igny8-system-step <?php echo $workflow_data['clusters']['status']; ?> <?php echo !$workflow_data['clusters']['module_enabled'] ? 'disabled' : ''; ?>"
<?php if ($workflow_data['clusters']['url']): ?>onclick="window.location.href='<?php echo admin_url($workflow_data['clusters']['url']); ?>'"<?php endif; ?>>
<div class="igny8-system-step-number">3</div>
<div class="igny8-system-step-title">Auto Cluster</div>
<div class="igny8-system-step-status">
<span class="igny8-system-step-status-icon"><?php echo $workflow_data['clusters']['status'] === 'completed' ? '✅' : ($workflow_data['clusters']['status'] === 'in_progress' ? '⏳' : '⚠'); ?></span>
<span class="igny8-system-step-status-text"><?php echo $workflow_data['clusters']['status'] === 'completed' ? 'Completed' : ($workflow_data['clusters']['status'] === 'in_progress' ? 'In Progress' : 'Missing'); ?></span>
</div>
<div class="igny8-system-step-data">
<?php if ($workflow_data['clusters']['unmapped_keywords'] > 0): ?>
<?php echo $workflow_data['clusters']['unmapped_keywords']; ?> unmapped keywords
<?php elseif ($workflow_data['clusters']['count'] > 0): ?>
<?php echo $workflow_data['clusters']['count']; ?> clusters created
<?php else: ?>
No clusters yet
<?php endif; ?>
</div>
<?php if ($workflow_data['clusters']['status'] === 'in_progress' && $workflow_data['clusters']['module_enabled']): ?>
<div class="igny8-system-step-action">
<a href="<?php echo admin_url($workflow_data['clusters']['url']); ?>" class="igny8-btn igny8-btn-primary">Start Clustering</a>
</div>
<?php endif; ?>
<div class="igny8-system-step-connector"></div>
</div>
<!-- Step 4: Generate Ideas -->
<div class="igny8-system-step <?php echo $workflow_data['ideas']['status']; ?> <?php echo !$workflow_data['ideas']['module_enabled'] ? 'disabled' : ''; ?>"
<?php if ($workflow_data['ideas']['url']): ?>onclick="window.location.href='<?php echo admin_url($workflow_data['ideas']['url']); ?>'"<?php endif; ?>>
<div class="igny8-system-step-number">4</div>
<div class="igny8-system-step-title">Generate Ideas</div>
<div class="igny8-system-step-status">
<span class="igny8-system-step-status-icon"><?php echo $workflow_data['ideas']['status'] === 'completed' ? '✅' : '⚠'; ?></span>
<span class="igny8-system-step-status-text"><?php echo $workflow_data['ideas']['status'] === 'completed' ? 'Completed' : 'Missing'; ?></span>
</div>
<div class="igny8-system-step-data">
<?php if ($workflow_data['ideas']['count'] > 0): ?>
<?php echo $workflow_data['ideas']['count']; ?> ideas generated
<?php else: ?>
No ideas yet
<?php endif; ?>
</div>
<?php if ($workflow_data['ideas']['status'] === 'missing' && $workflow_data['ideas']['module_enabled']): ?>
<div class="igny8-system-step-action">
<a href="<?php echo admin_url($workflow_data['ideas']['url']); ?>" class="igny8-btn igny8-btn-primary">Generate Ideas</a>
</div>
<?php endif; ?>
<div class="igny8-system-step-connector"></div>
</div>
<!-- Step 5: Queue to Writer -->
<div class="igny8-system-step <?php echo $workflow_data['queue']['status']; ?> <?php echo !$workflow_data['queue']['module_enabled'] ? 'disabled' : ''; ?>"
<?php if ($workflow_data['queue']['url']): ?>onclick="window.location.href='<?php echo admin_url($workflow_data['queue']['url']); ?>'"<?php endif; ?>>
<div class="igny8-system-step-number">5</div>
<div class="igny8-system-step-title">Queue to Writer</div>
<div class="igny8-system-step-status">
<span class="igny8-system-step-status-icon"><?php echo $workflow_data['queue']['status'] === 'completed' ? '✅' : ($workflow_data['queue']['status'] === 'in_progress' ? '⏳' : '⚠'); ?></span>
<span class="igny8-system-step-status-text"><?php echo $workflow_data['queue']['status'] === 'completed' ? 'Completed' : ($workflow_data['queue']['status'] === 'in_progress' ? 'In Progress' : 'Missing'); ?></span>
</div>
<div class="igny8-system-step-data">
<?php if ($workflow_data['queue']['queued_ideas'] > 0): ?>
<?php echo $workflow_data['queue']['queued_ideas']; ?> ideas ready to queue
<?php else: ?>
All ideas queued
<?php endif; ?>
</div>
<?php if ($workflow_data['queue']['status'] === 'in_progress' && $workflow_data['queue']['module_enabled']): ?>
<div class="igny8-system-step-action">
<a href="<?php echo admin_url($workflow_data['queue']['url']); ?>" class="igny8-btn igny8-btn-primary">Queue Ideas</a>
</div>
<?php endif; ?>
<div class="igny8-system-step-connector"></div>
</div>
<!-- Step 6: Generate Drafts -->
<div class="igny8-system-step <?php echo $workflow_data['drafts']['status']; ?> <?php echo !$workflow_data['drafts']['module_enabled'] ? 'disabled' : ''; ?>"
<?php if ($workflow_data['drafts']['url']): ?>onclick="window.location.href='<?php echo admin_url($workflow_data['drafts']['url']); ?>'"<?php endif; ?>>
<div class="igny8-system-step-number">6</div>
<div class="igny8-system-step-title">Generate Drafts</div>
<div class="igny8-system-step-status">
<span class="igny8-system-step-status-icon"><?php echo $workflow_data['drafts']['status'] === 'completed' ? '✅' : ($workflow_data['drafts']['status'] === 'in_progress' ? '⏳' : '⚠'); ?></span>
<span class="igny8-system-step-status-text"><?php echo $workflow_data['drafts']['status'] === 'completed' ? 'Completed' : ($workflow_data['drafts']['status'] === 'in_progress' ? 'In Progress' : 'Missing'); ?></span>
</div>
<div class="igny8-system-step-data">
<?php if ($workflow_data['drafts']['queued_tasks'] > 0): ?>
<?php echo $workflow_data['drafts']['queued_tasks']; ?> tasks ready for AI
<?php elseif ($workflow_data['drafts']['draft_tasks'] > 0): ?>
<?php echo $workflow_data['drafts']['draft_tasks']; ?> drafts generated
<?php else: ?>
No drafts yet
<?php endif; ?>
</div>
<?php if ($workflow_data['drafts']['status'] === 'in_progress' && $workflow_data['drafts']['module_enabled']): ?>
<div class="igny8-system-step-action">
<a href="<?php echo admin_url($workflow_data['drafts']['url']); ?>" class="igny8-btn igny8-btn-primary">Go to Writer</a>
</div>
<?php endif; ?>
<div class="igny8-system-step-connector"></div>
</div>
<!-- Step 7: Publish Content -->
<div class="igny8-system-step <?php echo $workflow_data['publish']['status']; ?> <?php echo !$workflow_data['publish']['module_enabled'] ? 'disabled' : ''; ?>"
<?php if ($workflow_data['publish']['url']): ?>onclick="window.location.href='<?php echo admin_url($workflow_data['publish']['url']); ?>'"<?php endif; ?>>
<div class="igny8-system-step-number">7</div>
<div class="igny8-system-step-title">Publish Content</div>
<div class="igny8-system-step-status">
<span class="igny8-system-step-status-icon"><?php echo $workflow_data['publish']['status'] === 'completed' ? '✅' : ($workflow_data['publish']['status'] === 'in_progress' ? '⏳' : '⚠'); ?></span>
<span class="igny8-system-step-status-text"><?php echo $workflow_data['publish']['status'] === 'completed' ? 'Completed' : ($workflow_data['publish']['status'] === 'in_progress' ? 'In Progress' : 'Missing'); ?></span>
</div>
<div class="igny8-system-step-data">
<?php if ($workflow_data['publish']['published_tasks'] > 0): ?>
<?php echo $workflow_data['publish']['published_tasks']; ?> content published
<?php elseif ($workflow_data['publish']['draft_tasks'] > 0): ?>
<?php echo $workflow_data['publish']['draft_tasks']; ?> drafts ready to publish
<?php else: ?>
No content to publish
<?php endif; ?>
</div>
<?php if ($workflow_data['publish']['status'] === 'in_progress' && $workflow_data['publish']['module_enabled']): ?>
<div class="igny8-system-step-action">
<a href="<?php echo admin_url($workflow_data['publish']['url']); ?>" class="igny8-btn igny8-btn-primary">Publish Now</a>
</div>
<?php endif; ?>
<div class="igny8-system-step-connector"></div>
</div>
</div>
</div>
<!-- Welcome Section -->
<div class="igny8-card">
<div class="igny8-card-header">
<h3>Welcome to Igny8 AI SEO OS</h3>
</div>
<div class="igny8-card-body">
<p>Your comprehensive SEO management platform. Use the workflow guide above to track your progress through the complete content creation and optimization pipeline.</p>
<p>Each step shows your current status and provides direct links to the relevant modules. Click on any step to navigate to the appropriate section.</p>
</div>
</div>
</div>
<?php
// Capture page content
$igny8_page_content = ob_get_clean();
// Include global layout
include_once plugin_dir_path(__FILE__) . '../core/global-layout.php';
?>

View File

@@ -0,0 +1,84 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : clusters.php
* @location : modules/planner/
* @type : Clusters Management Page
* @scope : Planner Module
* @allowed : Keyword clustering, content grouping, and cluster management
* @reusability : Planner Module
* @notes : Configuration-driven clusters management interface using complete component system
*
* @package Igny8Compact
* @since 1.0.0
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Set module variables
$GLOBALS['current_submodule'] = 'clusters';
$GLOBALS['current_module'] = 'planner';
// Load all required components
require_once plugin_dir_path(__FILE__) . '../../modules/components/filters-tpl.php';
require_once plugin_dir_path(__FILE__) . '../../modules/components/actions-tpl.php';
require_once plugin_dir_path(__FILE__) . '../../modules/components/table-tpl.php';
require_once plugin_dir_path(__FILE__) . '../../modules/components/pagination-tpl.php';
// Set table ID for components
$table_id = 'planner_clusters';
?>
<div class="igny8-module-page">
<div class="igny8-container">
<!-- Clusters Management Section -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Keyword Clusters</h3>
<p class="igny8-card-subtitle">Organize keywords into content clusters for better SEO strategy</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-networking igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
</div>
<!-- Complete Submodule Interface -->
<?php
// Render all components in correct order
echo igny8_render_filters($table_id);
echo igny8_render_table_actions($table_id);
echo igny8_render_table($table_id);
echo igny8_render_pagination($table_id);
// Get cluster options for dropdowns
$cluster_options = igny8_get_cluster_options();
// Load filters configuration for JavaScript
$filters_config = require plugin_dir_path(__FILE__) . '../../modules/config/filters-config.php';
// Localize script for table initialization
wp_localize_script('igny8-admin-js', 'IGNY8_PAGE', [
'tableId' => $table_id,
'module' => 'planner',
'submodule' => 'clusters',
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('igny8_planner_settings'),
'defaultPerPage' => get_option('igny8_records_per_page', 20),
'clusterOptions' => $cluster_options,
'filtersConfig' => $filters_config,
'cronKey' => igny8_needs_cron_functionality() ? igny8_get_or_generate_cron_key() : null
]);
?>
</div>
</div>
</div>

View File

@@ -0,0 +1,84 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : ideas.php
* @location : modules/planner/
* @type : Ideas Management Page
* @scope : Planner Module
* @allowed : Content ideas generation, management, and organization
* @reusability : Planner Module
* @notes : Configuration-driven ideas management interface using complete component system
*
* @package Igny8Compact
* @since 1.0.0
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Set module variables
$GLOBALS['current_submodule'] = 'ideas';
$GLOBALS['current_module'] = 'planner';
// Load all required components
require_once plugin_dir_path(__FILE__) . '../../modules/components/filters-tpl.php';
require_once plugin_dir_path(__FILE__) . '../../modules/components/actions-tpl.php';
require_once plugin_dir_path(__FILE__) . '../../modules/components/table-tpl.php';
require_once plugin_dir_path(__FILE__) . '../../modules/components/pagination-tpl.php';
// Set table ID for components
$table_id = 'planner_ideas';
?>
<div class="igny8-module-page">
<div class="igny8-container">
<!-- Ideas Management Section -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Content Ideas</h3>
<p class="igny8-card-subtitle">Generate and manage content ideas for your clusters</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-lightbulb igny8-dashboard-icon-lg igny8-dashboard-icon-purple"></span>
</div>
</div>
</div>
<!-- Complete Submodule Interface -->
<?php
// Render all components in correct order
echo igny8_render_filters($table_id);
echo igny8_render_table_actions($table_id);
echo igny8_render_table($table_id);
echo igny8_render_pagination($table_id);
// Get cluster options for dropdowns
$cluster_options = igny8_get_cluster_options();
// Load filters configuration for JavaScript
$filters_config = require plugin_dir_path(__FILE__) . '../../modules/config/filters-config.php';
// Localize script for table initialization
wp_localize_script('igny8-admin-js', 'IGNY8_PAGE', [
'tableId' => $table_id,
'module' => 'planner',
'submodule' => 'ideas',
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('igny8_planner_settings'),
'defaultPerPage' => get_option('igny8_records_per_page', 20),
'clusterOptions' => $cluster_options,
'filtersConfig' => $filters_config,
'cronKey' => igny8_needs_cron_functionality() ? igny8_get_or_generate_cron_key() : null
]);
?>
</div>
</div>
</div>

View File

@@ -0,0 +1,84 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : keywords.php
* @location : modules/planner/
* @type : Keywords Management Page
* @scope : Planner Module
* @allowed : Keywords research, import, analysis, and management
* @reusability : Planner Module
* @notes : Configuration-driven keywords management interface using complete component system
*
* @package Igny8Compact
* @since 1.0.0
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Set module variables
$GLOBALS['current_submodule'] = 'keywords';
$GLOBALS['current_module'] = 'planner';
// Load all required components
require_once plugin_dir_path(__FILE__) . '../../modules/components/filters-tpl.php';
require_once plugin_dir_path(__FILE__) . '../../modules/components/actions-tpl.php';
require_once plugin_dir_path(__FILE__) . '../../modules/components/table-tpl.php';
require_once plugin_dir_path(__FILE__) . '../../modules/components/pagination-tpl.php';
// Set table ID for components
$table_id = 'planner_keywords';
?>
<div class="igny8-module-page">
<div class="igny8-container">
<!-- Keywords Management Section -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Keywords Management</h3>
<p class="igny8-card-subtitle">Import, analyze, and organize your keywords</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-tag igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<!-- Complete Submodule Interface -->
<?php
// Render all components in correct order
echo igny8_render_filters($table_id);
echo igny8_render_table_actions($table_id);
echo igny8_render_table($table_id);
echo igny8_render_pagination($table_id);
// Get cluster options for dropdowns
$cluster_options = igny8_get_cluster_options();
// Load filters configuration for JavaScript
$filters_config = require plugin_dir_path(__FILE__) . '../../modules/config/filters-config.php';
// Localize script for table initialization
wp_localize_script('igny8-admin-js', 'IGNY8_PAGE', [
'tableId' => $table_id,
'module' => 'planner',
'submodule' => 'keywords',
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('igny8_planner_settings'),
'defaultPerPage' => get_option('igny8_records_per_page', 20),
'clusterOptions' => $cluster_options,
'filtersConfig' => $filters_config,
'cronKey' => igny8_needs_cron_functionality() ? igny8_get_or_generate_cron_key() : null
]);
?>
</div>
</div>
</div>

View File

@@ -0,0 +1,653 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : planner.php
* @location : /modules/planner/planner.php
* @type : Admin Page
* @scope : Module Only
* @allowed : Planner module logic, subpage routing, step data functions
* @reusability : Single Use
* @notes : Main planner page with subpage routing and step data functions
*/
if (!defined('ABSPATH')) exit;
// Component render functions are loaded centrally via global-layout.php
/**
* Get step data for Planner workflow guide
*
* @return array Array of step data with status and counts
*/
function igny8_get_planner_step_data() {
global $wpdb;
// Get counts for each step
$keywords_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_keywords");
$unmapped_keywords = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_keywords WHERE cluster_id IS NULL OR cluster_id = 0");
$clusters_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_clusters");
$ideas_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_content_ideas");
$queued_ideas = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_content_ideas WHERE status = 'new'");
// Check sector selection
$sector_selected = !empty(igny8_get_saved_sector_selection());
return [
'keywords' => [
'count' => $keywords_count,
'unmapped' => $unmapped_keywords,
'status' => $keywords_count > 0 ? 'completed' : 'pending'
],
'sector' => [
'selected' => $sector_selected,
'status' => $sector_selected ? 'completed' : 'current'
],
'clusters' => [
'count' => $clusters_count,
'unmapped_keywords' => $unmapped_keywords,
'status' => $unmapped_keywords == 0 && $clusters_count > 0 ? 'completed' : ($unmapped_keywords > 0 ? 'current' : 'pending')
],
'ideas' => [
'count' => $ideas_count,
'status' => $ideas_count > 0 ? 'completed' : 'pending'
],
'queue' => [
'queued_ideas' => $queued_ideas,
'status' => $queued_ideas == 0 && $ideas_count > 0 ? 'completed' : ($queued_ideas > 0 ? 'current' : 'pending')
]
];
}
// Handle URL parameters for subpages
$subpage = $_GET['sm'] ?? 'home';
$GLOBALS['current_submodule'] = $subpage;
$GLOBALS['current_module'] = 'planner';
// Start output buffering
ob_start();
switch ($subpage) {
case 'keywords':
include plugin_dir_path(__FILE__) . 'keywords.php';
break;
case 'clusters':
include plugin_dir_path(__FILE__) . 'clusters.php';
break;
case 'ideas':
include plugin_dir_path(__FILE__) . 'ideas.php';
break;
case 'home':
default:
// Home dashboard content
?>
<div class="igny8-module-home">
<?php
// Check if sector is selected - show alert if not (reuse existing function)
$sector_options = igny8_get_sector_options();
if (empty($sector_options)):
?>
<!-- Sector Selection Required Alert -->
<div class="igny8-card igny8-card-warning" style="margin-bottom: 20px; border-left: 4px solid var(--amber); background: linear-gradient(135deg, #fff8f0 0%, #fef3e7 100%);">
<div class="igny8-card-header">
<span style="font-size: 20px; margin-right: 12px;">⚠</span>
<strong>Please select a Sector to continue.</strong> Sector selection is required to start AI-based workflows.
</div>
</div>
<?php endif; ?>
<?php
// Load KPI data for dashboard calculations
if (!defined('IGNY8_INCLUDE_CONFIG')) {
define('IGNY8_INCLUDE_CONFIG', true);
}
$kpi_config = $GLOBALS['igny8_kpi_config'] ?? []; // Loaded globally in igny8.php
$kpi_data = igny8_get_kpi_data_safe('planner_home', $kpi_config['planner_home'] ?? []);
// Calculate dashboard metrics
$total_keywords = $kpi_data['total_keywords'] ?? 0;
$mapped_keywords = $kpi_data['mapped_keywords'] ?? 0;
$total_clusters = $kpi_data['total_clusters'] ?? 0;
$clusters_with_ideas = $kpi_data['clusters_with_ideas'] ?? 0;
$total_ideas = $kpi_data['total_ideas'] ?? 0;
$queued_ideas = $kpi_data['queued_ideas'] ?? 0;
// Calculate percentages for progress bars
$keyword_mapping_pct = $total_keywords > 0 ? round(($mapped_keywords / $total_keywords) * 100) : 0;
$clusters_ideas_pct = $total_clusters > 0 ? round(($clusters_with_ideas / $total_clusters) * 100) : 0;
$ideas_queued_pct = $total_ideas > 0 ? round(($queued_ideas / $total_ideas) * 100) : 0;
// Use fixed colors matching top metric cards
$keyword_color = 'blue'; // Keywords Ready card is blue
$cluster_color = 'green'; // Clusters Built card is green
$idea_color = 'amber'; // Ideas Generated card is amber
?>
<!-- Planning Status Cards -->
<div class="igny8-dashboard-section">
<div class="igny8-grid-3 igny8-status-cards">
<!-- Keywords Ready Card -->
<div class="igny8-card igny8-status-card igny8-clickable-card igny8-status-blue" onclick="window.location.href='<?php echo admin_url('admin.php?page=igny8-planner&sm=keywords'); ?>'">
<div class="igny8-card-body">
<div class="igny8-status-metric">
<span class="igny8-status-count"><?php echo $total_keywords; ?></span>
<span class="igny8-status-label">Keywords Ready</span>
<span class="igny8-status-desc">Research, analyze, and manage keywords strategy</span>
</div>
<div class="igny8-status-icon">
<span class="dashicons dashicons-search igny8-dashboard-icon-sm"></span>
</div>
</div>
</div>
<!-- Clusters Built Card -->
<div class="igny8-card igny8-status-card igny8-clickable-card igny8-status-green" onclick="window.location.href='<?php echo admin_url('admin.php?page=igny8-planner&sm=clusters'); ?>'">
<div class="igny8-card-body">
<div class="igny8-status-metric">
<span class="igny8-status-count"><?php echo $total_clusters; ?></span>
<span class="igny8-status-label">Clusters Built</span>
<span class="igny8-status-desc">Organize keywords into strategic topical clusters</span>
</div>
<div class="igny8-status-icon">
<span class="dashicons dashicons-groups igny8-dashboard-icon-sm"></span>
</div>
</div>
</div>
<!-- Ideas Generated Card -->
<div class="igny8-card igny8-status-card igny8-clickable-card igny8-status-amber" onclick="window.location.href='<?php echo admin_url('admin.php?page=igny8-planner&sm=ideas'); ?>'">
<div class="igny8-card-body">
<div class="igny8-status-metric">
<span class="igny8-status-count"><?php echo $total_ideas; ?></span>
<span class="igny8-status-label">Ideas Generated</span>
<span class="igny8-status-desc">Generate creative content ideas based on semantic strategy</span>
</div>
<div class="igny8-status-icon">
<span class="dashicons dashicons-lightbulb igny8-dashboard-icon-sm"></span>
</div>
</div>
</div>
</div>
</div>
<!-- Planner Workflow Steps -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Planner Workflow Steps</h3>
<p class="igny8-card-subtitle">Track your planning progress</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-update igny8-dashboard-icon-lg igny8-dashboard-icon-purple"></span>
</div>
</div>
</div>
<div class="igny8-grid igny8-grid-4">
<?php
// Step-by-Step UX Guide
$step_data = igny8_get_planner_step_data();
?>
<div class="igny8-card igny8-step-card <?php echo $step_data['keywords']['status']; ?>">
<div class="igny8-card-body">
<div class="igny8-step-header">
<div class="igny8-step-number">1</div>
<div class="igny8-step-title">Add Keywords</div>
</div>
<div class="igny8-step-status">
<span class="igny8-step-status-icon"><?php echo $step_data['keywords']['status'] === 'completed' ? '✅' : '⏳'; ?></span>
<span class="igny8-step-status-text"><?php echo $step_data['keywords']['status'] === 'completed' ? 'Completed' : 'Pending'; ?></span>
</div>
<div class="igny8-step-data">
<?php if ($step_data['keywords']['count'] > 0): ?>
<?php echo $step_data['keywords']['count']; ?> keywords added
<?php else: ?>
No keywords yet
<?php endif; ?>
</div>
<?php if ($step_data['keywords']['status'] === 'pending'): ?>
<div class="igny8-step-action">
<a href="?page=igny8-planner&sm=keywords" class="igny8-btn igny8-btn-primary igny8-btn-sm">Start Now</a>
</div>
<?php endif; ?>
</div>
</div>
<div class="igny8-card igny8-step-card <?php echo $step_data['sector']['status']; ?>">
<div class="igny8-card-body">
<div class="igny8-step-header">
<div class="igny8-step-number">2</div>
<div class="igny8-step-title">Select Sector</div>
</div>
<div class="igny8-step-status">
<span class="igny8-step-status-icon"><?php echo $step_data['sector']['status'] === 'completed' ? '✅' : '⚠'; ?></span>
<span class="igny8-step-status-text"><?php echo $step_data['sector']['status'] === 'completed' ? 'Selected' : 'Required'; ?></span>
</div>
<div class="igny8-step-data">
<?php if ($step_data['sector']['selected']): ?>
Sector configured
<?php else: ?>
Required for AI workflows
<?php endif; ?>
</div>
<?php if ($step_data['sector']['status'] === 'current'): ?>
<div class="igny8-step-action">
<a href="?page=igny8-planner" class="igny8-btn igny8-btn-primary igny8-btn-sm">Configure</a>
</div>
<?php endif; ?>
</div>
</div>
<div class="igny8-card igny8-step-card <?php echo $step_data['clusters']['status']; ?>">
<div class="igny8-card-body">
<div class="igny8-step-header">
<div class="igny8-step-number">3</div>
<div class="igny8-step-title">Auto Cluster</div>
</div>
<div class="igny8-step-status">
<span class="igny8-step-status-icon"><?php echo $step_data['clusters']['status'] === 'completed' ? '✅' : ($step_data['clusters']['status'] === 'current' ? '⏳' : '⏳'); ?></span>
<span class="igny8-step-status-text"><?php echo $step_data['clusters']['status'] === 'completed' ? 'Completed' : ($step_data['clusters']['status'] === 'current' ? 'Ready' : 'Pending'); ?></span>
</div>
<div class="igny8-step-data">
<?php if ($step_data['clusters']['unmapped_keywords'] > 0): ?>
<?php echo $step_data['clusters']['unmapped_keywords']; ?> unmapped keywords
<?php elseif ($step_data['clusters']['count'] > 0): ?>
<?php echo $step_data['clusters']['count']; ?> clusters created
<?php else: ?>
No clusters yet
<?php endif; ?>
</div>
<?php if ($step_data['clusters']['status'] === 'current'): ?>
<div class="igny8-step-action">
<a href="?page=igny8-planner&sm=clusters" class="igny8-btn igny8-btn-primary igny8-btn-sm">Start Now</a>
</div>
<?php endif; ?>
</div>
</div>
<div class="igny8-card igny8-step-card <?php echo $step_data['ideas']['status']; ?>">
<div class="igny8-card-body">
<div class="igny8-step-header">
<div class="igny8-step-number">4</div>
<div class="igny8-step-title">Generate Ideas</div>
</div>
<div class="igny8-step-status">
<span class="igny8-step-status-icon"><?php echo $step_data['ideas']['status'] === 'completed' ? '✅' : '⏳'; ?></span>
<span class="igny8-step-status-text"><?php echo $step_data['ideas']['status'] === 'completed' ? 'Completed' : 'Pending'; ?></span>
</div>
<div class="igny8-step-data">
<?php if ($step_data['ideas']['count'] > 0): ?>
<?php echo $step_data['ideas']['count']; ?> ideas generated
<?php else: ?>
No ideas yet
<?php endif; ?>
</div>
<?php if ($step_data['ideas']['status'] === 'pending' && $step_data['clusters']['count'] > 0): ?>
<div class="igny8-step-action">
<a href="?page=igny8-planner&sm=ideas" class="igny8-btn igny8-btn-primary igny8-btn-sm">Start Now</a>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<!-- Planner Settings & AI Integration -->
<div class="igny8-dashboard-section">
<div class="igny8-flex-row">
<!-- AI Integration Settings Card -->
<div class="igny8-card">
<div class="igny8-flex-row">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Workflow Status & AI Settings</h3>
<p class="igny8-card-subtitle igny8-centered">Workflow Status & AI Settings</p>
</div>
</div>
<form id="igny8-ai-integration-form">
<div class="igny8-form-group">
<div class="igny8-mode-toggle-label">
<span class="igny8-mode-label">Manual</span>
<label class="igny8-toggle">
<input type="checkbox" name="igny8_planner_mode" value="ai" <?php checked(igny8_get_ai_setting('planner_mode', 'manual'), 'ai'); ?>>
<span class="igny8-toggle-slider"></span>
</label>
<span class="igny8-mode-label">AI Mode</span>
</div>
</div>
<div class="igny8-form-actions">
<button type="submit" class="igny8-btn igny8-btn-success">Save AI Integration Settings</button>
</div>
</form>
</div>
</div>
<!-- Sector Selection Settings Card -->
<div class="igny8-card">
<div class="igny8-flex-row">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Workflow Status & AI Settings</h3>
<p class="igny8-card-subtitle igny8-centered">Workflow Status & AI Settings</p>
</div>
</div>
<div id="igny8-sector-selection-container">
<!-- Initial state: Parent sector selection -->
<div id="igny8-parent-selection" class="igny8-selection-step">
<label for="igny8-parent-sector">Select Parent Sector:</label>
<select id="igny8-parent-sector" class="igny8-form-control">
<option value="">Choose a parent sector...</option>
</select>
<button id="igny8-lock-parent" class="igny8-btn igny8-btn-primary" style="display: none;">Lock Selection</button>
</div>
<!-- Child sectors selection -->
<div id="igny8-child-selection" class="igny8-selection-step" style="display: none;">
<h4>Select Child Sectors:</h4>
<div id="igny8-child-checkboxes" class="igny8-checkbox-group">
<!-- Child checkboxes will be populated here -->
</div>
<button id="igny8-save-selection" class="igny8-btn igny8-btn-success">Save Selection</button>
</div>
<!-- Final selection display -->
<div id="igny8-final-selection" class="igny8-selection-step" style="display: none;">
<div id="igny8-selected-sectors-display">
<!-- Selected sectors will be displayed here -->
</div>
<button id="igny8-edit-selection" class="igny8-btn igny8-btn-outline">Remove or Edit My Selection</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="igny8-grid-3">
<!-- Progress / Readiness Summary -->
<div class="igny8-dashboard-section">
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Progress & Readiness Summary</h3>
<p class="igny8-card-subtitle">Planning workflow progress tracking</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-chart-area igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<!-- Keyword Mapping Progress -->
<div class="igny8-progress-item">
<div class="igny8-progress-header">
<span class="igny8-progress-label">Keyword Mapping</span>
<span class="igny8-progress-percent"><?php echo $keyword_mapping_pct; ?>%</span>
</div>
<div class="igny8-progress-bar">
<div class="igny8-progress-fill igny8-progress-<?php echo $keyword_color; ?>" style="width: <?php echo $keyword_mapping_pct; ?>%"></div>
</div>
<div class="igny8-progress-details"><?php echo $mapped_keywords; ?> of <?php echo $total_keywords; ?> keywords mapped</div>
</div>
<!-- Clusters With Ideas Progress -->
<div class="igny8-progress-item">
<div class="igny8-progress-header">
<span class="igny8-progress-label">Clusters With Ideas</span>
<span class="igny8-progress-percent"><?php echo $clusters_ideas_pct; ?>%</span>
</div>
<div class="igny8-progress-bar">
<div class="igny8-progress-fill igny8-progress-<?php echo $cluster_color; ?>" style="width: <?php echo $clusters_ideas_pct; ?>%"></div>
</div>
<div class="igny8-progress-details"><?php echo $clusters_with_ideas; ?> of <?php echo $total_clusters; ?> clusters have ideas</div>
</div>
<!-- Ideas Queued to Writer Progress -->
<div class="igny8-progress-item">
<div class="igny8-progress-header">
<span class="igny8-progress-label">Ideas Queued to Writer</span>
<span class="igny8-progress-percent"><?php echo $ideas_queued_pct; ?>%</span>
</div>
<div class="igny8-progress-bar">
<div class="igny8-progress-fill igny8-progress-<?php echo $idea_color; ?>" style="width: <?php echo $ideas_queued_pct; ?>%"></div>
</div>
<div class="igny8-progress-details"><?php echo $queued_ideas; ?> of <?php echo $total_ideas; ?> ideas queued</div>
</div>
</div>
</div>
</div>
<!-- Top 5 Clusters by Volume -->
<div class="igny8-dashboard-section">
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Top 5 Clusters by Volume</h3>
<p class="igny8-card-subtitle">Highest volume keyword clusters</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-chart-bar igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<?php
// Get top 5 clusters by volume
global $wpdb;
$top_clusters = $wpdb->get_results("
SELECT
c.cluster_name,
c.total_volume,
c.keyword_count
FROM {$wpdb->prefix}igny8_clusters c
WHERE c.status = 'active' AND c.total_volume > 0
ORDER BY c.total_volume DESC
LIMIT 5
");
if ($top_clusters):
$max_volume = $top_clusters[0]->total_volume; // Highest volume for percentage calculation
$color_classes = ['igny8-progress-blue', 'igny8-progress-green', 'igny8-progress-amber', 'igny8-progress-purple', 'igny8-progress-text-dim'];
?>
<div class="igny8-analytics-list">
<?php foreach ($top_clusters as $index => $cluster):
$percentage = $max_volume > 0 ? round(($cluster->total_volume / $max_volume) * 100) : 0;
$color_class = $color_classes[$index % 5];
?>
<div class="igny8-analytics-item">
<div class="igny8-item-info">
<div class="igny8-item-label"><?php echo esc_html($cluster->cluster_name); ?></div>
<div class="igny8-item-value"><?php echo number_format($cluster->total_volume); ?></div>
</div>
<div class="igny8-item-progress">
<div class="igny8-progress-track">
<div class="igny8-progress-fill <?php echo $color_class; ?>" style="width: <?php echo $percentage; ?>%"></div>
</div>
<span class="igny8-progress-percent"><?php echo $percentage; ?>%</span>
</div>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<div class="igny8-empty-analytics">
<span class="dashicons dashicons-info igny8-dashboard-icon-dim"></span>
<p>No clusters found yet</p>
<a href="<?php echo admin_url('admin.php?page=igny8-planner&sm=clusters'); ?>" class="igny8-btn igny8-btn-outline igny8-btn-sm">View Clusters</a>
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- Ideas by Status -->
<div class="igny8-dashboard-section">
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Ideas by Status</h3>
<p class="igny8-card-subtitle">Content ideas workflow status</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-lightbulb igny8-dashboard-icon-lg igny8-dashboard-icon-amber"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<?php
// Get ideas by status
$ideas_by_status = $wpdb->get_results("
SELECT
status,
COUNT(*) as count
FROM {$wpdb->prefix}igny8_content_ideas
GROUP BY status
ORDER BY count DESC
");
if ($ideas_by_status):
$total_ideas_status = array_sum(array_column($ideas_by_status, 'count'));
$status_colors = [
'new' => 'igny8-progress-blue',
'scheduled' => 'igny8-progress-amber',
'published' => 'igny8-progress-green',
'draft' => 'igny8-progress-purple',
'completed' => 'igny8-progress-green'
];
?>
<div class="igny8-analytics-list">
<?php foreach ($ideas_by_status as $status):
$percentage = $total_ideas_status > 0 ? round(($status->count / $total_ideas_status) * 100) : 0;
$color_class = $status_colors[$status->status] ?? 'igny8-progress-text-dim';
$status_display = ucfirst(str_replace('_', ' ', $status->status));
?>
<div class="igny8-analytics-item">
<div class="igny8-item-info">
<div class="igny8-item-label"><?php echo esc_html($status_display); ?></div>
<div class="igny8-item-value"><?php echo number_format($status->count); ?></div>
</div>
<div class="igny8-item-progress">
<div class="igny8-progress-track">
<div class="igny8-progress-fill <?php echo $color_class; ?>" style="width: <?php echo $percentage; ?>%"></div>
</div>
<span class="igny8-progress-percent"><?php echo $percentage; ?>%</span>
</div>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<div class="igny8-empty-analytics">
<span class="dashicons dashicons-info igny8-dashboard-icon-dim"></span>
<p>No ideas found yet</p>
<a href="<?php echo admin_url('admin.php?page=igny8-planner&sm=ideas'); ?>" class="igny8-btn igny8-btn-outline igny8-btn-sm">View Ideas</a>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<!-- Next Actions Panel -->
<div class="igny8-dashboard-section">
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Next Actions</h3>
<p class="igny8-card-subtitle">Actionable items requiring attention</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-arrow-right-alt igny8-dashboard-icon-lg igny8-dashboard-icon-purple"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<div class="igny8-next-actions">
<?php
$unmapped_keywords = $kpi_data['unmapped_keywords'] ?? 0;
$clusters_without_ideas = $total_clusters - $clusters_with_ideas;
$ideas_not_queued = $total_ideas - $queued_ideas;
?>
<?php if ($unmapped_keywords > 0): ?>
<div class="igny8-action-item">
<span class="igny8-action-text"><?php echo $unmapped_keywords; ?> keywords unmapped</span>
<a href="<?php echo admin_url('admin.php?page=igny8-planner&sm=keywords&filter_status=unmapped'); ?>" class="igny8-btn igny8-btn-text">Map Keywords</a>
</div>
<?php endif; ?>
<?php if ($clusters_without_ideas > 0): ?>
<div class="igny8-action-item">
<span class="igny8-action-text"><?php echo $clusters_without_ideas; ?> clusters without ideas</span>
<a href="<?php echo admin_url('admin.php?page=igny8-planner&sm=ideas'); ?>" class="igny8-btn igny8-btn-text">Generate Ideas</a>
</div>
<?php endif; ?>
<?php if ($ideas_not_queued > 0): ?>
<div class="igny8-action-item">
<span class="igny8-action-text"><?php echo $ideas_not_queued; ?> ideas not queued to writer</span>
<a href="<?php echo admin_url('admin.php?page=igny8-planner&sm=ideas&filter_status=new'); ?>" class="igny8-btn igny8-btn-text">Queue to Writer</a>
</div>
<?php endif; ?>
<div class="igny8-action-item">
<span class="igny8-action-text">Import new keywords to expand your strategy</span>
<a href="<?php echo admin_url('admin.php?page=igny8-import-export'); ?>" class="igny8-btn igny8-btn-text">Import Keywords</a>
</div>
<?php if ($unmapped_keywords == 0 && $clusters_without_ideas == 0 && $ideas_not_queued == 0): ?>
<div class="igny8-action-item igny8-action-complete">
<span class="igny8-action-text">All planning tasks completed!</span>
<span class="igny8-action-status">✓ Ready for content creation</span>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php
// Set submodule for subheader display - show keywords charts on home page
$current_submodule = 'keywords';
// Home page - add sector selection functionality
// Only load CRON key if this page needs CRON functionality
$cron_key = igny8_needs_cron_functionality() ? igny8_get_or_generate_cron_key() : null;
// Debug logging (remove in production)
// if (defined('WP_DEBUG') && WP_DEBUG) {
// error_log('Igny8 Planner: cronKey being passed to JS: ' . $cron_key);
// }
wp_localize_script('igny8-admin-js', 'IGNY8_PAGE', [
'module' => 'planner',
'submodule' => 'home',
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('igny8_planner_settings'),
'cronKey' => $cron_key
]);
break;
}
// Capture page content
$igny8_page_content = ob_get_clean();
// Include global layout
include_once plugin_dir_path(__FILE__) . '../../core/global-layout.php';

View File

@@ -0,0 +1,676 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : general-settings.php
* @location : /modules/settings/general-settings.php
* @type : Admin Page
* @scope : Module Only
* @allowed : Settings configuration, subpage routing, plugin preferences
* @reusability : Single Use
* @notes : Main settings page with subpage routing for settings module
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Initialize module manager and render the complete page
$module_manager = igny8_module_manager();
// Handle URL parameters for subpages
$subpage = $_GET['sp'] ?? 'general';
// Start output buffering
ob_start();
switch ($subpage) {
case 'status':
include plugin_dir_path(__FILE__) . 'status.php';
break;
case 'integration':
include plugin_dir_path(__FILE__) . 'integration.php';
break;
case 'schedules':
include plugin_dir_path(__FILE__) . 'schedules.php';
break;
case 'import-export':
include plugin_dir_path(__FILE__) . 'import-export.php';
break;
case 'general':
default:
// General settings content
// Handle image generation settings form submission
if (isset($_POST['igny8_image_settings_nonce']) && wp_verify_nonce($_POST['igny8_image_settings_nonce'], 'igny8_image_settings')) {
$image_type = sanitize_text_field($_POST['igny8_image_type'] ?? 'realistic');
$desktop_enabled = isset($_POST['igny8_desktop_enabled']) ? '1' : '0';
$mobile_enabled = isset($_POST['igny8_mobile_enabled']) ? '1' : '0';
$max_in_article_images = intval($_POST['igny8_max_in_article_images'] ?? 1);
$image_format = sanitize_text_field($_POST['igny8_image_format'] ?? 'jpg');
update_option('igny8_image_type', $image_type);
update_option('igny8_desktop_enabled', $desktop_enabled);
update_option('igny8_mobile_enabled', $mobile_enabled);
update_option('igny8_max_in_article_images', $max_in_article_images);
update_option('igny8_image_format', $image_format);
echo '<div class="notice notice-success"><p>Image generation settings saved successfully!</p></div>';
} elseif (isset($_POST['igny8_image_settings_nonce'])) {
echo '<div class="notice notice-error"><p>Security check failed. Please try again.</p></div>';
}
// Handle editor type settings form submission
if (isset($_POST['igny8_editor_type_nonce']) && wp_verify_nonce($_POST['igny8_editor_type_nonce'], 'igny8_editor_type_settings')) {
$editor_type = isset($_POST['igny8_editor_type']) ? sanitize_text_field($_POST['igny8_editor_type']) : 'block';
update_option('igny8_editor_type', $editor_type);
echo '<div class="notice notice-success"><p>Editor type settings saved successfully! Selected: ' . esc_html($editor_type) . '</p></div>';
} elseif (isset($_POST['igny8_editor_type_nonce'])) {
echo '<div class="notice notice-error"><p>Security check failed. Please try again.</p></div>';
}
// Handle image metabox settings form submission
if (isset($_POST['igny8_image_metabox_nonce']) && wp_verify_nonce($_POST['igny8_image_metabox_nonce'], 'igny8_image_metabox_settings')) {
$enabled_types = isset($_POST['igny8_enable_image_metabox']) ? $_POST['igny8_enable_image_metabox'] : [];
$enabled_types = array_map('sanitize_text_field', $enabled_types);
update_option('igny8_enable_image_metabox', $enabled_types);
echo '<div class="notice notice-success"><p>Image metabox settings saved successfully!</p></div>';
}
// Handle module settings form submission (only if not editor type or image metabox form)
if (isset($_POST['submit']) && !isset($_POST['igny8_editor_type_nonce']) && !isset($_POST['igny8_image_metabox_nonce'])) {
$module_manager->save_module_settings();
}
// Debug: Log form submission data (remove in production)
if (isset($_POST['submit']) && current_user_can('manage_options')) {
error_log('Igny8 Settings Debug - POST data: ' . print_r($_POST, true));
}
$settings = get_option('igny8_module_settings', []);
?>
<div class="igny8-settings-section">
<!-- Module Manager Section -->
<div class="igny8-settings-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Module Manager</h3>
<p class="igny8-card-subtitle">Enable or disable plugin modules and features</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-tools igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<!-- Module Settings -->
<div class="igny8-settings-section">
<div class="igny8-settings">
<form method="post" action="">
<?php wp_nonce_field('igny8_module_settings', 'igny8_module_nonce'); ?>
<!-- Main Modules Section -->
<div class="igny8-settings-section">
<div class="igny8-module-cards-grid">
<?php foreach ($module_manager->get_modules() as $module_key => $module): ?>
<?php if ($module['category'] === 'main'): ?>
<div class="igny8-card igny8-module-card">
<div class="igny8-module-header">
<div class="igny8-card-title">
<span class="dashicons <?php echo esc_attr($module['icon']); ?>" style="color: var(--blue); font-size: 20px; margin-right: 10px;"></span>
<h6><?php echo esc_html($module['name']); ?></h6>
</div>
<div class="igny8-module-toggle">
<label class="igny8-toggle-switch">
<input type="checkbox"
id="module_<?php echo esc_attr($module_key); ?>"
name="igny8_module_settings[<?php echo esc_attr($module_key); ?>]"
value="1"
<?php checked($module_manager->is_module_enabled($module_key)); ?>>
<span class="igny8-toggle-slider"></span>
</label>
</div>
</div>
<div class="igny8-module-description">
<p><?php echo esc_html($module['description']); ?></p>
</div>
</div>
<?php endif; ?>
<?php endforeach; ?>
</div>
</div>
<!-- Admin & Analytics Section -->
<div class="igny8-settings-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Admin & Analytics</h3>
<p class="igny8-card-subtitle">Administrative tools and analytics modules</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-chart-bar igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
</div>
<div class="igny8-module-cards-grid">
<?php foreach ($module_manager->get_modules() as $module_key => $module): ?>
<?php if ($module['category'] === 'admin'): ?>
<div class="igny8-card igny8-module-card">
<div class="igny8-module-header">
<div class="igny8-card-title">
<span class="dashicons <?php echo esc_attr($module['icon']); ?>" style="color: var(--green); font-size: 20px; margin-right: 10px;"></span>
<h6><?php echo esc_html($module['name']); ?></h6>
</div>
<div class="igny8-module-toggle">
<label class="igny8-toggle-switch">
<input type="checkbox"
id="module_<?php echo esc_attr($module_key); ?>"
name="igny8_module_settings[<?php echo esc_attr($module_key); ?>]"
value="1"
<?php checked($module_manager->is_module_enabled($module_key)); ?>>
<span class="igny8-toggle-slider"></span>
</label>
</div>
</div>
<div class="igny8-module-description">
<p><?php echo esc_html($module['description']); ?></p>
</div>
</div>
<?php endif; ?>
<?php endforeach; ?>
</div>
</div>
<?php submit_button('Save Module Settings', 'primary', 'submit', true, ['style' => 'margin-top: 20px;']); ?>
</form>
</div>
</div>
</div>
<!-- Editor Type Selection Section -->
<div class="igny8-settings-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Content Editor Type</h3>
<p class="igny8-card-subtitle">Choose between Classic editor or Block (Gutenberg) editor for AI-generated content</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-edit-page igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
</div>
<div class="igny8-settings">
<form method="post" action="">
<?php wp_nonce_field('igny8_editor_type_settings', 'igny8_editor_type_nonce'); ?>
<div class="igny8-form-group">
<label for="igny8_editor_type"><strong>Select Editor Type:</strong></label>
<div style="margin-top: 15px;">
<?php
$current_editor_type = get_option('igny8_editor_type', 'block');
?>
<!-- Current Setting Display -->
<div style="background: #e8f4fd; border: 1px solid #0073aa; padding: 10px; border-radius: 4px; margin-bottom: 15px;">
<strong>Current Setting:</strong>
<span style="color: #0073aa; font-weight: 600;">
<?php echo $current_editor_type === 'block' ? 'Block Editor (Gutenberg)' : 'Classic Editor'; ?>
</span>
</div>
<label class="igny8-editor-option <?php echo $current_editor_type === 'block' ? 'selected' : ''; ?>">
<input type="radio" name="igny8_editor_type" value="block" <?php checked($current_editor_type, 'block'); ?>>
<div class="igny8-editor-option-content">
<div class="igny8-editor-option-title">Block Editor (Gutenberg)</div>
<div class="igny8-editor-option-description">
Modern block-based editor with advanced formatting options. Recommended for better content structure and WordPress compatibility.
</div>
</div>
</label>
<label class="igny8-editor-option <?php echo $current_editor_type === 'classic' ? 'selected' : ''; ?>">
<input type="radio" name="igny8_editor_type" value="classic" <?php checked($current_editor_type, 'classic'); ?>>
<div class="igny8-editor-option-content">
<div class="igny8-editor-option-title">Classic Editor</div>
<div class="igny8-editor-option-description">
Traditional WYSIWYG editor. Choose this if you prefer the classic WordPress editing experience.
</div>
</div>
</label>
</div>
<div style="background: #f8f9fa; border: 1px solid #e9ecef; padding: 15px; border-radius: 6px; margin-top: 20px;">
<p style="margin: 0 0 10px 0; font-weight: bold; color: #495057;">📝 <strong>How this affects your content:</strong></p>
<ul style="margin: 0; padding-left: 20px; color: #6c757d; font-size: 14px;">
<li><strong>Block Editor:</strong> AI content will be converted to WordPress blocks for better formatting and structure</li>
<li><strong>Classic Editor:</strong> AI content will be saved as HTML and displayed in the classic editor</li>
<li>You can change this setting anytime and it will apply to all new AI-generated content</li>
</ul>
</div>
</div>
<?php submit_button('Save Editor Settings', 'primary', 'submit', true, ['style' => 'margin-top: 20px;']); ?>
</form>
</div>
</div>
<script>
// Enhanced editor selection interaction
document.addEventListener('DOMContentLoaded', function() {
const editorOptions = document.querySelectorAll('.igny8-editor-option');
editorOptions.forEach(option => {
const radio = option.querySelector('input[type="radio"]');
// Add click handler to the entire label
option.addEventListener('click', function(e) {
if (e.target !== radio) {
radio.checked = true;
updateSelection();
}
});
// Add change handler to radio buttons
radio.addEventListener('change', function() {
updateSelection();
});
});
function updateSelection() {
editorOptions.forEach(option => {
const radio = option.querySelector('input[type="radio"]');
if (radio.checked) {
option.classList.add('selected');
} else {
option.classList.remove('selected');
}
});
}
// Initialize selection on page load
updateSelection();
});
</script>
<!-- Image Generation Settings Section -->
<div class="igny8-settings-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Image Generation</h3>
<p class="igny8-card-subtitle">Configure AI image generation settings and preferences</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-format-image igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<div class="igny8-settings">
<form method="post" action="" id="igny8-image-generation-form">
<?php wp_nonce_field('igny8_image_settings', 'igny8_image_settings_nonce'); ?>
<div class="igny8-form-group">
<label><strong>Image Settings</strong></label>
<div style="display: flex; gap: 20px; align-items: end; margin-top: 10px;">
<div style="flex: 1;">
<label for="igny8_image_type" style="display: block; margin-bottom: 5px; font-weight: 500;">Image Type</label>
<select id="igny8_image_type" name="igny8_image_type" class="igny8-form-control">
<option value="realistic" <?php selected(get_option('igny8_image_type', 'realistic'), 'realistic'); ?>>Realistic</option>
<option value="illustration" <?php selected(get_option('igny8_image_type', 'realistic'), 'illustration'); ?>>Illustration</option>
<option value="3D render" <?php selected(get_option('igny8_image_type', 'realistic'), '3D render'); ?>>3D Render</option>
<option value="minimalist" <?php selected(get_option('igny8_image_type', 'realistic'), 'minimalist'); ?>>Minimalist</option>
<option value="cartoon" <?php selected(get_option('igny8_image_type', 'realistic'), 'cartoon'); ?>>Cartoon</option>
</select>
</div>
<div style="flex: 1;">
<label for="igny8_max_in_article_images" style="display: block; margin-bottom: 5px; font-weight: 500;">Max In-Article Images</label>
<select id="igny8_max_in_article_images" name="igny8_max_in_article_images" class="igny8-form-control">
<option value="1" <?php selected(get_option('igny8_max_in_article_images', '1'), '1'); ?>>1 Image</option>
<option value="2" <?php selected(get_option('igny8_max_in_article_images', '1'), '2'); ?>>2 Images</option>
<option value="3" <?php selected(get_option('igny8_max_in_article_images', '1'), '3'); ?>>3 Images</option>
<option value="4" <?php selected(get_option('igny8_max_in_article_images', '1'), '4'); ?>>4 Images</option>
<option value="5" <?php selected(get_option('igny8_max_in_article_images', '1'), '5'); ?>>5 Images</option>
</select>
</div>
<div style="flex: 1;">
<label for="igny8_image_format" style="display: block; margin-bottom: 5px; font-weight: 500;">Image Format</label>
<select id="igny8_image_format" name="igny8_image_format" class="igny8-form-control">
<option value="jpg" <?php selected(get_option('igny8_image_format', 'jpg'), 'jpg'); ?>>JPG</option>
<option value="png" <?php selected(get_option('igny8_image_format', 'jpg'), 'png'); ?>>PNG</option>
<option value="webp" <?php selected(get_option('igny8_image_format', 'jpg'), 'webp'); ?>>WEBP</option>
</select>
<div id="igny8-selected-format" style="margin-top: 5px; font-weight: bold; color: #0073aa;"></div>
</div>
</div>
</div>
<div class="igny8-form-group">
<label><strong>Current Image Provider & Model</strong></label>
<div style="background: #f8f9fa; border: 1px solid #e9ecef; padding: 15px; border-radius: 6px; margin-top: 10px;">
<?php
$current_service = get_option('igny8_image_service', 'openai');
$current_model = get_option('igny8_image_model', 'dall-e-3');
$current_runware_model = get_option('igny8_runware_model', 'runware:97@1');
if ($current_service === 'openai') {
$model_names = [
'dall-e-3' => 'DALL·E 3',
'dall-e-2' => 'DALL·E 2',
'gpt-image-1' => 'GPT Image 1 (Full)',
'gpt-image-1-mini' => 'GPT Image 1 Mini'
];
$model_name = $model_names[$current_model] ?? $current_model;
echo '<div style="display: flex; align-items: center; margin-bottom: 8px;">';
echo '<span style="font-weight: bold; color: #0073aa; margin-right: 10px;">Provider:</span>';
echo '<span style="color: #495057;">OpenAI</span>';
echo '</div>';
echo '<div style="display: flex; align-items: center;">';
echo '<span style="font-weight: bold; color: #0073aa; margin-right: 10px;">Model:</span>';
echo '<span style="color: #495057;">' . esc_html($model_name) . '</span>';
echo '</div>';
} else {
echo '<div style="display: flex; align-items: center; margin-bottom: 8px;">';
echo '<span style="font-weight: bold; color: #0073aa; margin-right: 10px;">Provider:</span>';
echo '<span style="color: #495057;">Runware</span>';
echo '</div>';
echo '<div style="display: flex; align-items: center;">';
echo '<span style="font-weight: bold; color: #0073aa; margin-right: 10px;">Model:</span>';
echo '<span style="color: #495057;">HiDream-I1 Full</span>';
echo '</div>';
}
?>
<div style="margin-top: 10px; padding-top: 10px; border-top: 1px solid #dee2e6;">
<small style="color: #6c757d;">
<strong>Note:</strong> To change the image provider or model, go to
<a href="<?php echo admin_url('admin.php?page=igny8-settings&sp=integration'); ?>" style="color: #0073aa;">Integration Settings</a>
</small>
</div>
</div>
</div>
<div class="igny8-form-group">
<label><strong>Image Size Options</strong></label>
<div class="igny8-size-checkbox-container">
<!-- Featured Image - Simple row without checkbox/count -->
<div class="igny8-size-option igny8-featured-image-row">
<div class="igny8-size-info igny8-size-featured">Featured Image</div>
<div class="igny8-size-info" id="featured-size-info">1280×832 pixels</div>
</div>
<div class="igny8-size-option">
<label class="igny8-checkbox-label">
<input type="checkbox" id="igny8_desktop_enabled" name="igny8_desktop_enabled" value="1" <?php checked(get_option('igny8_desktop_enabled', '1'), '1'); ?>>
<span class="igny8-checkbox-text">Desktop Images</span>
</label>
<div class="igny8-size-info" id="desktop-size-info">1024×1024 pixels</div>
</div>
<div class="igny8-size-option">
<label class="igny8-checkbox-label">
<input type="checkbox" id="igny8_mobile_enabled" name="igny8_mobile_enabled" value="1" <?php checked(get_option('igny8_mobile_enabled', '0'), '1'); ?>>
<span class="igny8-checkbox-text">Mobile Images</span>
</label>
<div class="igny8-size-info" id="mobile-size-info">960×1280 pixels</div>
</div>
</div>
<small class="form-help">Choose which image sizes to generate and how many of each type.</small>
</div>
<?php submit_button('Save Image Settings', 'primary', 'submit', true, ['style' => 'margin-top: 20px;']); ?>
</form>
</div>
</div>
<script>
// Image generation settings JavaScript
document.addEventListener('DOMContentLoaded', function() {
// Get current provider from integration settings
const currentProvider = '<?php echo esc_js(get_option('igny8_image_service', 'openai')); ?>';
function updateSizeInfo(provider) {
const sizeInfo = {
'runware': {
featured: '1280×832 pixels',
desktop: '1024×1024 pixels',
mobile: '960×1280 pixels'
},
'openai': {
featured: '1024×1024 pixels',
desktop: '1024×1024 pixels',
mobile: '1024×1024 pixels'
},
'dalle': {
featured: '1024×1024 pixels',
desktop: '1024×1024 pixels',
mobile: '1024×1024 pixels'
}
};
if (sizeInfo[provider]) {
$('#featured-size-info').text(sizeInfo[provider].featured);
$('#desktop-size-info').text(sizeInfo[provider].desktop);
$('#mobile-size-info').text(sizeInfo[provider].mobile);
}
}
// Initialize with current provider
updateSizeInfo(currentProvider);
// Image format selector functionality
const $formatSelector = $('#igny8_image_format');
const $formatDisplay = $('#igny8-selected-format');
$formatSelector.on('change', function() {
const selectedFormat = $(this).val().toUpperCase();
$formatDisplay.text(selectedFormat + ' format');
});
// Initialize format display with saved value
const savedFormat = '<?php echo esc_js(get_option('igny8_image_format', 'jpg')); ?>';
$formatSelector.val(savedFormat);
$formatSelector.trigger('change');
});
</script>
<style>
/* Image Generation Settings Styles */
.igny8-size-checkbox-container {
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 8px;
padding: 20px;
margin-top: 10px;
}
.igny8-size-option {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid #e9ecef;
}
.igny8-size-option:last-child {
border-bottom: none;
}
.igny8-featured-image-row {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 6px;
padding: 15px 20px;
margin-bottom: 15px;
border-bottom: none;
}
.igny8-size-featured {
font-weight: bold;
font-size: 16px;
}
.igny8-checkbox-label {
display: flex;
align-items: center;
cursor: pointer;
font-weight: 500;
}
.igny8-checkbox-label input[type="checkbox"] {
margin-right: 10px;
transform: scale(1.2);
}
.igny8-size-info {
font-size: 14px;
color: #6c757d;
font-weight: 500;
}
.igny8-featured-image-row .igny8-size-info {
color: rgba(255, 255, 255, 0.9);
}
.form-help {
display: block;
margin-top: 5px;
font-size: 13px;
color: #6c757d;
font-style: italic;
}
</style>
<!-- Image Metabox Settings Section -->
<div class="igny8-settings-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>In-Article Image Meta Box</h3>
<p class="igny8-card-subtitle">Enable image metabox for specific post types</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-format-image igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<div class="igny8-settings">
<form method="post" action="">
<?php wp_nonce_field('igny8_image_metabox_settings', 'igny8_image_metabox_nonce'); ?>
<div class="igny8-form-group">
<label for="igny8_enable_image_metabox"><strong>Enable In-Article Image Meta Box For:</strong></label>
<div style="max-height: 200px; overflow-y: auto; border: 1px solid #ddd; padding: 15px; background: #f9f9f9; border-radius: 4px; margin-top: 10px;">
<?php
$all_post_types = get_post_types(['public' => true], 'objects');
$enabled_types = (array) get_option('igny8_enable_image_metabox', []);
foreach ($all_post_types as $pt => $obj) {
$checked = in_array($pt, $enabled_types) ? 'checked' : '';
echo '<label style="display: block; margin-bottom: 8px; padding: 5px;">';
echo '<input type="checkbox" name="igny8_enable_image_metabox[]" value="' . esc_attr($pt) . '" ' . $checked . ' style="margin-right: 8px;"> ';
echo '<strong>' . esc_html($obj->label) . '</strong> <em>(' . esc_html($pt) . ')</em>';
echo '</label>';
}
?>
</div>
<p class="description" style="margin-top: 10px;">Select which post types should display the In-Article Image metabox in the WordPress editor.</p>
</div>
<!-- Shortcode Usage Examples -->
<div class="igny8-form-group" style="margin-top: 25px;">
<label><strong>Shortcode Usage Examples:</strong></label>
<div style="background: #f8f9fa; border: 1px solid #e9ecef; padding: 15px; border-radius: 4px; margin-top: 10px;">
<p style="margin: 0 0 10px 0; font-weight: bold; color: #495057;">Use these shortcodes in your posts/pages to display the selected images:</p>
<div style="margin-bottom: 12px;">
<code style="background: #fff; padding: 4px 8px; border: 1px solid #ddd; border-radius: 3px; font-size: 13px;">[igny8-images]</code>
<span style="margin-left: 8px; color: #6c757d; font-size: 13px;">Display all images</span>
</div>
<div style="margin-bottom: 12px;">
<code style="background: #fff; padding: 4px 8px; border: 1px solid #ddd; border-radius: 3px; font-size: 13px;">[igny8-image id="desktop-1"]</code>
<span style="margin-left: 8px; color: #6c757d; font-size: 13px;">Display specific image by ID</span>
</div>
<div style="margin-bottom: 12px;">
<code style="background: #fff; padding: 4px 8px; border: 1px solid #ddd; border-radius: 3px; font-size: 13px;">[igny8-desktop-images]</code>
<span style="margin-left: 8px; color: #6c757d; font-size: 13px;">Display only desktop images</span>
</div>
<div style="margin-bottom: 12px;">
<code style="background: #fff; padding: 4px 8px; border: 1px solid #ddd; border-radius: 3px; font-size: 13px;">[igny8-mobile-images]</code>
<span style="margin-left: 8px; color: #6c757d; font-size: 13px;">Display only mobile images</span>
</div>
<div style="margin-bottom: 12px;">
<code style="background: #fff; padding: 4px 8px; border: 1px solid #ddd; border-radius: 3px; font-size: 13px;">[igny8-responsive-gallery]</code>
<span style="margin-left: 8px; color: #6c757d; font-size: 13px;">Responsive gallery (desktop on large screens, mobile on small screens)</span>
</div>
<div style="margin-bottom: 0;">
<code style="background: #fff; padding: 4px 8px; border: 1px solid #ddd; border-radius: 3px; font-size: 13px;">[igny8-image-count]</code>
<span style="margin-left: 8px; color: #6c757d; font-size: 13px;">Display count of images</span>
</div>
<p style="margin: 15px 0 0 0; font-size: 12px; color: #6c757d; font-style: italic;">
💡 <strong>Tip:</strong> After selecting images in the metabox, use these shortcodes in your post content to display them on the frontend.
</p>
</div>
</div>
<?php submit_button('Save Image Metabox Settings', 'primary', 'submit', true, ['style' => 'margin-top: 20px;']); ?>
</form>
</div>
</div>
<!-- Table Settings Section -->
<div class="igny8-settings-section">
<form method="post" action="options.php">
<?php settings_fields('igny8_table_settings'); ?>
<table class="form-table">
<tr>
<th scope="row">
<label for="igny8_records_per_page">Records Per Page</label>
</th>
<td>
<select name="igny8_records_per_page" id="igny8_records_per_page">
<option value="10" <?php selected(get_option('igny8_records_per_page', 20), 10); ?>>10</option>
<option value="20" <?php selected(get_option('igny8_records_per_page', 20), 20); ?>>20</option>
<option value="50" <?php selected(get_option('igny8_records_per_page', 20), 50); ?>>50</option>
<option value="100" <?php selected(get_option('igny8_records_per_page', 20), 100); ?>>100</option>
</select>
<p class="description">Default number of records to display per page across all tables in the plugin.</p>
</td>
</tr>
</table>
<?php submit_button('Save Settings'); ?>
</form>
</div>
</div>
<?php
break;
}
// Capture page content
$igny8_page_content = ob_get_clean();
// Include global layout
include plugin_dir_path(__FILE__) . '../../core/global-layout.php';
?>

View File

@@ -0,0 +1,267 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : import-export.php
* @location : /modules/settings/import-export.php
* @type : Admin Page
* @scope : Module Only
* @allowed : Data import/export, backup operations, data transfer
* @reusability : Single Use
* @notes : Import/export settings page for settings module
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Start output buffering
ob_start();
// Get current import/export settings
$import_export_settings = get_option('igny8_import_export_settings', [
'default_format' => 'csv',
'overwrite_existing' => false,
'include_metrics' => true,
'file_naming_pattern' => 'igny8_export_[date].csv'
]);
// Get recent import/export logs
$recent_logs = get_option('igny8_import_export_logs', []);
$recent_logs = array_slice($recent_logs, 0, 10); // Last 10 entries
// Script localization will be done in the JavaScript section below
?>
<div class="igny8-import-export-page">
<div class="igny8-container">
<!-- Import Data Section -->
<div class="igny8-card igny8-mb-20">
<div class="igny8-card-header igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Import Data</h3>
<div class="igny8-card-subtitle">Download CSV templates and import your data into the Planner module</div>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-upload igny8-dashboard-icon-sm"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<!-- Template Downloads -->
<div class="igny8-mb-20">
<h4>Download Templates</h4>
<div class="igny8-grid-2 igny8-gap-10">
<a href="<?php echo admin_url('admin-ajax.php?action=igny8_download_template&template_type=keywords&nonce=' . wp_create_nonce('igny8_import_export_nonce')); ?>" class="igny8-btn igny8-btn-outline">
<span class="dashicons dashicons-download"></span> Keywords Template
</a>
<a href="<?php echo admin_url('admin-ajax.php?action=igny8_download_template&template_type=clusters&nonce=' . wp_create_nonce('igny8_import_export_nonce')); ?>" class="igny8-btn igny8-btn-outline">
<span class="dashicons dashicons-download"></span> Clusters Template
</a>
<a href="<?php echo admin_url('admin-ajax.php?action=igny8_download_template&template_type=ideas&nonce=' . wp_create_nonce('igny8_import_export_nonce')); ?>" class="igny8-btn igny8-btn-outline">
<span class="dashicons dashicons-download"></span> Ideas Template
</a>
</div>
</div>
<!-- Import Form -->
<form id="igny8-import-form" enctype="multipart/form-data" action="<?php echo admin_url('admin-ajax.php'); ?>" method="post">
<input type="hidden" name="action" value="igny8_run_import">
<input type="hidden" name="nonce" value="<?php echo wp_create_nonce('igny8_import_export_nonce'); ?>">
<div class="igny8-form-group">
<label for="import-file">Select CSV File</label>
<input type="file" id="import-file" name="import_file" accept=".csv" required>
<p class="description">Upload a CSV file with your data. Use the templates above for proper format.</p>
</div>
<div class="igny8-form-group">
<label for="import-type">Import Type</label>
<select id="import-type" name="import_type" required>
<option value="">Select import type...</option>
<option value="keywords">Keywords</option>
<option value="clusters">Clusters</option>
<option value="ideas">Content Ideas</option>
</select>
</div>
<div class="igny8-form-actions">
<button type="submit" class="igny8-btn igny8-btn-success">
<span class="dashicons dashicons-upload"></span> Run Import
</button>
</div>
</form>
<!-- Import Results -->
<div id="import-results" class="igny8-mt-20" style="display: none;"></div>
</div>
</div>
<!-- Export Data Section -->
<div class="igny8-card igny8-mb-20">
<div class="igny8-card-header igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Export Data</h3>
<div class="igny8-card-subtitle">Export your data in various formats for backup and migration</div>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-download igny8-dashboard-icon-sm"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<form id="igny8-export-form" action="<?php echo admin_url('admin-ajax.php'); ?>" method="post">
<input type="hidden" name="action" value="igny8_run_export">
<input type="hidden" name="nonce" value="<?php echo wp_create_nonce('igny8_import_export_nonce'); ?>">
<div class="igny8-form-group">
<label for="export-type">Export Type</label>
<select id="export-type" name="export_type" required>
<option value="">Select export type...</option>
<option value="keywords">Keywords</option>
<option value="clusters">Clusters</option>
<option value="ideas">Content Ideas</option>
</select>
</div>
<div class="igny8-form-group">
<h4>Export Options</h4>
<div class="igny8-checkbox-group">
<label class="igny8-checkbox-label">
<input type="checkbox" id="include-metrics" name="include_metrics" <?php checked($import_export_settings['include_metrics'], true); ?>>
<span class="igny8-checkbox-text">Include Metrics</span>
</label>
<label class="igny8-checkbox-label">
<input type="checkbox" id="include-relationships" name="include_relationships">
<span class="igny8-checkbox-text">Include Relationships</span>
</label>
<label class="igny8-checkbox-label">
<input type="checkbox" id="include-timestamps" name="include_timestamps">
<span class="igny8-checkbox-text">Include Timestamps</span>
</label>
</div>
</div>
<div class="igny8-form-actions">
<button type="submit" class="igny8-btn igny8-btn-primary">
<span class="dashicons dashicons-download"></span> Export CSV
</button>
</div>
</form>
<!-- Export Results -->
<div id="export-results" class="igny8-mt-20" style="display: none;"></div>
</div>
</div>
<!-- Settings & Logging Section -->
<div class="igny8-card">
<div class="igny8-card-header igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Import / Export Preferences</h3>
<div class="igny8-card-subtitle">Configure import/export settings and view operation logs</div>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-generic igny8-dashboard-icon-sm"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<form id="igny8-settings-form" action="<?php echo admin_url('admin-ajax.php'); ?>" method="post">
<input type="hidden" name="action" value="igny8_save_import_export_settings">
<input type="hidden" name="nonce" value="<?php echo wp_create_nonce('igny8_import_export_nonce'); ?>">
<div class="igny8-grid-2 igny8-gap-20">
<div class="igny8-form-group">
<label for="default-format">Default Format</label>
<select id="default-format" name="default_format">
<option value="csv" <?php selected($import_export_settings['default_format'], 'csv'); ?>>CSV</option>
<option value="json" <?php selected($import_export_settings['default_format'], 'json'); ?>>JSON</option>
</select>
</div>
<div class="igny8-form-group">
<label for="file-naming">File Naming Pattern</label>
<input type="text" id="file-naming" name="file_naming_pattern" value="<?php echo esc_attr($import_export_settings['file_naming_pattern']); ?>" placeholder="igny8_export_[date].csv">
<p class="description">Use [date], [type], [time] placeholders</p>
</div>
</div>
<div class="igny8-form-group">
<h4>Default Options</h4>
<div class="igny8-checkbox-group">
<label class="igny8-checkbox-label">
<input type="checkbox" id="overwrite-existing" name="overwrite_existing" <?php checked($import_export_settings['overwrite_existing'], true); ?>>
<span class="igny8-checkbox-text">Overwrite Existing Records</span>
</label>
<label class="igny8-checkbox-label">
<input type="checkbox" id="include-metrics-default" name="include_metrics" <?php checked($import_export_settings['include_metrics'], true); ?>>
<span class="igny8-checkbox-text">Include Metrics in Export by Default</span>
</label>
</div>
</div>
<div class="igny8-form-actions">
<button type="submit" class="igny8-btn igny8-btn-success">Save Preferences</button>
</div>
</form>
<!-- Recent Logs -->
<?php if (!empty($recent_logs)): ?>
<div class="igny8-mt-30">
<h4>Recent Import/Export Activity</h4>
<div class="igny8-logs-container" style="max-height: 300px; overflow-y: auto; border: 1px solid #ddd; border-radius: 4px; padding: 10px; background: #f9f9f9;">
<?php foreach ($recent_logs as $log): ?>
<div class="igny8-log-entry" style="border-bottom: 1px solid #eee; padding: 8px 0; font-size: 12px;">
<div style="display: flex; justify-content: space-between; align-items: center;">
<span style="font-weight: bold; color: <?php echo $log['status'] === 'success' ? 'green' : 'red'; ?>;">
<?php echo $log['status'] === 'success' ? '✓' : '✗'; ?>
<?php echo esc_html($log['operation']); ?>
</span>
<span style="color: #666;"><?php echo esc_html($log['timestamp']); ?></span>
</div>
<div style="color: #666; margin-top: 2px;">
<?php echo esc_html($log['message']); ?>
<?php if (isset($log['details'])): ?>
<br><small><?php echo esc_html($log['details']); ?></small>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<script>
// Localize script variables
window.IGNY8_IMPORT_EXPORT = {
ajaxUrl: '<?php echo admin_url('admin-ajax.php'); ?>',
nonce: '<?php echo wp_create_nonce('igny8_import_export_nonce'); ?>',
settings: <?php echo json_encode($import_export_settings); ?>
};
document.addEventListener('DOMContentLoaded', function() {
console.log('Import/Export page loaded');
console.log('IGNY8_IMPORT_EXPORT:', window.IGNY8_IMPORT_EXPORT);
// Initialize Import/Export functionality
if (typeof window.initializeImportExport === 'function') {
console.log('Initializing Import/Export functionality');
window.initializeImportExport();
} else {
console.error('initializeImportExport function not found');
}
});
</script>

View File

@@ -0,0 +1,744 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : integration.php
* @location : /modules/settings/integration.php
* @type : Admin Page
* @scope : Module Only
* @allowed : API integration, connection management, external service settings
* @reusability : Single Use
* @notes : API integration settings page for settings module
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Start output buffering
ob_start();
?>
<!-- API Integration Section -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>API Integration</h3>
<p class="igny8-card-subtitle">Configure external API connections and integrations</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-site igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<!-- API Services Grid -->
<div class="igny8-grid-2 igny8-dashboard-section" style="display: flex; flex-direction: row;">
<!-- OpenAI API Card -->
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>OpenAI API</h3>
<p class="igny8-card-subtitle">AI-powered content generation and analysis</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-site igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<form method="post" action="options.php">
<?php settings_fields('igny8_api_settings'); ?>
<table class="form-table">
<tr>
<th scope="row">
<label for="igny8_api_key">OpenAI API Key</label>
</th>
<td>
<input type="password" name="igny8_api_key" id="igny8_api_key" value="<?php echo esc_attr(get_option('igny8_api_key', '')); ?>" class="regular-text" />
<p class="description">Your OpenAI API key for DALL-E 3 image generation and text AI features.</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="igny8_runware_api_key">Runware API Key</label>
</th>
<td>
<input type="password" name="igny8_runware_api_key" id="igny8_runware_api_key" value="<?php echo esc_attr(get_option('igny8_runware_api_key', '')); ?>" class="regular-text" />
<p class="description">Your Runware API key for high-quality image generation. <a href="https://runware.com" target="_blank">Get your API key here</a>.</p>
</td>
</tr>
<tr>
<th scope="row">
<label>AI Model</label>
</th>
<td>
<fieldset>
<label style="display: block; margin-bottom: 10px;">
<input type="radio" name="igny8_model" value="gpt-4.1" <?php checked(get_option('igny8_model', 'gpt-4.1'), 'gpt-4.1'); ?> />
<strong>GPT-4.1</strong> — $2.00 / $8.00 per 1M tokens<br>
<span style="color: #666; font-size: 12px;">Content creation, coding, analysis, high-quality content generation</span>
</label>
<label style="display: block; margin-bottom: 10px;">
<input type="radio" name="igny8_model" value="gpt-4o-mini" <?php checked(get_option('igny8_model', 'gpt-4.1'), 'gpt-4o-mini'); ?> />
<strong>GPT-4o mini</strong> — $0.15 / $0.60 per 1M tokens<br>
<span style="color: #666; font-size: 12px;">Bulk tasks, lightweight AI, cost-effective for high-volume operations</span>
</label>
<label style="display: block;">
<input type="radio" name="igny8_model" value="gpt-4o" <?php checked(get_option('igny8_model', 'gpt-4.1'), 'gpt-4o'); ?> />
<strong>GPT-4o</strong> — $2.50 / $10.00 per 1M tokens<br>
<span style="color: #666; font-size: 12px;">Advanced AI for general and multimodal tasks, faster than GPT-4.1</span>
</label>
</fieldset>
<p class="description">Select the AI model to use for content generation. Pricing shown per 1M tokens.</p>
</td>
</tr>
<tr>
<th scope="row">Image Generation Service</th>
<td>
<fieldset>
<label style="display: block; margin-bottom: 10px;">
<input type="radio" name="igny8_image_service" value="openai" <?php checked(get_option('igny8_image_service', 'openai'), 'openai'); ?> />
<strong>OpenAI</strong> — Multiple models available<br>
<span style="color: #666; font-size: 12px;">High-quality image generation with OpenAI's models</span>
</label>
<label style="display: block;">
<input type="radio" name="igny8_image_service" value="runware" <?php checked(get_option('igny8_image_service', 'openai'), 'runware'); ?> />
<strong>Runware</strong> — $0.036 per image<br>
<span style="color: #666; font-size: 12px;">High-quality AI image generation with Runware's models</span>
</label>
</fieldset>
<p class="description">Select the image generation service to use. Each service requires its own API key.</p>
</td>
</tr>
<tr id="igny8-openai-models-row" style="display: none;">
<th scope="row">OpenAI Image Model</th>
<td>
<fieldset>
<label style="display: block; margin-bottom: 10px;">
<input type="radio" name="igny8_image_model" value="dall-e-3" <?php checked(get_option('igny8_image_model', 'dall-e-3'), 'dall-e-3'); ?> />
<strong>DALL·E 3</strong> — $0.040 per image<br>
<span style="color: #666; font-size: 12px;">High-quality image generation with advanced AI capabilities</span>
</label>
<label style="display: block; margin-bottom: 10px;">
<input type="radio" name="igny8_image_model" value="dall-e-2" <?php checked(get_option('igny8_image_model', 'dall-e-3'), 'dall-e-2'); ?> />
<strong>DALL·E 2</strong> — $0.020 per image<br>
<span style="color: #666; font-size: 12px;">Cost-effective image generation with good quality</span>
</label>
<label style="display: block; margin-bottom: 10px;">
<input type="radio" name="igny8_image_model" value="gpt-image-1" <?php checked(get_option('igny8_image_model', 'dall-e-3'), 'gpt-image-1'); ?> />
<strong>GPT Image 1 (Full)</strong> — $0.042 per image<br>
<span style="color: #666; font-size: 12px;">Full-featured image generation with comprehensive capabilities</span>
</label>
<label style="display: block;">
<input type="radio" name="igny8_image_model" value="gpt-image-1-mini" <?php checked(get_option('igny8_image_model', 'dall-e-3'), 'gpt-image-1-mini'); ?> />
<strong>GPT Image 1 Mini</strong> — $0.011 per image<br>
<span style="color: #666; font-size: 12px;">Lightweight, cost-effective image generation for bulk operations</span>
</label>
</fieldset>
<p class="description">Select the OpenAI image model to use. Pricing shown per image.</p>
</td>
</tr>
<tr id="igny8-runware-models-row" style="display: none;">
<th scope="row">Runware Image Model</th>
<td>
<fieldset>
<label style="display: block;">
<input type="radio" name="igny8_runware_model" value="runware:97@1" <?php checked(get_option('igny8_runware_model', 'runware:97@1'), 'runware:97@1'); ?> />
<strong>HiDream-I1 Full</strong> — $0.036 per image<br>
<span style="color: #666; font-size: 12px;">High-quality AI image generation with Runware's HiDream model</span>
</label>
</fieldset>
<p class="description">Select the Runware image model to use. Pricing shown per image.</p>
</td>
</tr>
<tr>
<th scope="row">API Validation</th>
<td>
<div id="igny8-openai-validation" style="display: none;">
<button type="button" id="igny8-test-api" class="button">Test OpenAI Connection</button>
<button type="button" id="igny8-test-response" class="button">Test OpenAI Response (Ping)</button>
</div>
<div id="igny8-runware-validation" style="display: none;">
<button type="button" id="igny8-test-runware-btn" class="button">Test Runware Connection</button>
<div id="igny8-runware-test-result" style="margin-top: 10px;"></div>
</div>
<div id="igny8-api-test-result" style="margin-top: 10px;"></div>
</td>
</tr>
</table>
<?php submit_button('Save API Settings'); ?>
</form>
</div>
</div>
<!-- Google Search Console API Card -->
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Google Search Console API</h3>
<p class="igny8-card-subtitle">Search performance and ranking data integration</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-chart-bar igny8-dashboard-icon-lg igny8-dashboard-icon-orange"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<div style="background: rgba(255, 193, 7, 0.1); border: 1px solid var(--amber); border-radius: var(--radius); padding: 16px; margin-bottom: 20px;">
<div style="display: flex; align-items: center; gap: 12px;">
<span style="font-size: 24px;">⚠️</span>
<div>
<strong style="color: var(--amber-dark);">Coming Soon</strong><br>
<span style="color: var(--text-dim); font-size: 14px;">
Google Search Console API integration is currently in development.
This will provide search performance data and ranking insights.
</span>
</div>
</div>
</div>
<div style="color: var(--text-dim); margin-bottom: 16px;">
<strong>Planned Features:</strong>
<ul style="margin: 8px 0; padding-left: 20px;">
<li>Search performance metrics</li>
<li>Keyword ranking data</li>
<li>Click-through rate analysis</li>
<li>Search appearance insights</li>
</ul>
</div>
<div style="text-align: center; padding: 20px; background: var(--bg-light); border-radius: var(--radius);">
<span style="color: var(--text-dim); font-size: 14px;">
Integration will be available in a future update
</span>
</div>
</div>
</div>
</div>
</div>
<!-- API Request Logs Section -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>API Request Logs</h3>
<p class="igny8-card-subtitle">Monitor API usage and performance metrics</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-chart-line igny8-dashboard-icon-lg igny8-dashboard-icon-purple"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div id="igny8-api-logs-container">
<div class="tablenav top">
<div class="alignleft actions">
<button type="button" id="igny8-refresh-logs" class="button">Refresh Logs</button>
<button type="button" id="igny8-clear-logs" class="button">Clear Logs</button>
</div>
<div class="alignright actions">
<span class="displaying-num">
<span id="igny8-logs-count">0</span> API calls
</span>
</div>
</div>
<table class="widefat fixed striped" id="igny8-api-logs">
<thead>
<tr>
<th>Timestamp</th>
<th>Status</th>
<th>Model</th>
<th>Tokens (In/Out)</th>
<th>Cost</th>
<th>API ID</th>
</tr>
</thead>
<tbody>
<?php
global $wpdb;
$logs = $wpdb->get_results("
SELECT * FROM {$wpdb->prefix}igny8_logs
WHERE source = 'openai_api'
ORDER BY created_at DESC
LIMIT 20
");
if ($logs) {
foreach ($logs as $log) {
$context = json_decode($log->context, true);
$status_class = $log->status === 'success' ? 'success' : 'error';
$status_icon = $log->status === 'success' ? '✅' : '❌';
// Debug logging for cost display
error_log("Igny8 Display Debug: Log ID=" . $log->id . ", total_cost=" . ($context['total_cost'] ?? 'null') . ", formatted=" . igny8_format_cost($context['total_cost'] ?? 0));
echo "<tr>
<td>" . esc_html($log->created_at) . "</td>
<td><span class='igny8-status {$status_class}'>{$status_icon} " . esc_html($log->status) . "</span></td>
<td>" . esc_html($context['model'] ?? 'Unknown') . "</td>
<td>" . intval($context['input_tokens'] ?? 0) . " / " . intval($context['output_tokens'] ?? 0) . "</td>
<td>" . igny8_format_cost($context['total_cost'] ?? 0) . "</td>
<td>" . esc_html($log->api_id ? substr($log->api_id, 0, 12) . '...' : 'N/A') . "</td>
</tr>";
}
} else {
echo '<tr><td colspan="6">No API logs found.</td></tr>';
}
?>
</tbody>
</table>
</div>
</div>
</div>
<!-- Image Request Logs Section -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Image Request Logs</h3>
<p class="igny8-card-subtitle">Monitor AI image generation requests and performance</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-format-image igny8-dashboard-icon-lg igny8-dashboard-icon-teal"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div id="igny8-image-logs-container">
<div class="tablenav top">
<div class="alignleft actions">
<button type="button" id="igny8-refresh-image-logs" class="button">Refresh Logs</button>
<button type="button" id="igny8-clear-image-logs" class="button">Clear Logs</button>
</div>
<div class="alignright actions">
<span class="displaying-num">
<span id="igny8-image-logs-count">0</span> image requests
</span>
</div>
</div>
<table class="widefat fixed striped" id="igny8-image-logs">
<thead>
<tr>
<th>Timestamp</th>
<th>Status</th>
<th>Model</th>
<th>Prompt Length</th>
<th>Cost</th>
<th>Image Size</th>
<th>API ID</th>
</tr>
</thead>
<tbody>
<?php
$image_logs = $wpdb->get_results("
SELECT * FROM {$wpdb->prefix}igny8_logs
WHERE source = 'openai_image'
ORDER BY created_at DESC
LIMIT 20
");
if ($image_logs) {
foreach ($image_logs as $log) {
$context = json_decode($log->context, true);
$status_class = $log->status === 'success' ? 'success' : 'error';
$status_icon = $log->status === 'success' ? '✅' : '❌';
echo "<tr>
<td>" . esc_html($log->created_at) . "</td>
<td><span class='igny8-status {$status_class}'>{$status_icon} " . esc_html($log->status) . "</span></td>
<td>" . esc_html($context['model'] ?? 'dall-e-3') . "</td>
<td>" . intval($context['prompt_length'] ?? 0) . " chars</td>
<td>" . igny8_format_cost($context['total_cost'] ?? 0) . "</td>
<td>" . esc_html($context['image_size'] ?? '1024x1024') . "</td>
<td>" . esc_html($log->api_id ? substr($log->api_id, 0, 12) . '...' : 'N/A') . "</td>
</tr>";
}
} else {
echo '<tr><td colspan="7">No image request logs found.</td></tr>';
}
?>
</tbody>
</table>
</div>
</div>
</div>
<!-- Content Engine Settings Section -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Content Engine Settings</h3>
<p class="igny8-card-subtitle">Personalization and content generation configuration</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-generic igny8-dashboard-icon-lg igny8-dashboard-icon-amber"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div style="background: rgba(59, 130, 246, 0.1); border: 1px solid var(--blue); border-radius: var(--radius); padding: 16px; margin-bottom: 20px;">
<div style="display: flex; align-items: center; gap: 12px;">
<span style="font-size: 24px;"></span>
<div>
<strong style="color: var(--blue-dark);">Settings Moved</strong><br>
<span style="color: var(--text-dim); font-size: 14px;">
Content Engine settings have been moved to the Personalize module for better organization.
</span>
</div>
</div>
</div>
<p><strong>Note:</strong> Content Engine settings have been moved to the <a href="<?php echo admin_url('admin.php?page=igny8-personalize&sm=settings'); ?>">Personalize module</a> for better organization.</p>
<p>Please configure personalization settings in the Personalize → Settings section.</p>
</div>
</div>
</div>
<!-- Third-party Integrations Section -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Third-party Integrations</h3>
<p class="igny8-card-subtitle">Additional SEO tools and data sources</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-tools igny8-dashboard-icon-lg igny8-dashboard-icon-red"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div style="background: rgba(255, 193, 7, 0.1); border: 1px solid var(--amber); border-radius: var(--radius); padding: 16px; margin-bottom: 20px;">
<div style="display: flex; align-items: center; gap: 12px;">
<span style="font-size: 24px;">⚠️</span>
<div>
<strong style="color: var(--amber-dark);">Coming Soon</strong><br>
<span style="color: var(--text-dim); font-size: 14px;">
Additional third-party integrations are currently in development.
</span>
</div>
</div>
</div>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
<div style="padding: 16px; background: var(--bg-light); border-radius: var(--radius);">
<h4 style="margin: 0 0 8px 0; color: var(--text-primary);">Ahrefs API</h4>
<p style="margin: 0; color: var(--text-dim); font-size: 14px;">Integration with Ahrefs for keyword and backlink data.</p>
</div>
<div style="padding: 16px; background: var(--bg-light); border-radius: var(--radius);">
<h4 style="margin: 0 0 8px 0; color: var(--text-primary);">SEMrush API</h4>
<p style="margin: 0; color: var(--text-dim); font-size: 14px;">Integration with SEMrush for competitive analysis.</p>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Handle conditional display of image model options and API validation
const imageServiceRadios = document.querySelectorAll('input[name="igny8_image_service"]');
const openaiModelsRow = document.getElementById('igny8-openai-models-row');
const runwareModelsRow = document.getElementById('igny8-runware-models-row');
const openaiValidation = document.getElementById('igny8-openai-validation');
const runwareValidation = document.getElementById('igny8-runware-validation');
function toggleServiceOptions() {
const selectedService = document.querySelector('input[name="igny8_image_service"]:checked');
if (selectedService) {
if (selectedService.value === 'openai') {
openaiModelsRow.style.display = 'table-row';
runwareModelsRow.style.display = 'none';
openaiValidation.style.display = 'block';
runwareValidation.style.display = 'none';
} else if (selectedService.value === 'runware') {
openaiModelsRow.style.display = 'none';
runwareModelsRow.style.display = 'table-row';
openaiValidation.style.display = 'none';
runwareValidation.style.display = 'block';
}
}
}
// Add event listeners to image service radio buttons
imageServiceRadios.forEach(radio => {
radio.addEventListener('change', toggleServiceOptions);
});
// Initialize display on page load
toggleServiceOptions();
const testApiButton = document.getElementById('igny8-test-api');
const testResponseButton = document.getElementById('igny8-test-response');
const refreshLogsButton = document.getElementById('igny8-refresh-logs');
const clearLogsButton = document.getElementById('igny8-clear-logs');
const refreshImageLogsButton = document.getElementById('igny8-refresh-image-logs');
const clearImageLogsButton = document.getElementById('igny8-clear-image-logs');
const resultDiv = document.getElementById('igny8-api-test-result');
// Test API Connection (without I/O messages)
if (testApiButton) {
testApiButton.addEventListener('click', function() {
testApiConnection(false);
});
}
// Test API Response (with "test ping")
if (testResponseButton) {
testResponseButton.addEventListener('click', function() {
testApiConnection(true);
});
}
// Refresh Logs
if (refreshLogsButton) {
refreshLogsButton.addEventListener('click', function() {
loadApiLogs();
});
}
// Clear Logs
if (clearLogsButton) {
clearLogsButton.addEventListener('click', function() {
clearApiLogs();
});
}
// Refresh Image Logs
if (refreshImageLogsButton) {
refreshImageLogsButton.addEventListener('click', function() {
loadImageLogs();
});
}
// Clear Image Logs
if (clearImageLogsButton) {
clearImageLogsButton.addEventListener('click', function() {
clearImageLogs();
});
}
function testApiConnection(withResponse = false) {
resultDiv.innerHTML = '<span style="color: blue;">Testing API connection...</span>';
const formData = new FormData();
formData.append('action', 'igny8_test_api');
formData.append('nonce', '<?php echo wp_create_nonce('igny8_ajax_nonce'); ?>');
formData.append('with_response', withResponse ? '1' : '0');
fetch('<?php echo admin_url('admin-ajax.php'); ?>', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
const modelUsed = data.data?.model_used || 'Unknown';
const response = data.data?.response || 'No response';
const cost = data.data?.cost || 'N/A';
const tokens = data.data?.tokens || 'N/A';
const tokensSent = data.data?.tokens_sent || 'N/A';
const tokensUsed = data.data?.tokens_used || 'N/A';
const totalTokens = data.data?.total_tokens || 'N/A';
resultDiv.innerHTML = `
<div style="color: green; margin-bottom: 10px;">
✓ API connection successful!
</div>
<div style="background: #f0f8ff; padding: 10px; border-radius: 4px; border-left: 4px solid #0073aa;">
<strong>Model Used:</strong> ${modelUsed}<br>
${withResponse ? `<strong>Expected:</strong> "OK! Ping Received"<br><strong>Actual Response:</strong> "${response}"<br>` : ''}
<strong>Token Limit Sent:</strong> ${tokensSent} (from your settings)<br>
<strong>Tokens Used:</strong> ${tokensUsed} (input/output)<br>
<strong>Total Tokens:</strong> ${totalTokens}<br>
<strong>Cost:</strong> ${cost}
</div>
`;
// Refresh logs to show new entry
setTimeout(() => loadApiLogs(), 1000);
} else {
const errorMessage = data.data?.message || 'Unknown error';
const errorDetails = data.data?.details || '';
resultDiv.innerHTML = `
<div style="color: red; margin-bottom: 10px;">
✗ API connection failed: ${errorMessage}
</div>
${errorDetails ? `<div style="background: #fff2f2; padding: 10px; border-radius: 4px; border-left: 4px solid #dc3232; font-size: 12px; color: #666;">
<strong>Details:</strong> ${errorDetails}
</div>` : ''}
`;
}
})
.catch(error => {
resultDiv.innerHTML = '<span style="color: red;">✗ API connection failed: ' + error.message + '</span>';
});
}
function loadApiLogs() {
fetch('<?php echo admin_url('admin-ajax.php'); ?>', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
action: 'igny8_get_api_logs',
nonce: '<?php echo wp_create_nonce('igny8_ajax_nonce'); ?>'
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
const logsTable = document.querySelector('#igny8-api-logs tbody');
const logsCount = document.getElementById('igny8-logs-count');
if (logsTable) {
logsTable.innerHTML = data.data.html;
}
if (logsCount) {
logsCount.textContent = data.data.total;
}
}
})
.catch(error => {
console.error('Error loading logs:', error);
});
}
function clearApiLogs() {
if (!confirm('Are you sure you want to clear all API logs? This action cannot be undone.')) {
return;
}
fetch('<?php echo admin_url('admin-ajax.php'); ?>', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
action: 'igny8_clear_api_logs',
nonce: '<?php echo wp_create_nonce('igny8_ajax_nonce'); ?>'
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
loadApiLogs();
alert('API logs cleared successfully!');
} else {
alert('Error clearing logs: ' + (data.data?.message || 'Unknown error'));
}
})
.catch(error => {
console.error('Error clearing logs:', error);
alert('Error clearing logs: ' + error.message);
});
}
function loadImageLogs() {
fetch('<?php echo admin_url('admin-ajax.php'); ?>', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
action: 'igny8_get_image_logs',
nonce: '<?php echo wp_create_nonce('igny8_ajax_nonce'); ?>'
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
const logsTable = document.querySelector('#igny8-image-logs tbody');
const logsCount = document.getElementById('igny8-image-logs-count');
if (logsTable) {
logsTable.innerHTML = data.data.html;
}
if (logsCount) {
logsCount.textContent = data.data.total;
}
}
})
.catch(error => {
console.error('Error loading image logs:', error);
});
}
function clearImageLogs() {
if (!confirm('Are you sure you want to clear all image request logs? This action cannot be undone.')) {
return;
}
fetch('<?php echo admin_url('admin-ajax.php'); ?>', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
action: 'igny8_clear_image_logs',
nonce: '<?php echo wp_create_nonce('igny8_ajax_nonce'); ?>'
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
loadImageLogs();
alert('Image request logs cleared successfully!');
} else {
alert('Error clearing image logs: ' + (data.data?.message || 'Unknown error'));
}
})
.catch(error => {
console.error('Error clearing image logs:', error);
alert('Error clearing image logs: ' + error.message);
});
}
// Load logs on page load
loadApiLogs();
loadImageLogs();
});
</script>
<style>
.igny8-status.success {
color: #46b450;
font-weight: bold;
}
.igny8-status.error {
color: #dc3232;
font-weight: bold;
}
#igny8-api-logs {
margin-top: 15px;
}
#igny8-api-logs th {
background: #f1f1f1;
font-weight: bold;
}
#igny8-api-logs td {
padding: 8px 10px;
}
</style>

View File

@@ -0,0 +1,297 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : schedules.php
* @location : /modules/settings/schedules.php
* @type : Admin Page
* @scope : Module Only
* @allowed : Automation scheduling, cron configuration, timing settings
* @reusability : Single Use
* @notes : Automation schedules configuration page for settings module
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Start output buffering
ob_start();
// Load master dispatcher functions
if (!function_exists('igny8_get_defined_cron_jobs')) {
include_once plugin_dir_path(__FILE__) . '../../core/cron/igny8-cron-master-dispatcher.php';
}
// Handle form submission
if (isset($_POST['igny8_save_cron_settings']) && wp_verify_nonce($_POST['igny8_cron_nonce'], 'igny8_cron_settings')) {
$cron_jobs = $_POST['cron_jobs'] ?? [];
$cron_settings = get_option('igny8_cron_settings', []);
$cron_limits = get_option('igny8_cron_limits', []);
// Update settings for each job
foreach ($cron_jobs as $job_name => $job_data) {
$cron_settings[$job_name] = [
'enabled' => isset($job_data['enabled']),
'last_run' => $cron_settings[$job_name]['last_run'] ?? 0
];
// Update limits
if (isset($job_data['limit'])) {
$cron_limits[$job_name] = intval($job_data['limit']);
}
}
update_option('igny8_cron_settings', $cron_settings);
update_option('igny8_cron_limits', $cron_limits);
echo '<div class="notice notice-success"><p>Settings saved successfully!</p></div>';
}
// Get current data
$defined_jobs = igny8_get_defined_cron_jobs();
$cron_settings = get_option('igny8_cron_settings', []);
$cron_limits = get_option('igny8_cron_limits', []);
$last_execution = get_option('igny8_cron_last_execution', []);
// Initialize defaults if needed
if (empty($cron_settings)) {
$cron_settings = igny8_get_default_cron_settings();
update_option('igny8_cron_settings', $cron_settings);
}
if (empty($cron_limits)) {
$cron_limits = igny8_get_default_cron_limits();
update_option('igny8_cron_limits', $cron_limits);
}
// Get health status for all jobs
$health_status = [];
foreach ($defined_jobs as $job_name => $job_config) {
$health_status[$job_name] = igny8_get_job_health_status($job_name);
}
?>
<div class="wrap igny8-admin-page">
<!-- Smart Automation Jobs Table -->
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Smart Automation Jobs</h3>
<p class="igny8-card-subtitle">Configure and manage all automation jobs</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-tools igny8-dashboard-icon-lg igny8-dashboard-icon-purple"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<form method="post" action="">
<?php wp_nonce_field('igny8_cron_settings', 'igny8_cron_nonce'); ?>
<table class="igny8-table" data-cron-key="<?php echo esc_attr(get_option('igny8_secure_cron_key')); ?>">
<thead>
<tr>
<th>Job Name</th>
<th>Module</th>
<th>Enable</th>
<th>Max Items</th>
<th>Last Run</th>
<th>Execution Time</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($defined_jobs as $job_name => $job_config):
// Skip crons if their respective modules are disabled
$analytics_crons = ['igny8_process_ai_queue_cron', 'igny8_auto_recalc_cron', 'igny8_health_check_cron'];
$writer_crons = ['igny8_auto_generate_content_cron', 'igny8_auto_generate_images_cron', 'igny8_auto_publish_drafts_cron'];
$optimizer_crons = ['igny8_auto_optimizer_cron'];
if (in_array($job_name, $analytics_crons) && !igny8_is_module_enabled('analytics')) {
continue;
}
if (in_array($job_name, $writer_crons) && !igny8_is_module_enabled('writer')) {
continue;
}
if (in_array($job_name, $optimizer_crons) && !igny8_is_module_enabled('optimizer')) {
continue;
}
$job_settings = $cron_settings[$job_name] ?? [];
$job_status = igny8_get_cron_job_status($job_name);
$job_health = $health_status[$job_name];
?>
<tr>
<td>
<strong><?php echo esc_html($job_config['description']); ?></strong>
</td>
<td>
<span class="igny8-module-badge igny8-module-<?php echo esc_attr($job_config['module']); ?>">
<?php echo esc_html(ucfirst($job_config['module'])); ?>
</span>
</td>
<td>
<label class="igny8-toggle">
<input type="checkbox" name="cron_jobs[<?php echo esc_attr($job_name); ?>][enabled]"
<?php checked($job_settings['enabled'] ?? false); ?>>
<span class="igny8-toggle-slider"></span>
</label>
</td>
<td>
<input type="number" name="cron_jobs[<?php echo esc_attr($job_name); ?>][limit]"
value="<?php echo esc_attr($cron_limits[$job_name] ?? 10); ?>"
min="1" max="100" style="width: 60px;">
<?php
// Add descriptive text based on job type
$item_text = '';
switch($job_name) {
case 'igny8_auto_cluster_cron':
$item_text = 'keywords';
break;
case 'igny8_auto_generate_ideas_cron':
$item_text = 'clusters';
break;
case 'igny8_auto_queue_cron':
$item_text = 'ideas';
break;
case 'igny8_auto_generate_content_cron':
$item_text = 'tasks';
break;
case 'igny8_auto_generate_images_cron':
$item_text = 'posts';
break;
case 'igny8_auto_publish_drafts_cron':
$item_text = 'drafts';
break;
case 'igny8_process_ai_queue_cron':
$item_text = 'tasks';
break;
case 'igny8_auto_recalc_cron':
$item_text = 'items';
break;
default:
$item_text = 'items';
}
echo ' <small style="color: #666; font-size: 12px;">' . $item_text . '</small>';
?>
</td>
<td>
<?php echo esc_html($job_health['last_run']); ?>
<?php if (!empty($job_health['result_details'])): ?>
<br><small style="color: <?php echo ($job_health['last_success'] === true) ? '#28a745' : '#dc3545'; ?>;">
<?php echo esc_html($job_health['result_details']); ?>
<?php if ($job_health['last_success'] === true): ?>
<?php else: ?>
<?php endif; ?>
</small>
<?php endif; ?>
</td>
<td>
<?php
$execution_time = $job_health['execution_time'] ?? 0;
echo $execution_time > 0 ? number_format($execution_time, 2) . 's' : 'N/A';
?>
<?php if (!empty($job_health['execution_method'])): ?>
<br><small style="color: #666;">
via <?php echo esc_html($job_health['execution_method']); ?>
</small>
<?php endif; ?>
</td>
<td class="igny8-align-center igny8-actions">
<button class="igny8-icon-only igny8-icon-external" data-action="openInNewWindow" data-hook="<?php echo esc_attr($job_name); ?>" title="Open in New Window">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
<polyline points="15,3 21,3 21,9"></polyline>
<line x1="10" y1="14" x2="21" y2="3"></line>
</svg>
</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<p class="submit">
<input type="submit" name="igny8_save_cron_settings" class="button-primary" value="Save All Settings">
</p>
</form>
</div>
</div>
<!-- Master Scheduler Status -->
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Master Scheduler Configuration</h3>
<p class="igny8-card-subtitle">Single cron job manages all automation</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-clock igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<p><strong>Single cPanel Configuration Required:</strong></p>
<code>*/5 * * * * curl -s "https://<?php echo $_SERVER['HTTP_HOST']; ?>/wp-load.php?import_key=<?php echo get_option('igny8_secure_cron_key'); ?>&import_id=igny8_cron&action=master_scheduler" > /dev/null 2>&1</code>
<p><em>This single cron job will intelligently manage all automation based on your settings below.</em></p>
</div>
</div>
<!-- System Overview -->
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>System Overview</h3>
<p class="igny8-card-subtitle">Current automation system status</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-dashboard igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<?php
$total_jobs = count($defined_jobs);
$enabled_jobs = count(array_filter($cron_settings, function($settings) { return $settings['enabled'] ?? false; }));
$scheduled_jobs = count(array_filter($health_status, function($status) { return $status['enabled']; }));
$failed_jobs = count(array_filter($health_status, function($status) { return $status['last_success'] === false; }));
?>
<div class="igny8-stats-grid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 20px; margin: 20px 0;">
<div class="igny8-metric-item" style="background: var(--panel); border: 1px solid var(--stroke); border-radius: var(--radius); padding: 15px; text-align: center;">
<h4 style="margin: 0 0 10px 0; color: var(--blue-dark);">Total Jobs</h4>
<p style="margin: 0; font-size: 24px; font-weight: 700; color: var(--text);"><?php echo esc_html($total_jobs); ?></p>
</div>
<div class="igny8-metric-item" style="background: var(--panel); border: 1px solid var(--stroke); border-radius: var(--radius); padding: 15px; text-align: center;">
<h4 style="margin: 0 0 10px 0; color: var(--green-dark);">Enabled Jobs</h4>
<p style="margin: 0; font-size: 24px; font-weight: 700; color: var(--text);"><?php echo esc_html($enabled_jobs); ?></p>
</div>
<div class="igny8-metric-item" style="background: var(--panel); border: 1px solid var(--stroke); border-radius: var(--radius); padding: 15px; text-align: center;">
<h4 style="margin: 0 0 10px 0; color: var(--amber-dark);">Scheduled Jobs</h4>
<p style="margin: 0; font-size: 24px; font-weight: 700; color: var(--text);"><?php echo esc_html($scheduled_jobs); ?></p>
</div>
<div class="igny8-metric-item" style="background: var(--panel); border: 1px solid var(--stroke); border-radius: var(--radius); padding: 15px; text-align: center;">
<h4 style="margin: 0 0 10px 0; color: var(--red-dark);">Failed Jobs</h4>
<p style="margin: 0; font-size: 24px; font-weight: 700; color: var(--text);"><?php echo esc_html($failed_jobs); ?></p>
</div>
</div>
</div>
</div>
</div>
<?php
// Capture page content
$igny8_page_content = ob_get_clean();
// Include global layout
include plugin_dir_path(__FILE__) . '../../core/global-layout.php';
?>

View File

@@ -0,0 +1,353 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : status.php
* @location : /modules/settings/status.php
* @type : Admin Page
* @scope : Module Only
* @allowed : System status monitoring, health checks, diagnostics
* @reusability : Single Use
* @notes : System status monitoring page for settings module
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
?>
<!-- System Status Section -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>System Status</h3>
<p class="igny8-card-subtitle">Monitor system health and component status</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-site igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<!-- Debug Monitoring & System Layers Section -->
<div class="igny8-grid-2 igny8-dashboard-section" style="
display: flex;
flex-direction: row;
">
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Debug Monitoring</h3>
<p class="igny8-card-subtitle">Real-time debug monitoring controls and status</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-tools igny8-dashboard-icon-lg igny8-dashboard-icon-orange"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<p style="color: var(--text-dim); margin-bottom: 16px;">Enable or disable submodule debug monitoring for Planner, Writer, Linker, Optimizer and Personalize</p>
<!-- Real-time Monitoring Controls -->
<div style="display: flex; align-items: center; gap: 12px; margin-bottom: 20px;">
<div style="font-size: 13px; color: var(--text-dim); text-align: right;">
Real-time Monitoring
</div>
<div style="display: flex; align-items: center; gap: 8px;">
<div style="display: flex; align-items: center; gap: 4px;">
<input type="radio" id="debug-enabled" name="debug_monitoring" value="1" <?php echo get_option('igny8_debug_enabled', false) ? 'checked' : ''; ?>>
<label for="debug-enabled" style="font-size: 12px; color: var(--text-dim);">On</label>
</div>
<div style="display: flex; align-items: center; gap: 4px;">
<input type="radio" id="debug-disabled" name="debug_monitoring" value="0" <?php echo !get_option('igny8_debug_enabled', false) ? 'checked' : ''; ?>>
<label for="debug-disabled" style="font-size: 12px; color: var(--text-dim);">Off</label>
</div>
<button type="button" id="save-debug-setting" class="igny8-btn igny8-btn-primary">
<span class="dashicons dashicons-yes-alt" style="font-size: 14px;"></span>
Save
</button>
</div>
</div>
<!-- Current Status Display -->
<?php
$debug_enabled = get_option('igny8_debug_enabled', false);
$status_text = $debug_enabled ? 'Enabled' : 'Disabled';
$status_color = $debug_enabled ? 'var(--green-dark)' : 'var(--red-dark)';
$status_icon = $debug_enabled ? '✅' : '❌';
// Set background and border colors based on status
if ($debug_enabled) {
$status_bg = 'rgba(16,185,129,0.1)';
$status_border = 'var(--green)';
} else {
$status_bg = 'rgba(239,68,68,0.1)';
$status_border = 'var(--red)';
}
?>
<div style="background: <?php echo $status_bg; ?>; border: 1px solid <?php echo $status_border; ?>; border-radius: var(--radius); padding: 16px; margin-top: 16px;">
<div style="display: flex; align-items: center; gap: 12px;">
<span style="font-size: 24px;"><?php echo $status_icon; ?></span>
<div>
<strong style="color: <?php echo $status_color; ?>;">Debug Monitoring: <?php echo $status_text; ?></strong><br>
<span style="color: var(--text-dim); font-size: 14px;">
<?php if ($debug_enabled): ?>
Real-time debug monitoring is active for all submodules. Debug cards and status indicators are visible on submodule pages.
<?php else: ?>
Debug monitoring is disabled. Debug cards and status indicators are hidden on submodule pages.
<?php endif; ?>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>System Layers</h3>
<p class="igny8-card-subtitle">System layer health and operational metrics</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-chart-bar igny8-dashboard-icon-lg igny8-dashboard-icon-teal"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<!-- Metrics Row -->
<div class="igny8-metrics-row">
<div class="igny8-metric">
<div class="igny8-metric-value">100%</div>
<div class="igny8-metric-label">Layer Health</div>
</div>
<div class="igny8-metric">
<div class="igny8-metric-value" style="color: var(--green);">6</div>
<div class="igny8-metric-label">Operational</div>
</div>
<div class="igny8-metric">
<div class="igny8-metric-value" style="color: var(--red-dark);">0</div>
<div class="igny8-metric-label">Failed</div>
</div>
<div class="igny8-metric">
<div class="igny8-metric-value">6</div>
<div class="igny8-metric-label">Total Layers</div>
</div>
</div>
<!-- Layer Status Circles -->
<div class="igny8-flex" style="justify-content: center; gap: 16px; margin: 20px 0;">
<?php
$layer_labels = [
'database_system' => 'DB',
'configuration_system' => 'CFG',
'rendering_system' => 'RND',
'javascript_ajax' => 'JS',
'component_functionality' => 'CMP',
'data_flow' => 'FLW'
];
foreach ($layer_labels as $layer_key => $label):
$layer_status = true; // Simplified - always true for basic layer summary
$status_class = $layer_status ? 'bg-success' : 'bg-error';
?>
<div class="bg-circle <?php echo $status_class; ?>" title="<?php echo ucfirst(str_replace('_', ' ', $layer_key)); ?>: <?php echo $layer_status ? 'Operational' : 'Failed'; ?>">
<?php echo $label; ?>
</div>
<?php endforeach; ?>
</div>
<div style="background: rgba(16,185,129,0.1); border: 1px solid var(--green); border-radius: var(--radius); padding: 16px; margin-top: 16px;">
<div style="display: flex; align-items: center; gap: 12px;">
<span style="font-size: 24px;">✅</span>
<div>
<strong style="color: var(--green-dark);">All Layers Operational</strong><br>
<span style="color: var(--text-dim); font-size: 14px;">
All 6 layers are functioning correctly.
The Igny8 plugin is operating at full capacity.
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- System Information & Database Status Section -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>System Information & Database Status</h3>
<p class="igny8-card-subtitle">System details, database tables, and module status</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-site igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<!-- 4 Cards Grid -->
<div class="igny8-grid igny8-grid-4">
<!-- Card 1: System Information & API Status -->
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>System & API Info</h3>
<p class="igny8-card-subtitle">System information and API configuration status</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-site igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<div style="margin-bottom: 15px;">
<div style="font-weight: bold; margin-bottom: 8px; color: #333;">System Information</div>
<div style="font-size: 12px; margin-bottom: 4px;"><strong>Plugin:</strong> <?php echo get_option('igny8_version', 'Unknown'); ?></div>
<div style="font-size: 12px; margin-bottom: 4px;"><strong>WordPress:</strong> <?php echo get_bloginfo('version'); ?></div>
<div style="font-size: 12px; margin-bottom: 4px;"><strong>PHP:</strong> <?php echo PHP_VERSION; ?></div>
<div style="font-size: 12px; margin-bottom: 4px;"><strong>MySQL:</strong> <?php global $wpdb; echo $wpdb->db_version(); ?></div>
</div>
<div>
<div style="font-weight: bold; margin-bottom: 8px; color: #333;">API Status</div>
<?php
$api_key = get_option('igny8_api_key');
$api_configured = !empty($api_key);
$api_status_color = $api_configured ? 'green' : 'red';
$api_status_icon = $api_configured ? '✓' : '✗';
?>
<div style="font-size: 12px; margin-bottom: 4px; color: <?php echo $api_status_color; ?>;">
<strong>OpenAI API:</strong> <?php echo $api_status_icon; ?> <?php echo $api_configured ? 'Configured' : 'Not Configured'; ?>
</div>
<div style="font-size: 12px; margin-bottom: 4px;"><strong>Model:</strong> <?php echo get_option('igny8_model', 'gpt-4.1'); ?></div>
</div>
</div>
</div>
<!-- Card 2: Database Tables Part 1 -->
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Database Tables 1</h3>
<p class="igny8-card-subtitle">Core database tables status and health</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-database igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<?php
global $wpdb;
$tables_part1 = [
'igny8_keywords' => 'Keywords',
'igny8_tasks' => 'Tasks',
'igny8_data' => 'Data',
'igny8_variations' => 'Variations',
'igny8_rankings' => 'Rankings',
'igny8_suggestions' => 'Suggestions',
'igny8_campaigns' => 'Campaigns'
];
foreach ($tables_part1 as $table => $name) {
$table_name = $wpdb->prefix . $table;
$exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") == $table_name;
$status_color = $exists ? 'green' : 'red';
$status_icon = $exists ? '✓' : '✗';
echo "<div style='font-size: 12px; margin-bottom: 3px; color: $status_color;'>$status_icon $name</div>";
}
?>
</div>
</div>
<!-- Card 3: Database Tables Part 2 -->
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Database Tables 2</h3>
<p class="igny8-card-subtitle">Extended database tables status and health</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-database igny8-dashboard-icon-lg igny8-dashboard-icon-purple"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<?php
$tables_part2 = [
'igny8_content_ideas' => 'Content Ideas',
'igny8_clusters' => 'Clusters',
'igny8_sites' => 'Sites',
'igny8_backlinks' => 'Backlinks',
'igny8_mapping' => 'Mapping',
'igny8_prompts' => 'Prompts',
'igny8_logs' => 'Logs'
];
foreach ($tables_part2 as $table => $name) {
$table_name = $wpdb->prefix . $table;
$exists = $wpdb->get_var("SHOW TABLES LIKE '$table_name'") == $table_name;
$status_color = $exists ? 'green' : 'red';
$status_icon = $exists ? '✓' : '✗';
echo "<div style='font-size: 12px; margin-bottom: 3px; color: $status_color;'>$status_icon $name</div>";
}
?>
</div>
</div>
<!-- Card 4: Module Status -->
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Module Status</h3>
<p class="igny8-card-subtitle">Plugin modules activation and status</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-tools igny8-dashboard-icon-lg igny8-dashboard-icon-amber"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<?php
$modules = [
'planner' => 'Planner',
'writer' => 'Writer',
'personalize' => 'Personalize',
'optimizer' => 'Optimizer',
'linker' => 'Linker'
];
foreach ($modules as $module => $name) {
$enabled = get_option("igny8_{$module}_enabled", true);
$status_color = $enabled ? 'green' : 'red';
$status_icon = $enabled ? '✓' : '✗';
echo "<div style='font-size: 12px; margin-bottom: 3px; color: $status_color;'>$status_icon $name</div>";
}
?>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
if (typeof window.initializeDebugToggle === 'function') {
window.initializeDebugToggle();
}
});
</script>

View File

@@ -0,0 +1,917 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : image-testing.php
* @location : /modules/thinker/image-testing.php
* @type : Admin Page
* @scope : Module Only
* @allowed : AI image generation testing, DALL·E 3 integration, image creation
* @reusability : Single Use
* @notes : AI image generation testing interface for thinker module
*/
if (!defined('ABSPATH')) exit;
// Component render functions are loaded centrally via global-layout.php
// Helper function to read last N lines of a file
function tail_file($filepath, $lines = 10) {
if (!file_exists($filepath)) {
return [];
}
$handle = fopen($filepath, "r");
if (!$handle) {
return [];
}
$linecounter = $lines;
$pos = -2;
$beginning = false;
$text = [];
while ($linecounter > 0) {
$t = " ";
while ($t != "\n") {
if (fseek($handle, $pos, SEEK_END) == -1) {
$beginning = true;
break;
}
$t = fgetc($handle);
$pos--;
}
$linecounter--;
if ($beginning) {
rewind($handle);
}
$text[$lines - $linecounter - 1] = fgets($handle);
if ($beginning) break;
}
fclose($handle);
return array_reverse($text);
}
// Image Testing content
ob_start();
// Handle Form Submit
$image_url = '';
$response_error = '';
$save_path = '';
$generated_image_data = null;
// Handle saving prompt settings
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save_prompt_nonce']) && wp_verify_nonce($_POST['save_prompt_nonce'], 'save_prompt')) {
$prompt_template = sanitize_textarea_field(wp_unslash($_POST['prompt_template']));
update_option('igny8_image_prompt_template', $prompt_template);
$settings_saved = true;
}
// Handle image generation
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['generate_image_nonce']) && wp_verify_nonce($_POST['generate_image_nonce'], 'generate_image')) {
$title = sanitize_text_field(wp_unslash($_POST['title']));
$desc = sanitize_textarea_field(wp_unslash($_POST['desc']));
$image_type = sanitize_text_field(wp_unslash($_POST['image_type']));
$image_provider = sanitize_text_field(wp_unslash($_POST['image_provider'] ?? 'openai'));
$negative_prompt = sanitize_textarea_field(wp_unslash($_POST['negative_prompt'] ?? 'text, watermark, logo, overlay, title, caption, writing on walls, writing on objects, UI, infographic elements, post title'));
$image_width = intval($_POST['image_width'] ?? 1024);
$image_height = intval($_POST['image_height'] ?? 1024);
$image_format = sanitize_text_field(wp_unslash($_POST['image_format'] ?? 'jpg'));
// Get custom prompt template or use default
$prompt_template = wp_unslash(get_option('igny8_image_prompt_template', 'Generate a {image_type} image for a blog post titled "{title}". Description: {description}'));
// Replace placeholders in prompt template
$prompt = str_replace(
['{image_type}', '{title}', '{description}'],
[$image_type, $title, $desc],
$prompt_template
);
// Get API keys
$openai_key = get_option('igny8_api_key', '');
$runware_key = get_option('igny8_runware_api_key', '');
// Check if the required API key is configured
$required_key = ($image_provider === 'runware') ? $runware_key : $openai_key;
$service_name = ($image_provider === 'runware') ? 'Runware' : 'OpenAI';
if (empty($required_key)) {
$response_error = $service_name . ' API key not configured. Please set your API key in the settings.';
} else {
// Debug: Log the request details
error_log('Igny8 Image Generation - Starting request');
error_log('Igny8 Image Generation - Prompt: ' . $prompt);
error_log('Igny8 Image Generation - Provider: ' . $image_provider);
error_log('Igny8 Image Generation - API Key: ' . substr($required_key, 0, 10) . '...');
try {
if ($image_provider === 'runware') {
// Runware API Call
error_log('Igny8 Image Generation - Using Runware service');
// Prepare Runware API payload
$payload = [
[
'taskType' => 'authentication',
'apiKey' => $runware_key
],
[
'taskType' => 'imageInference',
'taskUUID' => wp_generate_uuid4(),
'positivePrompt' => $prompt,
'negativePrompt' => $negative_prompt,
'model' => 'runware:97@1',
'width' => $image_width,
'height' => $image_height,
'steps' => 30,
'CFGScale' => 7.5,
'numberResults' => 1,
'outputFormat' => $image_format
]
];
// Make API request
$response = wp_remote_post('https://api.runware.ai/v1', [
'headers' => ['Content-Type' => 'application/json'],
'body' => json_encode($payload),
'timeout' => 60
]);
if (is_wp_error($response)) {
error_log('Igny8 Image Generation - Runware Error: ' . $response->get_error_message());
$response_error = 'Runware API Error: ' . $response->get_error_message();
} else {
$response_body = wp_remote_retrieve_body($response);
$response_data = json_decode($response_body, true);
if (isset($response_data['data'][0]['imageURL'])) {
// Download and save the image
$image_url = $response_data['data'][0]['imageURL'];
$filename = sanitize_file_name($title) . '_' . time() . '.' . $image_format;
// Create uploads directory
$upload_dir = wp_upload_dir();
$igny8_dir = $upload_dir['basedir'] . '/igny8-ai-images/';
if (!file_exists($igny8_dir)) {
wp_mkdir_p($igny8_dir);
}
// Download image
$image_response = wp_remote_get($image_url);
if (is_wp_error($image_response)) {
$response_error = 'Failed to download Runware image: ' . $image_response->get_error_message();
} else {
$image_data = wp_remote_retrieve_body($image_response);
$file_path = $igny8_dir . $filename;
$saved = file_put_contents($file_path, $image_data);
if ($saved === false) {
$response_error = 'Failed to save Runware image file';
} else {
$response_success = true;
$response_image_url = str_replace(ABSPATH, home_url('/'), $file_path);
$response_message = 'Image generated successfully using Runware!';
// Set generated image data for consistent display
$generated_image_data = [
'model' => 'runware:97@1',
'provider' => 'runware',
'negative_prompt' => $negative_prompt
];
error_log('Igny8 Image Generation - Runware image saved to: ' . $file_path);
}
}
} elseif (isset($response_data['errors'][0]['message'])) {
$response_error = 'Runware API Error: ' . $response_data['errors'][0]['message'];
} else {
$response_error = 'Unknown response from Runware API';
}
}
} else {
// OpenAI DALL-E 3 API Call
error_log('Igny8 Image Generation - Using OpenAI DALL-E 3 service');
$data = [
'model' => 'dall-e-3',
'prompt' => $prompt,
'n' => 1,
'size' => $image_width . 'x' . $image_height
];
// Add negative prompt if supported (DALL-E 3 doesn't support negative prompts, but we'll log it)
if (!empty($negative_prompt)) {
error_log('Igny8 Image Generation - Negative prompt provided but DALL-E 3 does not support negative prompts: ' . $negative_prompt);
}
$args = [
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $openai_key,
'OpenAI-Beta' => 'assistants=v2'
],
'body' => json_encode($data),
'timeout' => 60,
'sslverify' => true
];
error_log('Igny8 Image Generation - Making API request to OpenAI');
$api_response = wp_remote_post('https://api.openai.com/v1/images/generations', $args);
// Debug: Log response details
if (is_wp_error($api_response)) {
error_log('Igny8 Image Generation - WP Error: ' . $api_response->get_error_message());
error_log('Igny8 Image Generation - Error Code: ' . $api_response->get_error_code());
$response_error = 'WordPress HTTP Error: ' . $api_response->get_error_message();
} else {
$response_code = wp_remote_retrieve_response_code($api_response);
$response_body = wp_remote_retrieve_body($api_response);
error_log('Igny8 Image Generation - Response Code: ' . $response_code);
error_log('Igny8 Image Generation - Response Body: ' . substr($response_body, 0, 500));
if ($response_code === 200) {
$body = json_decode($response_body, true);
if (isset($body['data'][0]['url'])) {
$image_url = $body['data'][0]['url'];
$generated_image_data = $body['data'][0];
error_log('Igny8 Image Generation - Image URL received: ' . $image_url);
// Log successful image generation
global $wpdb;
$wpdb->insert($wpdb->prefix . 'igny8_logs', [
'level' => 'info',
'message' => 'Image generation successful',
'context' => wp_json_encode([
'model' => 'dall-e-3',
'prompt_length' => strlen($prompt),
'image_size' => '1024x1024',
'total_cost' => 0.040, // DALL-E 3 standard cost
'title' => $title,
'image_type' => $image_type
]),
'source' => 'openai_image',
'status' => 'success',
'api_id' => $body['data'][0]['id'] ?? null,
'user_id' => get_current_user_id(),
'created_at' => current_time('mysql')
], ['%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s']);
// Try to save to plugin directory first, fallback to WordPress uploads
$plugin_upload_dir = plugin_dir_path(__FILE__) . '../../assets/ai-images/';
$wp_upload_dir = wp_upload_dir();
$wp_upload_path = $wp_upload_dir['basedir'] . '/igny8-ai-images/';
$slug = sanitize_title($title);
$upload_dir = $plugin_upload_dir;
$folder = $upload_dir . $slug . '/';
$save_location = 'plugin';
// Check if plugin directory is writable
if (!file_exists($plugin_upload_dir)) {
if (wp_mkdir_p($plugin_upload_dir)) {
error_log('Igny8 Image Generation - Created plugin directory: ' . $plugin_upload_dir);
} else {
error_log('Igny8 Image Generation - Failed to create plugin directory: ' . $plugin_upload_dir);
// Fallback to WordPress uploads directory
$upload_dir = $wp_upload_path;
$folder = $upload_dir . $slug . '/';
$save_location = 'wordpress';
error_log('Igny8 Image Generation - Using WordPress uploads as fallback: ' . $wp_upload_path);
}
}
if (file_exists($plugin_upload_dir) && !is_writable($plugin_upload_dir)) {
// Try to fix permissions
chmod($plugin_upload_dir, 0755);
error_log('Igny8 Image Generation - Attempted to fix plugin directory permissions: ' . $plugin_upload_dir);
if (!is_writable($plugin_upload_dir)) {
// Fallback to WordPress uploads directory
$upload_dir = $wp_upload_path;
$folder = $upload_dir . $slug . '/';
$save_location = 'wordpress';
error_log('Igny8 Image Generation - Plugin directory not writable, using WordPress uploads: ' . $wp_upload_path);
}
}
// Ensure target directory exists
if (!file_exists($folder)) {
wp_mkdir_p($folder);
error_log('Igny8 Image Generation - Created directory: ' . $folder);
}
// Final check if directory is writable
if (file_exists($folder) && !is_writable($folder)) {
// Try to fix permissions one more time
chmod($folder, 0755);
error_log('Igny8 Image Generation - Attempted to fix final permissions for: ' . $folder);
if (!is_writable($folder)) {
$response_error = 'Directory is not writable. Please check file permissions for: ' . $folder;
error_log('Igny8 Image Generation - Directory still not writable: ' . $folder);
error_log('Igny8 Image Generation - Directory permissions: ' . substr(sprintf('%o', fileperms($folder)), -4));
}
} elseif (!file_exists($folder)) {
$response_error = 'Directory does not exist and could not be created: ' . $folder;
error_log('Igny8 Image Generation - Directory does not exist: ' . $folder);
}
if (empty($response_error)) {
// Download image data
$image_data = file_get_contents($image_url);
if ($image_data === false) {
$response_error = 'Failed to download image from URL: ' . $image_url;
error_log('Igny8 Image Generation - Failed to download image from: ' . $image_url);
} else {
$filename = $folder . 'image.png';
// Try to write the file
$bytes_written = file_put_contents($filename, $image_data);
if ($bytes_written === false) {
$response_error = 'Failed to save image file. Check file permissions.';
error_log('Igny8 Image Generation - Failed to write file: ' . $filename);
error_log('Igny8 Image Generation - Directory permissions: ' . substr(sprintf('%o', fileperms($folder)), -4));
error_log('Igny8 Image Generation - Directory writable: ' . (is_writable($folder) ? 'yes' : 'no'));
} else {
// Determine the correct path for display
if ($save_location === 'wordpress') {
$display_path = '/wp-content/uploads/igny8-ai-images/' . $slug . '/image.png';
$log_path = $display_path;
} else {
$display_path = '/assets/ai-images/' . $slug . '/image.png';
$log_path = $display_path;
}
$save_path = 'Saved to: ' . $display_path . ' (' . $bytes_written . ' bytes)';
error_log('Igny8 Image Generation - Image saved successfully: ' . $filename . ' (' . $bytes_written . ' bytes)');
error_log('Igny8 Image Generation - Save location: ' . $save_location);
// Update log with file path
$wpdb->update(
$wpdb->prefix . 'igny8_logs',
[
'context' => wp_json_encode([
'model' => 'dall-e-3',
'prompt_length' => strlen($prompt),
'image_size' => '1024x1024',
'total_cost' => 0.040,
'title' => $title,
'image_type' => $image_type,
'file_path' => $log_path,
'file_size' => $bytes_written,
'save_location' => $save_location
])
],
['api_id' => $body['data'][0]['id'] ?? null],
['%s'],
['%s']
);
}
}
}
} else {
$response_error = 'Image URL not returned from API. Response: ' . substr($response_body, 0, 200);
if (isset($body['error']['message'])) {
$response_error .= ' Error: ' . $body['error']['message'];
}
// Log failed image generation
global $wpdb;
$wpdb->insert($wpdb->prefix . 'igny8_logs', [
'level' => 'error',
'message' => 'Image generation failed - no URL returned',
'context' => wp_json_encode([
'model' => 'dall-e-3',
'prompt_length' => strlen($prompt),
'image_size' => '1024x1024',
'error_response' => substr($response_body, 0, 500),
'title' => $title,
'image_type' => $image_type
]),
'source' => 'openai_image',
'status' => 'error',
'user_id' => get_current_user_id(),
'created_at' => current_time('mysql')
], ['%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s']);
}
} else {
$response_error = 'API returned error code ' . $response_code . '. Response: ' . substr($response_body, 0, 200);
// Log failed image generation
global $wpdb;
$wpdb->insert($wpdb->prefix . 'igny8_logs', [
'level' => 'error',
'message' => 'Image generation failed - HTTP error',
'context' => wp_json_encode([
'model' => 'dall-e-3',
'prompt_length' => strlen($prompt),
'image_size' => '1024x1024',
'http_code' => $response_code,
'error_response' => substr($response_body, 0, 500),
'title' => $title,
'image_type' => $image_type
]),
'source' => 'openai_image',
'status' => 'error',
'user_id' => get_current_user_id(),
'created_at' => current_time('mysql')
], ['%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s']);
}
}
} // End of OpenAI else block
} catch (Exception $e) {
error_log('Igny8 Image Generation - Exception: ' . $e->getMessage());
$response_error = 'Exception occurred: ' . $e->getMessage();
// Log exception
global $wpdb;
$wpdb->insert($wpdb->prefix . 'igny8_logs', [
'level' => 'error',
'message' => 'Image generation failed - exception',
'context' => wp_json_encode([
'model' => 'dall-e-3',
'prompt_length' => strlen($prompt),
'image_size' => '1024x1024',
'exception_message' => $e->getMessage(),
'title' => $title,
'image_type' => $image_type
]),
'source' => 'openai_image',
'status' => 'error',
'user_id' => get_current_user_id(),
'created_at' => current_time('mysql')
], ['%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s']);
}
}
}
?>
<div class="igny8-module-home">
<!-- Page Header -->
<div class="igny8-dashboard-section">
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>AI Image Generation Test</h3>
<p class="igny8-card-subtitle">Test OpenAI DALL·E 3 image generation with your content data</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-format-image igny8-dashboard-icon-lg igny8-dashboard-icon-purple"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<p>Generate AI-powered images for your content using OpenAI's DALL·E 3. Enter your post details below to create custom images based on your title, description, keywords, and cluster information.</p>
</div>
</div>
</div>
<div class="igny8-grid-3">
<!-- Image Generation Form -->
<div class="igny8-dashboard-section">
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Generate Image</h3>
<p class="igny8-card-subtitle">Configure image generation parameters</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-generic igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<form method="POST" id="igny8-image-generation-form">
<?php wp_nonce_field('generate_image', 'generate_image_nonce'); ?>
<div class="igny8-form-group">
<label for="title">Post Title *</label>
<input type="text" id="title" name="title" class="igny8-form-control" value="<?php echo isset($_POST['title']) ? esc_attr(wp_unslash($_POST['title'])) : ''; ?>" required>
</div>
<div class="igny8-form-group">
<label for="desc">Prompt Description *</label>
<textarea id="desc" name="desc" rows="4" class="igny8-form-control" placeholder="Describe the image you want to generate..." required><?php echo isset($_POST['desc']) ? esc_textarea(wp_unslash($_POST['desc'])) : ''; ?></textarea>
<small class="form-help">Describe the visual elements, style, mood, and composition you want in the image.</small>
</div>
<div class="igny8-form-group">
<label for="negative_prompt">Negative Prompt</label>
<textarea id="negative_prompt" name="negative_prompt" rows="2" class="igny8-form-control" placeholder="Describe what you DON'T want in the image..."><?php echo isset($_POST['negative_prompt']) ? esc_textarea(wp_unslash($_POST['negative_prompt'])) : 'text, watermark, logo, overlay, title, caption, writing on walls, writing on objects, UI, infographic elements, post title'; ?></textarea>
<small class="form-help">Specify elements to avoid in the generated image (text, watermarks, logos, etc.).</small>
</div>
<div class="igny8-form-group">
<label for="image_type">Image Type</label>
<select id="image_type" name="image_type" class="igny8-form-control">
<option value="realistic" <?php selected(isset($_POST['image_type']) ? $_POST['image_type'] : 'realistic', 'realistic'); ?>>Realistic</option>
<option value="illustration" <?php selected(isset($_POST['image_type']) ? $_POST['image_type'] : '', 'illustration'); ?>>Illustration</option>
<option value="3D render" <?php selected(isset($_POST['image_type']) ? $_POST['image_type'] : '', '3D render'); ?>>3D Render</option>
<option value="minimalist" <?php selected(isset($_POST['image_type']) ? $_POST['image_type'] : '', 'minimalist'); ?>>Minimalist</option>
<option value="cartoon" <?php selected(isset($_POST['image_type']) ? $_POST['image_type'] : '', 'cartoon'); ?>>Cartoon</option>
</select>
</div>
<div class="igny8-form-group">
<label for="image_provider">Image Provider</label>
<select id="image_provider" name="image_provider" class="igny8-form-control">
<option value="openai" <?php selected(isset($_POST['image_provider']) ? $_POST['image_provider'] : 'openai', 'openai'); ?>>OpenAI DALL·E 3</option>
<option value="runware" <?php selected(isset($_POST['image_provider']) ? $_POST['image_provider'] : '', 'runware'); ?>>Runware HiDream-I1 Full</option>
</select>
<small class="form-help">Choose the AI image generation service to use.</small>
</div>
<div class="igny8-form-group">
<label for="igny8_image_size_selector">Image Size</label>
<select id="igny8_image_size_selector" name="image_size" class="igny8-form-control">
<!-- Options will be populated dynamically by JavaScript -->
</select>
<small class="form-help">Choose the image dimensions for your generated image.</small>
<div id="igny8-selected-dimensions" style="margin-top: 5px; font-weight: bold; color: #0073aa;"></div>
</div>
<div class="igny8-form-group">
<label for="igny8_image_format_selector">Image Format</label>
<select id="igny8_image_format_selector" name="image_format" class="igny8-form-control">
<option value="jpg" <?php selected(isset($_POST['image_format']) ? $_POST['image_format'] : 'jpg', 'jpg'); ?>>JPG</option>
<option value="png" <?php selected(isset($_POST['image_format']) ? $_POST['image_format'] : '', 'png'); ?>>PNG</option>
<option value="webp" <?php selected(isset($_POST['image_format']) ? $_POST['image_format'] : '', 'webp'); ?>>WEBP</option>
</select>
<small class="form-help">Choose the image file format for your generated image.</small>
<div id="igny8-selected-format" style="margin-top: 5px; font-weight: bold; color: #0073aa;"></div>
</div>
<!-- Hidden fields for width and height -->
<input type="hidden" id="image_width" name="image_width" value="1024">
<input type="hidden" id="image_height" name="image_height" value="1024">
<div class="igny8-form-actions">
<button type="submit" class="igny8-btn igny8-btn-primary" id="generate-btn">
<span class="dashicons dashicons-format-image"></span>
<span class="btn-text">Generate Image</span>
<span class="btn-loading" style="display: none;">
<span class="dashicons dashicons-update" style="animation: spin 1s linear infinite;"></span>
Sending Request...
</span>
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Results Panel -->
<div class="igny8-dashboard-section">
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Generated Image</h3>
<p class="igny8-card-subtitle">AI-generated image results</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-format-image igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<?php if ($image_url): ?>
<div class="igny8-image-result">
<div class="igny8-image-preview">
<img src="<?php echo esc_url($image_url); ?>" alt="Generated Image" style="max-width: 100%; height: auto; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15);">
</div>
<?php if ($generated_image_data): ?>
<div class="igny8-image-details">
<h4>Image Details</h4>
<ul class="igny8-details-list">
<li><strong>Size:</strong> <?php echo $image_width . 'x' . $image_height; ?> pixels</li>
<li><strong>Format:</strong> <?php echo strtoupper($image_format); ?></li>
<li><strong>Model:</strong> <?php echo ($image_provider === 'runware') ? 'Runware HiDream-I1 Full' : 'DALL·E 3'; ?></li>
<?php if (isset($generated_image_data['revised_prompt'])): ?>
<li><strong>Revised Prompt:</strong> <?php echo esc_html($generated_image_data['revised_prompt']); ?></li>
<?php endif; ?>
<?php if (!empty($negative_prompt)): ?>
<li><strong>Negative Prompt:</strong> <?php echo esc_html($negative_prompt); ?></li>
<?php endif; ?>
</ul>
</div>
<?php endif; ?>
<?php if ($save_path): ?>
<div class="igny8-save-info">
<p class="igny8-success-message">
<span class="dashicons dashicons-yes-alt" style="color: var(--green);"></span>
<?php echo esc_html($save_path); ?>
</p>
</div>
<?php endif; ?>
<div class="igny8-image-actions">
<a href="<?php echo esc_url($image_url); ?>" target="_blank" class="igny8-btn igny8-btn-outline">
<span class="dashicons dashicons-external"></span>
View Original
</a>
<button type="button" class="igny8-btn igny8-btn-success" onclick="navigator.clipboard.writeText('<?php echo esc_js($image_url); ?>')">
<span class="dashicons dashicons-admin-page"></span>
Copy URL
</button>
</div>
</div>
<?php elseif ($response_error): ?>
<div class="igny8-error-message">
<span class="dashicons dashicons-warning" style="color: var(--red); font-size: 24px;"></span>
<h4>Generation Failed</h4>
<p><?php echo esc_html($response_error); ?></p>
</div>
<?php else: ?>
<div class="igny8-empty-state">
<span class="dashicons dashicons-format-image" style="color: var(--text-dim); font-size: 48px;"></span>
<p>No image generated yet. Fill out the form and click "Generate Image" to create your first AI image.</p>
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- Prompt Settings -->
<div class="igny8-dashboard-section">
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Prompt Settings</h3>
<p class="igny8-card-subtitle">Customize your image generation prompt template</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-edit-page igny8-dashboard-icon-lg igny8-dashboard-icon-amber"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<?php if (isset($settings_saved)): ?>
<div class="igny8-notice igny8-notice-success">
<span class="dashicons dashicons-yes-alt"></span>
Prompt template saved successfully!
</div>
<?php endif; ?>
<form method="POST">
<?php wp_nonce_field('save_prompt', 'save_prompt_nonce'); ?>
<div class="igny8-form-group">
<label for="prompt_template">Prompt Template</label>
<textarea id="prompt_template" name="prompt_template" rows="6" class="igny8-form-control" placeholder="Enter your custom prompt template..."><?php echo esc_textarea(wp_unslash(get_option('igny8_image_prompt_template', 'Generate a {image_type} image for a blog post titled "{title}". Description: {description}'))); ?></textarea>
<small class="form-help">
<strong>Available placeholders:</strong><br>
<code>{title}</code> - Post title<br>
<code>{description}</code> - Prompt description<br>
<code>{image_type}</code> - Selected image type
</small>
</div>
<div class="igny8-form-actions">
<button type="submit" class="igny8-btn igny8-btn-success">
<span class="dashicons dashicons-saved"></span>
Save Prompt Template
</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- API Configuration Info -->
<div class="igny8-dashboard-section">
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>API Configuration</h3>
<p class="igny8-card-subtitle">OpenAI API settings and requirements</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-settings igny8-dashboard-icon-lg igny8-dashboard-icon-amber"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<div class="igny8-api-info">
<div class="igny8-info-grid">
<div class="igny8-info-item">
<h4>OpenAI API Key</h4>
<p>Your OpenAI API key is <?php echo !empty(get_option('igny8_api_key', '')) ? '<span class="igny8-status-ok">configured</span>' : '<span class="igny8-status-error">not set</span>'; ?></p>
<a href="<?php echo admin_url('admin.php?page=igny8-settings'); ?>" class="igny8-btn igny8-btn-outline igny8-btn-sm">
<span class="dashicons dashicons-admin-settings"></span>
Configure API Key
</a>
</div>
<div class="igny8-info-item">
<h4>Image Service</h4>
<p><?php
$current_provider = isset($image_provider) ? $image_provider : 'openai';
if ($current_provider === 'runware') {
echo 'Runware HiDream-I1 Full';
} else {
echo 'OpenAI DALL·E 3';
}
?> (1024x1024 resolution)</p>
</div>
<div class="igny8-info-item">
<h4>Image Storage</h4>
<p>Generated images are saved to <code>/assets/ai-images/[slug]/image.png</code></p>
</div>
<div class="igny8-info-item">
<h4>Usage Limits</h4>
<p>Subject to your OpenAI account limits and billing</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Debug Information -->
<div class="igny8-dashboard-section">
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Debug Information</h3>
<p class="igny8-card-subtitle">Technical details and troubleshooting</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-tools igny8-dashboard-icon-lg igny8-dashboard-icon-amber"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<div class="igny8-debug-info">
<div class="igny8-debug-grid">
<div class="igny8-debug-item">
<h4>Server Information</h4>
<ul class="igny8-debug-list">
<li><strong>PHP Version:</strong> <?php echo PHP_VERSION; ?></li>
<li><strong>WordPress Version:</strong> <?php echo get_bloginfo('version'); ?></li>
<li><strong>cURL Available:</strong> <?php echo function_exists('curl_init') ? 'Yes' : 'No'; ?></li>
<li><strong>SSL Support:</strong> <?php echo function_exists('openssl_version') ? 'Yes' : 'No'; ?></li>
<li><strong>Image Save Directory:</strong> <?php echo wp_upload_dir()['basedir'] . '/igny8-ai-images/'; ?></li>
<li><strong>Image Dir Writable:</strong>
<?php
$image_dir = wp_upload_dir()['basedir'] . '/igny8-ai-images/';
if (file_exists($image_dir)) {
echo is_writable($image_dir) ? '✅ Yes' : '❌ No (' . substr(sprintf('%o', fileperms($image_dir)), -4) . ')';
} else {
echo '❌ Directory does not exist';
}
?>
</li>
<li><strong>WordPress Uploads:</strong> <?php echo wp_upload_dir()['basedir']; ?></li>
<li><strong>WP Uploads Writable:</strong>
<?php
$wp_uploads = wp_upload_dir()['basedir'];
echo is_writable($wp_uploads) ? '✅ Yes' : '❌ No (' . substr(sprintf('%o', fileperms($wp_uploads)), -4) . ')';
?>
</li>
</ul>
</div>
<div class="igny8-debug-item">
<h4>API Configuration</h4>
<ul class="igny8-debug-list">
<li><strong>API Key Status:</strong> <?php echo !empty(get_option('igny8_api_key', '')) ? 'Configured' : 'Not Set'; ?></li>
<li><strong>API Key Length:</strong> <?php echo strlen(get_option('igny8_api_key', '')); ?> characters</li>
<li><strong>Prompt Template:</strong> <?php echo strlen(get_option('igny8_image_prompt_template', '')); ?> characters</li>
</ul>
</div>
<div class="igny8-debug-item">
<h4>Recent Errors</h4>
<div class="igny8-error-log">
<?php
$error_log = ini_get('error_log');
if ($error_log && file_exists($error_log)) {
$recent_errors = tail_file($error_log, 10);
if ($recent_errors) {
echo '<pre style="font-size: 11px; background: #f8f9fa; padding: 10px; border-radius: 4px; max-height: 200px; overflow-y: auto;">';
foreach ($recent_errors as $error) {
if (strpos($error, 'Igny8 Image Generation') !== false) {
echo esc_html($error) . "\n";
}
}
echo '</pre>';
} else {
echo '<p>No recent Igny8 errors found.</p>';
}
} else {
echo '<p>Error log not accessible.</p>';
}
?>
</div>
</div>
<div class="igny8-debug-item">
<h4>Test Connection</h4>
<button type="button" class="igny8-btn igny8-btn-outline" onclick="testConnection()">
<span class="dashicons dashicons-admin-network"></span>
Test API Connection
</button>
<div id="connection-test-result" style="margin-top: 10px;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const form = document.getElementById('igny8-image-generation-form');
const generateBtn = document.getElementById('generate-btn');
const btnText = generateBtn.querySelector('.btn-text');
const btnLoading = generateBtn.querySelector('.btn-loading');
if (form) {
form.addEventListener('submit', function(e) {
// Show loading state
btnText.style.display = 'none';
btnLoading.style.display = 'inline-flex';
generateBtn.disabled = true;
// Show notification that request is being sent
showNotification('Sending image generation request to DALL·E 3...', 'info');
});
}
// Use unified global notification system
function showNotification(message, type = 'info') {
if (typeof igny8GlobalNotification === 'function') {
igny8GlobalNotification(message, type);
} else {
// Fallback to basic alert if global system not available
alert(message);
}
}
// Show success/error notifications if page was just submitted
<?php if ($image_url): ?>
showNotification('Image generated successfully! 🎉', 'success');
<?php elseif ($response_error): ?>
showNotification('Image generation failed: <?php echo esc_js($response_error); ?>', 'error');
<?php endif; ?>
});
// Global function for testing API connection
function testConnection() {
const resultDiv = document.getElementById('connection-test-result');
resultDiv.innerHTML = '<p>Testing connection...</p>';
// Simple test request to check if server can make HTTP requests
fetch('<?php echo admin_url('admin-ajax.php'); ?>', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'action=igny8_test_connection&nonce=<?php echo wp_create_nonce('igny8_test_connection'); ?>'
})
.then(response => response.json())
.then(data => {
if (data.success) {
resultDiv.innerHTML = '<p style="color: green;">✓ Connection test successful</p>';
} else {
resultDiv.innerHTML = '<p style="color: red;">✗ Connection test failed: ' + (data.data || 'Unknown error') + '</p>';
}
})
.catch(error => {
resultDiv.innerHTML = '<p style="color: red;">✗ Connection test failed: ' + error.message + '</p>';
});
}
</script>
<?php
// Localize script for the image testing page
wp_localize_script('igny8-admin-js', 'IGNY8_PAGE', [
'module' => 'thinker',
'subpage' => 'image-testing',
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('igny8_image_testing_settings')
]);
?>

View File

@@ -0,0 +1,344 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : profile.php
* @location : /modules/thinker/profile.php
* @type : Admin Page
* @scope : Module Only
* @allowed : AI profile configuration, behavior settings, personality management
* @reusability : Single Use
* @notes : AI behavior configuration interface for thinker module
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
?>
<div class="igny8-module-page">
<div class="igny8-container">
<!-- Page title removed - titles should not appear inside page content -->
<!-- Writing Style Section -->
<div class="igny8-dashboard-section">
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Writing Style Preferences</h3>
<p class="igny8-card-subtitle">Configure AI writing tone, style, and behavior</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-edit-page igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<form id="igny8-ai-profile-form">
<!-- Tone Selection -->
<div class="igny8-form-group" style="margin-bottom: 20px;">
<label for="ai_tone" class="igny8-field-label">Default Tone</label>
<select name="ai_tone" id="ai_tone" class="igny8-select" style="width: 100%;">
<option value="professional" <?php selected(igny8_get_ai_setting('ai_tone', 'professional'), 'professional'); ?>>Professional</option>
<option value="casual" <?php selected(igny8_get_ai_setting('ai_tone', 'professional'), 'casual'); ?>>Casual</option>
<option value="authoritative" <?php selected(igny8_get_ai_setting('ai_tone', 'professional'), 'authoritative'); ?>>Authoritative</option>
<option value="friendly" <?php selected(igny8_get_ai_setting('ai_tone', 'professional'), 'friendly'); ?>>Friendly</option>
<option value="technical" <?php selected(igny8_get_ai_setting('ai_tone', 'professional'), 'technical'); ?>>Technical</option>
</select>
<small class="igny8-field-description">Choose the default tone for AI-generated content</small>
</div>
<!-- Writing Level -->
<div class="igny8-form-group" style="margin-bottom: 20px;">
<label for="writing_level" class="igny8-field-label">Writing Level</label>
<select name="writing_level" id="writing_level" class="igny8-select" style="width: 100%;">
<option value="beginner" <?php selected(igny8_get_ai_setting('writing_level', 'intermediate'), 'beginner'); ?>>Beginner (Simple, clear language)</option>
<option value="intermediate" <?php selected(igny8_get_ai_setting('writing_level', 'intermediate'), 'intermediate'); ?>>Intermediate (Balanced complexity)</option>
<option value="advanced" <?php selected(igny8_get_ai_setting('writing_level', 'intermediate'), 'advanced'); ?>>Advanced (Complex, detailed)</option>
</select>
<small class="igny8-field-description">Select the complexity level for AI-generated content</small>
</div>
<!-- Content Length Preference -->
<div class="igny8-form-group" style="margin-bottom: 20px;">
<label for="content_length" class="igny8-field-label">Preferred Content Length</label>
<select name="content_length" id="content_length" class="igny8-select" style="width: 100%;">
<option value="short" <?php selected(igny8_get_ai_setting('content_length', 'medium'), 'short'); ?>>Short (300-500 words)</option>
<option value="medium" <?php selected(igny8_get_ai_setting('content_length', 'medium'), 'medium'); ?>>Medium (500-1000 words)</option>
<option value="long" <?php selected(igny8_get_ai_setting('content_length', 'medium'), 'long'); ?>>Long (1000+ words)</option>
</select>
<small class="igny8-field-description">Choose the preferred length for AI-generated content</small>
</div>
<!-- Custom Instructions -->
<div class="igny8-form-group" style="margin-bottom: 20px;">
<label for="custom_instructions" class="igny8-field-label">Custom AI Instructions</label>
<textarea name="custom_instructions" id="custom_instructions" rows="6" class="igny8-textarea igny8-textarea-blue" style="width: 100%;" placeholder="Enter specific instructions for how the AI should behave, write, or respond..."><?php echo esc_textarea(wp_unslash(igny8_get_ai_setting('custom_instructions', ''))); ?></textarea>
<small class="igny8-field-description">These instructions will be added to all AI prompts to customize behavior.</small>
</div>
</form>
</div>
</div>
</div>
<!-- SEO Preferences Section -->
<div class="igny8-dashboard-section">
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>SEO Preferences</h3>
<p class="igny8-card-subtitle">Configure SEO optimization settings and preferences</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-search igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<form id="igny8-seo-profile-form">
<!-- Keyword Density -->
<div class="igny8-form-group" style="margin-bottom: 20px;">
<label for="keyword_density" class="igny8-field-label">Keyword Density Preference</label>
<select name="keyword_density" id="keyword_density" class="igny8-select" style="width: 100%;">
<option value="low" <?php selected(igny8_get_ai_setting('keyword_density', 'medium'), 'low'); ?>>Low (1-2%)</option>
<option value="medium" <?php selected(igny8_get_ai_setting('keyword_density', 'medium'), 'medium'); ?>>Medium (2-3%)</option>
<option value="high" <?php selected(igny8_get_ai_setting('keyword_density', 'medium'), 'high'); ?>>High (3-4%)</option>
</select>
<small class="igny8-field-description">Choose the keyword density for SEO optimization</small>
</div>
<!-- Meta Description Length -->
<div class="igny8-form-group" style="margin-bottom: 20px;">
<label for="meta_description_length" class="igny8-field-label">Meta Description Length</label>
<select name="meta_description_length" id="meta_description_length" class="igny8-select" style="width: 100%;">
<option value="short" <?php selected(igny8_get_ai_setting('meta_description_length', 'medium'), 'short'); ?>>Short (120-140 chars)</option>
<option value="medium" <?php selected(igny8_get_ai_setting('meta_description_length', 'medium'), 'medium'); ?>>Medium (140-160 chars)</option>
<option value="long" <?php selected(igny8_get_ai_setting('meta_description_length', 'medium'), 'long'); ?>>Long (160+ chars)</option>
</select>
<small class="igny8-field-description">Choose the preferred length for meta descriptions</small>
</div>
<!-- Include Schema Markup -->
<div class="igny8-form-group" style="margin-bottom: 15px;">
<label class="igny8-checkbox-label" style="display: flex; align-items: center; gap: 10px;">
<input type="checkbox" name="include_schema" id="include_schema" <?php checked(igny8_get_ai_setting('include_schema', false)); ?>>
<span class="igny8-checkbox-text">Include Schema.org markup suggestions</span>
</label>
<small class="igny8-field-description">Enable structured data recommendations for better SEO</small>
</div>
<!-- Include Internal Linking -->
<div class="igny8-form-group" style="margin-bottom: 15px;">
<label class="igny8-checkbox-label" style="display: flex; align-items: center; gap: 10px;">
<input type="checkbox" name="include_internal_links" id="include_internal_links" <?php checked(igny8_get_ai_setting('include_internal_links', true)); ?>>
<span class="igny8-checkbox-text">Suggest internal linking opportunities</span>
</label>
<small class="igny8-field-description">Enable internal linking suggestions for better site structure</small>
</div>
</form>
</div>
</div>
</div>
<!-- Content Structure Preferences -->
<div class="igny8-dashboard-section">
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Content Structure Preferences</h3>
<p class="igny8-card-subtitle">Configure content structure and formatting preferences</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-list-view igny8-dashboard-icon-lg igny8-dashboard-icon-purple"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<form id="igny8-structure-profile-form">
<!-- Heading Structure -->
<div class="igny8-form-group" style="margin-bottom: 20px;">
<label for="heading_style" class="igny8-field-label">Heading Style Preference</label>
<select name="heading_style" id="heading_style" class="igny8-select" style="width: 100%;">
<option value="hierarchical" <?php selected(igny8_get_ai_setting('heading_style', 'hierarchical'), 'hierarchical'); ?>>Hierarchical (H1 > H2 > H3)</option>
<option value="flat" <?php selected(igny8_get_ai_setting('heading_style', 'hierarchical'), 'flat'); ?>>Flat (H1 > H2 only)</option>
<option value="minimal" <?php selected(igny8_get_ai_setting('heading_style', 'hierarchical'), 'minimal'); ?>>Minimal (H1 only)</option>
</select>
<small class="igny8-field-description">Choose the heading structure for content organization</small>
</div>
<!-- Include Table of Contents -->
<div class="igny8-form-group" style="margin-bottom: 15px;">
<label class="igny8-checkbox-label" style="display: flex; align-items: center; gap: 10px;">
<input type="checkbox" name="include_toc" id="include_toc" <?php checked(igny8_get_ai_setting('include_toc', false)); ?>>
<span class="igny8-checkbox-text">Include Table of Contents for long articles</span>
</label>
<small class="igny8-field-description">Automatically generate table of contents for lengthy content</small>
</div>
<!-- Include Call-to-Action -->
<div class="igny8-form-group" style="margin-bottom: 15px;">
<label class="igny8-checkbox-label" style="display: flex; align-items: center; gap: 10px;">
<input type="checkbox" name="include_cta" id="include_cta" <?php checked(igny8_get_ai_setting('include_cta', true)); ?>>
<span class="igny8-checkbox-text">Include Call-to-Action sections</span>
</label>
<small class="igny8-field-description">Add call-to-action sections to engage readers</small>
</div>
<!-- Bullet Points Preference -->
<div class="igny8-form-group" style="margin-bottom: 20px;">
<label for="bullet_style" class="igny8-field-label">Bullet Points Style</label>
<select name="bullet_style" id="bullet_style" class="igny8-select" style="width: 100%;">
<option value="numbered" <?php selected(igny8_get_ai_setting('bullet_style', 'bulleted'), 'numbered'); ?>>Numbered Lists</option>
<option value="bulleted" <?php selected(igny8_get_ai_setting('bullet_style', 'bulleted'), 'bulleted'); ?>>Bulleted Lists</option>
<option value="mixed" <?php selected(igny8_get_ai_setting('bullet_style', 'bulleted'), 'mixed'); ?>>Mixed (as appropriate)</option>
</select>
<small class="igny8-field-description">Choose the preferred list style for content</small>
</div>
</form>
</div>
</div>
</div>
<!-- Save Actions -->
<div class="igny8-dashboard-section">
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Profile Actions</h3>
<p class="igny8-card-subtitle">Save, reset, or test your AI profile settings</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-tools igny8-dashboard-icon-lg igny8-dashboard-icon-amber"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<div class="igny8-form-actions">
<button type="button" id="igny8-save-profile" class="igny8-btn igny8-btn-success">
<span class="dashicons dashicons-yes"></span>
Save AI Profile
</button>
<button type="button" id="igny8-reset-profile" class="igny8-btn igny8-btn-outline">
<span class="dashicons dashicons-update"></span>
Reset to Defaults
</button>
<button type="button" id="igny8-test-profile" class="igny8-btn igny8-btn-primary">
<span class="dashicons dashicons-admin-network"></span>
Test Profile Settings
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
jQuery(document).ready(function($) {
// Save AI Profile
$('#igny8-save-profile').on('click', function() {
var formData = {
action: 'igny8_save_ai_profile',
nonce: '<?php echo wp_create_nonce('igny8_thinker_profile'); ?>',
ai_tone: $('#ai_tone').val(),
writing_level: $('#writing_level').val(),
content_length: $('#content_length').val(),
custom_instructions: $('#custom_instructions').val(),
keyword_density: $('#keyword_density').val(),
meta_description_length: $('#meta_description_length').val(),
include_schema: $('#include_schema').is(':checked'),
include_internal_links: $('#include_internal_links').is(':checked'),
heading_style: $('#heading_style').val(),
include_toc: $('#include_toc').is(':checked'),
include_cta: $('#include_cta').is(':checked'),
bullet_style: $('#bullet_style').val()
};
$.ajax({
url: ajaxurl,
type: 'POST',
data: formData,
success: function(response) {
if (response.success) {
igny8ShowNotification('AI Profile saved successfully!', 'success', 'thinker');
} else {
igny8ShowNotification('Error saving profile: ' + response.data, 'error', 'thinker');
}
},
error: function() {
igny8ShowNotification('Error saving profile', 'error', 'thinker');
}
});
});
// Reset Profile
$('#igny8-reset-profile').on('click', function() {
if (confirm('Are you sure you want to reset the AI profile to default settings?')) {
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'igny8_reset_ai_profile',
nonce: '<?php echo wp_create_nonce('igny8_thinker_profile'); ?>'
},
success: function(response) {
if (response.success) {
igny8ShowNotification('AI Profile reset to defaults!', 'success', 'thinker');
// Update form fields with reset values from response
if (response.data) {
if (response.data.ai_tone) $('#ai_tone').val(response.data.ai_tone);
if (response.data.writing_level) $('#writing_level').val(response.data.writing_level);
if (response.data.content_length) $('#content_length').val(response.data.content_length);
if (response.data.custom_instructions) $('#custom_instructions').val(response.data.custom_instructions);
if (response.data.keyword_density) $('#keyword_density').val(response.data.keyword_density);
if (response.data.meta_description_length) $('#meta_description_length').val(response.data.meta_description_length);
if (response.data.include_schema !== undefined) $('#include_schema').prop('checked', response.data.include_schema);
if (response.data.include_internal_links !== undefined) $('#include_internal_links').prop('checked', response.data.include_internal_links);
if (response.data.heading_style) $('#heading_style').val(response.data.heading_style);
if (response.data.include_toc !== undefined) $('#include_toc').prop('checked', response.data.include_toc);
if (response.data.include_cta !== undefined) $('#include_cta').prop('checked', response.data.include_cta);
if (response.data.bullet_style) $('#bullet_style').val(response.data.bullet_style);
}
} else {
igny8ShowNotification('Error resetting profile: ' + response.data, 'error', 'thinker');
}
},
error: function() {
igny8ShowNotification('Error resetting profile', 'error', 'thinker');
}
});
}
});
// Test Profile
$('#igny8-test-profile').on('click', function() {
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'igny8_test_ai_profile',
nonce: '<?php echo wp_create_nonce('igny8_thinker_profile'); ?>'
},
success: function(response) {
if (response.success) {
igny8ShowNotification('AI Profile test successful!', 'success', 'thinker');
} else {
igny8ShowNotification('AI Profile test failed: ' + response.data, 'error', 'thinker');
}
},
error: function() {
igny8ShowNotification('Error testing profile', 'error', 'thinker');
}
});
});
});
</script>
<!-- Global notification system is handled by core.js -->
<?php

View File

@@ -0,0 +1,461 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : prompts.php
* @location : /modules/thinker/prompts.php
* @type : Admin Page
* @scope : Module Only
* @allowed : AI prompt management, prompt editing, AI configuration
* @reusability : Single Use
* @notes : AI prompt management interface for thinker module
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Prompts subpage - show AI prompts management
?>
<div class="igny8-module-page">
<div class="igny8-container">
<!-- Page title removed - titles should not appear inside page content -->
<!-- Planner Prompts Section -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Planner Prompts</h3>
<p class="igny8-card-subtitle">Configure AI prompt templates for clustering and idea generation</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-edit igny8-dashboard-icon-lg igny8-dashboard-icon-amber"></span>
</div>
</div>
</div>
<?php if (igny8_get_ai_setting('planner_mode', 'manual') === 'ai'): ?>
<!-- Clustering Prompt Card -->
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h4>Clustering Prompt</h4>
<p class="igny8-card-subtitle">Group keywords into topic clusters</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-networking igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
</div>
<div class="igny8-card-body" style="text-align: center;">
<p class="description">This prompt is used to group keywords into topic clusters. Use [IGNY8_KEYWORDS] to inject keyword data.</p>
<textarea name="igny8_clustering_prompt" rows="8" class="igny8-textarea-green" style="width: 90%; margin: 0 auto; font-family: monospace; font-size: 14px; min-height: 300px; display: block;"><?php echo esc_textarea(wp_unslash(igny8_get_ai_setting('clustering_prompt', igny8_get_default_clustering_prompt()))); ?></textarea>
<div class="igny8-form-actions" style="margin-top: 15px;">
<button type="button" id="igny8-save-clustering-prompt" class="igny8-btn igny8-btn-success">Save Clustering Prompt</button>
<button type="button" id="igny8-reset-clustering-prompt" class="igny8-btn igny8-btn-outline">Reset to Default</button>
</div>
</div>
</div>
<!-- Ideas Generation Prompt Card -->
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h4>Ideas Generation Prompt</h4>
<p class="igny8-card-subtitle">Generate content ideas from clusters</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-lightbulb igny8-dashboard-icon-lg igny8-dashboard-icon-orange"></span>
</div>
</div>
</div>
<div class="igny8-card-body" style="text-align: center;">
<p class="description">This prompt generates content ideas from clusters. Use [IGNY8_CLUSTERS] to inject cluster data.</p>
<textarea name="igny8_ideas_prompt" rows="8" class="igny8-textarea-orange" style="width: 90%; margin: 0 auto; font-family: monospace; font-size: 14px; min-height: 300px; display: block;"><?php echo esc_textarea(wp_unslash(igny8_get_ai_setting('ideas_prompt', igny8_get_default_ideas_prompt()))); ?></textarea>
<div class="igny8-form-actions" style="margin-top: 15px;">
<button type="button" id="igny8-save-ideas-prompt" class="igny8-btn igny8-btn-success">Save Ideas Prompt</button>
<button type="button" id="igny8-reset-ideas-prompt" class="igny8-btn igny8-btn-outline">Reset to Default</button>
</div>
</div>
</div>
<?php else: ?>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-info-box">
<p><strong>Note:</strong> Planner prompts are only available when AI-Powered SEO Mode is enabled in the Planner module.</p>
<a href="<?php echo admin_url('admin.php?page=igny8-planner'); ?>" class="igny8-btn igny8-btn-primary">Go to Planner Settings</a>
</div>
</div>
</div>
<?php endif; ?>
</div>
<!-- Writer Prompts Section -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Writer Prompts</h3>
<p class="igny8-card-subtitle">Configure AI prompt templates for content writing</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-edit-page igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<?php if (igny8_get_ai_setting('writer_mode', 'manual') === 'ai'): ?>
<!-- Content Generation Prompt Card -->
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h4>Content Generation Prompt</h4>
<p class="igny8-card-subtitle">Generate content from ideas</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-edit-page igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<div class="igny8-card-body" style="text-align: center;">
<!-- Content Generation Prompt -->
<p class="description">This prompt is used to generate content from ideas. Use [IGNY8_IDEA] to inject idea data, [IGNY8_CLUSTER] for cluster data, and [IGNY8_KEYWORDS] for keywords.</p>
<textarea name="igny8_content_generation_prompt" rows="8" class="igny8-textarea-blue" style="width: 90%; margin: 0 auto; font-family: monospace; font-size: 14px; min-height: 300px; display: block;"><?php echo esc_textarea(wp_unslash(igny8_get_ai_setting('content_generation_prompt', igny8_content_generation_prompt()))); ?></textarea>
<div class="igny8-form-actions" style="margin-top: 15px;">
<button type="button" id="igny8-save-content-generation-prompt" class="igny8-btn igny8-btn-success">Save Content Generation Prompt</button>
<button type="button" id="igny8-reset-content-generation-prompt" class="igny8-btn igny8-btn-outline">Reset to Default</button>
</div>
</div>
</div>
<?php else: ?>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-info-box">
<p><strong>Note:</strong> Writer prompts are only available when AI-Powered Mode is enabled in the Writer module.</p>
<a href="<?php echo admin_url('admin.php?page=igny8-writer'); ?>" class="igny8-btn igny8-btn-primary">Go to Writer Settings</a>
</div>
</div>
</div>
<?php endif; ?>
</div>
<!-- Image Generation Section -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Image Generation</h3>
<p class="igny8-card-subtitle">Test and configure AI image generation with DALL·E 3 and Runware</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-format-image igny8-dashboard-icon-lg igny8-dashboard-icon-purple"></span>
</div>
</div>
</div>
<div class="">
<div class="igny8-card igny8-prompt-section"> <!-- Image Generation Form -->
<div class="igny8-dashboard-section">
<!-- Prompt Settings -->
<div class="igny8-card-body">
<form method="POST">
<?php wp_nonce_field('save_prompt', 'save_prompt_nonce'); ?>
<div class="igny8-form-group">
<label for="prompt_template">Prompt Template</label>
<textarea id="prompt_template" name="prompt_template" rows="8" class="igny8-form-control" placeholder="Enter your custom prompt template..."><?php echo esc_textarea(wp_unslash(get_option('igny8_image_prompt_template', 'Create a high-quality {image_type} image to use as a featured photo for a blog post titled "{post_title}". The image should visually represent the theme, mood, and subject implied by the image prompt: {image_prompt}. Focus on a realistic, well-composed scene that naturally communicates the topic without text or logos. Use balanced lighting, pleasing composition, and photographic detail suitable for lifestyle or editorial web content. Avoid adding any visible or readable text, brand names, or illustrative effects. **And make sure image is not blurry.**'))); ?></textarea>
<small class="form-help">
<strong>Available placeholders:</strong><br>
<code>{post_title}</code> - Post title<br>
<code>{image_prompt}</code> - Image prompt (featured or in-article based on request)<br>
<code>{image_type}</code> - Selected image type (from settings)
</small>
<div class="igny8-form-actions" style="margin-top: 10px;">
<button type="button" id="igny8-save-image-prompt-template" class="igny8-btn igny8-btn-success">Save Prompt Template</button>
<button type="button" id="igny8-reset-image-prompt-template" class="igny8-btn igny8-btn-outline">Reset to Default</button>
</div>
</div>
<div class="igny8-form-group">
<label for="negative_prompt">Negative Prompt</label>
<textarea id="negative_prompt" name="negative_prompt" rows="2" class="igny8-form-control" placeholder="Describe what you DON'T want in the image..."><?php echo esc_textarea(wp_unslash(get_option('igny8_negative_prompt', 'text, watermark, logo, overlay, title, caption, writing on walls, writing on objects, UI, infographic elements, post title'))); ?></textarea>
<small class="form-help">Specify elements to avoid in the generated image (text, watermarks, logos, etc.).</small>
<div class="igny8-form-actions" style="margin-top: 10px;">
<button type="button" id="igny8-save-negative-prompt" class="igny8-btn igny8-btn-success">Save Negative Prompt</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<?php
// Localize script for prompts page
wp_localize_script('igny8-admin-js', 'IGNY8_PAGE', [
'module' => 'thinker',
'subpage' => 'prompts',
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('igny8_thinker_settings')
]);
// Add JavaScript for prompt management
?>
<script type="text/javascript">
jQuery(document).ready(function($) {
// Save prompts
$('#igny8-save-clustering-prompt').on('click', function() {
savePrompt('clustering_prompt', $('textarea[name="igny8_clustering_prompt"]').val());
});
$('#igny8-save-ideas-prompt').on('click', function() {
savePrompt('ideas_prompt', $('textarea[name="igny8_ideas_prompt"]').val());
});
$('#igny8-save-content-generation-prompt').on('click', function() {
// Save content generation prompt
savePrompt('content_generation_prompt', $('textarea[name="igny8_content_generation_prompt"]').val());
});
// Reset prompts
$('#igny8-reset-clustering-prompt').on('click', function() {
resetPrompt('clustering_prompt');
});
$('#igny8-reset-ideas-prompt').on('click', function() {
resetPrompt('ideas_prompt');
});
$('#igny8-reset-content-generation-prompt').on('click', function() {
// Reset content generation prompt to default
if (confirm('Are you sure you want to reset the content generation prompt to default? This will overwrite any custom changes.')) {
$.ajax({
url: IGNY8_PAGE.ajaxUrl,
type: 'POST',
data: {
action: 'igny8_reset_multiple_prompts',
prompt_types: ['content_generation_prompt'],
nonce: IGNY8_PAGE.nonce
},
success: function(response) {
if (response.success) {
if (typeof igny8ShowNotification === 'function') {
igny8ShowNotification('Content generation prompt reset to default successfully!', 'success', 'thinker');
}
// Update textarea with reset value
if (response.data && response.data.content_generation_prompt) {
$('textarea[name="igny8_content_generation_prompt"]').val(response.data.content_generation_prompt);
}
} else {
if (typeof igny8ShowNotification === 'function') {
igny8ShowNotification('Error resetting prompts: ' + (response.data || 'Unknown error'), 'error', 'thinker');
}
}
},
error: function() {
if (typeof igny8ShowNotification === 'function') {
igny8ShowNotification('Error resetting prompts. Please try again.', 'error', 'thinker');
}
}
});
}
});
// Reset image prompt template
$('#igny8-reset-image-prompt-template').on('click', function() {
resetImagePromptTemplate();
});
// Save image prompt template
$('#igny8-save-image-prompt-template').on('click', function() {
saveImagePromptTemplate();
});
// Save negative prompt
$('#igny8-save-negative-prompt').on('click', function() {
saveNegativePrompt();
});
function savePrompt(promptType, promptValue) {
$.ajax({
url: IGNY8_PAGE.ajaxUrl,
type: 'POST',
data: {
action: 'igny8_save_prompt',
prompt_type: promptType,
prompt_value: promptValue,
nonce: IGNY8_PAGE.nonce
},
success: function(response) {
if (response.success) {
// Show specific prompt name in notification
var promptName = promptType.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase());
if (typeof igny8ShowNotification === 'function') {
igny8ShowNotification(promptName + ' saved successfully!', 'success', 'thinker');
}
} else {
if (typeof igny8ShowNotification === 'function') {
igny8ShowNotification('Error saving prompt: ' + response.data, 'error', 'thinker');
}
}
},
error: function() {
if (typeof igny8ShowNotification === 'function') {
igny8ShowNotification('Error saving prompt', 'error', 'thinker');
}
}
});
}
function resetPrompt(promptType) {
if (confirm('Are you sure you want to reset this prompt to default? This will overwrite any custom changes.')) {
$.ajax({
url: IGNY8_PAGE.ajaxUrl,
type: 'POST',
data: {
action: 'igny8_reset_prompt',
prompt_type: promptType,
nonce: IGNY8_PAGE.nonce
},
success: function(response) {
if (response.success) {
// Show specific prompt name in notification
var promptName = promptType.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase());
if (typeof igny8ShowNotification === 'function') {
igny8ShowNotification(promptName + ' reset to default successfully!', 'success', 'thinker');
}
// Update the textarea with the reset value from response
if (response.data && response.data.prompt_value) {
$('textarea[name="igny8_' + promptType + '"]').val(response.data.prompt_value);
}
} else {
if (typeof igny8ShowNotification === 'function') {
igny8ShowNotification('Error resetting prompt: ' + response.data, 'error', 'thinker');
}
}
},
error: function() {
if (typeof igny8ShowNotification === 'function') {
igny8ShowNotification('Error resetting prompt', 'error', 'thinker');
}
}
});
}
}
function saveImagePromptTemplate() {
const promptTemplate = $('#prompt_template').val();
$.ajax({
url: IGNY8_PAGE.ajaxUrl,
type: 'POST',
data: {
action: 'igny8_save_prompt',
prompt_type: 'image_prompt_template',
prompt_value: promptTemplate,
nonce: IGNY8_PAGE.nonce
},
success: function(response) {
if (response.success) {
if (typeof igny8ShowNotification === 'function') {
igny8ShowNotification('Image prompt template saved successfully!', 'success', 'thinker');
}
} else {
if (typeof igny8ShowNotification === 'function') {
igny8ShowNotification('Error saving image prompt template: ' + response.data, 'error', 'thinker');
}
}
},
error: function() {
if (typeof igny8ShowNotification === 'function') {
igny8ShowNotification('Error saving image prompt template', 'error', 'thinker');
}
}
});
}
function saveNegativePrompt() {
const negativePrompt = $('#negative_prompt').val();
$.ajax({
url: IGNY8_PAGE.ajaxUrl,
type: 'POST',
data: {
action: 'igny8_save_prompt',
prompt_type: 'negative_prompt',
prompt_value: negativePrompt,
nonce: IGNY8_PAGE.nonce
},
success: function(response) {
if (response.success) {
if (typeof igny8ShowNotification === 'function') {
igny8ShowNotification('Negative prompt saved successfully!', 'success', 'thinker');
}
} else {
if (typeof igny8ShowNotification === 'function') {
igny8ShowNotification('Error saving negative prompt: ' + response.data, 'error', 'thinker');
}
}
},
error: function() {
if (typeof igny8ShowNotification === 'function') {
igny8ShowNotification('Error saving negative prompt', 'error', 'thinker');
}
}
});
}
function resetImagePromptTemplate() {
if (confirm('Are you sure you want to reset the image prompt template to default? This will overwrite any custom changes.')) {
$.ajax({
url: IGNY8_PAGE.ajaxUrl,
type: 'POST',
data: {
action: 'igny8_reset_image_prompt_template',
nonce: IGNY8_PAGE.nonce
},
success: function(response) {
if (response.success) {
if (typeof igny8ShowNotification === 'function') {
igny8ShowNotification('Image Prompt Template reset to default successfully!', 'success', 'thinker');
}
// Update the textarea with the reset value from response
if (response.data && response.data.prompt_value) {
$('#prompt_template').val(response.data.prompt_value);
}
} else {
if (typeof igny8ShowNotification === 'function') {
igny8ShowNotification('Error resetting image prompt template: ' + response.data, 'error', 'thinker');
}
}
},
error: function() {
if (typeof igny8ShowNotification === 'function') {
igny8ShowNotification('Error resetting image prompt template', 'error', 'thinker');
}
}
});
}
}
});
</script>
// Global notification system is handled by core.js

View File

@@ -0,0 +1,408 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : strategies.php
* @location : /modules/thinker/strategies.php
* @type : Admin Page
* @scope : Module Only
* @allowed : Content strategy management, AI generation patterns, strategic approaches
* @reusability : Single Use
* @notes : Content strategy management interface for thinker module
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
?>
<div class="igny8-module-page">
<div class="igny8-container">
<!-- Page title removed - titles should not appear inside page content -->
<!-- Strategy Templates Section -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Strategy Templates</h3>
<p class="igny8-card-subtitle">Pre-built content strategies for different use cases</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-page igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<div class="igny8-grid-4">
<!-- Blog Post Strategy -->
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h4>Blog Post Strategy</h4>
<p class="igny8-card-subtitle">Comprehensive blog post creation with SEO optimization</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-edit-page igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<p>Comprehensive blog post creation with SEO optimization, internal linking, and engagement elements.</p>
<div class="igny8-strategy-features">
<span class="igny8-badge igny8-badge-info">SEO Optimized</span>
<span class="igny8-badge igny8-badge-info">Internal Links</span>
<span class="igny8-badge igny8-badge-info">CTA Included</span>
</div>
<div class="igny8-form-actions">
<button type="button" class="igny8-btn igny8-btn-sm igny8-btn-primary" onclick="igny8EditStrategy('blog_post')">
<span class="dashicons dashicons-edit"></span>
Edit
</button>
<button type="button" class="igny8-btn igny8-btn-sm igny8-btn-outline" onclick="igny8TestStrategy('blog_post')">
<span class="dashicons dashicons-admin-network"></span>
Test
</button>
</div>
</div>
</div>
<!-- Product Description Strategy -->
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h4>Product Description Strategy</h4>
<p class="igny8-card-subtitle">E-commerce focused product descriptions with conversion optimization</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-cart igny8-dashboard-icon-lg igny8-dashboard-icon-orange"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<p>E-commerce focused product descriptions with conversion optimization and feature highlighting.</p>
<div class="igny8-strategy-features">
<span class="igny8-badge igny8-badge-warning">Conversion Focused</span>
<span class="igny8-badge igny8-badge-warning">Feature Rich</span>
<span class="igny8-badge igny8-badge-warning">Benefit Driven</span>
</div>
<div class="igny8-form-actions">
<button type="button" class="igny8-btn igny8-btn-sm igny8-btn-primary" onclick="igny8EditStrategy('product_description')">
<span class="dashicons dashicons-edit"></span>
Edit
</button>
<button type="button" class="igny8-btn igny8-btn-sm igny8-btn-outline" onclick="igny8TestStrategy('product_description')">
<span class="dashicons dashicons-admin-network"></span>
Test
</button>
</div>
</div>
</div>
<!-- Landing Page Strategy -->
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h4>Landing Page Strategy</h4>
<p class="igny8-card-subtitle">High-converting landing pages with persuasive copy and social proof</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-home igny8-dashboard-icon-lg igny8-dashboard-icon-purple"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<p>High-converting landing pages with persuasive copy, social proof, and clear value propositions.</p>
<div class="igny8-strategy-features">
<span class="igny8-badge igny8-badge-success">Persuasive Copy</span>
<span class="igny8-badge igny8-badge-success">Social Proof</span>
<span class="igny8-badge igny8-badge-success">Clear CTA</span>
</div>
<div class="igny8-form-actions">
<button type="button" class="igny8-btn igny8-btn-sm igny8-btn-primary" onclick="igny8EditStrategy('landing_page')">
<span class="dashicons dashicons-edit"></span>
Edit
</button>
<button type="button" class="igny8-btn igny8-btn-sm igny8-btn-outline" onclick="igny8TestStrategy('landing_page')">
<span class="dashicons dashicons-admin-network"></span>
Test
</button>
</div>
</div>
</div>
<!-- Email Campaign Strategy -->
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h4>Email Campaign Strategy</h4>
<p class="igny8-card-subtitle">Engaging email sequences with personalization and automation</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-email igny8-dashboard-icon-lg igny8-dashboard-icon-teal"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<p>Engaging email sequences with personalization, segmentation, and automated follow-ups.</p>
<div class="igny8-strategy-features">
<span class="igny8-badge igny8-badge-info">Personalized</span>
<span class="igny8-badge igny8-badge-info">Segmented</span>
<span class="igny8-badge igny8-badge-info">Automated</span>
</div>
<div class="igny8-form-actions">
<button type="button" class="igny8-btn igny8-btn-sm igny8-btn-primary" onclick="igny8EditStrategy('email_campaign')">
<span class="dashicons dashicons-edit"></span>
Edit
</button>
<button type="button" class="igny8-btn igny8-btn-sm igny8-btn-outline" onclick="igny8TestStrategy('email_campaign')">
<span class="dashicons dashicons-admin-network"></span>
Test
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Custom Strategy Builder -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Custom Strategy Builder</h3>
<p class="igny8-card-subtitle">Create custom content strategies tailored to your needs</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-plus-alt igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<form id="igny8-custom-strategy-form">
<div class="igny8-form-row" style="display: flex; gap: 20px; margin-bottom: 20px;">
<div class="igny8-form-group" style="flex: 1;">
<label for="strategy_name" class="igny8-field-label">Strategy Name</label>
<input type="text" name="strategy_name" id="strategy_name" class="igny8-input" style="width: 100%;" placeholder="Enter strategy name...">
<small class="igny8-field-description">Choose a descriptive name for your strategy</small>
</div>
<div class="igny8-form-group" style="flex: 1;">
<label for="strategy_type" class="igny8-field-label">Strategy Type</label>
<select name="strategy_type" id="strategy_type" class="igny8-select" style="width: 100%;">
<option value="content">Content Creation</option>
<option value="marketing">Marketing Campaign</option>
<option value="seo">SEO Strategy</option>
<option value="social">Social Media</option>
<option value="email">Email Marketing</option>
</select>
<small class="igny8-field-description">Select the category for your strategy</small>
</div>
</div>
<div class="igny8-form-group" style="margin-bottom: 20px;">
<label for="strategy_description" class="igny8-field-label">Strategy Description</label>
<textarea name="strategy_description" id="strategy_description" rows="4" class="igny8-textarea igny8-textarea-blue" style="width: 100%;" placeholder="Describe the purpose and goals of this strategy..."></textarea>
<small class="igny8-field-description">Provide a clear description of what this strategy aims to achieve</small>
</div>
<div class="igny8-form-group" style="margin-bottom: 20px;">
<label for="strategy_goals" class="igny8-field-label">Goals & Objectives</label>
<textarea name="strategy_goals" id="strategy_goals" rows="3" class="igny8-textarea igny8-textarea-green" style="width: 100%;" placeholder="List the specific goals and objectives for this strategy..."></textarea>
<small class="igny8-field-description">Define the specific goals and measurable objectives</small>
</div>
<div class="igny8-form-group" style="margin-bottom: 20px;">
<label for="strategy_instructions" class="igny8-field-label">AI Instructions</label>
<textarea name="strategy_instructions" id="strategy_instructions" rows="6" class="igny8-textarea igny8-textarea-purple" style="width: 100%;" placeholder="Provide specific instructions for how the AI should approach this strategy..."></textarea>
<small class="igny8-field-description">Detailed instructions for AI behavior and approach</small>
</div>
<div class="igny8-form-actions">
<button type="button" id="igny8-save-strategy" class="igny8-btn igny8-btn-success">
<span class="dashicons dashicons-yes"></span>
Save Strategy
</button>
<button type="button" id="igny8-test-strategy" class="igny8-btn igny8-btn-primary">
<span class="dashicons dashicons-admin-network"></span>
Test Strategy
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Strategy Performance -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Strategy Performance</h3>
<p class="igny8-card-subtitle">Track the performance and success of your strategies</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-chart-bar igny8-dashboard-icon-lg igny8-dashboard-icon-purple"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-performance-grid">
<div class="igny8-performance-item">
<div class="igny8-performance-value"><?php echo esc_html(igny8_get_ai_setting('strategies_created', 0)); ?></div>
<div class="igny8-performance-label">Strategies Created</div>
</div>
<div class="igny8-performance-item">
<div class="igny8-performance-value"><?php echo esc_html(igny8_get_ai_setting('strategy_success_rate', '0%')); ?></div>
<div class="igny8-performance-label">Success Rate</div>
</div>
<div class="igny8-performance-item">
<div class="igny8-performance-value"><?php echo esc_html(igny8_get_ai_setting('content_generated', 0)); ?></div>
<div class="igny8-performance-label">Content Generated</div>
</div>
<div class="igny8-performance-item">
<div class="igny8-performance-value"><?php echo esc_html(igny8_get_ai_setting('avg_engagement', '0%')); ?></div>
<div class="igny8-performance-label">Avg Engagement</div>
</div>
</div>
</div>
</div>
</div>
<!-- Recent Strategy Activity -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Recent Strategy Activity</h3>
<p class="igny8-card-subtitle">Monitor recent strategy usage and activity</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-clock igny8-dashboard-icon-lg igny8-dashboard-icon-orange"></span>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-card-body">
<div class="igny8-activity-list">
<div class="igny8-empty-state">
<p>No recent strategy activity. Create and use strategies to see activity here.</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
jQuery(document).ready(function($) {
// Edit Strategy
window.igny8EditStrategy = function(strategyType) {
// Open strategy editor modal or redirect to edit page
igny8ShowNotification('Opening strategy editor for ' + strategyType, 'info', 'thinker');
};
// Test Strategy
window.igny8TestStrategy = function(strategyType) {
$.ajax({
url: ajaxurl,
type: 'POST',
data: {
action: 'igny8_test_strategy',
strategy_type: strategyType,
nonce: '<?php echo wp_create_nonce('igny8_thinker_strategies'); ?>'
},
success: function(response) {
if (response.success) {
igny8ShowNotification('Strategy test successful for ' + strategyType, 'success', 'thinker');
} else {
igny8ShowNotification('Strategy test failed: ' + response.data, 'error', 'thinker');
}
},
error: function() {
igny8ShowNotification('Error testing strategy', 'error', 'thinker');
}
});
};
// Save Custom Strategy
$('#igny8-save-strategy').on('click', function() {
var formData = {
action: 'igny8_save_custom_strategy',
nonce: '<?php echo wp_create_nonce('igny8_thinker_strategies'); ?>',
strategy_name: $('#strategy_name').val(),
strategy_type: $('#strategy_type').val(),
strategy_description: $('#strategy_description').val(),
strategy_goals: $('#strategy_goals').val(),
strategy_instructions: $('#strategy_instructions').val()
};
if (!formData.strategy_name || !formData.strategy_description) {
igny8ShowNotification('Please fill in required fields', 'warning', 'thinker');
return;
}
$.ajax({
url: ajaxurl,
type: 'POST',
data: formData,
success: function(response) {
if (response.success) {
igny8ShowNotification('Custom strategy saved successfully!', 'success', 'thinker');
$('#igny8-custom-strategy-form')[0].reset();
} else {
igny8ShowNotification('Error saving strategy: ' + response.data, 'error', 'thinker');
}
},
error: function() {
igny8ShowNotification('Error saving strategy', 'error', 'thinker');
}
});
});
// Test Custom Strategy
$('#igny8-test-strategy').on('click', function() {
var formData = {
action: 'igny8_test_custom_strategy',
nonce: '<?php echo wp_create_nonce('igny8_thinker_strategies'); ?>',
strategy_name: $('#strategy_name').val(),
strategy_type: $('#strategy_type').val(),
strategy_description: $('#strategy_description').val(),
strategy_goals: $('#strategy_goals').val(),
strategy_instructions: $('#strategy_instructions').val()
};
if (!formData.strategy_name || !formData.strategy_description) {
igny8ShowNotification('Please fill in required fields before testing', 'warning', 'thinker');
return;
}
$.ajax({
url: ajaxurl,
type: 'POST',
data: formData,
success: function(response) {
if (response.success) {
igny8ShowNotification('Custom strategy test successful!', 'success', 'thinker');
} else {
igny8ShowNotification('Custom strategy test failed: ' + response.data, 'error', 'thinker');
}
},
error: function() {
igny8ShowNotification('Error testing strategy', 'error', 'thinker');
}
});
});
});
</script>
<!-- Notification Area -->
<!-- Global notification system is handled by core.js -->
<?php

View File

@@ -0,0 +1,224 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : thinker.php
* @location : /modules/thinker/thinker.php
* @type : Admin Page
* @scope : Module Only
* @allowed : Thinker module logic, subpage routing, AI interface
* @reusability : Single Use
* @notes : Main thinker page with subpage routing for AI features
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Handle URL parameters for subpages
$subpage = $_GET['sp'] ?? 'main';
$GLOBALS['igny8_current_subpage'] = $subpage;
// Start output buffering
ob_start();
switch ($subpage) {
case 'prompts':
include plugin_dir_path(__FILE__) . 'prompts.php';
break;
case 'profile':
include plugin_dir_path(__FILE__) . 'profile.php';
break;
case 'strategies':
include plugin_dir_path(__FILE__) . 'strategies.php';
break;
case 'image-testing':
include plugin_dir_path(__FILE__) . 'image-testing.php';
break;
case 'main':
default:
// Main dashboard content
?>
<div class="igny8-module-page">
<div class="igny8-container">
<!-- Thinker Workflow Steps -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Thinker Workflow Steps</h3>
<p class="igny8-card-subtitle">Track your AI configuration progress</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-tools igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
</div>
<div class="igny8-grid igny8-grid-4">
<!-- AI Prompts Step -->
<div class="igny8-card igny8-step-card completed">
<div class="igny8-card-body">
<div class="igny8-step-header">
<div class="igny8-step-number">1</div>
<div class="igny8-step-title">AI Prompts</div>
</div>
<div class="igny8-step-status">
<span class="igny8-step-status-icon">✅</span>
<span class="igny8-step-status-text">Configured</span>
</div>
<div class="igny8-step-data">
Manage AI prompt templates
</div>
<div class="igny8-step-action">
<a href="?page=igny8-thinker&sp=prompts" class="igny8-btn igny8-btn-primary igny8-btn-sm">Configure</a>
</div>
</div>
</div>
<!-- AI Profile Step -->
<div class="igny8-card igny8-step-card current">
<div class="igny8-card-body">
<div class="igny8-step-header">
<div class="igny8-step-number">2</div>
<div class="igny8-step-title">AI Profile</div>
</div>
<div class="igny8-step-status">
<span class="igny8-step-status-icon">⏳</span>
<span class="igny8-step-status-text">Ready</span>
</div>
<div class="igny8-step-data">
Set AI behavior and tone
</div>
<div class="igny8-step-action">
<a href="?page=igny8-thinker&sp=profile" class="igny8-btn igny8-btn-primary igny8-btn-sm">Configure</a>
</div>
</div>
</div>
<!-- Content Strategies Step -->
<div class="igny8-card igny8-step-card pending">
<div class="igny8-card-body">
<div class="igny8-step-header">
<div class="igny8-step-number">3</div>
<div class="igny8-step-title">Content Strategies</div>
</div>
<div class="igny8-step-status">
<span class="igny8-step-status-icon">⏳</span>
<span class="igny8-step-status-text">Pending</span>
</div>
<div class="igny8-step-data">
Define content strategies
</div>
<div class="igny8-step-action">
<a href="?page=igny8-thinker&sp=strategies" class="igny8-btn igny8-btn-primary igny8-btn-sm">Configure</a>
</div>
</div>
</div>
<!-- Image Testing Step -->
<div class="igny8-card igny8-step-card pending">
<div class="igny8-card-body">
<div class="igny8-step-header">
<div class="igny8-step-number">4</div>
<div class="igny8-step-title">Image Testing</div>
</div>
<div class="igny8-step-status">
<span class="igny8-step-status-icon">⏳</span>
<span class="igny8-step-status-text">Pending</span>
</div>
<div class="igny8-step-data">
Test DALL·E 3 integration
</div>
<div class="igny8-step-action">
<a href="?page=igny8-thinker&sp=image-testing" class="igny8-btn igny8-btn-primary igny8-btn-sm">Test</a>
</div>
</div>
</div>
</div>
</div>
<!-- AI Settings & Quick Actions -->
<div class="igny8-dashboard-section">
<div class="igny8-card">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>AI Settings & Quick Actions</h3>
<p class="igny8-card-subtitle">Manage AI configuration and test connections</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-admin-settings igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<div class="igny8-flex-row">
<button onclick="igny8TestAIConnection()" class="igny8-btn igny8-btn-primary">Test AI Connection</button>
<button onclick="igny8ResetAISettings()" class="igny8-btn igny8-btn-outline">Reset AI Settings</button>
<button onclick="igny8ViewAILogs()" class="igny8-btn igny8-btn-outline">View AI Logs</button>
</div>
</div>
</div>
</div>
<script type="text/javascript">
jQuery(document).ready(function($) {
// Test AI Connection
window.igny8TestAIConnection = function() {
// Check if API key is configured
const apiKey = '<?php echo get_option('igny8_api_key', ''); ?>';
if (!apiKey) {
igny8ShowNotification('Please configure your OpenAI API key in Settings first.', 'warning', 'thinker');
return;
}
// Show testing notification
igny8ShowNotification('Testing AI connection...', 'info', 'thinker');
// Simulate connection test (replace with actual API test)
setTimeout(() => {
igny8ShowNotification('AI connection test completed successfully!', 'success', 'thinker');
}, 2000);
};
// Execute Action based on selection
window.igny8ExecuteAction = function() {
const selectedAction = $('input[name="ai_action"]:checked').val();
if (selectedAction === 'reset') {
if (confirm('Are you sure you want to reset all AI settings to default? This will affect all modules.')) {
// Simulate reset action
igny8ShowNotification('AI settings have been reset to default values.', 'success', 'thinker');
}
} else if (selectedAction === 'logs') {
window.open('<?php echo admin_url('admin.php?page=igny8-analytics&sp=status'); ?>', '_blank');
}
};
// Reset AI Settings (legacy function)
window.igny8ResetAISettings = function() {
if (confirm('Are you sure you want to reset all AI settings to default? This will affect all modules.')) {
igny8ShowNotification('AI settings reset feature coming soon!', 'info', 'thinker');
}
};
// View AI Logs (legacy function)
window.igny8ViewAILogs = function() {
window.open('<?php echo admin_url('admin.php?page=igny8-analytics&sp=status'); ?>', '_blank');
};
});
</script>
</div>
</div>
<?php
break;
}
// Capture page content
$igny8_page_content = ob_get_clean();
// Include global layout
include_once plugin_dir_path(__FILE__) . '../../core/global-layout.php';

View File

@@ -0,0 +1,90 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : drafts.php
* @location : modules/writer/
* @type : Drafts Management Page
* @scope : Writer Module
* @allowed : Draft review, content editing, and approval workflows
* @reusability : Writer Module
* @notes : Configuration-driven drafts management interface using complete component system
*
* @package Igny8Compact
* @since 1.0.0
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Set module variables
$GLOBALS['current_submodule'] = 'drafts';
$GLOBALS['current_module'] = 'writer';
// Load all required components
require_once plugin_dir_path(__FILE__) . '../../modules/components/filters-tpl.php';
require_once plugin_dir_path(__FILE__) . '../../modules/components/actions-tpl.php';
require_once plugin_dir_path(__FILE__) . '../../modules/components/table-tpl.php';
require_once plugin_dir_path(__FILE__) . '../../modules/components/pagination-tpl.php';
// Set table ID for components
$table_id = 'writer_drafts';
?>
<div class="igny8-module-page">
<div class="igny8-container">
<!-- Drafts Review Section -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Content Drafts</h3>
<p class="igny8-card-subtitle">Review and manage your content drafts</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-edit-page igny8-dashboard-icon-lg igny8-dashboard-icon-amber"></span>
</div>
</div>
</div>
<!-- Complete Submodule Interface -->
<?php
// Render all components in correct order
echo igny8_render_filters($table_id);
echo igny8_render_table_actions($table_id);
echo igny8_render_table($table_id);
echo igny8_render_pagination($table_id);
// Get cluster options for dropdowns
$cluster_options = igny8_get_cluster_options();
// Load filters configuration for JavaScript
$filters_config = require plugin_dir_path(__FILE__) . '../../modules/config/filters-config.php';
// Localize script for table initialization
wp_localize_script('igny8-admin-js', 'IGNY8_PAGE', [
'tableId' => $table_id,
'module' => 'writer',
'submodule' => 'drafts',
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('igny8_writer_settings'),
'parseNonce' => wp_create_nonce('igny8_parse_content'),
'defaultPerPage' => get_option('igny8_records_per_page', 20),
'clusterOptions' => $cluster_options,
'filtersConfig' => $filters_config,
'cronKey' => igny8_needs_cron_functionality() ? igny8_get_or_generate_cron_key() : null,
'imageSettings' => [
'desktop_enabled' => get_option('igny8_desktop_enabled', '1') === '1',
'mobile_enabled' => get_option('igny8_mobile_enabled', '0') === '1',
'max_in_article_images' => intval(get_option('igny8_max_in_article_images', 1))
]
]);
?>
</div>
</div>
</div>

View File

@@ -0,0 +1,90 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : published.php
* @location : modules/writer/
* @type : Published Content Management Page
* @scope : Writer Module
* @allowed : Published content overview, performance metrics, and content management
* @reusability : Writer Module
* @notes : Configuration-driven published content management interface using complete component system
*
* @package Igny8Compact
* @since 1.0.0
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Set module variables
$GLOBALS['current_submodule'] = 'published';
$GLOBALS['current_module'] = 'writer';
// Load all required components
require_once plugin_dir_path(__FILE__) . '../../modules/components/filters-tpl.php';
require_once plugin_dir_path(__FILE__) . '../../modules/components/actions-tpl.php';
require_once plugin_dir_path(__FILE__) . '../../modules/components/table-tpl.php';
require_once plugin_dir_path(__FILE__) . '../../modules/components/pagination-tpl.php';
// Set table ID for components
$table_id = 'writer_published';
?>
<div class="igny8-module-page">
<div class="igny8-container">
<!-- Published Content Section -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Published Content</h3>
<p class="igny8-card-subtitle">View and manage your published content</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-yes-alt igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
</div>
<!-- Complete Submodule Interface -->
<?php
// Render all components in correct order
echo igny8_render_filters($table_id);
echo igny8_render_table_actions($table_id);
echo igny8_render_table($table_id);
echo igny8_render_pagination($table_id);
// Get cluster options for dropdowns
$cluster_options = igny8_get_cluster_options();
// Load filters configuration for JavaScript
$filters_config = require plugin_dir_path(__FILE__) . '../../modules/config/filters-config.php';
// Localize script for table initialization
wp_localize_script('igny8-admin-js', 'IGNY8_PAGE', [
'tableId' => $table_id,
'module' => 'writer',
'submodule' => 'published',
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('igny8_writer_settings'),
'parseNonce' => wp_create_nonce('igny8_parse_content'),
'defaultPerPage' => get_option('igny8_records_per_page', 20),
'clusterOptions' => $cluster_options,
'filtersConfig' => $filters_config,
'cronKey' => igny8_needs_cron_functionality() ? igny8_get_or_generate_cron_key() : null,
'imageSettings' => [
'desktop_enabled' => get_option('igny8_desktop_enabled', '1') === '1',
'mobile_enabled' => get_option('igny8_mobile_enabled', '0') === '1',
'max_in_article_images' => intval(get_option('igny8_max_in_article_images', 1))
]
]);
?>
</div>
</div>
</div>

View File

@@ -0,0 +1,90 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : tasks.php
* @location : modules/writer/
* @type : Tasks Management Page
* @scope : Writer Module
* @allowed : Writing tasks management, queued content, and task workflow
* @reusability : Writer Module
* @notes : Configuration-driven tasks management interface using complete component system
*
* @package Igny8Compact
* @since 1.0.0
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
// Set module variables
$GLOBALS['current_submodule'] = 'tasks';
$GLOBALS['current_module'] = 'writer';
// Load all required components
require_once plugin_dir_path(__FILE__) . '../../modules/components/filters-tpl.php';
require_once plugin_dir_path(__FILE__) . '../../modules/components/actions-tpl.php';
require_once plugin_dir_path(__FILE__) . '../../modules/components/table-tpl.php';
require_once plugin_dir_path(__FILE__) . '../../modules/components/pagination-tpl.php';
// Set table ID for components
$table_id = 'writer_tasks';
?>
<div class="igny8-module-page">
<div class="igny8-container">
<!-- Tasks Management Section -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Writing Tasks</h3>
<p class="igny8-card-subtitle">Manage your content writing tasks and workflow</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-clipboard igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<!-- Complete Submodule Interface -->
<?php
// Render all components in correct order
echo igny8_render_filters($table_id);
echo igny8_render_table_actions($table_id);
echo igny8_render_table($table_id);
echo igny8_render_pagination($table_id);
// Get cluster options for dropdowns
$cluster_options = igny8_get_cluster_options();
// Load filters configuration for JavaScript
$filters_config = require plugin_dir_path(__FILE__) . '../../modules/config/filters-config.php';
// Localize script for table initialization
wp_localize_script('igny8-admin-js', 'IGNY8_PAGE', [
'tableId' => $table_id,
'module' => 'writer',
'submodule' => 'tasks',
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('igny8_writer_settings'),
'parseNonce' => wp_create_nonce('igny8_parse_content'),
'defaultPerPage' => get_option('igny8_records_per_page', 20),
'clusterOptions' => $cluster_options,
'filtersConfig' => $filters_config,
'cronKey' => igny8_needs_cron_functionality() ? igny8_get_or_generate_cron_key() : null,
'imageSettings' => [
'desktop_enabled' => get_option('igny8_desktop_enabled', '1') === '1',
'mobile_enabled' => get_option('igny8_mobile_enabled', '0') === '1',
'max_in_article_images' => intval(get_option('igny8_max_in_article_images', 1))
]
]);
?>
</div>
</div>
</div>

View File

@@ -0,0 +1,660 @@
<?php
/**
* ==========================
* 🔐 IGNY8 FILE RULE HEADER
* ==========================
* @file : writer.php
* @location : /modules/writer/writer.php
* @type : Admin Page
* @scope : Module Only
* @allowed : Writer module logic, subpage routing, step data functions
* @reusability : Single Use
* @notes : Main writer page with subpage routing and step data functions
*/
if (!defined('ABSPATH')) exit;
// Component render functions are loaded centrally via global-layout.php
/**
* Get step data for Writer workflow guide
*
* @return array Array of step data with status and counts
*/
function igny8_get_writer_step_data() {
global $wpdb;
// Get counts for each step
$queued_tasks = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_tasks WHERE status IN ('pending', 'queued', 'new')");
$draft_tasks = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_tasks WHERE status IN ('draft', 'in_progress', 'review')");
$published_tasks = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_tasks WHERE status = 'completed'");
$total_tasks = $queued_tasks + $draft_tasks + $published_tasks;
return [
'view_tasks' => [
'queued' => $queued_tasks,
'total' => $total_tasks,
'status' => $total_tasks > 0 ? 'completed' : 'pending'
],
'generate_drafts' => [
'queued' => $queued_tasks,
'status' => $queued_tasks > 0 ? 'current' : ($draft_tasks > 0 ? 'completed' : 'pending')
],
'review_drafts' => [
'drafts' => $draft_tasks,
'status' => $draft_tasks > 0 ? 'current' : ($published_tasks > 0 ? 'completed' : 'pending')
],
'publish' => [
'published' => $published_tasks,
'drafts' => $draft_tasks,
'status' => $published_tasks > 0 ? 'completed' : ($draft_tasks > 0 ? 'current' : 'pending')
]
];
}
// Handle URL parameters for subpages
$subpage = $_GET['sm'] ?? 'home';
$GLOBALS['current_submodule'] = $subpage;
$GLOBALS['current_module'] = 'writer';
// Start output buffering
ob_start();
switch ($subpage) {
case 'tasks':
include plugin_dir_path(__FILE__) . 'tasks.php';
break;
case 'drafts':
include plugin_dir_path(__FILE__) . 'drafts.php';
break;
case 'published':
include plugin_dir_path(__FILE__) . 'published.php';
break;
case 'home':
default:
// Home dashboard content
// Load KPI data for dashboard calculations
if (!defined('IGNY8_INCLUDE_CONFIG')) {
define('IGNY8_INCLUDE_CONFIG', true);
}
$kpi_config = $GLOBALS['igny8_kpi_config'] ?? []; // Loaded globally in igny8.php
$kpi_data = igny8_get_kpi_data_safe('writer_home', $kpi_config['writer_home'] ?? []);
// Calculate dashboard metrics - Updated queries for correct status
global $wpdb;
// Queued tasks - tasks that are pending/not started
$queued_tasks = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_tasks WHERE status IN ('pending', 'queued', 'new')");
// Draft tasks - content generated and saved as draft, awaiting review/publish
$draft_tasks = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_tasks WHERE status IN ('draft', 'in_progress', 'review')");
// Published tasks - status is 'completed' in tasks table
$published_tasks = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_tasks WHERE status = 'completed'");
$total_tasks = $queued_tasks + $draft_tasks + $published_tasks;
// Calculate percentages for progress bars
$queued_pct = $total_tasks > 0 ? round(($queued_tasks / $total_tasks) * 100) : 0;
$draft_pct = $total_tasks > 0 ? round(($draft_tasks / $total_tasks) * 100) : 0;
$published_pct = $total_tasks > 0 ? round(($published_tasks / $total_tasks) * 100) : 0;
// Home page content
?>
<div class="igny8-module-home">
<!-- Writing Status Cards -->
<div class="igny8-dashboard-section">
<div class="igny8-grid-3 igny8-status-cards">
<!-- Queued Tasks Card -->
<div class="igny8-card igny8-status-card igny8-clickable-card igny8-status-blue" onclick="window.location.href='<?php echo admin_url('admin.php?page=igny8-writer&sm=tasks'); ?>'">
<div class="igny8-card-body">
<div class="igny8-status-metric">
<span class="igny8-status-count"><?php echo number_format($queued_tasks); ?></span>
<span class="igny8-status-label">Tasks Queued</span>
<span class="igny8-status-desc">awaiting writing start</span>
</div>
<div class="igny8-status-icon">
<span class="dashicons dashicons-clipboard igny8-dashboard-icon-sm"></span>
</div>
</div>
</div>
<!-- Drafts Card -->
<div class="igny8-card igny8-status-card igny8-clickable-card igny8-status-amber" onclick="window.location.href='<?php echo admin_url('admin.php?page=igny8-writer&sm=drafts'); ?>'">
<div class="igny8-card-body">
<div class="igny8-status-metric">
<span class="igny8-status-count"><?php echo number_format($draft_tasks); ?></span>
<span class="igny8-status-label">Drafts</span>
<span class="igny8-status-desc">currently being written</span>
</div>
<div class="igny8-status-icon">
<span class="dashicons dashicons-edit-page igny8-dashboard-icon-sm"></span>
</div>
</div>
</div>
<!-- Published Card -->
<div class="igny8-card igny8-status-card igny8-clickable-card igny8-status-green" onclick="window.location.href='<?php echo admin_url('admin.php?page=igny8-writer&sm=published'); ?>'">
<div class="igny8-card-body">
<div class="igny8-status-metric">
<span class="igny8-status-count"><?php echo number_format($published_tasks); ?></span>
<span class="igny8-status-label">Published</span>
<span class="igny8-status-desc">live on your site</span>
</div>
<div class="igny8-status-icon">
<span class="dashicons dashicons-yes-alt igny8-dashboard-icon-sm"></span>
</div>
</div>
</div>
</div>
</div>
<!-- Workflow Status & AI Settings -->
<!-- Writer Workflow Steps -->
<div class="igny8-dashboard-section">
<div class="igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Writer Workflow Steps</h3>
<p class="igny8-card-subtitle">Track your content creation progress</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-edit-page igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<div class="igny8-grid igny8-grid-4">
<?php
// Step-by-Step UX Guide
$step_data = igny8_get_writer_step_data();
?>
<div class="igny8-card igny8-step-card <?php echo $step_data['view_tasks']['status']; ?>">
<div class="igny8-card-body">
<div class="igny8-step-header">
<div class="igny8-step-number">1</div>
<div class="igny8-step-title">View Queued Tasks</div>
</div>
<div class="igny8-step-status">
<span class="igny8-step-status-icon"><?php echo $step_data['view_tasks']['status'] === 'completed' ? '✅' : '⏳'; ?></span>
<span class="igny8-step-status-text"><?php echo $step_data['view_tasks']['status'] === 'completed' ? 'Available' : 'No Tasks'; ?></span>
</div>
<div class="igny8-step-data">
<?php if ($step_data['view_tasks']['total'] > 0): ?>
<?php echo $step_data['view_tasks']['queued']; ?> tasks queued
<?php else: ?>
No tasks available
<?php endif; ?>
</div>
<?php if ($step_data['view_tasks']['status'] === 'completed'): ?>
<div class="igny8-step-action">
<a href="?page=igny8-writer&sm=tasks" class="igny8-btn igny8-btn-primary igny8-btn-sm">View Tasks</a>
</div>
<?php endif; ?>
</div>
</div>
<div class="igny8-card igny8-step-card <?php echo $step_data['generate_drafts']['status']; ?>">
<div class="igny8-card-body">
<div class="igny8-step-header">
<div class="igny8-step-number">2</div>
<div class="igny8-step-title">Generate Drafts (AI)</div>
</div>
<div class="igny8-step-status">
<span class="igny8-step-status-icon"><?php echo $step_data['generate_drafts']['status'] === 'completed' ? '✅' : ($step_data['generate_drafts']['status'] === 'current' ? '⏳' : '⏳'); ?></span>
<span class="igny8-step-status-text"><?php echo $step_data['generate_drafts']['status'] === 'completed' ? 'Completed' : ($step_data['generate_drafts']['status'] === 'current' ? 'Ready' : 'Pending'); ?></span>
</div>
<div class="igny8-step-data">
<?php if ($step_data['generate_drafts']['queued'] > 0): ?>
<?php echo $step_data['generate_drafts']['queued']; ?> tasks ready for AI
<?php elseif ($step_data['view_tasks']['total'] > 0): ?>
All tasks processed
<?php else: ?>
No tasks to process
<?php endif; ?>
</div>
<?php if ($step_data['generate_drafts']['status'] === 'current'): ?>
<div class="igny8-step-action">
<a href="?page=igny8-writer&sm=tasks" class="igny8-btn igny8-btn-primary igny8-btn-sm">Generate Now</a>
</div>
<?php endif; ?>
</div>
</div>
<div class="igny8-card igny8-step-card <?php echo $step_data['review_drafts']['status']; ?>">
<div class="igny8-card-body">
<div class="igny8-step-header">
<div class="igny8-step-number">3</div>
<div class="igny8-step-title">Review Drafts</div>
</div>
<div class="igny8-step-status">
<span class="igny8-step-status-icon"><?php echo $step_data['review_drafts']['status'] === 'completed' ? '✅' : ($step_data['review_drafts']['status'] === 'current' ? '⏳' : '⏳'); ?></span>
<span class="igny8-step-status-text"><?php echo $step_data['review_drafts']['status'] === 'completed' ? 'Completed' : ($step_data['review_drafts']['status'] === 'current' ? 'Ready' : 'Pending'); ?></span>
</div>
<div class="igny8-step-data">
<?php if ($step_data['review_drafts']['drafts'] > 0): ?>
<?php echo $step_data['review_drafts']['drafts']; ?> drafts to review
<?php elseif ($step_data['view_tasks']['total'] > 0): ?>
All drafts reviewed
<?php else: ?>
No drafts to review
<?php endif; ?>
</div>
<?php if ($step_data['review_drafts']['status'] === 'current'): ?>
<div class="igny8-step-action">
<a href="?page=igny8-writer&sm=drafts" class="igny8-btn igny8-btn-primary igny8-btn-sm">Review Now</a>
</div>
<?php endif; ?>
</div>
</div>
<div class="igny8-card igny8-step-card <?php echo $step_data['publish']['status']; ?>">
<div class="igny8-card-body">
<div class="igny8-step-header">
<div class="igny8-step-number">4</div>
<div class="igny8-step-title">Publish Content</div>
</div>
<div class="igny8-step-status">
<span class="igny8-step-status-icon"><?php echo $step_data['publish']['status'] === 'completed' ? '✅' : ($step_data['publish']['status'] === 'current' ? '⏳' : '⏳'); ?></span>
<span class="igny8-step-status-text"><?php echo $step_data['publish']['status'] === 'completed' ? 'Completed' : ($step_data['publish']['status'] === 'current' ? 'Ready' : 'Pending'); ?></span>
</div>
<div class="igny8-step-data">
<?php if ($step_data['publish']['drafts'] > 0): ?>
<?php echo $step_data['publish']['drafts']; ?> drafts ready to publish
<?php elseif ($step_data['publish']['published'] > 0): ?>
<?php echo $step_data['publish']['published']; ?> content published
<?php else: ?>
No content to publish
<?php endif; ?>
</div>
<?php if ($step_data['publish']['status'] === 'current'): ?>
<div class="igny8-step-action">
<a href="?page=igny8-writer&sm=drafts" class="igny8-btn igny8-btn-primary igny8-btn-sm">Publish Now</a>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
<div class="igny8-flex-row">
<div class="igny8-card">
<div class="igny8-flex-row">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Workflow Status & AI Settings</h3>
<p class="igny8-card-subtitle igny8-centered">Workflow Status & AI Settings</p>
</div>
<form id="igny8-ai-integration-form">
<div class="igny8-flex-row">
<div class="igny8-form-group">
<div class="igny8-mode-toggle-row">
<div class="igny8-mode-toggle-label">
<span class="igny8-mode-label">Manual</span>
<label class="igny8-toggle">
<input type="checkbox" name="igny8_writer_mode" value="ai" <?php checked(igny8_get_ai_setting('writer_mode', 'manual'), 'ai'); ?>>
<span class="igny8-toggle-slider"></span>
</label>
<span class="igny8-mode-label">AI Mode</span>
</div>
</div>
</div>
<div class="igny8-form-actions">
<button type="button" id="igny8-save-writer-ai-settings" class="igny8-btn igny8-btn-success">Save AI Integration Settings</button>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="igny8-card">
<div class="igny8-flex-row">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Workflow Status & AI Settings</h3>
<p class="igny8-card-subtitle igny8-centered">Workflow Status & AI Settings</p>
</div>
<form id="igny8-new-content-form">
<div class="igny8-flex-row">
<div class="igny8-form-group">
<div class="igny8-radio-group">
<label class="igny8-radio-label">
<input type="radio" name="new_content_action" value="draft" <?php checked(get_option('igny8_new_content_action', 'draft'), 'draft'); ?>>
<span class="igny8-radio-text">Save as Draft</span>
</label>
<label class="igny8-radio-label">
<input type="radio" name="new_content_action" value="publish" <?php checked(get_option('igny8_new_content_action', 'draft'), 'publish'); ?>>
<span class="igny8-radio-text">Publish Directly</span>
</label>
</div>
</div>
<div class="igny8-form-actions">
<button type="button" id="igny8-save-content-decision" class="igny8-btn igny8-btn-success">Save Content Decision</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="igny8-grid-3 igny8-equal-height">
<!-- Content Published by Type -->
<div class="igny8-dashboard-section igny8-analytics-card">
<div class="igny8-card">
<div class="igny8-card-header igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Content Published by Type</h3>
<p class="igny8-card-subtitle igny8-centered">Content distribution analysis</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-category igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
<div class="igny8-card-metric">
<?php
global $wpdb;
$total_published = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_tasks WHERE status = 'completed'");
echo '<span class="igny8-metric-value">' . number_format($total_published) . '</span>';
echo '<span class="igny8-metric-label">Total Published</span>';
?>
</div>
</div>
<div class="igny8-card-body">
<?php
$content_by_type = $wpdb->get_results("
SELECT content_type, COUNT(*) as count
FROM {$wpdb->prefix}igny8_tasks
WHERE status = 'completed'
GROUP BY content_type
ORDER BY count DESC
");
if ($content_by_type):
?>
<div class="igny8-analytics-list">
<?php
$color_classes = ['igny8-progress-blue', 'igny8-progress-green', 'igny8-progress-amber', 'igny8-progress-purple'];
$color_index = 0;
foreach ($content_by_type as $type):
$type_display = ucfirst(str_replace('_', ' ', $type->content_type));
$percentage = $total_published > 0 ? round(($type->count / $total_published) * 100) : 0;
$color_class = $color_classes[$color_index % 4];
$color_index++;
?>
<div class="igny8-analytics-item">
<div class="igny8-item-info">
<div class="igny8-item-label"><?php echo esc_html($type_display); ?></div>
<div class="igny8-item-value"><?php echo number_format($type->count); ?></div>
</div>
<div class="igny8-item-progress">
<div class="igny8-progress-track">
<div class="igny8-progress-fill <?php echo $color_class; ?>" style="width: <?php echo $percentage; ?>%"></div>
</div>
<span class="igny8-progress-percent"><?php echo $percentage; ?>%</span>
</div>
</div>
<?php endforeach; ?>
</div>
<?php else: ?>
<div class="igny8-empty-analytics">
<span class="dashicons dashicons-info igny8-dashboard-icon-dim"></span>
<p>No published content found yet</p>
<a href="<?php echo admin_url('admin.php?page=igny8-writer&sm=tasks'); ?>" class="igny8-btn igny8-btn-outline igny8-btn-sm">View Tasks</a>
</div>
<?php endif; ?>
</div>
</div>
</div>
<!-- Publishing Activity -->
<div class="igny8-dashboard-section igny8-analytics-card">
<div class="igny8-card">
<div class="igny8-card-header igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Publishing Activity</h3>
<p class="igny8-card-subtitle igny8-centered">Content publishing timeline</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-chart-line igny8-dashboard-icon-lg igny8-dashboard-icon-green"></span>
</div>
</div>
<div class="igny8-card-metric">
<?php
$recent_7_days = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_tasks WHERE status = 'completed' AND updated_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)");
echo '<span class="igny8-metric-value">' . number_format($recent_7_days) . '</span>';
echo '<span class="igny8-metric-label">This Week</span>';
?>
</div>
</div>
<div class="igny8-card-body">
<?php
$yesterday_count = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_tasks WHERE status = 'completed' AND DATE(updated_at) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)");
$last_7_days = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_tasks WHERE status = 'completed' AND updated_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)");
$last_20_days = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_tasks WHERE status = 'completed' AND updated_at >= DATE_SUB(NOW(), INTERVAL 20 DAY)");
$all_time = $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->prefix}igny8_tasks WHERE status = 'completed'");
?>
<div class="igny8-analytics-list">
<div class="igny8-analytics-item">
<div class="igny8-item-info">
<div class="igny8-item-label">Yesterday</div>
<div class="igny8-item-value"><?php echo number_format($yesterday_count); ?></div>
</div>
<div class="igny8-item-progress">
<div class="igny8-progress-track">
<div class="igny8-progress-fill igny8-progress-blue" style="width: <?php echo $all_time > 0 ? round(($yesterday_count / $all_time) * 100) : 0; ?>%"></div>
</div>
<span class="igny8-progress-percent"><?php echo $all_time > 0 ? round(($yesterday_count / $all_time) * 100) : 0; ?>%</span>
</div>
</div>
<div class="igny8-analytics-item">
<div class="igny8-item-info">
<div class="igny8-item-label">Last 7 Days</div>
<div class="igny8-item-value"><?php echo number_format($last_7_days); ?></div>
</div>
<div class="igny8-item-progress">
<div class="igny8-progress-track">
<div class="igny8-progress-fill igny8-progress-green" style="width: <?php echo $all_time > 0 ? round(($last_7_days / $all_time) * 100) : 0; ?>%"></div>
</div>
<span class="igny8-progress-percent"><?php echo $all_time > 0 ? round(($last_7_days / $all_time) * 100) : 0; ?>%</span>
</div>
</div>
<div class="igny8-analytics-item">
<div class="igny8-item-info">
<div class="igny8-item-label">Last 20 Days</div>
<div class="igny8-item-value"><?php echo number_format($last_20_days); ?></div>
</div>
<div class="igny8-item-progress">
<div class="igny8-progress-track">
<div class="igny8-progress-fill igny8-progress-amber" style="width: <?php echo $all_time > 0 ? round(($last_20_days / $all_time) * 100) : 0; ?>%"></div>
</div>
<span class="igny8-progress-percent"><?php echo $all_time > 0 ? round(($last_20_days / $all_time) * 100) : 0; ?>%</span>
</div>
</div>
<div class="igny8-analytics-item igny8-analytics-total">
<div class="igny8-item-info">
<div class="igny8-item-label">All Time</div>
<div class="igny8-item-value"><?php echo number_format($all_time); ?></div>
</div>
<div class="igny8-item-progress">
<div class="igny8-progress-track">
<div class="igny8-progress-fill igny8-progress-purple" style="width: 100%"></div>
</div>
<span class="igny8-progress-percent">100%</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Content Workflow Summary -->
<div class="igny8-dashboard-section">
<div class="igny8-card">
<div class="igny8-card-header igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Content Workflow Summary</h3>
<p class="igny8-card-subtitle igny8-centered">Content distribution analysis</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-chart-line igny8-dashboard-icon-lg igny8-dashboard-icon-blue"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<div class="igny8-progress-item">
<div class="igny8-progress-header">
<span class="igny8-progress-label">Queued Tasks</span>
<span class="igny8-progress-percent"><?php echo $queued_pct; ?>%</span>
</div>
<div class="igny8-progress-bar">
<div class="igny8-progress-fill igny8-progress-blue" style="width: <?php echo $queued_pct; ?>%"></div>
</div>
<div class="igny8-progress-details"><?php echo number_format($queued_tasks); ?> tasks waiting to start</div>
</div>
<div class="igny8-progress-item">
<div class="igny8-progress-header">
<span class="igny8-progress-label">Draft / Review</span>
<span class="igny8-progress-percent"><?php echo $draft_pct; ?>%</span>
</div>
<div class="igny8-progress-bar">
<div class="igny8-progress-fill igny8-progress-amber" style="width: <?php echo $draft_pct; ?>%"></div>
</div>
<div class="igny8-progress-details"><?php echo number_format($draft_tasks); ?> drafts awaiting review and publish</div>
</div>
<div class="igny8-progress-item">
<div class="igny8-progress-header">
<span class="igny8-progress-label">Published</span>
<span class="igny8-progress-percent"><?php echo $published_pct; ?>%</span>
</div>
<div class="igny8-progress-bar">
<div class="igny8-progress-fill igny8-progress-green" style="width: <?php echo $published_pct; ?>%"></div>
</div>
<div class="igny8-progress-details"><?php echo number_format($published_tasks); ?> tasks completed and published</div>
</div>
</div>
</div>
</div>
</div>
<div class="igny8-grid-2">
<!-- AI Integration Settings Card -->
<!-- Pending Actions Panel -->
<div class="igny8-dashboard-section">
<div class="igny8-card">
<div class="igny8-card-header igny8-standard-header">
<div class="igny8-card-header-content">
<div class="igny8-card-title-text">
<h3>Pending Actions</h3>
<p class="igny8-card-subtitle igny8-centered">Action items requiring attention</p>
</div>
<div class="igny8-card-icon">
<span class="dashicons dashicons-clock igny8-dashboard-icon-lg igny8-dashboard-icon-amber"></span>
</div>
</div>
</div>
<div class="igny8-card-body">
<div class="igny8-next-actions">
<?php if ($queued_tasks > 0): ?>
<div class="igny8-action-item">
<span class="igny8-action-text"><?php echo number_format($queued_tasks); ?> tasks not yet started</span>
<a href="<?php echo admin_url('admin.php?page=igny8-writer&sm=tasks'); ?>" class="igny8-btn igny8-btn-text">Start Writing</a>
</div>
<?php endif; ?>
<?php if ($draft_tasks > 0): ?>
<div class="igny8-action-item">
<span class="igny8-action-text"><?php echo number_format($draft_tasks); ?> drafts awaiting review and publish</span>
<a href="<?php echo admin_url('admin.php?page=igny8-writer&sm=drafts'); ?>" class="igny8-btn igny8-btn-text">Review Drafts</a>
</div>
<?php endif; ?>
<?php if ($published_tasks > 0): ?>
<div class="igny8-action-item">
<span class="igny8-action-text"><?php echo number_format($published_tasks); ?> posts ready to publish</span>
<a href="<?php echo admin_url('admin.php?page=igny8-writer&sm=published'); ?>" class="igny8-btn igny8-btn-text">View Published</a>
</div>
<?php endif; ?>
<?php if ($total_tasks === 0): ?>
<div class="igny8-action-item igny8-action-complete">
<span class="igny8-action-text">All caught up! Consider adding new content ideas.</span>
<a href="<?php echo admin_url('admin.php?page=igny8-planner'); ?>" class="igny8-btn igny8-btn-text">Plan New Content</a>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
<?php
// Home page - add dashboard functionality
// Only load CRON key if this page needs CRON functionality
$cron_key = igny8_needs_cron_functionality() ? igny8_get_or_generate_cron_key() : null;
// Debug logging (remove in production)
// if (defined('WP_DEBUG') && WP_DEBUG) {
// error_log('Igny8 Writer: cronKey being passed to JS: ' . $cron_key);
// }
wp_localize_script('igny8-admin-js', 'IGNY8_PAGE', [
'module' => 'writer',
'submodule' => 'home',
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('igny8_writer_settings'),
'cronKey' => $cron_key
]);
break;
}
// Capture page content
$igny8_page_content = ob_get_clean();
// Include global layout
include_once plugin_dir_path(__FILE__) . '../../core/global-layout.php';