Lead Magnet implementiert
This commit is contained in:
parent
be54d388bb
commit
2879fd0d8e
|
|
@ -38,14 +38,26 @@ export const ReprintSavingsCalculator: React.FC = () => {
|
|||
const annualWaste = cost * updates;
|
||||
const withQRMaster = qrMasterYearlyCost;
|
||||
const savings = annualWaste - withQRMaster;
|
||||
const savingsPercent = Math.round((savings / annualWaste) * 100);
|
||||
const monthsPaidFor = Math.round(annualWaste / (qrMasterYearlyCost / 12));
|
||||
|
||||
// Calculate percentages based on the larger value to avoid overflow bars
|
||||
const maxValue = Math.max(annualWaste, withQRMaster);
|
||||
const wastePercentOfMax = (annualWaste / maxValue) * 100;
|
||||
const costPercentOfMax = (withQRMaster / maxValue) * 100;
|
||||
|
||||
const isPositiveSavings = savings > 0;
|
||||
const savingsPercent = isPositiveSavings
|
||||
? Math.round((savings / annualWaste) * 100)
|
||||
: Math.round((Math.abs(savings) / withQRMaster) * 100);
|
||||
|
||||
const monthsPaidFor = isPositiveSavings
|
||||
? Math.round(annualWaste / (qrMasterYearlyCost / 12))
|
||||
: 0;
|
||||
|
||||
return {
|
||||
annualWaste,
|
||||
withQRMaster,
|
||||
savings: Math.max(0, savings),
|
||||
savingsPercent: Math.max(0, savingsPercent),
|
||||
savings,
|
||||
savingsPercent,
|
||||
monthsPaidFor,
|
||||
};
|
||||
}, [reprintCost, updatesPerYear]);
|
||||
|
|
@ -120,6 +132,8 @@ export const ReprintSavingsCalculator: React.FC = () => {
|
|||
}).format(value);
|
||||
};
|
||||
|
||||
const isPositiveSavings = results ? results.savings > 0 : false;
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className="py-20 bg-gradient-to-br from-slate-50 to-white relative overflow-hidden">
|
||||
|
|
@ -243,12 +257,21 @@ export const ReprintSavingsCalculator: React.FC = () => {
|
|||
className="h-full flex flex-col"
|
||||
>
|
||||
<div className="flex-1">
|
||||
{isPositiveSavings ? (
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-green-50 text-green-700 text-sm font-medium mb-6">
|
||||
<TrendingDown className="w-4 h-4" />
|
||||
Potential Savings Found
|
||||
</div>
|
||||
) : (
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-blue-50 text-blue-700 text-sm font-medium mb-6">
|
||||
<TrendingDown className="w-4 h-4" />
|
||||
Cost Analysis
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mb-8">
|
||||
{isPositiveSavings ? (
|
||||
<>
|
||||
<p className="text-slate-500 text-sm font-medium uppercase tracking-wide mb-1">Annual Waste Identified</p>
|
||||
<div className="flex items-baseline gap-2">
|
||||
<span className="text-5xl lg:text-6xl font-black text-slate-900 tracking-tight">
|
||||
|
|
@ -258,6 +281,20 @@ export const ReprintSavingsCalculator: React.FC = () => {
|
|||
<p className="text-red-500 text-sm mt-2 font-medium flex items-center gap-1">
|
||||
<AlertTriangle className="w-3 h-3" /> Money currently lost to static reprints
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p className="text-slate-500 text-sm font-medium uppercase tracking-wide mb-1">Estimated Annual Investment</p>
|
||||
<div className="flex items-baseline gap-2">
|
||||
<span className="text-5xl lg:text-6xl font-black text-slate-900 tracking-tight">
|
||||
{formatCurrency(Math.abs(results.savings))}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-slate-500 text-sm mt-2 font-medium">
|
||||
Difference for upgrading to Dynamic QR features
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Comparison Bars */}
|
||||
|
|
@ -270,7 +307,7 @@ export const ReprintSavingsCalculator: React.FC = () => {
|
|||
<div className="h-3 bg-slate-100 rounded-full w-full overflow-hidden">
|
||||
<motion.div
|
||||
initial={{ width: 0 }}
|
||||
animate={{ width: "100%" }}
|
||||
animate={{ width: `${(results.annualWaste / Math.max(results.annualWaste, results.withQRMaster)) * 100}%` }}
|
||||
transition={{ duration: 1, ease: "easeOut" }}
|
||||
className="h-full bg-red-500 rounded-full"
|
||||
/>
|
||||
|
|
@ -280,19 +317,20 @@ export const ReprintSavingsCalculator: React.FC = () => {
|
|||
<div className="relative">
|
||||
<div className="flex justify-between text-sm mb-2">
|
||||
<span className="font-semibold text-slate-700">QR Master Cost</span>
|
||||
<span className="font-bold text-emerald-600">{formatCurrency(results.withQRMaster)}</span>
|
||||
<span className={isPositiveSavings ? "font-bold text-emerald-600" : "font-bold text-blue-600"}>{formatCurrency(results.withQRMaster)}</span>
|
||||
</div>
|
||||
<div className="h-3 bg-slate-100 rounded-full w-full overflow-hidden">
|
||||
<motion.div
|
||||
initial={{ width: 0 }}
|
||||
animate={{ width: `${(results.withQRMaster / results.annualWaste) * 100}%` }}
|
||||
animate={{ width: `${(results.withQRMaster / Math.max(results.annualWaste, results.withQRMaster)) * 100}%` }}
|
||||
transition={{ duration: 1, ease: "easeOut", delay: 0.5 }}
|
||||
className="h-full bg-emerald-500 rounded-full"
|
||||
className={isPositiveSavings ? "h-full bg-emerald-500 rounded-full" : "h-full bg-blue-500 rounded-full"}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isPositiveSavings ? (
|
||||
<div className="bg-emerald-50/50 border border-emerald-100 rounded-2xl p-6 mb-8">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="p-3 bg-emerald-100 rounded-xl text-emerald-600 shrink-0">
|
||||
|
|
@ -309,6 +347,23 @@ export const ReprintSavingsCalculator: React.FC = () => {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-blue-50/50 border border-blue-100 rounded-2xl p-6 mb-8">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="p-3 bg-blue-100 rounded-xl text-blue-600 shrink-0">
|
||||
<CheckCircle className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="text-lg font-bold text-blue-900 mb-1">
|
||||
Premium features for just {formatCurrency(Math.abs(results.savings))}/year
|
||||
</h4>
|
||||
<p className="text-blue-800/80 leading-relaxed">
|
||||
For a small investment, you gain full control, analytics, and the ability to update destinations anytime.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-auto space-y-3">
|
||||
|
|
@ -319,10 +374,10 @@ export const ReprintSavingsCalculator: React.FC = () => {
|
|||
</Link>
|
||||
<button
|
||||
onClick={handleDownloadReport}
|
||||
className="w-full flex items-center justify-center gap-2 py-3 px-4 text-sm font-medium text-slate-700 bg-slate-100 hover:bg-slate-200 rounded-xl transition-colors"
|
||||
className="w-full flex items-center justify-center gap-2 py-4 px-6 text-base font-bold text-white bg-gradient-to-r from-slate-800 to-slate-900 hover:from-slate-700 hover:to-slate-800 rounded-xl shadow-lg shadow-slate-900/10 hover:shadow-slate-900/20 transform hover:-translate-y-0.5 transition-all duration-200"
|
||||
>
|
||||
<FileDown className="w-4 h-4" />
|
||||
Download Your Savings Report (PDF)
|
||||
<FileDown className="w-5 h-5" />
|
||||
Get Official Savings Report PDF
|
||||
</button>
|
||||
<p className="text-center text-xs text-slate-400">
|
||||
Based on QR Master Pro annual plan. No credit card required to start.
|
||||
|
|
|
|||
|
|
@ -12,131 +12,309 @@ interface ReportData {
|
|||
export function generateSavingsReport(data: ReportData): void {
|
||||
const doc = new jsPDF();
|
||||
const pageWidth = doc.internal.pageSize.getWidth();
|
||||
const pageHeight = doc.internal.pageSize.getHeight();
|
||||
const margin = 20;
|
||||
|
||||
// Colors
|
||||
const primaryColor: [number, number, number] = [79, 70, 229]; // Indigo
|
||||
const greenColor: [number, number, number] = [16, 185, 129]; // Emerald
|
||||
const redColor: [number, number, number] = [239, 68, 68]; // Red
|
||||
const grayColor: [number, number, number] = [100, 116, 139]; // Slate
|
||||
// Brand Colors
|
||||
const colors = {
|
||||
primary: [79, 70, 229] as [number, number, number], // Indigo 600
|
||||
primaryLight: [224, 231, 255] as [number, number, number], // Indigo 100
|
||||
success: [16, 185, 129] as [number, number, number], // Emerald 500
|
||||
successBg: [236, 253, 245] as [number, number, number], // Emerald 50
|
||||
text: [30, 41, 59] as [number, number, number], // Slate 800
|
||||
textLight: [100, 116, 139] as [number, number, number], // Slate 500
|
||||
border: [226, 232, 240] as [number, number, number], // Slate 200
|
||||
danger: [239, 68, 68] as [number, number, number], // Red 500
|
||||
dangerBg: [254, 242, 242] as [number, number, number], // Red 50
|
||||
};
|
||||
|
||||
// Header section with brand color
|
||||
doc.setFillColor(...primaryColor);
|
||||
doc.rect(0, 0, pageWidth, 45, 'F');
|
||||
let currentY = 20;
|
||||
|
||||
// Logo text
|
||||
doc.setTextColor(255, 255, 255);
|
||||
doc.setFontSize(28);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text('QR Master', 20, 28);
|
||||
|
||||
// Subtitle
|
||||
doc.setFontSize(12);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.text('Your Personalized Savings Report', 20, 38);
|
||||
|
||||
// Main title
|
||||
doc.setTextColor(30, 41, 59);
|
||||
doc.setFontSize(22);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text('Reprint Cost Analysis', 20, 65);
|
||||
|
||||
// Date
|
||||
doc.setTextColor(...grayColor);
|
||||
doc.setFontSize(10);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.text(`Generated on ${new Date().toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})}`, 20, 75);
|
||||
|
||||
// Input summary box
|
||||
doc.setFillColor(248, 250, 252);
|
||||
doc.roundedRect(20, 85, pageWidth - 40, 45, 3, 3, 'F');
|
||||
|
||||
doc.setTextColor(30, 41, 59);
|
||||
doc.setFontSize(12);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text('Your Input', 28, 98);
|
||||
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.setFontSize(11);
|
||||
doc.setTextColor(...grayColor);
|
||||
doc.text(`Reprint Cost per Batch:`, 28, 112);
|
||||
doc.text(`URL Updates per Year:`, 28, 122);
|
||||
|
||||
doc.setTextColor(30, 41, 59);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text(`€${data.reprintCost.toLocaleString()}`, 100, 112);
|
||||
doc.text(`${data.updatesPerYear}`, 100, 122);
|
||||
|
||||
// Results section
|
||||
doc.setTextColor(30, 41, 59);
|
||||
doc.setFontSize(14);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text('Cost Comparison', 20, 150);
|
||||
|
||||
// Static QR Code cost (red)
|
||||
doc.setFillColor(254, 242, 242);
|
||||
doc.roundedRect(20, 158, pageWidth - 40, 28, 3, 3, 'F');
|
||||
|
||||
doc.setTextColor(...redColor);
|
||||
doc.setFontSize(11);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text('Static QR Codes (Annual Waste)', 28, 172);
|
||||
doc.setFontSize(16);
|
||||
doc.text(`€${data.annualWaste.toLocaleString()}`, pageWidth - 28, 172, { align: 'right' });
|
||||
|
||||
// QR Master cost (green)
|
||||
doc.setFillColor(236, 253, 245);
|
||||
doc.roundedRect(20, 192, pageWidth - 40, 28, 3, 3, 'F');
|
||||
|
||||
doc.setTextColor(...greenColor);
|
||||
doc.setFontSize(11);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text('QR Master Pro (Annual Cost)', 28, 206);
|
||||
doc.setFontSize(16);
|
||||
doc.text(`€${data.qrMasterCost.toLocaleString()}`, pageWidth - 28, 206, { align: 'right' });
|
||||
|
||||
// Savings highlight
|
||||
doc.setFillColor(...greenColor);
|
||||
doc.roundedRect(20, 230, pageWidth - 40, 40, 3, 3, 'F');
|
||||
|
||||
doc.setTextColor(255, 255, 255);
|
||||
doc.setFontSize(12);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.text('Your Annual Savings', 28, 245);
|
||||
// --- Header ---
|
||||
// Logo / Brand Name
|
||||
doc.setTextColor(...colors.primary);
|
||||
doc.setFontSize(24);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text(`€${data.savings.toLocaleString()}`, 28, 262);
|
||||
doc.setFontSize(14);
|
||||
doc.text(`(${data.savingsPercent}%)`, 90, 262);
|
||||
doc.text('QR Master', margin, currentY + 8);
|
||||
|
||||
// CTA section
|
||||
doc.setTextColor(30, 41, 59);
|
||||
doc.setFontSize(14);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text('Ready to Start Saving?', 20, 295);
|
||||
|
||||
doc.setTextColor(...grayColor);
|
||||
// Date (Right aligned)
|
||||
doc.setTextColor(...colors.textLight);
|
||||
doc.setFontSize(10);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.text('Dynamic QR codes let you update destinations without reprinting.', 20, 307);
|
||||
doc.text('Track scans, analyze performance, and never waste print budget again.', 20, 317);
|
||||
const dateStr = new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
|
||||
doc.text(dateStr, pageWidth - margin, currentY + 8, { align: 'right' });
|
||||
|
||||
// Website link
|
||||
doc.setTextColor(...primaryColor);
|
||||
currentY += 20;
|
||||
|
||||
// Title Section
|
||||
doc.setLineWidth(0.5);
|
||||
doc.setDrawColor(...colors.primary);
|
||||
doc.line(margin, currentY, margin + 40, currentY); // Small accent line
|
||||
|
||||
currentY += 10;
|
||||
|
||||
doc.setTextColor(...colors.text);
|
||||
doc.setFontSize(28);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text('Start your free trial: www.qrmaster.net/signup', 20, 332);
|
||||
doc.text('Savings Analysis', margin, currentY);
|
||||
|
||||
// Footer
|
||||
doc.setTextColor(...grayColor);
|
||||
currentY += 8;
|
||||
doc.setTextColor(...colors.textLight);
|
||||
doc.setFontSize(12);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.text('Prepared specifically for your business based on your input parameters.', margin, currentY);
|
||||
|
||||
currentY += 20;
|
||||
|
||||
// --- Input Summary ---
|
||||
// Background for inputs
|
||||
doc.setFillColor(248, 250, 252); // Slate 50
|
||||
doc.setDrawColor(...colors.border);
|
||||
doc.roundedRect(margin, currentY, pageWidth - (margin * 2), 35, 2, 2, 'FD');
|
||||
|
||||
const inputInnerY = currentY + 12;
|
||||
|
||||
// Label 1
|
||||
doc.setFontSize(10);
|
||||
doc.setTextColor(...colors.textLight);
|
||||
doc.text('REPRINT COST PER BATCH', margin + 10, inputInnerY);
|
||||
|
||||
// Value 1
|
||||
doc.setFontSize(16);
|
||||
doc.setTextColor(...colors.text);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text(`€${data.reprintCost.toLocaleString()}`, margin + 10, inputInnerY + 10);
|
||||
|
||||
// Divider
|
||||
doc.setDrawColor(...colors.border);
|
||||
doc.line(pageWidth / 2, inputInnerY - 5, pageWidth / 2, inputInnerY + 18);
|
||||
|
||||
// Label 2
|
||||
doc.setFontSize(10);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.setTextColor(...colors.textLight);
|
||||
doc.text('URL UPDATES/YEAR', (pageWidth / 2) + 10, inputInnerY);
|
||||
|
||||
// Value 2
|
||||
doc.setFontSize(16);
|
||||
doc.setTextColor(...colors.text);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text(`${data.updatesPerYear}`, (pageWidth / 2) + 10, inputInnerY + 10);
|
||||
|
||||
currentY += 50;
|
||||
|
||||
// --- Analysis Results ---
|
||||
|
||||
doc.setFontSize(14);
|
||||
doc.setTextColor(...colors.text);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text('Cost Analysis', margin, currentY);
|
||||
currentY += 10;
|
||||
|
||||
// 1. Static Codes (Bad)
|
||||
const cardHeight = 26; // Reduced height
|
||||
// perfect vertical centering: (Height 26 / 2) + (Approx cap-height correction ~3) = 16
|
||||
const textCenterOffset = 16;
|
||||
|
||||
// Draw "Static" Row
|
||||
doc.setDrawColor(...colors.border);
|
||||
doc.setFillColor(255, 255, 255);
|
||||
doc.roundedRect(margin, currentY, pageWidth - (margin * 2), cardHeight, 2, 2, 'FD');
|
||||
|
||||
// Icon/Label
|
||||
doc.setTextColor(...colors.danger);
|
||||
doc.setFontSize(11);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text('• Static QR Codes', margin + 8, currentY + textCenterOffset);
|
||||
|
||||
doc.setTextColor(...colors.textLight);
|
||||
doc.setFontSize(10);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
// Align sub-label relative to first label
|
||||
doc.text('(Print & Reprint Costs)', margin + 50, currentY + textCenterOffset);
|
||||
|
||||
doc.setTextColor(...colors.text);
|
||||
doc.setFontSize(12);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text(`€${data.annualWaste.toLocaleString()}`, pageWidth - margin - 10, currentY + textCenterOffset, { align: 'right' });
|
||||
|
||||
currentY += cardHeight + 4;
|
||||
|
||||
// Draw "QR Master" Row
|
||||
doc.setDrawColor(...colors.border);
|
||||
doc.setFillColor(255, 255, 255);
|
||||
doc.roundedRect(margin, currentY, pageWidth - (margin * 2), cardHeight, 2, 2, 'FD');
|
||||
|
||||
// Icon/Label
|
||||
doc.setTextColor(...colors.success);
|
||||
doc.setFontSize(11);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text('• QR Master Pro', margin + 8, currentY + textCenterOffset);
|
||||
|
||||
doc.setTextColor(...colors.textLight);
|
||||
doc.setFontSize(10);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.text('(Subscription)', margin + 50, currentY + textCenterOffset);
|
||||
|
||||
doc.setTextColor(...colors.text);
|
||||
doc.setFontSize(12);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text(`€${data.qrMasterCost.toLocaleString()}`, pageWidth - margin - 10, currentY + textCenterOffset, { align: 'right' });
|
||||
|
||||
currentY += cardHeight + 15;
|
||||
|
||||
// --- PAGE CHANGE: Results & Strategy on Page 2 ---
|
||||
doc.addPage();
|
||||
currentY = 40;
|
||||
|
||||
// --- TOTAL SAVINGS / INVESTMENT HERO SECTION ---
|
||||
// (No line divider needed at top of new page)
|
||||
|
||||
const isPositiveSavings = data.savings > 0;
|
||||
const heroColor = isPositiveSavings ? colors.success : colors.primary; // Green or Indigo
|
||||
const heroBg = isPositiveSavings ? colors.successBg : colors.primaryLight;
|
||||
|
||||
doc.setTextColor(...colors.textLight);
|
||||
doc.setFontSize(11);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
|
||||
const heroTitle = isPositiveSavings ? 'PROJECTED ANNUAL SAVINGS' : 'ANNUAL INVESTMENT REQUIRED';
|
||||
doc.text(heroTitle, margin, currentY);
|
||||
|
||||
// Big Number and Badge Container
|
||||
const savingsY = currentY + 12;
|
||||
|
||||
// 1. The Amount
|
||||
doc.setTextColor(...heroColor);
|
||||
doc.setFontSize(42);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
const amountText = `€${Math.abs(data.savings).toLocaleString()}`;
|
||||
doc.text(amountText, margin, savingsY + 12);
|
||||
|
||||
// 2. The Percentage Badge (Centered relative to the number)
|
||||
const numberWidth = doc.getStringUnitWidth(amountText) * 42 / doc.internal.scaleFactor;
|
||||
const badgeX = margin + numberWidth + 10; // Slightly tighter gap
|
||||
|
||||
// Determine badge text and dynamic width
|
||||
doc.setFontSize(9);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
const badgeText = isPositiveSavings ? `${data.savingsPercent}% Saved` : 'Upgrade';
|
||||
const textWidth = doc.getStringUnitWidth(badgeText) * 9 / doc.internal.scaleFactor;
|
||||
|
||||
const badgePadding = 12; // 6px on each side
|
||||
const badgeWidth = textWidth + badgePadding;
|
||||
const badgeHeight = 14; // Reduced height (was 16)
|
||||
|
||||
// Draw Compact Badge
|
||||
doc.setFillColor(...heroBg);
|
||||
doc.setDrawColor(...heroColor);
|
||||
doc.setLineWidth(0.5);
|
||||
doc.roundedRect(badgeX, savingsY + 1, badgeWidth, badgeHeight, 6, 6, 'FD'); // y+1 to align better with text baseline
|
||||
|
||||
doc.setTextColor(...heroColor);
|
||||
doc.text(badgeText, badgeX + (badgeWidth / 2), savingsY + 10, { align: 'center' }); // Centered
|
||||
|
||||
currentY += 40; // Reduced gap
|
||||
|
||||
// --- Recommended Resources ---
|
||||
doc.setTextColor(...colors.text);
|
||||
doc.setFontSize(12);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text('Recommended Reading', margin, currentY);
|
||||
currentY += 8;
|
||||
|
||||
const resources = [
|
||||
{ title: "The ROI of Dynamic QR Codes", url: "www.qrmaster.net/blog/roi-dynamic-qr" },
|
||||
{ title: "How to Track Physical Marketing", url: "www.qrmaster.net/blog/tracking-guide" },
|
||||
{ title: "Best Practices for QR Campaigns", url: "www.qrmaster.net/blog/best-practices" }
|
||||
];
|
||||
|
||||
resources.forEach(res => {
|
||||
doc.setTextColor(...colors.primary);
|
||||
doc.setFontSize(10);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.textWithLink(`• ${res.title}`, margin, currentY, { url: `https://${res.url}` });
|
||||
currentY += 5;
|
||||
});
|
||||
|
||||
currentY += 12;
|
||||
|
||||
// --- Call to Action ---
|
||||
// No page check needed, we are on Page 2
|
||||
|
||||
doc.setTextColor(...colors.text);
|
||||
doc.setFontSize(12);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text('Next Steps', margin, currentY);
|
||||
|
||||
currentY += 8;
|
||||
doc.setTextColor(...colors.textLight);
|
||||
doc.setFontSize(10);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.text('Ready to eliminate reprint costs? Create your account today.', margin, currentY);
|
||||
|
||||
currentY += 8; // FIXED: Restored spacing to prevent overlap
|
||||
doc.setTextColor(...colors.primary);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.textWithLink('www.qrmaster.net/signup', margin, currentY, { url: 'https://www.qrmaster.net/signup' });
|
||||
|
||||
currentY += 20;
|
||||
|
||||
// --- Why Dynamic? (Value Add) ---
|
||||
// If we are close to bottom, force page 2 for this section to make it look intentional
|
||||
if (currentY > pageHeight - 60) {
|
||||
doc.addPage();
|
||||
currentY = 40;
|
||||
}
|
||||
|
||||
doc.setTextColor(...colors.text);
|
||||
doc.setFontSize(12);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text('Why Professionals Choose Dynamic', margin, currentY);
|
||||
currentY += 10;
|
||||
|
||||
const benefits = [
|
||||
{ title: "Real-time Control", desc: "Update destinations instantly. Fix mistakes locally without reprinting." },
|
||||
{ title: "Smart Analytics", desc: "Track scans, locations, and device types to measure ROI." },
|
||||
{ title: "Brand Identity", desc: "Fully customizable designs that match your corporate identity." }
|
||||
];
|
||||
|
||||
benefits.forEach(benefit => {
|
||||
// Bullet
|
||||
doc.setFillColor(...colors.success); // Green bullets
|
||||
doc.circle(margin + 1, currentY - 1, 1.5, 'F');
|
||||
|
||||
// Title
|
||||
doc.setTextColor(...colors.text);
|
||||
doc.setFontSize(10);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text(benefit.title, margin + 6, currentY);
|
||||
|
||||
// Desc
|
||||
const titleWidth = doc.getStringUnitWidth(benefit.title) * 10 / doc.internal.scaleFactor;
|
||||
doc.setTextColor(...colors.textLight);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.text(`- ${benefit.desc}`, margin + 6 + titleWidth + 2, currentY);
|
||||
|
||||
currentY += 7;
|
||||
});
|
||||
|
||||
// --- Footer ---
|
||||
const footerY = pageHeight - 15;
|
||||
const pageCount = doc.getNumberOfPages();
|
||||
|
||||
for (let i = 1; i <= pageCount; i++) {
|
||||
doc.setPage(i);
|
||||
doc.setDrawColor(...colors.border);
|
||||
doc.line(margin, footerY - 10, pageWidth - margin, footerY - 10);
|
||||
|
||||
doc.setTextColor(...colors.textLight);
|
||||
doc.setFontSize(8);
|
||||
doc.setFont('helvetica', 'normal');
|
||||
const footerY = doc.internal.pageSize.getHeight() - 15;
|
||||
doc.text('© 2026 QR Master. All rights reserved.', pageWidth / 2, footerY, { align: 'center' });
|
||||
doc.text('www.qrmaster.net', pageWidth / 2, footerY + 8, { align: 'center' });
|
||||
doc.text('© 2026 QR Master. All rights reserved.', margin, footerY);
|
||||
doc.text('www.qrmaster.net', pageWidth - margin, footerY, { align: 'right' });
|
||||
}
|
||||
|
||||
// Download the PDF
|
||||
doc.save('QRMaster-Savings-Report.pdf');
|
||||
// Save
|
||||
doc.save('QRMaster_Savings_Analysis.pdf');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ export function middleware(req: NextRequest) {
|
|||
'/dynamic-qr-code-generator',
|
||||
'/bulk-qr-code-generator',
|
||||
'/qr-code-tracking',
|
||||
'/reprint-calculator',
|
||||
];
|
||||
|
||||
// Check if path is public
|
||||
|
|
|
|||
Loading…
Reference in New Issue