diff --git a/public/app.js b/public/app.js new file mode 100644 index 0000000..4040a43 --- /dev/null +++ b/public/app.js @@ -0,0 +1,93 @@ +function openSignup() { + document.getElementById('signupModal').classList.add('active'); + document.getElementById('signupForm').style.display = 'block'; + document.getElementById('keyResult').style.display = 'none'; + document.getElementById('signupEmail').value = ''; + document.getElementById('signupError').style.display = 'none'; + setTimeout(function() { document.getElementById('signupEmail').focus(); }, 100); +} + +function closeSignup() { + document.getElementById('signupModal').classList.remove('active'); +} + +// Close on overlay click +document.getElementById('signupModal').addEventListener('click', function(e) { + if (e.target === this) closeSignup(); +}); + +// Submit on Enter +document.getElementById('signupEmail').addEventListener('keydown', function(e) { + if (e.key === 'Enter') submitSignup(); +}); + +async function submitSignup() { + var email = document.getElementById('signupEmail').value.trim(); + var errEl = document.getElementById('signupError'); + var btn = document.getElementById('signupBtn'); + + if (!email) { + errEl.textContent = 'Please enter your email.'; + errEl.style.display = 'block'; + return; + } + + btn.textContent = 'Creating...'; + btn.disabled = true; + + try { + var res = await fetch('/v1/signup/free', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email: email }) + }); + var data = await res.json(); + + if (!res.ok) { + errEl.textContent = data.error || 'Something went wrong.'; + 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; + } catch (err) { + 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; + navigator.clipboard.writeText(key).then(function() { + var btn = document.getElementById('apiKeyDisplay'); var origText = btn.textContent; btn.textContent = 'Copied!'; document.querySelector('.copy-hint').textContent = '✓ Copied!'; + setTimeout(function() { btn.textContent = origText; document.querySelector('.copy-hint').textContent = 'Click to copy'; + }); +} + +async function checkout() { + try { + 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.'); + } 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); +}); diff --git a/public/index.html b/public/index.html index b7fd8fc..b93fb9a 100644 --- a/public/index.html +++ b/public/index.html @@ -91,7 +91,7 @@ footer { padding: 40px 0; text-align: center; color: var(--muted); font-size: 0.

HTML & Markdown to PDF

One API call. Beautiful PDFs. Built-in invoice templates. No headless browser setup, no dependencies, no hassle.

- + View Docs
@@ -193,7 +193,7 @@ footer { padding: 40px 0; text-align: center; color: var(--muted); font-size: 0.
  • All templates
  • Community support
  • - +
    @@ -220,108 +220,24 @@ footer { padding: 40px 0; text-align: center; color: var(--muted); font-size: 0. - + diff --git a/src/routes/templates.ts b/src/routes/templates.ts index dd7ca01..d65a518 100644 --- a/src/routes/templates.ts +++ b/src/routes/templates.ts @@ -25,7 +25,7 @@ templatesRouter.post("/:id/render", async (req: Request, res: Response) => { return; } - const data = req.body; + const data = req.body.data || req.body; const html = renderTemplate(id, data); const pdf = await renderPdf(html, { format: data._format || "A4", diff --git a/src/services/browser.ts b/src/services/browser.ts index acb831a..0f2efb4 100644 --- a/src/services/browser.ts +++ b/src/services/browser.ts @@ -39,10 +39,10 @@ export async function renderPdf( landscape: options.landscape || false, printBackground: options.printBackground !== false, margin: options.margin || { - top: "20mm", - right: "15mm", - bottom: "20mm", - left: "15mm", + top: "0", + right: "0", + bottom: "0", + left: "0", }, headerTemplate: options.headerTemplate, footerTemplate: options.footerTemplate, @@ -79,10 +79,10 @@ export async function renderUrlPdf( landscape: options.landscape || false, printBackground: options.printBackground !== false, margin: options.margin || { - top: "20mm", - right: "15mm", - bottom: "20mm", - left: "15mm", + top: "0", + right: "0", + bottom: "0", + left: "0", }, });