feat(fusion_plating_shopfloor): wire FpTabletLock + Hand-Off into Landing/Workspace/Manager (P6.2.5)
Three OWL client actions all wrap their root in <FpTabletLock>:
ShopfloorLanding wraps o_fp_landing
JobWorkspace wraps o_fp_ws
ManagerDashboard wraps o_fp_manager
Each adds FpTabletLock to static components, imports tech_store, and
gains a handOff() method that calls techStore.lock(). The Hand-Off
button (yellow, lock icon) lands next to the scan/QR controls in each
header — pressing it instantly returns the tablet to the tile grid
without waiting for the idle timer.
Component composition (per spec §6.5):
FpTabletLock
if isLocked → tile grid + FpPinPad
else → existing client action (via <t t-slot="default"/>) + FpIdleWarning
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -25,16 +25,18 @@ import { WorkflowChip } from "./components/workflow_chip";
|
||||
import { GateViz } from "./components/gate_viz";
|
||||
import { FpSignaturePad } from "./components/signature_pad";
|
||||
import { FpHoldComposer } from "./components/hold_composer";
|
||||
import { FpTabletLock } from "./tablet_lock";
|
||||
|
||||
export class FpJobWorkspace extends Component {
|
||||
static template = "fusion_plating_shopfloor.JobWorkspace";
|
||||
static props = ["*"];
|
||||
static components = { WorkflowChip, GateViz, FpSignaturePad, FpHoldComposer };
|
||||
static components = { WorkflowChip, GateViz, FpSignaturePad, FpHoldComposer, FpTabletLock };
|
||||
|
||||
setup() {
|
||||
this.notification = useService("notification");
|
||||
this.action = useService("action");
|
||||
this.dialog = useService("dialog");
|
||||
this.techStore = useService("fp_shopfloor_tech_store");
|
||||
|
||||
this.state = useState({
|
||||
data: null,
|
||||
@@ -76,6 +78,11 @@ export class FpJobWorkspace extends Component {
|
||||
this.action.doAction({ type: "ir.actions.act_window_close" });
|
||||
}
|
||||
|
||||
// ---- Hand-Off (Phase 6.2) ---------------------------------------------
|
||||
handOff() {
|
||||
this.techStore.lock();
|
||||
}
|
||||
|
||||
onJumpToBlocker({ model, id }) {
|
||||
// If the predecessor is in this same workspace, just scroll to it
|
||||
const inThisJob = (this.state.data.steps || []).find((s) => s.id === id);
|
||||
|
||||
@@ -17,15 +17,17 @@ import { registry } from "@web/core/registry";
|
||||
import { rpc } from "@web/core/network/rpc";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { QrScanner } from "./qr_scanner";
|
||||
import { FpTabletLock } from "./tablet_lock";
|
||||
|
||||
export class ManagerDashboard extends Component {
|
||||
static template = "fusion_plating_shopfloor.ManagerDashboard";
|
||||
static props = ["*"];
|
||||
static components = { QrScanner };
|
||||
static components = { QrScanner, FpTabletLock };
|
||||
|
||||
setup() {
|
||||
this.notification = useService("notification");
|
||||
this.action = useService("action");
|
||||
this.techStore = useService("fp_shopfloor_tech_store");
|
||||
|
||||
this.state = useState({
|
||||
overview: null,
|
||||
@@ -148,6 +150,11 @@ export class ManagerDashboard extends Component {
|
||||
this.state.mode = this.state.mode === "quick" ? "detailed" : "quick";
|
||||
}
|
||||
|
||||
// ---- Hand-Off (Phase 6.2) ---------------------------------------------
|
||||
handOff() {
|
||||
this.techStore.lock();
|
||||
}
|
||||
|
||||
toggleCard(jobId) {
|
||||
this.state.expandedJobId = this.state.expandedJobId === jobId ? null : jobId;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import { rpc } from "@web/core/network/rpc";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { QrScanner } from "./qr_scanner";
|
||||
import { FpKanbanCard } from "./components/kanban_card";
|
||||
import { FpTabletLock } from "./tablet_lock";
|
||||
|
||||
const LS_STATION_ID = "fp_landing_station_id";
|
||||
const LS_MODE = "fp_landing_mode";
|
||||
@@ -31,11 +32,12 @@ const REFRESH_MS = 15000;
|
||||
export class FpShopfloorLanding extends Component {
|
||||
static template = "fusion_plating_shopfloor.ShopfloorLanding";
|
||||
static props = ["*"];
|
||||
static components = { QrScanner, FpKanbanCard };
|
||||
static components = { QrScanner, FpKanbanCard, FpTabletLock };
|
||||
|
||||
setup() {
|
||||
this.notification = useService("notification");
|
||||
this.action = useService("action");
|
||||
this.techStore = useService("fp_shopfloor_tech_store");
|
||||
|
||||
this.state = useState({
|
||||
mode: localStorage.getItem(LS_MODE) || "all_plant",
|
||||
@@ -120,6 +122,12 @@ export class FpShopfloorLanding extends Component {
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
// ---- Hand-Off (Phase 6.2) ---------------------------------------------
|
||||
handOff() {
|
||||
// Tech walking away: lock the tablet so the next operator must PIN in
|
||||
this.techStore.lock();
|
||||
}
|
||||
|
||||
// ---- Search ------------------------------------------------------------
|
||||
onSearchInput(ev) {
|
||||
this.state.search = ev.target.value;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="fusion_plating_shopfloor.JobWorkspace">
|
||||
<FpTabletLock>
|
||||
<t t-set-slot="default">
|
||||
<div class="o_fp_ws">
|
||||
|
||||
<!-- Loading state -->
|
||||
@@ -20,6 +22,12 @@
|
||||
<button class="btn btn-link o_fp_ws_back" t-on-click="onBack">
|
||||
<i class="fa fa-arrow-left"/> Back
|
||||
</button>
|
||||
<!-- Phase 6.2 — Hand-Off: lock the tablet -->
|
||||
<button class="btn btn-sm btn-warning ms-2"
|
||||
t-on-click="handOff"
|
||||
title="Lock the tablet for the next operator">
|
||||
<i class="fa fa-lock"/> Hand Off
|
||||
</button>
|
||||
<span class="o_fp_ws_wo"><t t-esc="state.data.job.display_wo_name"/></span>
|
||||
<span class="o_fp_ws_dot"> · </span>
|
||||
<span class="o_fp_ws_cust"><t t-esc="state.data.job.partner_name"/></span>
|
||||
@@ -225,6 +233,8 @@
|
||||
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</FpTabletLock>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="fusion_plating_shopfloor.ManagerDashboard">
|
||||
<FpTabletLock>
|
||||
<t t-set-slot="default">
|
||||
<div class="o_fp_manager">
|
||||
|
||||
<!-- ============ Hero ============ -->
|
||||
@@ -45,6 +47,12 @@
|
||||
<i t-att-class="'fa fa-refresh' + (state.isFetching ? ' fa-spin' : '')"/>
|
||||
</button>
|
||||
<QrScanner cssClass="'btn'"/>
|
||||
<!-- Phase 6.2 — Hand-Off: lock the tablet -->
|
||||
<button class="btn btn-warning"
|
||||
t-on-click="handOff"
|
||||
title="Lock the tablet for the next operator">
|
||||
<i class="fa fa-lock"/> Hand Off
|
||||
</button>
|
||||
<button t-att-class="'btn ' + (state.mode === 'quick' ? 'btn-primary' : '')"
|
||||
t-on-click="toggleMode">
|
||||
<t t-if="state.mode === 'quick'">Quick View</t>
|
||||
@@ -583,6 +591,8 @@
|
||||
<div>Loading manager data…</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</FpTabletLock>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="fusion_plating_shopfloor.ShopfloorLanding">
|
||||
<FpTabletLock>
|
||||
<t t-set-slot="default">
|
||||
<div class="o_fp_landing">
|
||||
|
||||
<!-- Loading state -->
|
||||
@@ -62,6 +64,13 @@
|
||||
</button>
|
||||
<QrScanner cssClass="'btn btn-sm btn-outline-secondary'" label="'Camera'"/>
|
||||
|
||||
<!-- Phase 6.2 — Hand-Off: lock the tablet for the next operator -->
|
||||
<button class="btn btn-sm btn-warning"
|
||||
t-on-click="handOff"
|
||||
title="Lock the tablet for the next operator">
|
||||
<i class="fa fa-lock"/> Hand Off
|
||||
</button>
|
||||
|
||||
<!-- Refresh indicator -->
|
||||
<span class="o_fp_landing_refresh text-muted">
|
||||
<i class="fa fa-clock-o"/> <t t-esc="state.lastRefresh"/>
|
||||
@@ -158,6 +167,8 @@
|
||||
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</FpTabletLock>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
|
||||
Reference in New Issue
Block a user