Commit Graph

37 Commits

Author SHA1 Message Date
gsinghpal
719853c251 fix(fusion_clock): leave From/To use CSS grid so they don't stack on iOS
iOS Safari date inputs have a large intrinsic min-width that can break a flex
row; switch .fclk-leave-daterange to grid 1fr 1fr + min-width:0 on the inputs
so the two fields always share the row and shrink. Also changes the bundle hash
to force iOS to drop the cached CSS. Live on entech 19.0.3.13.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 23:07:32 -04:00
gsinghpal
6a9c7c74ea feat(fusion_clock): multi-day leave requests (date range)
Request Leave now takes a From/To date range instead of a single day (the To
field is optional -> single-day). Added date_to to fusion.clock.leave.request
(start kept as leave_date), with overlap detection on submit and a date_to >=
leave_date constraint. The absence check and reports now treat a leave as
covering its whole span. The form shows two date inputs; the controller accepts
date_from/date_to (the old single leave_date payload is still honoured). A
migration backfills date_to = leave_date for existing rows.

Live and verified on entech 19.0.3.13.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 23:01:19 -04:00
gsinghpal
360370db15 fix(fusion_clock): kill portal white border — neutralise .o_fp_portal_shell
Verified from the live DOM that fusion_plating_portal wraps the app in
#wrapwrap > main > .o_fp_portal_shell > .o_fp_portal_main > #wrap.o_portal_wrap
> .container. The white frame was .o_fp_portal_shell (+ .container max-width),
which my earlier wrapper-neutralisation didn't target. Add the shell + inner
main + force all wrappers transparent/full-width/no-padding under
body:has(.fclk-app). Live on entech 19.0.3.12.4.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 22:46:19 -04:00
gsinghpal
85bbd8a20e fix(portal): recover full-bleed wrapper fix + Schedule Payslips tab
These round-2 portal fixes (white-border wrapper neutralisation in
portal_clock.css, and the Payslips nav tab on the fusion_planning Schedule
page) were briefly bundled into a concurrent NFC commit that a parallel session
then rebased, dropping them from main. They are deployed and verified on entech
(fusion_clock 3.12.3 / fusion_planning 1.3.0); re-committing so git matches.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 22:36:38 -04:00
gsinghpal
a479052b72 fix(fusion_clock): portal white-border + responsive timesheet entries
- White border on every portal page: the .fclk-app full-bleed relied on exact
  negative margins to cancel the portal layout's container padding; when it
  didn't match, the white page chrome showed through. Match the PAGE background
  to the app (light #f3f4f6 / dark #0f1117, via body:has(.fclk-app)) so the
  gutter is invisible, and clip horizontal overflow.
- Timesheets not responsive: the 6-column table crammed/wrapped on phones.
  Replaced the table with stacked cards (date + net up top, in -> out, then
  break / location / Correct) that read cleanly at any width. Correction-link
  data attributes preserved; the xpath-inherited .fclk-nav-bar untouched.

Live on entech 19.0.3.12.2 (both rules verified in the served frontend bundle).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 22:20:56 -04:00
gsinghpal
2aaa1a57e7 feat(fusion_clock): schedule-driven attendance automation
Reminders, absence detection, late/early penalties, and auto-clock-out are now
driven by each employee's real schedule (posted planner entry -> recurring
shift), never the global 9-5 default. Employees who aren't scheduled get no
reminders/absence. Overtime past the scheduled end is never cut off — auto
clock-out only fires at a max-shift safety cap (default raised 12 -> 16h). Team
leads build the planner in draft and Post it (publishes + emails employees).

- hr.employee._get_fclk_day_plan: explicit `scheduled` flag; posted-only planner
  entries (drafts ignored), else recurring shift covering that weekday, else
  not-scheduled; sources 'schedule'/'shift'/'none'.
