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. 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 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 anywhere in particular. When it is filled in it appears in the on-site list, on the evacuation
evacuation sheet, and in the visit log, and the log search matches on it, so you can pull up every sheet, and in the visit log, and the log search matches on it, so you can pull up every visit from
visit from one contractor. one contractor. It is **not** printed on the badge — the label stays name, host, time and check
status.
## Quick start ## 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 pad = unit * 0.07;
const nameSize = Math.max(3.2, unit * (portrait ? 0.105 : 0.115)); const nameSize = Math.max(3.2, unit * (portrait ? 0.105 : 0.115));
const bodySize = Math.max(2.0, unit * (portrait ? 0.055 : 0.062)); 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 // Square, matching the crop taken at the kiosk.
// name, so on a portrait badge the photo gives that line back rather than const photoWidth = portrait ? unit * 0.52 : unit * 0.5;
// 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;
// Red only appears on a two-colour roll (DK-22251 on the QL-820NWB). Anywhere // 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. // 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; letter-spacing: -0.01em;
overflow-wrap: anywhere; overflow-wrap: anywhere;
} }
.org {
font-size: ${bodySize * 0.95}mm;
line-height: 1.2;
margin-top: ${unit * 0.02}mm;
overflow-wrap: anywhere;
}
.rows { .rows {
/* Portrait badges centre the whole block; wider ones push the detail rows to /* 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. */ the bottom edge, which is where the eye expects them beside a photo. */
margin-top: ${portrait ? `${unit * 0.045}mm` : 'auto'}; margin-top: ${portrait ? `${unit * 0.05}mm` : 'auto'};
padding-top: ${unit * 0.035}mm; padding-top: ${unit * 0.04}mm;
font-size: ${bodySize}mm; font-size: ${bodySize}mm;
/* Tightened from 1.35 to make room for the company line without shrinking /* 1.3 rather than 1.35: on a 62 x 90 mm label a two-line name plus a custom
the photo, which is the thing people actually look at. */ footer line leaves very little room, and the difference is not visible. */
line-height: 1.28; line-height: 1.3;
} }
.rows b { font-weight: 700; } .rows b { font-weight: 700; }
.note { font-size: ${bodySize * 0.85}mm; margin-top: ${unit * 0.025}mm; } .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="body">
<div class="site">${esc(site.name)} &middot; Visitor</div> <div class="site">${esc(site.name)} &middot; Visitor</div>
<div class="name">${esc(visit.first_name)} ${esc(visit.last_name)}</div> <div class="name">${esc(visit.first_name)} ${esc(visit.last_name)}</div>
${visit.company ? `<div class="org">${esc(visit.company)}</div>` : ''}
${details} ${details}
</div> </div>
</div> </div>