From c0e726d5f7033cd5b5ced6200bdb205368ab491a Mon Sep 17 00:00:00 2001 From: Philip Date: Wed, 18 Feb 2026 15:19:20 -0800 Subject: [PATCH] Add print-to-PDF feature for the squares grid Adds a Print button to the header that opens /print in a new tab. The print page auto-triggers the browser print dialog, formatted for 11x8.5" landscape. Includes team logos, AFC/NFC axis numbers, player names in each cell (color-coded confirmed/pending/available), and a legend. Co-Authored-By: Claude Sonnet 4.6 --- src/app/print/page.tsx | 188 +++++++++++++++++++++++++++++ src/components/PrintTrigger.tsx | 11 ++ src/components/grid/GridHeader.tsx | 8 ++ src/middleware.ts | 1 + 4 files changed, 208 insertions(+) create mode 100644 src/app/print/page.tsx create mode 100644 src/components/PrintTrigger.tsx diff --git a/src/app/print/page.tsx b/src/app/print/page.tsx new file mode 100644 index 0000000..377e409 --- /dev/null +++ b/src/app/print/page.tsx @@ -0,0 +1,188 @@ +import { prisma } from '@/lib/prisma'; +import { PrintTrigger } from '@/components/PrintTrigger'; + +export const dynamic = 'force-dynamic'; + +export default async function PrintPage() { + const settings = await prisma.gameSettings.findUnique({ + where: { id: 'singleton' }, + }); + + const squares = await prisma.square.findMany({ + include: { user: { select: { name: true } } }, + orderBy: { position: 'asc' }, + }); + + const gridNumbers = await prisma.gridNumber.findMany({ + orderBy: { position: 'asc' }, + }); + + const hasNumbers = gridNumbers.length === 10; + const nfcNumbers = hasNumbers ? gridNumbers.map((g) => g.nfcNumber) : Array(10).fill('?'); + const afcNumbers = hasNumbers ? gridNumbers.map((g) => g.afcNumber) : Array(10).fill('?'); + + // Build 10x10 grid + const grid: { name: string | null; confirmed: boolean }[][] = []; + for (let row = 0; row < 10; row++) { + grid[row] = []; + for (let col = 0; col < 10; col++) { + const sq = squares.find((s) => s.position === `${row}${col}`); + const rawName = sq?.user?.name || sq?.guestName || null; + // First name only to fit in cell + const name = rawName ? rawName.split(' ')[0] : null; + grid[row][col] = { name, confirmed: sq?.confirmed ?? false }; + } + } + + const nfcTeam = settings?.nfcTeam || 'NFC'; + const afcTeam = settings?.afcTeam || 'AFC'; + const nfcLogo = settings?.nfcLogo || null; + const afcLogo = settings?.afcLogo || null; + const sbLogo = settings?.sbLogo || null; + const eventName = settings?.eventName || 'Super Bowl Squares'; + const eventDate = settings?.eventDate || ''; + + const CELL = 62; // px per cell + const NUM_CELL = 28; // px for number header/side column + const LOGO = 48; + + return ( + <> + + + +