- fusion.clock.shift: day_mon..day_sun weekday pattern + covers_weekday().
- fusion.clock.schedule: draft/posted state + posted_date; planner edits reset
  to draft; fclk_email_posted_week notification.
- Rewrote the reminder / absence / auto-clock-out crons: schedule-gated,
  per-employee savepoints, OT-aware cap, weekend hardcode removed.
- Penalties + all three clock-in paths skip days the employee isn't scheduled.
- shift_planner: Post Week route + planner Post button + draft count.
- Migration backfills pre-existing schedule entries to 'posted' so they keep
  driving automation after upgrade.
- Tests: resolver matrix, cron gating, OT cap; fixed the existing planner test
  for the new state/source semantics.

Design: docs/superpowers/specs/2026-05-30-schedule-driven-attendance-design.md
Frontend footprint kept at zero to avoid colliding with the concurrent
employee-portal (payslips) work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 21:54:05 -04:00
gsinghpal
95abd2e337 style(fusion_clock): payslip list/detail, 4-item nav, and sign-out styles
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 21:38:22 -04:00
gsinghpal
2c8ad83d43 fix(fusion_clock): NFC clock-out shows gross worked time, not net-of-penalty
The result card showed x_fclk_net_hours = worked_hours − break − early-out
penalty minutes. Tapping out before the scheduled end adds a 15-min early-out
penalty to the break field, so short shifts clamped to 0 → "Worked 0h 0m".

Show GROSS attendance.worked_hours (the actual clock-in → clock-out elapsed
time) instead, and format adaptively (Xh Ym / Ym / Ys) so brief shifts and
quick tests don't all read 0. Net-of-deductions stays in the payroll reports.

