From eb986581e4d499d8d07d7f7482a7f1a8ac288faf Mon Sep 17 00:00:00 2001 From: jessikitty Date: Mon, 7 Sep 2026 11:18:13 +1000 Subject: [PATCH] Printing Debug --- README.md | 8 ++++++ public/css/admin.css | 15 ++++++++++ public/js/admin.js | 67 ++++++++++++++++++++++++++++++++++++-------- src/printer.js | 26 +++++++++-------- src/routes/admin.js | 32 ++++++++++++++++++++- 5 files changed, 123 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 85fb5a1..e98d84c 100644 --- a/README.md +++ b/README.md @@ -212,6 +212,14 @@ it is laid out along the length and turned before printing, which is what you wa hangs from its short edge. Set it per site and check the bitmap preview — the two look very different and only one will suit how you hang them. +**Label stock** at the top of the badge settings is a shortcut that fills in the size, the roll +type and the colour option. It is not itself a saved setting — the three fields it fills are what +get stored, which is why it can appear to "revert" when the same dimensions describe two rolls. + +**Diagnostics.** The site card has a **Diagnostics** button showing exactly what would be sent, +including the `brother_ql` command line, so it can be run by hand on the host. A failed test print +shows the printer's own words rather than a summary. + **If the printer cannot be reached**, sign in still completes. The kiosk falls back to its own browser print dialog, and the failure is shown against the site in **Admin → Sites** with the reason. Admins can reprint any badge from the **On site** list. diff --git a/public/css/admin.css b/public/css/admin.css index 1cbdab1..80f45b9 100644 --- a/public/css/admin.css +++ b/public/css/admin.css @@ -400,3 +400,18 @@ tr.row-bad td { background: #fdf0f2; } vertical-align: -2px; margin-right: 2px; } + +pre.raw { + margin: 0 0 14px; + padding: 12px; + background: var(--paper); + border: 1px solid var(--rule); + border-radius: 3px; + font-family: ui-monospace, Menlo, Consolas, monospace; + font-size: 12.5px; + line-height: 1.45; + white-space: pre-wrap; + word-break: break-word; + max-height: 320px; + overflow: auto; +} diff --git a/public/js/admin.js b/public/js/admin.js index 08105ba..92bd151 100644 --- a/public/js/admin.js +++ b/public/js/admin.js @@ -689,6 +689,7 @@ async function loadSites() { ${s.printer.enabled ? `` : ''} + ${s.printer.enabled ? `` : ''}
@@ -740,6 +741,17 @@ async function loadSites() { window.open(`/admin/api/sites/${btn.dataset.bitmap}/badge-bitmap`, '_blank') ) ); + $$('[data-print-diag]').forEach((btn) => + btn.addEventListener('click', async () => { + const d = await api(`/sites/${btn.dataset.printDiag}/printer-diagnostics`); + openModal( + 'Printer diagnostics', + `
${esc(JSON.stringify(d, null, 2))}
`, + null, + { saveLabel: 'Close', hideCancel: true } + ); + }) + ); $$('[data-test-print]').forEach((btn) => btn.addEventListener('click', async () => { btn.disabled = true; @@ -748,7 +760,17 @@ async function loadSites() { await api(`/sites/${btn.dataset.testPrint}/test-print`, { method: 'POST' }); toast('Sent to the printer.'); } catch (err) { - toast(err.message, true); + // Show the printer's own words as well as the summary, because a refusal + // usually names the reason and the summary cannot cover every case. + const raw = err.payload?.raw; + openModal( + 'The printer refused the job', + `

${esc(err.message)}

+ ${err.payload?.command ? `

Command:

${esc(err.payload.command)}
` : ''} + ${raw ? `

What the printer and brother_ql said:

${esc(raw)}
` : ''}`, + null, + { saveLabel: 'Close', hideCancel: true } + ); } finally { btn.disabled = false; btn.textContent = 'Test print'; @@ -764,16 +786,29 @@ async function loadSites() { * 62 mm is for a different printer. */ const LABEL_PRESETS = [ - { id: 'dk22205-90', label: 'Brother DK-22205 continuous, cut at 90 mm', w: 62, h: 90, photo: true }, - { id: 'dk11202', label: 'Brother DK-11202 die-cut 62 × 100 mm', w: 62, h: 100, photo: true }, - { id: 'dk22251-90', label: 'Brother DK-22251 black/red continuous, cut at 90 mm', w: 62, h: 90, photo: true, accent: true }, - { id: 'dk11208', label: 'Brother DK-11208 die-cut 38 × 90 mm', w: 38, h: 90, photo: false }, - { id: 'dk11209', label: 'Brother DK-11209 die-cut 29 × 62 mm', w: 29, h: 62, photo: false }, - { id: 'dk11201', label: 'Brother DK-11201 die-cut 29 × 90 mm', w: 29, h: 90, photo: false }, - { id: 'card', label: 'Card size 86 × 54 mm (not a QL-820NWB size)', w: 86, h: 54, photo: true }, - { id: 'dymo99014', label: 'Dymo 99014 101 × 54 mm', w: 101, h: 54, photo: true }, + { id: 'dk22205-90', label: 'Brother DK-22205 continuous, cut at 90 mm', w: 62, h: 90, photo: true, roll: '62' }, + { id: 'dk11202', label: 'Brother DK-11202 die-cut 62 × 100 mm', w: 62, h: 100, photo: true, roll: '62' }, + { id: 'dk22251-90', label: 'Brother DK-22251 black/red continuous, cut at 90 mm', w: 62, h: 90, photo: true, roll: '62red', accent: true }, + { id: 'dk11208', label: 'Brother DK-11208 die-cut 38 × 90 mm', w: 38, h: 90, photo: false, roll: '62' }, + { id: 'dk11209', label: 'Brother DK-11209 die-cut 29 × 62 mm', w: 29, h: 62, photo: false, roll: '62' }, + { id: 'dk11201', label: 'Brother DK-11201 die-cut 29 × 90 mm', w: 29, h: 90, photo: false, roll: '62' }, + { id: 'card', label: 'Card size 86 × 54 mm (not a QL-820NWB size)', w: 86, h: 54, photo: true, roll: '62' }, + { id: 'dymo99014', label: 'Dymo 99014 101 × 54 mm', w: 101, h: 54, photo: true, roll: '62' }, ]; +/** + * Which preset the saved settings correspond to. Dimensions alone are ambiguous — + * DK-22205 and DK-22251 are both 62 x 90 — so the roll type decides between them. + */ +function matchingPreset(site) { + return LABEL_PRESETS.find( + (p) => + Number(site.badge.widthMm) === p.w && + Number(site.badge.heightMm) === p.h && + p.roll === (site.printer.label === '62red' ? '62red' : '62') + ); +} + function openSiteModal(site) { openModal( `Edit ${site.name}`, @@ -787,11 +822,11 @@ function openSiteModal(site) { ${LABEL_PRESETS.map( (p) => - `` + `` ).join('')} +

A shortcut that fills in the boxes below. The size, the roll type and the + colour option are what actually get saved.