1552 lines
65 KiB
PHP
1552 lines
65 KiB
PHP
<?php
|
||
/**
|
||
* Settings Page
|
||
*
|
||
* Handles the plugin settings page in WordPress admin.
|
||
*
|
||
* @package WP_Agentic_Writer
|
||
*/
|
||
|
||
if ( ! defined( 'ABSPATH' ) ) {
|
||
exit;
|
||
}
|
||
|
||
/**
|
||
* Class WP_Agentic_Writer_Settings
|
||
*
|
||
* @since 0.1.0
|
||
*/
|
||
class WP_Agentic_Writer_Settings {
|
||
|
||
/**
|
||
* Get singleton instance.
|
||
*
|
||
* @since 0.1.0
|
||
* @return WP_Agentic_Writer_Settings
|
||
*/
|
||
public static function get_instance() {
|
||
static $instance = null;
|
||
|
||
if ( null === $instance ) {
|
||
$instance = new self();
|
||
}
|
||
|
||
return $instance;
|
||
}
|
||
|
||
/**
|
||
* Constructor.
|
||
*
|
||
* @since 0.1.0
|
||
*/
|
||
private function __construct() {
|
||
add_action( 'admin_menu', array( $this, 'add_settings_page' ) );
|
||
add_action( 'admin_init', array( $this, 'register_settings' ) );
|
||
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
|
||
add_action( 'wp_ajax_wpaw_refresh_models', array( $this, 'ajax_refresh_models' ) );
|
||
}
|
||
|
||
/**
|
||
* Enqueue scripts for settings page.
|
||
*
|
||
* @since 0.1.0
|
||
*/
|
||
public function enqueue_scripts( $hook ) {
|
||
if ( 'settings_page_wp-agentic-writer' !== $hook ) {
|
||
return;
|
||
}
|
||
|
||
// Enqueue Select2 for searchable dropdowns.
|
||
wp_enqueue_style( 'select2', 'https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css', array(), '4.1.0' );
|
||
wp_enqueue_script( 'select2', 'https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js', array( 'jquery' ), '4.1.0', true );
|
||
|
||
wp_enqueue_style( 'wp-agentic-writer-admin', WP_AGENTIC_WRITER_URL . 'assets/css/admin.css', array( 'select2' ), WP_AGENTIC_WRITER_VERSION );
|
||
wp_enqueue_script( 'wp-agentic-writer-settings', WP_AGENTIC_WRITER_URL . 'assets/js/settings.js', array( 'jquery', 'select2' ), WP_AGENTIC_WRITER_VERSION, true );
|
||
|
||
$settings = get_option( 'wp_agentic_writer_settings', array() );
|
||
wp_localize_script( 'wp-agentic-writer-settings', 'wpawSettings', array(
|
||
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
|
||
'nonce' => wp_create_nonce( 'wpaw_settings' ),
|
||
'models' => $this->get_models_for_select(),
|
||
'currentModels' => array(
|
||
'planning' => $settings['planning_model'] ?? 'google/gemini-2.0-flash-exp:free',
|
||
'writing' => $settings['writing_model'] ?? ( $settings['execution_model'] ?? 'anthropic/claude-3.5-sonnet' ),
|
||
'execution' => $settings['writing_model'] ?? ( $settings['execution_model'] ?? 'anthropic/claude-3.5-sonnet' ),
|
||
'clarity' => $settings['clarity_model'] ?? 'google/gemini-2.0-flash-exp:free',
|
||
'refinement' => $settings['refinement_model'] ?? 'anthropic/claude-3.5-sonnet',
|
||
'chat' => $settings['chat_model'] ?? 'google/gemini-2.0-flash-exp:free',
|
||
'image' => $settings['image_model'] ?? 'openai/gpt-4o',
|
||
),
|
||
) );
|
||
}
|
||
|
||
/**
|
||
* Get models for select dropdowns.
|
||
*
|
||
* @since 0.1.0
|
||
* @return array Models grouped by category.
|
||
*/
|
||
private function get_models_for_select() {
|
||
$provider = WP_Agentic_Writer_OpenRouter_Provider::get_instance();
|
||
$models = $provider->get_cached_models();
|
||
|
||
if ( is_wp_error( $models ) ) {
|
||
// Return fallback defaults if API fails.
|
||
return array(
|
||
'planning' => array(
|
||
'recommended' => array(
|
||
array( 'id' => 'google/gemini-2.0-flash-exp:free', 'name' => 'Google Gemini 2.0 Flash' ),
|
||
),
|
||
'all' => array(
|
||
array( 'id' => 'google/gemini-2.0-flash-exp:free', 'name' => 'Google Gemini 2.0 Flash' ),
|
||
),
|
||
),
|
||
'execution' => array(
|
||
'recommended' => array(
|
||
array( 'id' => 'anthropic/claude-3.5-sonnet', 'name' => 'Anthropic Claude 3.5 Sonnet' ),
|
||
),
|
||
'all' => array(
|
||
array( 'id' => 'anthropic/claude-3.5-sonnet', 'name' => 'Anthropic Claude 3.5 Sonnet' ),
|
||
),
|
||
),
|
||
'image' => array(
|
||
'recommended' => array(
|
||
array( 'id' => 'openai/gpt-4o', 'name' => 'OpenAI GPT-4o' ),
|
||
),
|
||
'all' => array(
|
||
array( 'id' => 'openai/gpt-4o', 'name' => 'OpenAI GPT-4o' ),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
// Transform model structure to match what JS expects.
|
||
return $this->transform_models_for_js( $models );
|
||
}
|
||
|
||
/**
|
||
* Transform models structure for JavaScript consumption.
|
||
*
|
||
* @since 0.1.0
|
||
* @param array $models Models from provider.
|
||
* @return array Transformed models.
|
||
*/
|
||
private function transform_models_for_js( $models ) {
|
||
// Handle flat model list from OpenRouter.
|
||
if ( ! empty( $models ) && array_keys( $models ) === range( 0, count( $models ) - 1 ) ) {
|
||
$settings = get_option( 'wp_agentic_writer_settings', array() );
|
||
$planning_id = $settings['planning_model'] ?? 'google/gemini-2.0-flash-exp:free';
|
||
$execution_id = $settings['execution_model'] ?? 'anthropic/claude-3.5-sonnet';
|
||
$image_id = $settings['image_model'] ?? 'openai/gpt-4o';
|
||
|
||
$text_models = array();
|
||
$image_models = array();
|
||
|
||
// Known image model prefixes
|
||
$image_model_prefixes = array( 'black-forest-labs/', 'stability-ai/', 'dall-e', 'midjourney' );
|
||
|
||
foreach ( $models as $model ) {
|
||
if ( empty( $model['id'] ) ) {
|
||
continue;
|
||
}
|
||
$prompt_price = isset( $model['pricing']['prompt'] ) ? (float) $model['pricing']['prompt'] : 0;
|
||
$completion_price = isset( $model['pricing']['completion'] ) ? (float) $model['pricing']['completion'] : 0;
|
||
$image_price = isset( $model['pricing']['image'] ) ? (float) $model['pricing']['image'] : 0;
|
||
|
||
$model_data = array(
|
||
'id' => $model['id'],
|
||
'name' => $model['name'] ?? $model['id'],
|
||
'is_free' => $prompt_price <= 0.0 && $completion_price <= 0.0,
|
||
'pricing' => array(
|
||
'prompt' => $prompt_price,
|
||
'completion' => $completion_price,
|
||
'image' => $image_price,
|
||
),
|
||
);
|
||
|
||
// Check if this is an image model
|
||
$is_image_model = false;
|
||
foreach ( $image_model_prefixes as $prefix ) {
|
||
if ( stripos( $model['id'], $prefix ) === 0 ) {
|
||
$is_image_model = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Also check if it has image pricing but no text pricing
|
||
if ( $image_price > 0 && $prompt_price <= 0 && $completion_price <= 0 ) {
|
||
$is_image_model = true;
|
||
}
|
||
|
||
if ( $is_image_model ) {
|
||
$image_models[] = $model_data;
|
||
} else {
|
||
$text_models[] = $model_data;
|
||
}
|
||
}
|
||
|
||
$find_model = function( $model_id ) use ( $text_models, $image_models ) {
|
||
foreach ( array_merge( $text_models, $image_models ) as $model ) {
|
||
if ( $model['id'] === $model_id ) {
|
||
return $model;
|
||
}
|
||
}
|
||
return null;
|
||
};
|
||
|
||
$chat_id = $settings['chat_model'] ?? 'google/gemini-2.0-flash-exp:free';
|
||
|
||
return array(
|
||
'planning' => array(
|
||
'recommended' => array_filter( array( $find_model( $planning_id ) ) ),
|
||
'all' => $text_models,
|
||
),
|
||
'execution' => array(
|
||
'recommended' => array_filter( array( $find_model( $execution_id ) ) ),
|
||
'all' => $text_models,
|
||
),
|
||
'chat' => array(
|
||
'recommended' => array_filter( array( $find_model( $chat_id ) ) ),
|
||
'all' => $text_models,
|
||
),
|
||
'image' => array(
|
||
'recommended' => array_filter( array( $find_model( $image_id ) ) ),
|
||
'all' => $image_models,
|
||
),
|
||
);
|
||
}
|
||
|
||
$transformed = array();
|
||
|
||
foreach ( $models as $type => $categories ) {
|
||
if ( ! isset( $transformed[ $type ] ) ) {
|
||
$transformed[ $type ] = array(
|
||
'recommended' => array(),
|
||
'all' => array(),
|
||
);
|
||
}
|
||
|
||
// Combine free and paid into 'all' array.
|
||
$all_models = array_merge(
|
||
$categories['free'] ?? array(),
|
||
$categories['paid'] ?? array()
|
||
);
|
||
|
||
// Remove duplicates (in case a model is in both recommended and all).
|
||
$recommended_ids = array();
|
||
foreach ( $categories['recommended'] ?? array() as $model ) {
|
||
$transformed[ $type ]['recommended'][] = $model;
|
||
$recommended_ids[ $model['id'] ] = true;
|
||
}
|
||
|
||
// Add all models, avoiding duplicates with recommended.
|
||
foreach ( $all_models as $model ) {
|
||
if ( ! isset( $recommended_ids[ $model['id'] ] ) ) {
|
||
$transformed[ $type ]['all'][] = $model;
|
||
}
|
||
}
|
||
}
|
||
|
||
return $transformed;
|
||
}
|
||
|
||
/**
|
||
* AJAX handler for refreshing models.
|
||
*
|
||
* @since 0.1.0
|
||
*/
|
||
public function ajax_refresh_models() {
|
||
check_ajax_referer( 'wpaw_settings', 'nonce' );
|
||
|
||
if ( ! current_user_can( 'manage_options' ) ) {
|
||
wp_send_json_error( array( 'message' => 'Permission denied' ) );
|
||
}
|
||
|
||
$provider = WP_Agentic_Writer_OpenRouter_Provider::get_instance();
|
||
$models = $provider->fetch_and_cache_models( true );
|
||
|
||
if ( is_wp_error( $models ) ) {
|
||
wp_send_json_error( array( 'message' => $models->get_error_message() ) );
|
||
}
|
||
|
||
// Transform models for JS consumption.
|
||
$transformed = $this->transform_models_for_js( $models );
|
||
|
||
wp_send_json_success( array(
|
||
'models' => $transformed,
|
||
'message' => __( 'Models refreshed successfully!', 'wp-agentic-writer' ),
|
||
) );
|
||
}
|
||
|
||
/**
|
||
* Add settings page to admin menu.
|
||
*
|
||
* @since 0.1.0
|
||
*/
|
||
public function add_settings_page() {
|
||
add_options_page(
|
||
__( 'WP Agentic Writer', 'wp-agentic-writer' ),
|
||
__( 'Agentic Writer', 'wp-agentic-writer' ),
|
||
'manage_options',
|
||
'wp-agentic-writer',
|
||
array( $this, 'render_settings_page' )
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Register settings.
|
||
*
|
||
* @since 0.1.0
|
||
*/
|
||
public function register_settings() {
|
||
register_setting(
|
||
'wp_agentic_writer_settings',
|
||
'wp_agentic_writer_settings',
|
||
array(
|
||
'sanitize_callback' => array( $this, 'sanitize_settings' ),
|
||
)
|
||
);
|
||
}
|
||
|
||
/**
|
||
* Sanitize settings.
|
||
*
|
||
* @since 0.1.0
|
||
* @param array $input Settings input.
|
||
* @return array Sanitized settings.
|
||
*/
|
||
public function sanitize_settings( $input ) {
|
||
$sanitized = array();
|
||
|
||
// Sanitize API key (don't strip tags, but trim).
|
||
$sanitized['openrouter_api_key'] = trim( $input['openrouter_api_key'] ?? '' );
|
||
|
||
// Sanitize model names (6 models as per model-preset-brief.md).
|
||
$sanitized['chat_model'] = sanitize_text_field( $input['chat_model'] ?? 'google/gemini-2.5-flash' );
|
||
$sanitized['clarity_model'] = sanitize_text_field( $input['clarity_model'] ?? 'google/gemini-2.5-flash' );
|
||
$sanitized['planning_model'] = sanitize_text_field( $input['planning_model'] ?? 'google/gemini-2.5-flash' );
|
||
$sanitized['writing_model'] = sanitize_text_field( $input['writing_model'] ?? 'anthropic/claude-3.5-sonnet' );
|
||
$sanitized['refinement_model'] = sanitize_text_field( $input['refinement_model'] ?? 'anthropic/claude-3.5-sonnet' );
|
||
$sanitized['image_model'] = sanitize_text_field( $input['image_model'] ?? 'openai/gpt-4o' );
|
||
// Legacy support: map execution_model to writing_model
|
||
if ( isset( $input['execution_model'] ) && ! isset( $input['writing_model'] ) ) {
|
||
$sanitized['writing_model'] = sanitize_text_field( $input['execution_model'] );
|
||
}
|
||
|
||
// Sanitize boolean values.
|
||
$sanitized['web_search_enabled'] = isset( $input['web_search_enabled'] ) && '1' === $input['web_search_enabled'];
|
||
$sanitized['cost_tracking_enabled'] = isset( $input['cost_tracking_enabled'] ) && '1' === $input['cost_tracking_enabled'];
|
||
$sanitized['enable_clarification_quiz'] = isset( $input['enable_clarification_quiz'] ) && '1' === $input['enable_clarification_quiz'];
|
||
|
||
// Sanitize search options.
|
||
$sanitized['search_engine'] = in_array( $input['search_engine'], array( 'auto', 'native', 'exa' ), true )
|
||
? $input['search_engine']
|
||
: 'auto';
|
||
$sanitized['search_depth'] = isset( $input['search_depth'] ) && in_array( $input['search_depth'], array( 'low', 'medium', 'high' ), true )
|
||
? $input['search_depth']
|
||
: 'medium';
|
||
|
||
// Sanitize budget.
|
||
$sanitized['monthly_budget'] = floatval( $input['monthly_budget'] ?? 600 );
|
||
|
||
// Sanitize chat history limit.
|
||
$chat_history_limit = isset( $input['chat_history_limit'] ) ? absint( $input['chat_history_limit'] ) : 20;
|
||
$sanitized['chat_history_limit'] = min( $chat_history_limit, 200 );
|
||
|
||
// Sanitize clarification quiz settings.
|
||
$sanitized['clarity_confidence_threshold'] = in_array( $input['clarity_confidence_threshold'], array( '0.5', '0.6', '0.7', '0.8', '0.9' ), true )
|
||
? $input['clarity_confidence_threshold']
|
||
: '0.6';
|
||
|
||
if ( isset( $input['required_context_categories'] ) && is_array( $input['required_context_categories'] ) ) {
|
||
$valid_categories = array( 'target_outcome', 'target_audience', 'tone', 'content_depth', 'expertise_level', 'content_type', 'pov' );
|
||
$sanitized['required_context_categories'] = array_intersect( $input['required_context_categories'], $valid_categories );
|
||
} else {
|
||
$sanitized['required_context_categories'] = array( 'target_outcome', 'target_audience', 'tone', 'content_depth', 'expertise_level', 'content_type', 'pov' );
|
||
}
|
||
|
||
// Sanitize preferred languages
|
||
if ( isset( $input['preferred_languages'] ) && is_array( $input['preferred_languages'] ) ) {
|
||
$sanitized['preferred_languages'] = array_map( 'sanitize_text_field', $input['preferred_languages'] );
|
||
} else {
|
||
$sanitized['preferred_languages'] = array( 'auto', 'English', 'Indonesian' );
|
||
}
|
||
|
||
// Sanitize custom languages
|
||
if ( isset( $input['custom_languages'] ) && is_array( $input['custom_languages'] ) ) {
|
||
$sanitized['custom_languages'] = array_filter( array_map( 'sanitize_text_field', $input['custom_languages'] ) );
|
||
} else {
|
||
$sanitized['custom_languages'] = array();
|
||
}
|
||
|
||
return $sanitized;
|
||
}
|
||
|
||
/**
|
||
* Render settings page.
|
||
*
|
||
* @since 0.1.0
|
||
*/
|
||
public function render_settings_page() {
|
||
$settings = get_option( 'wp_agentic_writer_settings', array() );
|
||
|
||
// Extract settings (6 models as per model-preset-brief.md).
|
||
$api_key = $settings['openrouter_api_key'] ?? '';
|
||
$chat_model = $settings['chat_model'] ?? 'google/gemini-2.5-flash';
|
||
$clarity_model = $settings['clarity_model'] ?? 'google/gemini-2.5-flash';
|
||
$planning_model = $settings['planning_model'] ?? 'google/gemini-2.5-flash';
|
||
$writing_model = $settings['writing_model'] ?? ( $settings['execution_model'] ?? 'anthropic/claude-3.5-sonnet' );
|
||
$refinement_model = $settings['refinement_model'] ?? 'anthropic/claude-3.5-sonnet';
|
||
$image_model = $settings['image_model'] ?? 'openai/gpt-4o';
|
||
$web_search_enabled = $settings['web_search_enabled'] ?? false;
|
||
$search_engine = $settings['search_engine'] ?? 'auto';
|
||
$search_depth = $settings['search_depth'] ?? 'medium';
|
||
$cost_tracking_enabled = $settings['cost_tracking_enabled'] ?? true;
|
||
$monthly_budget = $settings['monthly_budget'] ?? 600;
|
||
$chat_history_limit = $settings['chat_history_limit'] ?? 20;
|
||
|
||
// Clarification quiz settings.
|
||
$enable_clarification_quiz = $settings['enable_clarification_quiz'] ?? true;
|
||
$clarity_confidence_threshold = $settings['clarity_confidence_threshold'] ?? '0.6';
|
||
$required_context_categories = $settings['required_context_categories'] ?? array(
|
||
'target_outcome',
|
||
'target_audience',
|
||
'tone',
|
||
'content_depth',
|
||
'expertise_level',
|
||
'content_type',
|
||
'pov',
|
||
);
|
||
|
||
// Get cost tracking data.
|
||
$cost_tracker = WP_Agentic_Writer_Cost_Tracker::get_instance();
|
||
$monthly_used = $cost_tracker->get_monthly_total();
|
||
$budget_percent = $monthly_budget > 0 ? ( $monthly_used / $monthly_budget ) * 100 : 0;
|
||
$budget_status = $budget_percent > 90 ? 'danger' : ( $budget_percent > 70 ? 'warning' : '' );
|
||
?>
|
||
<div class="wpaw-settings-wrap">
|
||
<!-- Header -->
|
||
<div class="wpaw-settings-header">
|
||
<div class="wpaw-settings-logo">✨</div>
|
||
<div>
|
||
<h1 class="wpaw-settings-title"><?php esc_html_e( 'WP Agentic Writer', 'wp-agentic-writer' ); ?></h1>
|
||
<p class="wpaw-settings-subtitle"><?php esc_html_e( 'AI-powered writing assistant for WordPress', 'wp-agentic-writer' ); ?></p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Tab Navigation -->
|
||
<div class="wpaw-settings-nav">
|
||
<button type="button" class="wpaw-settings-nav-btn active" data-tab="general">
|
||
<span class="dashicons dashicons-admin-settings"></span>
|
||
<?php esc_html_e( 'General', 'wp-agentic-writer' ); ?>
|
||
</button>
|
||
<button type="button" class="wpaw-settings-nav-btn" data-tab="models">
|
||
<span class="dashicons dashicons-superhero"></span>
|
||
<?php esc_html_e( 'AI Models', 'wp-agentic-writer' ); ?>
|
||
</button>
|
||
<button type="button" class="wpaw-settings-nav-btn" data-tab="cost-log">
|
||
<span class="dashicons dashicons-chart-line"></span>
|
||
<?php esc_html_e( 'Cost Log', 'wp-agentic-writer' ); ?>
|
||
</button>
|
||
<button type="button" class="wpaw-settings-nav-btn" data-tab="guide">
|
||
<span class="dashicons dashicons-book"></span>
|
||
<?php esc_html_e( 'Model Guide', 'wp-agentic-writer' ); ?>
|
||
</button>
|
||
</div>
|
||
|
||
<form method="post" action="options.php">
|
||
<?php settings_fields( 'wp_agentic_writer_settings' ); ?>
|
||
|
||
<!-- ==================== GENERAL TAB ==================== -->
|
||
<div class="wpaw-tab-content active" data-tab="general">
|
||
|
||
<!-- API Configuration Section -->
|
||
<div class="wpaw-settings-section">
|
||
<div class="wpaw-section-header">
|
||
<div class="wpaw-section-icon">🔑</div>
|
||
<div>
|
||
<h2 class="wpaw-section-title"><?php esc_html_e( 'API Configuration', 'wp-agentic-writer' ); ?></h2>
|
||
<p class="wpaw-section-desc"><?php esc_html_e( 'Connect to OpenRouter to access AI models', 'wp-agentic-writer' ); ?></p>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-section-body">
|
||
<div class="wpaw-field-row">
|
||
<div class="wpaw-field-label">
|
||
<?php esc_html_e( 'OpenRouter API Key', 'wp-agentic-writer' ); ?>
|
||
<small><?php esc_html_e( 'Required for all AI features', 'wp-agentic-writer' ); ?></small>
|
||
</div>
|
||
<div class="wpaw-field-input">
|
||
<input type="password" id="openrouter_api_key" name="wp_agentic_writer_settings[openrouter_api_key]" value="<?php echo esc_attr( $api_key ); ?>" placeholder="sk-or-v1-..." />
|
||
<p class="description">
|
||
<?php printf( wp_kses_post( __( 'Get your API key from <a href="%s" target="_blank">OpenRouter</a>.', 'wp-agentic-writer' ) ), 'https://openrouter.ai/keys' ); ?>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Budget & Cost Section -->
|
||
<div class="wpaw-settings-section">
|
||
<div class="wpaw-section-header">
|
||
<div class="wpaw-section-icon">💰</div>
|
||
<div>
|
||
<h2 class="wpaw-section-title"><?php esc_html_e( 'Budget & Cost Tracking', 'wp-agentic-writer' ); ?></h2>
|
||
<p class="wpaw-section-desc"><?php esc_html_e( 'Monitor and control your API spending', 'wp-agentic-writer' ); ?></p>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-section-body">
|
||
<div class="wpaw-budget-display">
|
||
<div class="wpaw-budget-stat">
|
||
<div class="wpaw-budget-stat-value">$<?php echo number_format( $monthly_used, 2 ); ?></div>
|
||
<div class="wpaw-budget-stat-label"><?php esc_html_e( 'Used This Month', 'wp-agentic-writer' ); ?></div>
|
||
</div>
|
||
<div class="wpaw-budget-bar-wrapper">
|
||
<div class="wpaw-budget-bar-label">
|
||
<span><?php esc_html_e( 'Monthly Budget', 'wp-agentic-writer' ); ?></span>
|
||
<span><?php echo number_format( $budget_percent, 1 ); ?>%</span>
|
||
</div>
|
||
<div class="wpaw-budget-bar-track">
|
||
<div class="wpaw-budget-bar-fill <?php echo esc_attr( $budget_status ); ?>" style="width: <?php echo min( $budget_percent, 100 ); ?>%"></div>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-budget-stat">
|
||
<div class="wpaw-budget-stat-value">$<?php echo number_format( max( 0, $monthly_budget - $monthly_used ), 2 ); ?></div>
|
||
<div class="wpaw-budget-stat-label"><?php esc_html_e( 'Remaining', 'wp-agentic-writer' ); ?></div>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-cost-shortcuts">
|
||
<a href="<?php echo esc_url( admin_url( 'options-general.php?page=wp-agentic-writer-settings&tab=cost-log' ) ); ?>" class="wpaw-cost-shortcut-link">
|
||
<span class="dashicons dashicons-chart-line"></span>
|
||
<?php esc_html_e( 'View Full Cost Log', 'wp-agentic-writer' ); ?> →
|
||
</a>
|
||
</div>
|
||
<div class="wpaw-field-row">
|
||
<div class="wpaw-field-label">
|
||
<?php esc_html_e( 'Monthly Budget', 'wp-agentic-writer' ); ?>
|
||
<small><?php esc_html_e( 'Maximum spend per month', 'wp-agentic-writer' ); ?></small>
|
||
</div>
|
||
<div class="wpaw-field-input">
|
||
<input type="number" id="monthly_budget" name="wp_agentic_writer_settings[monthly_budget]" value="<?php echo esc_attr( $monthly_budget ); ?>" min="0" step="0.01" style="max-width: 120px;" /> USD
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-field-row">
|
||
<div class="wpaw-field-label"><?php esc_html_e( 'Cost Tracking', 'wp-agentic-writer' ); ?></div>
|
||
<div class="wpaw-field-input">
|
||
<label class="wpaw-toggle">
|
||
<input type="checkbox" name="wp_agentic_writer_settings[cost_tracking_enabled]" value="1" <?php checked( $cost_tracking_enabled ); ?> />
|
||
<span class="wpaw-toggle-switch"></span>
|
||
<span class="wpaw-toggle-label"><?php esc_html_e( 'Enable cost tracking in editor sidebar', 'wp-agentic-writer' ); ?></span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Research & Web Search Section -->
|
||
<div class="wpaw-settings-section">
|
||
<div class="wpaw-section-header">
|
||
<div class="wpaw-section-icon">🔍</div>
|
||
<div>
|
||
<h2 class="wpaw-section-title"><?php esc_html_e( 'Research & Web Search', 'wp-agentic-writer' ); ?></h2>
|
||
<p class="wpaw-section-desc"><?php esc_html_e( 'Configure web search for up-to-date content', 'wp-agentic-writer' ); ?></p>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-section-body">
|
||
<div class="wpaw-field-row">
|
||
<div class="wpaw-field-label"><?php esc_html_e( 'Web Search', 'wp-agentic-writer' ); ?></div>
|
||
<div class="wpaw-field-input">
|
||
<label class="wpaw-toggle">
|
||
<input type="checkbox" name="wp_agentic_writer_settings[web_search_enabled]" value="1" <?php checked( $web_search_enabled ); ?> />
|
||
<span class="wpaw-toggle-switch"></span>
|
||
<span class="wpaw-toggle-label"><?php esc_html_e( 'Enable Web Search (~$0.02 per search)', 'wp-agentic-writer' ); ?></span>
|
||
</label>
|
||
<p class="description"><?php esc_html_e( 'Search the web for current information. Can be toggled per-request in sidebar.', 'wp-agentic-writer' ); ?></p>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-field-row">
|
||
<div class="wpaw-field-label"><?php esc_html_e( 'Search Engine', 'wp-agentic-writer' ); ?></div>
|
||
<div class="wpaw-field-input">
|
||
<select id="search_engine" name="wp_agentic_writer_settings[search_engine]">
|
||
<option value="auto" <?php selected( $search_engine, 'auto' ); ?>><?php esc_html_e( 'Auto (Native if available, Exa fallback)', 'wp-agentic-writer' ); ?></option>
|
||
<option value="native" <?php selected( $search_engine, 'native' ); ?>><?php esc_html_e( 'Native (Provider\'s built-in search)', 'wp-agentic-writer' ); ?></option>
|
||
<option value="exa" <?php selected( $search_engine, 'exa' ); ?>><?php esc_html_e( 'Exa (Always use Exa search)', 'wp-agentic-writer' ); ?></option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-field-row">
|
||
<div class="wpaw-field-label"><?php esc_html_e( 'Search Depth', 'wp-agentic-writer' ); ?></div>
|
||
<div class="wpaw-field-input">
|
||
<select id="search_depth" name="wp_agentic_writer_settings[search_depth]">
|
||
<option value="low" <?php selected( $search_depth, 'low' ); ?>><?php esc_html_e( 'Low (Basic queries)', 'wp-agentic-writer' ); ?></option>
|
||
<option value="medium" <?php selected( $search_depth, 'medium' ); ?>><?php esc_html_e( 'Medium (General queries)', 'wp-agentic-writer' ); ?></option>
|
||
<option value="high" <?php selected( $search_depth, 'high' ); ?>><?php esc_html_e( 'High (Detailed research)', 'wp-agentic-writer' ); ?></option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Clarification Quiz Section -->
|
||
<div class="wpaw-settings-section">
|
||
<div class="wpaw-section-header">
|
||
<div class="wpaw-section-icon">❓</div>
|
||
<div>
|
||
<h2 class="wpaw-section-title"><?php esc_html_e( 'Clarification Quiz', 'wp-agentic-writer' ); ?></h2>
|
||
<p class="wpaw-section-desc"><?php esc_html_e( 'Gather context before writing for better results', 'wp-agentic-writer' ); ?></p>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-section-body">
|
||
<div class="wpaw-field-row">
|
||
<div class="wpaw-field-label"><?php esc_html_e( 'Enable Quiz', 'wp-agentic-writer' ); ?></div>
|
||
<div class="wpaw-field-input">
|
||
<label class="wpaw-toggle">
|
||
<input type="checkbox" name="wp_agentic_writer_settings[enable_clarification_quiz]" value="1" <?php checked( $enable_clarification_quiz ); ?> />
|
||
<span class="wpaw-toggle-switch"></span>
|
||
<span class="wpaw-toggle-label"><?php esc_html_e( 'Ask clarifying questions when context is missing', 'wp-agentic-writer' ); ?></span>
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-field-row">
|
||
<div class="wpaw-field-label">
|
||
<?php esc_html_e( 'Confidence Threshold', 'wp-agentic-writer' ); ?>
|
||
<small><?php esc_html_e( 'When to trigger the quiz', 'wp-agentic-writer' ); ?></small>
|
||
</div>
|
||
<div class="wpaw-field-input">
|
||
<select id="clarity_confidence_threshold" name="wp_agentic_writer_settings[clarity_confidence_threshold]" <?php disabled( ! $enable_clarification_quiz ); ?>>
|
||
<option value="0.5" <?php selected( $clarity_confidence_threshold, '0.5' ); ?>><?php esc_html_e( 'Very Sensitive (50%)', 'wp-agentic-writer' ); ?></option>
|
||
<option value="0.6" <?php selected( $clarity_confidence_threshold, '0.6' ); ?>><?php esc_html_e( 'Sensitive (60%) - Recommended', 'wp-agentic-writer' ); ?></option>
|
||
<option value="0.7" <?php selected( $clarity_confidence_threshold, '0.7' ); ?>><?php esc_html_e( 'Balanced (70%)', 'wp-agentic-writer' ); ?></option>
|
||
<option value="0.8" <?php selected( $clarity_confidence_threshold, '0.8' ); ?>><?php esc_html_e( 'Strict (80%)', 'wp-agentic-writer' ); ?></option>
|
||
<option value="0.9" <?php selected( $clarity_confidence_threshold, '0.9' ); ?>><?php esc_html_e( 'Very Strict (90%)', 'wp-agentic-writer' ); ?></option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-field-row">
|
||
<div class="wpaw-field-label">
|
||
<?php esc_html_e( 'Context Categories', 'wp-agentic-writer' ); ?>
|
||
<small><?php esc_html_e( 'What to clarify', 'wp-agentic-writer' ); ?></small>
|
||
</div>
|
||
<div class="wpaw-field-input">
|
||
<select id="required_context_categories" name="wp_agentic_writer_settings[required_context_categories][]" multiple="multiple" size="7" <?php disabled( ! $enable_clarification_quiz ); ?>>
|
||
<option value="target_outcome" <?php echo in_array( 'target_outcome', $required_context_categories ) ? 'selected' : ''; ?>><?php esc_html_e( 'Target Outcome', 'wp-agentic-writer' ); ?></option>
|
||
<option value="target_audience" <?php echo in_array( 'target_audience', $required_context_categories ) ? 'selected' : ''; ?>><?php esc_html_e( 'Target Audience', 'wp-agentic-writer' ); ?></option>
|
||
<option value="tone" <?php echo in_array( 'tone', $required_context_categories ) ? 'selected' : ''; ?>><?php esc_html_e( 'Tone of Voice', 'wp-agentic-writer' ); ?></option>
|
||
<option value="content_depth" <?php echo in_array( 'content_depth', $required_context_categories ) ? 'selected' : ''; ?>><?php esc_html_e( 'Content Depth', 'wp-agentic-writer' ); ?></option>
|
||
<option value="expertise_level" <?php echo in_array( 'expertise_level', $required_context_categories ) ? 'selected' : ''; ?>><?php esc_html_e( 'Expertise Level', 'wp-agentic-writer' ); ?></option>
|
||
<option value="content_type" <?php echo in_array( 'content_type', $required_context_categories ) ? 'selected' : ''; ?>><?php esc_html_e( 'Content Type', 'wp-agentic-writer' ); ?></option>
|
||
<option value="pov" <?php echo in_array( 'pov', $required_context_categories ) ? 'selected' : ''; ?>><?php esc_html_e( 'Point of View', 'wp-agentic-writer' ); ?></option>
|
||
</select>
|
||
<p class="description"><?php esc_html_e( 'Select which aspects the AI should clarify before writing.', 'wp-agentic-writer' ); ?></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Content Settings Section -->
|
||
<div class="wpaw-settings-section">
|
||
<div class="wpaw-section-header">
|
||
<div class="wpaw-section-icon">🌍</div>
|
||
<div>
|
||
<h2 class="wpaw-section-title"><?php esc_html_e( 'Content Settings', 'wp-agentic-writer' ); ?></h2>
|
||
<p class="wpaw-section-desc"><?php esc_html_e( 'Configure language preferences for your content', 'wp-agentic-writer' ); ?></p>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-section-body">
|
||
<?php $this->render_language_preferences( $settings ); ?>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Advanced Section -->
|
||
<div class="wpaw-settings-section">
|
||
<div class="wpaw-section-header">
|
||
<div class="wpaw-section-icon">⚙️</div>
|
||
<div>
|
||
<h2 class="wpaw-section-title"><?php esc_html_e( 'Advanced Settings', 'wp-agentic-writer' ); ?></h2>
|
||
<p class="wpaw-section-desc"><?php esc_html_e( 'Additional configuration options', 'wp-agentic-writer' ); ?></p>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-section-body">
|
||
<div class="wpaw-field-row">
|
||
<div class="wpaw-field-label">
|
||
<?php esc_html_e( 'Chat History Limit', 'wp-agentic-writer' ); ?>
|
||
<small><?php esc_html_e( 'Messages stored per post', 'wp-agentic-writer' ); ?></small>
|
||
</div>
|
||
<div class="wpaw-field-input">
|
||
<input type="number" id="chat_history_limit" name="wp_agentic_writer_settings[chat_history_limit]" value="<?php echo esc_attr( $chat_history_limit ); ?>" min="0" max="200" style="max-width: 80px;" />
|
||
<p class="description"><?php esc_html_e( 'Set to 0 to disable chat history.', 'wp-agentic-writer' ); ?></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- END GENERAL TAB -->
|
||
|
||
<!-- ==================== MODELS TAB ==================== -->
|
||
<div class="wpaw-tab-content" data-tab="models">
|
||
|
||
<!-- Quick Presets Section (per model-preset-brief.md) -->
|
||
<div class="wpaw-settings-section">
|
||
<div class="wpaw-section-header">
|
||
<div class="wpaw-section-icon">⚡</div>
|
||
<div>
|
||
<h2 class="wpaw-section-title"><?php esc_html_e( 'Quick Presets', 'wp-agentic-writer' ); ?></h2>
|
||
<p class="wpaw-section-desc"><?php esc_html_e( 'One-click configurations for different budgets', 'wp-agentic-writer' ); ?></p>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-section-body">
|
||
<div class="wpaw-preset-grid">
|
||
<div class="wpaw-preset-card" data-preset="budget" onclick="wpawApplyPreset('budget')">
|
||
<div class="wpaw-preset-name"><?php esc_html_e( 'Budget', 'wp-agentic-writer' ); ?> <span class="wpaw-preset-badge">💰 ~$0.06/article</span></div>
|
||
<div class="wpaw-preset-models">
|
||
Chat/Clarity/Planning/Refinement: Gemini 2.5 Flash<br>
|
||
Writing: Mistral Small Creative<br>
|
||
Image: GPT-4o
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-preset-card" data-preset="balanced" onclick="wpawApplyPreset('balanced')">
|
||
<div class="wpaw-preset-name"><?php esc_html_e( 'Balanced', 'wp-agentic-writer' ); ?> <span class="wpaw-preset-badge recommended">⭐ ~$0.14/article</span></div>
|
||
<div class="wpaw-preset-models">
|
||
Chat/Clarity/Planning: Gemini 2.5 Flash<br>
|
||
Writing/Refinement: Claude 3.5 Sonnet<br>
|
||
Image: GPT-4o
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-preset-card" data-preset="premium" onclick="wpawApplyPreset('premium')">
|
||
<div class="wpaw-preset-name"><?php esc_html_e( 'Premium', 'wp-agentic-writer' ); ?> <span class="wpaw-preset-badge">✨ ~$0.31/article</span></div>
|
||
<div class="wpaw-preset-models">
|
||
Chat/Planning: Gemini 3 Flash Preview<br>
|
||
Clarity: Claude Sonnet 4<br>
|
||
Writing/Refinement: GPT-4.1<br>
|
||
Image: GPT-4o
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Individual Model Selectors (6 models per model-preset-brief.md) -->
|
||
<div class="wpaw-settings-section">
|
||
<div class="wpaw-section-header">
|
||
<div class="wpaw-section-icon">🤖</div>
|
||
<div>
|
||
<h2 class="wpaw-section-title"><?php esc_html_e( 'AI Model Configuration', 'wp-agentic-writer' ); ?></h2>
|
||
<p class="wpaw-section-desc"><?php esc_html_e( '6 specialized models for optimal results. Use search to find models.', 'wp-agentic-writer' ); ?></p>
|
||
</div>
|
||
<button type="button" id="wpaw-refresh-models" class="button" style="margin-left: auto;">
|
||
<?php esc_html_e( 'Refresh Models', 'wp-agentic-writer' ); ?>
|
||
</button>
|
||
<span id="wpaw-models-spinner" class="spinner" style="display: none;"></span>
|
||
</div>
|
||
<div class="wpaw-section-body">
|
||
<div id="wpaw-models-message" style="display: none; margin-bottom: 16px;"></div>
|
||
|
||
<div class="wpaw-field-row">
|
||
<div class="wpaw-field-label">
|
||
<?php esc_html_e( 'Chat Model', 'wp-agentic-writer' ); ?>
|
||
<small><?php esc_html_e( 'Discussion, research, recommendations', 'wp-agentic-writer' ); ?></small>
|
||
</div>
|
||
<div class="wpaw-field-input">
|
||
<select id="chat_model" name="wp_agentic_writer_settings[chat_model]" class="wpaw-select2-model" style="width: 100%;">
|
||
<option value="<?php echo esc_attr( $chat_model ); ?>"><?php echo esc_html( $chat_model ); ?></option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="wpaw-field-row">
|
||
<div class="wpaw-field-label">
|
||
<?php esc_html_e( 'Clarity Model', 'wp-agentic-writer' ); ?>
|
||
<small><?php esc_html_e( 'Prompt analysis, quiz generation', 'wp-agentic-writer' ); ?></small>
|
||
</div>
|
||
<div class="wpaw-field-input">
|
||
<select id="clarity_model" name="wp_agentic_writer_settings[clarity_model]" class="wpaw-select2-model" style="width: 100%;">
|
||
<option value="<?php echo esc_attr( $clarity_model ); ?>"><?php echo esc_html( $clarity_model ); ?></option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="wpaw-field-row">
|
||
<div class="wpaw-field-label">
|
||
<?php esc_html_e( 'Planning Model', 'wp-agentic-writer' ); ?>
|
||
<small><?php esc_html_e( 'Article outline generation', 'wp-agentic-writer' ); ?></small>
|
||
</div>
|
||
<div class="wpaw-field-input">
|
||
<select id="planning_model" name="wp_agentic_writer_settings[planning_model]" class="wpaw-select2-model" style="width: 100%;">
|
||
<option value="<?php echo esc_attr( $planning_model ); ?>"><?php echo esc_html( $planning_model ); ?></option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="wpaw-field-row">
|
||
<div class="wpaw-field-label">
|
||
<?php esc_html_e( 'Writing Model', 'wp-agentic-writer' ); ?>
|
||
<small><?php esc_html_e( 'Article draft generation (2-5k words)', 'wp-agentic-writer' ); ?></small>
|
||
</div>
|
||
<div class="wpaw-field-input">
|
||
<select id="writing_model" name="wp_agentic_writer_settings[writing_model]" class="wpaw-select2-model" style="width: 100%;">
|
||
<option value="<?php echo esc_attr( $writing_model ); ?>"><?php echo esc_html( $writing_model ); ?></option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="wpaw-field-row">
|
||
<div class="wpaw-field-label">
|
||
<?php esc_html_e( 'Refinement Model', 'wp-agentic-writer' ); ?>
|
||
<small><?php esc_html_e( 'Paragraph edits, rewrites, polish', 'wp-agentic-writer' ); ?></small>
|
||
</div>
|
||
<div class="wpaw-field-input">
|
||
<select id="refinement_model" name="wp_agentic_writer_settings[refinement_model]" class="wpaw-select2-model" style="width: 100%;">
|
||
<option value="<?php echo esc_attr( $refinement_model ); ?>"><?php echo esc_html( $refinement_model ); ?></option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="wpaw-field-row">
|
||
<div class="wpaw-field-label">
|
||
<?php esc_html_e( 'Image Model', 'wp-agentic-writer' ); ?>
|
||
<small><?php esc_html_e( 'Image generation', 'wp-agentic-writer' ); ?></small>
|
||
</div>
|
||
<div class="wpaw-field-input">
|
||
<select id="image_model" name="wp_agentic_writer_settings[image_model]" class="wpaw-select2-model" style="width: 100%;">
|
||
<option value="<?php echo esc_attr( $image_model ); ?>"><?php echo esc_html( $image_model ); ?></option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Cost Estimate Display -->
|
||
<div class="wpaw-cost-estimate-box">
|
||
<div class="wpaw-cost-estimate-label"><?php esc_html_e( 'Estimated Cost Per Article:', 'wp-agentic-writer' ); ?></div>
|
||
<div id="wpaw-cost-estimate" class="wpaw-cost-estimate-value">~$0.00 per article</div>
|
||
<p class="wpaw-cost-estimate-note"><?php esc_html_e( 'Based on ~2K planning tokens, ~4K execution tokens, and 1 image.', 'wp-agentic-writer' ); ?></p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- END MODELS TAB -->
|
||
|
||
<!-- ==================== COST LOG TAB ==================== -->
|
||
<div class="wpaw-tab-content" data-tab="cost-log">
|
||
<?php $this->render_cost_log_tab(); ?>
|
||
</div><!-- END COST LOG TAB -->
|
||
|
||
<!-- ==================== GUIDE TAB ==================== -->
|
||
<div class="wpaw-tab-content" data-tab="guide">
|
||
|
||
<!-- How Models Are Used -->
|
||
<div class="wpaw-settings-section">
|
||
<div class="wpaw-section-header">
|
||
<div class="wpaw-section-icon">📖</div>
|
||
<div>
|
||
<h2 class="wpaw-section-title"><?php esc_html_e( 'How AI Models Are Used', 'wp-agentic-writer' ); ?></h2>
|
||
<p class="wpaw-section-desc"><?php esc_html_e( 'Understanding which model handles each task', 'wp-agentic-writer' ); ?></p>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-section-body">
|
||
<table class="wpaw-guide-table">
|
||
<thead>
|
||
<tr>
|
||
<th><?php esc_html_e( 'Task', 'wp-agentic-writer' ); ?></th>
|
||
<th><?php esc_html_e( 'Model Used', 'wp-agentic-writer' ); ?></th>
|
||
<th><?php esc_html_e( 'Est. Cost', 'wp-agentic-writer' ); ?></th>
|
||
<th><?php esc_html_e( 'Description', 'wp-agentic-writer' ); ?></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td><strong><?php esc_html_e( 'Planning', 'wp-agentic-writer' ); ?></strong></td>
|
||
<td><span class="wpaw-model-badge planning"><?php esc_html_e( 'Planning Model', 'wp-agentic-writer' ); ?></span></td>
|
||
<td>~$0.001</td>
|
||
<td><?php esc_html_e( 'Creates article outline with sections and structure', 'wp-agentic-writer' ); ?></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong><?php esc_html_e( 'Clarity Check', 'wp-agentic-writer' ); ?></strong></td>
|
||
<td><span class="wpaw-model-badge clarity"><?php esc_html_e( 'Clarity Model', 'wp-agentic-writer' ); ?></span></td>
|
||
<td>~$0.0005</td>
|
||
<td><?php esc_html_e( 'Analyzes your prompt and asks clarifying questions', 'wp-agentic-writer' ); ?></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong><?php esc_html_e( 'Writing', 'wp-agentic-writer' ); ?></strong></td>
|
||
<td><span class="wpaw-model-badge execution"><?php esc_html_e( 'Writing Model', 'wp-agentic-writer' ); ?></span></td>
|
||
<td>~$0.05-0.15</td>
|
||
<td><?php esc_html_e( 'Generates full article content section by section', 'wp-agentic-writer' ); ?></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong><?php esc_html_e( 'Chat', 'wp-agentic-writer' ); ?></strong></td>
|
||
<td><span class="wpaw-model-badge chat"><?php esc_html_e( 'Chat Model', 'wp-agentic-writer' ); ?></span></td>
|
||
<td>~$0.001</td>
|
||
<td><?php esc_html_e( 'Answers questions and provides quick assistance', 'wp-agentic-writer' ); ?></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong><?php esc_html_e( 'Block Refinement', 'wp-agentic-writer' ); ?></strong></td>
|
||
<td><span class="wpaw-model-badge refinement"><?php esc_html_e( 'Refinement Model', 'wp-agentic-writer' ); ?></span></td>
|
||
<td>~$0.01-0.03</td>
|
||
<td><?php esc_html_e( 'Improves or rewrites selected content blocks', 'wp-agentic-writer' ); ?></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong><?php esc_html_e( 'Web Search', 'wp-agentic-writer' ); ?></strong></td>
|
||
<td><span class="wpaw-model-badge search"><?php esc_html_e( 'OpenRouter Plugin', 'wp-agentic-writer' ); ?></span></td>
|
||
<td>~$0.02</td>
|
||
<td><?php esc_html_e( 'Searches web for current information (toggle in sidebar)', 'wp-agentic-writer' ); ?></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong><?php esc_html_e( 'Image Generation', 'wp-agentic-writer' ); ?></strong></td>
|
||
<td><span class="wpaw-model-badge image"><?php esc_html_e( 'Image Model', 'wp-agentic-writer' ); ?></span></td>
|
||
<td>$0.003-0.04</td>
|
||
<td><?php esc_html_e( 'Creates images for articles', 'wp-agentic-writer' ); ?></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Recommended Models -->
|
||
<div class="wpaw-settings-section">
|
||
<div class="wpaw-section-header">
|
||
<div class="wpaw-section-icon">⭐</div>
|
||
<div>
|
||
<h2 class="wpaw-section-title"><?php esc_html_e( 'Recommended Models', 'wp-agentic-writer' ); ?></h2>
|
||
<p class="wpaw-section-desc"><?php esc_html_e( 'Best models for each purpose based on quality and cost', 'wp-agentic-writer' ); ?></p>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-section-body">
|
||
<table class="wpaw-guide-table">
|
||
<thead>
|
||
<tr>
|
||
<th><?php esc_html_e( 'Model', 'wp-agentic-writer' ); ?></th>
|
||
<th><?php esc_html_e( 'Best For', 'wp-agentic-writer' ); ?></th>
|
||
<th><?php esc_html_e( 'Cost (per 1M tokens)', 'wp-agentic-writer' ); ?></th>
|
||
<th><?php esc_html_e( 'Notes', 'wp-agentic-writer' ); ?></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="wpaw-guide-row-highlight">
|
||
<td><strong>Gemini 2.5 Flash</strong></td>
|
||
<td><?php esc_html_e( 'Chat, Clarity, Planning', 'wp-agentic-writer' ); ?></td>
|
||
<td>$0.15 / $0.60</td>
|
||
<td><?php esc_html_e( '⭐ Best balanced option. Fast reasoning.', 'wp-agentic-writer' ); ?></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Gemini 3 Flash Preview</strong></td>
|
||
<td><?php esc_html_e( 'Chat, Clarity, Planning (Premium)', 'wp-agentic-writer' ); ?></td>
|
||
<td>$0.50 / $3.00</td>
|
||
<td><?php esc_html_e( '🏆 Latest Gemini. Near Pro performance.', 'wp-agentic-writer' ); ?></td>
|
||
</tr>
|
||
<tr class="wpaw-guide-row-highlight">
|
||
<td><strong>Claude 3.5 Sonnet</strong></td>
|
||
<td><?php esc_html_e( 'Writing, Refinement', 'wp-agentic-writer' ); ?></td>
|
||
<td>$3.00 / $15.00</td>
|
||
<td><?php esc_html_e( '⭐ Best quality writing. Recommended.', 'wp-agentic-writer' ); ?></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Mistral Small Creative</strong></td>
|
||
<td><?php esc_html_e( 'Writing (Budget)', 'wp-agentic-writer' ); ?></td>
|
||
<td>$0.10 / $0.30</td>
|
||
<td><?php esc_html_e( '💰 Best budget writing. Creative style.', 'wp-agentic-writer' ); ?></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>GPT-4.1</strong></td>
|
||
<td><?php esc_html_e( 'Writing, Refinement (Premium)', 'wp-agentic-writer' ); ?></td>
|
||
<td>$2.00 / $8.00</td>
|
||
<td><?php esc_html_e( '🏆 Premium option. Excellent instruction following.', 'wp-agentic-writer' ); ?></td>
|
||
</tr>
|
||
<tr>
|
||
<td><strong>Claude Sonnet 4</strong></td>
|
||
<td><?php esc_html_e( 'Clarity (Premium)', 'wp-agentic-writer' ); ?></td>
|
||
<td>$3.00 / $15.00</td>
|
||
<td><?php esc_html_e( '🏆 Latest Claude. Deep analysis.', 'wp-agentic-writer' ); ?></td>
|
||
</tr>
|
||
<tr class="wpaw-guide-row-highlight">
|
||
<td><strong>GPT-4o</strong></td>
|
||
<td><?php esc_html_e( 'Image Prompts', 'wp-agentic-writer' ); ?></td>
|
||
<td>$2.50 / $10.00</td>
|
||
<td><?php esc_html_e( '⭐ Generates image descriptions for prompts.', 'wp-agentic-writer' ); ?></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Cost Examples -->
|
||
<div class="wpaw-settings-section">
|
||
<div class="wpaw-section-header">
|
||
<div class="wpaw-section-icon">💡</div>
|
||
<div>
|
||
<h2 class="wpaw-section-title"><?php esc_html_e( 'Cost Examples', 'wp-agentic-writer' ); ?></h2>
|
||
<p class="wpaw-section-desc"><?php esc_html_e( 'Real-world cost estimates for common tasks', 'wp-agentic-writer' ); ?></p>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-section-body">
|
||
<div class="wpaw-cost-examples">
|
||
<div class="wpaw-cost-example">
|
||
<div class="wpaw-cost-example-title"><?php esc_html_e( 'Single Article (Balanced)', 'wp-agentic-writer' ); ?></div>
|
||
<div class="wpaw-cost-example-breakdown">
|
||
<span><?php esc_html_e( 'Clarity Check', 'wp-agentic-writer' ); ?>: ~$0.001</span>
|
||
<span><?php esc_html_e( 'Planning', 'wp-agentic-writer' ); ?>: ~$0.001</span>
|
||
<span><?php esc_html_e( 'Writing (5 sections)', 'wp-agentic-writer' ); ?>: ~$0.10</span>
|
||
<span><?php esc_html_e( '1 Image', 'wp-agentic-writer' ); ?>: ~$0.003</span>
|
||
</div>
|
||
<div class="wpaw-cost-example-total"><?php esc_html_e( 'Total', 'wp-agentic-writer' ); ?>: <strong>~$0.10-0.15</strong></div>
|
||
</div>
|
||
<div class="wpaw-cost-example">
|
||
<div class="wpaw-cost-example-title"><?php esc_html_e( '10 Articles/Month (Balanced)', 'wp-agentic-writer' ); ?></div>
|
||
<div class="wpaw-cost-example-breakdown">
|
||
<span><?php esc_html_e( '10 articles × $0.15', 'wp-agentic-writer' ); ?></span>
|
||
<span><?php esc_html_e( 'Some chat interactions', 'wp-agentic-writer' ); ?>: ~$0.05</span>
|
||
<span><?php esc_html_e( 'Refinements', 'wp-agentic-writer' ); ?>: ~$0.20</span>
|
||
</div>
|
||
<div class="wpaw-cost-example-total"><?php esc_html_e( 'Total', 'wp-agentic-writer' ); ?>: <strong>~$1.50-2.00</strong></div>
|
||
</div>
|
||
<div class="wpaw-cost-example">
|
||
<div class="wpaw-cost-example-title"><?php esc_html_e( 'With Web Search', 'wp-agentic-writer' ); ?></div>
|
||
<div class="wpaw-cost-example-breakdown">
|
||
<span><?php esc_html_e( 'Add ~$0.02 per search request', 'wp-agentic-writer' ); ?></span>
|
||
<span><?php esc_html_e( 'Typical: 1-2 searches per article', 'wp-agentic-writer' ); ?></span>
|
||
</div>
|
||
<div class="wpaw-cost-example-total"><?php esc_html_e( 'Extra', 'wp-agentic-writer' ); ?>: <strong>~$0.02-0.04</strong></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- END GUIDE TAB -->
|
||
|
||
<!-- Submit Section -->
|
||
<div class="wpaw-submit-section">
|
||
<div class="wpaw-submit-info">
|
||
<?php esc_html_e( 'Changes are saved immediately after clicking Save.', 'wp-agentic-writer' ); ?>
|
||
</div>
|
||
<?php submit_button( __( 'Save Settings', 'wp-agentic-writer' ), 'primary', 'submit', false ); ?>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<script>
|
||
function wpawApplyPreset(preset) {
|
||
// Preset configurations with valid OpenRouter model IDs
|
||
const presets = {
|
||
budget: {
|
||
chat: 'google/gemini-2.5-flash',
|
||
clarity: 'google/gemini-2.5-flash',
|
||
planning: 'google/gemini-2.5-flash',
|
||
writing: 'mistralai/mistral-small-creative',
|
||
refinement: 'google/gemini-2.5-flash',
|
||
image: 'openai/gpt-4o'
|
||
},
|
||
balanced: {
|
||
chat: 'google/gemini-2.5-flash',
|
||
clarity: 'google/gemini-2.5-flash',
|
||
planning: 'google/gemini-2.5-flash',
|
||
writing: 'anthropic/claude-3.5-sonnet',
|
||
refinement: 'anthropic/claude-3.5-sonnet',
|
||
image: 'openai/gpt-4o'
|
||
},
|
||
premium: {
|
||
chat: 'google/gemini-3-flash-preview',
|
||
clarity: 'anthropic/claude-sonnet-4',
|
||
planning: 'google/gemini-3-flash-preview',
|
||
writing: 'openai/gpt-4.1',
|
||
refinement: 'openai/gpt-4.1',
|
||
image: 'openai/gpt-4o'
|
||
}
|
||
};
|
||
|
||
const config = presets[preset];
|
||
if (!config) return;
|
||
|
||
const $ = jQuery;
|
||
const selectors = {
|
||
chat: $('#chat_model'),
|
||
clarity: $('#clarity_model'),
|
||
planning: $('#planning_model'),
|
||
writing: $('#writing_model'),
|
||
refinement: $('#refinement_model'),
|
||
image: $('#image_model')
|
||
};
|
||
|
||
// Helper to set model value, adding option if needed
|
||
const setModelValue = ($select, targetId) => {
|
||
if (!$select.length) return;
|
||
|
||
// Check if option exists
|
||
if (!$select.find(`option[value="${targetId}"]`).length) {
|
||
// Add the option if it doesn't exist
|
||
const displayName = targetId.split('/').pop().replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
||
$select.append($('<option>', { value: targetId, text: displayName }));
|
||
}
|
||
|
||
// Set value and trigger change for Select2
|
||
$select.val(targetId).trigger('change');
|
||
};
|
||
|
||
// Apply each model
|
||
Object.keys(config).forEach(type => {
|
||
const $select = selectors[type];
|
||
if (!$select || !$select.length) return;
|
||
setModelValue($select, config[type]);
|
||
});
|
||
|
||
// Update selected state
|
||
document.querySelectorAll('.wpaw-preset-card').forEach(card => {
|
||
card.classList.remove('selected');
|
||
});
|
||
document.querySelector(`[data-preset="${preset}"]`)?.classList.add('selected');
|
||
}
|
||
</script>
|
||
<?php
|
||
}
|
||
|
||
/**
|
||
* Render language preferences section.
|
||
*
|
||
* @since 0.1.0
|
||
* @param array $settings Plugin settings.
|
||
*/
|
||
private function render_language_preferences( $settings ) {
|
||
$preferred_languages = $settings['preferred_languages'] ?? array( 'auto', 'English', 'Indonesian' );
|
||
$custom_languages = $settings['custom_languages'] ?? array();
|
||
|
||
// Predefined language list
|
||
$available_languages = array(
|
||
'auto' => 'Auto-detect',
|
||
'English' => 'English',
|
||
'Indonesian' => 'Indonesian (Bahasa Indonesia)',
|
||
'Javanese' => 'Javanese (Basa Jawa)',
|
||
'Sundanese' => 'Sundanese (Basa Sunda)',
|
||
'Spanish' => 'Spanish (Español)',
|
||
'French' => 'French (Français)',
|
||
'Arabic' => 'Arabic (العربية)',
|
||
'Chinese' => 'Chinese (中文)',
|
||
'Japanese' => 'Japanese (日本語)',
|
||
'Portuguese' => 'Portuguese (Português)',
|
||
'German' => 'German (Deutsch)',
|
||
'Hindi' => 'Hindi (हिंदी)',
|
||
'Korean' => 'Korean (한국어)',
|
||
'Vietnamese' => 'Vietnamese (Tiếng Việt)',
|
||
'Thai' => 'Thai (ไทย)',
|
||
'Tagalog' => 'Tagalog',
|
||
'Malay' => 'Malay (Bahasa Melayu)',
|
||
'Russian' => 'Russian (Русский)',
|
||
'Italian' => 'Italian (Italiano)',
|
||
'Dutch' => 'Dutch (Nederlands)',
|
||
'Polish' => 'Polish (Polski)',
|
||
'Turkish' => 'Turkish (Türkçe)',
|
||
'Swedish' => 'Swedish (Svenska)',
|
||
);
|
||
?>
|
||
<div class="wpaw-field-row">
|
||
<div class="wpaw-field-label">
|
||
<?php esc_html_e( 'Preferred Languages', 'wp-agentic-writer' ); ?>
|
||
<small><?php esc_html_e( 'Languages available when creating articles', 'wp-agentic-writer' ); ?></small>
|
||
</div>
|
||
<div class="wpaw-field-input">
|
||
<div class="wpaw-language-grid">
|
||
<?php foreach ( $available_languages as $code => $label ) : ?>
|
||
<label class="wpaw-language-checkbox">
|
||
<input
|
||
type="checkbox"
|
||
name="wp_agentic_writer_settings[preferred_languages][]"
|
||
value="<?php echo esc_attr( $code ); ?>"
|
||
<?php checked( in_array( $code, $preferred_languages, true ) ); ?>
|
||
/>
|
||
<span><?php echo esc_html( $label ); ?></span>
|
||
</label>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<p class="description">
|
||
<?php esc_html_e( 'Select which languages your site will use. These will appear in the language selector when creating articles.', 'wp-agentic-writer' ); ?>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="wpaw-field-row">
|
||
<div class="wpaw-field-label">
|
||
<?php esc_html_e( 'Custom Languages', 'wp-agentic-writer' ); ?>
|
||
<small><?php esc_html_e( 'Add languages not in the list above', 'wp-agentic-writer' ); ?></small>
|
||
</div>
|
||
<div class="wpaw-field-input">
|
||
<div id="wpaw-custom-languages-list">
|
||
<?php if ( ! empty( $custom_languages ) ) : ?>
|
||
<?php foreach ( $custom_languages as $index => $lang ) : ?>
|
||
<div class="wpaw-custom-language-item">
|
||
<input
|
||
type="text"
|
||
name="wp_agentic_writer_settings[custom_languages][]"
|
||
value="<?php echo esc_attr( $lang ); ?>"
|
||
placeholder="<?php esc_attr_e( 'e.g., Betawi, Minangkabau', 'wp-agentic-writer' ); ?>"
|
||
/>
|
||
<button type="button" class="button wpaw-remove-language">✗</button>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</div>
|
||
<button type="button" id="wpaw-add-custom-language" class="button">
|
||
<?php esc_html_e( '+ Add Custom Language', 'wp-agentic-writer' ); ?>
|
||
</button>
|
||
<p class="description">
|
||
<?php esc_html_e( 'Add any language not listed above (e.g., regional dialects, minority languages).', 'wp-agentic-writer' ); ?>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
jQuery(document).ready(function($) {
|
||
// Add custom language
|
||
$('#wpaw-add-custom-language').on('click', function() {
|
||
const html = `
|
||
<div class="wpaw-custom-language-item">
|
||
<input
|
||
type="text"
|
||
name="wp_agentic_writer_settings[custom_languages][]"
|
||
value=""
|
||
placeholder="<?php esc_attr_e( 'e.g., Betawi, Minangkabau', 'wp-agentic-writer' ); ?>"
|
||
/>
|
||
<button type="button" class="button wpaw-remove-language">✗</button>
|
||
</div>
|
||
`;
|
||
$('#wpaw-custom-languages-list').append(html);
|
||
});
|
||
|
||
// Remove custom language
|
||
$(document).on('click', '.wpaw-remove-language', function() {
|
||
$(this).closest('.wpaw-custom-language-item').remove();
|
||
});
|
||
});
|
||
</script>
|
||
<?php
|
||
}
|
||
|
||
/**
|
||
* Render Cost Log tab content.
|
||
*
|
||
* @since 0.1.0
|
||
*/
|
||
private function render_cost_log_tab() {
|
||
global $wpdb;
|
||
$table_name = $wpdb->prefix . 'wpaw_cost_tracking';
|
||
|
||
// Get filter parameters
|
||
$filter_post = isset( $_GET['filter_post'] ) ? intval( $_GET['filter_post'] ) : 0;
|
||
$filter_model = isset( $_GET['filter_model'] ) ? sanitize_text_field( $_GET['filter_model'] ) : '';
|
||
$filter_type = isset( $_GET['filter_type'] ) ? sanitize_text_field( $_GET['filter_type'] ) : '';
|
||
$filter_date_from = isset( $_GET['filter_date_from'] ) ? sanitize_text_field( $_GET['filter_date_from'] ) : '';
|
||
$filter_date_to = isset( $_GET['filter_date_to'] ) ? sanitize_text_field( $_GET['filter_date_to'] ) : '';
|
||
$per_page = 50;
|
||
$paged = isset( $_GET['paged'] ) ? max( 1, intval( $_GET['paged'] ) ) : 1;
|
||
$offset = ( $paged - 1 ) * $per_page;
|
||
|
||
// Build query
|
||
$where = array( '1=1' );
|
||
if ( $filter_post > 0 ) {
|
||
$where[] = $wpdb->prepare( 'post_id = %d', $filter_post );
|
||
}
|
||
if ( ! empty( $filter_model ) ) {
|
||
$where[] = $wpdb->prepare( 'model = %s', $filter_model );
|
||
}
|
||
if ( ! empty( $filter_type ) ) {
|
||
$where[] = $wpdb->prepare( 'action = %s', $filter_type );
|
||
}
|
||
if ( ! empty( $filter_date_from ) ) {
|
||
$where[] = $wpdb->prepare( 'DATE(created_at) >= %s', $filter_date_from );
|
||
}
|
||
if ( ! empty( $filter_date_to ) ) {
|
||
$where[] = $wpdb->prepare( 'DATE(created_at) <= %s', $filter_date_to );
|
||
}
|
||
$where_clause = implode( ' AND ', $where );
|
||
|
||
// Get total count
|
||
$total_items = $wpdb->get_var( "SELECT COUNT(*) FROM {$table_name} WHERE {$where_clause}" );
|
||
$total_pages = ceil( $total_items / $per_page );
|
||
|
||
// Get cost records
|
||
$costs = $wpdb->get_results(
|
||
"SELECT * FROM {$table_name} WHERE {$where_clause} ORDER BY created_at DESC LIMIT {$per_page} OFFSET {$offset}"
|
||
);
|
||
|
||
// Get summary stats
|
||
$cost_tracker = WP_Agentic_Writer_Cost_Tracker::get_instance();
|
||
$total_all_time = $wpdb->get_var( "SELECT SUM(cost) FROM {$table_name}" );
|
||
$monthly_total = $cost_tracker->get_monthly_total();
|
||
$today_total = $wpdb->get_var(
|
||
$wpdb->prepare(
|
||
"SELECT SUM(cost) FROM {$table_name} WHERE DATE(created_at) = %s",
|
||
current_time( 'Y-m-d' )
|
||
)
|
||
);
|
||
$total_posts = $wpdb->get_var( "SELECT COUNT(DISTINCT post_id) FROM {$table_name} WHERE post_id > 0" );
|
||
$avg_per_post = $total_posts > 0 ? $total_all_time / $total_posts : 0;
|
||
|
||
// Get unique models and types for filters
|
||
$models = $wpdb->get_col( "SELECT DISTINCT model FROM {$table_name} ORDER BY model" );
|
||
$types = $wpdb->get_col( "SELECT DISTINCT action FROM {$table_name} ORDER BY action" );
|
||
|
||
?>
|
||
<!-- Summary Stats -->
|
||
<div class="wpaw-settings-section">
|
||
<div class="wpaw-section-header">
|
||
<div class="wpaw-section-icon">📊</div>
|
||
<div>
|
||
<h2 class="wpaw-section-title"><?php esc_html_e( 'Cost Summary', 'wp-agentic-writer' ); ?></h2>
|
||
<p class="wpaw-section-desc"><?php esc_html_e( 'Overview of your API spending', 'wp-agentic-writer' ); ?></p>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-section-body">
|
||
<div class="wpaw-cost-stats-grid">
|
||
<div class="wpaw-cost-stat-card">
|
||
<div class="wpaw-cost-stat-icon">💰</div>
|
||
<div class="wpaw-cost-stat-value">$<?php echo number_format( (float) $total_all_time, 4 ); ?></div>
|
||
<div class="wpaw-cost-stat-label"><?php esc_html_e( 'All Time', 'wp-agentic-writer' ); ?></div>
|
||
</div>
|
||
<div class="wpaw-cost-stat-card">
|
||
<div class="wpaw-cost-stat-icon">📅</div>
|
||
<div class="wpaw-cost-stat-value">$<?php echo number_format( (float) $monthly_total, 4 ); ?></div>
|
||
<div class="wpaw-cost-stat-label"><?php esc_html_e( 'This Month', 'wp-agentic-writer' ); ?></div>
|
||
</div>
|
||
<div class="wpaw-cost-stat-card">
|
||
<div class="wpaw-cost-stat-icon">☀️</div>
|
||
<div class="wpaw-cost-stat-value">$<?php echo number_format( (float) $today_total, 4 ); ?></div>
|
||
<div class="wpaw-cost-stat-label"><?php esc_html_e( 'Today', 'wp-agentic-writer' ); ?></div>
|
||
</div>
|
||
<div class="wpaw-cost-stat-card">
|
||
<div class="wpaw-cost-stat-icon">📝</div>
|
||
<div class="wpaw-cost-stat-value">$<?php echo number_format( (float) $avg_per_post, 4 ); ?></div>
|
||
<div class="wpaw-cost-stat-label"><?php esc_html_e( 'Avg Per Post', 'wp-agentic-writer' ); ?></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Filters -->
|
||
<div class="wpaw-settings-section">
|
||
<div class="wpaw-section-header">
|
||
<div class="wpaw-section-icon">🔍</div>
|
||
<div>
|
||
<h2 class="wpaw-section-title"><?php esc_html_e( 'Filter Cost Log', 'wp-agentic-writer' ); ?></h2>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-section-body">
|
||
<form method="get" class="wpaw-cost-filters">
|
||
<input type="hidden" name="page" value="wp-agentic-writer-settings" />
|
||
<input type="hidden" name="tab" value="cost-log" />
|
||
|
||
<div class="wpaw-filter-row">
|
||
<div class="wpaw-filter-field">
|
||
<label><?php esc_html_e( 'Post ID', 'wp-agentic-writer' ); ?></label>
|
||
<input type="number" name="filter_post" value="<?php echo esc_attr( $filter_post ); ?>" placeholder="<?php esc_attr_e( 'All posts', 'wp-agentic-writer' ); ?>" />
|
||
</div>
|
||
|
||
<div class="wpaw-filter-field">
|
||
<label><?php esc_html_e( 'Model', 'wp-agentic-writer' ); ?></label>
|
||
<select name="filter_model">
|
||
<option value=""><?php esc_html_e( 'All models', 'wp-agentic-writer' ); ?></option>
|
||
<?php foreach ( $models as $model ) : ?>
|
||
<option value="<?php echo esc_attr( $model ); ?>" <?php selected( $filter_model, $model ); ?>>
|
||
<?php echo esc_html( $model ); ?>
|
||
</option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="wpaw-filter-field">
|
||
<label><?php esc_html_e( 'Type', 'wp-agentic-writer' ); ?></label>
|
||
<select name="filter_type">
|
||
<option value=""><?php esc_html_e( 'All types', 'wp-agentic-writer' ); ?></option>
|
||
<?php foreach ( $types as $type ) : ?>
|
||
<option value="<?php echo esc_attr( $type ); ?>" <?php selected( $filter_type, $type ); ?>>
|
||
<?php echo esc_html( ucfirst( str_replace( '_', ' ', $type ) ) ); ?>
|
||
</option>
|
||
<?php endforeach; ?>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="wpaw-filter-field">
|
||
<label><?php esc_html_e( 'Date From', 'wp-agentic-writer' ); ?></label>
|
||
<input type="date" name="filter_date_from" value="<?php echo esc_attr( $filter_date_from ); ?>" />
|
||
</div>
|
||
|
||
<div class="wpaw-filter-field">
|
||
<label><?php esc_html_e( 'Date To', 'wp-agentic-writer' ); ?></label>
|
||
<input type="date" name="filter_date_to" value="<?php echo esc_attr( $filter_date_to ); ?>" />
|
||
</div>
|
||
|
||
<div class="wpaw-filter-actions">
|
||
<button type="submit" class="button button-primary"><?php esc_html_e( 'Apply Filters', 'wp-agentic-writer' ); ?></button>
|
||
<a href="<?php echo esc_url( admin_url( 'options-general.php?page=wp-agentic-writer-settings&tab=cost-log' ) ); ?>" class="button"><?php esc_html_e( 'Clear', 'wp-agentic-writer' ); ?></a>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Cost Log Table -->
|
||
<div class="wpaw-settings-section">
|
||
<div class="wpaw-section-header">
|
||
<div class="wpaw-section-icon">📋</div>
|
||
<div>
|
||
<h2 class="wpaw-section-title"><?php esc_html_e( 'Detailed Cost Log', 'wp-agentic-writer' ); ?></h2>
|
||
<p class="wpaw-section-desc">
|
||
<?php
|
||
printf(
|
||
esc_html__( 'Showing %d-%d of %d records', 'wp-agentic-writer' ),
|
||
$offset + 1,
|
||
min( $offset + $per_page, $total_items ),
|
||
$total_items
|
||
);
|
||
?>
|
||
</p>
|
||
</div>
|
||
<div style="margin-left: auto;">
|
||
<button type="button" class="button" id="wpaw-export-csv">
|
||
<span class="dashicons dashicons-download"></span>
|
||
<?php esc_html_e( 'Export CSV', 'wp-agentic-writer' ); ?>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="wpaw-section-body">
|
||
<div class="wpaw-cost-log-table-wrapper">
|
||
<table class="wpaw-cost-log-table">
|
||
<thead>
|
||
<tr>
|
||
<th><?php _e( 'Date/Time', 'wp-agentic-writer' ); ?></th>
|
||
<th><?php _e( 'Post', 'wp-agentic-writer' ); ?></th>
|
||
<th><?php _e( 'Model', 'wp-agentic-writer' ); ?></th>
|
||
<th><?php _e( 'Type', 'wp-agentic-writer' ); ?></th>
|
||
<th><?php _e( 'Input Tokens', 'wp-agentic-writer' ); ?></th>
|
||
<th><?php _e( 'Output Tokens', 'wp-agentic-writer' ); ?></th>
|
||
<th><?php _e( 'Cost', 'wp-agentic-writer' ); ?></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php if ( empty( $costs ) ) : ?>
|
||
<tr>
|
||
<td colspan="7" style="text-align: center; padding: 2rem; color: #999;">
|
||
<?php esc_html_e( 'No cost records found.', 'wp-agentic-writer' ); ?>
|
||
</td>
|
||
</tr>
|
||
<?php else : ?>
|
||
<?php foreach ( $costs as $cost ) : ?>
|
||
<?php
|
||
$post_title = get_the_title( $cost->post_id );
|
||
if ( ! $post_title && $cost->post_id > 0 ) {
|
||
$post_title = sprintf( __( '[Removed Post #%d]', 'wp-agentic-writer' ), $cost->post_id );
|
||
$post_link = '#';
|
||
$post_class = 'wpaw-removed-post';
|
||
} elseif ( $cost->post_id > 0 ) {
|
||
$post_link = get_edit_post_link( $cost->post_id );
|
||
$post_class = '';
|
||
} else {
|
||
$post_title = '-';
|
||
$post_link = '#';
|
||
$post_class = '';
|
||
}
|
||
?>
|
||
<tr>
|
||
<td><?php echo esc_html( date_i18n( 'Y-m-d H:i:s', strtotime( $cost->created_at ) ) ); ?></td>
|
||
<td>
|
||
<?php if ( $post_link !== '#' ) : ?>
|
||
<a href="<?php echo esc_url( $post_link ); ?>" class="<?php echo esc_attr( $post_class ); ?>">
|
||
<?php echo esc_html( $post_title ); ?>
|
||
</a>
|
||
<?php else : ?>
|
||
<span class="<?php echo esc_attr( $post_class ); ?>"><?php echo esc_html( $post_title ); ?></span>
|
||
<?php endif; ?>
|
||
</td>
|
||
<td><code><?php echo esc_html( $cost->model ); ?></code></td>
|
||
<td><?php echo esc_html( ucfirst( str_replace( '_', ' ', $cost->action ) ) ); ?></td>
|
||
<td><?php echo number_format( $cost->input_tokens ); ?></td>
|
||
<td><?php echo number_format( $cost->output_tokens ); ?></td>
|
||
<td><strong>$<?php echo number_format( $cost->cost, 4 ); ?></strong></td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<?php if ( $total_pages > 1 ) : ?>
|
||
<div class="wpaw-pagination">
|
||
<?php
|
||
$base_url = add_query_arg(
|
||
array(
|
||
'page' => 'wp-agentic-writer-settings',
|
||
'tab' => 'cost-log',
|
||
'filter_post' => $filter_post,
|
||
'filter_model' => $filter_model,
|
||
'filter_type' => $filter_type,
|
||
'filter_date_from' => $filter_date_from,
|
||
'filter_date_to' => $filter_date_to,
|
||
),
|
||
admin_url( 'options-general.php' )
|
||
);
|
||
|
||
if ( $paged > 1 ) {
|
||
printf(
|
||
'<a href="%s" class="button">« %s</a>',
|
||
esc_url( add_query_arg( 'paged', $paged - 1, $base_url ) ),
|
||
esc_html__( 'Previous', 'wp-agentic-writer' )
|
||
);
|
||
}
|
||
|
||
printf(
|
||
'<span class="wpaw-pagination-info">%s %d / %d</span>',
|
||
esc_html__( 'Page', 'wp-agentic-writer' ),
|
||
$paged,
|
||
$total_pages
|
||
);
|
||
|
||
if ( $paged < $total_pages ) {
|
||
printf(
|
||
'<a href="%s" class="button">%s »</a>',
|
||
esc_url( add_query_arg( 'paged', $paged + 1, $base_url ) ),
|
||
esc_html__( 'Next', 'wp-agentic-writer' )
|
||
);
|
||
}
|
||
?>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
jQuery(document).ready(function($) {
|
||
// Export CSV functionality
|
||
$('#wpaw-export-csv').on('click', function() {
|
||
const table = $('.wpaw-cost-log-table');
|
||
let csv = [];
|
||
|
||
// Headers
|
||
const headers = [];
|
||
table.find('thead th').each(function() {
|
||
headers.push($(this).text());
|
||
});
|
||
csv.push(headers.join(','));
|
||
|
||
// Rows
|
||
table.find('tbody tr').each(function() {
|
||
const row = [];
|
||
$(this).find('td').each(function() {
|
||
let text = $(this).text().trim();
|
||
text = text.replace(/"/g, '""'); // Escape quotes
|
||
row.push('"' + text + '"');
|
||
});
|
||
if (row.length > 0) {
|
||
csv.push(row.join(','));
|
||
}
|
||
});
|
||
|
||
// Download
|
||
const csvContent = csv.join('\n');
|
||
const blob = new Blob([csvContent], { type: 'text/csv' });
|
||
const url = window.URL.createObjectURL(blob);
|
||
const a = document.createElement('a');
|
||
a.href = url;
|
||
a.download = 'wp-agentic-writer-costs-' + new Date().toISOString().split('T')[0] + '.csv';
|
||
document.body.appendChild(a);
|
||
a.click();
|
||
document.body.removeChild(a);
|
||
window.URL.revokeObjectURL(url);
|
||
});
|
||
});
|
||
</script>
|
||
<?php
|
||
}
|
||
}
|