feat(shopfloor): tablet_lock branches on tablet_session_mode
When ir.config_parameter[fp.shopfloor.tablet_session_mode]='session_swap',
PIN submit calls /fp/tablet/unlock_session and reloads the page; the
new session manager service kicks in on next mount. handOff() calls
lockBack('manual') which destroys the tech session server-side and
re-auths as kiosk.
Legacy mode unchanged — same /fp/tablet/unlock + techStore flow.
The feature flag, kiosk_uid, and current_uid arrive via the existing
/fp/tablet/tiles bootstrap response (Task D0).
Adds a tablet_lock-owned Hand-Off button visible only in session_swap
mode (in legacy mode wrapper components own their own buttons that hit
techStore.lock(); session_swap renders our own button so the manual
hand-off goes through lockBack() + page reload).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -75,6 +75,20 @@
|
||||
<t t-slot="default"/>
|
||||
<FpIdleWarning t-if="state.idleSecondsRemaining !== null"
|
||||
secondsRemaining="state.idleSecondsRemaining"/>
|
||||
<!-- Phase D: floating Hand-Off button for session_swap mode.
|
||||
In legacy mode the wrapper components (landing/workspace/
|
||||
manager) own their own Hand-Off buttons that hit
|
||||
techStore.lock(). In session_swap mode those become
|
||||
no-ops (techStore isn't the source of truth anymore),
|
||||
so we render a tablet_lock-owned button that calls
|
||||
our own handOff() which routes through the session
|
||||
manager + page reload. -->
|
||||
<button t-if="state.sessionMode === 'session_swap'"
|
||||
class="o_fp_lock_handoff_btn btn btn-warning"
|
||||
t-on-click="handOff"
|
||||
title="Lock the tablet for the next operator">
|
||||
<i class="fa fa-lock"/> Hand Off
|
||||
</button>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user