From a61bd05a5c3ad51f8f7f43f50b6115331fe390c4 Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Fri, 22 May 2026 21:48:13 -0400 Subject: [PATCH] feat(fusion_plating_shopfloor): KanbanCard shared OWL service MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plan task P1.7. Final shared service — standard WO card used on Landing kanban, Manager Plant Board, and Workflow Funnel. Embeds WorkflowChip, shows progress bar, priority dot, blocker badge from step.blocker_kind. Density prop ('compact' vs 'normal') swaps padding for funnel use. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../fusion_plating_shopfloor/__manifest__.py | 3 + .../static/src/js/components/kanban_card.js | 59 +++++++++++++ .../src/scss/components/_kanban_card.scss | 83 +++++++++++++++++++ .../static/src/xml/components/kanban_card.xml | 40 +++++++++ 4 files changed, 185 insertions(+) create mode 100644 fusion_plating/fusion_plating_shopfloor/static/src/js/components/kanban_card.js create mode 100644 fusion_plating/fusion_plating_shopfloor/static/src/scss/components/_kanban_card.scss create mode 100644 fusion_plating/fusion_plating_shopfloor/static/src/xml/components/kanban_card.xml diff --git a/fusion_plating/fusion_plating_shopfloor/__manifest__.py b/fusion_plating/fusion_plating_shopfloor/__manifest__.py index 1ec19d85..b5a0172e 100644 --- a/fusion_plating/fusion_plating_shopfloor/__manifest__.py +++ b/fusion_plating/fusion_plating_shopfloor/__manifest__.py @@ -77,6 +77,9 @@ Copyright (c) 2026 Nexa Systems Inc. All rights reserved. 'fusion_plating_shopfloor/static/src/scss/components/_hold_composer.scss', 'fusion_plating_shopfloor/static/src/xml/components/hold_composer.xml', 'fusion_plating_shopfloor/static/src/js/components/hold_composer.js', + 'fusion_plating_shopfloor/static/src/scss/components/_kanban_card.scss', + 'fusion_plating_shopfloor/static/src/xml/components/kanban_card.xml', + 'fusion_plating_shopfloor/static/src/js/components/kanban_card.js', 'fusion_plating_shopfloor/static/src/scss/qr_scanner.scss', 'fusion_plating_shopfloor/static/src/scss/fusion_plating_shopfloor.scss', 'fusion_plating_shopfloor/static/src/scss/plant_overview.scss', diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/js/components/kanban_card.js b/fusion_plating/fusion_plating_shopfloor/static/src/js/components/kanban_card.js new file mode 100644 index 00000000..6dc75581 --- /dev/null +++ b/fusion_plating/fusion_plating_shopfloor/static/src/js/components/kanban_card.js @@ -0,0 +1,59 @@ +/** @odoo-module **/ +// ============================================================================= +// Fusion Plating — KanbanCard (shared OWL service) +// +// Standard WO/step card used on: +// • Shop Floor Landing kanban (station + all-plant modes) +// • Manager Plant Board (Needs Worker + In Progress columns) +// • Manager Workflow Funnel (compact density per stage) +// +// Embeds WorkflowChip + a blocker badge from the backend's step.blocker_kind. +// +// Props: +// data : { job_id, display_wo_name, customer, part, qty, +// qty_done, qty_scrapped, date_deadline, priority, +// workflow_state, blocker_kind, blocker_reason, +// current_step_id, work_center } +// density : 'compact' | 'normal' (default 'normal') +// showWorkflowChip : Boolean +// showWorkcenter : Boolean +// showAssignedTo : Boolean +// onTap : Function(data) — called on card click +// ============================================================================= + +import { Component } from "@odoo/owl"; +import { WorkflowChip } from "./workflow_chip"; + +export class FpKanbanCard extends Component { + static template = "fusion_plating_shopfloor.KanbanCard"; + static components = { WorkflowChip }; + static props = { + data: { type: Object, optional: false }, + density: { type: String, optional: true }, + showWorkflowChip: { type: Boolean, optional: true }, + showWorkcenter: { type: Boolean, optional: true }, + showAssignedTo: { type: Boolean, optional: true }, + onTap: { type: Function, optional: true }, + }; + + get isCompact() { + return this.props.density === "compact"; + } + + get progressPct() { + const d = this.props.data; + if (!d.qty || d.qty <= 0) return 0; + return Math.min(100, Math.round((d.qty_done || 0) * 100 / d.qty)); + } + + get priorityDot() { + const p = this.props.data.priority; + if (p === "rush") return "danger"; + if (p === "high") return "warning"; + return "muted"; + } + + onClick() { + if (this.props.onTap) this.props.onTap(this.props.data); + } +} diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/scss/components/_kanban_card.scss b/fusion_plating/fusion_plating_shopfloor/static/src/scss/components/_kanban_card.scss new file mode 100644 index 00000000..47dbae69 --- /dev/null +++ b/fusion_plating/fusion_plating_shopfloor/static/src/scss/components/_kanban_card.scss @@ -0,0 +1,83 @@ +// ============================================================================= +// KanbanCard — standard WO/step card for Landing + Manager surfaces +// Dark-mode aware via $o-webclient-color-scheme branch. +// ============================================================================= + +$o-webclient-color-scheme: bright !default; + +$_kc-bg-hex: #ffffff; +$_kc-border-hex: #d8dadd; +$_kc-hover-hex: #f5f5f7; + +@if $o-webclient-color-scheme == dark { + $_kc-bg-hex: #22262d !global; + $_kc-border-hex: #424245 !global; + $_kc-hover-hex: #2d3138 !global; +} + +.o_fp_kcard { + background: $_kc-bg-hex; + border: 1px solid $_kc-border-hex; + border-radius: 6px; + padding: 0.55rem 0.7rem; + cursor: pointer; + transition: background 0.1s ease; + + &:hover { background: $_kc-hover-hex; } +} + +.o_fp_kcard_compact { padding: 0.4rem 0.55rem; } + +.o_fp_kcard_h1 { + display: flex; justify-content: space-between; align-items: center; + font-weight: 700; font-size: 0.85rem; +} + +.o_fp_kcard_h2 { + color: var(--text-secondary, #666); + font-size: 0.75rem; + margin-top: 0.15rem; +} + +.o_fp_kcard_qty { + display: flex; justify-content: space-between; + font-size: 0.7rem; color: var(--text-secondary, #777); + margin-top: 0.3rem; +} + +.o_fp_kcard_due { color: var(--text-secondary, #999); } + +.o_fp_kcard_bar { + height: 4px; background: rgba(0,0,0,0.08); + border-radius: 2px; overflow: hidden; + margin-top: 0.3rem; + + > div { height: 100%; background: #34c759; } +} + +.o_fp_kcard_chips { + display: flex; gap: 0.35rem; flex-wrap: wrap; + margin-top: 0.4rem; +} + +.o_fp_kcard_pri { width: 8px; height: 8px; border-radius: 50%; } +.o_fp_kcard_pri_danger { background: #ff3b30; } +.o_fp_kcard_pri_warning { background: #ff9f0a; } +.o_fp_kcard_pri_muted { background: transparent; } + +.o_fp_kcard_blocked { + background: rgba(255,159,10,0.15); + color: #b06600; + padding: 0.15rem 0.4rem; + border-radius: 4px; + font-size: 0.7rem; +} + +.o_fp_kcard_wc { + color: var(--text-secondary, #999); + font-size: 0.7rem; +} + +@if $o-webclient-color-scheme == dark { + .o_fp_kcard_blocked { color: #ffb84d; } +} diff --git a/fusion_plating/fusion_plating_shopfloor/static/src/xml/components/kanban_card.xml b/fusion_plating/fusion_plating_shopfloor/static/src/xml/components/kanban_card.xml new file mode 100644 index 00000000..9ae10e5f --- /dev/null +++ b/fusion_plating/fusion_plating_shopfloor/static/src/xml/components/kanban_card.xml @@ -0,0 +1,40 @@ + + + + +
+
+ + +
+
+ + · +
+
+ / done + + Due + +
+
+
+
+
+ + + Blocked + + + @ + +
+
+ + +