/** * useTabSettings Hook * * Reusable hook extracted from Site Identity state management patterns. * Provides consistent state management, API integration, and optimization * functionality across all Essential SEO tabs. * * @package ThinkRank * @since 1.0.0 */ import { useState, useEffect } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; import apiFetch from '@wordpress/api-fetch'; /** * useTabSettings Hook * * @param {string} tabName Tab identifier for API endpoints (e.g., 'site-identity', 'homepage-seo') * @param {Object} defaultSettings Default settings object * @param {Object} options Hook configuration options * @param {boolean} options.autoLoad Auto-load settings on mount (default: true) * @param {boolean} options.enableOptimization Enable optimization functionality (default: true) * @param {boolean} options.enableValidation Enable validation functionality (default: true) * * @return {Object} Hook state and methods */ const useTabSettings = (tabName, defaultSettings, options = {}) => { const { autoLoad = true, enableOptimization = true, enableValidation = true, tabDisplayName = null // Human-readable tab name for notices } = options; // Core state management (extracted from Site Identity pattern) const [isLoading, setIsLoading] = useState(true); const [isSaving, setIsSaving] = useState(false); const [settings, setSettings] = useState({}); const [hasChanges, setHasChanges] = useState(false); const [notice, setNotice] = useState(null); // Optimization states (conditional based on options) const [isOptimizing, setIsOptimizing] = useState(false); const [isValidating, setIsValidating] = useState(false); const [optimizationResults, setOptimizationResults] = useState(null); const [validationResults, setValidationResults] = useState(null); const apiBasePath = `/thinkrank/v1/${tabName}`; /** * Get human-readable tab name for notices */ const getTabDisplayName = () => { if (tabDisplayName) return tabDisplayName; // Generate display name from tabName const displayNames = { 'site-identity': __('Site Identity', 'thinkrank'), 'homepage-seo': __('Homepage SEO', 'thinkrank'), 'analytics': __('Analytics', 'thinkrank'), 'schema': __('Schema', 'thinkrank'), 'social-media': __('Social Media', 'thinkrank'), 'sitemap': __('Sitemap', 'thinkrank') }; return displayNames[tabName] || __('Settings', 'thinkrank'); }; /** * Load settings from API * Extracted from Site Identity loadSettings pattern */ const loadSettings = async () => { try { setIsLoading(true); const response = await apiFetch({ path: `${apiBasePath}/settings`, method: 'GET' }); if (response.success) { setSettings({ ...defaultSettings, ...response.data.settings }); } else { setSettings(defaultSettings); } } catch (error) { console.error(`${tabName} settings load error:`, error); setSettings(defaultSettings); } finally { setIsLoading(false); } }; /** * Save settings to API * Extracted from Site Identity saveSettings pattern */ const saveSettings = async () => { try { setIsSaving(true); setNotice(null); const response = await apiFetch({ path: `${apiBasePath}/settings`, method: 'POST', data: { settings, context_type: 'site' } }); if (response.success) { setHasChanges(false); setNotice({ status: 'success', message: __(`${getTabDisplayName()} settings saved successfully!`, 'thinkrank') }); } else { throw new Error(response.error || 'Failed to save settings'); } } catch (error) { console.error(`${tabName} settings save error:`, error); setNotice({ status: 'error', message: __(`Failed to save ${getTabDisplayName()} settings. Please try again.`, 'thinkrank') }); } finally { setIsSaving(false); } }; /** * Handle setting change * Extracted from Site Identity handleSettingChange pattern */ const handleSettingChange = (key, value) => { setSettings(prev => ({ ...prev, [key]: value })); setHasChanges(true); }; /** * Generic optimization handler * Extracted from Site Identity optimization patterns */ const runOptimization = async (optimizationType, data, options = {}) => { if (!enableOptimization) { console.warn('Optimization is disabled for this tab'); return; } try { setIsOptimizing(true); setNotice(null); const response = await apiFetch({ path: `${apiBasePath}/optimize`, method: 'POST', data: { type: optimizationType, data, options } }); if (response.success) { setOptimizationResults(response.data); setNotice({ status: 'success', message: __(`${getTabDisplayName()} optimization completed successfully!`, 'thinkrank') }); // Auto-apply results if specified if (options.autoApply && response.data.optimized_settings) { setSettings(prev => ({ ...prev, ...response.data.optimized_settings })); setHasChanges(true); } return response.data; } } catch (error) { console.error(`${tabName} optimization error:`, error); // Enhanced error handling from Site Identity let errorMessage = __('Optimization failed.', 'thinkrank'); if (error.message?.includes('API key')) { errorMessage = __('AI optimization requires an API key. Please configure your API key in ThinkRank settings.', 'thinkrank'); } else if (error.message?.includes('rate limit')) { errorMessage = __('AI service rate limit reached. Please try again in a few minutes.', 'thinkrank'); } setNotice({ status: 'error', message: errorMessage }); } finally { setIsOptimizing(false); } }; /** * Generic validation handler * Extracted from Site Identity validation patterns */ const runValidation = async (validationType, data) => { if (!enableValidation) { console.warn('Validation is disabled for this tab'); return; } try { setIsValidating(true); const response = await apiFetch({ path: `${apiBasePath}/validate`, method: 'POST', data: { type: validationType, data } }); if (response.success) { setValidationResults(response.data); setNotice({ status: response.data.valid ? 'success' : 'warning', message: response.data.message || __('Validation completed.', 'thinkrank') }); return response.data; } } catch (error) { console.error(`${tabName} validation error:`, error); setNotice({ status: 'error', message: __('Validation failed. Please try again.', 'thinkrank') }); } finally { setIsValidating(false); } }; /** * Clear notice */ const clearNotice = () => { setNotice(null); }; /** * Reset settings to defaults */ const resetSettings = () => { setSettings(defaultSettings); setHasChanges(true); }; // Load settings on mount if autoLoad is enabled useEffect(() => { if (autoLoad) { loadSettings(); } }, []); // Clear notice when component unmounts or becomes inactive useEffect(() => { return () => { // Clear notice on cleanup setNotice(null); }; }, []); return { // State isLoading, isSaving, isOptimizing, isValidating, settings, hasChanges, notice, optimizationResults, validationResults, // Methods loadSettings, saveSettings, handleSettingChange, runOptimization, runValidation, clearNotice, resetSettings, setNotice, setSettings }; }; export default useTabSettings;