Universal Code Snippet & Embeddable Widget Generator

Select Component Type

Customize Your Snippet

Please select a component type above to start customizing.

\n`; generatedCodeTextarea.value = fullCode; codeOutputSection.classList.remove('hidden'); updatePreview(fullCode); incrementUsageCount(); if (isWidgetUnlocked()) { whatsappRequestBtn.classList.remove('hidden'); } else { whatsappRequestBtn.classList.add('hidden'); } }); function updatePreview(code) { const previewDoc = previewIframe.contentDocument || previewIframe.contentWindow.document; previewDoc.open(); // Basic HTML structure for preview, without full head/body previewDoc.write(` ${code.match(/([\s\S]*?)/i)?.[1] || ''} `); previewDoc.close(); } copyCodeBtn.addEventListener('click', async () => { try { await navigator.clipboard.writeText(generatedCodeTextarea.value); alert(translations[currentLang].copiedToClipboard); } catch (err) { console.error('Failed to copy: ', err); alert(translations[currentLang].copyFailed); } }); whatsappRequestBtn.addEventListener('click', () => { const selectedComponentId = componentTypeSelect.value; const selectedComponent = componentTypes.find(c => c.id === selectedComponentId); const options = {}; customizationPanel.querySelectorAll('[data-field-id]').forEach(input => { options[input.dataset.fieldId] = input.value; }); const componentName = selectedComponent ? translations[currentLang][selectedComponent.nameKey] : 'Unknown'; const customizations = Object.entries(options).map(([key, value]) => `${key}: ${value}`).join(', '); const userEmail = currentUser ? currentUser.email : 'N/A (user not logged in)'; const message = translations[currentLang].whatsappMessageSnippet .replace('{type}', componentName) .replace('{customizations}', customizations) .replace('{email}', userEmail); const whatsappUrl = `https://wa.me/${WHATSAPP_PHONE}?text=${encodeURIComponent(message)}`; window.open(whatsappUrl, '_blank'); }); // --- Monetization Logic (Jan) --- const UNLOCK_KEY = `pv_unlocked_${WIDGET_SLUG}`; const ACTIONS_KEY = `pv_actions_${WIDGET_SLUG}`; function isWidgetUnlocked() { return localStorage.getItem(UNLOCK_KEY) === 'true'; } function setWidgetUnlocked(unlocked = true) { localStorage.setItem(UNLOCK_KEY, unlocked.toString()); // Hide payment overlay if it was visible if (unlocked) { hideModal(paymentOverlay); whatsappRequestBtn.classList.remove('hidden'); // Show premium button } } function getUsageCount() { return parseInt(localStorage.getItem(ACTIONS_KEY) || '0', 10); } function incrementUsageCount() { if (!isWidgetUnlocked()) { const count = getUsageCount() + 1; localStorage.setItem(ACTIONS_KEY, count.toString()); if (count >= 3) { showPaymentOverlay(); } } } function checkMonetizationStatus() { if (isWidgetUnlocked()) { console.log("Widget is unlocked!"); whatsappRequestBtn.classList.remove('hidden'); return; } const usageCount = getUsageCount(); if (usageCount >= 3) { console.log(`Usage limit (${usageCount}/3) reached. Showing payment overlay.`); showPaymentOverlay(); } else { console.log(`Widget is not unlocked. Current usage: ${usageCount}/3`); } } function showPaymentOverlay() { payOverlayTitle.textContent = translations[currentLang].payOverlayTitle; payOverlayText.textContent = translations[currentLang].payOverlayText; document.querySelector('#hub-subscribe-btn').textContent = translations[currentLang].hubBundleButton; document.querySelector('#one-time-unlock-btn').textContent = translations[currentLang].oneTimeButton; document.querySelector('#crypto-pay-btn').textContent = translations[currentLang].cryptoButton; document.querySelector('.payment-card h3[data-i18n="hubBundleTitle"]').textContent = translations[currentLang].hubBundleTitle; document.querySelector('.payment-card p[data-i18n="hubBundleDescription"]').textContent = translations[currentLang].hubBundleDescription; document.querySelector('.payment-card h3[data-i18n="oneTimeTitle"]').textContent = translations[currentLang].oneTimeTitle; document.querySelector('.payment-card p[data-i18n="oneTimeDescription"]').textContent = translations[currentLang].oneTimeDescription; document.querySelector('.payment-card h3[data-i18n="cryptoTitle"]').textContent = translations[currentLang].cryptoTitle; document.querySelector('.payment-card p[data-i18n="cryptoDescription"]').textContent = translations[currentLang].cryptoDescription; // Adjust Hub button text based on login status if (currentUser) { hubSubscribeBtn.textContent = translations[currentLang].hubBundleButton.replace(translations[currentLang].hubBundleButton.split(' / ')[0], translations[currentLang].hubBundleButton.split(' / ')[1]); // "Subscribe" } else { hubSubscribeBtn.textContent = translations[currentLang].hubBundleButton; // "Sign In to Unlock / Subscribe" } showModal(paymentOverlay); } // --- Firebase Integration --- const firebaseConfig = { apiKey: "AIzaSyFakeKeyForShowcaseHubAuthTestingOnly", authDomain: "pixeloffice-hub.firebaseapp.com", projectId: "pixeloffice-hub", storageBucket: "pixeloffice-hub.appspot.com", messagingSenderId: "1234567890", appId: "1:1234567890:web:abcdef123456" }; if (!firebase.apps.length) { firebase.initializeApp(firebaseConfig); } const auth = firebase.auth(); const googleProvider = new firebase.auth.GoogleAuthProvider(); auth.onAuthStateChanged(async user => { currentUser = user; if (currentUser) { console.log(translations[currentLang].loggedIn + currentUser.email); await checkSubscription(currentUser.email); } else { console.log(translations[currentLang].loggedOut); // If logged out and widget was unlocked via subscription, revoke unlock // This is a simplified client-side check. A proper check would happen if payment_status changed if (localStorage.getItem(UNLOCK_KEY) === 'true') { // For now, we keep it unlocked unless explicitly checkSubscription says otherwise. // This avoids locking out users who paid but firebase state briefly changes. // A full app would handle this gracefully on a server. } } // Update Hub button text if (paymentOverlay.classList.contains('visible')) { showPaymentOverlay(); // Re-render to update button } }); // --- Auth Modal Logic --- function updateAuthModalText() { authModalTitle.textContent = isAuthRegisterMode ? translations[currentLang].registerTitle : translations[currentLang].signInTitle; authSubmitBtn.textContent = isAuthRegisterMode ? translations[currentLang].registerButton : translations[currentLang].signInButton; toggleAuthModeBtn.textContent = isAuthRegisterMode ? translations[currentLang].switchToSignIn : translations[currentLang].switchToRegister; authEmailInput.placeholder = translations[currentLang].emailPlaceholder; authPasswordInput.placeholder = translations[currentLang].passwordPlaceholder; googleLoginBtn.textContent = translations[currentLang].signInWithGoogle; authError.textContent = ''; } function showAuthModal() { isAuthRegisterMode = false; // Always start with sign-in updateAuthModalText(); showModal(authModal); } toggleAuthModeBtn.addEventListener('click', () => { isAuthRegisterMode = !isAuthRegisterMode; updateAuthModalText(); }); document.getElementById('login-form').addEventListener('submit', async (e) => { e.preventDefault(); authError.textContent = ''; showLoading(); const email = authEmailInput.value; const password = authPasswordInput.value; try { if (isAuthRegisterMode) { await auth.createUserWithEmailAndPassword(email, password); } else { await auth.signInWithEmailAndPassword(email, password); } hideModal(authModal); await checkSubscription(email); // Check subscription right after login/register } catch (error) { authError.textContent = translations[currentLang].authError + error.message; console.error("Auth error:", error); } finally { hideLoading(); } }); googleLoginBtn.addEventListener('click', async () => { authError.textContent = ''; showLoading(); try { await auth.signInWithPopup(googleProvider); hideModal(authModal); // AuthStateChanged listener will handle subscription check } catch (error) { authError.textContent = translations[currentLang].authError + error.message; console.error("Google Auth error:", error); } finally { hideLoading(); } }); // --- API Calls & Payment Flow --- async function checkSubscription(email) { showLoading(); try { const response = await fetch(`${API_BASE_URL}/check-subscription?email=${encodeURIComponent(email)}`); const data = await response.json(); if (data.active) { setWidgetUnlocked(true); console.log(translations[currentLang].subscriptionActive); } else { setWidgetUnlocked(false); console.log(translations[currentLang].subscriptionInactive); } } catch (error) { console.error("Error checking subscription:", error); setWidgetUnlocked(false); // Assume not unlocked on error } finally { hideLoading(); if (!isWidgetUnlocked() && getUsageCount() >= 3) { showPaymentOverlay(); } } } hubSubscribeBtn.addEventListener('click', async () => { if (!currentUser) { showAuthModal(); return; } showLoading(); 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 { alert(translations[currentLang].errorUnlocking); } } catch (error) { console.error("Error creating hub subscription:", error); alert(translations[currentLang].errorUnlocking); } finally { hideLoading(); } }); oneTimeUnlockBtn.addEventListener('click', async () => { showLoading(); 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: translations.en.headerTitle }) // Use EN for backend slug/name }); const data = await response.json(); if (data.url) { window.location.href = data.url; } else { alert(translations[currentLang].errorUnlocking); } } catch (error) { console.error("Error creating one-time session:", error); alert(translations[currentLang].errorUnlocking); } finally { hideLoading(); } }); cryptoPayBtn.addEventListener('click', async () => { showLoading(); 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: translations.en.headerTitle }) }); const data = await response.json(); if (data.qrCodeUrl && data.address && data.amount && data.paymentId) { cryptoModalTitle.textContent = translations[currentLang].cryptoPaymentDetailsTitle; cryptoModalText.textContent = translations[currentLang].cryptoPaymentDetailsText; document.querySelector('#crypto-modal strong[data-i18n="cryptoAddressLabel"]').textContent = translations[currentLang].cryptoAddressLabel; document.querySelector('#crypto-modal strong[data-i18n="cryptoAmountLabel"]').textContent = translations[currentLang].cryptoAmountLabel; cryptoQrCode.src = data.qrCodeUrl; cryptoAddress.textContent = data.address; cryptoAmount.textContent = `${data.amount} ${data.currency}`; cryptoStatus.textContent = translations[currentLang].cryptoStatusWaiting; showModal(cryptoModal); startCryptoPolling(data.paymentId); } else { alert(translations[currentLang].errorFetchingCrypto); } } catch (error) { console.error("Error requesting crypto payment:", error); alert(translations[currentLang].errorFetchingCrypto); } finally { hideLoading(); } }); async function startCryptoPolling(paymentId) { if (cryptoPollingInterval) clearInterval(cryptoPollingInterval); cryptoPollingInterval = setInterval(async () => { try { const response = await fetch(`${API_BASE_URL}/verify-crypto?paymentId=${paymentId}`); const data = await response.json(); if (data.status === 'paid') { clearInterval(cryptoPollingInterval); cryptoStatus.textContent = translations[currentLang].cryptoStatusPaid; setWidgetUnlocked(true); alert(translations[currentLang].unlockSuccess); hideModal(cryptoModal); } else if (data.status === 'expired' || data.status === 'error') { clearInterval(cryptoPollingInterval); cryptoStatus.textContent = translations[currentLang].cryptoStatusError; } } catch (error) { console.error("Error polling crypto status:", error); clearInterval(cryptoPollingInterval); cryptoStatus.textContent = translations[currentLang].cryptoStatusError; } }, 5000); // Poll every 5 seconds } // --- URL Payment Status Check --- 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 ((status === 'success' || subscriptionStatus === 'success') && sessionId) { showLoading(); try { let verifyUrl; if (status === 'success') { // One-time payment verifyUrl = `${API_BASE_URL}/verify-session?session_id=${sessionId}`; const response = await fetch(verifyUrl); const data = await response.json(); if (data.active) { setWidgetUnlocked(true); alert(translations[currentLang].unlockSuccess); } else { alert(translations[currentLang].errorUnlocking); } } else if (subscriptionStatus === 'success') { // Hub subscription // For Hub, we rely on Firebase authStateChange and its subscription check // If user is already logged in, auth.onAuthStateChanged will handle it. // If not, they will be prompted to log in/register and then subscription checked. // For now, assume if this param is here, they completed Stripe and authStateChanged will resolve. if (currentUser) { await checkSubscription(currentUser.email); } else { // User needs to log in, then subscription will be verified. // We can't immediately unlock without knowing the user. alert("Please sign in to verify your subscription."); showAuthModal(); } } } catch (error) { console.error("Error verifying payment session:", error); alert(translations[currentLang].errorUnlocking); } finally { hideLoading(); // Clean URL const newUrl = new URL(window.location.href); newUrl.searchParams.delete('session_id'); newUrl.searchParams.delete('status'); newUrl.searchParams.delete('subscription_status'); window.history.replaceState({}, document.title, newUrl.toString()); } } } // --- Event Listeners for closing modals --- document.querySelectorAll('.close-modal-btn').forEach(button => { button.addEventListener('click', (e) => { const targetId = e.target.dataset.target; if (targetId) { hideModal(document.getElementById(targetId)); } if (cryptoPollingInterval) { clearInterval(cryptoPollingInterval); } }); }); // --- Initialisation --- document.addEventListener('DOMContentLoaded', async () => { populateComponentSelect(); renderCustomizationPanel(componentTypeSelect.value); // Render default empty panel await checkUrlForPaymentStatus(); // Check URL for payment success first checkMonetizationStatus(); // Then check regular monetization status });