DataViz Embed Builder: Interactive Chart & Graph Generator

1. Data Input

4. Embed Code

Free use limit reached. Unlock unlimited access.

You've used your 3 free generations. Choose an option below to continue creating stunning data visualizations.

Showcase Hub Bundle (Recommended)

Unlock ALL 19+ Pixel Office apps for just $9/month. Billed monthly.

Lifetime Access to This App

Get unlimited, lifetime access to "DataViz Embed Builder" for a one-time payment.

Crypto Micro-Payment

Pay securely using Solana or Bitcoin for lifetime access to this specific app.

Sign In

Don't have an account? Register.

`; embedCodeTextarea.value = embedHtml.trim(); embedCodeSection.style.display = 'flex'; showToast(translations[currentLang]['embedCodeGenerated']); } async function copyEmbedCode() { try { await navigator.clipboard.writeText(embedCodeTextarea.value); showToast(translations[currentLang]['copiedToClipboard']); } catch (err) { console.error('Failed to copy: ', err); showToast('Failed to copy embed code.', true); } } // --- Event Listeners for main functionality --- loadSampleCSVButton.addEventListener('click', () => { dataSourceTextarea.value = sampleCSV; parseDataAndPopulateUI(); }); loadSampleJSONButton.addEventListener('click', () => { dataSourceTextarea.value = sampleJSON; parseDataAndPopulateUI(); }); parseDataButton.addEventListener('click', () => { incrementUsageCounter(parseDataAndPopulateUI); }); generateChartButton.addEventListener('click', () => { incrementUsageCounter(generateChart); }); generateEmbedCodeButton.addEventListener('click', () => { incrementUsageCounter(generateEmbedCode); }); copyEmbedCodeButton.addEventListener('click', copyEmbedCode); // --- Monetization Logic --- const LOCAL_STORAGE_KEY_UNLOCKED = `pv_unlocked_${WIDGET_SLUG}`; const LOCAL_STORAGE_KEY_ACTIONS = `pv_actions_${WIDGET_SLUG}`; const FREE_USES_LIMIT = 3; let authModalState = 'signin'; // 'signin' or 'register' // Display/Hide Loading Spinner for Payment Modal function togglePaymentLoading(show) { if (show) paymentLoadingSpinner.classList.add('active'); else paymentLoadingSpinner.classList.remove('active'); hubSubscribeButton.disabled = show; oneTimeUnlockButton.disabled = show; cryptoPayButton.disabled = show; } // Display/Hide Loading Spinner for Auth Modal function toggleAuthLoading(show) { if (show) authLoadingSpinner.classList.add('active'); else authLoadingSpinner.classList.remove('active'); authEmailInput.disabled = show; authPasswordInput.disabled = show; authSubmitButton.disabled = show; googleSignInButton.disabled = show; authToggleLink.style.pointerEvents = show ? 'none' : 'auto'; authErrorMessage.textContent = ''; } function showPaymentOverlay() { if (isWidgetUnlocked) return; paymentOverlay.classList.add('active'); // Update button texts based on auth state if (currentUser) { hubSubscribeButton.innerHTML = `${translations[currentLang]['hubSubscribeButton']}`; hubSubscribeButton.onclick = createHubSubscription; } else { hubSubscribeButton.innerHTML = `${translations[currentLang]['hubSignInToUnlock']}`; hubSubscribeButton.onclick = () => showAuthModal(true); } oneTimeUnlockButton.innerHTML = `${translations[currentLang]['oneTimeUnlockButton']} $1.99`; hubSubscribeButton.disabled = false; oneTimeUnlockButton.disabled = false; cryptoPayButton.disabled = false; setLanguage(currentLang); // Re-apply translations for dynamic content } function hidePaymentOverlay() { paymentOverlay.classList.remove('active'); } function unlockWidget() { isWidgetUnlocked = true; localStorage.setItem(LOCAL_STORAGE_KEY_UNLOCKED, 'true'); hidePaymentOverlay(); showToast("Widget unlocked successfully!"); } function incrementUsageCounter(callback) { if (isWidgetUnlocked) { callback(); return; } let usageCount = parseInt(localStorage.getItem(LOCAL_STORAGE_KEY_ACTIONS) || '0', 10); if (usageCount < FREE_USES_LIMIT) { usageCount++; localStorage.setItem(LOCAL_STORAGE_KEY_ACTIONS, usageCount.toString()); callback(); } else { showPaymentOverlay(); } } // --- Auth Modal Logic --- function showAuthModal(forHubSubscription = false) { authModalOverlay.classList.add('active'); authModalState = 'signin'; // Always start with sign-in updateAuthModalUI(); authEmailInput.value = ''; authPasswordInput.value = ''; authErrorMessage.textContent = ''; authSubmitButton.dataset.forHubSubscription = forHubSubscription; googleSignInButton.dataset.forHubSubscription = forHubSubscription; } function hideAuthModal() { authModalOverlay.classList.remove('active'); } function updateAuthModalUI() { if (authModalState === 'signin') { authModalTitle.textContent = translations[currentLang]['signInTitle']; authSubmitButton.textContent = translations[currentLang]['signInButton']; authToggleLink.innerHTML = `${translations[currentLang]['dontHaveAccount']}`; } else { // register authModalTitle.textContent = translations[currentLang]['registerTitle']; authSubmitButton.textContent = translations[currentLang]['registerButton']; authToggleLink.innerHTML = `${translations[currentLang]['alreadyHaveAccount']}`; } } authToggleLink.addEventListener('click', (e) => { e.preventDefault(); authModalState = authModalState === 'signin' ? 'register' : 'signin'; updateAuthModalUI(); authErrorMessage.textContent = ''; }); authModalCloseButton.addEventListener('click', hideAuthModal); authSubmitButton.addEventListener('click', async () => { toggleAuthLoading(true); const email = authEmailInput.value; const password = authPasswordInput.value; const forHubSubscription = authSubmitButton.dataset.forHubSubscription === 'true'; try { if (authModalState === 'signin') { await auth.signInWithEmailAndPassword(email, password); } else { // register await auth.createUserWithEmailAndPassword(email, password); } showToast(translations[currentLang]['authSuccess']); hideAuthModal(); } catch (error) { console.error("Auth error:", error); authErrorMessage.textContent = translations[currentLang]['authError'] + error.message; } finally { toggleAuthLoading(false); } }); googleSignInButton.addEventListener('click', async () => { toggleAuthLoading(true); const forHubSubscription = googleSignInButton.dataset.forHubSubscription === 'true'; try { await auth.signInWithPopup(googleProvider); showToast(translations[currentLang]['authSuccess']); hideAuthModal(); } catch (error) { console.error("Google Auth error:", error); authErrorMessage.textContent = translations[currentLang]['authError'] + error.message; } finally { toggleAuthLoading(false); } }); // --- Stripe / Crypto Payment Functions --- async function createHubSubscription() { if (!currentUser) { showToast(translations[currentLang]['hubSignInToUnlock'], true); showAuthModal(true); // Open auth modal to sign in/register for hub return; } togglePaymentLoading(true); try { const response = await fetch(`${API_BASE_URL}/create-hub-subscription`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: currentUser.email, userId: currentUser.uid }) }); const data = await response.json(); if (data.url) { window.location.href = data.url; } else { throw new Error(data.error || 'Failed to create Hub subscription session.'); } } catch (error) { console.error("Hub subscription error:", error); showToast(`Error: ${error.message}`, true); } finally { togglePaymentLoading(false); } } async function createOneTimePayment() { togglePaymentLoading(true); try { const response = await fetch(`${API_BASE_URL}/create-session`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ slug: WIDGET_SLUG, amount: 199, widgetName: WIDGET_NAME }) }); const data = await response.json(); if (data.url) { window.location.href = data.url; } else { throw new Error(data.error || 'Failed to create Stripe checkout session.'); } } catch (error) { console.error("One-time payment error:", error); showToast(`Error: ${error.message}`, true); } finally { togglePaymentLoading(false); } } async function requestCryptoPayment() { togglePaymentLoading(true); try { const response = await fetch(`${API_BASE_URL}/request-crypto`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ slug: WIDGET_SLUG, amount: 199, widgetName: WIDGET_NAME }) }); const data = await response.json(); if (data.address && data.amount && data.currency) { // This is where you would display a QR code and polling UI alert(`To pay for ${WIDGET_NAME} ($1.99): Send ${data.amount} ${data.currency} to ${data.address}. Polling for payment...`); // For a real app, you'd show a modal with QR code and start polling /verify-crypto // For this single-file app, a simple alert and manual unlock simulation: const confirmUnlock = confirm("Simulate crypto payment success? Click OK to unlock widget."); if (confirmUnlock) { unlockWidget(); } } else { throw new Error(data.error || 'Failed to initiate crypto payment.'); } } catch (error) { console.error("Crypto payment error:", error); showToast(`Error: ${error.message}`, true); } finally { togglePaymentLoading(false); } } hubSubscribeButton.addEventListener('click', createHubSubscription); oneTimeUnlockButton.addEventListener('click', createOneTimePayment); cryptoPayButton.addEventListener('click', requestCryptoPayment); // --- Subscription / Payment Check on Load --- async function checkSubscriptionStatus(user) { try { const response = await fetch(`${API_BASE_URL}/check-subscription?email=${encodeURIComponent(user.email)}`); const data = await response.json(); if (data.active) { unlockWidget(); return true; } } catch (error) { console.error("Failed to check subscription:", error); } return false; } async function checkUrlForPaymentStatus() { const urlParams = new URLSearchParams(window.location.search); const sessionId = urlParams.get('session_id'); const status = urlParams.get('status'); const subscriptionStatus = urlParams.get('subscription_status'); if (sessionId && (status === 'success' || subscriptionStatus === 'success')) { showToast("Verifying your payment...", false); togglePaymentLoading(true); try { const endpoint = status === 'success' ? `${API_BASE_URL}/verify-session?session_id=${sessionId}` : `${API_BASE_URL}/check-subscription?email=${encodeURIComponent(currentUser.email)}`; // For Hub subscription via URL callback const response = await fetch(endpoint); const data = await response.json(); if (data.verified || data.active) { unlockWidget(); showToast("Payment confirmed! Widget unlocked.", false); } else { showToast("Payment verification failed. Please contact support.", true); } } catch (error) { console.error("Payment verification error:", error); showToast("Payment verification failed due to an error.", true); } finally { togglePaymentLoading(false); // Clean URL const newUrl = new URL(window.location.origin + window.location.pathname); window.history.replaceState({}, document.title, newUrl.toString()); } } } // --- Main Initialization Flow --- document.addEventListener('DOMContentLoaded', async () => { // Check local storage for permanent unlock if (localStorage.getItem(LOCAL_STORAGE_KEY_UNLOCKED) === 'true') { unlockWidget(); return; } // Check URL for payment success callback await checkUrlForPaymentStatus(); // Firebase Auth State Listener auth.onAuthStateChanged(async (user) => { currentUser = user; if (isWidgetUnlocked) return; // If unlocked by URL param or local storage, skip checks if (user) { // User is signed in, check their subscription status const hasActiveSub = await checkSubscriptionStatus(user); if (hasActiveSub) return; } // If not unlocked and no active subscription, apply the usage limit logic let usageCount = parseInt(localStorage.getItem(LOCAL_STORAGE_KEY_ACTIONS) || '0', 10); if (usageCount >= FREE_USES_LIMIT) { showPaymentOverlay(); } }); // Initial call to hide sections toggleChartOptions(); });