feat(jobs): Sub 14 polish — workflow state form layout + Simple Editor field
Two follow-ups on the workflow state work:
1) Form layout
The "How triggers combine" help text was crammed into a 2-column
group, taking ~25% of the available width. Pulled it out of the
group and rendered as a full-width <div class="alert alert-info">
below the trigger fields. Same fix applied to Notes — uses a
<separator> + bare <field> for full sheet width.
2) Simple Recipe Editor support
The trigger field was only exposed in the Tree Editor. Added it
to the Simple Editor's inline library form too:
* fp.step.template.triggers_workflow_state_id (new Many2one) —
per-template default, snapshot-copied to recipe nodes when
dropped into a recipe (added to _SNAPSHOT_FIELDS).
* /fp/simple_recipe/workflow_states/list — new endpoint to feed
the dropdown. Soft-fails when fusion_plating_jobs isn't
installed (returns []).
* Library editor JS — _fpEnsureWorkflowStatesLoaded helper
caches the catalog on first open (create + edit paths both
warm it). Save vals carry the trigger id.
* Library editor XML — dropdown rendered after the flag
checkboxes. Hidden when the catalog is empty so the form
doesn't show a useless "— None —" pick.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -50,6 +50,10 @@ export class FpSimpleRecipeEditor extends Component {
|
||||
libraryEditor: null,
|
||||
libraryEditorBusy: false,
|
||||
tankSearchResults: [],
|
||||
// Sub 14 — workflow-state catalog cache for the inline
|
||||
// library form's "Triggers Workflow State" dropdown. Lazy-
|
||||
// loaded the first time the user opens the library editor.
|
||||
workflowStates: [],
|
||||
});
|
||||
|
||||
this._recipeId = null;
|
||||
@@ -231,7 +235,8 @@ export class FpSimpleRecipeEditor extends Component {
|
||||
* mirrors the shape returned by `/fp/simple_recipe/library/load` so
|
||||
* the same template renders both create + edit.
|
||||
*/
|
||||
onOpenLibraryCreate() {
|
||||
async onOpenLibraryCreate() {
|
||||
await this._fpEnsureWorkflowStatesLoaded();
|
||||
this.state.libraryEditor = {
|
||||
id: null, // null = create
|
||||
name: "",
|
||||
@@ -242,6 +247,8 @@ export class FpSimpleRecipeEditor extends Component {
|
||||
requires_signoff: false,
|
||||
requires_predecessor_done: false,
|
||||
parallel_start: false, // Sub 13 — per-step opt-out
|
||||
triggers_workflow_state_id: false, // Sub 14 — workflow trigger
|
||||
triggers_workflow_state_name: "",
|
||||
requires_rack_assignment: false,
|
||||
requires_transition_form: false,
|
||||
tank_ids: [],
|
||||
@@ -250,8 +257,28 @@ export class FpSimpleRecipeEditor extends Component {
|
||||
this.state.tankSearchResults = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub 14 — fetch the workflow-state catalog once per editor session,
|
||||
* cache on this.state.workflowStates. Used by both create + edit
|
||||
* flows to populate the "Triggers Workflow State" dropdown.
|
||||
*/
|
||||
async _fpEnsureWorkflowStatesLoaded() {
|
||||
if (this.state.workflowStates && this.state.workflowStates.length) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const data = await rpc(
|
||||
"/fp/simple_recipe/workflow_states/list", {}
|
||||
);
|
||||
this.state.workflowStates = data.workflow_states || [];
|
||||
} catch (err) {
|
||||
this.state.workflowStates = [];
|
||||
}
|
||||
}
|
||||
|
||||
async onOpenLibraryEdit(templateId) {
|
||||
this.state.libraryEditorBusy = true;
|
||||
await this._fpEnsureWorkflowStatesLoaded();
|
||||
const data = await rpc("/fp/simple_recipe/library/load", {
|
||||
template_id: templateId,
|
||||
});
|
||||
@@ -291,6 +318,8 @@ export class FpSimpleRecipeEditor extends Component {
|
||||
requires_signoff: !!ed.requires_signoff,
|
||||
requires_predecessor_done: !!ed.requires_predecessor_done,
|
||||
parallel_start: !!ed.parallel_start,
|
||||
// Sub 14 — workflow trigger (Many2one int or false)
|
||||
triggers_workflow_state_id: ed.triggers_workflow_state_id || false,
|
||||
requires_rack_assignment: !!ed.requires_rack_assignment,
|
||||
requires_transition_form: !!ed.requires_transition_form,
|
||||
tank_ids: (ed.tank_ids || []).map((t) => t.id),
|
||||
|
||||
@@ -435,6 +435,33 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Sub 14 — workflow milestone trigger dropdown.
|
||||
Hidden when no states exist (e.g. catalog
|
||||
not seeded yet). -->
|
||||
<div class="o_fp_le_field"
|
||||
t-if="state.workflowStates and state.workflowStates.length">
|
||||
<label class="form-label">Triggers Workflow State</label>
|
||||
<select class="form-select"
|
||||
t-on-change="(ev) => { state.libraryEditor.triggers_workflow_state_id = ev.target.value ? parseInt(ev.target.value, 10) : false; }">
|
||||
<option value=""
|
||||
t-att-selected="!state.libraryEditor.triggers_workflow_state_id">
|
||||
— None (use default-kind matching) —
|
||||
</option>
|
||||
<t t-foreach="state.workflowStates" t-as="ws" t-key="ws.id">
|
||||
<option t-att-value="ws.id"
|
||||
t-att-selected="state.libraryEditor.triggers_workflow_state_id === ws.id"
|
||||
t-esc="ws.name"/>
|
||||
</t>
|
||||
</select>
|
||||
<small class="text-muted d-block mt-1">
|
||||
When a recipe step generated from this template
|
||||
finishes (or is skipped/cancelled), the parent
|
||||
job advances to the chosen state on its status
|
||||
bar. Leave blank to fall back to default-kind
|
||||
matching configured on the workflow state catalog.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<!-- ============== PROMPTS ============== -->
|
||||
<div class="o_fp_le_prompts">
|
||||
<div class="o_fp_le_prompts_header">
|
||||
|
||||
Reference in New Issue
Block a user