Visitor sign in kiosk: multi-site, badge printing, WWCC expiry warnings, admin accounts with 2FA

This commit is contained in:
2026-09-03 14:47:59 +10:00
parent e6731dbdfa
commit c3ce46913d
2 changed files with 11 additions and 21 deletions
+4 -3
View File
@@ -29,9 +29,10 @@ two factor.
Sign out only needs a **last name** plus a **mobile number or email**, which works for both.
The company field is optional and clearly marked as such — plenty of visitors are not from
anywhere in particular. When it is filled in it appears on the badge, in the on-site list, on the
evacuation sheet, and in the visit log, and the log search matches on it, so you can pull up every
visit from one contractor.
anywhere in particular. When it is filled in it appears in the on-site list, on the evacuation
sheet, and in the visit log, and the log search matches on it, so you can pull up every visit from
one contractor. It is **not** printed on the badge — the label stays name, host, time and check
status.
## Quick start
+7 -18
View File
@@ -97,12 +97,8 @@ export function badgeHtml(visit, site, { autoPrint = true, photoUrl = null } = {
const pad = unit * 0.07;
const nameSize = Math.max(3.2, unit * (portrait ? 0.105 : 0.115));
const bodySize = Math.max(2.0, unit * (portrait ? 0.055 : 0.062));
// Square, matching the crop taken at the kiosk. A company adds a line under the
// name, so on a portrait badge the photo gives that line back rather than
// letting a long name get clipped at the bottom edge.
const roomy = height >= width * 1.5;
const portraitPhoto = visit.company && !roomy ? 0.44 : 0.52;
const photoWidth = portrait ? unit * portraitPhoto : unit * 0.5;
// Square, matching the crop taken at the kiosk.
const photoWidth = portrait ? unit * 0.52 : unit * 0.5;
// Red only appears on a two-colour roll (DK-22251 on the QL-820NWB). Anywhere
// else it prints as grey, so it is off unless the site opts in.
@@ -181,21 +177,15 @@ export function badgeHtml(visit, site, { autoPrint = true, photoUrl = null } = {
letter-spacing: -0.01em;
overflow-wrap: anywhere;
}
.org {
font-size: ${bodySize * 0.95}mm;
line-height: 1.2;
margin-top: ${unit * 0.02}mm;
overflow-wrap: anywhere;
}
.rows {
/* Portrait badges centre the whole block; wider ones push the detail rows to
the bottom edge, which is where the eye expects them beside a photo. */
margin-top: ${portrait ? `${unit * 0.045}mm` : 'auto'};
padding-top: ${unit * 0.035}mm;
margin-top: ${portrait ? `${unit * 0.05}mm` : 'auto'};
padding-top: ${unit * 0.04}mm;
font-size: ${bodySize}mm;
/* Tightened from 1.35 to make room for the company line without shrinking
the photo, which is the thing people actually look at. */
line-height: 1.28;
/* 1.3 rather than 1.35: on a 62 x 90 mm label a two-line name plus a custom
footer line leaves very little room, and the difference is not visible. */
line-height: 1.3;
}
.rows b { font-weight: 700; }
.note { font-size: ${bodySize * 0.85}mm; margin-top: ${unit * 0.025}mm; }
@@ -219,7 +209,6 @@ export function badgeHtml(visit, site, { autoPrint = true, photoUrl = null } = {
<div class="body">
<div class="site">${esc(site.name)} &middot; Visitor</div>
<div class="name">${esc(visit.first_name)} ${esc(visit.last_name)}</div>
${visit.company ? `<div class="org">${esc(visit.company)}</div>` : ''}
${details}
</div>
</div>