diff --git a/fusion_clock/docs/superpowers/specs/2026-05-31-pin-kiosk-design.md b/fusion_clock/docs/superpowers/specs/2026-05-31-pin-kiosk-design.md new file mode 100644 index 00000000..c00ec01b --- /dev/null +++ b/fusion_clock/docs/superpowers/specs/2026-05-31-pin-kiosk-design.md @@ -0,0 +1,85 @@ +# Fusion Clock — PIN Kiosk Design + +**Date:** 2026-05-31 +**Module:** `fusion_clock` +**Status:** Approved (brainstorming) — ready for implementation plan + +--- + +## 1. Problem / goal + +The module has a premium **NFC kiosk** but only a bare-Bootstrap **classic PIN kiosk** (`/fusion_clock/kiosk`) with no logo, gradient, or polish, reachable only by direct URL. We want a properly designed, **opt-in PIN kiosk** as an additional feature for clients who want simple PIN entry instead of NFC — matching the NFC kiosk's look and quality. This makes the existing `enable_kiosk` setting meaningful (it becomes this feature's on/off). + +## 2. Decisions (from brainstorming) + +- **Flow:** photo-tile grid (with search) → tap your tile → enter PIN → clock. (Not PIN-as-identifier; not name-search.) +- **PIN always required.** Drop the `kiosk_pin_required` toggle. +- **Set PIN on first use:** an employee with no PIN is walked through creating + confirming one on first tap. +- **Selfie respects the global Photo Verification master toggle** (`enable_photo_verification`): ON → guided selfie after PIN; OFF → none. +- **Style:** always-dark glass + brand-hue mesh gradient, logo pill, live clock/date — matching the NFC kiosk. + +## 3. Design + +### 3.1 Screens / flow +1. **Grid** (`#pin_kiosk_root`): company logo pill (top-centre), live clock + date, a search box, and a responsive grid of employee **photo tiles** (avatar + name) over the animated brand-tinted mesh gradient. Bottom chrome: location pill (left), operator ⚙ + lock (right). +2. **Tap a tile** → **PIN pad**: glass panel with the person's avatar + name, masked PIN dots, a big touch numpad (`⌫ 0 ✓`), Cancel. + - If the employee **has no PIN** → **first-use setup**: "Create a PIN" → enter → "Re-enter to confirm" → saved, then proceed to clock. + - Wrong PIN → shake + clear + retry (max 3 attempts, then back to grid). +3. **Selfie** (only if `enable_photo_verification` master is ON) → guided capture with the oval face-guide + countdown (reuse the NFC kiosk's capture). +4. **Result**: green-glow success card (avatar, name, "Clocked In/Out", time · location), auto-returns to the grid after ~3 s. Error → red shake card. + +### 3.2 Style +New `static/src/scss/pin_kiosk.scss`, **scoped to `:has(#pin_kiosk_root)`** (never leaks to other pages — same discipline as `nfc_kiosk.scss`). Mirrors the NFC kiosk's tokens/patterns: dark page, animated mesh `::before`, vignette `::after`, frosted logo pill, clock/date, `%glass` panels, numpad, result card (success/error), photo panel + oval guide, reduced-motion fallback. Brand hue in its **own** CSS var `--pk-h` (don't collide with `--nfc-h`); deliberately a parallel file, not shared, to avoid coupling the two kiosks. + +### 3.3 Backend — rework `controllers/clock_kiosk.py` +All routes `auth='user'`, gated by `_is_kiosk_operator` (Clock Manager **or** Kiosk Operator group — unchanged). Page additionally gated by `enable_kiosk`. + +- **`GET /fusion_clock/kiosk`** — render the new template. Context: `company_name`, `company_logo_url` (for display + hue extraction), `location_name`, `sounds_enabled`, `photo_required` (= `enable_photo_verification` master). Redirect to `/my` if `enable_kiosk` off or not an operator (as today). +- **`POST /fusion_clock/kiosk/search`** (extend the existing — keep the name; the NFC kiosk's `employee_search` delegates to it) — add `avatar_url` (via `hr.employee.public`, `?unique=write_date` cache-buster) and `has_pin` (bool) to each row, alongside the current `id/name/department/is_checked_in/card_uid`. +- **`POST /fusion_clock/kiosk/verify_pin`** (rework) — if `not employee.x_fclk_kiosk_pin` → `{'needs_setup': True}`; else compare and return `{'success': True, ...}` or `{'error': 'invalid_pin'}`. +- **`POST /fusion_clock/kiosk/set_pin`** (NEW) — first-use: validate a 4-digit numeric PIN, reject if the employee already has one (`already_set`), else write `x_fclk_kiosk_pin` (sudo) and return success. +- **`POST /fusion_clock/kiosk/clock`** (rework) — accept `photo_b64`. Use the **configured kiosk location** (`company.x_fclk_nfc_kiosk_location_id`) — a fixed wall device, so NO per-clock GPS geofence (matches the NFC kiosk); return `no_location_configured` if unset. Clock via `_attendance_action_change`; write source `'kiosk'`, location, logs, penalties (as today). If `enable_photo_verification` master ON and `photo_b64` present → store on `x_fclk_check_in_photo` (in) / `x_fclk_check_out_photo` (out), stripping the data-URL prefix. Unscheduled-day → `unscheduled_shift` log (as today). Module-level tap debounce (~5 s, like NFC). + +### 3.4 Frontend — rewrite `static/src/js/fusion_clock_kiosk.js` +Rebuild as a proper Odoo-19 **Interaction** (`@web/public/interaction`, registered in `registry.category("public.interactions")`) — not the old IIFE. State machine: `grid → pin | setup → (photo) → result → grid`. Reuse the NFC kiosk's **dominant-hue extraction** (set `--pk-h` from the logo) and **guided photo capture** (camera + oval guide + countdown) — replicate those helpers cleanly in this file (parallel, not imported, to keep the two kiosks decoupled). Search filters the tile grid client-side; tile tap loads the PIN/setup panel; numpad drives the dots; success plays a sound if `sounds_enabled`. + +### 3.5 Template — rebuild `views/kiosk_templates.xml` +Root `