PluginProbe
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO / 1.0.0
ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO v1.0.0
2.7.0 2.6.0 2.5.0 2.4.0 2.3.0 2.2.0 2.1.1 2.1.0 2.0.2 2.0.1 2.0.0 1.32.0 1.31.0 1.30.0 1.29.0 1.28.0 1.27.0 1.26.0 1.25.0 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.10.0 All 48 releases
thinkrank / src / admin / hooks / useTabSettings.js

useTabSettings.js in ThinkRank AI SEO – AI SEO Plugin for WordPress: Schema, XML Sitemaps, Meta Tags, Search Console & Local SEO 1.0.0, at src/admin/hooks/useTabSettings.js

304 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * useTabSettings Hook
3 *
4 * Reusable hook extracted from Site Identity state management patterns.
5 * Provides consistent state management, API integration, and optimization
6 * functionality across all Essential SEO tabs.
7 *
8 * @package ThinkRank
9 * @since 1.0.0
10 */
11
12 import { useState, useEffect } from '@wordpress/element';
13 import { __ } from '@wordpress/i18n';
14 import apiFetch from '@wordpress/api-fetch';
15
16 /**
17 * useTabSettings Hook
18 *
19 * @param {string} tabName Tab identifier for API endpoints (e.g., 'site-identity', 'homepage-seo')
20 * @param {Object} defaultSettings Default settings object
21 * @param {Object} options Hook configuration options
22 * @param {boolean} options.autoLoad Auto-load settings on mount (default: true)
23 * @param {boolean} options.enableOptimization Enable optimization functionality (default: true)
24 * @param {boolean} options.enableValidation Enable validation functionality (default: true)
25 *
26 * @return {Object} Hook state and methods
27 */
28 const useTabSettings = (tabName, defaultSettings, options = {}) => {
29 const {
30 autoLoad = true,
31 enableOptimization = true,
32 enableValidation = true,
33 tabDisplayName = null // Human-readable tab name for notices
34 } = options;
35
36 // Core state management (extracted from Site Identity pattern)
37 const [isLoading, setIsLoading] = useState(true);
38 const [isSaving, setIsSaving] = useState(false);
39 const [settings, setSettings] = useState({});
40 const [hasChanges, setHasChanges] = useState(false);
41 const [notice, setNotice] = useState(null);
42
43 // Optimization states (conditional based on options)
44 const [isOptimizing, setIsOptimizing] = useState(false);
45 const [isValidating, setIsValidating] = useState(false);
46 const [optimizationResults, setOptimizationResults] = useState(null);
47 const [validationResults, setValidationResults] = useState(null);
48
49 const apiBasePath = `/thinkrank/v1/${tabName}`;
50
51 /**
52 * Get human-readable tab name for notices
53 */
54 const getTabDisplayName = () => {
55 if (tabDisplayName) return tabDisplayName;
56
57 // Generate display name from tabName
58 const displayNames = {
59 'site-identity': __('Site Identity', 'thinkrank'),
60 'homepage-seo': __('Homepage SEO', 'thinkrank'),
61 'analytics': __('Analytics', 'thinkrank'),
62 'schema': __('Schema', 'thinkrank'),
63 'social-media': __('Social Media', 'thinkrank'),
64 'sitemap': __('Sitemap', 'thinkrank')
65 };
66
67 return displayNames[tabName] || __('Settings', 'thinkrank');
68 };
69
70 /**
71 * Load settings from API
72 * Extracted from Site Identity loadSettings pattern
73 */
74 const loadSettings = async () => {
75 try {
76 setIsLoading(true);
77 const response = await apiFetch({
78 path: `${apiBasePath}/settings`,
79 method: 'GET'
80 });
81
82 if (response.success) {
83 setSettings({ ...defaultSettings, ...response.data.settings });
84 } else {
85 setSettings(defaultSettings);
86 }
87 } catch (error) {
88 console.error(`${tabName} settings load error:`, error);
89 setSettings(defaultSettings);
90 } finally {
91 setIsLoading(false);
92 }
93 };
94
95 /**
96 * Save settings to API
97 * Extracted from Site Identity saveSettings pattern
98 */
99 const saveSettings = async () => {
100 try {
101 setIsSaving(true);
102 setNotice(null);
103
104 const response = await apiFetch({
105 path: `${apiBasePath}/settings`,
106 method: 'POST',
107 data: {
108 settings,
109 context_type: 'site'
110 }
111 });
112
113 if (response.success) {
114 setHasChanges(false);
115 setNotice({
116 status: 'success',
117 message: __(`${getTabDisplayName()} settings saved successfully!`, 'thinkrank')
118 });
119 } else {
120 throw new Error(response.error || 'Failed to save settings');
121 }
122 } catch (error) {
123 console.error(`${tabName} settings save error:`, error);
124 setNotice({
125 status: 'error',
126 message: __(`Failed to save ${getTabDisplayName()} settings. Please try again.`, 'thinkrank')
127 });
128 } finally {
129 setIsSaving(false);
130 }
131 };
132
133 /**
134 * Handle setting change
135 * Extracted from Site Identity handleSettingChange pattern
136 */
137 const handleSettingChange = (key, value) => {
138 setSettings(prev => ({
139 ...prev,
140 [key]: value
141 }));
142 setHasChanges(true);
143 };
144
145 /**
146 * Generic optimization handler
147 * Extracted from Site Identity optimization patterns
148 */
149 const runOptimization = async (optimizationType, data, options = {}) => {
150 if (!enableOptimization) {
151 console.warn('Optimization is disabled for this tab');
152 return;
153 }
154
155 try {
156 setIsOptimizing(true);
157 setNotice(null);
158
159 const response = await apiFetch({
160 path: `${apiBasePath}/optimize`,
161 method: 'POST',
162 data: {
163 type: optimizationType,
164 data,
165 options
166 }
167 });
168
169 if (response.success) {
170 setOptimizationResults(response.data);
171 setNotice({
172 status: 'success',
173 message: __(`${getTabDisplayName()} optimization completed successfully!`, 'thinkrank')
174 });
175
176 // Auto-apply results if specified
177 if (options.autoApply && response.data.optimized_settings) {
178 setSettings(prev => ({
179 ...prev,
180 ...response.data.optimized_settings
181 }));
182 setHasChanges(true);
183 }
184
185 return response.data;
186 }
187 } catch (error) {
188 console.error(`${tabName} optimization error:`, error);
189
190 // Enhanced error handling from Site Identity
191 let errorMessage = __('Optimization failed.', 'thinkrank');
192 if (error.message?.includes('API key')) {
193 errorMessage = __('AI optimization requires an API key. Please configure your API key in ThinkRank settings.', 'thinkrank');
194 } else if (error.message?.includes('rate limit')) {
195 errorMessage = __('AI service rate limit reached. Please try again in a few minutes.', 'thinkrank');
196 }
197
198 setNotice({
199 status: 'error',
200 message: errorMessage
201 });
202 } finally {
203 setIsOptimizing(false);
204 }
205 };
206
207 /**
208 * Generic validation handler
209 * Extracted from Site Identity validation patterns
210 */
211 const runValidation = async (validationType, data) => {
212 if (!enableValidation) {
213 console.warn('Validation is disabled for this tab');
214 return;
215 }
216
217 try {
218 setIsValidating(true);
219
220 const response = await apiFetch({
221 path: `${apiBasePath}/validate`,
222 method: 'POST',
223 data: {
224 type: validationType,
225 data
226 }
227 });
228
229 if (response.success) {
230 setValidationResults(response.data);
231 setNotice({
232 status: response.data.valid ? 'success' : 'warning',
233 message: response.data.message || __('Validation completed.', 'thinkrank')
234 });
235 return response.data;
236 }
237 } catch (error) {
238 console.error(`${tabName} validation error:`, error);
239 setNotice({
240 status: 'error',
241 message: __('Validation failed. Please try again.', 'thinkrank')
242 });
243 } finally {
244 setIsValidating(false);
245 }
246 };
247
248 /**
249 * Clear notice
250 */
251 const clearNotice = () => {
252 setNotice(null);
253 };
254
255 /**
256 * Reset settings to defaults
257 */
258 const resetSettings = () => {
259 setSettings(defaultSettings);
260 setHasChanges(true);
261 };
262
263 // Load settings on mount if autoLoad is enabled
264 useEffect(() => {
265 if (autoLoad) {
266 loadSettings();
267 }
268 }, []);
269
270 // Clear notice when component unmounts or becomes inactive
271 useEffect(() => {
272 return () => {
273 // Clear notice on cleanup
274 setNotice(null);
275 };
276 }, []);
277
278 return {
279 // State
280 isLoading,
281 isSaving,
282 isOptimizing,
283 isValidating,
284 settings,
285 hasChanges,
286 notice,
287 optimizationResults,
288 validationResults,
289
290 // Methods
291 loadSettings,
292 saveSettings,
293 handleSettingChange,
294 runOptimization,
295 runValidation,
296 clearNotice,
297 resetSettings,
298 setNotice,
299 setSettings
300 };
301 };
302
303 export default useTabSettings;
304