PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.5.4
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.5.4
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / vendor / team-updraft / lib-onboarding-wizard / Wizard / Onboarding / src / components / Fields / TrackingTest.jsx

TrackingTest.jsx in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 4.5.4, at vendor/team-updraft/lib-onboarding-wizard/Wizard/Onboarding/src/components/Fields/TrackingTest.jsx

381 lines 16.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { __ } from '@wordpress/i18n';
2 import {handleRequest} from '@/utils/api.js';
3 import {glue} from '@/utils/glue';
4 import {useEffect} from "@wordpress/element";
5 import useOnboardingStore from "@/store/useOnboardingStore";
6 import {useState, useRef} from "react";
7 import Icon from "@/utils/Icon";
8 import {getNonce} from "@/utils/getNonce";
9 import Support from "@/components/Support.js";
10
11 const getLiveVisitors = async(nonce) => {
12 const url = 'burst/v1/data/live-visitors';
13 const path = url + glue(url) + getNonce(nonce);
14 const method = 'GET';
15
16 let args = {
17 path,
18 method,
19 data: {
20 isOnboarding: true, // Indicate this is an onboarding request
21 },
22 };
23 let response = await handleRequest( args );
24 return response.data;
25 };
26
27 const ConnectionAnimation = ({ isRunning, isSuccess, testCompleted, isFailed, retryCount, isPro, handleManualRetry }) => {
28 const [showSuccessAnimation, setShowSuccessAnimation] = useState(false);
29 const [showCheckmark, setShowCheckmark] = useState(false);
30 const [showFailureAnimation, setShowFailureAnimation] = useState(false);
31 const [showXMark, setShowXMark] = useState(false);
32
33 useEffect(() => {
34 if (isRunning) {
35 // Reset all animation states when test starts running
36 setShowSuccessAnimation(false);
37 setShowCheckmark(false);
38 setShowFailureAnimation(false);
39 setShowXMark(false);
40 } else if (testCompleted && isSuccess && !showSuccessAnimation) {
41 // Start success animation after a brief delay
42 setTimeout(() => {
43 setShowSuccessAnimation(true);
44 // Show checkmark after icons reach center
45 setTimeout(() => {
46 setShowCheckmark(true);
47 }, 500);
48 }, 500);
49 } else if (testCompleted && isFailed && !showFailureAnimation) {
50 // Start failure animation after a brief delay
51 setTimeout(() => {
52 setShowFailureAnimation(true);
53 // Show X mark after icons reach center
54 setTimeout(() => {
55 setShowXMark(true);
56 }, 500);
57 }, 500);
58 }
59 }, [isRunning, testCompleted, isSuccess, isFailed, showSuccessAnimation, showFailureAnimation]);
60
61 const getIconClasses = (baseClasses, position) => {
62 let classes = baseClasses;
63
64 if (showSuccessAnimation || showFailureAnimation) {
65 classes += position === 'left' ? ' transform translate-x-24' : ' transform -translate-x-24';
66 classes += ' opacity-0 transition-all duration-500 ease-in-out';
67 } else {
68 classes += ' transition-all duration-500 ease-in-out';
69 }
70
71 if (testCompleted && isSuccess && !showSuccessAnimation) return `${classes} bg-green-light border-green`;
72 if (testCompleted && isFailed) return `${classes} bg-red-light border-red`;
73 if (isRunning) {
74 if (position === 'left') return `${classes} bg-blue-light border-blue`;
75 if (position === 'right') {
76 if (retryCount === 0) return `${classes} bg-blue-light border-blue`;
77 if (retryCount === 1) return `${classes} bg-yellow-light border-yellow`;
78 if (retryCount === 2) return `${classes} bg-orange-light border-orange`;
79 }
80 }
81 return `${classes} bg-gray-100 border-gray-300`;
82 };
83
84 const getIconColor = (position) => {
85 if (testCompleted && isSuccess) return '#2B8133';
86 if (testCompleted && isFailed) return '#c6273b';
87 if (isRunning) {
88 if (position === 'left') return '#1D3C8F'; // Left icon always blue
89 if (position === 'right') {
90 if (retryCount === 0) return '#1D3C8F'; // blue
91 if (retryCount === 1) return '#D97706'; // yellow
92 if (retryCount === 2) return '#EA580C'; // orange
93 }
94 }
95 return '#6c757d';
96 };
97
98 const getDotColor = () => {
99 if (testCompleted && isSuccess) return 'bg-green';
100 if (testCompleted && isFailed) return 'bg-red';
101 if (isRunning) {
102 if (retryCount === 0) return 'bg-blue';
103 if (retryCount === 1) return 'bg-yellow';
104 if (retryCount === 2) return 'bg-orange';
105 }
106 return 'bg-blue';
107 };
108
109 return (
110 <>
111 <style>{`
112 @keyframes flowDots {
113 0% {
114 left: -6px;
115 opacity: 0;
116 }
117 10% {
118 opacity: 1;
119 }
120 90% {
121 opacity: 1;
122 }
123 100% {
124 left: calc(100% + 6px);
125 opacity: 0;
126 }
127 }
128
129 @keyframes checkmarkAppear {
130 0% {
131 opacity: 0;
132 transform: translate(-50%, -50%) scale(0);
133 }
134 50% {
135 transform: translate(-50%, -50%) scale(1.2);
136 }
137 100% {
138 opacity: 1;
139 transform: translate(-50%, -50%) scale(1);
140 }
141 }
142
143 @keyframes xMarkAppear {
144 0% {
145 opacity: 0;
146 transform: translate(-50%, -50%) scale(0) rotate(0deg);
147 }
148 50% {
149 transform: translate(-50%, -50%) scale(1.2) rotate(90deg);
150 }
151 100% {
152 opacity: 1;
153 transform: translate(-50%, -50%) scale(1) rotate(0deg);
154 }
155 }
156
157 .burst-connection-dot {
158 animation: ${isRunning && !showSuccessAnimation && !showFailureAnimation ? 'flowDots 2s infinite ease-in-out' : 'none'};
159 }
160
161 .burst-connection-dot:nth-child(1) { animation-delay: 0s; }
162 .burst-connection-dot:nth-child(2) { animation-delay: 0.4s; }
163 .burst-connection-dot:nth-child(3) { animation-delay: 0.8s; }
164 .burst-connection-dot:nth-child(4) { animation-delay: 1.2s; }
165 .burst-connection-dot:nth-child(5) { animation-delay: 1.6s; }
166
167 .burst-checkmark {
168 animation: checkmarkAppear 0.6s ease-out forwards;
169 }
170
171 .burst-xmark {
172 animation: xMarkAppear 0.6s ease-out forwards;
173 }
174 `}</style>
175
176 <div className="flex items-center justify-center py-5 gap-5 relative">
177 {/* Left Icon */}
178 <div className={getIconClasses("flex items-center justify-center w-10 h-10 rounded-full border-2", "left")}>
179 <Icon name="visitors" size="20" color={getIconColor('left')} />
180 </div>
181
182 {/* Connection Flow */}
183 <div className={`relative w-48 h-1 bg-gray-200 rounded-sm overflow-hidden transition-opacity duration-500 ${showSuccessAnimation || showFailureAnimation ? 'opacity-0' : 'opacity-100'}`}>
184 {/* Background line */}
185 <div
186 className={`absolute inset-0 bg-gradient-to-r from-transparent to-transparent transition-all duration-500 ease-in-out ${isRunning && !showSuccessAnimation && !showFailureAnimation ? 'opacity-100' : 'opacity-30'} ${
187 isRunning ? (
188 retryCount === 0 ? 'via-blue' :
189 retryCount === 1 ? 'via-yellow' :
190 retryCount === 2 ? 'via-orange' : 'via-blue'
191 ) : 'via-blue'
192 }`}
193 />
194
195 {/* Animated dots */}
196 <div className="absolute inset-0">
197 {[...Array(5)].map((_, index) => (
198 <div
199 key={index}
200 className={`burst-connection-dot absolute w-1.5 h-1.5 rounded-full top-1/2 transform -translate-y-1/2 opacity-0 ${getDotColor()}`}
201 />
202 ))}
203 </div>
204 </div>
205
206 {/* Right Icon */}
207 <div className={getIconClasses("flex items-center justify-center w-10 h-10 rounded-full border-2", "right")}>
208 <Icon name="website" size="20" color={getIconColor('right')} />
209 </div>
210
211 {/* Success Checkmark */}
212 {showCheckmark && (
213 <div className="absolute top-1/2 left-1/2 transform">
214 <div className="burst-checkmark flex items-center justify-center w-12 h-12 rounded-full bg-green border-2 border-green">
215 <Icon name="check" size="24" color="white" strokeWidth={3} />
216 </div>
217 </div>
218 )}
219
220 {/* Failure X Mark */}
221 {showXMark && (
222 <div className="absolute top-1/2 left-1/2 transform">
223 <div className="burst-xmark flex items-center justify-center w-12 h-12 rounded-full bg-red border-2 border-red">
224 <Icon name="times" size="24" color="white" strokeWidth={3} />
225 </div>
226 </div>
227 )}
228
229 </div>
230 {/* display grid all items in same row and center */}
231 <div className="grid">
232 <p className={`row-start-1 row-end-2 col-start-1 col-end-2 mt-2 text-center text-gray font-semibold text-lg opacity-0 transition-opacity duration-500 ${isRunning ? 'opacity-100' : 'opacity-0'}`}>
233 {isRunning && retryCount === 0 && __("Running test, please wait...", "ONBOARDING_WIZARD_TEXT_DOMAIN")}
234 {isRunning && retryCount === 1 && __("Hmm, let's try that again...", "ONBOARDING_WIZARD_TEXT_DOMAIN")}
235 {isRunning && retryCount === 2 && __("One more attempt, hang tight...", "ONBOARDING_WIZARD_TEXT_DOMAIN")}
236 </p>
237 <p className={`row-start-1 row-end-2 col-start-1 col-end-2 mt-2 text-center text-gray font-semibold text-lg opacity-0 transition-opacity duration-500 ${isFailed ? 'opacity-100' : 'opacity-0'}`}>
238 {__("Unfortunately, Burst could not detect the test visit.", "ONBOARDING_WIZARD_TEXT_DOMAIN")}
239 </p>
240 <p className={`row-start-1 row-end-2 col-start-1 col-end-2 mt-2 text-center text-gray font-semibold text-lg opacity-0 transition-opacity duration-500 ${isSuccess ? 'opacity-100' : 'opacity-0'}`}>
241 {__("Successfully detected a visit on your site!", "ONBOARDING_WIZARD_TEXT_DOMAIN")}
242 </p>
243 </div>
244
245 {/* if failed, show list with four steps to troubleshoot, last step should be support. Second to last should be link to troubleshooting article */}
246 {isFailed && (
247 <Support handleManualRetry={handleManualRetry} isRunning={isRunning}/>
248 )}
249 </>
250 );
251 }
252
253 const TrackingTest = () => {
254 const {
255 onboardingData,
256 setTrackingTestRunning,
257 setTrackingTestCompleted,
258 setTrackingTestSuccess,
259 } = useOnboardingStore();
260
261 const [visitors, setVisitors] = useState(0);
262 const [testState, setTestState] = useState('idle'); // 'idle', 'running', 'completed'
263 const retryCountRef = useRef(0);
264 const maxRetries = 2;
265 const isPro = onboardingData.is_pro;
266
267 const runTrackingTest = async () => {
268 const startTime = Date.now();
269 const minDuration = 3000; // 3 seconds minimum
270
271 setTestState('running');
272 setTrackingTestRunning(true);
273 setTrackingTestCompleted(false);
274 setTrackingTestSuccess(false);
275
276 try {
277 // Create and load test iframe
278 const iframe = document.createElement('iframe');
279 iframe.style.display = 'none';
280 document.body.appendChild(iframe);
281
282 // Wait for both test pages to load
283 await new Promise((resolve) => {
284 let stage = 0;
285
286 const handleLoad = () => {
287 if (stage === 0) {
288 // Load second page
289 iframe.src = onboardingData.site_url + '/404' + glue(onboardingData.site_url) + 'burst_test_hit&burst_nextpage';
290 stage = 1;
291 } else {
292 // Both stages complete
293 iframe.removeEventListener('load', handleLoad);
294 document.body.removeChild(iframe);
295 resolve();
296 }
297 };
298
299 iframe.addEventListener('load', handleLoad);
300 iframe.src = onboardingData.site_url + glue(onboardingData.site_url) + 'burst_test_hit';
301 });
302
303 // Check if tracking worked
304 const response = await getLiveVisitors(onboardingData.nonce);
305 const success = response > 0;
306
307 // Calculate elapsed time and ensure minimum duration
308 const elapsedTime = Date.now() - startTime;
309 const remainingTime = Math.max(0, minDuration - elapsedTime);
310
311 // If test completed too quickly, wait for remaining time
312 if (remainingTime > 0) {
313 await new Promise(resolve => setTimeout(resolve, remainingTime));
314 }
315
316 // Now set the final results
317 setVisitors(response);
318 setTrackingTestSuccess(success);
319
320 if (!success && retryCountRef.current < maxRetries) {
321 // Retry after delay - don't call finally block for retries
322 retryCountRef.current++;
323 setTimeout(() => runTrackingTest(), 3000);
324 return; // Exit without calling finally
325 }
326
327 // Test completed (success or max retries reached)
328 setTestState('completed');
329 setTrackingTestCompleted(true);
330 setTrackingTestRunning(false);
331
332 } catch (error) {
333 console.error('Tracking test error:', error);
334
335 // Still ensure minimum duration even on error
336 const elapsedTime = Date.now() - startTime;
337 const remainingTime = Math.max(0, minDuration - elapsedTime);
338
339 if (remainingTime > 0) {
340 await new Promise(resolve => setTimeout(resolve, remainingTime));
341 }
342
343 setTestState('completed');
344 setTrackingTestCompleted(true);
345 setTrackingTestSuccess(false);
346 setTrackingTestRunning(false);
347 }
348 };
349
350 const handleManualRetry = () => {
351 retryCountRef.current = 0; // Reset retry counter for manual retry
352 runTrackingTest();
353 };
354
355 useEffect(() => {
356 runTrackingTest();
357 }, []); // Only run once on mount
358
359
360
361 const isRunning = testState === 'running';
362 const isCompleted = testState === 'completed';
363 const isSuccess = isCompleted && visitors > 0;
364 const isFailed = isCompleted && visitors === 0;
365
366 return (
367 <div className="burst-today-select-item burst-tooltip-live">
368 <ConnectionAnimation
369 isRunning={isRunning}
370 isSuccess={isSuccess}
371 testCompleted={isCompleted}
372 isFailed={isFailed}
373 retryCount={retryCountRef.current}
374 isPro={isPro}
375 handleManualRetry={handleManualRetry}
376 />
377 </div>
378 );
379 };
380
381 export default TrackingTest;