diff --git a/fusion_plating/fusion_plating/__manifest__.py b/fusion_plating/fusion_plating/__manifest__.py index 2f1e7b68..e9d774e9 100644 --- a/fusion_plating/fusion_plating/__manifest__.py +++ b/fusion_plating/fusion_plating/__manifest__.py @@ -5,7 +5,7 @@ { 'name': 'Fusion Plating', - 'version': '19.0.7.2.0', + 'version': '19.0.7.3.0', 'category': 'Manufacturing/Plating', 'summary': 'Core plating / metal finishing ERP: facilities, processes, tanks, baths, jobs, operators.', 'description': """ diff --git a/fusion_plating/fusion_plating/static/src/js/recipe_tree_editor.js b/fusion_plating/fusion_plating/static/src/js/recipe_tree_editor.js index fccbb59b..bd5e81af 100644 --- a/fusion_plating/fusion_plating/static/src/js/recipe_tree_editor.js +++ b/fusion_plating/fusion_plating/static/src/js/recipe_tree_editor.js @@ -157,16 +157,19 @@ export class RecipeTreeEditor extends Component { if (result && result.ok) { this.state.recipe = result.recipe; this.state.tree = result.tree; - // Auto-expand every node on first load so the full - // hierarchy is visible. The horizontal bracket layout - // works best when everything is open by default; - // operators can still collapse individual branches. - if (result.tree && Object.keys(this.state.expandedNodes).length === 0) { - const expandAll = (n) => { - this.state.expandedNodes[n.id] = true; - for (const c of (n.children || [])) expandAll(c); + // Auto-expand every node on first load AND auto-expand + // any node we haven't seen before (e.g. freshly imported + // nodes after a "Import from recipe" run). Nodes the + // user has explicitly collapsed stay collapsed — we only + // touch nodes that are missing from expandedNodes. + if (result.tree) { + const applyDefault = (n) => { + if (!(n.id in this.state.expandedNodes)) { + this.state.expandedNodes[n.id] = true; + } + for (const c of (n.children || [])) applyDefault(c); }; - expandAll(result.tree); + applyDefault(result.tree); } // Refresh selected node data if panel is open if (this.state.selectedNodeId) { @@ -209,6 +212,25 @@ export class RecipeTreeEditor extends Component { this.state.expandedNodes[nodeId] = !this.state.expandedNodes[nodeId]; } + expandAll() { + const walk = (n) => { + this.state.expandedNodes[n.id] = true; + for (const c of (n.children || [])) walk(c); + }; + if (this.state.tree) walk(this.state.tree); + } + + collapseAll() { + // Collapse everything EXCEPT the recipe root itself — otherwise + // the canvas goes blank and the user has to click the root open + // again to see anything. + const walk = (n, isRoot) => { + this.state.expandedNodes[n.id] = isRoot; + for (const c of (n.children || [])) walk(c, false); + }; + if (this.state.tree) walk(this.state.tree, true); + } + // ---- Node selection (side panel) ---------------------------------------- selectNode(node) { diff --git a/fusion_plating/fusion_plating/static/src/xml/recipe_tree_editor.xml b/fusion_plating/fusion_plating/static/src/xml/recipe_tree_editor.xml index 770e9422..124e2e9c 100644 --- a/fusion_plating/fusion_plating/static/src/xml/recipe_tree_editor.xml +++ b/fusion_plating/fusion_plating/static/src/xml/recipe_tree_editor.xml @@ -179,6 +179,16 @@
+ +