Live as 19.0.3.11.5 (verified worked_hours computes correctly in the DB).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 18:59:10 -04:00
gsinghpal
3fd074ff6d fix(fusion_clock): kiosk photo now shows on clock + profile (right image fields)
Root-caused on live entech (not guessed):
- The kiosk runs as a non-HR operator (uid 141) who gets AccessError reading
  hr.employee images, so /web/image served a placeholder. Point the result-card
  avatar at hr.employee.public/avatar_128 — verified readable as the operator,
  returns the real photo. (Odoo's own UI uses .public for employee images.)
- The Odoo profile/preferences avatar is res.users → res.partner.image_1920,
  which the capture never wrote. Propagate the captured photo to the linked
  user's partner image so the profile updates too.
- Enlarge the capture oval (it was small): stage 62vh/520px, guide width 64%.

Live as 19.0.3.11.4. Also backfilled the existing test photo to the user's
partner image so the profile shows it without re-capturing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 18:29:46 -04:00
gsinghpal
e26a7cd9e8 feat(fusion_clock): NFC photo capture — 10s auto-capture, vertical oval, cache-busted avatar
- Profile photo DID save (verified: image_1920 attachment persists); the
  "doesn't update" was a browser-cache miss. Add ?unique=<write_date> to the
  result-card avatar URL so a freshly-captured photo shows on clock in/out.
- Capture now starts a 10-second countdown (time to get into frame) then
  auto-snaps; the button toggles to Cancel while counting.
- Face guide is now a VERTICAL oval (aspect-ratio 3/4) over a portrait stage —
  it was rendering horizontal. Faces are taller than wide.

Deployed live to entech (LXC 111) as 19.0.3.11.3; frontend bundle verified to
compile clean and contain the new rules.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 18:14:19 -04:00
gsinghpal
09cea73e50 fix(fusion_clock): SCSS compile error — replace CSS min() with width+max-width
Odoo's Sass compiler evaluates the built-in min() function and errors with
"Incompatible units: 'px' and 'vw'" on `width: min(86vw, 380px)`, which broke
the entire web.assets_frontend bundle (kiosk + all portal pages unstyled).
Equivalent, compiler-safe: `width: 86vw; max-width: 380px;`.

Verified: forced a fresh frontend bundle compile on entech — no Incompatible
-units error, served CSS contains the compiled --pin rule. Live as 19.0.3.11.2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 17:48:29 -04:00
gsinghpal
3235d4ceca fix(fusion_clock): un-squeeze the NFC kiosk Manager PIN pad on tablets
The --pin panel used width:auto, so in the centred flex overlay it
collapsed to its content width and crushed the 3-column numpad. Give it
a definite width (min(86vw, 380px)) and make the keys proper tappable
squares (min-height 60px, 1.6rem font).

Deployed live to entech (LXC 111) as 19.0.3.11.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 17:43:54 -04:00
gsinghpal
5a488ae86e 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>
2026-05-30 17:38:34 -04:00
gsinghpal
55898dd1d4 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>
2026-05-30 17:21:33 -04:00
gsinghpal
005daade55 changes 2026-05-23 07:53:41 -04:00
gsinghpal
00981a502a feat(acr-wedge+kiosk): SSE bridge for ACR122U / PC-SC readers
macOS keystroke injection from a CLI-launched Python hits multiple
TCC permission walls (Accessibility AND Automation, both attaching
to identities macOS often can't resolve cleanly). After bouncing
through Quartz, AppleScript, and pyautogui fallbacks, none of them
worked reliably in our test environment.

Switch to a proper IPC channel instead of pretending to be a
keyboard.

Daemon (wedge.py):
  - Adds a ThreadingHTTPServer on 127.0.0.1:8765 exposing /events
  - SSE stream pushes each detected UID as one event
  - 30s keep-alive comments to keep idle connections open
  - CORS: Access-Control-Allow-Origin: * (kiosk page may be on any
    client-domain HTTPS origin; SSE source is always localhost)
  - Keystroke injection kept as best-effort fallback for non-SSE
    clients

Kiosk JS (fusion_clock_nfc_kiosk.js):
  - Adds startWedgeSseListener() that opens EventSource to
    http://localhost:8765/events on setup
  - On message: same handleTap()/_onEnrollTap() flow as Web NFC + HID
  - EventSource auto-reconnects; first error is logged then silenced
  - http://localhost is a "potentially trustworthy origin" so this
    works from https:// pages without mixed-content blocking

Result: ACR122U + wedge.py daemon now drives the kiosk with zero
macOS permission prompts and no focused-window dependency. Same
input plumbing as Web NFC and HID — penalty/photo/activity log
fire identically.

Bump fusion_clock to 19.0.3.3.0.
2026-05-15 20:10:40 -04:00
gsinghpal
9001b6fc51 feat(fusion_clock): USB HID reader support + desktop-tolerant kiosk setup
The NFC kiosk previously required Web NFC, which is Android-Chrome-only.
This blocked desktop testing and locked us to a single hardware path.

Add a keyboard-wedge listener that captures keystrokes from USB HID NFC
readers (the standard Sycreader/Yanzeo class). The listener buffers hex
chars + separators, flushes on Enter (or 600ms idle as fallback for
readers without a terminator), and routes the UID through the same
handleTap()/_onEnrollTap() codepath as Web NFC. Photo verification,
penalty calc, and activity logging all fire identically.

Make the setup button tolerant: try Web NFC, but treat its absence as
non-fatal. USB HID always activates. Only hard-fail when photoRequired
is True AND the camera is unavailable.

Result: same kiosk page now works on Android Chrome (Web NFC), desktop
Chrome with a USB reader, or both at once.

Bump manifest to 19.0.3.2.0.
2026-05-15 19:30:51 -04:00
gsinghpal
1414ef2c1c fix(fusion_clock): NFC kiosk header — center logo at top, stack clock+date below 2026-05-14 08:42:26 -04:00
gsinghpal
42e8fe3d21 fix(fusion_clock): NFC kiosk — subtle logo glass, centered clock, AM/PM 2026-05-14 08:38:25 -04:00
gsinghpal
bad73fcea8 fix(fusion_clock): NFC kiosk visual polish — bigger chip, uncut waves, logo glass pill, no clock collision 2026-05-14 08:29:33 -04:00
gsinghpal
94249ba67d feat(fusion_clock): premium glass NFC kiosk + scope CSS to kiosk page
Visual rewrite of the NFC kiosk page:
- Animated mesh gradient background (drifts on a 28s loop)
- Glass-panel state cards with backdrop-filter blur
- Animated SVG NFC icon (concentric waves emanate from a chip)
- Company logo pulled from res.company.logo, displayed in header
- Dominant-hue extraction from logo sets --nfc-h CSS var; entire
  palette interpolates from that one HSL hue
- Success burst (green glow + scale), error shake, smooth state fades
- Reduced-motion fallback respects prefers-reduced-motion
- Glass numpad + employee picker in Enroll Mode

CRITICAL FIX: scoped all kiosk styles under :has(#nfc_kiosk_root) so
they no longer leak into other frontend pages. Previous version applied
html/body overflow:hidden + display:none on header/footer globally,
breaking website scrolling and chrome on every frontend page.
2026-05-14 08:22:47 -04:00
gsinghpal
2abd859a29 feat(fusion_clock): NFC kiosk Wake Lock to keep screen on while active 2026-05-14 08:09:27 -04:00
gsinghpal
98cb42d2e5 feat(fusion_clock): NFC kiosk on-screen debug overlay + clearer settings label 2026-05-14 08:03:47 -04:00
gsinghpal
878d05685c fix(fusion_clock): split min(80vw,700px) into width+max-width to avoid Sass unit error 2026-05-14 07:23:49 -04:00
gsinghpal
06c49ecec6 feat(fusion_clock): NFC kiosk mock-tap debug shortcut
Add Ctrl+Shift+T keyboard shortcut (guarded by debugEnabled / nfc_kiosk_debug
setting) that prompts for a UID and fires _onEnrollTap or handleTap depending
on currentState (ENROLL vs IDLE). Persists last-used UID in localStorage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 01:30:35 -04:00
gsinghpal
37deaedf0d feat(fusion_clock): NFC kiosk Enroll Mode UI
Replaces the Task 18 stub renderEnroll with the full four-phase
implementation (password numpad → employee picker → tap-to-enroll →
result), adds _onEnrollTap wired to the NFC reading event, and exposes
it via window.__nfcKiosk.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 01:28:36 -04:00
gsinghpal
30f7f18472 feat(fusion_clock): camera capture on NFC kiosk
Replace camera stub with real getUserMedia + canvas capture. Setup button
now starts NFC reader and camera together; camera failure is non-fatal when
photo is not required.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 01:25:12 -04:00
gsinghpal
66e9749853 feat(fusion_clock): Web NFC integration on kiosk page
Adds NDEFReader scan loop, onNfcReading tap dispatcher, handleTap
state machine, postJson helper, capturePhoto stub (Task 17), and
setup wizard activation with error display.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 01:22:55 -04:00
gsinghpal
c9be68a575 feat(fusion_clock): NFC kiosk JS scaffold + state machine + clock display
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 01:20:31 -04:00
gsinghpal
0351dcd497 feat(fusion_clock): NFC kiosk SCSS (always-dark, high-contrast)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 01:14:14 -04:00
gsinghpal
595dccc17d changes 2026-03-17 13:32:08 -04:00
gsinghpal
e56974d46f update 2026-03-16 08:14:56 -04:00
gsinghpal
acd3fc455e changes 2026-03-09 15:21:22 -04:00
gsinghpal
a3e85a23ef changes 2026-03-01 14:42:49 -05:00
gsinghpal
b925766966 changes 2026-02-27 14:32:32 -05:00
gsinghpal
e8e554de95 changes 2026-02-23 00:32:20 -05:00
gsinghpal
5200d5baf0 Initial commit 2026-02-22 01:22:18 -05:00