feat(fusion_clock): always-available kiosk photo action + compact manager PIN pad

NFC kiosk:
- Add "📷 Photo" action to every Manage-page employee row and to the
  post-enroll result card, so a manager can set/replace a profile photo
  at any time (previously only surfaced when the employee had no image).
- Slim the Manager PIN pad: dedicated --pin panel variant (max-width 360px,
  reduced padding) with a tighter numpad, removing the oversized whitespace.

Deployed live to entech (LXC 111) as 19.0.3.11.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-30 17:38:34 -04:00
parent 55898dd1d4
commit 5a488ae86e
3 changed files with 14 additions and 8 deletions

View File

@@ -5,7 +5,7 @@
{
'name': 'Fusion Clock',
'version': '19.0.3.10.0',
'version': '19.0.3.11.0',
'category': 'Human Resources/Attendances',
'summary': 'Complete Employee T&A with Geofencing, Shifts, Penalties, Overtime, Kiosk, Dashboard & Payroll Export',
'description': """

View File

@@ -306,7 +306,7 @@
let pin = "";
stateContainer.innerHTML = `
<div class="nfc-kiosk__enroll-overlay">
<div class="nfc-kiosk__enroll-panel">
<div class="nfc-kiosk__enroll-panel nfc-kiosk__enroll-panel--pin">
<h2>${escapeHtml(opts.title || "Enter PIN")}</h2>
<div class="pin-display" id="nfc_pin_display"></div>
<div class="numpad">
@@ -415,6 +415,7 @@
? `<button class="m-btn m-danger" data-act="delok" data-id="${e.id}">Confirm delete</button>
<button class="m-btn" data-act="delno" data-id="${e.id}">Cancel</button>`
: `<button class="m-btn" data-act="assign" data-id="${e.id}" data-name="${escapeHtml(e.name)}">${e.card_uid ? "Re-tag" : "Assign"}</button>
<button class="m-btn" data-act="photo" data-id="${e.id}" data-name="${escapeHtml(e.name)}">📷 Photo</button>
${e.card_uid ? `<button class="m-btn" data-act="clear" data-id="${e.id}">Clear tag</button>` : ""}
<button class="m-btn m-danger" data-act="del" data-id="${e.id}">Delete</button>`;
return `<div class="manager-row">
@@ -430,6 +431,8 @@
enrollSelectedEmployee = { id, name: btn.dataset.name };
pendingEnrollUid = null;
renderEnroll({ phase: "tap" });
} else if (act === "photo") {
openPhotoCapture(id, btn.dataset.name, () => renderEnroll({ phase: "manager" }));
} else if (act === "clear") {
try { await postJson("/fusion_clock/kiosk/nfc/clear_tag", { employee_id: id, enroll_password: enrollPassword }); } catch (e) {}
refresh();
@@ -566,14 +569,14 @@
<div class="nfc-kiosk__enroll-panel" style="text-align:center">
<h2 style="color:${ok ? "var(--nfc-success)" : "var(--nfc-error)"}">${msg}</h2>
<div class="actions" style="justify-content:center">
${ok && payload.needs_photo && payload.employee_id ? `<button class="confirm" id="enroll_photo">📷 Take photo</button>` : ""}
${ok && payload.employee_id ? `<button class="confirm" id="enroll_photo">📷 Take photo</button>` : ""}
<button class="confirm" id="enroll_another">Enroll another</button>
<button class="cancel" id="enroll_done">Done</button>
</div>
</div>
</div>
`;
if (ok && payload.needs_photo && payload.employee_id) {
if (ok && payload.employee_id) {
document.getElementById("enroll_photo").addEventListener("click", () => {
openPhotoCapture(payload.employee_id, payload.employee_name, () => {
if (enrollPassword) renderEnroll({ phase: "manager" }); else exitEnrollMode();

View File

@@ -512,6 +512,9 @@ html:has(#nfc_kiosk_root) {
max-height: 92vh;
overflow-y: auto;
// Compact PIN-pad variant — narrower + tighter than the wide list panels
&--pin { width: auto; max-width: 360px; padding: 1.5rem 1.5rem 1.25rem; }
h2 {
font-size: 1.5rem;
margin: 0 0 1.5rem;
@@ -522,12 +525,12 @@ html:has(#nfc_kiosk_root) {
.numpad {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.75rem;
margin: 1rem 0;
gap: 0.5rem;
margin: 0.5rem 0 0.75rem;
button {
font-size: 1.7rem;
padding: 1.1rem 0;
font-size: 1.45rem;
padding: 0.7rem 0;
background: rgba(255,255,255,0.05);
color: var(--nfc-text);
border: 1px solid rgba(255,255,255,0.1);