From df0a36f03aea3eed595f888d56401aca314cce52 Mon Sep 17 00:00:00 2001 From: robbinat Date: Thu, 13 Nov 2025 00:39:55 -0800 Subject: [PATCH 1/3] feat: update seed pdf template --- scripts/generateSeeds.ts | 132 ++++++++++++++++++++++++++++++++++----- 1 file changed, 118 insertions(+), 14 deletions(-) diff --git a/scripts/generateSeeds.ts b/scripts/generateSeeds.ts index 987187b4..4a74b255 100644 --- a/scripts/generateSeeds.ts +++ b/scripts/generateSeeds.ts @@ -57,6 +57,7 @@ async function generateQRCodeBuffer(surveyCode: string, baseUrl: string, qrSize: return Buffer.from(qrDataUrl.split(',')[1], 'base64'); } + async function addQRCodePage( doc: any, surveyCode: string, @@ -69,33 +70,136 @@ async function addQRCodePage( } const pageWidth = doc.page.width; - const pageHeight = doc.page.height; - const qrSize = 200; + const margin = 50; + const contentWidth = pageWidth - (margin * 2); + + let currentY = margin; - // Generate and position QR code - const qrBuffer = await generateQRCodeBuffer(surveyCode, baseUrl, qrSize); - const qrX = (pageWidth - qrSize) / 2; - const qrY = (pageHeight - qrSize) / 2 - 50; + // Title + doc.fontSize(18) + .font('Helvetica-Bold') + .text('Understanding Unsheltered Homelessness', margin, currentY, { + align: 'center', + width: contentWidth + }); + + currentY += 40; + + // Instructions + doc.fontSize(12) + .font('Helvetica') + .text( + 'Bring this coupon to one of the locations below to complete a survey about your experience being unsheltered (including living in an RV or car/vehicle) and to receive a ', + margin, + currentY, + { + align: 'left', + width: contentWidth, + continued: true + } + ) + .font('Helvetica-Bold') + .text('$20', { continued: true }) + .font('Helvetica') + .text(' Gift Card.'); + + currentY += 50; - doc.image(qrBuffer, qrX, qrY, { + doc.fontSize(12) + .font('Helvetica') + .text( + 'Our locations are accessible with free parking and bike racks unless marked otherwise.', + margin, + currentY, + { + align: 'left', + width: contentWidth + } + ); + + currentY += 25; + + doc.text('Pets and service animals welcome.', margin, currentY, { + align: 'left', + width: contentWidth + }); + + currentY += 50; + + // QR Code and Referral Code + const qrSize = 100; + const qrX = (pageWidth - qrSize) / 2; + + const qrBuffer = await generateQRCodeBuffer(surveyCode, baseUrl, qrSize); + doc.image(qrBuffer, qrX, currentY, { width: qrSize, height: qrSize }); - // Add referral code text - doc.fontSize(20) + currentY += qrSize + 15; + + doc.fontSize(16) .font('Helvetica-Bold') - .text(`Referral Code: ${surveyCode}`, 0, qrY + qrSize + 30, { + .text(`Referral Code: ${surveyCode}`, margin, currentY, { align: 'center', - width: pageWidth + width: contentWidth }); - // Add location text + currentY += 50; + + // Locations section doc.fontSize(12) + .font('Helvetica-Bold') + .text('Locations', margin, currentY, { + align: 'left', + width: contentWidth + }); + + currentY += 20; + + doc.fontSize(11) + .font('Helvetica') + .text('• Highline United Methodist Church', margin + 10, currentY, { + align: 'left', + width: contentWidth - 10 + }); + + currentY += 15; + + doc.text(' 13015 1st AVE S, Burien, WA 98168', margin + 10, currentY, { + align: 'left', + width: contentWidth - 10 + }); + + currentY += 20; + + doc.text('• Interview Hours:', margin + 10, currentY, { + align: 'left', + width: contentWidth - 10 + }); + + currentY += 15; + + doc.text(' Monday - Friday (11/17 - 11/21)', margin + 10, currentY, { + align: 'left', + width: contentWidth - 10 + }); + + currentY += 15; + + doc.text(' 10am to 3pm', margin + 10, currentY, { + align: 'left', + width: contentWidth - 10 + }); + + currentY += 50; + + // Contact info + doc.fontSize(10) .font('Helvetica') - .text(`Location: ${locationName}`, 0, pageHeight - 80, { + .text('For questions, please call +1 (833) 393-1621', margin, currentY, { align: 'center', - width: pageWidth + width: contentWidth }); } From 9b74579cd4995ed1a862281aa1974ba11807a806 Mon Sep 17 00:00:00 2001 From: Natalie Robbins <102693174+natalierobbins@users.noreply.github.com> Date: Thu, 13 Nov 2025 12:47:01 -0800 Subject: [PATCH 2/3] fix: style --- scripts/generateSeeds.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/generateSeeds.ts b/scripts/generateSeeds.ts index 4a74b255..c2ef6f19 100644 --- a/scripts/generateSeeds.ts +++ b/scripts/generateSeeds.ts @@ -57,7 +57,6 @@ async function generateQRCodeBuffer(surveyCode: string, baseUrl: string, qrSize: return Buffer.from(qrDataUrl.split(',')[1], 'base64'); } - async function addQRCodePage( doc: any, surveyCode: string, From 281120b3c52ea0f2930468887637eb75c91efb79 Mon Sep 17 00:00:00 2001 From: robbinat Date: Thu, 13 Nov 2025 12:48:16 -0800 Subject: [PATCH 3/3] feat: update logo + wording --- scripts/assets/logo.png | Bin 0 -> 4309 bytes scripts/generateSeeds.ts | 25 ++++++++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 scripts/assets/logo.png diff --git a/scripts/assets/logo.png b/scripts/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8433be789d095343fc266920b7aacca01371d8d8 GIT binary patch literal 4309 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 literal 0 HcmV?d00001 diff --git a/scripts/generateSeeds.ts b/scripts/generateSeeds.ts index 4a74b255..66f754f9 100644 --- a/scripts/generateSeeds.ts +++ b/scripts/generateSeeds.ts @@ -1,5 +1,6 @@ #!/usr/bin/env tsx /** + * NOTE: Make sure to set CLIENT_URL in .env to the correct deployment URL * Script to generate N seeds for a given location * Usage: npm run generate-seeds -- * Example: npm run generate-seeds -- "Main Hub" 10 @@ -21,6 +22,7 @@ const __dirname = dirname(__filename); const serverRequire = createRequire(path.join(__dirname, '../server/package.json')); const QRCode = serverRequire('qrcode'); const PDFDocument = serverRequire('pdfkit'); +const logoPath = path.join(__dirname, 'assets/logo.png'); // ===== PDF Generation Helper Functions ===== @@ -57,7 +59,6 @@ async function generateQRCodeBuffer(surveyCode: string, baseUrl: string, qrSize: return Buffer.from(qrDataUrl.split(',')[1], 'base64'); } - async function addQRCodePage( doc: any, surveyCode: string, @@ -75,6 +76,14 @@ async function addQRCodePage( let currentY = margin; + if (fs.existsSync(logoPath)) { + const logoWidth = 60; + doc.image(logoPath, (pageWidth - logoWidth) / 2, currentY, { + fit: [logoWidth, logoWidth], + }); + currentY += logoWidth + 10; + } + // Title doc.fontSize(18) .font('Helvetica-Bold') @@ -173,7 +182,7 @@ async function addQRCodePage( currentY += 20; - doc.text('• Interview Hours:', margin + 10, currentY, { + doc.text('• Interview Dates and Hours:', margin + 10, currentY, { align: 'left', width: contentWidth - 10 }); @@ -216,9 +225,6 @@ async function generatePDF(seeds: any[], locationName: string): Promise { const stream = fs.createWriteStream(filepath); doc.pipe(stream); - // BE SURE TO SET THIS TO CORRECT URL IN ENV - const baseUrl = process.env.CLIENT_URL || 'http://localhost:3000'; - // Generate one page per seed for (let i = 0; i < seeds.length; i++) { await addQRCodePage(doc, seeds[i].surveyCode, locationName, baseUrl, i === 0); @@ -340,6 +346,15 @@ async function generateSeeds(locationIdentifier: string, count: number): Promise } } +// Require CLIENT_URL to be set +const baseUrl = process.env.CLIENT_URL; +if (!baseUrl) { + console.error('\n✗ Error: CLIENT_URL environment variable is not set'); + console.error(' Please set CLIENT_URL in your .env file before running this script.'); + console.error(' Example: CLIENT_URL=https://your-domain.com\n'); + process.exit(1); +} + // Parse command line arguments const args = process.argv.slice(2);