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 / components / pages / UsageAnalytics.js

UsageAnalytics.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/components/pages/UsageAnalytics.js

331 lines 15.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Usage Analytics Page Component
3 *
4 * AI usage analytics and performance tracking with real data integration
5 *
6 * @package ThinkRank
7 * @since 1.0.0
8 */
9
10 import { __ } from '@wordpress/i18n';
11 import { Card, CardBody, CardHeader, Spinner, Notice, SelectControl, TabPanel } from '@wordpress/components';
12 import { useSelect, useDispatch } from '@wordpress/data';
13 import { useEffect, useState } from '@wordpress/element';
14 import { edit, chartBar, currencyDollar, backup, plugins, starFilled, check, trendingUp } from '@wordpress/icons';
15 import UsageBreakdown from './UsageBreakdown';
16
17 /**
18 * Icon Component - Renders WordPress icons consistently
19 */
20 const Icon = ({ icon, className = "thinkrank-w-5 thinkrank-h-5" }) => {
21 return (
22 <span className={className}>
23 {icon}
24 </span>
25 );
26 };
27
28 /**
29 * MetricCard Component
30 */
31 const MetricCard = ({ title, value, change, prefix = '', suffix = '', subtitle = '', icon = null }) => {
32 const changeColorClass = change > 0 ? 'thinkrank-text-green' : change < 0 ? 'thinkrank-text-red' : 'thinkrank-text-secondary';
33 const changeSymbol = change > 0 ? '+' : '';
34
35 // More meaningful text for zero change
36 const getChangeText = () => {
37 if (change !== 0) {
38 return `${changeSymbol}${change}%`;
39 }
40 // Show meaningful text based on the metric type
41 if (title.includes('Success Rate')) return 'Successful AI Responses';
42 if (title.includes('Features Used')) return 'Diverse toolkit usage';
43 if (title.includes('Most Used')) return ''; // No change text needed - has subtitle
44 if (title.includes('AI Optimizations')) return 'Total count';
45 return 'No change';
46 };
47
48 return (
49 <div className="thinkrank-card thinkrank-card--elevated">
50 <div className="thinkrank-card__body">
51 <div className="thinkrank-flex thinkrank-items-center thinkrank-gap-2 thinkrank-mb-4">
52 {icon && <Icon icon={icon} className="thinkrank-w-5 thinkrank-h-5 thinkrank-text-blue thinkrank-flex-shrink-0" />}
53 <h3 className="thinkrank-text-xs thinkrank-font-semibold thinkrank-text-muted thinkrank-uppercase thinkrank-tracking-wide thinkrank-mt-0 thinkrank-mb-0">
54 {title}
55 </h3>
56 </div>
57 <div>
58 <p className="thinkrank-text-3xl thinkrank-font-bold thinkrank-text-primary thinkrank-mb-2 thinkrank-leading-none">
59 {prefix}{typeof value === 'number' ? value.toLocaleString() : value}{suffix}
60 </p>
61 {subtitle && <p className="thinkrank-text-sm thinkrank-text-secondary thinkrank-mb-1">{subtitle}</p>}
62 <p className={`thinkrank-text-sm thinkrank-font-medium ${changeColorClass}`}>
63 {getChangeText()}
64 </p>
65 </div>
66 </div>
67 </div>
68 );
69 };
70
71 /**
72 * Overview Tab Component
73 */
74 const OverviewTab = () => {
75 const [period, setPeriod] = useState('30d');
76
77 // Select data from the analytics store
78 const {
79 dashboardMetrics,
80 isLoading,
81 error,
82 hasData,
83 lastUpdated,
84 featureBreakdown,
85 providerBreakdown
86 } = useSelect((select) => {
87 const analyticsSelect = select('thinkrank/analytics');
88 return {
89 dashboardMetrics: analyticsSelect.getDashboardMetrics(),
90 isLoading: analyticsSelect.getIsLoading(),
91 error: analyticsSelect.getError(),
92 hasData: analyticsSelect.hasData(),
93 lastUpdated: analyticsSelect.getLastUpdated(),
94 featureBreakdown: analyticsSelect.getFeatureBreakdown(),
95 providerBreakdown: analyticsSelect.getProviderBreakdown()
96 };
97 }, []);
98
99 // Get dispatch functions
100 const { fetchMetrics } = useDispatch('thinkrank/analytics');
101
102 // Fetch data on component mount and when period changes
103 useEffect(() => {
104 fetchMetrics(period);
105 }, [period, fetchMetrics]);
106
107 // Period options
108 const periodOptions = [
109 { label: __('Last 7 days', 'thinkrank'), value: '7d' },
110 { label: __('Last 30 days', 'thinkrank'), value: '30d' },
111 { label: __('Last 90 days', 'thinkrank'), value: '90d' },
112 { label: __('All time', 'thinkrank'), value: 'all' }
113 ];
114
115 return (
116 <div className="thinkrank-card thinkrank-card--elevated thinkrank-mb-6">
117 <div className="thinkrank-card__header">
118 <div className="thinkrank-flex thinkrank-justify-between thinkrank-items-center thinkrank-flex-wrap thinkrank-gap-4">
119 <h2 className="thinkrank-text-2xl thinkrank-font-bold thinkrank-text-primary thinkrank-mb-0">
120 {__('Usage Overview', 'thinkrank')}
121 </h2>
122 <SelectControl
123 label={__('Time Period', 'thinkrank')}
124 value={period}
125 options={periodOptions}
126 onChange={setPeriod}
127 className="thinkrank-min-w-40"
128 __next40pxDefaultSize={true}
129 __nextHasNoMarginBottom={true}
130 />
131 </div>
132 </div>
133 <div className="thinkrank-card__body">
134 {error && (
135 <Notice status="error" isDismissible={false}>
136 <p>{__('Error loading analytics data:', 'thinkrank')} {error.message}</p>
137 </Notice>
138 )}
139
140 {isLoading && (
141 <div className="thinkrank-flex thinkrank-flex-col thinkrank-items-center thinkrank-justify-center thinkrank-p-8 thinkrank-text-center">
142 <Spinner />
143 <p className="thinkrank-mt-4 thinkrank-text-secondary">{__('Loading analytics data...', 'thinkrank')}</p>
144 </div>
145 )}
146
147 {!isLoading && !error && (
148 <>
149 {/* Row 1: Core Performance Metrics */}
150 <div className="thinkrank-grid thinkrank-grid-cols-1-xs thinkrank-grid-cols-2-md thinkrank-grid-cols-4-lg thinkrank-gap-6 thinkrank-mb-6">
151 <MetricCard
152 title={dashboardMetrics.postsAnalyzed.label}
153 value={dashboardMetrics.postsAnalyzed.value}
154 change={dashboardMetrics.postsAnalyzed.change}
155 icon={edit}
156 />
157
158 <MetricCard
159 title={dashboardMetrics.aiOptimizations.label}
160 value={dashboardMetrics.aiOptimizations.value}
161 change={dashboardMetrics.aiOptimizations.change}
162 icon={trendingUp}
163 />
164
165 <MetricCard
166 title={dashboardMetrics.averageSeoScore.label}
167 value={dashboardMetrics.averageSeoScore.value}
168 change={dashboardMetrics.averageSeoScore.change}
169 suffix={dashboardMetrics.averageSeoScore.suffix}
170 icon={chartBar}
171 />
172
173 <MetricCard
174 title={dashboardMetrics.totalCost.label}
175 value={dashboardMetrics.totalCost.value.toFixed(4)}
176 change={dashboardMetrics.totalCost.change}
177 prefix={dashboardMetrics.totalCost.prefix}
178 subtitle={dashboardMetrics.totalCost.subtitle}
179 icon={currencyDollar}
180 />
181 </div>
182
183 {/* Row 2: Feature & Efficiency Metrics */}
184 <div className="thinkrank-grid thinkrank-grid-cols-1-xs thinkrank-grid-cols-2-md thinkrank-grid-cols-4-lg thinkrank-gap-6 thinkrank-mb-8">
185 <MetricCard
186 title={dashboardMetrics.featuresUsed.label}
187 value={dashboardMetrics.featuresUsed.value}
188 change={dashboardMetrics.featuresUsed.change}
189 icon={plugins}
190 />
191
192 <MetricCard
193 title={dashboardMetrics.timeSaved.label}
194 value={dashboardMetrics.timeSaved.value}
195 change={dashboardMetrics.timeSaved.change}
196 suffix={dashboardMetrics.timeSaved.suffix}
197 subtitle={dashboardMetrics.timeSaved.subtitle}
198 icon={backup}
199 />
200
201 <MetricCard
202 title={dashboardMetrics.mostUsedFeature.label}
203 value={dashboardMetrics.mostUsedFeature.value}
204 change={dashboardMetrics.mostUsedFeature.change}
205 subtitle={dashboardMetrics.mostUsedFeature.subtitle}
206 icon={starFilled}
207 />
208
209 <MetricCard
210 title={dashboardMetrics.successRate.label}
211 value={dashboardMetrics.successRate.value}
212 change={dashboardMetrics.successRate.change}
213 suffix={dashboardMetrics.successRate.suffix}
214 icon={check}
215 />
216 </div>
217
218 {!hasData && (
219 <div className="thinkrank-text-center thinkrank-p-8 thinkrank-bg-gray-50 thinkrank-rounded-lg thinkrank-mb-6">
220 <p className="thinkrank-text-secondary thinkrank-text-base thinkrank-mb-0">
221 {__('No analytics data available yet. Start using AI features to see your usage statistics.', 'thinkrank')}
222 </p>
223 </div>
224 )}
225
226 {hasData && (
227 <div className="thinkrank-grid thinkrank-grid-cols-1-xs thinkrank-grid-cols-2-lg thinkrank-gap-8 thinkrank-mt-8">
228 <div className="thinkrank-card">
229 <div className="thinkrank-card__body">
230 <h3 className="thinkrank-text-lg thinkrank-font-semibold thinkrank-text-primary thinkrank-mb-4">
231 {__('Feature Usage Breakdown', 'thinkrank')}
232 </h3>
233 <div className="thinkrank-space-y-3">
234 {Object.entries(featureBreakdown).map(([feature, count]) => (
235 <div key={feature} className="thinkrank-flex thinkrank-justify-between thinkrank-items-center thinkrank-py-2 thinkrank-border-b thinkrank-border-gray-200 last:thinkrank-border-b-0">
236 <span className="thinkrank-font-medium thinkrank-text-primary">
237 {feature.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()).replace(/\bSeo\b/g, 'SEO')}
238 </span>
239 <span className="thinkrank-font-semibold thinkrank-text-blue">{count}</span>
240 </div>
241 ))}
242 </div>
243 </div>
244 </div>
245
246 <div className="thinkrank-card">
247 <div className="thinkrank-card__body">
248 <h3 className="thinkrank-text-lg thinkrank-font-semibold thinkrank-text-primary thinkrank-mb-4">
249 {__('Provider Cost Breakdown', 'thinkrank')}
250 </h3>
251 <div className="thinkrank-space-y-3">
252 {Object.entries(providerBreakdown).map(([provider, data]) => (
253 <div key={provider} className="thinkrank-flex thinkrank-justify-between thinkrank-items-center thinkrank-py-2 thinkrank-border-b thinkrank-border-gray-200 last:thinkrank-border-b-0">
254 <span className="thinkrank-font-medium thinkrank-text-primary">
255 {provider.charAt(0).toUpperCase() + provider.slice(1)}
256 </span>
257 <span className="thinkrank-font-semibold thinkrank-text-blue">
258 ${data.cost?.toFixed(4) || '0.0000'} ({data.percentage || 0}%)
259 </span>
260 </div>
261 ))}
262 </div>
263 </div>
264 </div>
265 </div>
266 )}
267
268 {lastUpdated && (
269 <div className="thinkrank-mt-8 thinkrank-pt-6 thinkrank-border-t thinkrank-border-gray-200 thinkrank-text-center">
270 <p className="thinkrank-text-sm thinkrank-text-secondary thinkrank-mb-0">
271 {__('Last updated:', 'thinkrank')} {new Date(lastUpdated).toLocaleString()}
272 </p>
273 </div>
274 )}
275 </>
276 )}
277 </div>
278 </div>
279 );
280 };
281
282 /**
283 * Main Analytics Component with Tabs
284 */
285 const Analytics = () => {
286 const tabs = [
287 {
288 name: 'overview',
289 title: __('Overview', 'thinkrank'),
290 className: 'thinkrank-tab-overview'
291 },
292 {
293 name: 'usage-breakdown',
294 title: __('Usage Breakdown', 'thinkrank'),
295 className: 'thinkrank-tab-usage-breakdown'
296 }
297 ];
298
299 return (
300 <div className="thinkrank-ui">
301 <div className="thinkrank-mb-6">
302 <h1 className="thinkrank-text-3xl thinkrank-font-bold thinkrank-text-primary thinkrank-mb-2">
303 {__('AI Usage & Performance Analytics', 'thinkrank')}
304 </h1>
305 <p className="thinkrank-text-secondary thinkrank-text-lg">
306 {__('Track your AI usage, costs, and plugin performance analytics', 'thinkrank')}
307 </p>
308 </div>
309
310 <TabPanel
311 className="thinkrank-tab-panel"
312 activeClass="is-active"
313 tabs={tabs}
314 >
315 {(tab) => {
316 switch (tab.name) {
317 case 'overview':
318 return <OverviewTab />;
319 case 'usage-breakdown':
320 return <UsageBreakdown />;
321 default:
322 return <OverviewTab />;
323 }
324 }}
325 </TabPanel>
326 </div>
327 );
328 };
329
330 export default Analytics;
331