Fix adoption report photos and section order
Deploy / deploy-dev (push) Successful in 1m41s
Deploy / deploy-prod (push) Has been skipped

This commit is contained in:
Corey Blais
2026-06-02 18:26:51 -04:00
parent cadbdc2a7f
commit 979a17132d
2 changed files with 57 additions and 21 deletions
+34 -2
View File
@@ -1387,6 +1387,37 @@ const deleteBirdPhotoObjectIfNeeded = async (objectKey: string | null) => {
} }
}; };
const loadBirdReportPhotoBuffer = async (bird: BirdRow) => {
if (!bird.photo_object_key) {
return null;
}
const s3Config = getS3ImageStorageConfig();
if (!s3Config) {
return null;
}
const signedUrl = getSignedS3ObjectUrl({
config: s3Config,
objectKey: bird.photo_object_key,
expiresInSeconds: 5 * 60,
});
const imageResponse = await fetch(signedUrl);
if (!imageResponse.ok) {
return null;
}
const contentType = imageResponse.headers.get('content-type') || bird.photo_content_type || '';
if (!/^image\/(?:png|jpe?g)$/i.test(contentType)) {
return null;
}
return Buffer.from(await imageResponse.arrayBuffer());
};
const getDefaultBirdPhotoAttachment = () => { const getDefaultBirdPhotoAttachment = () => {
const defaultPhotoPath = path.join(process.cwd(), 'assets', 'yoda-default.png'); const defaultPhotoPath = path.join(process.cwd(), 'assets', 'yoda-default.png');
@@ -3568,10 +3599,11 @@ app.post(
throw new Error('Unable to create bird transfer code.'); throw new Error('Unable to create bird transfer code.');
} }
const [weights, vetVisits, notes] = await Promise.all([ const [weights, vetVisits, notes, birdPhotoBuffer] = await Promise.all([
listWeightsForBird(sourceBird.id, req.auth!.workspace.id, adoptionReportWeightHistoryDays), listWeightsForBird(sourceBird.id, req.auth!.workspace.id, adoptionReportWeightHistoryDays),
listVetVisitsForBird(sourceBird.id, req.auth!.workspace.id), listVetVisitsForBird(sourceBird.id, req.auth!.workspace.id),
listFlockNotes(req.auth!.workspace.id), listFlockNotes(req.auth!.workspace.id),
loadBirdReportPhotoBuffer(sourceBird),
]); ]);
const birdNotes = notes.filter((note) => note.bird_id === sourceBird.id); const birdNotes = notes.filter((note) => note.bird_id === sourceBird.id);
const pdf = await renderAdoptionReportPdf({ const pdf = await renderAdoptionReportPdf({
@@ -3579,8 +3611,8 @@ app.post(
weights, weights,
vetVisits, vetVisits,
notes: birdNotes, notes: birdNotes,
workspace: req.auth!.workspace,
transferCode: transferCode.code, transferCode: transferCode.code,
birdPhotoBuffer,
printFriendly: req.query.printFriendly === 'true', printFriendly: req.query.printFriendly === 'true',
assets: { assets: {
logoPath: path.join(process.cwd(), 'assets', 'flockpal-logo.png'), logoPath: path.join(process.cwd(), 'assets', 'flockpal-logo.png'),
+23 -19
View File
@@ -2,15 +2,15 @@ import fs from 'fs';
import PDFDocument from 'pdfkit'; import PDFDocument from 'pdfkit';
import QRCode from 'qrcode'; import QRCode from 'qrcode';
import type { BirdRow, FlockNoteRow, VetVisitRow, WeightRow, WorkspaceRow } from '../types.js'; import type { BirdRow, FlockNoteRow, VetVisitRow, WeightRow } from '../types.js';
type AdoptionReportInput = { type AdoptionReportInput = {
bird: BirdRow; bird: BirdRow;
weights: WeightRow[]; weights: WeightRow[];
vetVisits: VetVisitRow[]; vetVisits: VetVisitRow[];
notes: FlockNoteRow[]; notes: FlockNoteRow[];
workspace: WorkspaceRow;
transferCode: string; transferCode: string;
birdPhotoBuffer?: Buffer | null;
assets: { assets: {
logoPath: string; logoPath: string;
wordmarkPath: string; wordmarkPath: string;
@@ -256,8 +256,8 @@ export const renderAdoptionReportPdf = async ({
weights, weights,
vetVisits, vetVisits,
notes, notes,
workspace,
transferCode, transferCode,
birdPhotoBuffer = null,
assets, assets,
printFriendly = false, printFriendly = false,
}: AdoptionReportInput) => { }: AdoptionReportInput) => {
@@ -275,7 +275,7 @@ export const renderAdoptionReportPdf = async ({
const logoPath = fs.existsSync(assets.logoPath) ? assets.logoPath : null; const logoPath = fs.existsSync(assets.logoPath) ? assets.logoPath : null;
const wordmarkPath = fs.existsSync(assets.wordmarkPath) ? assets.wordmarkPath : logoPath; const wordmarkPath = fs.existsSync(assets.wordmarkPath) ? assets.wordmarkPath : logoPath;
const defaultPhotoPath = fs.existsSync(assets.defaultBirdPhotoPath) ? assets.defaultBirdPhotoPath : null; const defaultPhotoPath = fs.existsSync(assets.defaultBirdPhotoPath) ? assets.defaultBirdPhotoPath : null;
const photoBuffer = dataUrlToBuffer(bird.photo_data_url); const photoBuffer = birdPhotoBuffer ?? dataUrlToBuffer(bird.photo_data_url);
const contentWidth = page.width - page.margin * 2; const contentWidth = page.width - page.margin * 2;
const headerY = page.margin; const headerY = page.margin;
const headerHeight = 136; const headerHeight = 136;
@@ -346,21 +346,6 @@ export const renderAdoptionReportPdf = async ({
); );
y += 72; y += 72;
y = drawSectionTitle(doc, 'Weight Graph', y);
drawSimpleWeightChart(doc, weights, bird.chart_color, page.margin, y, contentWidth, 120);
y += 140;
y = drawSectionTitle(doc, 'Weight History', y);
y = drawTable(
doc,
['Date', 'Weight', 'Notes'],
weights.length ? weights.map((entry) => [formatDate(entry.recorded_on), formatWeight(entry.weight_grams), entry.notes || '']) : [['No weights recorded.', '', '']],
page.margin,
y,
[95, 70, contentWidth - 165],
24,
);
if (y > 610) { if (y > 610) {
doc.addPage(); doc.addPage();
y = page.margin; y = page.margin;
@@ -388,6 +373,25 @@ export const renderAdoptionReportPdf = async ({
28, 28,
); );
if (y > 575) {
doc.addPage();
y = page.margin;
}
y = drawSectionTitle(doc, 'Weight Graph', y);
drawSimpleWeightChart(doc, weights, bird.chart_color, page.margin, y, contentWidth, 120);
y += 140;
y = drawSectionTitle(doc, 'Weight History', y);
y = drawTable(
doc,
['Date', 'Weight', 'Notes'],
weights.length ? weights.map((entry) => [formatDate(entry.recorded_on), formatWeight(entry.weight_grams), entry.notes || '']) : [['No weights recorded.', '', '']],
page.margin,
y,
[95, 70, contentWidth - 165],
24,
);
if (notes.length) { if (notes.length) {
if (y > 635) { if (y > 635) {
doc.addPage(); doc.addPage();