diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/js/services/activity_tracker.js b/fusion_plating/fusion_plating_shopfloor/static/src/js/services/activity_tracker.js new file mode 100644 index 00000000..6bc6ed52 --- /dev/null +++ b/fusion_plating/fusion_plating_shopfloor/static/src/js/services/activity_tracker.js @@ -0,0 +1,73 @@ +/** @odoo-module **/ +// ============================================================================= +// Fusion Plating — Activity Tracker (shared OWL service) +// +// Watches the document for pointer/touch/keydown/visibility events and +// tracks lastActiveAt. FpTabletLock reads getSecondsUntilLock() once per +// second to drive the idle warning + auto-lock transitions. +// +// Threshold reads from ir.config_parameter at service start; refreshes +// every 5 min in case the manager changed it. +// ============================================================================= + +import { rpc } from "@web/core/network/rpc"; +import { registry } from "@web/core/registry"; + +const DEFAULT_IDLE_MIN = 5; +const DEFAULT_WARN_SEC = 30; + +export const fpShopfloorActivityTracker = { + async start() { + let lastActiveAt = Date.now(); + let idleThresholdMs = DEFAULT_IDLE_MIN * 60 * 1000; + let warnThresholdSec = DEFAULT_WARN_SEC; + + async function refreshThreshold() { + try { + const minutes = await rpc("/web/dataset/call_kw", { + model: "ir.config_parameter", + method: "get_param", + args: ["fp.shopfloor.tablet_idle_lock_minutes", String(DEFAULT_IDLE_MIN)], + kwargs: {}, + }); + idleThresholdMs = (parseInt(minutes, 10) || DEFAULT_IDLE_MIN) * 60 * 1000; + const warn = await rpc("/web/dataset/call_kw", { + model: "ir.config_parameter", + method: "get_param", + args: ["fp.shopfloor.tablet_warn_seconds_before_lock", String(DEFAULT_WARN_SEC)], + kwargs: {}, + }); + warnThresholdSec = parseInt(warn, 10) || DEFAULT_WARN_SEC; + } catch (e) { + // keep defaults if RPC fails (e.g. no session yet) + } + } + await refreshThreshold(); + setInterval(refreshThreshold, 5 * 60 * 1000); + + // Activity = explicit user input. Mouse-move alone DOES NOT count + // because something brushing the screen (a stray glove, a tool + // resting on the tablet) could otherwise keep the session alive. + const bump = () => { lastActiveAt = Date.now(); }; + document.addEventListener("pointerdown", bump, { capture: true }); + document.addEventListener("touchstart", bump, { capture: true, passive: true }); + document.addEventListener("keydown", bump, { capture: true }); + document.addEventListener("visibilitychange", () => { + if (document.visibilityState === "visible") bump(); + }); + + return { + bump, + getSecondsUntilLock() { + return Math.max(0, Math.floor((lastActiveAt + idleThresholdMs - Date.now()) / 1000)); + }, + getWarnThresholdSec() { return warnThresholdSec; }, + getIdleThresholdMs() { return idleThresholdMs; }, + getLastActiveAt() { return lastActiveAt; }, + }; + }, +}; + +registry + .category("services") + .add("fp_shopfloor_activity", fpShopfloorActivityTracker); diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/js/services/tech_store.js b/fusion_plating/fusion_plating_shopfloor/static/src/js/services/tech_store.js new file mode 100644 index 00000000..53519fc7 --- /dev/null +++ b/fusion_plating/fusion_plating_shopfloor/static/src/js/services/tech_store.js @@ -0,0 +1,42 @@ +/** @odoo-module **/ +// ============================================================================= +// Fusion Plating — Tech Store (shared OWL service) +// +// Holds the "current tech of record" for the locked tablet. Set by +// FpTabletLock on successful PIN unlock; cleared on auto-lock / Hand-Off. +// Other components read currentTechId via useService("fp_shopfloor_tech_store") +// and pass it through fpRpc() so server actions credit the right user. +// ============================================================================= + +import { reactive } from "@odoo/owl"; +import { registry } from "@web/core/registry"; + +export const fpShopfloorTechStore = { + start() { + const state = reactive({ + currentTechId: null, + currentTechName: "", + lockedAt: null, + }); + return { + get currentTechId() { return state.currentTechId; }, + get currentTechName() { return state.currentTechName; }, + get isLocked() { return !state.currentTechId; }, + setTech(id, name) { + state.currentTechId = id; + state.currentTechName = name; + state.lockedAt = null; + }, + lock() { + state.currentTechId = null; + state.currentTechName = ""; + state.lockedAt = Date.now(); + }, + state, // exposed for OWL reactive subscriptions + }; + }, +}; + +registry + .category("services") + .add("fp_shopfloor_tech_store", fpShopfloorTechStore);