feat(fusion_clock): NFC kiosk — enrollment, manager page, sounds, lock, profile photos
Kiosk work across this session (19.0.3.6.0 -> 19.0.3.10.0): - Program-from-unknown-tap: amber prompt -> Manager PIN -> pick/create employee -> binds the captured UID (no re-tap). Reassign moves a card between employees. - Manager page (gear, when unlocked): search employees + tag status; assign/re-tag, clear tag, archive employee, + new employee. Server-gated by the enroll password. - Screen lock: kiosk starts locked (tap-only); Unlock -> Manager PIN, Lock button; PIN remembered for the session so the gear never re-prompts. - Sounds: pleasant + loud sine chimes (rising in / descending out) + a low "denied" tone for wrong/unknown taps. Gated by fusion_clock.enable_sounds. - Guided profile-photo capture for employees with no picture (clock-in or enroll): live camera + oval face guide -> capture -> preview -> save to hr.employee. - PIN no longer re-renders per digit; centered result card; 12h time; clock-out shows "Worked Xh Ym this shift"; modern clock idle icon; faster animations/result timers; session keep-alive so the kiosk login never expires. - New endpoints: create_employee, clear_tag, delete_employee (archive), verify_pin, save_profile_photo; enroll gains force-reassign. - Docs: fusion_clock is now developed in Claude Code (dropped Cursor references). Spec/plan under fusion_clock/docs/superpowers/. Deployed live on entech (odoo-entech / LXC 111 on pve-worker5), v19.0.3.10.0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
# NFC Kiosk — Enrollment UX, PIN fix, Speed, Clock-out Hours
|
||||
|
||||
**Date:** 2026-05-30
|
||||
**Module:** `fusion_clock` (NFC tap kiosk at `/fusion_clock/kiosk/nfc`)
|
||||
**Status:** Approved design, ready for implementation plan.
|
||||
|
||||
## Context
|
||||
|
||||
The NFC kiosk (`static/src/js/fusion_clock_nfc_kiosk.js`, an IIFE state machine) handles
|
||||
tap-to-clock on a wall tablet at the entech client. Four issues to address, all driven by
|
||||
real shop-floor use (lines of 10–20 people).
|
||||
|
||||
**Implementation approach:** extend the existing IIFE in place. A migration to an Odoo 19
|
||||
`Interaction` (per repo CLAUDE.md guidance) is deliberately out of scope — the file is a
|
||||
large, working state machine on a live client device and the four changes here are
|
||||
surgical; a rewrite would be high-risk for no functional gain. Noted deviation.
|
||||
|
||||
## Requirements & Design
|
||||
|
||||
### 1. PIN entry: stop the per-digit full re-render
|
||||
**Problem:** in `renderEnroll(phase:"password")`, every numpad press calls
|
||||
`renderEnroll(...)` which rebuilds the whole panel via `stateContainer.innerHTML = ...` and
|
||||
replays the 400ms `nfc-state-in` entrance animation → the screen visibly "refreshes" on each
|
||||
digit (entry is preserved, but it flickers).
|
||||
**Design:** a reusable PIN-pad component that renders the panel **once**, then on
|
||||
digit/backspace mutates only the masked `.pin-display` text node + an in-memory buffer.
|
||||
No `innerHTML` rebuild, no re-animation. Used by both the ⚙ enroll PIN and the new
|
||||
Manager-PIN step (§2). OK/Cancel callbacks are parameters.
|
||||
|
||||
### 2. Program a tag from an unknown tap
|
||||
**Problem:** an unknown card tap returns `{error:"card_unknown"}` and shows a red error that
|
||||
auto-dismisses. Programming requires the separate ⚙ flow (enter password → search → **re-tap**).
|
||||
**Design:** the tapped UID is already captured, so program *that* card with no re-tap:
|
||||
1. Unknown tap → **amber** "This card isn't programmed yet" panel with **"Program this card"**
|
||||
and **"Cancel"** buttons. Auto-cancel to idle after ~8s of inactivity.
|
||||
2. **"Program this card"** → **Manager PIN** step (reuses §1 component; credential =
|
||||
`fusion_clock.nfc_enroll_password`, currently `1120`; labelled "Manager PIN" in UI).
|
||||
3. **Employee step**: search-and-pick an existing employee **or** "+ New employee" →
|
||||
enter a name → create a minimal `hr.employee`.
|
||||
4. **Assign**: bind the captured UID to that employee → success confirmation.
|
||||
- The ⚙ enroll mode stays as a proactive path, reusing the same fixed PIN component.
|
||||
|
||||
**Backend:**
|
||||
- Reuse `POST /fusion_clock/kiosk/nfc/enroll` (`employee_id`, `card_uid`, `enroll_password`)
|
||||
for the bind. Already manager/Kiosk-Operator + password gated, sudo data ops.
|
||||
- **New endpoint** `POST /fusion_clock/kiosk/nfc/create_employee` (`name`, `enroll_password`):
|
||||
Kiosk-Operator-gated + password-gated; creates `hr.employee` via **sudo** with
|
||||
`name`, `x_fclk_enable_clock=True`, `company_id = request.env.company.id`; returns
|
||||
`{employee_id, employee_name}` (or `{error}`). JS then calls `enroll` with the captured UID.
|
||||
Minimal fields only — department/contract/etc. are completed later in HR.
|
||||
|
||||
### 3. Faster clock-in/out ("Fast")
|
||||
**Problem:** result card lingers 3s (errors 4s) and entrance animations are 0.4–0.7s →
|
||||
slow throughput for long lines.
|
||||
**Design (JS timers):** success result display **3000 → 1800 ms**; error **4000 → 3000 ms**.
|
||||
**Design (SCSS durations):** `nfc-state-in` 400→200ms; `nfc-success-burst` 700→350ms;
|
||||
`nfc-avatar-in` 600→300ms. Ambient idle wave/chip loop unchanged (does not gate throughput).
|
||||
`prefers-reduced-motion` fallback preserved.
|
||||
|
||||
### 4. Clock-out shows shift hours, clearly
|
||||
**Problem:** clock-out shows `${net_hours_today.toFixed(1)}h today` — mislabelled "today",
|
||||
small, and hidden when it rounds to 0.
|
||||
**Design:** on clock-out always show a prominent **"Worked Xh Ym this shift"** computed from
|
||||
`net_hours_today` (the just-closed attendance's net hours = worked − break). Render h+m;
|
||||
show even when 0 (e.g. "Worked 0h 4m this shift"). Backend already returns the value; this is
|
||||
a JS label/format + SCSS prominence change. Clock-in unchanged.
|
||||
|
||||
## Files
|
||||
- `static/src/js/fusion_clock_nfc_kiosk.js` — PIN component; unknown-tap → program flow;
|
||||
create-employee call; result timers; clock-out hours formatting.
|
||||
- `static/src/scss/nfc_kiosk.scss` — animation durations; amber "unknown card" panel +
|
||||
create-employee styles; prominent clock-out hours.
|
||||
- `controllers/clock_nfc_kiosk.py` — new `nfc_create_employee` endpoint.
|
||||
- `__manifest__.py` — version bump (assets changed).
|
||||
|
||||
## Out of scope / non-goals
|
||||
- No migration of the kiosk JS to an `Interaction`.
|
||||
- No new employee fields beyond name/clock-enabled/company at kiosk-create time.
|
||||
- Classic PIN kiosk (`/fusion_clock/kiosk`) untouched (disabled at entech).
|
||||
|
||||
## Test / verify
|
||||
- Local: `pyflakes` the controller; `xmllint`/manifest parse; review the JS by hand
|
||||
(no local Odoo container available this session).
|
||||
- entech: deploy, upgrade, then on the tablet — PIN entry no longer flickers; unknown tap →
|
||||
program (existing + new employee) binds without re-tap; clock-in/out visibly faster;
|
||||
clock-out shows "Worked Xh Ym this shift".
|
||||
Reference in New Issue
Block a user