932 lines
43 KiB
HTML
932 lines
43 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Upgrade Plan | Plugin Compass</title>
|
|
<link rel="icon" type="image/png" href="/assets/Plugin.png">
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link
|
|
href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Inter:wght@400;500;600;700&display=swap"
|
|
rel="stylesheet">
|
|
<script>
|
|
// Simple auth check
|
|
fetch('/api/me')
|
|
.then(res => {
|
|
if (res.status === 401) {
|
|
window.location.href = '/login?next=' + encodeURIComponent(window.location.pathname);
|
|
}
|
|
})
|
|
.catch(() => {
|
|
// If api fails, assume logged out or valid error handling elsewhere
|
|
});
|
|
</script>
|
|
<link rel="stylesheet" href="/chat/styles.css">
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
|
<script>
|
|
tailwind.config = {
|
|
theme: {
|
|
extend: {
|
|
fontFamily: {
|
|
sans: ['Inter', 'sans-serif'],
|
|
display: ['Space Grotesk', 'sans-serif'],
|
|
},
|
|
colors: {
|
|
brand: {
|
|
50: '#f0fdf4',
|
|
100: '#dcfce7',
|
|
200: '#bbf7d0',
|
|
300: '#86efac',
|
|
400: '#4ade80',
|
|
500: '#22c55e',
|
|
600: '#16a34a',
|
|
700: '#15803d',
|
|
800: '#166534',
|
|
900: '#008060', // Shopify Green
|
|
950: '#004c3f',
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
<style>
|
|
:root {
|
|
--shopify-green: #008060;
|
|
--shopify-green-dark: #004c3f;
|
|
--shopify-green-light: #e3f5ef;
|
|
--border: #e5e7eb;
|
|
--muted: #6b7280;
|
|
}
|
|
|
|
body {
|
|
background: #fffbeb; /* amber-50 to match select-plan */
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.glass-panel {
|
|
background: rgba(255, 255, 255, 0.7);
|
|
backdrop-filter: blur(20px);
|
|
-webkit-backdrop-filter: blur(20px);
|
|
border: 1px solid rgba(255, 255, 255, 0.5);
|
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.05);
|
|
}
|
|
|
|
.premium-card {
|
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
|
|
.premium-card:hover {
|
|
transform: translateY(-8px);
|
|
box-shadow: 0 25px 50px rgba(0, 128, 96, 0.15);
|
|
}
|
|
|
|
.gradient-text {
|
|
background: linear-gradient(135deg, #008060, #004c3f);
|
|
-webkit-background-clip: text;
|
|
-webkit-text-fill-color: transparent;
|
|
}
|
|
|
|
.check-icon {
|
|
background: transparent;
|
|
color: #16a34a; /* green-600 */
|
|
border-radius: 0;
|
|
padding: 0;
|
|
margin-right: 0.75rem;
|
|
}
|
|
|
|
/* Custom scrollbar */
|
|
::-webkit-scrollbar {
|
|
width: 8px;
|
|
}
|
|
|
|
::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
background: #cbd5e1;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background: #94a3b8;
|
|
}
|
|
|
|
.currency-dropdown.open #currency-btn {
|
|
border-color: #008060;
|
|
box-shadow: 0 0 0 3px rgba(0, 128, 96, 0.1);
|
|
}
|
|
|
|
.currency-option {
|
|
transition: all 0.15s ease;
|
|
}
|
|
|
|
.currency-option:hover {
|
|
background-color: #dcfce7;
|
|
color: #15803d;
|
|
}
|
|
</style>
|
|
|
|
<style>
|
|
.payment-modal-overlay {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(15, 23, 42, 0.7);
|
|
backdrop-filter: blur(8px);
|
|
z-index: 100000;
|
|
opacity: 0;
|
|
visibility: hidden;
|
|
transition: all 0.3s ease;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.payment-modal-overlay.active {
|
|
opacity: 1;
|
|
visibility: visible;
|
|
}
|
|
|
|
.payment-modal {
|
|
background: #fff;
|
|
border-radius: 12px;
|
|
width: 95vw;
|
|
max-width: 1100px;
|
|
max-height: 96vh;
|
|
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
|
transform: scale(0.95) translateY(8px);
|
|
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
height: auto;
|
|
}
|
|
|
|
.payment-modal-overlay.active .payment-modal {
|
|
transform: scale(1) translateY(0);
|
|
}
|
|
|
|
.payment-modal-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 20px 24px;
|
|
border-bottom: 1px solid #e2e8f0;
|
|
background: #fff;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.payment-modal-header h3 { margin: 0; font-size: 18px; font-weight: 700; color: #1e293b; }
|
|
|
|
.payment-modal-close { background: none; border: none; font-size: 28px; cursor: pointer; color: #64748b; }
|
|
|
|
/* Make the frame container flexible and scrollable so iframe content fills the modal and isn't clipped */
|
|
.payment-frame-container { display: flex; flex: 1 1 auto; overflow: auto; min-height: 60vh; background: #f8fafc; height: auto; }
|
|
.payment-frame-container iframe { display: block; width: 100%; height: calc(96vh - 72px); max-height: calc(96vh - 72px); border: none; }
|
|
</style>
|
|
|
|
<!-- Dodo Payments -->
|
|
<script src="https://cdn.jsdelivr.net/npm/dodopayments-checkout@latest/dist/index.js"></script>
|
|
|
|
<!-- PostHog Analytics -->
|
|
<script src="/posthog.js"></script>
|
|
</head>
|
|
|
|
<body class="font-sans text-slate-800 antialiased selection:bg-brand-200 selection:text-brand-900">
|
|
|
|
<!-- Simple App Header -->
|
|
<header class="fixed top-0 w-full z-50 px-6 py-4">
|
|
<div class="max-w-7xl mx-auto flex justify-between items-center glass-panel rounded-2xl px-6 py-3">
|
|
<div class="flex items-center gap-3">
|
|
<img src="/assets/Plugin.png" alt="Plugin Compass" style="width: 40px; height: 40px; border-radius: 10px;">
|
|
<div>
|
|
<h1 class="font-display font-bold text-lg leading-tight text-slate-900">Plugin Compass</h1>
|
|
<p class="text-xs text-slate-500 font-medium uppercase tracking-wide">Upgrade</p>
|
|
</div>
|
|
</div>
|
|
<a href="/apps"
|
|
class="group flex items-center gap-2 text-sm font-semibold text-slate-600 hover:text-brand-900 transition-colors bg-white/50 hover:bg-white px-4 py-2 rounded-xl border border-transparent hover:border-slate-200">
|
|
<i class="fa-solid fa-arrow-left transition-transform group-hover:-translate-x-1"></i>
|
|
Back to Apps
|
|
</a>
|
|
</div>
|
|
</header>
|
|
|
|
<main class="pt-32 pb-20 px-4 sm:px-6">
|
|
<div class="max-w-7xl mx-auto">
|
|
|
|
<div class="text-center max-w-3xl mx-auto mb-16">
|
|
<!-- Currency Dropdown - Centered -->
|
|
<div class="relative currency-dropdown inline-flex" id="currency-dropdown">
|
|
<button id="currency-btn" class="bg-white border border-gray-200 rounded-full px-4 py-2 pr-10 text-sm font-medium text-gray-700 hover:border-green-700 hover:bg-gray-50 transition-all cursor-pointer flex items-center gap-2">
|
|
<span id="currency-flag">🇺🇸</span>
|
|
<span id="currency-code">USD</span>
|
|
<i class="fa-solid fa-chevron-down absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 pointer-events-none text-xs transition-transform" id="currency-chevron"></i>
|
|
</button>
|
|
<div id="currency-options" class="hidden absolute top-full left-1/2 -translate-x-1/2 mt-2 w-full bg-white border border-gray-200 rounded-xl shadow-lg z-50 overflow-hidden min-w-[120px]">
|
|
<button class="currency-option w-full px-4 py-2 text-sm text-gray-700 hover:bg-green-50 hover:text-gray-900 font-medium text-left flex items-center gap-2 justify-center" data-value="USD">
|
|
🇺🇸 <span class="ml-1">USD</span>
|
|
</button>
|
|
<button class="currency-option w-full px-4 py-2 text-sm text-gray-700 hover:bg-green-50 hover:text-gray-900 font-medium text-left flex items-center gap-2 justify-center" data-value="GBP">
|
|
🇬🇧 <span class="ml-1">GBP</span>
|
|
</button>
|
|
<button class="currency-option w-full px-4 py-2 text-sm text-gray-700 hover:bg-green-50 hover:text-gray-900 font-medium text-left flex items-center gap-2 justify-center" data-value="EUR">
|
|
🇪🇺 <span class="ml-1">EUR</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Billing Toggle -->
|
|
<div
|
|
class="mt-8 inline-flex items-center bg-white p-1.5 rounded-full border border-slate-200 shadow-sm">
|
|
<button id="monthly-toggle"
|
|
class="px-6 py-2.5 rounded-full text-sm font-bold bg-brand-900 text-white shadow-md transition-all">
|
|
Monthly
|
|
</button>
|
|
<button id="yearly-toggle"
|
|
class="px-6 py-2.5 rounded-full text-sm font-bold text-slate-500 hover:text-slate-900 transition-all flex items-center gap-2">
|
|
Yearly <span
|
|
class="bg-amber-100 text-amber-700 text-[10px] px-2 py-0.5 rounded-full uppercase tracking-wide">-20%</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pricing Grid -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 w-full max-w-5xl mx-auto justify-center">
|
|
|
|
<!-- Hobby (Free) plan removed from upgrade flow -->
|
|
<!-- The free Hobby plan option is intentionally not selectable on this page. Users can still select Hobby from account settings. -->
|
|
|
|
<!-- Starter Plan -->
|
|
<div class="premium-card bg-white rounded-[2rem] p-8 border border-slate-200 shadow-xl shadow-slate-200/50 flex flex-col relative overflow-hidden"
|
|
data-plan="starter">
|
|
<div class="absolute top-0 left-0 w-full h-1 bg-green-700"></div>
|
|
<div class="mb-8">
|
|
<h3 class="font-display text-2xl font-bold text-slate-900">Starter</h3>
|
|
<p class="text-slate-500 text-sm mt-2">Great for small business needs.</p>
|
|
</div>
|
|
<div class="mb-8 flex items-baseline gap-1">
|
|
<span id="starter-price" class="text-4xl font-bold text-slate-900">$7.50</span>
|
|
<span class="text-slate-500 font-medium price-duration">/mo</span>
|
|
</div>
|
|
<ul class="space-y-4 mb-8 flex-grow">
|
|
<li class="flex items-start gap-3 text-sm text-slate-700">
|
|
<i class="fa-solid fa-check text-green-600 mr-3 mt-0.5"></i>
|
|
<span>Up to 10 active apps</span>
|
|
</li>
|
|
<li class="flex items-start gap-3 text-sm text-slate-700">
|
|
<i class="fa-solid fa-check text-green-600 mr-3 mt-0.5"></i>
|
|
<span>
|
|
<a href="/credits" target="_blank" class="inline-flex items-center text-green-700 hover:text-green-600 mr-1" title="Learn more about AI credits">
|
|
<i class="fa-solid fa-circle-question"></i>
|
|
</a>
|
|
100,000 monthly AI credits
|
|
</span>
|
|
</li>
|
|
<li class="flex items-start gap-3 text-sm text-slate-700">
|
|
<i class="fa-solid fa-check text-green-600 mr-3 mt-0.5"></i>
|
|
<span>Access to templates</span>
|
|
</li>
|
|
<li class="flex items-start gap-3 text-sm text-slate-700">
|
|
<i class="fa-solid fa-check text-green-600 mr-3 mt-0.5"></i>
|
|
<span>Faster queue than Hobby</span>
|
|
</li>
|
|
</ul>
|
|
<button
|
|
class="upgrade-btn block w-full py-4 px-6 rounded-xl border-2 border-green-700 text-green-700 font-bold hover:bg-green-50 transition-colors text-center"
|
|
data-plan="starter">
|
|
Choose Starter
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Pro Plan -->
|
|
<div class="premium-card bg-white rounded-[2rem] p-8 border-2 border-green-700 shadow-2xl shadow-green-900/10 flex flex-col relative z-10"
|
|
data-plan="professional">
|
|
<div class="absolute top-0 inset-x-0 h-1.5 bg-gradient-to-r from-green-700 to-green-600"></div>
|
|
<div class="absolute top-4 right-4">
|
|
<span
|
|
class="inline-flex items-center gap-1.5 px-3 py-1 bg-green-50 text-green-700 text-xs font-bold uppercase tracking-wider rounded-full border border-green-100">
|
|
<i class="fa-solid fa-star text-[10px]"></i> Popular
|
|
</span>
|
|
</div>
|
|
<div class="mb-8 mt-2">
|
|
<h3 class="font-display text-2xl font-bold text-slate-900">Professional</h3>
|
|
<p class="text-slate-500 text-sm mt-2">For serious builders shipping plugins.</p>
|
|
</div>
|
|
<div class="mb-8 flex items-baseline gap-1">
|
|
<span id="pro-price" class="text-5xl font-bold text-slate-900 tracking-tight">$25</span>
|
|
<span class="text-slate-500 font-medium price-duration">/mo</span>
|
|
</div>
|
|
<ul class="space-y-4 mb-8 flex-grow">
|
|
<li class="flex items-start gap-3 text-sm text-slate-700 font-medium">
|
|
<div class="bg-brand-100 text-brand-700 rounded-full p-1"><i
|
|
class="fa-solid fa-check text-xs"></i></div>
|
|
<span>Up to 20 active apps</span>
|
|
</li>
|
|
<li class="flex items-start gap-3 text-sm text-slate-700 font-medium">
|
|
<div class="bg-brand-100 text-brand-700 rounded-full p-1"><i
|
|
class="fa-solid fa-check text-xs"></i></div>
|
|
<span>Choice of AI models</span>
|
|
</li>
|
|
<li class="flex items-start gap-3 text-sm text-slate-700 font-medium">
|
|
<div class="bg-brand-100 text-brand-700 rounded-full p-1"><i
|
|
class="fa-solid fa-check text-xs"></i></div>
|
|
<span>
|
|
<a href="/credits" target="_blank" class="inline-flex items-center text-green-700 hover:text-green-600 mr-1" title="Learn more about AI credits">
|
|
<i class="fa-solid fa-circle-question"></i>
|
|
</a>
|
|
5,000,000 monthly AI credits
|
|
</span>
|
|
</li>
|
|
<li class="flex items-start gap-3 text-sm text-slate-700 font-medium">
|
|
<div class="bg-brand-100 text-brand-700 rounded-full p-1"><i
|
|
class="fa-solid fa-check text-xs"></i></div>
|
|
<span>Access to templates</span>
|
|
</li>
|
|
<li class="flex items-start gap-3 text-sm text-slate-700 font-medium">
|
|
<div class="bg-brand-100 text-brand-700 rounded-full p-1"><i
|
|
class="fa-solid fa-check text-xs"></i></div>
|
|
<span>Priority queue (ahead of Hobby)</span>
|
|
</li>
|
|
</ul>
|
|
<button
|
|
class="upgrade-btn relative w-full py-4 px-6 rounded-xl bg-green-700 text-white font-bold hover:bg-green-600 transition-all shadow-lg shadow-green-700/25 hover:shadow-green-700/40 text-center group overflow-hidden"
|
|
data-plan="professional">
|
|
<span class="relative z-10 flex items-center justify-center gap-2">
|
|
Upgrade to Pro <i
|
|
class="fa-solid fa-arrow-right transition-transform group-hover:translate-x-1"></i>
|
|
</span>
|
|
<div
|
|
class="absolute inset-0 bg-white/10 translate-y-full group-hover:translate-y-0 transition-transform duration-300">
|
|
</div>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Enterprise Plan -->
|
|
<div class="premium-card bg-white rounded-[2rem] p-8 border border-slate-200 shadow-xl shadow-slate-200/50 flex flex-col relative overflow-hidden"
|
|
data-plan="enterprise">
|
|
<div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-green-700 to-green-600"></div>
|
|
<div class="mb-8">
|
|
<h3 class="font-display text-2xl font-bold text-slate-900">Enterprise</h3>
|
|
<p class="text-slate-500 text-sm mt-2">Maximum power and volume.</p>
|
|
</div>
|
|
<div class="mb-8 flex items-baseline gap-1">
|
|
<span id="enterprise-price" class="text-4xl font-bold text-slate-900">$75</span>
|
|
<span class="text-slate-500 font-medium price-duration">/mo</span>
|
|
</div>
|
|
<ul class="space-y-4 mb-8 flex-grow">
|
|
<li class="flex items-start gap-3 text-sm text-slate-700">
|
|
<i class="fa-solid fa-check text-green-600 mr-3 mt-0.5"></i>
|
|
<span>Unlimited active apps</span>
|
|
</li>
|
|
<li class="flex items-start gap-3 text-sm text-slate-700">
|
|
<i class="fa-solid fa-check text-green-600 mr-3 mt-0.5"></i>
|
|
<span>Fastest queue priority</span>
|
|
</li>
|
|
<li class="flex items-start gap-3 text-sm text-slate-700">
|
|
<i class="fa-solid fa-check text-green-600 mr-3 mt-0.5"></i>
|
|
<span>
|
|
<a href="/credits" target="_blank" class="inline-flex items-center text-green-700 hover:text-green-600 mr-1" title="Learn more about AI credits">
|
|
<i class="fa-solid fa-circle-question"></i>
|
|
</a>
|
|
50,000,000 monthly AI credits
|
|
</span>
|
|
</li>
|
|
<li class="flex items-start gap-3 text-sm text-slate-700">
|
|
<i class="fa-solid fa-check text-green-600 mr-3 mt-0.5"></i>
|
|
<span>Access to templates</span>
|
|
</li>
|
|
</ul>
|
|
<button
|
|
class="upgrade-btn block w-full py-4 px-6 rounded-xl border-2 border-green-700 text-green-700 font-bold hover:bg-green-50 transition-colors text-center"
|
|
data-plan="enterprise">
|
|
Choose Enterprise
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<!-- Payment Modal (uses same embedding method as settings/select-plan) -->
|
|
<div class="payment-modal-overlay" id="payment-modal">
|
|
<div class="payment-modal">
|
|
<div class="payment-modal-header">
|
|
<h3>Secure Checkout</h3>
|
|
<button type="button" class="payment-modal-close" id="payment-modal-close">×</button>
|
|
</div>
|
|
<div id="payment-frame-container" class="payment-frame-container"></div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<script>
|
|
// Track upgrade popup source
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const upgradeSource = urlParams.get('source') || 'unknown';
|
|
let isProcessing = false;
|
|
let checkoutModal = null;
|
|
|
|
async function trackUpgradePopupSource() {
|
|
try {
|
|
const authHeaders = getAuthHeaders();
|
|
await fetch('/api/upgrade-popup-tracking', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...authHeaders
|
|
},
|
|
body: JSON.stringify({ source: upgradeSource }),
|
|
credentials: 'include',
|
|
});
|
|
} catch (err) {
|
|
console.error('Failed to track upgrade popup source:', err);
|
|
}
|
|
}
|
|
|
|
// 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 {};
|
|
}
|
|
|
|
// Track source when page loads
|
|
trackUpgradePopupSource();
|
|
|
|
// Fetch current user plan and update UI accordingly
|
|
async function fetchUserPlan() {
|
|
try {
|
|
const authHeaders = getAuthHeaders();
|
|
const response = await fetch('/api/me', {
|
|
method: 'GET',
|
|
headers: { ...authHeaders },
|
|
credentials: 'include',
|
|
});
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
const currentPlan = (data.account?.plan || 'hobby').toLowerCase();
|
|
|
|
// If user is already on enterprise, hide upgrade buttons and show message
|
|
if (currentPlan === 'enterprise') {
|
|
const enterpriseCard = document.querySelector('[data-plan="enterprise"]');
|
|
if (enterpriseCard) {
|
|
const enterpriseBtn = enterpriseCard.querySelector('.upgrade-btn');
|
|
if (enterpriseBtn) {
|
|
enterpriseBtn.textContent = 'Current Plan';
|
|
enterpriseBtn.classList.add('bg-brand-900', 'text-white', 'border-brand-900');
|
|
enterpriseBtn.classList.remove('hover:bg-slate-50', 'hover:border-slate-300');
|
|
enterpriseBtn.disabled = true;
|
|
}
|
|
}
|
|
|
|
// Update other plan buttons to show downgrade option only
|
|
document.querySelectorAll('.upgrade-btn').forEach(btn => {
|
|
const plan = btn.getAttribute('data-plan');
|
|
if (plan !== 'enterprise' && plan !== 'hobby') {
|
|
btn.textContent = 'Downgrade to ' + plan.charAt(0).toUpperCase() + plan.slice(1);
|
|
}
|
|
});
|
|
|
|
// Show notification at top of page
|
|
const heroSection = document.querySelector('main .max-w-7xl .text-center');
|
|
if (heroSection) {
|
|
const notification = document.createElement('div');
|
|
notification.className = 'bg-brand-50 border border-brand-200 text-brand-800 px-4 py-2 rounded-lg text-sm font-medium mb-6';
|
|
notification.textContent = 'You are already on the Enterprise plan with full access to all features.';
|
|
heroSection.insertBefore(notification, heroSection.firstChild);
|
|
}
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.error('Failed to fetch user plan:', err);
|
|
}
|
|
}
|
|
|
|
// Fetch user plan when page loads
|
|
fetchUserPlan();
|
|
|
|
// Currency configuration
|
|
const currencyConfig = {
|
|
USD: { rate: 1, symbol: '$', flag: '🇺🇸' },
|
|
GBP: { rate: 0.79, symbol: '£', flag: '🇬🇧' },
|
|
EUR: { rate: 1, symbol: '€', flag: '🇪🇺' }
|
|
};
|
|
|
|
const baseMonthlyPrices = {
|
|
hobby: 0,
|
|
starter: 7.5,
|
|
pro: 25,
|
|
enterprise: 75
|
|
};
|
|
|
|
let currentCurrency = 'USD';
|
|
let currentBillingPeriod = 'monthly';
|
|
|
|
function updateDisplay() {
|
|
const config = currencyConfig[currentCurrency];
|
|
const isYearly = currentBillingPeriod === 'yearly';
|
|
|
|
const formatPrice = (basePrice) => {
|
|
if (basePrice === 0) return config.symbol + '0';
|
|
|
|
if (currentCurrency === 'GBP') {
|
|
if (basePrice === 25) {
|
|
return isYearly ? '£200' : '£20';
|
|
}
|
|
if (basePrice === 75) {
|
|
return isYearly ? '£600' : '£60';
|
|
}
|
|
}
|
|
|
|
if (currentCurrency === 'EUR') {
|
|
if (basePrice === 7.5) {
|
|
return isYearly ? '€75' : '€7.50';
|
|
}
|
|
if (basePrice === 25) {
|
|
return isYearly ? '€250' : '€25';
|
|
}
|
|
if (basePrice === 75) {
|
|
return isYearly ? '€750' : '€75';
|
|
}
|
|
}
|
|
|
|
let finalPrice = basePrice * config.rate;
|
|
|
|
if (basePrice === 7.5) {
|
|
finalPrice = (isYearly ? 50 : 5);
|
|
} else if (basePrice === 25) {
|
|
finalPrice = (isYearly ? 200 : 20);
|
|
} else if (basePrice === 75) {
|
|
finalPrice = (isYearly ? 600 : 60);
|
|
} else {
|
|
finalPrice = Math.round(finalPrice * 10) / 10;
|
|
if (finalPrice % 1 === 0) finalPrice = Math.round(finalPrice);
|
|
}
|
|
|
|
if (currentCurrency === 'USD' && basePrice === 7.5 && !isYearly) return '$7.50';
|
|
|
|
return config.symbol + finalPrice;
|
|
};
|
|
|
|
const hobbyPrice = document.getElementById('hobby-price');
|
|
const starterPrice = document.getElementById('starter-price');
|
|
const proPrice = document.getElementById('pro-price');
|
|
const entPrice = document.getElementById('enterprise-price');
|
|
|
|
if (hobbyPrice) hobbyPrice.textContent = formatPrice(baseMonthlyPrices.hobby);
|
|
if (starterPrice) starterPrice.textContent = formatPrice(baseMonthlyPrices.starter);
|
|
if (proPrice) proPrice.textContent = formatPrice(baseMonthlyPrices.pro);
|
|
if (entPrice) entPrice.textContent = formatPrice(baseMonthlyPrices.enterprise);
|
|
|
|
document.querySelectorAll('.price-duration').forEach(el => {
|
|
el.textContent = isYearly ? '/mo (billed yearly)' : '/mo';
|
|
});
|
|
|
|
// Update toggle button styles
|
|
const monthlyBtn = document.getElementById('monthly-toggle');
|
|
const yearlyBtn = document.getElementById('yearly-toggle');
|
|
if (monthlyBtn && yearlyBtn) {
|
|
if (isYearly) {
|
|
yearlyBtn.classList.add('bg-brand-900', 'text-white');
|
|
yearlyBtn.classList.remove('text-slate-500', 'hover:text-slate-900');
|
|
monthlyBtn.classList.remove('bg-brand-900', 'text-white');
|
|
monthlyBtn.classList.add('text-slate-500', 'hover:text-slate-900');
|
|
} else {
|
|
monthlyBtn.classList.add('bg-brand-900', 'text-white');
|
|
monthlyBtn.classList.remove('text-slate-500', 'hover:text-slate-900');
|
|
yearlyBtn.classList.remove('bg-brand-900', 'text-white');
|
|
yearlyBtn.classList.add('text-slate-500', 'hover:text-slate-900');
|
|
}
|
|
}
|
|
|
|
// Update currency dropdown button
|
|
const flagEl = document.getElementById('currency-flag');
|
|
const codeEl = document.getElementById('currency-code');
|
|
if (flagEl) flagEl.textContent = config.flag;
|
|
if (codeEl) codeEl.textContent = currentCurrency;
|
|
}
|
|
|
|
async function autoDetectCurrency() {
|
|
try {
|
|
const response = await fetch('https://ipapi.co/json/');
|
|
const data = await response.json();
|
|
if (data.currency && currencyConfig[data.currency]) {
|
|
currentCurrency = data.currency;
|
|
updateDisplay();
|
|
}
|
|
} catch (err) {
|
|
console.log('Currency detection failed, defaulting to USD');
|
|
}
|
|
}
|
|
|
|
// Monthly/Yearly Listeners
|
|
document.getElementById('monthly-toggle')?.addEventListener('click', () => {
|
|
currentBillingPeriod = 'monthly';
|
|
updateDisplay();
|
|
});
|
|
|
|
document.getElementById('yearly-toggle')?.addEventListener('click', () => {
|
|
currentBillingPeriod = 'yearly';
|
|
updateDisplay();
|
|
});
|
|
|
|
// Currency Dropdown Listeners
|
|
const currencyBtn = document.getElementById('currency-btn');
|
|
const currencyOptions = document.getElementById('currency-options');
|
|
const currencyChevron = document.getElementById('currency-chevron');
|
|
const currencyDropdown = document.getElementById('currency-dropdown');
|
|
|
|
currencyBtn?.addEventListener('click', (e) => {
|
|
e.stopPropagation();
|
|
const isOpen = !currencyOptions.classList.contains('hidden');
|
|
if (isOpen) {
|
|
currencyOptions.classList.add('hidden');
|
|
currencyChevron.style.transform = 'translateY(-50%) rotate(0deg)';
|
|
currencyDropdown.classList.remove('open');
|
|
} else {
|
|
currencyOptions.classList.remove('hidden');
|
|
currencyChevron.style.transform = 'translateY(-50%) rotate(180deg)';
|
|
currencyDropdown.classList.add('open');
|
|
}
|
|
});
|
|
|
|
document.querySelectorAll('.currency-option').forEach(option => {
|
|
option.addEventListener('click', (e) => {
|
|
currentCurrency = e.currentTarget.getAttribute('data-value');
|
|
updateDisplay();
|
|
currencyOptions.classList.add('hidden');
|
|
currencyChevron.style.transform = 'translateY(-50%) rotate(0deg)';
|
|
currencyDropdown.classList.remove('open');
|
|
});
|
|
});
|
|
|
|
document.addEventListener('click', () => {
|
|
if (currencyOptions) {
|
|
currencyOptions.classList.add('hidden');
|
|
currencyChevron.style.transform = 'translateY(-50%) rotate(0deg)';
|
|
currencyDropdown.classList.remove('open');
|
|
}
|
|
});
|
|
|
|
// Initialize display and detection
|
|
updateDisplay();
|
|
autoDetectCurrency();
|
|
|
|
// Simple Billing Toggle Logic (legacy - kept for reference, now handled by updateDisplay)
|
|
const monthlyBtn = document.getElementById('monthly-toggle');
|
|
const yearlyBtn = document.getElementById('yearly-toggle');
|
|
const proPrice = document.getElementById('pro-price');
|
|
const entPrice = document.getElementById('enterprise-price');
|
|
const durations = document.querySelectorAll('.price-duration');
|
|
|
|
// Prices
|
|
const prices = {
|
|
monthly: { starter: 7.5, pro: 25, ent: 75 },
|
|
yearly: { starter: 6, pro: 20, ent: 60 } // calculated as monthly cost when paying yearly
|
|
};
|
|
|
|
// Handle plan selection (Upgrade Logic with Dodo Payments)
|
|
document.querySelectorAll('.premium-card').forEach(card => {
|
|
card.addEventListener('click', async (e) => {
|
|
// Let links within the card work normally
|
|
if (e.target.closest('a')) return;
|
|
|
|
e.preventDefault();
|
|
if (isProcessing) return;
|
|
|
|
const plan = card.getAttribute('data-plan');
|
|
if (!plan) return;
|
|
|
|
isProcessing = true;
|
|
const btn = card.querySelector('.upgrade-btn');
|
|
const originalText = btn ? btn.innerHTML : 'Processing...';
|
|
|
|
// Disable button
|
|
if (btn) {
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<i class="fa-solid fa-circle-notch fa-spin"></i> Processing...';
|
|
btn.classList.add('opacity-75', 'cursor-not-allowed');
|
|
}
|
|
|
|
try {
|
|
const authHeaders = getAuthHeaders();
|
|
|
|
// Hobby plan is free - use select-plan endpoint
|
|
if (plan === 'hobby') {
|
|
const response = await fetch('/api/select-plan', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...authHeaders
|
|
},
|
|
body: JSON.stringify({ plan }),
|
|
credentials: 'include',
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (response.ok && data.ok) {
|
|
window.location.href = '/settings?msg=plan_updated';
|
|
} else {
|
|
throw new Error(data.error || 'Failed to update plan. Please try again.');
|
|
}
|
|
} else {
|
|
// Paid plans use Dodo Payments subscription checkout
|
|
const response = await fetch('/api/subscription/checkout', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...authHeaders
|
|
},
|
|
body: JSON.stringify({
|
|
plan: plan,
|
|
billingCycle: currentBillingPeriod,
|
|
currency: currentCurrency.toLowerCase(),
|
|
inline: true,
|
|
previousPlan: (typeof window.currentPlan !== 'undefined' ? window.currentPlan : '')
|
|
}),
|
|
credentials: 'include',
|
|
});
|
|
|
|
const data = await response.json();
|
|
|
|
if (response.ok && data.sessionId) {
|
|
// Prefer inlineCheckoutUrl when available (settings page embeds an iframe)
|
|
const checkoutUrl = data.inlineCheckoutUrl || data.checkoutUrl;
|
|
if (checkoutUrl) {
|
|
// Use same embedding strategy as settings page: open payment modal with iframe
|
|
await openInlineCheckout(checkoutUrl, data.sessionId, plan);
|
|
} else {
|
|
throw new Error('No checkout URL provided');
|
|
}
|
|
} else {
|
|
throw new Error(data.error || 'Failed to start checkout');
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Error updating plan:', error);
|
|
alert(error.message || 'Failed to update plan. Please try again.');
|
|
if (btn) {
|
|
btn.disabled = false;
|
|
btn.innerHTML = originalText;
|
|
btn.classList.remove('opacity-75', 'cursor-not-allowed');
|
|
}
|
|
isProcessing = false;
|
|
}
|
|
});
|
|
});
|
|
|
|
async function ensureDodoPaymentsLoaded() {
|
|
return new Promise((resolve) => {
|
|
if (typeof DodoPaymentsCheckout !== 'undefined' && DodoPaymentsCheckout.DodoPayments && DodoPaymentsCheckout.DodoPayments.Checkout) {
|
|
resolve();
|
|
return;
|
|
}
|
|
|
|
const existingScript = document.querySelector('script[src="https://cdn.jsdelivr.net/npm/dodopayments-checkout@latest/dist/index.js"]');
|
|
if (existingScript) {
|
|
const checkLoaded = () => {
|
|
if (typeof DodoPaymentsCheckout !== 'undefined' && DodoPaymentsCheckout.DodoPayments && DodoPaymentsCheckout.DodoPayments.Checkout) {
|
|
resolve();
|
|
} else {
|
|
setTimeout(checkLoaded, 100);
|
|
}
|
|
};
|
|
checkLoaded();
|
|
return;
|
|
}
|
|
|
|
const script = document.createElement('script');
|
|
script.src = 'https://cdn.jsdelivr.net/npm/dodopayments-checkout@latest/dist/index.js';
|
|
script.async = true;
|
|
|
|
const timeout = setTimeout(() => {
|
|
console.warn('DodoPayments script loading timed out, will use iframe fallback');
|
|
resolve();
|
|
}, 5000);
|
|
|
|
script.onload = () => {
|
|
clearTimeout(timeout);
|
|
resolve();
|
|
};
|
|
script.onerror = () => {
|
|
clearTimeout(timeout);
|
|
console.error('Failed to load DodoPayments script, will use iframe fallback');
|
|
resolve();
|
|
};
|
|
|
|
document.head.appendChild(script);
|
|
});
|
|
}
|
|
|
|
async function openInlineCheckout(checkoutUrl, sessionId, plan) {
|
|
// Embed the Dodo checkout in the shared payment modal (same as settings/select-plan)
|
|
const modal = document.getElementById('payment-modal');
|
|
const container = document.getElementById('payment-frame-container');
|
|
|
|
if (!modal || !container) {
|
|
// Fallback: open checkout in a new window
|
|
window.open(checkoutUrl, 'dodo-payment-checkout', 'width=600,height=700');
|
|
return;
|
|
}
|
|
|
|
// Show iframe in modal
|
|
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: 'same-origin'
|
|
});
|
|
|
|
if (resp.ok) {
|
|
const data = await resp.json();
|
|
if (data.ok) {
|
|
clearInterval(pollTimer);
|
|
// Close modal and redirect to settings so account data is refreshed
|
|
modal.classList.remove('active');
|
|
container.innerHTML = '';
|
|
window.location.href = '/settings?msg=plan_updated';
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (pollCount >= maxPolls) {
|
|
clearInterval(pollTimer);
|
|
modal.classList.remove('active');
|
|
container.innerHTML = '';
|
|
alert('Payment confirmation timeout. Please check your account status.');
|
|
isProcessing = false;
|
|
}
|
|
} catch (error) {
|
|
console.error('Error checking payment status:', error);
|
|
}
|
|
};
|
|
|
|
const pollTimer = setInterval(checkPaymentStatus, pollInterval);
|
|
|
|
// Close handlers
|
|
const closeBtn = document.getElementById('payment-modal-close');
|
|
if (closeBtn) {
|
|
closeBtn.onclick = () => {
|
|
clearInterval(pollTimer);
|
|
modal.classList.remove('active');
|
|
container.innerHTML = '';
|
|
isProcessing = false;
|
|
};
|
|
}
|
|
|
|
modal.onclick = (e) => {
|
|
if (e.target === modal) {
|
|
clearInterval(pollTimer);
|
|
modal.classList.remove('active');
|
|
container.innerHTML = '';
|
|
isProcessing = false;
|
|
}
|
|
};
|
|
|
|
const handleEsc = (e) => {
|
|
if (e.key === 'Escape') {
|
|
clearInterval(pollTimer);
|
|
modal.classList.remove('active');
|
|
container.innerHTML = '';
|
|
isProcessing = false;
|
|
}
|
|
};
|
|
document.addEventListener('keydown', handleEsc);
|
|
}
|
|
</script>
|
|
<footer
|
|
style="margin-top: 80px; padding: 40px 24px; border-top: 1px solid #e1e3e5; text-align: center; color: #6d7175; font-size: 0.875rem;">
|
|
<p>© 2026 Plugin Compass. All rights reserved.</p>
|
|
<div style="margin-top: 12px; display: flex; justify-content: center; gap: 16px;">
|
|
<a href="/terms" style="color: #008060; text-decoration: none;">Terms</a>
|
|
<a href="/privacy" style="color: #008060; text-decoration: none;">Privacy</a>
|
|
<a href="/contact" style="color: #008060; text-decoration: none;">Contact Us</a>
|
|
</div>
|
|
</footer>
|
|
</body>
|
|
|
|
</html>
|