diff --git a/public/app.js b/public/app.js index 6a04382..6e99687 100644 --- a/public/app.js +++ b/public/app.js @@ -1,28 +1,26 @@ +function showState(state) { + ['signupInitial', 'signupLoading', 'signupResult'].forEach(function(id) { + document.getElementById(id).classList.remove('active'); + }); + document.getElementById(state).classList.add('active'); +} + function openSignup() { document.getElementById('signupModal').classList.add('active'); - document.getElementById('signupForm').style.display = 'block'; - document.getElementById('keyResult').style.display = 'none'; + showState('signupInitial'); document.getElementById('signupError').style.display = 'none'; - document.getElementById('signupBtn').textContent = 'Get API Key'; - document.getElementById('signupBtn').disabled = false; } function closeSignup() { document.getElementById('signupModal').classList.remove('active'); } -// Close on overlay click -document.getElementById('signupModal').addEventListener('click', function(e) { - if (e.target === this) closeSignup(); -}); - async function submitSignup() { var errEl = document.getElementById('signupError'); var btn = document.getElementById('signupBtn'); - - btn.textContent = 'Creating...'; - btn.disabled = true; errEl.style.display = 'none'; + btn.disabled = true; + showState('signupLoading'); try { var res = await fetch('/v1/signup/free', { @@ -30,43 +28,32 @@ async function submitSignup() { headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}) }); - - if (res.status === 429) { - errEl.textContent = 'Too many requests. Please try again in a few minutes.'; - errEl.style.display = 'block'; - btn.textContent = 'Get API Key'; - btn.disabled = false; - return; - } - var data = await res.json(); if (!res.ok) { + showState('signupInitial'); errEl.textContent = data.error || 'Something went wrong. Please try again.'; errEl.style.display = 'block'; - btn.textContent = 'Get API Key'; btn.disabled = false; return; } - // Show key - document.getElementById('signupForm').style.display = 'none'; - document.getElementById('keyResult').style.display = 'block'; - document.getElementById('apiKeyDisplay').textContent = data.apiKey; + document.getElementById('apiKeyText').textContent = data.apiKey; + showState('signupResult'); } catch (err) { + showState('signupInitial'); errEl.textContent = 'Network error. Please try again.'; errEl.style.display = 'block'; - btn.textContent = 'Get API Key'; btn.disabled = false; } } function copyKey() { - var key = document.getElementById('apiKeyDisplay').textContent; - var hint = document.querySelector('.copy-hint'); + var key = document.getElementById('apiKeyText').textContent; + var btn = document.getElementById('copyBtn'); function showCopied() { - hint.textContent = '✓ Copied!'; - setTimeout(function() { hint.textContent = 'Click to copy'; }, 2000); + btn.textContent = '✓ Copied!'; + setTimeout(function() { btn.textContent = 'Copy'; }, 2000); } try { navigator.clipboard.writeText(key).then(showCopied).catch(function() { @@ -87,18 +74,28 @@ async function checkout() { var res = await fetch('/v1/billing/checkout', { method: 'POST' }); var data = await res.json(); if (data.url) window.location.href = data.url; - else alert('Something went wrong. Please try again.'); + else alert('Checkout is not available yet. Please try again later.'); } catch (err) { alert('Something went wrong. Please try again.'); } } -// BUG-005 fix: attach all click handlers via JS instead of inline onclick document.addEventListener('DOMContentLoaded', function() { document.getElementById('btn-signup').addEventListener('click', openSignup); document.getElementById('btn-signup-2').addEventListener('click', openSignup); document.getElementById('btn-checkout').addEventListener('click', checkout); document.getElementById('btn-close-signup').addEventListener('click', closeSignup); document.getElementById('signupBtn').addEventListener('click', submitSignup); - document.getElementById('apiKeyDisplay').addEventListener('click', copyKey); + document.getElementById('copyBtn').addEventListener('click', copyKey); + document.getElementById('signupModal').addEventListener('click', function(e) { + if (e.target === this) closeSignup(); + }); + // Smooth scroll for nav links + document.querySelectorAll('a[href^="#"]').forEach(function(a) { + a.addEventListener('click', function(e) { + e.preventDefault(); + var el = document.querySelector(this.getAttribute('href')); + if (el) el.scrollIntoView({ behavior: 'smooth' }); + }); + }); }); diff --git a/public/index.html b/public/index.html index bc3c9e3..81b72a2 100644 --- a/public/index.html +++ b/public/index.html @@ -5,207 +5,273 @@
One API call. Beautiful PDFs. Built-in invoice templates. No headless browser setup, no dependencies, no hassle.
+Convert HTML, Markdown, or URLs to pixel-perfect PDFs. Built-in templates for invoices & receipts. No headless browser headaches.
A complete PDF generation API. No SDKs, no dependencies, no setup.
Sub-second PDF generation. Persistent browser pool means no cold starts.
+Persistent browser pool — no cold starts. Your PDFs are ready before your spinner shows.
Full CSS support. Custom fonts, colors, layouts. Your PDFs, your brand.
+Full CSS support including flexbox, grid, and custom fonts. Your brand, your PDFs.
Built-in invoice and receipt templates. Pass data, get PDF. No HTML needed.
+Invoice and receipt templates out of the box. Pass JSON data, get beautiful PDFs.
REST API with JSON in, PDF out. Works with any language. No SDKs required.
+REST API. JSON in, PDF out. Works with curl, Python, Node, Go — anything with HTTP.
A4, Letter, custom sizes. Portrait or landscape. Configurable margins.
+A4, Letter, custom sizes. Portrait or landscape. Headers, footers, and margins.
Your data is never stored. PDFs are generated and streamed — nothing hits disk.
+HTTPS only. Rate limiting. No data stored. PDFs stream directly — nothing touches disk.
No per-page fees. No hidden limits. Pay for what you use.
+Start free. Upgrade when you're ready. No surprise charges.