文件操作 - api-tab.php
返回文件管理
返回主菜单
删除本文件
文件: /storage/v12552/avantibody/public_html/wp-content/plugins/call-tracking-metrics/views/api-tab.php
编辑文件内容
<?php /** * API Tab View * * This view file displays the API configuration tab in the CallTrackingMetrics * admin interface, allowing users to configure API credentials and test connections. * * @package CallTrackingMetrics * @subpackage Views * @author CallTrackingMetrics Team * @copyright 2024 CallTrackingMetrics * @license GPL-2.0+ * @version 2.0.0 * @link https://calltrackingmetrics.com * @since 1.0.0 */ // API Activity tab view // Data is passed from Options.php: $apiKey, $apiSecret, $apiStatus, $accountInfo $api_connected = ($apiStatus === 'connected'); $apiBaseUrl = isset($apiBaseUrl) ? rtrim($apiBaseUrl, '/') : ctm_get_api_url(); $availableApiEndpoints = isset($availableApiEndpoints) && is_array($availableApiEndpoints) ? $availableApiEndpoints : ctm_get_available_api_endpoints(); ?> <?php // Add nonces for AJAX actions $ctm_change_api_nonce = wp_create_nonce('ctm_change_api_keys'); $ctm_disable_api_nonce = wp_create_nonce('ctm_disable_api'); ?> <div class="space-y-6"> <?php if ($api_connected && !empty($accountInfo)): ?> <!-- Header Section --> <div class="bg-white border border-gray-200 rounded-lg shadow-sm"> <div class="px-6 py-4"> <div class="flex items-center justify-between"> <div class="flex items-center space-x-3"> <div class="w-6 h-6 bg-blue-500 rounded-lg flex items-center justify-center"> <svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/> </svg> </div> <div> <h1 class="text-xl font-semibold text-gray-900"><?php _e('API Dashboard', 'call-tracking-metrics'); ?></h1> <p class="text-gray-600 text-sm"><?php _e('Monitor your Call Tracking Metrics API connection and account status', 'call-tracking-metrics'); ?></p> </div> </div> <div class="flex space-x-2"> <button id="ctm-change-api-btn" class="bg-blue-50 hover:bg-blue-100 text-blue-700 px-4 py-2 rounded-lg font-medium transition-all duration-200 flex items-center space-x-2 border border-blue-200"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/> </svg> <span><?php _e('Change Keys', 'call-tracking-metrics'); ?></span> </button> <button id="ctm-disable-api-btn" class="bg-red-50 hover:bg-red-100 text-red-700 px-4 py-2 rounded-lg font-medium transition-all duration-200 flex items-center space-x-2 border border-red-200"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728L5.636 5.636m12.728 12.728L18.364 5.636M5.636 18.364l12.728-12.728"/> </svg> <span><?php _e('Disable API', 'call-tracking-metrics'); ?></span> </button> </div> </div> </div> </div> <!-- Dashboard Grid --> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6"> <!-- Account Information Card --> <div class="bg-white border border-gray-200 rounded-lg shadow-sm overflow-hidden"> <div class="px-6 py-4 border-b border-gray-200"> <div class="flex items-center space-x-3"> <div class="w-6 h-6 bg-blue-500 rounded-lg flex items-center justify-center"> <svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/> </svg> </div> <h3 class="text-lg font-semibold text-gray-900"><?php _e('Account Information', 'call-tracking-metrics'); ?></h3> <span class="ml-auto px-2 py-1 bg-green-100 text-green-800 text-xs font-medium rounded-full"><?php _e('Active', 'call-tracking-metrics'); ?></span> </div> </div> <div class="p-6"> <?php if (isset($accountInfo['account']) && is_array($accountInfo['account'])): ?> <?php $account = $accountInfo['account']; ?> <div class="space-y-3"> <div class="flex justify-between items-center py-2"> <span class="text-gray-600 text-sm"><?php _e('Account Name', 'call-tracking-metrics'); ?></span> <span class="font-medium text-gray-900"><?= esc_html($account['name'] ?? 'N/A') ?></span> </div> <div class="flex justify-between items-center py-2"> <span class="text-gray-600 text-sm"><?php _e('Account ID', 'call-tracking-metrics'); ?></span> <span class="font-mono font-medium text-gray-900"><?= esc_html($account['id'] ?? 'N/A') ?></span> </div> <div class="flex justify-between items-center py-2"> <span class="text-gray-600 text-sm"><?php _e('Status', 'call-tracking-metrics'); ?></span> <span class="px-2 py-1 bg-green-100 text-green-800 text-xs font-medium rounded-full flex items-center space-x-1"> <div class="w-1.5 h-1.5 bg-green-500 rounded-full"></div> <span><?= esc_html(ucfirst($account['status'] ?? 'active')) ?></span> </span> </div> <?php if (!empty($account['phone'])): ?> <div class="flex justify-between items-center py-2"> <span class="text-gray-600 text-sm"><?php _e('Phone', 'call-tracking-metrics'); ?></span> <span class="font-medium text-gray-900"><?= esc_html($account['phone']) ?></span> </div> <?php endif; ?> <?php if (!empty($account['website'])): ?> <div class="flex justify-between items-center py-2"> <span class="text-gray-600 text-sm"><?php _e('Website', 'call-tracking-metrics'); ?></span> <a href="<?= esc_url($account['website']) ?>" target="_blank" class="text-blue-600 hover:text-blue-800 underline font-medium text-sm"><?= esc_html($account['website']) ?></a> </div> <?php endif; ?> <?php if (!empty($account['plan'])): ?> <div class="flex justify-between items-center py-2"> <span class="text-gray-600 text-sm"><?php _e('Plan', 'call-tracking-metrics'); ?></span> <span class="font-medium text-gray-900"><?= esc_html($account['plan']) ?></span> </div> <?php endif; ?> </div> <?php else: ?> <div class="text-center py-8"> <svg class="w-12 h-12 text-gray-400 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/> </svg> <p class="text-gray-500 text-sm"><?php _e('Account information not available', 'call-tracking-metrics'); ?></p> </div> <?php endif; ?> </div> </div> <!-- API Connection Card --> <div class="bg-white border border-gray-200 rounded-lg shadow-sm overflow-hidden"> <div class="px-6 py-4 border-b border-gray-200"> <div class="flex items-center space-x-3"> <div class="w-6 h-6 bg-gray-500 rounded-lg flex items-center justify-center"> <svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/> </svg> </div> <h3 class="text-lg font-semibold text-gray-900"><?php _e('API Connection', 'call-tracking-metrics'); ?></h3> <span class="ml-auto px-2 py-1 bg-green-100 text-green-800 text-xs font-medium rounded-full"><?php _e('Connected', 'call-tracking-metrics'); ?></span> </div> </div> <div class="p-6"> <div class="space-y-3"> <div class="flex justify-between items-center py-2"> <span class="text-gray-600 text-sm"><?php _e('API Key', 'call-tracking-metrics'); ?></span> <span class="font-mono font-medium text-gray-900 bg-gray-50 px-2 py-1 rounded text-sm"><?= esc_html(substr($apiKey ?? '', 0, 8)) ?>••••••••</span> </div> <div class="flex justify-between items-center py-2"> <span class="text-gray-600 text-sm"><?php _e('Base URL', 'call-tracking-metrics'); ?></span> <span id="ctm-api-base-url-display" class="font-mono font-medium text-gray-900 text-sm break-all"><?= esc_html($apiBaseUrl) ?></span> </div> <div class="flex justify-between items-center py-2"> <span class="text-gray-600 text-sm"><?php _e('Last Connected', 'call-tracking-metrics'); ?></span> <span class="font-medium text-gray-900"><?= date('M j, Y g:i A') ?></span> </div> </div> <div class="mt-6"> <div class="bg-gray-50 border border-gray-200 rounded-xl p-5"> <div class="flex flex-col gap-4"> <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-3"> <div> <h4 class="text-base font-semibold text-gray-900"><?php _e('Switch API Endpoint', 'call-tracking-metrics'); ?></h4> <p class="text-sm text-gray-500 mt-1"><?php _e('Select the regional CallTrackingMetrics API that best fits your account.', 'call-tracking-metrics'); ?></p> </div> <span class="inline-flex items-center text-sm font-medium text-gray-600 bg-white border border-gray-200 rounded-full px-4 py-1.5 shadow-sm"> <span class="uppercase tracking-wide text-xs text-gray-400 mr-2"><?php _e('Current', 'call-tracking-metrics'); ?></span> <span class="font-mono text-gray-700"><?= esc_html(parse_url($apiBaseUrl, PHP_URL_HOST)); ?></span> </span> </div> <div class="flex flex-col md:flex-row md:items-end gap-3"> <div class="md:flex-1 md:max-w-md"> <label for="ctm_api_base_url_select" class="block text-xs font-semibold text-gray-600 mb-1 uppercase tracking-wide"><?php _e('Endpoint', 'call-tracking-metrics'); ?></label> <select id="ctm_api_base_url_select" class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm bg-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all shadow-sm"> <?php foreach ($availableApiEndpoints as $endpointUrl => $label): ?> <option value="<?= esc_attr($endpointUrl); ?>" <?= selected($apiBaseUrl, rtrim($endpointUrl, '/'), false); ?>> <?= esc_html($label); ?> (<?= esc_html(parse_url($endpointUrl, PHP_URL_HOST)); ?>) </option> <?php endforeach; ?> <?php if (!array_key_exists($apiBaseUrl, $availableApiEndpoints)): ?> <option value="<?= esc_attr($apiBaseUrl); ?>" selected> <?= sprintf(__('Custom (%s)', 'call-tracking-metrics'), esc_html($apiBaseUrl)); ?> </option> <?php endif; ?> </select> </div> <button type="button" id="ctm-update-endpoint-btn" class="inline-flex items-center justify-center bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 text-sm font-semibold rounded-full shadow-sm transition-all duration-200 gap-2 self-start"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path> </svg> <span><?php _e('Update', 'call-tracking-metrics'); ?></span> </button> </div> <div id="ctm-endpoint-update-message" class="text-xs"></div> </div> </div> </div> </div> </div> </div> <!-- Connection Monitor Card - Full Width --> <div class="bg-white border border-gray-200 rounded-lg shadow-sm overflow-hidden mb-6"> <div class="px-6 py-4 border-b border-gray-200"> <div class="flex items-center justify-between"> <div class="flex items-center space-x-3"> <div class="w-6 h-6 bg-green-500 rounded-lg flex items-center justify-center"> <svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/> </svg> </div> <h3 class="text-lg font-semibold text-gray-900"><?php _e('Connection Monitor', 'call-tracking-metrics'); ?></h3> <span id="ctm-status-badge" class="px-2 py-1 bg-green-100 text-green-800 text-xs font-medium rounded-full"><?php _e('Connected', 'call-tracking-metrics'); ?></span> </div> <div class="text-right"> <div id="ctm-last-test" class="text-xs text-gray-500"><?= date('g:i:s A') ?></div> </div> </div> </div> <div class="p-6"> <div class="flex flex-wrap gap-3 mb-6"> <button id="ctm-test-api-btn" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg font-medium transition-all duration-200 flex items-center space-x-2"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/> </svg> <span><?php _e('Test Connection', 'call-tracking-metrics'); ?></span> </button> </div> <!-- Progress Bar --> <div id="ctm-progress-container" class="hidden mb-6"> <div class="flex justify-between text-sm text-gray-600 mb-2"> <span class="font-medium"><?php _e('Connection Test Progress', 'call-tracking-metrics'); ?></span> <span id="ctm-progress-percent" class="font-semibold">0%</span> </div> <div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden"> <div id="ctm-progress-bar" class="bg-blue-600 h-2 rounded-full transition-all duration-300" style="width: 0%"></div> </div> </div> <!-- Test Logs --> <div id="ctm-test-logs" class="bg-gray-50 border border-gray-200 rounded-lg p-4 h-48 overflow-y-auto font-mono text-sm mt-4"> <div class="text-gray-500 italic"><?php _e('Click "Test Connection" to see real-time logs...', 'call-tracking-metrics'); ?></div> </div> </div> </div> <!-- Account Summary Card --> <div id="ctm-account-summary" class="hidden bg-white border border-gray-200 rounded-lg shadow-sm overflow-hidden"> <div class="px-6 py-4 border-b border-gray-200"> <div class="flex items-center space-x-3"> <div class="w-6 h-6 bg-green-500 rounded-lg flex items-center justify-center"> <svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/> </svg> </div> <h3 class="text-lg font-semibold text-gray-900"><?php _e('Test Results', 'call-tracking-metrics'); ?></h3> <span class="ml-auto px-2 py-1 bg-green-100 text-green-800 text-xs font-medium rounded-full"><?php _e('Success', 'call-tracking-metrics'); ?></span> </div> </div> <div class="p-6"> <div id="ctm-account-details" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"> <!-- Populated by JavaScript --> </div> </div> </div> <!-- Technical Details Card --> <div id="ctm-technical-details" class="hidden bg-white border border-gray-200 rounded-lg shadow-sm overflow-hidden"> <div class="px-6 py-4 border-b border-gray-200"> <div class="flex items-center justify-between"> <div class="flex items-center space-x-3"> <div class="w-6 h-6 bg-gray-500 rounded-lg flex items-center justify-center"> <svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z"/> </svg> </div> <h3 class="text-lg font-semibold text-gray-900"><?php _e('Technical Details', 'call-tracking-metrics'); ?></h3> </div> <button type="button" id="ctm-toggle-technical" class="text-gray-400 hover:text-gray-600 transition-colors"> <svg class="w-5 h-5 transform transition-transform duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/> </svg> </button> </div> </div> <div id="ctm-technical-content" class="hidden p-6"> <div id="ctm-tech-details-content" class="space-y-6"> <!-- Populated by JavaScript --> </div> </div> </div> <!-- Modals --> <!-- Change API Keys Modal --> <div id="ctm-change-api-modal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm hidden"> <div class="bg-white rounded-lg shadow-xl p-6 max-w-md w-full mx-4 relative border border-gray-200"> <div class="flex items-center space-x-3 mb-6"> <div class="w-6 h-6 bg-blue-500 rounded-lg flex items-center justify-center"> <svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/> </svg> </div> <h3 class="text-lg font-semibold text-gray-900"><?php _e('Change API Keys', 'call-tracking-metrics'); ?></h3> </div> <form id="ctm-change-api-form"> <div class="mb-4"> <label for="ctm_new_api_key" class="block text-gray-700 font-medium mb-2"><?php _e('API Key', 'call-tracking-metrics'); ?></label> <input type="text" id="ctm_new_api_key" name="ctm_new_api_key" class="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all" required autocomplete="off"> </div> <div class="mb-6"> <label for="ctm_new_api_secret" class="block text-gray-700 font-medium mb-2"><?php _e('API Secret', 'call-tracking-metrics'); ?></label> <input type="text" id="ctm_new_api_secret" name="ctm_new_api_secret" class="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all" required autocomplete="off"> </div> <div class="mb-6"> <label for="ctm_new_api_base_url" class="block text-gray-700 font-medium mb-2"><?php _e('API Endpoint', 'call-tracking-metrics'); ?></label> <select id="ctm_new_api_base_url" name="ctm_new_api_base_url" class="w-full border border-gray-300 rounded-lg px-3 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-all"> <?php foreach ($availableApiEndpoints as $endpointUrl => $label): ?> <option value="<?= esc_attr($endpointUrl); ?>" <?= selected($apiBaseUrl, rtrim($endpointUrl, '/'), false); ?>> <?= esc_html($label); ?> (<?= esc_html(parse_url($endpointUrl, PHP_URL_HOST)); ?>) </option> <?php endforeach; ?> <?php if (!array_key_exists($apiBaseUrl, $availableApiEndpoints)): ?> <option value="<?= esc_attr($apiBaseUrl); ?>" selected> <?= sprintf(__('Custom (%s)', 'call-tracking-metrics'), esc_html($apiBaseUrl)); ?> </option> <?php endif; ?> </select> <p class="text-gray-500 text-xs mt-2"><?php _e('Pick the regional endpoint that matches where your CTM account is hosted.', 'call-tracking-metrics'); ?></p> </div> <div class="flex justify-end space-x-3"> <button type="button" id="ctm-cancel-change-api" class="bg-gray-100 hover:bg-gray-200 text-gray-700 px-4 py-2 rounded-lg font-medium transition-all duration-200 flex items-center space-x-2"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/> </svg> <span><?php _e('Cancel', 'call-tracking-metrics'); ?></span> </button> <button type="submit" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg font-medium transition-all duration-200 flex items-center space-x-2"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/> </svg> <span><?php _e('Save Changes', 'call-tracking-metrics'); ?></span> </button> </div> </form> <button id="ctm-close-change-api" class="absolute top-4 right-4 text-gray-400 hover:text-gray-700 text-xl leading-none transition-colors">×</button> </div> </div> <!-- Disable API Modal --> <div id="ctm-disable-api-modal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm hidden"> <div class="bg-white rounded-lg shadow-xl p-6 max-w-md w-full mx-4 relative border border-gray-200"> <div class="flex items-center space-x-3 mb-6"> <div class="w-6 h-6 bg-red-500 rounded-lg flex items-center justify-center"> <svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728L5.636 5.636m12.728 12.728L18.364 5.636M5.636 18.364l12.728-12.728"/> </svg> </div> <h3 class="text-lg font-semibold text-red-700"><?php _e('Disable API', 'call-tracking-metrics'); ?></h3> </div> <div class="bg-red-50 rounded-lg p-4 border border-red-200 mb-6"> <div class="flex items-start space-x-3"> <svg class="w-5 h-5 text-red-500 mt-0.5 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.08 15.5c-.77.833.192 2.5 1.732 2.5z"/> </svg> <p class="text-red-800 text-sm leading-relaxed"><?php _e('Are you sure you want to disable the API and clear all credentials? This action cannot be undone.', 'call-tracking-metrics'); ?></p> </div> </div> <div class="flex justify-end space-x-3"> <button type="button" id="ctm-cancel-disable-api" class="bg-gray-100 hover:bg-gray-200 text-gray-700 px-4 py-2 rounded-lg font-medium transition-all duration-200 flex items-center space-x-2"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/> </svg> <span><?php _e('Cancel', 'call-tracking-metrics'); ?></span> </button> <button type="button" id="ctm-confirm-disable-api" class="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded-lg font-medium transition-all duration-200 flex items-center space-x-2"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/> </svg> <span><?php _e('Disable API', 'call-tracking-metrics'); ?></span> </button> </div> <button id="ctm-close-disable-api" class="absolute top-4 right-4 text-gray-400 hover:text-gray-700 text-xl leading-none transition-colors">×</button> </div> </div> <?php else: ?> <!-- Not Connected State --> <div class="bg-white border border-red-200 rounded-lg shadow-sm overflow-hidden"> <div class="px-6 py-4 border-b border-red-200"> <div class="flex items-center space-x-3"> <div class="w-6 h-6 bg-red-500 rounded-lg flex items-center justify-center"> <svg class="w-4 h-4 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.08 15.5c-.77.833.192 2.5 1.732 2.5z"/> </svg> </div> <h2 class="text-lg font-semibold text-gray-900"><?php _e('API Not Connected', 'call-tracking-metrics'); ?></h2> </div> </div> <div class="p-6 text-center"> <div class="w-16 h-16 mx-auto mb-4 bg-red-100 rounded-lg flex items-center justify-center"> <svg class="w-8 h-8 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.08 15.5c-.77.833.192 2.5 1.732 2.5z"/> </svg> </div> <h3 class="text-lg font-semibold text-gray-900 mb-2"><?php _e('API Connection Required', 'call-tracking-metrics'); ?></h3> <p class="text-gray-600 mb-6 text-sm leading-relaxed max-w-md mx-auto"> <?php _e('You need to configure your API credentials to view account information and API activity.', 'call-tracking-metrics'); ?> </p> <a href="?page=call-tracking-metrics&tab=general" class="inline-flex items-center px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white rounded-lg font-medium transition-all duration-200"> <svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/> </svg> <?php _e('Configure API Settings', 'call-tracking-metrics'); ?> </a> </div> </div> <?php endif; ?> </div> <script> // Localize script data for API tab var ctmApiData = { api_key: '<?= esc_js($apiKey ?? '') ?>', api_secret: '<?= esc_js($apiSecret ?? '') ?>', nonce: '<?= wp_create_nonce('ctm_test_api_connection') ?>', change_api_nonce: '<?= wp_create_nonce('ctm_change_api_keys') ?>', disable_api_nonce: '<?= wp_create_nonce('ctm_disable_api') ?>', api_base_url: '<?= esc_js($apiBaseUrl); ?>', api_endpoints: <?= wp_json_encode($availableApiEndpoints); ?>, custom_endpoint_label: '<?= esc_js(__('Custom (%s)', 'call-tracking-metrics')); ?>', endpoint_update_success: '<?= esc_js(__('API endpoint updated successfully.', 'call-tracking-metrics')); ?>', endpoint_update_error: '<?= esc_js(__('Failed to update API endpoint. Please try again.', 'call-tracking-metrics')); ?>', endpoint_update_in_progress: '<?= esc_js(__('Updating...', 'call-tracking-metrics')); ?>', initial_connected: <?= ($api_connected && !empty($accountInfo)) ? 'true' : 'false' ?> }; jQuery(document).ready(function($) { // Only initialize if we're on the API tab if ($('#ctm-test-logs').length === 0) { return; // Not on API tab, exit early } let isTestInProgress = false; // Update status indicators function updateStatus(success, message = '') { const indicator = $('#ctm-status-indicator'); const badge = $('#ctm-status-badge'); const lastTest = $('#ctm-last-test'); if (success) { indicator.removeClass('bg-red-500 bg-yellow-500').addClass('bg-green-500'); badge.removeClass('bg-red-100 text-red-800 bg-yellow-100 text-yellow-800').addClass('bg-green-100 text-green-800').text('Connected'); } else { indicator.removeClass('bg-green-500 bg-yellow-500').addClass('bg-red-500'); badge.removeClass('bg-green-100 text-green-800 bg-yellow-100 text-yellow-800').addClass('bg-red-100 text-red-800').text('Failed'); } lastTest.text('Last test: ' + new Date().toLocaleTimeString()); } // Perform API test function performApiTest() { if (isTestInProgress) return; isTestInProgress = true; const button = $('#ctm-test-api-btn'); const logs = $('#ctm-test-logs'); const progressContainer = $('#ctm-progress-container'); const progressBar = $('#ctm-progress-bar'); const progressPercent = $('#ctm-progress-percent'); // Update button state button.prop('disabled', true).html('<svg class="animate-spin w-4 h-4 mr-2" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>Testing...'); // Add test log appendLog('info', 'Starting API connection test...'); progressContainer.removeClass('hidden'); // AJAX call to test API $.ajax({ url: ajaxurl, type: 'POST', data: { action: 'ctm_test_api_connection', api_key: ctmApiData.api_key, api_secret: ctmApiData.api_secret, nonce: ctmApiData.nonce, api_base_url: ctmApiData.api_base_url }, success: function(response) { if (response.success) { updateStatus(true); progressBar.css('width', '100%'); progressPercent.text('100%'); let logHtml = ''; if (response.data.logs) { response.data.logs.forEach(log => { let bgClass = 'bg-gray-50'; let textClass = 'text-gray-700'; let icon = '•'; if (log.includes('✓') || log.includes('Success')) { bgClass = 'bg-green-50 border-l-4 border-green-500'; textClass = 'text-green-700'; icon = '✓'; } else if (log.includes('✗') || log.includes('Error')) { bgClass = 'bg-red-50 border-l-4 border-red-500'; textClass = 'text-red-700'; icon = '✗'; } else if (log.includes('→') || log.includes('Sending')) { bgClass = 'bg-blue-50 border-l-4 border-blue-500'; textClass = 'text-blue-700'; icon = '→'; } logHtml += `<div class="mb-2 p-2 rounded ${bgClass}"><span class="${textClass}">${icon} ${log}</span></div>`; }); } logs.html(logHtml); // Show duration if available if (response.data.duration) { logs.append(`<div class="mt-3 p-2 bg-gray-100 rounded text-center"><span class="text-gray-600">Total test duration: ${response.data.duration}ms</span></div>`); } displayAccountSummary(response.data.account_info.account, response.data.account_details?.account, response.data.capabilities); displayTechnicalDetails(response.data); displayPerformanceMetrics(response.data.performance, response.data.connection_quality); if (response.data.metadata && response.data.metadata.api_endpoint) { updateApiBaseUrl(response.data.metadata.api_endpoint); } } else { updateStatus(false, response.data || 'Test failed'); appendLog('error', `❌ ${response.data || 'Test failed'}`); } }, error: function() { updateStatus(false, 'AJAX request failed'); appendLog('error', '❌ AJAX request failed'); }, complete: function() { isTestInProgress = false; button.prop('disabled', false).html('<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path></svg>Test Now'); } }); } // Event handlers $('#ctm-test-api-btn').on('click', function() { performApiTest(); }); // Clear Logs Button $('#ctm-clear-logs-btn').on('click', function() { clearTestLogs(); }); // Technical Details Toggle $('#ctm-toggle-technical').on('click', function() { const content = $('#ctm-technical-content'); const icon = $(this).find('svg'); content.toggleClass('hidden'); icon.toggleClass('rotate-90'); }); // Quick endpoint update button $('#ctm-update-endpoint-btn').on('click', function() { const newBase = $('#ctm_api_base_url_select').val(); if (!newBase) { return; } const $button = $(this); const originalText = $button.text(); showEndpointMessage('', ''); $button.prop('disabled', true).addClass('opacity-75 cursor-wait').text(ctmApiData.endpoint_update_in_progress || originalText); $.post(ajaxurl, { action: 'ctm_change_api_keys', api_key: '', api_secret: '', api_base_url: newBase, nonce: ctmApiData.change_api_nonce }, function(resp) { if (resp.success) { updateApiBaseUrl(newBase); showEndpointMessage('success', ctmApiData.endpoint_update_success); } else { const errorMessage = resp.data && resp.data.message ? resp.data.message : ctmApiData.endpoint_update_error; showEndpointMessage('error', errorMessage); } }).fail(function() { showEndpointMessage('error', ctmApiData.endpoint_update_error); }).always(function() { $button.prop('disabled', false).removeClass('opacity-75 cursor-wait').text(originalText); }); }); // Modal logic for Change API Keys $('#ctm-change-api-btn').on('click', function() { ensureEndpointOption(ctmApiData.api_base_url); $('#ctm_new_api_base_url').val(ctmApiData.api_base_url); $('#ctm-change-api-modal').removeClass('hidden'); }); $('#ctm-close-change-api, #ctm-cancel-change-api').on('click', function() { $('#ctm-change-api-modal').addClass('hidden'); }); // AJAX submit for Change API Keys $('#ctm-change-api-form').on('submit', function(e) { e.preventDefault(); var apiKey = $('#ctm_new_api_key').val(); var apiSecret = $('#ctm_new_api_secret').val(); var nonce = ctmApiData.change_api_nonce; var apiBaseUrl = $('#ctm_new_api_base_url').val(); var $modal = $('#ctm-change-api-modal'); var $saveBtn = $(this).find('button[type=submit]'); $saveBtn.prop('disabled', true).text('Saving...'); $.post(ajaxurl, { action: 'ctm_change_api_keys', api_key: apiKey, api_secret: apiSecret, api_base_url: apiBaseUrl, nonce: nonce }, function(resp) { $saveBtn.prop('disabled', false).text('Save'); if (resp.success) { $modal.addClass('hidden'); alert('API keys updated successfully.'); location.reload(); } else { alert(resp.data && resp.data.message ? resp.data.message : 'Failed to update API keys.'); } }); }); // Modal logic for Disable API $('#ctm-disable-api-btn').on('click', function() { $('#ctm-disable-api-modal').removeClass('hidden'); }); $('#ctm-close-disable-api, #ctm-cancel-disable-api').on('click', function() { $('#ctm-disable-api-modal').addClass('hidden'); }); // AJAX for Disable API $('#ctm-confirm-disable-api').on('click', function() { var nonce = ctmApiData.disable_api_nonce; var $modal = $('#ctm-disable-api-modal'); var $btn = $(this); $btn.prop('disabled', true).text('Disabling...'); $.post(ajaxurl, { action: 'ctm_disable_api', nonce: nonce }, function(resp) { $btn.prop('disabled', false).text('Disable API'); if (resp.success) { $modal.addClass('hidden'); alert('API credentials cleared.'); location.reload(); } else { alert(resp.data && resp.data.message ? resp.data.message : 'Failed to disable API.'); } }); }); // Add initial connection status if (ctmApiData.initial_connected) { appendLog('success', `✓ [${new Date().toLocaleTimeString()}] API connection verified`); } function ensureEndpointOption(value) { if (!value) { return; } const selects = $('#ctm_new_api_base_url, #ctm_api_base_url_select'); if (!selects.length) { return; } selects.each(function() { const select = $(this); if (select.find('option[value="' + value + '"]').length === 0) { const label = ctmApiData.api_endpoints && ctmApiData.api_endpoints[value] ? ctmApiData.api_endpoints[value] : (ctmApiData.custom_endpoint_label || 'Custom (%s)').replace('%s', value); const option = $('<option>').attr('value', value).text(label); select.append(option); } }); } function updateApiBaseUrl(newUrl) { if (!newUrl) { return; } const normalized = newUrl.replace(/\/+$/, ''); ctmApiData.api_base_url = normalized; $('#ctm-api-base-url-display').text(normalized); ensureEndpointOption(normalized); $('#ctm_new_api_base_url').val(normalized); $('#ctm_api_base_url_select').val(normalized); } function showEndpointMessage(type, message) { const messageEl = $('#ctm-endpoint-update-message'); messageEl.removeClass(); messageEl.addClass('text-xs mt-2'); if (!message) { messageEl.text(''); return; } if (type === 'success') { messageEl.addClass('px-3 py-2 rounded border bg-green-50 border-green-200 text-green-700'); } else { messageEl.addClass('px-3 py-2 rounded border bg-red-50 border-red-200 text-red-700'); } messageEl.text(message); } function appendLog(type, message) { const logs = $('#ctm-test-logs'); const timestamp = new Date().toLocaleTimeString(); let colorClass = 'text-gray-600'; let bgClass = 'bg-gray-50'; let icon = '•'; switch(type) { case 'success': colorClass = 'text-green-700'; bgClass = 'bg-green-50'; icon = '✓'; break; case 'error': colorClass = 'text-red-700'; bgClass = 'bg-red-50'; icon = '✗'; break; case 'warning': colorClass = 'text-yellow-700'; bgClass = 'bg-yellow-50'; icon = '⚠'; break; case 'info': default: colorClass = 'text-blue-700'; bgClass = 'bg-blue-50'; icon = 'ⓘ'; break; } const logEntry = $(` <div class="${colorClass} ${bgClass} p-2 rounded border-l-2 border-current flex gap-2 items-start"> <span class="text-gray-400 text-xs font-mono mt-0.5">[${timestamp}]</span> <span class="font-bold mt-0.5">${icon}</span> <span class="flex-1 font-medium">${message}</span> </div> `); logs.append(logEntry); logs.scrollTop(logs[0].scrollHeight); } function displayAccountSummary(account, details, capabilities) { const accountSummary = $('#ctm-account-summary'); const accountDetails = $('#ctm-account-details'); accountSummary.removeClass('hidden'); let summaryHTML = ''; summaryHTML += `<div class="bg-gray-50 p-3 rounded border border-gray-200 text-sm"><span class="font-medium text-gray-700">Name:</span> <span class="text-gray-900">${account.name || 'N/A'}</span></div>`; summaryHTML += `<div class="bg-gray-50 p-3 rounded border border-gray-200 text-sm"><span class="font-medium text-gray-700">Account ID:</span> <code class="bg-gray-100 px-2 py-1 rounded text-xs font-mono">${account.id || 'N/A'}</code></div>`; if (account.email) { summaryHTML += `<div class="bg-gray-50 p-3 rounded border border-gray-200 text-sm"><span class="font-medium text-gray-700">Email:</span> <span class="text-gray-900">${account.email}</span></div>`; } if (capabilities) { const accessIcon = capabilities.account_access ? '✓' : '✗'; const detailsIcon = capabilities.details_access ? '✓' : '✗'; summaryHTML += `<div class="bg-gray-50 p-3 rounded border border-gray-200 text-sm"><span class="font-medium text-gray-700">API Access:</span> <span class="text-green-600">${accessIcon}</span> Account, <span class="${capabilities.details_access ? 'text-green-600' : 'text-yellow-600'}">${detailsIcon}</span> Details</div>`; summaryHTML += `<div class="bg-gray-50 p-3 rounded border border-gray-200 text-sm"><span class="font-medium text-gray-700">API Version:</span> <span class="text-gray-900">${capabilities.api_version}</span></div>`; } if (details) { if (details.status) { const statusColor = details.status === 'active' ? 'text-green-600' : 'text-yellow-600'; summaryHTML += `<div class="bg-gray-50 p-3 rounded border border-gray-200 text-sm"><span class="font-medium text-gray-700">Status:</span> <span class="${statusColor} font-semibold">${details.status}</span></div>`; } if (details.timezone) { summaryHTML += `<div class="bg-gray-50 p-3 rounded border border-gray-200 text-sm"><span class="font-medium text-gray-700">Timezone:</span> <span class="text-gray-900">${details.timezone}</span></div>`; } if (details.created_at) { const date = new Date(details.created_at).toLocaleDateString(); summaryHTML += `<div class="bg-gray-50 p-3 rounded border border-gray-200 text-sm"><span class="font-medium text-gray-700">Created:</span> <span class="text-gray-900">${date}</span></div>`; } if (details.phone) { summaryHTML += `<div class="bg-gray-50 p-3 rounded border border-gray-200 text-sm"><span class="font-medium text-gray-700">Phone:</span> <span class="text-gray-900">${details.phone}</span></div>`; } if (details.website) { summaryHTML += `<div class="bg-gray-50 p-3 rounded border border-gray-200 text-sm"><span class="font-medium text-gray-700">Website:</span> <a href="${details.website}" target="_blank" class="text-blue-600 underline">${details.website}</a></div>`; } if (details.plan || details.subscription) { summaryHTML += `<div class="bg-gray-50 p-3 rounded border border-gray-200 text-sm"><span class="font-medium text-gray-700">Plan:</span> <span class="text-gray-900">${details.plan || details.subscription || 'Not specified'}</span></div>`; } } accountDetails.html(summaryHTML); appendLog('success', `Account verified: ${account.name} (${account.id})`); } function displayTechnicalDetails(data) { const technicalDetails = $('#ctm-technical-details'); const techDetailsContent = $('#ctm-tech-details-content'); technicalDetails.removeClass('hidden'); let detailsHTML = ''; // Request Metadata Section if (data.metadata) { detailsHTML += ` <div class="bg-gray-50 border border-gray-200 rounded-lg p-4 mb-4"> <div class="flex items-center space-x-2 mb-3"> <div class="w-5 h-5 bg-blue-500 rounded-full flex items-center justify-center"> <svg class="w-3 h-3 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/> </svg> </div> <h4 class="text-sm font-semibold text-gray-900">Request Metadata</h4> </div> <div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-sm"> <div class="flex justify-between items-center"> <span class="text-gray-600 font-medium">Request ID:</span> <span class="font-mono font-medium text-gray-900 bg-gray-100 px-2 py-1 rounded text-xs">${data.metadata.request_id?.substring(0, 8) || 'N/A'}</span> </div> <div class="flex justify-between items-center"> <span class="text-gray-600 font-medium">WordPress:</span> <span class="font-medium text-gray-900">${data.metadata.wordpress_version || 'N/A'}</span> </div> <div class="flex justify-between items-center"> <span class="text-gray-600 font-medium">Plugin:</span> <span class="font-medium text-gray-900">${data.metadata.plugin_version || 'N/A'}</span> </div> <div class="flex justify-between items-center"> <span class="text-gray-600 font-medium">Timestamp:</span> <span class="font-medium text-gray-900">${data.metadata.timestamp || 'N/A'}</span> </div> <div class="flex justify-between items-center"> <span class="text-gray-600 font-medium">PHP:</span> <span class="font-medium text-gray-900">${data.metadata.php_version || 'N/A'}</span> </div> <div class="flex justify-between items-center"> <span class="text-gray-600 font-medium">Auth Method:</span> <span class="font-medium text-gray-900">${data.metadata.auth_method || 'N/A'}</span> </div> </div> </div> `; } // Performance Metrics Section if (data.performance) { detailsHTML += ` <div class="bg-gray-50 border border-gray-200 rounded-lg p-4 mb-4"> <div class="flex items-center space-x-2 mb-3"> <div class="w-5 h-5 bg-green-500 rounded-full flex items-center justify-center"> <svg class="w-3 h-3 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/> </svg> </div> <h4 class="text-sm font-semibold text-gray-900">Performance Metrics</h4> </div> <div class="grid grid-cols-1 md:grid-cols-2 gap-3 text-sm"> <div class="flex justify-between items-center"> <span class="text-gray-600 font-medium">Total Time:</span> <span class="font-medium text-gray-900">${data.performance.total_execution_time}ms</span> </div> <div class="flex justify-between items-center"> <span class="text-gray-600 font-medium">Details Response:</span> <span class="font-medium text-gray-900">${data.performance.details_response_time || 'N/A'}ms</span> </div> <div class="flex justify-between items-center"> <span class="text-gray-600 font-medium">API Response:</span> <span class="font-medium text-gray-900">${data.performance.api_response_time}ms</span> </div> <div class="flex justify-between items-center"> <span class="text-gray-600 font-medium">Network Overhead:</span> <span class="font-medium text-gray-900">${data.performance.network_overhead?.toFixed(1) || 'N/A'}ms</span> </div> </div> </div> `; } // API Endpoints Section if (data.metadata) { detailsHTML += ` <div class="bg-gray-50 border border-gray-200 rounded-lg p-4 mb-4"> <div class="flex items-center space-x-2 mb-3"> <div class="w-5 h-5 bg-purple-500 rounded-full flex items-center justify-center"> <svg class="w-3 h-3 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"/> </svg> </div> <h4 class="text-sm font-semibold text-gray-900">API Endpoints</h4> </div> <div class="space-y-3 text-sm"> <div class="flex justify-between items-center"> <span class="text-gray-600 font-medium">Base URL:</span> <span class="font-mono font-medium text-gray-900 bg-gray-100 px-2 py-1 rounded text-xs">${data.metadata.api_endpoint || 'N/A'}</span> </div> <div class="flex justify-between items-center"> <span class="text-gray-600 font-medium">Account:</span> <span class="font-mono font-medium text-gray-900 bg-gray-100 px-2 py-1 rounded text-xs">${data.metadata.account_endpoint || 'N/A'}</span> </div> ${data.metadata.details_endpoint ? ` <div class="flex justify-between items-center"> <span class="text-gray-600 font-medium">Details:</span> <span class="font-mono font-medium text-gray-900 bg-gray-100 px-2 py-1 rounded text-xs">${data.metadata.details_endpoint}</span> </div> ` : ''} </div> </div> `; } // Connection Quality Section if (data.connection_quality) { const quality = data.connection_quality; const qualityBadgeColor = quality.color === 'green' ? 'bg-green-100 text-green-800' : quality.color === 'blue' ? 'bg-blue-100 text-blue-800' : quality.color === 'yellow' ? 'bg-yellow-100 text-yellow-800' : 'bg-red-100 text-red-800'; detailsHTML += ` <div class="bg-gray-50 border border-gray-200 rounded-lg p-4 mb-4"> <div class="flex items-center space-x-2 mb-3"> <div class="w-5 h-5 bg-orange-500 rounded-full flex items-center justify-center"> <svg class="w-3 h-3 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/> </svg> </div> <h4 class="text-sm font-semibold text-gray-900">Connection Quality</h4> </div> <div class="flex items-center space-x-3 text-sm"> <span class="px-3 py-1 rounded ${qualityBadgeColor} font-medium text-xs">${quality.rating.toUpperCase()}</span> <span class="text-gray-600 font-medium">${quality.total_time}ms total response time</span> </div> </div> `; } // Raw Response Data (collapsible) const sanitizedData = JSON.parse(JSON.stringify(data)); if (sanitizedData.account_info?.account) { delete sanitizedData.account_info.account.api_key; delete sanitizedData.account_info.account.api_secret; } const jsonString = JSON.stringify(sanitizedData, null, 2); detailsHTML += ` <div class="bg-white border border-gray-200 rounded-lg p-4"> <button type="button" id="ctm-toggle-raw-data" class="flex items-center space-x-2 text-xs text-gray-600 hover:text-gray-800 transition-colors w-full text-left"> <svg id="ctm-raw-data-icon" class="w-3 h-3 transform transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/> </svg> <span>Raw API Response (JSON)</span> </button> <div id="ctm-raw-data-content" class="hidden mt-3"> <div class="bg-gray-50 border border-gray-200 rounded p-3"> <div class="text-xs text-gray-600 mb-2">Complete API Response (sensitive data removed)</div> <pre class="whitespace-pre-wrap text-xs overflow-x-auto text-gray-800">${escapeHtml(jsonString)}</pre> </div> </div> </div> `; techDetailsContent.html(detailsHTML); // Add event handler for raw data toggle $('#ctm-toggle-raw-data').on('click', function() { const content = $('#ctm-raw-data-content'); const icon = $('#ctm-raw-data-icon'); content.toggleClass('hidden'); icon.toggleClass('rotate-90'); }); } function escapeHtml(text) { return $('<div/>').text(text).html(); } function displayPerformanceMetrics(performance, quality) { if (quality) { appendLog('info', `${quality.description}`); appendLog('info', `API Response: ${performance.api_response_time}ms`); if (performance.details_response_time) { appendLog('info', `Details Response: ${performance.details_response_time}ms`); } if (performance.network_overhead > 0) { appendLog('info', `Network Overhead: ${performance.network_overhead.toFixed(1)}ms`); } } } }); </script>
修改文件时间
将文件时间修改为当前时间的前一年
删除文件