import { __ } from '@wordpress/i18n'; import {handleRequest} from '@/utils/api.js'; import {glue} from '@/utils/glue'; import {useEffect} from "@wordpress/element"; import useOnboardingStore from "@/store/useOnboardingStore"; import {useState, useRef} from "react"; import Icon from "@/utils/Icon"; import {getNonce} from "@/utils/getNonce"; import Support from "@/components/Support.js"; const getLiveVisitors = async(nonce) => { const url = 'burst/v1/data/live-visitors'; const path = url + glue(url) + getNonce(nonce); const method = 'GET'; let args = { path, method, data: { isOnboarding: true, // Indicate this is an onboarding request }, }; let response = await handleRequest( args ); return response.data; }; const ConnectionAnimation = ({ isRunning, isSuccess, testCompleted, isFailed, retryCount, isPro, handleManualRetry }) => { const [showSuccessAnimation, setShowSuccessAnimation] = useState(false); const [showCheckmark, setShowCheckmark] = useState(false); const [showFailureAnimation, setShowFailureAnimation] = useState(false); const [showXMark, setShowXMark] = useState(false); useEffect(() => { if (isRunning) { // Reset all animation states when test starts running setShowSuccessAnimation(false); setShowCheckmark(false); setShowFailureAnimation(false); setShowXMark(false); } else if (testCompleted && isSuccess && !showSuccessAnimation) { // Start success animation after a brief delay setTimeout(() => { setShowSuccessAnimation(true); // Show checkmark after icons reach center setTimeout(() => { setShowCheckmark(true); }, 500); }, 500); } else if (testCompleted && isFailed && !showFailureAnimation) { // Start failure animation after a brief delay setTimeout(() => { setShowFailureAnimation(true); // Show X mark after icons reach center setTimeout(() => { setShowXMark(true); }, 500); }, 500); } }, [isRunning, testCompleted, isSuccess, isFailed, showSuccessAnimation, showFailureAnimation]); const getIconClasses = (baseClasses, position) => { let classes = baseClasses; if (showSuccessAnimation || showFailureAnimation) { classes += position === 'left' ? ' transform translate-x-24' : ' transform -translate-x-24'; classes += ' opacity-0 transition-all duration-500 ease-in-out'; } else { classes += ' transition-all duration-500 ease-in-out'; } if (testCompleted && isSuccess && !showSuccessAnimation) return `${classes} bg-green-light border-green`; if (testCompleted && isFailed) return `${classes} bg-red-light border-red`; if (isRunning) { if (position === 'left') return `${classes} bg-blue-light border-blue`; if (position === 'right') { if (retryCount === 0) return `${classes} bg-blue-light border-blue`; if (retryCount === 1) return `${classes} bg-yellow-light border-yellow`; if (retryCount === 2) return `${classes} bg-orange-light border-orange`; } } return `${classes} bg-gray-100 border-gray-300`; }; const getIconColor = (position) => { if (testCompleted && isSuccess) return '#2B8133'; if (testCompleted && isFailed) return '#c6273b'; if (isRunning) { if (position === 'left') return '#1D3C8F'; // Left icon always blue if (position === 'right') { if (retryCount === 0) return '#1D3C8F'; // blue if (retryCount === 1) return '#D97706'; // yellow if (retryCount === 2) return '#EA580C'; // orange } } return '#6c757d'; }; const getDotColor = () => { if (testCompleted && isSuccess) return 'bg-green'; if (testCompleted && isFailed) return 'bg-red'; if (isRunning) { if (retryCount === 0) return 'bg-blue'; if (retryCount === 1) return 'bg-yellow'; if (retryCount === 2) return 'bg-orange'; } return 'bg-blue'; }; return ( <>
{/* Left Icon */}
{/* Connection Flow */}
{/* Background line */}
{/* Animated dots */}
{[...Array(5)].map((_, index) => (
))}
{/* Right Icon */}
{/* Success Checkmark */} {showCheckmark && (
)} {/* Failure X Mark */} {showXMark && (
)}
{/* display grid all items in same row and center */}

{isRunning && retryCount === 0 && __("Running test, please wait...", "ONBOARDING_WIZARD_TEXT_DOMAIN")} {isRunning && retryCount === 1 && __("Hmm, let's try that again...", "ONBOARDING_WIZARD_TEXT_DOMAIN")} {isRunning && retryCount === 2 && __("One more attempt, hang tight...", "ONBOARDING_WIZARD_TEXT_DOMAIN")}

{__("Unfortunately, Burst could not detect the test visit.", "ONBOARDING_WIZARD_TEXT_DOMAIN")}

{__("Successfully detected a visit on your site!", "ONBOARDING_WIZARD_TEXT_DOMAIN")}

{/* if failed, show list with four steps to troubleshoot, last step should be support. Second to last should be link to troubleshooting article */} {isFailed && ( )} ); } const TrackingTest = () => { const { onboardingData, setTrackingTestRunning, setTrackingTestCompleted, setTrackingTestSuccess, } = useOnboardingStore(); const [visitors, setVisitors] = useState(0); const [testState, setTestState] = useState('idle'); // 'idle', 'running', 'completed' const retryCountRef = useRef(0); const maxRetries = 2; const isPro = onboardingData.is_pro; const runTrackingTest = async () => { const startTime = Date.now(); const minDuration = 3000; // 3 seconds minimum setTestState('running'); setTrackingTestRunning(true); setTrackingTestCompleted(false); setTrackingTestSuccess(false); try { // Create and load test iframe const iframe = document.createElement('iframe'); iframe.style.display = 'none'; document.body.appendChild(iframe); // Wait for both test pages to load await new Promise((resolve) => { let stage = 0; const handleLoad = () => { if (stage === 0) { // Load second page iframe.src = onboardingData.site_url + '/404' + glue(onboardingData.site_url) + 'burst_test_hit&burst_nextpage'; stage = 1; } else { // Both stages complete iframe.removeEventListener('load', handleLoad); document.body.removeChild(iframe); resolve(); } }; iframe.addEventListener('load', handleLoad); iframe.src = onboardingData.site_url + glue(onboardingData.site_url) + 'burst_test_hit'; }); // Check if tracking worked const response = await getLiveVisitors(onboardingData.nonce); const success = response > 0; // Calculate elapsed time and ensure minimum duration const elapsedTime = Date.now() - startTime; const remainingTime = Math.max(0, minDuration - elapsedTime); // If test completed too quickly, wait for remaining time if (remainingTime > 0) { await new Promise(resolve => setTimeout(resolve, remainingTime)); } // Now set the final results setVisitors(response); setTrackingTestSuccess(success); if (!success && retryCountRef.current < maxRetries) { // Retry after delay - don't call finally block for retries retryCountRef.current++; setTimeout(() => runTrackingTest(), 3000); return; // Exit without calling finally } // Test completed (success or max retries reached) setTestState('completed'); setTrackingTestCompleted(true); setTrackingTestRunning(false); } catch (error) { console.error('Tracking test error:', error); // Still ensure minimum duration even on error const elapsedTime = Date.now() - startTime; const remainingTime = Math.max(0, minDuration - elapsedTime); if (remainingTime > 0) { await new Promise(resolve => setTimeout(resolve, remainingTime)); } setTestState('completed'); setTrackingTestCompleted(true); setTrackingTestSuccess(false); setTrackingTestRunning(false); } }; const handleManualRetry = () => { retryCountRef.current = 0; // Reset retry counter for manual retry runTrackingTest(); }; useEffect(() => { runTrackingTest(); }, []); // Only run once on mount const isRunning = testState === 'running'; const isCompleted = testState === 'completed'; const isSuccess = isCompleted && visitors > 0; const isFailed = isCompleted && visitors === 0; return (
); }; export default TrackingTest;