472 lines
17 KiB
JavaScript
472 lines
17 KiB
JavaScript
// Current selection state
|
|
let currentBillingCycle = 'monthly';
|
|
let currentCurrency = 'USD';
|
|
let isProcessing = false;
|
|
|
|
// Plan pricing configuration (matching pricing.html logic)
|
|
const planPricing = {
|
|
starter: {
|
|
monthly: { USD: 7.5, GBP: 5, EUR: 7.5 },
|
|
yearly: { USD: 75, GBP: 50, EUR: 75 }
|
|
},
|
|
professional: {
|
|
monthly: { USD: 25, GBP: 20, EUR: 25 },
|
|
yearly: { USD: 250, GBP: 200, EUR: 250 }
|
|
},
|
|
enterprise: {
|
|
monthly: { USD: 75, GBP: 60, EUR: 75 },
|
|
yearly: { USD: 750, GBP: 600, EUR: 750 }
|
|
}
|
|
};
|
|
|
|
// Currency symbols
|
|
const currencySymbols = {
|
|
USD: '$',
|
|
GBP: '£',
|
|
EUR: '€'
|
|
};
|
|
|
|
// Helper to get auth headers from localStorage
|
|
function getAuthHeaders() {
|
|
try {
|
|
const userStr = localStorage.getItem('wordpress_plugin_ai_user') || localStorage.getItem('shopify_ai_user');
|
|
if (userStr) {
|
|
const user = JSON.parse(userStr);
|
|
if (user && user.sessionToken) {
|
|
return { 'Authorization': `Bearer ${user.sessionToken}` };
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.warn('Failed to get auth headers from localStorage', err);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
// Initialize on page load
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
setupBillingCycleToggle();
|
|
setupCurrencyDropdown();
|
|
setupPlanSelection();
|
|
updatePlanPrices();
|
|
autoDetectCurrency();
|
|
|
|
// Check for pre-selected plan from URL query parameter
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const preselectedPlan = urlParams.get('plan');
|
|
if (preselectedPlan) {
|
|
preselectPlan(preselectedPlan);
|
|
}
|
|
});
|
|
|
|
function preselectPlan(plan) {
|
|
const card = document.querySelector(`.plan-card[data-plan="${plan}"]`);
|
|
if (card) {
|
|
// Add visual selection state
|
|
const allCards = document.querySelectorAll('.plan-card');
|
|
allCards.forEach(c => {
|
|
c.classList.remove('ring-4', 'ring-green-700', 'ring-offset-2');
|
|
});
|
|
card.classList.add('ring-4', 'ring-green-700', 'ring-offset-2');
|
|
|
|
// Auto-trigger checkout for paid plans (hobby is free, no checkout needed)
|
|
if (plan !== 'hobby') {
|
|
// Small delay to ensure UI is ready
|
|
setTimeout(() => {
|
|
triggerPlanSelection(plan);
|
|
}, 100);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Separate function to trigger plan selection (can be called from click or auto-trigger)
|
|
async function triggerPlanSelection(plan) {
|
|
// Don't process if already in progress
|
|
if (isProcessing) return;
|
|
|
|
const card = document.querySelector(`.plan-card[data-plan="${plan}"]`);
|
|
if (!card) return;
|
|
|
|
console.log('Plan selected:', plan);
|
|
isProcessing = true;
|
|
|
|
// Update UI state
|
|
const allCards = document.querySelectorAll('.plan-card');
|
|
allCards.forEach(c => {
|
|
c.classList.remove('ring-4', 'ring-green-700', 'ring-offset-2');
|
|
});
|
|
card.classList.add('ring-4', 'ring-green-700', 'ring-offset-2');
|
|
|
|
// Disable all buttons during selection
|
|
const allButtons = document.querySelectorAll('.plan-btn');
|
|
allButtons.forEach(btn => {
|
|
btn.disabled = true;
|
|
btn.textContent = 'Processing...';
|
|
btn.classList.add('opacity-50', 'cursor-not-allowed');
|
|
});
|
|
|
|
try {
|
|
const authHeaders = getAuthHeaders();
|
|
let data;
|
|
|
|
if (plan === 'hobby') {
|
|
// Hobby plan is free, use original endpoint
|
|
const response = await fetch('/api/select-plan', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...authHeaders
|
|
},
|
|
body: JSON.stringify({ plan }),
|
|
credentials: 'include',
|
|
});
|
|
|
|
data = await response.json();
|
|
|
|
if (response.ok && data.ok) {
|
|
window.location.href = '/apps';
|
|
} else {
|
|
throw new Error(data.error || 'Failed to select plan');
|
|
}
|
|
} else {
|
|
// Paid plans use inline subscription checkout
|
|
const response = await fetch('/api/subscription/checkout', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...authHeaders
|
|
},
|
|
body: JSON.stringify({
|
|
plan: plan,
|
|
billingCycle: currentBillingCycle,
|
|
currency: currentCurrency.toLowerCase(),
|
|
inline: true
|
|
}),
|
|
credentials: 'include',
|
|
});
|
|
|
|
data = await response.json();
|
|
|
|
if (response.ok && data.sessionId) {
|
|
// Use Dodo inline checkout - prefer inlineCheckoutUrl if available, otherwise use checkoutUrl
|
|
const checkoutUrl = data.inlineCheckoutUrl || data.checkoutUrl;
|
|
if (checkoutUrl) {
|
|
await openInlineCheckout(checkoutUrl, data.sessionId);
|
|
} else {
|
|
throw new Error('No checkout URL provided');
|
|
}
|
|
} else {
|
|
throw new Error(data.error || 'Failed to start checkout');
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error selecting plan:', error);
|
|
|
|
alert(error.message || 'Failed to select plan. Please try again.');
|
|
|
|
// Re-enable buttons
|
|
allButtons.forEach(btn => {
|
|
btn.disabled = false;
|
|
const planType = btn.getAttribute('data-plan');
|
|
if (planType === 'hobby') {
|
|
btn.textContent = 'Start for Free';
|
|
} else if (planType === 'starter') {
|
|
btn.textContent = 'Choose Starter';
|
|
} else if (planType === 'professional') {
|
|
btn.textContent = 'Get Started';
|
|
} else if (planType === 'enterprise') {
|
|
btn.textContent = 'Choose Enterprise';
|
|
}
|
|
btn.classList.remove('opacity-50', 'cursor-not-allowed');
|
|
});
|
|
isProcessing = false;
|
|
}
|
|
}
|
|
|
|
function setupBillingCycleToggle() {
|
|
const monthlyBtn = document.getElementById('monthly-toggle');
|
|
const yearlyBtn = document.getElementById('yearly-toggle');
|
|
if (!monthlyBtn || !yearlyBtn) return;
|
|
|
|
monthlyBtn.addEventListener('click', () => {
|
|
currentBillingCycle = 'monthly';
|
|
monthlyBtn.className = 'px-6 py-2 rounded-full text-sm font-medium bg-green-700 text-white shadow-sm transition-all';
|
|
yearlyBtn.className = 'px-6 py-2 rounded-full text-sm font-medium text-gray-600 hover:text-gray-900 transition-all';
|
|
updatePlanPrices();
|
|
});
|
|
|
|
yearlyBtn.addEventListener('click', () => {
|
|
currentBillingCycle = 'yearly';
|
|
yearlyBtn.className = 'px-6 py-2 rounded-full text-sm font-medium bg-green-700 text-white shadow-sm transition-all';
|
|
monthlyBtn.className = 'px-6 py-2 rounded-full text-sm font-medium text-gray-600 hover:text-gray-900 transition-all';
|
|
updatePlanPrices();
|
|
});
|
|
}
|
|
|
|
function setupCurrencyDropdown() {
|
|
const dropdown = document.getElementById('currency-dropdown');
|
|
const btn = document.getElementById('currency-btn');
|
|
const options = document.getElementById('currency-options');
|
|
const chevron = document.getElementById('currency-chevron');
|
|
if (!dropdown || !btn || !options) return;
|
|
|
|
btn.addEventListener('click', (e) => {
|
|
e.stopPropagation();
|
|
const isOpen = !dropdown.classList.contains('open');
|
|
|
|
if (isOpen) {
|
|
dropdown.classList.add('open');
|
|
options.classList.remove('hidden');
|
|
if (chevron) chevron.style.transform = 'rotate(180deg)';
|
|
} else {
|
|
dropdown.classList.remove('open');
|
|
options.classList.add('hidden');
|
|
if (chevron) chevron.style.transform = 'rotate(0deg)';
|
|
}
|
|
});
|
|
|
|
// Handle currency selection
|
|
document.querySelectorAll('.currency-option').forEach(option => {
|
|
option.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
const currency = option.getAttribute('data-value');
|
|
currentCurrency = currency;
|
|
|
|
// Update button display
|
|
const flags = { USD: '🇺🇸', GBP: '🇬🇧', EUR: '🇪🇺' };
|
|
const flagEl = document.getElementById('currency-flag');
|
|
const codeEl = document.getElementById('currency-code');
|
|
if (flagEl) flagEl.textContent = flags[currency];
|
|
if (codeEl) codeEl.textContent = currency;
|
|
|
|
// Close dropdown
|
|
dropdown.classList.remove('open');
|
|
options.classList.add('hidden');
|
|
if (chevron) chevron.style.transform = 'rotate(0deg)';
|
|
|
|
updatePlanPrices();
|
|
});
|
|
});
|
|
|
|
// Close dropdown when clicking outside
|
|
document.addEventListener('click', () => {
|
|
dropdown.classList.remove('open');
|
|
options.classList.add('hidden');
|
|
if (chevron) chevron.style.transform = 'rotate(0deg)';
|
|
});
|
|
}
|
|
|
|
function updatePlanPrices() {
|
|
// Update Hobby plan price
|
|
const hobbyPrice = document.getElementById('hobby-price');
|
|
const hobbyPeriod = document.getElementById('hobby-period');
|
|
if (hobbyPrice) hobbyPrice.textContent = currencySymbols[currentCurrency] + '0';
|
|
if (hobbyPeriod) hobbyPeriod.textContent = currentBillingCycle === 'monthly' ? '/mo' : '/yr';
|
|
|
|
// Update Starter plan price
|
|
const starterPrice = document.getElementById('starter-price');
|
|
const starterPeriod = document.getElementById('starter-period');
|
|
const starterAmount = planPricing.starter[currentBillingCycle][currentCurrency];
|
|
if (starterPrice) starterPrice.textContent = currencySymbols[currentCurrency] + starterAmount.toFixed(starterAmount % 1 === 0 ? 0 : 2);
|
|
if (starterPeriod) starterPeriod.textContent = currentBillingCycle === 'monthly' ? '/mo' : '/yr';
|
|
|
|
// Update Professional plan price
|
|
const proPrice = document.getElementById('pro-price');
|
|
const proPeriod = document.getElementById('pro-period');
|
|
const proAmount = planPricing.professional[currentBillingCycle][currentCurrency];
|
|
if (proPrice) proPrice.textContent = currencySymbols[currentCurrency] + proAmount.toFixed(proAmount % 1 === 0 ? 0 : 2);
|
|
if (proPeriod) proPeriod.textContent = currentBillingCycle === 'monthly' ? '/mo' : '/yr';
|
|
|
|
// Update Enterprise plan price
|
|
const enterprisePrice = document.getElementById('enterprise-price');
|
|
const enterprisePeriod = document.getElementById('enterprise-period');
|
|
const enterpriseAmount = planPricing.enterprise[currentBillingCycle][currentCurrency];
|
|
if (enterprisePrice) enterprisePrice.textContent = currencySymbols[currentCurrency] + enterpriseAmount.toFixed(enterpriseAmount % 1 === 0 ? 0 : 2);
|
|
if (enterprisePeriod) enterprisePeriod.textContent = currentBillingCycle === 'monthly' ? '/mo' : '/yr';
|
|
}
|
|
|
|
function setupPlanSelection() {
|
|
// Only handle card clicks to avoid double-triggering
|
|
document.querySelectorAll('.plan-card').forEach(card => {
|
|
card.addEventListener('click', async (e) => {
|
|
// Let links within the card work normally
|
|
if (e.target.closest('a')) return;
|
|
|
|
e.preventDefault();
|
|
|
|
const plan = card.getAttribute('data-plan');
|
|
if (!plan) return;
|
|
|
|
// Use the shared triggerPlanSelection function
|
|
triggerPlanSelection(plan);
|
|
});
|
|
});
|
|
|
|
// Prevent button clicks from bubbling to card (card listener handles everything)
|
|
document.querySelectorAll('.plan-btn').forEach(btn => {
|
|
btn.addEventListener('click', (e) => {
|
|
// Just let it bubble to the card
|
|
});
|
|
});
|
|
}
|
|
|
|
// Detect Location and Set Currency
|
|
async function autoDetectCurrency() {
|
|
try {
|
|
const response = await fetch('https://ipapi.co/json/');
|
|
const data = await response.json();
|
|
if (data.currency && currencySymbols[data.currency]) {
|
|
currentCurrency = data.currency;
|
|
|
|
// Update button display
|
|
const flags = { USD: '🇺🇸', GBP: '🇬🇧', EUR: '🇪🇺' };
|
|
const flagEl = document.getElementById('currency-flag');
|
|
const codeEl = document.getElementById('currency-code');
|
|
if (flagEl) flagEl.textContent = flags[currentCurrency];
|
|
if (codeEl) codeEl.textContent = currentCurrency;
|
|
|
|
updatePlanPrices();
|
|
}
|
|
} catch (err) {
|
|
console.log('Currency detection failed, defaulting to USD');
|
|
}
|
|
}
|
|
|
|
async function openInlineCheckout(checkoutUrl, sessionId) {
|
|
const modal = document.getElementById('payment-modal');
|
|
const container = document.getElementById('payment-frame-container');
|
|
|
|
container.innerHTML = `<iframe src="${checkoutUrl}" allowpaymentrequest="true"></iframe>`;
|
|
modal.classList.add('active');
|
|
|
|
let pollCount = 0;
|
|
const maxPolls = 180;
|
|
const pollInterval = 1000;
|
|
|
|
const checkPaymentStatus = async () => {
|
|
try {
|
|
pollCount++;
|
|
|
|
const resp = await fetch(`/api/subscription/confirm?session_id=${encodeURIComponent(sessionId)}`, {
|
|
credentials: 'include'
|
|
});
|
|
|
|
if (resp.ok) {
|
|
const data = await resp.json();
|
|
if (data.ok) {
|
|
clearInterval(pollTimer);
|
|
closeCheckoutModal();
|
|
window.location.href = '/subscription-success';
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (pollCount >= maxPolls) {
|
|
clearInterval(pollTimer);
|
|
closeCheckoutModal();
|
|
alert('Payment confirmation timeout. Please check your account status.');
|
|
reenableButtons();
|
|
isProcessing = false;
|
|
}
|
|
} catch (error) {
|
|
console.error('Error checking payment status:', error);
|
|
}
|
|
};
|
|
|
|
const pollTimer = setInterval(checkPaymentStatus, pollInterval);
|
|
|
|
function closeCheckoutModal() {
|
|
clearInterval(pollTimer);
|
|
modal.classList.remove('active');
|
|
container.innerHTML = '';
|
|
}
|
|
|
|
function reenableButtons() {
|
|
const allButtons = document.querySelectorAll('.plan-btn');
|
|
allButtons.forEach(btn => {
|
|
btn.disabled = false;
|
|
const planType = btn.getAttribute('data-plan');
|
|
if (planType === 'hobby') {
|
|
btn.textContent = 'Start for Free';
|
|
} else if (planType === 'starter') {
|
|
btn.textContent = 'Choose Starter';
|
|
} else if (planType === 'professional') {
|
|
btn.textContent = 'Get Started';
|
|
} else if (planType === 'enterprise') {
|
|
btn.textContent = 'Choose Enterprise';
|
|
}
|
|
btn.classList.remove('opacity-50', 'cursor-not-allowed');
|
|
});
|
|
}
|
|
|
|
document.getElementById('payment-modal-close').onclick = () => {
|
|
closeCheckoutModal();
|
|
reenableButtons();
|
|
isProcessing = false;
|
|
};
|
|
|
|
modal.onclick = (e) => {
|
|
if (e.target === modal) {
|
|
closeCheckoutModal();
|
|
reenableButtons();
|
|
isProcessing = false;
|
|
}
|
|
};
|
|
|
|
const handleEsc = (e) => {
|
|
if (e.key === 'Escape') {
|
|
closeCheckoutModal();
|
|
reenableButtons();
|
|
isProcessing = false;
|
|
}
|
|
};
|
|
document.addEventListener('keydown', handleEsc);
|
|
}
|
|
|
|
// Email Signup Form Handler
|
|
const signupForm = document.getElementById('footer-signup-form');
|
|
const signupMessage = document.getElementById('signup-message');
|
|
|
|
if (signupForm) {
|
|
signupForm.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
const email = signupForm.querySelector('input[name="email"]').value;
|
|
const button = signupForm.querySelector('button');
|
|
|
|
button.disabled = true;
|
|
button.textContent = 'Subscribing...';
|
|
|
|
try {
|
|
const response = await fetch('https://emailmarketing.modelrailway3d.co.uk/api/webhooks/incoming/wh_0Z49zi_DGj4-lKJMOPO8-g', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({
|
|
email: email,
|
|
source: 'plugin_compass_select_plan',
|
|
timestamp: new Date().toISOString()
|
|
})
|
|
});
|
|
|
|
if (response.ok) {
|
|
signupMessage.textContent = 'Successfully subscribed!';
|
|
signupMessage.className = 'mt-2 text-xs text-green-600';
|
|
signupForm.reset();
|
|
} else {
|
|
throw new Error('Failed to subscribe');
|
|
}
|
|
} catch (error) {
|
|
signupMessage.textContent = 'Failed to subscribe. Please try again.';
|
|
signupMessage.className = 'mt-2 text-xs text-red-600';
|
|
} finally {
|
|
signupMessage.classList.remove('hidden');
|
|
button.disabled = false;
|
|
button.textContent = 'Subscribe';
|
|
|
|
setTimeout(() => {
|
|
signupMessage.classList.add('hidden');
|
|
}, 5000);
|
|
}
|
|
});
|
|
}
|
|
} |