# thinkrank/1.0.0/src/admin/components/pages/Settings.js

ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console &amp; Local SEO, version 1.0.0. 785 lines.

- Page: https://pluginprobe.com/plugins/thinkrank/1.0.0/code/src/admin/components/pages/Settings.js
- Raw: https://pluginprobe.com/plugins/thinkrank/1.0.0/raw/src/admin/components/pages/Settings.js
- Modified: 2025-08-10T10:57:26+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/thinkrank/1.0.0/code/src/admin/components/pages/Settings.js#L10-L20`.

```javascript
/**
 * Settings Page Component
 *
 * Plugin settings and configuration
 *
 * @package ThinkRank
 * @since 1.0.0
 */

import { __ } from '@wordpress/i18n';
import { TextControl, SelectControl, Notice, Spinner, CheckboxControl } from '@wordpress/components';
import { useState, useEffect } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import ModelBadge from '../shared/ModelBadge';

// AI Provider Icons (using WordPress Dashicons as fallback)
const OpenAIIcon = () => (
    <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
        <path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z"/>
    </svg>
);

const ClaudeIcon = () => (
    <svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
        <path d="M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z" fillRule="nonzero"/>
    </svg>
);

const GeminiIcon = () => (
    <svg height="1em" style={{flex:'none',lineHeight:1}} viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg">
        <title>Gemini</title>
        <path d="M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z" fill="#3186FF"/>
        <defs>
            <linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-gemini-fill-0" x1="7" x2="11" y1="15.5" y2="12">
                <stop stopColor="#08B962"/>
                <stop offset="1" stopColor="#08B962" stopOpacity="0"/>
            </linearGradient>
            <linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-gemini-fill-1" x1="8" x2="11.5" y1="5.5" y2="11">
                <stop stopColor="#F94543"/>
                <stop offset="1" stopColor="#F94543" stopOpacity="0"/>
            </linearGradient>
            <linearGradient gradientUnits="userSpaceOnUse" id="lobe-icons-gemini-fill-2" x1="3.5" x2="17.5" y1="13.5" y2="12">
                <stop stopColor="#FABC12"/>
                <stop offset=".46" stopColor="#FABC12" stopOpacity="0"/>
            </linearGradient>
        </defs>
    </svg>
);

/**
 * Settings Component
 */
const Settings = () => {
    const [apiKey, setApiKey] = useState('');
    const [aiProvider, setAiProvider] = useState('openai');
    const [selectedModel, setSelectedModel] = useState('');
    // Provider-specific model states to avoid cross-provider bleed
    const [openaiModelState, setOpenaiModelState] = useState('gpt-5-nano');
    const [claudeModelState, setClaudeModelState] = useState('claude-3-7-sonnet-latest');
    const [geminiModelState, setGeminiModelState] = useState('gemini-2.5-flash');
    const [isSaving, setIsSaving] = useState(false);
    const [isLoading, setIsLoading] = useState(true);
    const [isTesting, setIsTesting] = useState(false);
    const [notice, setNotice] = useState(null);
    const [hasExistingKey, setHasExistingKey] = useState(false);
    const [deleteDataOnUninstall, setDeleteDataOnUninstall] = useState(false);

    // Model options for each provider (July 2025)
    const modelOptions = {
        openai: [
            { value: 'gpt-5-nano', label: 'GPT‑5‑nano (Default – fastest, lowest cost)' },
            { value: 'gpt-5-mini', label: 'GPT‑5‑mini (Balanced quality/cost)' },
            { value: 'gpt-5', label: 'GPT‑5 (Highest quality)' },
            { value: 'gpt-4o', label: 'GPT‑4o (Compatibility)' }
        ],
        claude: [
            { value: 'claude-sonnet-4-0', label: 'Claude 4 Sonnet (Latest)' },
            { value: 'claude-opus-4-0', label: 'Claude 4 Opus (Latest)' },
            { value: 'claude-3-7-sonnet-latest', label: 'Claude 3.7 Sonnet (Recommended)' },
            { value: 'claude-3-5-sonnet-latest', label: 'Claude 3.5 Sonnet' },
            { value: 'claude-3-5-haiku-latest', label: 'Claude 3.5 Haiku (Fast)' }
        ],
        gemini: [
            { value: 'gemini-2.5-flash', label: 'Gemini 2.5 Flash (Recommended)' },
            { value: 'gemini-2.5-flash-lite', label: 'Gemini 2.5 Flash-Lite (Fastest)' },
            { value: 'gemini-2.5-pro', label: 'Gemini 2.5 Pro' },
            { value: 'gemini-2.0-flash', label: 'Gemini 2.0 Flash' }
        ]
    };

    // Track if initial load is complete to prevent duplicate API calls
    const [initialLoadComplete, setInitialLoadComplete] = useState(false);

    // Load settings on component mount
    useEffect(() => {
        loadSettings();
    }, []);

    // Track the previous provider to detect actual user changes
    const [previousProvider, setPreviousProvider] = useState(null);

    // Handle provider changes (only for user-initiated changes)
    useEffect(() => {
        // Skip if this is the initial load, provider not set, or no previous provider to compare
        if (!initialLoadComplete || !aiProvider || previousProvider === null) {
            setPreviousProvider(aiProvider);
            return;
        }

        // Only proceed if this is an actual provider change (user-initiated)
        if (previousProvider !== aiProvider) {
            // Clear API key field when switching providers to avoid confusion
            setApiKey('');
            setHasExistingKey(false);

            // Set selectedModel to the appropriate provider-specific model
            if (aiProvider === 'openai') {
                setSelectedModel(openaiModelState);
            } else if (aiProvider === 'claude') {
                setSelectedModel(claudeModelState);
            } else if (aiProvider === 'gemini') {
                setSelectedModel(geminiModelState);
            }

            // Show "switched" message only for user-initiated changes
            const providerName = aiProvider === 'openai' ? 'OpenAI' : aiProvider === 'claude' ? 'Claude' : 'Gemini';
            setNotice({
                type: 'info',
                message: __(`Switched to ${providerName}. Please enter your API key if you haven't already.`, 'thinkrank')
            });

            // Update previous provider
            setPreviousProvider(aiProvider);
        }
    }, [aiProvider, initialLoadComplete, previousProvider, openaiModelState, claudeModelState, geminiModelState]);

    // API key format validation
    const validateApiKey = (key, provider) => {
        if (!key || !key.trim()) return { valid: true, message: '' };

        if (provider === 'openai') {
            if (!key.startsWith('sk-')) {
                return {
                    valid: false,
                    message: __('OpenAI API keys should start with "sk-"', 'thinkrank')
                };
            }
            if (key.length < 20) {
                return {
                    valid: false,
                    message: __('OpenAI API key appears too short', 'thinkrank')
                };
            }
        } else if (provider === 'claude') {
            if (!key.startsWith('sk-ant-')) {
                return {
                    valid: false,
                    message: __('Claude API keys should start with "sk-ant-"', 'thinkrank')
                };
            }
            if (key.length < 30) {
                return {
                    valid: false,
                    message: __('Claude API key appears too short', 'thinkrank')
                };
            }
        } else if (provider === 'gemini') {
            if (!key.startsWith('AIza')) {
                return {
                    valid: false,
                    message: __('Gemini API keys should start with "AIza"', 'thinkrank')
                };
            }
            if (key.length < 30) {
                return {
                    valid: false,
                    message: __('Gemini API key appears too short', 'thinkrank')
                };
            }
        }

        return { valid: true, message: '' };
    };

    const loadSettings = async () => {
        setIsLoading(true);
        try {
            const response = await apiFetch({
                path: '/thinkrank/v1/settings',
            });

            // Check if response has the expected structure
            const settings = response.success ? response.data : response;

            if (settings) {
                // Always use saved provider from backend (this is the source of truth)
                const savedProvider = settings.ai_provider || 'openai';
                const currentApiKey = settings[`${savedProvider}_api_key`] || '';

                // Check if we have a masked API key (indicates existing key)
                const hasKey = currentApiKey && currentApiKey.includes('••••');
                setHasExistingKey(hasKey);

                // Don't show masked key in input
                setApiKey(hasKey ? '' : currentApiKey);

                // Always sync frontend state with backend settings
                setAiProvider(savedProvider);
                setPreviousProvider(savedProvider); // Set initial previous provider

                // Set all provider-specific model states from backend settings
                const openaiModel = settings.openai_model || 'gpt-5-nano';
                const claudeModel = settings.claude_model || 'claude-3-7-sonnet-latest';
                const geminiModel = settings.gemini_model || 'gemini-2.5-flash';

                setOpenaiModelState(openaiModel);
                setClaudeModelState(claudeModel);
                setGeminiModelState(geminiModel);

                // Set selectedModel based on current provider
                if (savedProvider === 'claude') {
                    setSelectedModel(claudeModel);
                } else if (savedProvider === 'gemini') {
                    setSelectedModel(geminiModel);
                } else {
                    setSelectedModel(openaiModel);
                }

                // Set delete data on uninstall setting (defaults to false - preserve data)
                setDeleteDataOnUninstall(settings.keep_data_on_uninstall === false);

                // Show appropriate notice based on API key status
                if (!hasKey) {
                    const providerName = savedProvider === 'openai' ? 'OpenAI' : savedProvider === 'claude' ? 'Claude' : 'Gemini';
                    setNotice({
                        type: 'warning',
                        message: __(`No ${providerName} API key found. Please enter your API key to use this provider.`, 'thinkrank')
                    });
                } else {
                    // Clear any existing notices if key exists
                    setNotice(null);
                }
            }
        } catch (error) {
            console.error('Failed to load settings:', error);
            setNotice({
                type: 'error',
                message: __('Failed to load settings.', 'thinkrank')
            });
        } finally {
            setIsLoading(false);
            setInitialLoadComplete(true); // Mark initial load as complete
        }
    };

    const handleSave = async () => {
        // Check if we have an API key for the selected provider
        if (!apiKey.trim() && !hasExistingKey) {
            const providerName = aiProvider === 'openai' ? 'OpenAI' : aiProvider === 'claude' ? 'Claude' : 'Gemini';
            setNotice({
                type: 'error',
                message: __(`Please enter a ${providerName} API key for the selected provider.`, 'thinkrank')
            });
            return;
        }

        // Validate API key format if provided
        if (apiKey.trim()) {
            const validation = validateApiKey(apiKey, aiProvider);
            if (!validation.valid) {
                setNotice({
                    type: 'error',
                    message: validation.message
                });
                return;
            }
        }

        setIsSaving(true);
        setNotice(null);

        try {
            const saveData = {
                ai_provider: aiProvider,
                keep_data_on_uninstall: !deleteDataOnUninstall, // Invert because UI asks "delete data"
            };

            // Include API key if user entered a new one
            if (apiKey.trim()) {
                saveData[`${aiProvider}_api_key`] = apiKey;
            }

            // Include model selection (use provider-specific state)
            if (aiProvider === 'claude') {
                saveData.claude_model = claudeModelState;
            } else if (aiProvider === 'gemini') {
                saveData.gemini_model = geminiModelState;
            } else {
                saveData.openai_model = openaiModelState;
            }

            const response = await apiFetch({
                path: '/thinkrank/v1/settings',
                method: 'POST',
                data: saveData,
            });

            if (response.success) {
                setNotice({
                    type: 'success',
                    message: __('Settings saved successfully!', 'thinkrank')
                });

                // If we saved a new API key, mark that we have one and clear input
                if (apiKey.trim()) {
                    setHasExistingKey(true);
                    setApiKey('');
                }

                // Refresh the settings to ensure UI is in sync
                await loadSettings();
            } else {
                setNotice({
                    type: 'error',
                    message: response.message || __('Failed to save settings.', 'thinkrank')
                });
            }
        } catch (error) {
            console.error('Failed to save settings:', error);
            setNotice({
                type: 'error',
                message: __('Failed to save settings. Please try again.', 'thinkrank')
            });
        } finally {
            setIsSaving(false);
        }
    };

    const handleTestConnection = async () => {
        if (!apiKey.trim()) {
            setNotice({
                type: 'error',
                message: __('Please enter an API key first.', 'thinkrank')
            });
            return;
        }

        // Validate API key format before testing
        const validation = validateApiKey(apiKey, aiProvider);
        if (!validation.valid) {
            setNotice({
                type: 'error',
                message: validation.message
            });
            return;
        }

        setIsTesting(true);
        setNotice(null);

        try {
            const response = await apiFetch({
                path: '/thinkrank/v1/ai/test-connection',
                method: 'POST',
                data: {
                    api_key: apiKey,
                    provider: aiProvider,
                },
            });

            if (response && response.success) {
                setNotice({
                    type: 'success',
                    message: __('API connection successful!', 'thinkrank')
                });
            } else {
                const errorMessage = response?.message ||
                    __('API connection failed. Please check your API key and try again.', 'thinkrank');
                setNotice({
                    type: 'error',
                    message: errorMessage
                });
            }
        } catch (error) {
            console.error('Failed to test connection:', error);

            // Better error handling for different types of failures
            let errorMessage = __('Failed to test API connection.', 'thinkrank');

            if (error.message) {
                if (error.message.includes('fetch')) {
                    errorMessage = __('Network error. Please check your internet connection.', 'thinkrank');
                } else if (error.message.includes('401') || error.message.includes('403')) {
                    errorMessage = __('Invalid API key. Please check your credentials.', 'thinkrank');
                } else if (error.message.includes('429')) {
                    errorMessage = __('Rate limit exceeded. Please try again later.', 'thinkrank');
                } else {
                    errorMessage = `${__('Connection failed:', 'thinkrank')} ${error.message}`;
                }
            }

            setNotice({
                type: 'error',
                message: errorMessage
            });
        } finally {
            setIsTesting(false);
        }
    };

    const handleClearApiKey = async () => {
        if (!hasExistingKey) {
            setNotice({
                type: 'warning',
                message: __('No API key is currently saved.', 'thinkrank')
            });
            return;
        }

        if (!confirm(__('Are you sure you want to clear the saved API key? This action cannot be undone.', 'thinkrank'))) {
            return;
        }

        setIsSaving(true);
        setNotice(null);

        try {
            const response = await apiFetch({
                path: '/thinkrank/v1/settings',
                method: 'POST',
                data: {
                    [`${aiProvider}_api_key`]: '', // Clear the API key
                },
            });

            if (response.success) {
                setNotice({
                    type: 'success',
                    message: __('API key cleared successfully.', 'thinkrank')
                });
                setHasExistingKey(false);
                setApiKey('');

                // Refresh the settings to ensure UI is in sync
                await loadSettings();
            } else {
                setNotice({
                    type: 'error',
                    message: response.message || __('Failed to clear API key.', 'thinkrank')
                });
            }
        } catch (error) {
            console.error('Failed to clear API key:', error);
            setNotice({
                type: 'error',
                message: __('Failed to clear API key. Please try again.', 'thinkrank')
            });
        } finally {
            setIsSaving(false);
        }
    };

    if (isLoading) {
        return (
            <div className="thinkrank-ui thinkrank-max-w-4xl">
                <div className="thinkrank-flex-center thinkrank-p-8">
                    <div className="thinkrank-text-center">
                        <Spinner />
                        <p className="thinkrank-mt-4 thinkrank-text-secondary">{__('Loading settings...', 'thinkrank')}</p>
                    </div>
                </div>
            </div>
        );
    }

    return (
        <div className="thinkrank-ui thinkrank-max-w-4xl">
            {notice && (
                <div className="thinkrank-mb-6">
                    <Notice
                        status={notice.type}
                        isDismissible
                        onRemove={() => setNotice(null)}
                    >
                        {notice.message}
                    </Notice>
                </div>
            )}

            <div className="thinkrank-mb-8">
                <h1 className="thinkrank-text-2xl thinkrank-font-bold thinkrank-text-primary thinkrank-mb-2">
                    {__('ThinkRank Settings', 'thinkrank')}
                </h1>
                <p className="thinkrank-text-secondary thinkrank-text-base">
                    {__('Configure your AI provider and API settings to start generating content briefs.', 'thinkrank')}
                </p>
            </div>

            <div className="thinkrank-flex thinkrank-flex-col thinkrank-gap-6">
            <div className="thinkrank-card thinkrank-card--elevated">
                <div className="thinkrank-card__header">
                    <h2 className="thinkrank-text-xl thinkrank-font-semibold thinkrank-text-primary">
                        {__('AI Settings', 'thinkrank')}
                    </h2>
                </div>
                <div className="thinkrank-card__body">
                    {/* AI Provider Visual Selector */}
                    <div className="thinkrank-mb-6">
                        <label className="thinkrank-block thinkrank-text-sm thinkrank-font-semibold thinkrank-text-primary thinkrank-mb-2">
                            {__('AI Provider', 'thinkrank')}
                        </label>
                        <p className="thinkrank-text-sm thinkrank-text-secondary thinkrank-mb-4">
                            {__('Choose your preferred AI provider for content generation.', 'thinkrank')}
                        </p>

                        <div className="thinkrank-grid thinkrank-grid-cols-1-xs thinkrank-grid-cols-3-sm thinkrank-gap-4">
                            <button
                                type="button"
                                className={`thinkrank-card thinkrank-card--interactive thinkrank-relative thinkrank-p-4 thinkrank-border-2 thinkrank-transition-all ${
                                    aiProvider === 'openai'
                                        ? 'thinkrank-border-blue thinkrank-bg-blue thinkrank-bg-opacity-5'
                                        : 'thinkrank-border-light hover:thinkrank-border-blue'
                                }`}
                                onClick={() => setAiProvider('openai')}
                            >
                                <div className="thinkrank-flex thinkrank-items-center thinkrank-gap-3">
                                    <div className={`thinkrank-w-10 thinkrank-h-10 thinkrank-rounded-lg thinkrank-flex-center ${
                                        aiProvider === 'openai'
                                            ? 'thinkrank-bg-green thinkrank-text-white'
                                            : 'thinkrank-bg-gray-100 thinkrank-text-gray-600'
                                    }`}>
                                        <OpenAIIcon />
                                    </div>
                                    <div className="thinkrank-flex-1 thinkrank-text-left">
                                        <h4 className="thinkrank-text-base thinkrank-font-semibold thinkrank-text-primary thinkrank-mb-1">{__('OpenAI', 'thinkrank')}</h4>
                                        <span className="thinkrank-text-sm thinkrank-text-secondary">
                                                {aiProvider === 'openai' ? (
                                                    <ModelBadge provider="openai" model={openaiModelState || settings?.openai_model || 'gpt-5-nano'} />
                                                ) : (
                                                    __('OpenAI API', 'thinkrank')
                                                )}
                                            </span>
                                    </div>
                                    {(aiProvider === 'openai' && hasExistingKey) && (
                                        <div className="thinkrank-w-5 thinkrank-h-5 thinkrank-text-green">
                                            <span className="dashicons dashicons-yes-alt"></span>
                                        </div>
                                    )}
                                </div>
                            </button>

                            <button
                                type="button"
                                className={`thinkrank-card thinkrank-card--interactive thinkrank-relative thinkrank-p-4 thinkrank-border-2 thinkrank-transition-all ${
                                    aiProvider === 'claude'
                                        ? 'thinkrank-border-orange thinkrank-bg-orange thinkrank-bg-opacity-5'
                                        : 'thinkrank-border-light hover:thinkrank-border-orange'
                                }`}
                                onClick={() => setAiProvider('claude')}
                            >
                                <div className="thinkrank-flex thinkrank-items-center thinkrank-gap-3">
                                    <div className={`thinkrank-w-10 thinkrank-h-10 thinkrank-rounded-lg thinkrank-flex-center ${
                                        aiProvider === 'claude'
                                            ? 'thinkrank-bg-orange thinkrank-text-white'
                                            : 'thinkrank-bg-gray-100 thinkrank-text-gray-600'
                                    }`}>
                                        <ClaudeIcon />
                                    </div>
                                    <div className="thinkrank-flex-1 thinkrank-text-left">
                                        <h4 className="thinkrank-text-base thinkrank-font-semibold thinkrank-text-primary thinkrank-mb-1">{__('Claude', 'thinkrank')}</h4>
                                        <span className="thinkrank-text-sm thinkrank-text-secondary">
                                                {aiProvider === 'claude' ? (
                                                    <ModelBadge provider="claude" model={claudeModelState || settings?.claude_model || 'claude-3-7-sonnet-latest'} />
                                                ) : (
                                                    __('Anthropic', 'thinkrank')
                                                )}
                                            </span>
                                    </div>
                                    {(aiProvider === 'claude' && hasExistingKey) && (
                                        <div className="thinkrank-w-5 thinkrank-h-5 thinkrank-text-green">
                                            <span className="dashicons dashicons-yes-alt"></span>
                                        </div>
                                    )}
                                </div>
                            </button>

                            <button
                                type="button"
                                className={`thinkrank-card thinkrank-card--interactive thinkrank-relative thinkrank-p-4 thinkrank-border-2 thinkrank-transition-all ${
                                    aiProvider === 'gemini'
                                        ? 'thinkrank-border-purple thinkrank-bg-purple thinkrank-bg-opacity-5'
                                        : 'thinkrank-border-light hover:thinkrank-border-purple'
                                }`}
                                onClick={() => setAiProvider('gemini')}
                            >
                                <div className="thinkrank-flex thinkrank-items-center thinkrank-gap-3">
                                    <div className={`thinkrank-w-10 thinkrank-h-10 thinkrank-rounded-lg thinkrank-flex-center ${
                                        aiProvider === 'gemini'
                                            ? 'thinkrank-bg-purple thinkrank-text-white'
                                            : 'thinkrank-bg-gray-100 thinkrank-text-gray-600'
                                    }`}>
                                        <GeminiIcon />
                                    </div>
                                    <div className="thinkrank-flex-1 thinkrank-text-left">
                                        <h4 className="thinkrank-text-base thinkrank-font-semibold thinkrank-text-primary thinkrank-mb-1">{__('Gemini', 'thinkrank')}</h4>
                                        <span className="thinkrank-text-sm thinkrank-text-secondary">
                                                {aiProvider === 'gemini' ? (
                                                    <ModelBadge provider="gemini" model={geminiModelState || settings?.gemini_model || 'gemini-2.5-flash'} />
                                                ) : (
                                                    __('Google AI', 'thinkrank')
                                                )}
                                            </span>
                                    </div>
                                    {(aiProvider === 'gemini' && hasExistingKey) && (
                                        <div className="thinkrank-w-5 thinkrank-h-5 thinkrank-text-green">
                                            <span className="dashicons dashicons-yes-alt"></span>
                                        </div>
                                    )}
                                </div>
                            </button>
                        </div>
                    </div>

                    {/* API Key Input */}
                    <div className="thinkrank-mb-6">
                        <div className="thinkrank-mb-4">
                            <TextControl
                                label={__(`${aiProvider === 'openai' ? 'OpenAI' : aiProvider === 'claude' ? 'Claude' : 'Gemini'} API Key`, 'thinkrank')}
                                type="password"
                                value={apiKey}
                                onChange={setApiKey}
                                help={hasExistingKey
                                    ? __('API key is saved. Enter a new key to update it.', 'thinkrank')
                                    : __('Enter your API key. This will be encrypted and stored securely.', 'thinkrank')
                                }
                                placeholder={hasExistingKey
                                    ? __('Enter new API key to update...', 'thinkrank')
                                    : (aiProvider === 'openai' ? __('sk-...', 'thinkrank') : aiProvider === 'claude' ? __('sk-ant-...', 'thinkrank') : __('AIza...', 'thinkrank'))
                                }
                                __next40pxDefaultSize={true}
                                __nextHasNoMarginBottom={true}
                            />
                        </div>

                        {/* Model Selection - Provider Specific */}
                        <div className="thinkrank-mb-4">
                            {aiProvider === 'openai' && (
                                <SelectControl
                                    label={__('OpenAI Model', 'thinkrank')}
                                    value={openaiModelState}
                                    options={modelOptions.openai}
                                    onChange={(v) => { setOpenaiModelState(v); setSelectedModel(v); }}
                                    help={__('Choose the OpenAI model to use for content generation.', 'thinkrank')}
                                    __next40pxDefaultSize={true}
                                    __nextHasNoMarginBottom={true}
                                />
                            )}

                            {aiProvider === 'claude' && (
                                <SelectControl
                                    label={__('Claude Model', 'thinkrank')}
                                    value={claudeModelState}
                                    options={modelOptions.claude}
                                    onChange={(v) => { setClaudeModelState(v); setSelectedModel(v); }}
                                    help={__('Choose the Claude model to use for content generation.', 'thinkrank')}
                                    __next40pxDefaultSize={true}
                                    __nextHasNoMarginBottom={true}
                                />
                            )}

                            {aiProvider === 'gemini' && (
                                <SelectControl
                                    label={__('Gemini Model', 'thinkrank')}
                                    value={geminiModelState}
                                    options={modelOptions.gemini}
                                    onChange={(v) => { setGeminiModelState(v); setSelectedModel(v); }}
                                    help={__('Choose the Gemini model to use for content generation.', 'thinkrank')}
                                    __next40pxDefaultSize={true}
                                    __nextHasNoMarginBottom={true}
                                />
                            )}
                        </div>

                        {hasExistingKey && (
                            <div className="thinkrank-mt-4 thinkrank-p-4 thinkrank-bg-green thinkrank-bg-opacity-10 thinkrank-border thinkrank-border-green thinkrank-border-opacity-20 thinkrank-rounded-lg">
                                <div className="thinkrank-flex thinkrank-items-center thinkrank-gap-2 thinkrank-mb-2">
                                    <span className="dashicons dashicons-yes-alt thinkrank-text-green"></span>
                                    <span className="thinkrank-text-sm thinkrank-font-medium thinkrank-text-green">
                                        {__('API key is configured and saved.', 'thinkrank')}
                                    </span>
                                </div>
                                <div className="thinkrank-flex thinkrank-items-start thinkrank-gap-2">
                                    <span className="dashicons dashicons-info thinkrank-text-blue thinkrank-mt-1"></span>
                                    <span className="thinkrank-text-sm thinkrank-text-secondary thinkrank-leading-relaxed">
                                        {__('With your own API key, you have unlimited AI generations at no additional cost. You pay the provider directly.', 'thinkrank')}
                                    </span>
                                </div>
                            </div>
                        )}
                    </div>

                    {/* Action Buttons */}
                    <div className="thinkrank-flex thinkrank-flex-col-xs thinkrank-flex-row-sm thinkrank-gap-3 thinkrank-pt-6 thinkrank-border-t thinkrank-border-light">
                        <button
                            className={`thinkrank-btn thinkrank-btn--primary thinkrank-btn--base ${isSaving ? 'thinkrank-btn--loading' : ''}`}
                            onClick={handleSave}
                            disabled={isTesting || isSaving}
                        >
                            {isSaving ? __('Saving...', 'thinkrank') : __('Save Settings', 'thinkrank')}
                        </button>

                        <button
                            className={`thinkrank-btn thinkrank-btn--secondary thinkrank-btn--base ${isTesting ? 'thinkrank-btn--loading' : ''}`}
                            onClick={handleTestConnection}
                            disabled={isSaving || !apiKey.trim() || isTesting}
                        >
                            {isTesting ? __('Testing...', 'thinkrank') : __('Test Connection', 'thinkrank')}
                        </button>

                        {hasExistingKey && (
                            <button
                                className="thinkrank-btn thinkrank-btn--danger thinkrank-btn--base"
                                onClick={handleClearApiKey}
                                disabled={isTesting || isSaving}
                            >
                                {__('Clear API Key', 'thinkrank')}
                            </button>
                        )}
                    </div>
                </div>
            </div>

            <div className="thinkrank-card thinkrank-card--elevated">
                <div className="thinkrank-card__header">
                    <h3 className="thinkrank-text-xl thinkrank-font-semibold thinkrank-text-primary">
                        {__('Data Management', 'thinkrank')}
                    </h3>
                </div>
                <div className="thinkrank-card__body">
                    <div className="thinkrank-mb-6">
                        <CheckboxControl
                            label={__('Delete all data when uninstalling plugin', 'thinkrank')}
                            help={__('By default, your API keys and content briefs are preserved when you uninstall ThinkRank. Check this box to delete everything instead.', 'thinkrank')}
                            checked={deleteDataOnUninstall}
                            onChange={setDeleteDataOnUninstall}
                            __nextHasNoMarginBottom={true}
                        />
                    </div>

                    <div className="thinkrank-p-5 thinkrank-bg-blue thinkrank-bg-opacity-5 thinkrank-border thinkrank-border-blue thinkrank-border-opacity-20 thinkrank-rounded-lg thinkrank-border-l-4 thinkrank-border-l-blue">
                        <div className="thinkrank-grid thinkrank-grid-cols-1-xs thinkrank-grid-cols-2-md thinkrank-gap-6">
                            <div>
                                <h4 className="thinkrank-flex thinkrank-items-center thinkrank-gap-2 thinkrank-text-sm thinkrank-font-semibold thinkrank-text-blue thinkrank-mb-3">
                                    <span className="thinkrank-text-green">✓</span>
                                    {__('What gets preserved', 'thinkrank')}
                                </h4>
                                <ul className="thinkrank-text-sm thinkrank-text-secondary thinkrank-leading-relaxed thinkrank-pl-4 thinkrank-space-y-1">
                                    <li>{__('API keys and provider settings', 'thinkrank')}</li>
                                    <li>{__('Generated content briefs', 'thinkrank')}</li>
                                    <li>{__('Plugin configuration', 'thinkrank')}</li>
                                </ul>
                            </div>

                            <div>
                                <h4 className="thinkrank-flex thinkrank-items-center thinkrank-gap-2 thinkrank-text-sm thinkrank-font-semibold thinkrank-text-blue thinkrank-mb-3">
                                    <span className="thinkrank-text-red">✗</span>
                                    {__('What gets cleaned up', 'thinkrank')}
                                </h4>
                                <ul className="thinkrank-text-sm thinkrank-text-secondary thinkrank-leading-relaxed thinkrank-pl-4 thinkrank-space-y-1">
                                    <li>{__('Temporary cache data', 'thinkrank')}</li>
                                    <li>{__('Usage analytics', 'thinkrank')}</li>
                                    <li>{__('Plugin activation flags', 'thinkrank')}</li>
                                </ul>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            </div>
        </div>
    );
};

export default Settings;

```
