diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/js/tablet_lock.js b/fusion_plating/fusion_plating_shopfloor/static/src/js/tablet_lock.js index ea6287bb..4909bc2a 100644 --- a/fusion_plating/fusion_plating_shopfloor/static/src/js/tablet_lock.js +++ b/fusion_plating/fusion_plating_shopfloor/static/src/js/tablet_lock.js @@ -34,6 +34,9 @@ export class FpTabletLock extends Component { this.techStore = useService("fp_shopfloor_tech_store"); this.activity = useService("fp_shopfloor_activity"); this.notification = useService("notification"); + // Phase D: idle + ceiling timer for session_swap mode. Started + // once tiles bootstrap shows we're already on a tech session. + this.tabletSessionManager = useService("fp_tablet_session_manager"); this.state = useState({ tiles: [], @@ -46,6 +49,10 @@ export class FpTabletLock extends Component { clockText: this._formatTime(new Date()), dateText: this._formatDate(new Date()), company: null, + // Phase D — feature flag + kiosk identity from bootstrap + sessionMode: "legacy", // 'legacy' or 'session_swap' + kioskUid: null, + currentUid: null, }); onMounted(async () => { @@ -65,16 +72,33 @@ export class FpTabletLock extends Component { this.state.clockText = this._formatTime(now); this.state.dateText = this._formatDate(now); }, 60000); + // Session-swap mode: if we're already on a TECH session (uid + // != kiosk), start the idle/ceiling timer immediately. This + // handles the case where the page was reloaded after + // unlock_session minted the tech's session. + if (this.state.sessionMode === "session_swap" + && this.state.currentUid + && this.state.currentUid !== this.state.kioskUid) { + this.tabletSessionManager.beginSession(); + } }); onWillUnmount(() => { if (this._tick) clearInterval(this._tick); if (this._ping) clearInterval(this._ping); if (this._clockInterval) clearInterval(this._clockInterval); + this.tabletSessionManager.endSession(); }); } get isLocked() { + // SESSION-SWAP MODE: the BROWSER session itself tells us whether + // a tech is unlocked — current_uid != kiosk_uid means unlocked. + // LEGACY MODE: defer to the techStore client-side flag. + if (this.state.sessionMode === "session_swap") { + return !this.state.currentUid + || this.state.currentUid === this.state.kioskUid; + } return this.techStore.isLocked; } @@ -85,6 +109,11 @@ export class FpTabletLock extends Component { const res = await rpc("/fp/tablet/tiles", { station_id: stationId }); if (res && res.ok) { this.state.company = res.company || null; + // Phase D — capture session_mode + kiosk/current uids so + // unlock() / isLocked / handOff can branch on mode. + this.state.sessionMode = res.tablet_session_mode || "legacy"; + this.state.kioskUid = res.kiosk_uid || null; + this.state.currentUid = res.current_uid || null; // Decorate each tile with an animation-delay (50ms staggered, // capped at 300ms so the screen doesn't take 3s to settle on // shops with 20+ operators). @@ -127,6 +156,25 @@ export class FpTabletLock extends Component { async unlock(pin) { try { + // SESSION-SWAP MODE: call the new endpoint, then reload the + // page so the browser re-bootstraps under the tech's session. + if (this.state.sessionMode === "session_swap") { + const res = await rpc("/fp/tablet/unlock_session", { + user_id: this.state.selectedTileUserId, + pin, + }); + if (res && res.ok) { + // Cookie has swapped. Reload so OWL/services re-init + // under the new (tech) session. The session manager + // (Task D1) picks up on the next page load. + window.location.reload(); + // Return a pending state so the caller doesn't try to + // navigate while we're tearing down. + return { ok: true, reloading: true }; + } + return { ok: false, error: (res && res.error) || "Unlock failed" }; + } + // LEGACY MODE: existing /fp/tablet/unlock path const res = await rpc("/fp/tablet/unlock", { user_id: this.state.selectedTileUserId, pin, @@ -148,6 +196,13 @@ export class FpTabletLock extends Component { } handOff() { + // SESSION-SWAP MODE: the server destroys the tech session, then + // we reload to re-bootstrap as the kiosk. + if (this.state.sessionMode === "session_swap") { + this.tabletSessionManager.lockBack("manual"); + return; + } + // LEGACY MODE: client-side state flip only. this.techStore.lock(); this.state.selectedTileUserId = null; this.state.idleSecondsRemaining = null; diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/xml/tablet_lock.xml b/fusion_plating/fusion_plating_shopfloor/static/src/xml/tablet_lock.xml index e9626953..12d3c549 100644 --- a/fusion_plating/fusion_plating_shopfloor/static/src/xml/tablet_lock.xml +++ b/fusion_plating/fusion_plating_shopfloor/static/src/xml/tablet_lock.xml @@ -75,6 +75,20 @@ + +