/** * Koha Donation System * Handles donation form functionality with CSP compliance */ // Form state let selectedFrequency = 'monthly'; let selectedTier = '15'; let selectedAmount = 1500; // in cents (NZD) let currentCurrency = typeof detectUserCurrency === 'function' ? detectUserCurrency() : 'NZD'; document.addEventListener('DOMContentLoaded', function() { // Initialize event listeners initializeFrequencyButtons(); initializeTierCards(); initializePublicAcknowledgement(); initializeDonationForm(); }); /** * Initialize frequency selection buttons */ function initializeFrequencyButtons() { const monthlyBtn = document.getElementById('freq-monthly'); const onetimeBtn = document.getElementById('freq-onetime'); if (monthlyBtn) { monthlyBtn.addEventListener('click', function() { selectFrequency('monthly'); }); } if (onetimeBtn) { onetimeBtn.addEventListener('click', function() { selectFrequency('one_time'); }); } } /** * Initialize tier card click handlers */ function initializeTierCards() { const tierCards = document.querySelectorAll('[data-tier]'); tierCards.forEach(card => { card.addEventListener('click', function() { const tier = this.dataset.tier; const amount = parseInt(this.dataset.amount); selectTier(tier, amount); }); }); } /** * Initialize public acknowledgement checkbox */ function initializePublicAcknowledgement() { const checkbox = document.getElementById('public-acknowledgement'); if (checkbox) { checkbox.addEventListener('change', togglePublicName); } } /** * Initialize donation form submission */ function initializeDonationForm() { const form = document.getElementById('donation-form'); if (form) { form.addEventListener('submit', handleFormSubmit); } } /** * Update prices when currency changes */ window.updatePricesForCurrency = function(currency) { currentCurrency = currency; if (typeof getTierPrices !== 'function' || typeof formatCurrency !== 'function') { console.warn('Currency utilities not loaded'); return; } const prices = getTierPrices(currency); // Update tier card prices const tierCards = document.querySelectorAll('.tier-card'); if (tierCards[0]) { const priceEl = tierCards[0].querySelector('.text-4xl'); const currencyEl = tierCards[0].querySelector('.text-sm.text-gray-500'); if (priceEl) priceEl.textContent = formatCurrency(prices.tier_5, currency).replace(/\.\d+$/, ''); if (currencyEl) currencyEl.textContent = `${currency} / month`; } if (tierCards[1]) { const priceEl = tierCards[1].querySelector('.text-4xl'); const currencyEl = tierCards[1].querySelector('.text-sm.text-gray-500'); if (priceEl) priceEl.textContent = formatCurrency(prices.tier_15, currency).replace(/\.\d+$/, ''); if (currencyEl) currencyEl.textContent = `${currency} / month`; } if (tierCards[2]) { const priceEl = tierCards[2].querySelector('.text-4xl'); const currencyEl = tierCards[2].querySelector('.text-sm.text-gray-500'); if (priceEl) priceEl.textContent = formatCurrency(prices.tier_50, currency).replace(/\.\d+$/, ''); if (currencyEl) currencyEl.textContent = `${currency} / month`; } // Update custom amount placeholder const amountInput = document.getElementById('amount-input'); if (amountInput) { amountInput.placeholder = 'Enter amount'; const amountCurrencyLabel = amountInput.nextElementSibling; if (amountCurrencyLabel) { amountCurrencyLabel.textContent = currency; } } // Update help text const amountHelp = document.getElementById('amount-help'); if (amountHelp && typeof formatCurrency === 'function') { amountHelp.textContent = `Minimum donation: ${formatCurrency(100, currency)}`; } }; /** * Select donation frequency (monthly or one-time) */ function selectFrequency(freq) { selectedFrequency = freq; // Update button styles const monthlyBtn = document.getElementById('freq-monthly'); const onetimeBtn = document.getElementById('freq-onetime'); if (freq === 'monthly') { monthlyBtn.className = 'flex-1 py-3 px-6 border-2 border-blue-600 bg-blue-600 text-white rounded-lg font-semibold hover:bg-blue-700 transition'; onetimeBtn.className = 'flex-1 py-3 px-6 border-2 border-gray-300 bg-white text-gray-700 rounded-lg font-semibold hover:border-blue-600 transition'; // Show tier selection, hide custom amount const tierSelection = document.getElementById('tier-selection'); const customAmount = document.getElementById('custom-amount'); if (tierSelection) tierSelection.classList.remove('hidden'); if (customAmount) customAmount.classList.add('hidden'); } else { monthlyBtn.className = 'flex-1 py-3 px-6 border-2 border-gray-300 bg-white text-gray-700 rounded-lg font-semibold hover:border-blue-600 transition'; onetimeBtn.className = 'flex-1 py-3 px-6 border-2 border-blue-600 bg-blue-600 text-white rounded-lg font-semibold hover:bg-blue-700 transition'; // Hide tier selection, show custom amount const tierSelection = document.getElementById('tier-selection'); const customAmount = document.getElementById('custom-amount'); if (tierSelection) tierSelection.classList.add('hidden'); if (customAmount) customAmount.classList.remove('hidden'); const amountInput = document.getElementById('amount-input'); if (amountInput) amountInput.focus(); } } /** * Select tier amount */ function selectTier(tier, amountNZDCents) { selectedTier = tier; // Calculate amount in current currency if (typeof getTierPrices === 'function') { const prices = getTierPrices(currentCurrency); selectedAmount = prices[`tier_${tier}`]; } else { selectedAmount = amountNZDCents; } // Update card styles const cards = document.querySelectorAll('.tier-card'); cards.forEach(card => { card.classList.remove('selected'); }); // Add selected class to clicked card const selectedCard = document.querySelector(`[data-tier="${tier}"]`); if (selectedCard) { selectedCard.classList.add('selected'); } } /** * Toggle public name field visibility */ function togglePublicName() { const checkbox = document.getElementById('public-acknowledgement'); const nameField = document.getElementById('public-name-field'); if (checkbox && nameField) { if (checkbox.checked) { nameField.classList.remove('hidden'); const publicNameInput = document.getElementById('public-name'); if (publicNameInput) publicNameInput.focus(); } else { nameField.classList.add('hidden'); } } } /** * Handle form submission */ async function handleFormSubmit(e) { e.preventDefault(); const submitBtn = e.target.querySelector('button[type="submit"]'); submitBtn.disabled = true; submitBtn.textContent = 'Processing...'; try { // Get form data const donorName = document.getElementById('donor-name').value.trim() || 'Anonymous'; const donorEmail = document.getElementById('donor-email').value.trim(); const donorCountry = document.getElementById('donor-country').value.trim(); const publicAck = document.getElementById('public-acknowledgement').checked; const publicName = document.getElementById('public-name').value.trim(); // Validate email if (!donorEmail) { alert('Please enter your email address.'); submitBtn.disabled = false; submitBtn.textContent = 'Proceed to Secure Payment'; return; } // Get amount let amount; if (selectedFrequency === 'monthly') { amount = selectedAmount; } else { const customAmount = parseFloat(document.getElementById('amount-input').value); if (!customAmount || customAmount < 1) { const minAmount = typeof formatCurrency === 'function' ? formatCurrency(100, currentCurrency) : `${currentCurrency} 1.00`; alert(`Please enter a donation amount of at least ${minAmount}.`); submitBtn.disabled = false; submitBtn.textContent = 'Proceed to Secure Payment'; return; } amount = Math.round(customAmount * 100); // Convert to cents } // Create checkout session const response = await fetch('/api/koha/checkout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ amount: amount, currency: currentCurrency, frequency: selectedFrequency, tier: selectedFrequency === 'monthly' ? selectedTier : 'custom', donor: { name: donorName, email: donorEmail, country: donorCountry }, public_acknowledgement: publicAck, public_name: publicAck ? (publicName || donorName) : null }) }); const data = await response.json(); if (data.success && data.data.checkoutUrl) { // Redirect to Stripe Checkout window.location.href = data.data.checkoutUrl; } else { throw new Error(data.error || 'Failed to create checkout session'); } } catch (error) { console.error('Donation error:', error); alert('An error occurred while processing your donation. Please try again or contact support.'); submitBtn.disabled = false; submitBtn.textContent = 'Proceed to Secure Payment'; } } /** * Initialize manage subscription functionality */ document.addEventListener('DOMContentLoaded', function() { const manageBtn = document.getElementById('manage-subscription-btn'); const emailInput = document.getElementById('manage-email'); if (manageBtn && emailInput) { manageBtn.addEventListener('click', handleManageSubscription); emailInput.addEventListener('keypress', function(e) { if (e.key === 'Enter') { e.preventDefault(); handleManageSubscription(); } }); } }); /** * Handle manage subscription button click */ async function handleManageSubscription() { const emailInput = document.getElementById('manage-email'); const manageBtn = document.getElementById('manage-subscription-btn'); const errorDiv = document.getElementById('manage-error'); const loadingDiv = document.getElementById('manage-loading'); const email = emailInput.value.trim(); // Validate email if (!email) { showManageError('Please enter your email address.'); emailInput.focus(); return; } if (!isValidEmail(email)) { showManageError('Please enter a valid email address.'); emailInput.focus(); return; } // Hide error, show loading hideManageError(); showManageLoading(); manageBtn.disabled = true; try { const response = await fetch('/api/koha/portal', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email }) }); const data = await response.json(); if (data.success && data.data.url) { // Redirect to Stripe Customer Portal window.location.href = data.data.url; } else if (response.status === 404) { showManageError('No subscription found for this email address. Please check your email and try again.'); manageBtn.disabled = false; hideManageLoading(); } else { throw new Error(data.error || 'Failed to access subscription portal'); } } catch (error) { console.error('Manage subscription error:', error); showManageError('An error occurred. Please try again or contact support@agenticgovernance.digital'); manageBtn.disabled = false; hideManageLoading(); } } /** * Validate email format */ function isValidEmail(email) { const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return re.test(email); } /** * Show error message */ function showManageError(message) { const errorDiv = document.getElementById('manage-error'); if (errorDiv) { errorDiv.textContent = message; errorDiv.classList.remove('hidden'); } } /** * Hide error message */ function hideManageError() { const errorDiv = document.getElementById('manage-error'); if (errorDiv) { errorDiv.classList.add('hidden'); } } /** * Show loading indicator */ function showManageLoading() { const loadingDiv = document.getElementById('manage-loading'); if (loadingDiv) { loadingDiv.classList.remove('hidden'); } } /** * Hide loading indicator */ function hideManageLoading() { const loadingDiv = document.getElementById('manage-loading'); if (loadingDiv) { loadingDiv.classList.add('hidden'); } }