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 / stores / core.js

core.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/stores/core.js

336 lines 8.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Core Data Store
3 *
4 * Manages global application state
5 *
6 * @package ThinkRank
7 * @since 1.0.0
8 */
9
10 import { createReduxStore } from '@wordpress/data';
11 import apiFetch from '@wordpress/api-fetch';
12
13 // Initial state
14 const DEFAULT_STATE = {
15 isInitialized: false,
16 isLoading: false,
17 hasError: false,
18 errorMessage: '',
19 notices: [],
20 userCapabilities: {},
21 pluginInfo: {},
22 systemStatus: {},
23 };
24
25 // Action types
26 const TYPES = {
27 SET_INITIALIZED: 'SET_INITIALIZED',
28 SET_LOADING: 'SET_LOADING',
29 SET_ERROR: 'SET_ERROR',
30 CLEAR_ERROR: 'CLEAR_ERROR',
31 ADD_NOTICE: 'ADD_NOTICE',
32 REMOVE_NOTICE: 'REMOVE_NOTICE',
33 SET_USER_CAPABILITIES: 'SET_USER_CAPABILITIES',
34 SET_PLUGIN_INFO: 'SET_PLUGIN_INFO',
35 SET_SYSTEM_STATUS: 'SET_SYSTEM_STATUS',
36 };
37
38 // Reducer
39 const reducer = (state = DEFAULT_STATE, action) => {
40 switch (action.type) {
41 case TYPES.SET_INITIALIZED:
42 return {
43 ...state,
44 isInitialized: action.initialized,
45 };
46
47 case TYPES.SET_LOADING:
48 return {
49 ...state,
50 isLoading: action.loading,
51 };
52
53 case TYPES.SET_ERROR:
54 return {
55 ...state,
56 hasError: true,
57 errorMessage: action.message,
58 isLoading: false,
59 };
60
61 case TYPES.CLEAR_ERROR:
62 return {
63 ...state,
64 hasError: false,
65 errorMessage: '',
66 };
67
68 case TYPES.ADD_NOTICE:
69 return {
70 ...state,
71 notices: [
72 ...state.notices,
73 {
74 id: action.id || Date.now(),
75 message: action.message,
76 type: action.noticeType || 'info',
77 isDismissible: action.isDismissible !== false,
78 },
79 ],
80 };
81
82 case TYPES.REMOVE_NOTICE:
83 return {
84 ...state,
85 notices: state.notices.filter(notice => notice.id !== action.id),
86 };
87
88 case TYPES.SET_USER_CAPABILITIES:
89 return {
90 ...state,
91 userCapabilities: action.capabilities,
92 };
93
94 case TYPES.SET_PLUGIN_INFO:
95 return {
96 ...state,
97 pluginInfo: action.info,
98 };
99
100 case TYPES.SET_SYSTEM_STATUS:
101 return {
102 ...state,
103 systemStatus: action.status,
104 };
105
106 default:
107 return state;
108 }
109 };
110
111 // Actions
112 const actions = {
113 setInitialized(initialized) {
114 return {
115 type: TYPES.SET_INITIALIZED,
116 initialized,
117 };
118 },
119
120 setLoading(loading) {
121 return {
122 type: TYPES.SET_LOADING,
123 loading,
124 };
125 },
126
127 setError(message) {
128 return {
129 type: TYPES.SET_ERROR,
130 message,
131 };
132 },
133
134 clearError() {
135 return {
136 type: TYPES.CLEAR_ERROR,
137 };
138 },
139
140 addNotice(message, noticeType = 'info', isDismissible = true) {
141 return {
142 type: TYPES.ADD_NOTICE,
143 message,
144 noticeType,
145 isDismissible,
146 id: Date.now(),
147 };
148 },
149
150 removeNotice(id) {
151 return {
152 type: TYPES.REMOVE_NOTICE,
153 id,
154 };
155 },
156
157 setUserCapabilities(capabilities) {
158 return {
159 type: TYPES.SET_USER_CAPABILITIES,
160 capabilities,
161 };
162 },
163
164 setPluginInfo(info) {
165 return {
166 type: TYPES.SET_PLUGIN_INFO,
167 info,
168 };
169 },
170
171 setSystemStatus(status) {
172 return {
173 type: TYPES.SET_SYSTEM_STATUS,
174 status,
175 };
176 },
177
178 // Async actions
179 *initialize() {
180 yield actions.setLoading(true);
181
182 try {
183 // Set up API fetch with nonce
184 if (window.thinkrankAdmin?.restNonce) {
185 apiFetch.use(apiFetch.createNonceMiddleware(window.thinkrankAdmin.restNonce));
186 }
187
188 // PERFORMANCE FIX: Parallelize API requests to reduce initialization time
189 // Create individual API request functions with error handling
190 const fetchCapabilities = async () => {
191 try {
192 return await apiFetch({ path: '/thinkrank/v1/capabilities' });
193 } catch (error) {
194 console.error('Failed to fetch capabilities:', error);
195 return {
196 manage_settings: false,
197 view_analytics: false,
198 manage_credits: false,
199 use_ai_features: false,
200 };
201 }
202 };
203
204 const fetchPluginInfo = async () => {
205 try {
206 return await apiFetch({ path: '/thinkrank/v1/plugin-info' });
207 } catch (error) {
208 console.error('Failed to fetch plugin info:', error);
209 return {
210 version: '1.0.0',
211 name: 'ThinkRank',
212 description: 'AI-native SEO plugin for WordPress',
213 };
214 }
215 };
216
217 const fetchSystemStatus = async () => {
218 try {
219 return await apiFetch({ path: '/thinkrank/v1/system-status' });
220 } catch (error) {
221 console.error('Failed to fetch system status:', error);
222 return {
223 status: 'unknown',
224 issues: [],
225 };
226 }
227 };
228
229 // Execute all API calls in parallel
230 const [capabilities, pluginInfo, systemStatus] = yield Promise.all([
231 fetchCapabilities(),
232 fetchPluginInfo(),
233 fetchSystemStatus()
234 ]);
235
236 yield actions.setUserCapabilities(capabilities);
237 yield actions.setPluginInfo(pluginInfo);
238 yield actions.setSystemStatus(systemStatus);
239 yield actions.setInitialized(true);
240
241 } catch (error) {
242 console.error('ThinkRank initialization error:', error);
243 yield actions.setError(
244 error.message || 'Failed to initialize ThinkRank'
245 );
246 } finally {
247 yield actions.setLoading(false);
248 }
249 },
250
251 *checkSystemHealth() {
252 try {
253 const status = yield apiFetch({
254 path: '/thinkrank/v1/system-status'
255 });
256
257 yield actions.setSystemStatus(status);
258
259 // Add notices for any issues
260 if (status.issues && status.issues.length > 0) {
261 for (const issue of status.issues) {
262 yield actions.addNotice(issue.message, issue.type);
263 }
264 }
265
266 } catch (error) {
267 yield actions.setError('Failed to check system health');
268 }
269 },
270 };
271
272 // Selectors
273 const selectors = {
274 getIsInitialized(state) {
275 return state.isInitialized;
276 },
277
278 getIsLoading(state) {
279 return state.isLoading;
280 },
281
282 getHasError(state) {
283 return state.hasError;
284 },
285
286 getErrorMessage(state) {
287 return state.errorMessage;
288 },
289
290 getNotices(state) {
291 return state.notices;
292 },
293
294 getUserCapabilities(state) {
295 return state.userCapabilities;
296 },
297
298 getPluginInfo(state) {
299 return state.pluginInfo;
300 },
301
302 getSystemStatus(state) {
303 return state.systemStatus;
304 },
305
306 // Computed selectors
307 canManageSettings(state) {
308 return state.userCapabilities.manage_settings !== false;
309 },
310
311 canViewAnalytics(state) {
312 return state.userCapabilities.view_analytics !== false;
313 },
314
315 canManageCredits(state) {
316 return state.userCapabilities.manage_credits !== false;
317 },
318
319 getPluginVersion(state) {
320 return state.pluginInfo.version || '1.0.0';
321 },
322
323 isSystemHealthy(state) {
324 return !state.systemStatus.issues || state.systemStatus.issues.length === 0;
325 },
326 };
327
328 // Create and export store
329 const store = createReduxStore('thinkrank/core', {
330 reducer,
331 actions,
332 selectors,
333 });
334
335 export default store;
336