@adcops/autocore-react 3.3.113 → 3.3.116

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/dist/components/index.d.ts +10 -2
  2. package/dist/components/index.d.ts.map +1 -1
  3. package/dist/components/index.js +1 -1
  4. package/dist/components/tis/CycleConfigDialog.d.ts +39 -0
  5. package/dist/components/tis/CycleConfigDialog.d.ts.map +1 -0
  6. package/dist/components/tis/CycleConfigDialog.js +1 -0
  7. package/dist/components/tis/CycleConfigForm.d.ts +22 -0
  8. package/dist/components/tis/CycleConfigForm.d.ts.map +1 -0
  9. package/dist/components/tis/CycleConfigForm.js +1 -0
  10. package/dist/components/tis/CycleStatusStrip.d.ts +10 -0
  11. package/dist/components/tis/CycleStatusStrip.d.ts.map +1 -0
  12. package/dist/components/tis/CycleStatusStrip.js +1 -0
  13. package/dist/components/tis/ResultHistoryTable.d.ts.map +1 -1
  14. package/dist/components/tis/ResultHistoryTable.js +1 -1
  15. package/dist/components/tis/ScienceTable.d.ts +15 -0
  16. package/dist/components/tis/ScienceTable.d.ts.map +1 -1
  17. package/dist/components/tis/ScienceTable.js +1 -1
  18. package/dist/components/tis/TestDataView.d.ts +14 -4
  19. package/dist/components/tis/TestDataView.d.ts.map +1 -1
  20. package/dist/components/tis/TestDataView.js +1 -1
  21. package/dist/components/tis/TestFieldRow.d.ts +151 -0
  22. package/dist/components/tis/TestFieldRow.d.ts.map +1 -0
  23. package/dist/components/tis/TestFieldRow.js +1 -0
  24. package/dist/components/tis/TestSetupForm.d.ts +15 -51
  25. package/dist/components/tis/TestSetupForm.d.ts.map +1 -1
  26. package/dist/components/tis/TestSetupForm.js +1 -1
  27. package/dist/components/tis/TestSetupView.d.ts.map +1 -1
  28. package/dist/components/tis/TestSetupView.js +1 -1
  29. package/dist/components/tis/TisProvider.d.ts +80 -0
  30. package/dist/components/tis/TisProvider.d.ts.map +1 -1
  31. package/dist/components/tis/TisProvider.js +1 -1
  32. package/dist/components/tis-editor/TisConfigEditor.d.ts.map +1 -1
  33. package/dist/components/tis-editor/TisConfigEditor.js +1 -1
  34. package/dist/components/tis-editor/editor/CycleConfigSection.d.ts +19 -0
  35. package/dist/components/tis-editor/editor/CycleConfigSection.d.ts.map +1 -0
  36. package/dist/components/tis-editor/editor/CycleConfigSection.js +1 -0
  37. package/dist/components/tis-editor/editor/FieldArrayEditor.d.ts.map +1 -1
  38. package/dist/components/tis-editor/editor/FieldArrayEditor.js +1 -1
  39. package/dist/components/tis-editor/editor/MethodFormEditor.d.ts.map +1 -1
  40. package/dist/components/tis-editor/editor/MethodFormEditor.js +1 -1
  41. package/dist/components/tis-editor/editor/TestFieldDialog.d.ts +3 -0
  42. package/dist/components/tis-editor/editor/TestFieldDialog.d.ts.map +1 -1
  43. package/dist/components/tis-editor/editor/TestFieldDialog.js +1 -1
  44. package/dist/components/tis-editor/editor/ViewsEditor.d.ts.map +1 -1
  45. package/dist/components/tis-editor/editor/ViewsEditor.js +1 -1
  46. package/dist/components/tis-editor/types.d.ts +28 -1
  47. package/dist/components/tis-editor/types.d.ts.map +1 -1
  48. package/dist/components/tis-editor/validation.d.ts.map +1 -1
  49. package/dist/components/tis-editor/validation.js +1 -1
  50. package/package.json +110 -110
  51. package/src/components/index.ts +14 -0
  52. package/src/components/tis/CycleConfigDialog.tsx +173 -0
  53. package/src/components/tis/CycleConfigForm.tsx +192 -0
  54. package/src/components/tis/CycleStatusStrip.tsx +84 -0
  55. package/src/components/tis/ResultHistoryTable.tsx +141 -1
  56. package/src/components/tis/ScienceTable.tsx +71 -12
  57. package/src/components/tis/TestDataView.tsx +82 -7
  58. package/src/components/tis/TestFieldRow.tsx +297 -0
  59. package/src/components/tis/TestSetupForm.tsx +52 -212
  60. package/src/components/tis/TestSetupView.tsx +60 -3
  61. package/src/components/tis/TisProvider.tsx +191 -0
  62. package/src/components/tis-editor/TisConfigEditor.tsx +1 -0
  63. package/src/components/tis-editor/editor/CycleConfigSection.tsx +93 -0
  64. package/src/components/tis-editor/editor/FieldArrayEditor.tsx +10 -0
  65. package/src/components/tis-editor/editor/MethodFormEditor.tsx +10 -4
  66. package/src/components/tis-editor/editor/TestFieldDialog.tsx +22 -2
  67. package/src/components/tis-editor/editor/ViewsEditor.tsx +3 -0
  68. package/src/components/tis-editor/types.ts +34 -1
  69. package/src/components/tis-editor/validation.ts +20 -2
@@ -1,4 +1,6 @@
1
1
  import React from 'react';
2
+ import { type CycleConfigScope, type TestFieldDef } from './TestFieldRow';
3
+ export type { TestFieldDef } from './TestFieldRow';
2
4
  /**
3
5
  * One asset_ref declared on a test method. We only consume the subset
4
6
  * the form cares about — `field`, `asset_type`, `select`, `from`,
@@ -12,61 +14,24 @@ interface TisAssetRef {
12
14
  from?: string;
13
15
  location?: string;
14
16
  }
15
- export interface TestFieldDef {
16
- /** Canonical key — wire format, generated code, on-disk JSON. */
17
- name: string;
18
- type: string;
19
- units?: string;
20
- required?: boolean;
21
- source?: string;
22
- /** Pretty label rendered by the form. Falls back to `name`. Units
23
- * are appended automatically; don't pre-format `[mm]` into label. */
24
- label?: string;
25
- /** Long-form guidance surfaced as a hover tooltip on an info icon. */
26
- description?: string;
27
- /** Seed value applied when the operator selects this test method.
28
- * For source-bound fields the default is written to the GM tag so
29
- * the control program sees it; for non-source fields it's stashed
30
- * straight into stagedConfig. Operator edits override per-stage,
31
- * but the schema default never mutates — re-selecting the method
32
- * re-applies the default. **Authored in display units** when
33
- * `scale` is set — the form divides by `scale` before writing to
34
- * GM / stagedConfig so the underlying storage stays raw. */
35
- default?: any;
36
- /** Optional unit-conversion multiplier. Mirrors AutoCoreTagContext:
37
- * ```
38
- * display = raw * scale
39
- * raw = display / scale
40
- * ```
41
- * Example: backend stores degrees, operator enters revolutions →
42
- * `scale: 0.00277778`. None / 1.0 = no conversion.
43
- *
44
- * Storage stays raw — the form scales only on input and display.
45
- * Cycle and results values are scaled by the corresponding paths
46
- * in TestDataView; the server scales CSV exports too. */
47
- scale?: number;
48
- /** Optional inclusive bounds (display units, same convention as `default`
49
- * and `scale`) for the operator's numeric entry. The numeric input
50
- * rejects values outside `[min, max]`; non-numeric fields ignore them. */
51
- min?: number;
52
- max?: number;
53
- /** Optional fixed set of choices. When present, the field renders
54
- * as a dropdown and the operator must pick one of the declared
55
- * values rather than typing freely. Each entry is either a bare
56
- * scalar (label === value) or an explicit `{ label, value }` pair
57
- * when the displayed text should differ from the stored value.
58
- * Works with any `type`; like `default`, values are authored in
59
- * display units when `scale` is set. */
60
- options?: Array<string | number | boolean | {
61
- label?: string;
62
- value: string | number | boolean;
63
- }>;
64
- }
65
17
  export interface TestMethod {
66
18
  project_fields: TestFieldDef[];
67
19
  config_fields: TestFieldDef[];
68
20
  cycle_fields: TestFieldDef[];
69
21
  results_fields: TestFieldDef[];
22
+ /**
23
+ * Operator-entered per-row annotation, rendered on the Cycle tab by
24
+ * <CycleConfigForm> rather than by this form. Declared here so a
25
+ * generated schema literal typechecks against `TestMethod`.
26
+ */
27
+ cycle_config_fields?: TestFieldDef[];
28
+ /** Which boundary the operator annotates at: `'run'` (default — one
29
+ * latch per Start, stamping every cycle of the run) or `'cycle'`
30
+ * (one latch per cycle, re-gated between cycles). */
31
+ cycle_config_scope?: CycleConfigScope;
32
+ /** Open the Cycle Configuration dialog at the boundary instead of
33
+ * relying on the operator having filled the Cycle tab. */
34
+ prompt_before_cycle?: boolean;
70
35
  /**
71
36
  * AMS asset references resolved at start_test. The form scans these
72
37
  * for `select=by_id_field` entries pointing at a config field
@@ -135,5 +100,4 @@ export interface TestSetupFormProps {
135
100
  onValidationChange?: (isValid: boolean, config: any) => void;
136
101
  }
137
102
  export declare const TestSetupForm: React.FC<TestSetupFormProps>;
138
- export {};
139
103
  //# sourceMappingURL=TestSetupForm.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TestSetupForm.d.ts","sourceRoot":"","sources":["../../../src/components/tis/TestSetupForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAexE;;;;;GAKG;AACH,UAAU,WAAW;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,aAAa,GAAG,aAAa,CAAC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IACzB,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;0EACsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;iEAO6D;IAC7D,OAAO,CAAC,EAAE,GAAG,CAAC;IACd;;;;;;;;;;8DAU0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;+EAE2E;IAC3E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;;6CAMyC;IACzC,OAAO,CAAC,EAAE,KAAK,CACT,MAAM,GACN,MAAM,GACN,OAAO,GACP;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;KAAE,CACzD,CAAC;CACL;AAED,MAAM,WAAW,UAAU;IACvB,cAAc,EAAE,YAAY,EAAE,CAAC;IAC/B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,cAAc,EAAE,YAAY,EAAE,CAAC;IAC/B;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,WAAW,EAAE,CAAC;IAC3B;0CACsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;2CACuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;gEAG4D;IAC5D,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf;;sDAEkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,iBAAiB,EAAE,CAAC;CACxC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAC9B,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAClC;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,kBAAkB;IAC/B,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;CAChE;AA2KD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAqoBtD,CAAC"}
1
+ {"version":3,"file":"TestSetupForm.d.ts","sourceRoot":"","sources":["../../../src/components/tis/TestSetupForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAaxE,OAAO,EAMH,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACpB,MAAM,gBAAgB,CAAC;AAMxB,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD;;;;;GAKG;AACH,UAAU,WAAW;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,aAAa,GAAG,aAAa,CAAC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACvB,cAAc,EAAE,YAAY,EAAE,CAAC;IAC/B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,cAAc,EAAE,YAAY,EAAE,CAAC;IAC/B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,YAAY,EAAE,CAAC;IACrC;;0DAEsD;IACtD,kBAAkB,CAAC,EAAE,gBAAgB,CAAC;IACtC;+DAC2D;IAC3D,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,WAAW,EAAE,CAAC;IAC3B;0CACsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;2CACuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;gEAG4D;IAC5D,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf;;sDAEkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,iBAAiB,EAAE,CAAC;CACxC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAC9B,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAClC;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,kBAAkB;IAC/B,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;CAChE;AAwGD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAkkBtD,CAAC"}
@@ -1 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import React,{useState,useEffect,useContext,useMemo}from"react";import{Button}from"primereact/button";import{InputText}from"primereact/inputtext";import{Dropdown}from"primereact/dropdown";import{Dialog}from"primereact/dialog";import{EventEmitterContext}from"../../core/EventEmitterContext";import{AutoCoreTagContext}from"../../core/AutoCoreTagContext";import{MessageType}from"../../hub/CommandMessage";import{ValueInput}from"../ValueInput";import{TextInput}from"../TextInput";import{useTis}from"./TisProvider";import{useAmsAssets,useAmsRoles}from"../ams/AmsProvider";import{TestMethodDialog}from"./TestMethodDialog";import{ConfigurationDialog,configLabelOf}from"./ConfigurationDialog";const labelOf=e=>{const t=e.label&&e.label.length>0?e.label:e.name;return e.units?`${t} [${e.units}]`:t},rawToDisplay=(e,t)=>t&&1!==t&&"number"==typeof e&&Number.isFinite(e)?e*t:e,displayToRaw=(e,t)=>t&&1!==t&&"number"==typeof e&&Number.isFinite(e)?e/t:e,hasDescription=e=>"string"==typeof e.description&&e.description.length>0,DEFAULT_TOKEN_RE=/^\$\{\s*([^}]+?)\s*\}$/,resolveDefaultRaw=(e,t,s)=>{const i=e.default;if("string"==typeof i){const e=i.match(DEFAULT_TOKEN_RE);if(e){const i=t(e[1]),n=i?s[i.tagName]:void 0;return null==n?{raw:void 0,ok:!1}:{raw:n,ok:!0}}}return{raw:displayToRaw(i,e.scale),ok:!0}},rangeIssue=(e,t)=>{if(void 0===t||""===t||null===t)return null;if(null==e.min&&null==e.max)return null;const s=Number(rawToDisplay(Number(t),e.scale));if(!Number.isFinite(s))return null;const i=null!=e.min&&null!=e.max?Math.min(e.min,e.max):e.min,n=null!=e.min&&null!=e.max?Math.max(e.min,e.max):e.max;return null!=i&&s<i?`must be ≥ ${i}`:null!=n&&s>n?`must be ≤ ${n}`:null},normalizeOptions=e=>(e??[]).map(e=>null!==e&&"object"==typeof e?{label:String(e.label??e.value),value:e.value}:{label:String(e),value:e}),methodLabelOf=(e,t)=>t?.label&&t.label.length>0?t.label:e,AssetIdPicker=({assetType:e,value:t,onChange:s,invalid:i})=>{const n=useAmsAssets(),a=useAmsRoles(),o=useMemo(()=>n.filter(t=>t.asset_type===e&&"active"===t.status).map(e=>{const t=a[e.asset_type]?.find(t=>t.location===e.location)?.label,s=[e.asset_id];return t?s.push(`— ${t}`):e.location&&s.push(`— ${e.location}`),e.serial&&s.push(`(s/n ${e.serial})`),{label:s.join(" "),value:e.asset_id}}),[n,a,e]);return _jsx(Dropdown,{value:t,options:o,onChange:e=>s(e.value??""),placeholder:0===o.length?`No active ${e} assets registered — add one in Settings → Assets`:`Select ${e}…`,className:"ac-dropdown-clearable"+(i?" p-invalid":""),filter:!0,showClear:!0,disabled:0===o.length})};export const TestSetupForm=({schema:e,defaultMethodId:t,onMethodChange:s,onValidationChange:i})=>{const n=useTis(),{invoke:a,write:o}=useContext(EventEmitterContext),{rawValues:l,findTagByFqdn:r}=useContext(AutoCoreTagContext),c=useMemo(()=>Object.keys(n.schemas),[n.schemas]),d=n.selection.projectId,m=""!==d.trim()&&n.projectKnown(d.trim()),[u,p]=useState(n.selection.methodId||t||n.defaultMethodId||""),[f,h]=useState(n.selection.sampleId||""),g=n.stagedConfig,x=e=>{const t="function"==typeof e?e(n.stagedConfig):e;t!==n.stagedConfig&&n.setStagedConfig(t)},_=e??(u?n.schemas[u]:void 0),j=(e,t)=>{for(const[s,i]of Object.entries(e.defaults??{})){if("sample_id"===s)continue;const e=_?.config_fields.find(e=>e.name===s),n=displayToRaw(i,e?.scale);t[s]=n,e?.source&&Promise.resolve().then(()=>o(e.source,n)).catch(e=>{})}return t};useEffect(()=>{n.selection.methodId!==u&&u&&n.setSelection({methodId:u}),s&&s(u)},[u]),useEffect(()=>{n.selection.sampleId!==f&&n.setSelection({sampleId:f})},[f]),useEffect(()=>{n.state.stagedSampleId&&n.state.stagedSampleId!==f&&h(n.state.stagedSampleId)},[n.state.stagedSampleId]),useEffect(()=>{n.selection.methodId&&n.selection.methodId!==u&&p(n.selection.methodId)},[n.selection.methodId]);const[b,v]=useState(!1),[y,N]=useState(!1),[C,I]=useState(!1),T=_?.configurations??[],S=T.find(e=>e.name===n.configurationName),[w,k]=useState({open:!1,title:"",body:null});useEffect(()=>{if(!_||!u)return;if(n.defaultsAppliedForMethod===u)return;n.markDefaultsAppliedForMethod(u);const e=_.configurations&&_.configurations.length>0?_.configurations[0]:void 0;x(t=>{let s=t;for(const e of _.config_fields){if("sample_id"===e.name)continue;if(void 0===e.default||null===e.default)continue;if(e.source){const t=r(e.source),s=t?l[t.tagName]:void 0;if(null!=s)continue}const{raw:i,ok:n}=resolveDefaultRaw(e,r,l);n&&(s===t&&(s={...t}),s[e.name]=i,e.source&&Promise.resolve().then(()=>o(e.source,i)).catch(e=>{}))}return e&&(s===t&&(s={...t}),s=j(e,s)),s}),n.setConfigurationName(e?e.name:"")},[_,u,o,l,r,n.defaultsAppliedForMethod,n.markDefaultsAppliedForMethod,n.setConfigurationName]),useEffect(()=>{_&&x(e=>{let t=e;for(const s of _.config_fields){if("sample_id"===s.name)continue;if(!s.source)continue;const i=r(s.source);if(!i)continue;const n=l[i.tagName];null!=n&&(t[s.name]!==n&&(t===e&&(t={...e}),t[s.name]=n))}return t})},[_,l,r]),useEffect(()=>{if(!_)return void v(!1);let e=!0;m||(e=!1),u.trim()||(e=!1),f.trim()||(e=!1),e&&!n.projectFieldsLoaded&&(e=!1);for(const t of _.config_fields){if("sample_id"===t.name)continue;const s=g[t.name],i=void 0===s||""===s||null===s;if(t.required&&i){e=!1;break}if(rangeIssue(t,s)){e=!1;break}}if(v(e),i&&i(e,g),e){const{sample_id:e,...t}=g??{},s={...n.projectFields,...t};a("tis.stage_test",MessageType.Request,{project_id:d,method_id:u,sample_id:f,config:s}).catch(e=>{})}},[g,_,d,u,f,m,n.projectFields,n.projectFieldsLoaded,i,a]);const A=async(e,t)=>{const s=displayToRaw(t,e.scale);if(x({...g,[e.name]:s}),e.source)try{await o(e.source,s)}catch(e){}};if(!_)return _jsx("div",{className:"ac-form-grid",style:{padding:"1.25rem"},children:_jsx("h3",{className:"ac-form-section",children:n.schemasLoaded?"No Test Method Selected":"Loading test methods…"})});if(!m)return _jsxs("div",{style:{padding:"1.25rem",maxWidth:"600px"},children:[_jsx("h3",{className:"ac-form-section",children:"No project selected"}),_jsxs("p",{style:{color:"var(--text-secondary-color)",marginTop:"0.5rem"},children:["Pick a project on the ",_jsx("strong",{children:"Project"})," tab first",""!==d.trim()&&` (or click + there to create "${d.trim()}")`,"."]})]});const D={padding:"1.25rem",gridTemplateColumns:"auto 1fr 1.75rem 1.75rem"},F=b&&!n.state.lastStartError;return _jsxs("div",{className:"ac-test-setup-form",children:[_jsxs("div",{className:"ac-form-grid ac-test-setup-form__head",style:D,children:[_jsxs("h3",{className:"ac-form-section",style:{display:"flex",alignItems:"center",gap:"10px"},children:["Test Setup",_jsx(Button,{icon:F?"pi pi-check-circle":"pi pi-exclamation-circle",severity:F?"success":"danger",text:!0,rounded:!0,"aria-label":"Test Setup status",onClick:()=>{const e=(()=>{const e=[];if(m||e.push("No project is selected. Pick or create one on the Project tab."),n.projectFieldsLoaded||e.push("Project fields are still loading."),u.trim()||e.push("No test method selected."),f.trim()||e.push("Sample ID is required."),_)for(const t of _.config_fields){if("sample_id"===t.name)continue;const s=g[t.name],i=void 0===s||""===s||null===s;if(t.required&&i){e.push(`Required field "${labelOf(t)}" is empty.`);continue}const n=rangeIssue(t,s);n&&e.push(`"${labelOf(t)}" ${n}.`)}return e})(),t=n.state.lastStartError?.trim()??"";let s;s=0!==e.length||t?_jsxs("div",{children:[e.length>0&&_jsxs(_Fragment,{children:[_jsx("p",{style:{margin:"0 0 0.5rem 0"},children:"The form is incomplete:"}),_jsx("ul",{style:{margin:"0 0 1rem 1.25rem"},children:e.map((e,t)=>_jsx("li",{children:e},t))})]}),t&&_jsxs(_Fragment,{children:[_jsx("p",{style:{margin:"0 0 0.25rem 0",fontWeight:600},children:"Last start_test error from the server:"}),_jsx("pre",{style:{margin:0,padding:"0.5rem",background:"rgba(0,0,0,0.25)",borderRadius:"4px",whiteSpace:"pre-wrap",fontSize:"0.875rem"},children:t})]})]}):_jsx("p",{style:{margin:0},children:"All required fields are complete. The test is staged and ready to start."}),k({open:!0,title:"Test Setup Status",body:s})}}),_jsxs("span",{style:{fontSize:"0.85em",color:"var(--text-secondary-color)",fontWeight:"normal",marginLeft:"0.25rem"},children:["project: ",_jsx("strong",{children:d})]})]}),_jsx("span",{className:"ac-form-label",children:"Sample ID"}),_jsx(TextInput,{label:void 0,value:f,onValueChanged:e=>{h(e)},className:f.trim()?"":"p-invalid"}),_jsx("span",{"aria-hidden":"true"}),_jsx("span",{style:{color:f.trim()?"var(--green-500)":"var(--red-500)",display:"flex",alignItems:"center"},children:_jsx("i",{className:f.trim()?"pi pi-check":"pi pi-times"})}),c.length>0&&_jsxs(_Fragment,{children:[_jsx("span",{className:"ac-form-label",children:"Test Method"}),_jsxs("div",{className:"p-inputgroup",style:{flex:1},children:[_jsx(InputText,{value:methodLabelOf(u,_),readOnly:!0,style:{flex:1},tabIndex:-1}),_jsx(Button,{icon:"pi pi-folder",type:"button",onClick:()=>N(!0),tooltip:c.length>1?"Change test method":"View test method details",tooltipOptions:{position:"top"}})]}),_jsx("span",{"aria-hidden":"true"}),_jsx("span",{style:{color:u?"var(--green-500)":"var(--red-500)",display:"flex",alignItems:"center"},children:_jsx("i",{className:u?"pi pi-check":"pi pi-times"})})]}),T.length>0&&_jsxs(_Fragment,{children:[_jsx("span",{className:"ac-form-label",children:"Configuration"}),_jsxs("div",{className:"p-inputgroup",style:{flex:1},children:[_jsx(InputText,{value:S?configLabelOf(S):"",placeholder:"No configuration selected",readOnly:!0,style:{flex:1},tabIndex:-1}),_jsx(Button,{icon:"pi pi-folder",type:"button",onClick:()=>I(!0),tooltip:"Change configuration",tooltipOptions:{position:"top"}})]}),_jsx("span",{"aria-hidden":"true"}),_jsx("span",{style:{color:S?"var(--green-500)":"var(--text-secondary-color)",display:"flex",alignItems:"center"},children:_jsx("i",{className:S?"pi pi-check":"pi pi-minus"})})]})]}),_jsx("div",{className:"ac-test-setup-form__body",children:_jsxs("div",{className:"ac-form-grid",style:D,children:[_jsx("h3",{className:"ac-form-section",children:"Test Configuration"}),_.config_fields.map(e=>{if("sample_id"===e.name)return null;const t=(e=>{const t=g[e.name],s=void 0===t||""===t||null===t;return!(e.required&&s||rangeIssue(e,t))})(e),s=(e=>{const t=_?.asset_refs??[],s=n.projectAssetRefs??[],i=`config.${e.name}`,a=t.find(e=>"by_id_field"===e.select&&e.from===i);if(a)return a.asset_type;const o=s.find(e=>"by_id_field"===e.select&&e.from===i);return o?o.asset_type:null})(e),i=!s&&Array.isArray(e.options)&&e.options.length>0?normalizeOptions(e.options):null,a=!s&&!i&&"string"!==e.type&&"bool"!==e.type;return _jsxs(React.Fragment,{children:[_jsx("span",{className:"ac-form-label",children:labelOf(e)}),s?_jsx(AssetIdPicker,{assetType:s,value:null!=g[e.name]?String(g[e.name]):"",onChange:t=>A(e,t),invalid:!t}):i?_jsx(Dropdown,{value:g[e.name]??null,options:i,onChange:t=>A(e,t.value),placeholder:`Select ${e.label??e.name}…`,className:"ac-dropdown-clearable"+(t?"":" p-invalid"),showClear:!e.required}):a?_jsx(ValueInput,{label:void 0,value:null!=g[e.name]?Number(rawToDisplay(Number(g[e.name]),e.scale)):null,min:e.min,max:e.max,onValueChanged:t=>A(e,t),className:t?"":"p-invalid"}):_jsx(TextInput,{label:void 0,value:null!=g[e.name]?String(g[e.name]):"",onValueChanged:t=>A(e,t),className:t?"":"p-invalid"}),hasDescription(e)?_jsx(Button,{icon:"pi pi-info-circle",text:!0,rounded:!0,"aria-label":`About ${labelOf(e)}`,onClick:()=>k({open:!0,title:labelOf(e),body:_jsx("p",{style:{margin:0,whiteSpace:"pre-wrap"},children:e.description})})}):_jsx("span",{"aria-hidden":"true"}),_jsx("span",{style:{color:t?"var(--green-500)":"var(--red-500)",display:"flex",alignItems:"center"},children:_jsx("i",{className:t?"pi pi-check":"pi pi-times"})})]},e.name)})]})}),_jsx(TestMethodDialog,{visible:y,onHide:()=>N(!1),currentMethodId:u,onSelected:e=>p(e)}),_jsx(ConfigurationDialog,{visible:C,onHide:()=>I(!1),configurations:T,currentConfigName:n.configurationName,onSelected:e=>{const t=T.find(t=>t.name===e);t&&(x(e=>j(t,{...e})),n.setConfigurationName(e))}}),_jsx(Dialog,{header:w.title,visible:w.open,onHide:()=>k({open:!1,title:"",body:null}),style:{width:"32rem",maxWidth:"90vw"},modal:!0,dismissableMask:!0,children:w.body})]})};
1
+ import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import React,{useState,useEffect,useContext,useMemo}from"react";import{Button}from"primereact/button";import{InputText}from"primereact/inputtext";import{Dropdown}from"primereact/dropdown";import{Dialog}from"primereact/dialog";import{EventEmitterContext}from"../../core/EventEmitterContext";import{AutoCoreTagContext}from"../../core/AutoCoreTagContext";import{MessageType}from"../../hub/CommandMessage";import{TextInput}from"../TextInput";import{useTis}from"./TisProvider";import{useAmsAssets,useAmsRoles}from"../ams/AmsProvider";import{TestMethodDialog}from"./TestMethodDialog";import{ConfigurationDialog,configLabelOf}from"./ConfigurationDialog";import{TestFieldRow,displayToRaw,fieldIsValid,fieldIssue,labelOf}from"./TestFieldRow";const DEFAULT_TOKEN_RE=/^\$\{\s*([^}]+?)\s*\}$/,resolveDefaultRaw=(e,t,s)=>{const o=e.default;if("string"==typeof o){const e=o.match(DEFAULT_TOKEN_RE);if(e){const o=t(e[1]),i=o?s[o.tagName]:void 0;return null==i?{raw:void 0,ok:!1}:{raw:i,ok:!0}}}return{raw:displayToRaw(o,e.scale),ok:!0}},methodLabelOf=(e,t)=>t?.label&&t.label.length>0?t.label:e,AssetIdPicker=({assetType:e,value:t,onChange:s,invalid:o})=>{const i=useAmsAssets(),a=useAmsRoles(),n=useMemo(()=>i.filter(t=>t.asset_type===e&&"active"===t.status).map(e=>{const t=a[e.asset_type]?.find(t=>t.location===e.location)?.label,s=[e.asset_id];return t?s.push(`— ${t}`):e.location&&s.push(`— ${e.location}`),e.serial&&s.push(`(s/n ${e.serial})`),{label:s.join(" "),value:e.asset_id}}),[i,a,e]);return _jsx(Dropdown,{value:t,options:n,onChange:e=>s(e.value??""),placeholder:0===n.length?`No active ${e} assets registered — add one in Settings → Assets`:`Select ${e}…`,className:"ac-dropdown-clearable"+(o?" p-invalid":""),filter:!0,showClear:!0,disabled:0===n.length})};export const TestSetupForm=({schema:e,defaultMethodId:t,onMethodChange:s,onValidationChange:o})=>{const i=useTis(),{invoke:a,write:n}=useContext(EventEmitterContext),{rawValues:r,findTagByFqdn:l}=useContext(AutoCoreTagContext),c=useMemo(()=>Object.keys(i.schemas),[i.schemas]),d=i.selection.projectId,m=""!==d.trim()&&i.projectKnown(d.trim()),[p,f]=useState(i.selection.methodId||t||i.defaultMethodId||""),[u,h]=useState(i.selection.sampleId||""),g=i.stagedConfig,_=e=>{const t="function"==typeof e?e(i.stagedConfig):e;t!==i.stagedConfig&&i.setStagedConfig(t)},x=e??(p?i.schemas[p]:void 0),j=(e,t)=>{for(const[s,o]of Object.entries(e.defaults??{})){if("sample_id"===s)continue;const e=x?.config_fields.find(e=>e.name===s),i=displayToRaw(o,e?.scale);t[s]=i,e?.source&&Promise.resolve().then(()=>n(e.source,i)).catch(e=>{})}return t};useEffect(()=>{i.selection.methodId!==p&&p&&i.setSelection({methodId:p}),s&&s(p)},[p]),useEffect(()=>{i.selection.sampleId!==u&&i.setSelection({sampleId:u})},[u]),useEffect(()=>{i.state.stagedSampleId&&i.state.stagedSampleId!==u&&h(i.state.stagedSampleId)},[i.state.stagedSampleId]),useEffect(()=>{i.selection.methodId&&i.selection.methodId!==p&&f(i.selection.methodId)},[i.selection.methodId]);const[y,v]=useState(!1),[b,T]=useState(!1),[C,I]=useState(!1),N=x?.configurations??[],S=N.find(e=>e.name===i.configurationName),[w,k]=useState({open:!1,title:"",body:null});useEffect(()=>{if(!x||!p)return;if(i.defaultsAppliedForMethod===p)return;i.markDefaultsAppliedForMethod(p);const e=x.configurations&&x.configurations.length>0?x.configurations[0]:void 0;_(t=>{let s=t;for(const e of x.config_fields){if("sample_id"===e.name)continue;if(void 0===e.default||null===e.default)continue;if(e.source){const t=l(e.source),s=t?r[t.tagName]:void 0;if(null!=s)continue}const{raw:o,ok:i}=resolveDefaultRaw(e,l,r);i&&(s===t&&(s={...t}),s[e.name]=o,e.source&&Promise.resolve().then(()=>n(e.source,o)).catch(e=>{}))}return e&&(s===t&&(s={...t}),s=j(e,s)),s}),i.setConfigurationName(e?e.name:"")},[x,p,n,r,l,i.defaultsAppliedForMethod,i.markDefaultsAppliedForMethod,i.setConfigurationName]),useEffect(()=>{x&&_(e=>{let t=e;for(const s of x.config_fields){if("sample_id"===s.name)continue;if(!s.source)continue;const o=l(s.source);if(!o)continue;const i=r[o.tagName];null!=i&&(t[s.name]!==i&&(t===e&&(t={...e}),t[s.name]=i))}return t})},[x,r,l]),useEffect(()=>{if(!x)return void v(!1);let e=!0;m||(e=!1),p.trim()||(e=!1),u.trim()||(e=!1),e&&!i.projectFieldsLoaded&&(e=!1);for(const t of x.config_fields)if("sample_id"!==t.name&&fieldIssue(t,g[t.name])){e=!1;break}if(v(e),o&&o(e,g),e){const{sample_id:e,...t}=g??{},s={...i.projectFields,...t};a("tis.stage_test",MessageType.Request,{project_id:d,method_id:p,sample_id:u,config:s}).catch(e=>{})}},[g,x,d,p,u,m,i.projectFields,i.projectFieldsLoaded,o,a]);const E=async(e,t)=>{if(_({...g,[e.name]:t}),e.source)try{await n(e.source,t)}catch(e){}};if(!x)return _jsx("div",{className:"ac-form-grid",style:{padding:"1.25rem"},children:_jsx("h3",{className:"ac-form-section",children:i.schemasLoaded?"No Test Method Selected":"Loading test methods…"})});if(!m)return _jsxs("div",{style:{padding:"1.25rem",maxWidth:"600px"},children:[_jsx("h3",{className:"ac-form-section",children:"No project selected"}),_jsxs("p",{style:{color:"var(--text-secondary-color)",marginTop:"0.5rem"},children:["Pick a project on the ",_jsx("strong",{children:"Project"})," tab first",""!==d.trim()&&` (or click + there to create "${d.trim()}")`,"."]})]});const F={padding:"1.25rem",gridTemplateColumns:"auto 1fr 1.75rem 1.75rem"},A=y&&!i.state.lastStartError;return _jsxs("div",{className:"ac-test-setup-form",children:[_jsxs("div",{className:"ac-form-grid ac-test-setup-form__head",style:F,children:[_jsxs("h3",{className:"ac-form-section",style:{display:"flex",alignItems:"center",gap:"10px"},children:["Test Setup",_jsx(Button,{icon:A?"pi pi-check-circle":"pi pi-exclamation-circle",severity:A?"success":"danger",text:!0,rounded:!0,"aria-label":"Test Setup status",onClick:()=>{const e=(()=>{const e=[];if(m||e.push("No project is selected. Pick or create one on the Project tab."),i.projectFieldsLoaded||e.push("Project fields are still loading."),p.trim()||e.push("No test method selected."),u.trim()||e.push("Sample ID is required."),x)for(const t of x.config_fields){if("sample_id"===t.name)continue;const s=fieldIssue(t,g[t.name]);s&&e.push(`"${labelOf(t)}" ${s}.`)}return e})(),t=i.state.lastStartError?.trim()??"";let s;s=0!==e.length||t?_jsxs("div",{children:[e.length>0&&_jsxs(_Fragment,{children:[_jsx("p",{style:{margin:"0 0 0.5rem 0"},children:"The form is incomplete:"}),_jsx("ul",{style:{margin:"0 0 1rem 1.25rem"},children:e.map((e,t)=>_jsx("li",{children:e},t))})]}),t&&_jsxs(_Fragment,{children:[_jsx("p",{style:{margin:"0 0 0.25rem 0",fontWeight:600},children:"Last start_test error from the server:"}),_jsx("pre",{style:{margin:0,padding:"0.5rem",background:"rgba(0,0,0,0.25)",borderRadius:"4px",whiteSpace:"pre-wrap",fontSize:"0.875rem"},children:t})]})]}):_jsx("p",{style:{margin:0},children:"All required fields are complete. The test is staged and ready to start."}),k({open:!0,title:"Test Setup Status",body:s})}}),_jsxs("span",{style:{fontSize:"0.85em",color:"var(--text-secondary-color)",fontWeight:"normal",marginLeft:"0.25rem"},children:["project: ",_jsx("strong",{children:d})]})]}),_jsx("span",{className:"ac-form-label",children:"Sample ID"}),_jsx(TextInput,{label:void 0,value:u,onValueChanged:e=>{h(e)},className:u.trim()?"":"p-invalid"}),_jsx("span",{"aria-hidden":"true"}),_jsx("span",{style:{color:u.trim()?"var(--green-500)":"var(--red-500)",display:"flex",alignItems:"center"},children:_jsx("i",{className:u.trim()?"pi pi-check":"pi pi-times"})}),c.length>0&&_jsxs(_Fragment,{children:[_jsx("span",{className:"ac-form-label",children:"Test Method"}),_jsxs("div",{className:"p-inputgroup",style:{flex:1},children:[_jsx(InputText,{value:methodLabelOf(p,x),readOnly:!0,style:{flex:1},tabIndex:-1}),_jsx(Button,{icon:"pi pi-folder",type:"button",onClick:()=>T(!0),tooltip:c.length>1?"Change test method":"View test method details",tooltipOptions:{position:"top"}})]}),_jsx("span",{"aria-hidden":"true"}),_jsx("span",{style:{color:p?"var(--green-500)":"var(--red-500)",display:"flex",alignItems:"center"},children:_jsx("i",{className:p?"pi pi-check":"pi pi-times"})})]}),N.length>0&&_jsxs(_Fragment,{children:[_jsx("span",{className:"ac-form-label",children:"Configuration"}),_jsxs("div",{className:"p-inputgroup",style:{flex:1},children:[_jsx(InputText,{value:S?configLabelOf(S):"",placeholder:"No configuration selected",readOnly:!0,style:{flex:1},tabIndex:-1}),_jsx(Button,{icon:"pi pi-folder",type:"button",onClick:()=>I(!0),tooltip:"Change configuration",tooltipOptions:{position:"top"}})]}),_jsx("span",{"aria-hidden":"true"}),_jsx("span",{style:{color:S?"var(--green-500)":"var(--text-secondary-color)",display:"flex",alignItems:"center"},children:_jsx("i",{className:S?"pi pi-check":"pi pi-minus"})})]})]}),_jsx("div",{className:"ac-test-setup-form__body",children:_jsxs("div",{className:"ac-form-grid",style:F,children:[_jsx("h3",{className:"ac-form-section",children:"Test Configuration"}),x.config_fields.map(e=>{if("sample_id"===e.name)return null;const t=(e=>fieldIsValid(e,g[e.name]))(e),s=(e=>{const t=x?.asset_refs??[],s=i.projectAssetRefs??[],o=`config.${e.name}`,a=t.find(e=>"by_id_field"===e.select&&e.from===o);if(a)return a.asset_type;const n=s.find(e=>"by_id_field"===e.select&&e.from===o);return n?n.asset_type:null})(e);return _jsx(TestFieldRow,{field:e,value:g[e.name],valid:t,onChange:t=>E(e,t),onInfo:e=>k({open:!0,title:labelOf(e),body:_jsx("p",{style:{margin:0,whiteSpace:"pre-wrap"},children:e.description})}),control:s?_jsx(AssetIdPicker,{assetType:s,value:null!=g[e.name]?String(g[e.name]):"",onChange:t=>E(e,t),invalid:!t}):void 0},e.name)})]})}),_jsx(TestMethodDialog,{visible:b,onHide:()=>T(!1),currentMethodId:p,onSelected:e=>f(e)}),_jsx(ConfigurationDialog,{visible:C,onHide:()=>I(!1),configurations:N,currentConfigName:i.configurationName,onSelected:e=>{const t=N.find(t=>t.name===e);t&&(_(e=>j(t,{...e})),i.setConfigurationName(e))}}),_jsx(Dialog,{header:w.title,visible:w.open,onHide:()=>k({open:!1,title:"",body:null}),style:{width:"32rem",maxWidth:"90vw"},modal:!0,dismissableMask:!0,children:w.body})]})};
@@ -1 +1 @@
1
- {"version":3,"file":"TestSetupView.d.ts","sourceRoot":"","sources":["../../../src/components/tis/TestSetupView.tsx"],"names":[],"mappings":"AAwBA,OAAO,KAAK,MAAM,OAAO,CAAC;AAM1B,MAAM,WAAW,kBAAkB;IAC/B,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC/B;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAkCtD,CAAC"}
1
+ {"version":3,"file":"TestSetupView.d.ts","sourceRoot":"","sources":["../../../src/components/tis/TestSetupView.tsx"],"names":[],"mappings":"AAkCA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAQ3D,MAAM,WAAW,kBAAkB;IAC/B,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC/B;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA+EtD,CAAC"}
@@ -1 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import React from"react";import{TabView,TabPanel}from"primereact/tabview";import{ProjectSelector}from"./ProjectSelector";import{ResultHistoryTable}from"./ResultHistoryTable";import{TestSetupForm}from"./TestSetupForm";export const TestSetupView=({className:e,style:s})=>_jsx("div",{className:e?`ac-test-setup-view ${e}`:"ac-test-setup-view",style:s,children:_jsxs(TabView,{children:[_jsx(TabPanel,{header:"Project",children:_jsxs("div",{className:"ac-test-setup-view__tab",children:[_jsx("div",{className:"ac-test-setup-view__fixed",children:_jsx(ProjectSelector,{})}),_jsx("div",{className:"ac-test-setup-view__scroll",children:_jsx(ResultHistoryTable,{})})]})}),_jsx(TabPanel,{header:"Test",children:_jsx(TestSetupForm,{})})]})});
1
+ import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import React,{useEffect,useRef,useState}from"react";import{TabView,TabPanel}from"primereact/tabview";import{ProjectSelector}from"./ProjectSelector";import{ResultHistoryTable}from"./ResultHistoryTable";import{TestSetupForm}from"./TestSetupForm";import{CycleConfigForm}from"./CycleConfigForm";import{useTis}from"./TisProvider";export const TestSetupView=({className:e,style:s})=>{const t=e?`ac-test-setup-view ${e}`:"ac-test-setup-view",a=useTis(),r=a.cycleConfigFields.length>0,i=r&&!a.cycleConfig.ready,[c,l]=useState(0),o=useRef(!1);return useEffect(()=>{a.state.active&&!o.current&&r&&l(2),o.current=a.state.active},[a.state.active,r]),_jsx("div",{className:t,style:s,children:_jsxs(TabView,{activeIndex:c,onTabChange:e=>l(e.index),children:[_jsx(TabPanel,{header:"Project",children:_jsxs("div",{className:"ac-test-setup-view__tab",children:[_jsx("div",{className:"ac-test-setup-view__fixed",children:_jsx(ProjectSelector,{})}),_jsx("div",{className:"ac-test-setup-view__scroll",children:_jsx(ResultHistoryTable,{})})]})}),_jsx(TabPanel,{header:"Test",children:_jsx(TestSetupForm,{})}),r&&_jsx(TabPanel,{header:_jsxs("span",{style:{display:"inline-flex",alignItems:"center",gap:"0.4rem"},children:["Cycle",i&&_jsx("i",{className:"pi pi-exclamation-circle",style:{color:"var(--red-500)"},"aria-label":"Cycle Configuration incomplete"})]}),children:_jsx("div",{className:"ac-test-setup-view__tab",children:_jsx("div",{className:"ac-test-setup-view__scroll",children:_jsx(CycleConfigForm,{})})})})]})})};
@@ -1,4 +1,5 @@
1
1
  import React, { type ReactNode } from 'react';
2
+ import type { CycleConfigScope, TestFieldDef } from './TestFieldRow';
2
3
  /**
3
4
  * One slot in the schema registry. Kept as an opaque object so the
4
5
  * provider doesn't have to mirror every schema field — components that
@@ -30,6 +31,36 @@ export interface TisLiveState {
30
31
  */
31
32
  lastStartError: string;
32
33
  }
34
+ /**
35
+ * Live Cycle Configuration state for the selected (project, method) —
36
+ * the operator's per-row annotation. Mirrors the server's
37
+ * `tis.cycle_config` payload one-for-one; the server owns every rule
38
+ * (which boundary latches, when autoclear fires, whether the gate is
39
+ * satisfied) so the HMI never has to re-derive them and can never
40
+ * disagree with the `tis.cycle_ready` scalar the machine gates on.
41
+ */
42
+ export interface TisCycleConfigState {
43
+ projectId: string;
44
+ methodId: string;
45
+ /** `'run'` = one latch per Start; `'cycle'` = one per cycle. */
46
+ scope: CycleConfigScope;
47
+ /** Method asks for the entry dialog at each boundary. */
48
+ promptBeforeCycle: boolean;
49
+ /** What the operator has entered but no row has consumed yet (RAW). */
50
+ values: Record<string, any>;
51
+ /** The snapshot the cycle now running will be stamped with, or null
52
+ * when nothing is latched. Non-null means the fields are locked for
53
+ * the row in flight — editing them targets the *next* row. */
54
+ latched: Record<string, any> | null;
55
+ /** False while any `required` field is empty. The same condition
56
+ * drives `tis.cycle_ready`, which gates the machine. */
57
+ ready: boolean;
58
+ /** Names of the required-but-empty fields, in declared order. */
59
+ missing: string[];
60
+ /** A `prompt_before_cycle` method has reached a boundary with nothing
61
+ * latched — the edge on which <CycleConfigDialog> opens. */
62
+ promptDue: boolean;
63
+ }
33
64
  export interface TisSelection {
34
65
  projectId: string;
35
66
  methodId: string;
@@ -151,6 +182,23 @@ export interface TisContextValue {
151
182
  * seeding effect auto-applies the first configuration and when the
152
183
  * operator accepts a different one in the picker dialog. */
153
184
  setConfigurationName: (name: string) => void;
185
+ /** Live state for the selected method. Server-owned; every mutation
186
+ * round-trips so the HMI and the `tis.cycle_ready` gate agree. */
187
+ cycleConfig: TisCycleConfigState;
188
+ /** The selected method's `cycle_config_fields`, in declared order.
189
+ * Empty when the method doesn't use the feature — which is the
190
+ * signal to hide the Cycle tab entirely. */
191
+ cycleConfigFields: TestFieldDef[];
192
+ /** Merge a sparse patch of RAW values into the operator's pending
193
+ * entry. An explicit null/'' clears a field. Resolves once the
194
+ * server has applied it and the state has been refreshed. */
195
+ setCycleConfigValues: (patch: Record<string, any>) => Promise<void>;
196
+ /** Wipe every pending value regardless of `autoclear`. Leaves the
197
+ * latch alone — a cycle already running keeps its label. */
198
+ clearCycleConfig: () => Promise<void>;
199
+ /** Re-read Cycle Configuration state from the server. Called on
200
+ * selection change; broadcasts keep it fresh after that. */
201
+ refreshCycleConfig: () => Promise<void>;
154
202
  /** Fetch the run list for a (project, method?) pair. Method may be
155
203
  * omitted to aggregate runs across every method in the project —
156
204
  * the History tab uses this. */
@@ -176,6 +224,38 @@ export declare const TisProvider: React.FC<TisProviderProps>;
176
224
  export declare const useTis: () => TisContextValue;
177
225
  export declare const useTisSchemas: () => SchemaRegistry;
178
226
  export declare const useTisState: () => TisLiveState;
227
+ /**
228
+ * Cycle Configuration for the selected method: the live state, the field
229
+ * declarations, and the two mutators.
230
+ *
231
+ * `fields.length === 0` means the method doesn't use per-row annotation —
232
+ * hide the Cycle tab and the status strip rather than showing an empty one.
233
+ */
234
+ export declare const useTisCycleConfig: () => {
235
+ fields: TestFieldDef[];
236
+ setValues: (patch: Record<string, any>) => Promise<void>;
237
+ clear: () => Promise<void>;
238
+ projectId: string;
239
+ methodId: string;
240
+ /** `'run'` = one latch per Start; `'cycle'` = one per cycle. */
241
+ scope: CycleConfigScope;
242
+ /** Method asks for the entry dialog at each boundary. */
243
+ promptBeforeCycle: boolean;
244
+ /** What the operator has entered but no row has consumed yet (RAW). */
245
+ values: Record<string, any>;
246
+ /** The snapshot the cycle now running will be stamped with, or null
247
+ * when nothing is latched. Non-null means the fields are locked for
248
+ * the row in flight — editing them targets the *next* row. */
249
+ latched: Record<string, any> | null;
250
+ /** False while any `required` field is empty. The same condition
251
+ * drives `tis.cycle_ready`, which gates the machine. */
252
+ ready: boolean;
253
+ /** Names of the required-but-empty fields, in declared order. */
254
+ missing: string[];
255
+ /** A `prompt_before_cycle` method has reached a boundary with nothing
256
+ * latched — the edge on which <CycleConfigDialog> opens. */
257
+ promptDue: boolean;
258
+ };
179
259
  /**
180
260
  * Tuple of `[selection, setSelection]`.
181
261
  *
@@ -1 +1 @@
1
- {"version":3,"file":"TisProvider.d.ts","sourceRoot":"","sources":["../../../src/components/tis/TisProvider.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"TisProvider.d.ts","sourceRoot":"","sources":["../../../src/components/tis/TisProvider.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- import{jsx as _jsx}from"react/jsx-runtime";import React,{createContext,useCallback,useContext,useEffect,useMemo,useReducer,useRef,useState}from"react";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";const EMPTY_STATE={staged:!1,stagedProjectId:"",stagedMethodId:"",stagedSampleId:"",active:!1,activeProjectId:"",activeMethodId:"",activeSampleId:"",activeRunId:"",lastStartError:""},EMPTY_SELECTION={projectId:"",methodId:"",sampleId:"",runId:""},TisContext=createContext({schemas:{},projectAssetRefs:[],defaultMethodId:"",schemasLoaded:!1,state:EMPTY_STATE,selection:EMPTY_SELECTION,setSelection:()=>{},existingProjects:[],projectKnown:()=>!1,refreshProjects:async()=>{},markProjectJustCreated:()=>{},projectFields:{},projectFieldsLoaded:!1,loadProjectFields:async()=>null,setProjectFields:()=>{},stagedConfig:{},setStagedConfig:()=>{},clearStagedConfig:()=>{},defaultsAppliedForMethod:"",markDefaultsAppliedForMethod:()=>{},configurationName:"",setConfigurationName:()=>{},fetchRuns:async()=>[],fetchRun:async()=>null,runCache:{}});function liveReducer(e,t){switch(t.kind){case"staged":return{...e,staged:t.value};case"staged_project_id":return{...e,stagedProjectId:t.value};case"staged_method_id":return{...e,stagedMethodId:t.value};case"staged_sample_id":return{...e,stagedSampleId:t.value};case"active":return{...e,active:t.value};case"active_project_id":return{...e,activeProjectId:t.value};case"active_method_id":return{...e,activeMethodId:t.value};case"active_sample_id":return{...e,activeSampleId:t.value};case"active_run_id":return{...e,activeRunId:t.value};case"last_start_error":return{...e,lastStartError:t.value}}}export const TisProvider=({children:e,defaultMethodId:t})=>{const{invoke:s,subscribe:a,unsubscribe:r}=useContext(EventEmitterContext),[c,d]=useState({}),[n,u]=useState([]),[o,i]=useState(t??""),[l,p]=useState(!1),[_,f]=useReducer(liveReducer,EMPTY_STATE),[m,I]=useState({projectId:null,methodId:null,sampleId:null,runId:null});useEffect(()=>{let e=!1;return(async()=>{try{const a=await s("tis.list_schemas",MessageType.Request,{});if(e)return;if(a?.success&&a.data){const e=a.data.test_methods??{},s=a.data.default_method_id??"",r=Array.isArray(a.data.asset_refs)?a.data.asset_refs:[];d(e),u(r),!t&&s&&i(s),p(!0)}}catch(e){}})(),()=>{e=!0}},[]),useEffect(()=>{let e=!1;return(async()=>{try{const t=await s("gnv.ux.selected_project_id",MessageType.Read,{});if(e)return;const a=t?.success?t.data??"":"";"string"==typeof a&&a.length>0&&I(e=>null===e.projectId?{...e,projectId:a}:e)}catch{}})(),()=>{e=!0}},[]),useEffect(()=>{const e=[a("tis.staged",e=>f({kind:"staged",value:!!e})),a("tis.staged_project_id",e=>f({kind:"staged_project_id",value:String(e??"")})),a("tis.staged_method_id",e=>f({kind:"staged_method_id",value:String(e??"")})),a("tis.staged_sample_id",e=>f({kind:"staged_sample_id",value:String(e??"")})),a("tis.active",e=>f({kind:"active",value:!!e})),a("tis.active_project_id",e=>f({kind:"active_project_id",value:String(e??"")})),a("tis.active_method_id",e=>f({kind:"active_method_id",value:String(e??"")})),a("tis.active_sample_id",e=>f({kind:"active_sample_id",value:String(e??"")})),a("tis.active_run_id",e=>f({kind:"active_run_id",value:String(e??"")})),a("tis.last_start_error",e=>f({kind:"last_start_error",value:String(e??"")}))];return()=>{e.forEach(r)}},[a,r]);const v=useRef({}),[h,j]=useState(0),g=useCallback(()=>j(e=>e+1),[]),C=useCallback((e,t)=>{if(!e)return;const s=v.current[e]??{meta:null,cycles:[],results:{},rawData:{}};v.current[e]={...s,cycles:[...s.cycles,t]},g()},[g]),S=useCallback((e,t)=>{if(!e)return;const s=v.current[e]??{meta:null,cycles:[],results:{},rawData:{}};v.current[e]={...s,results:t},g()},[g]);useEffect(()=>{const e=a("tis.cycle_added",e=>{e?.run_id&&e.cycle&&C(e.run_id,e.cycle)}),t=a("tis.results_updated",e=>{e?.run_id&&S(e.run_id,e.results??{})});return()=>{r(e),r(t)}},[a,r,C,S]);const y=useMemo(()=>({projectId:m.projectId??_.activeProjectId,methodId:m.methodId??(_.activeMethodId||o),sampleId:m.sampleId??_.activeSampleId,runId:m.runId??_.activeRunId}),[m,_,o]),x=useCallback(e=>{I(t=>({projectId:void 0===e.projectId?t.projectId:e.projectId,methodId:void 0===e.methodId?t.methodId:e.methodId,sampleId:void 0===e.sampleId?t.sampleId:e.sampleId,runId:void 0===e.runId?t.runId:e.runId})),void 0!==e.projectId&&s("gnv.ux.selected_project_id",MessageType.Write,e.projectId??"").catch(e=>{})},[s]),T=useRef("");useEffect(()=>{const e=_.activeRunId;e&&e!==T.current&&(T.current=e,I({projectId:null,methodId:null,sampleId:null,runId:null}))},[_.activeRunId]);const k=useCallback(async(e,t)=>{if(!e)return[];const a={project_id:e};t&&(a.method_id=t);try{const e=await s("tis.list_tests",MessageType.Request,a);if(e?.success&&e.data?.tests)return e.data.tests}catch(e){}return[]},[s]),E=useCallback(async(e,t,a)=>{if(!e||!t||!a)return null;try{const r=await s("tis.read_test",MessageType.Request,{project_id:e,method_id:t,run_id:a}),c=await s("tis.read_cycles",MessageType.Request,{project_id:e,method_id:t,run_id:a,offset:0,limit:1e3,order:"asc"});if(!r?.success)return null;const d={meta:r.data??null,cycles:c?.success?c.data?.cycles??[]:[],results:r.data?.results??{},rawData:v.current[a]?.rawData??{}};return v.current[a]=d,g(),d}catch(e){return null}},[s,g]),M=useMemo(()=>({...v.current}),[h]),[R,P]=useState([]),b=useRef(new Set),[w,F]=useState(0),[A,D]=useState({}),q=useCallback(async()=>{try{const e=await s("tis.list_projects",MessageType.Request,{});e?.success&&e.data?.projects&&P(e.data.projects)}catch(e){}},[s]),L=useCallback(e=>{e&&(b.current.has(e)||(b.current.add(e),F(e=>e+1)))},[]),N=useCallback(e=>!!e&&(!!b.current.has(e)||R.includes(e)),[R,w]),Y=useCallback((e,t)=>{e&&D(s=>({...s,[e]:t}))},[]),J=useCallback(async e=>{if(!e)return null;try{const t=await s("tis.read_project",MessageType.Request,{project_id:e});if(t?.success){const s=t.data?.project_fields??{};return D(t=>({...t,[e]:s})),s}}catch(e){}return D(t=>({...t,[e]:{}})),{}},[s]);useEffect(()=>{q()},[q]),useEffect(()=>{const e=a("tis.project_created",()=>{q()}),t=a("tis.project_updated",e=>{const t="string"==typeof e?.project_id?e.project_id:"";t&&J(t)});return()=>{r(e),r(t)}},[a,r,q,J]),useEffect(()=>{const e=y.projectId;e&&N(e)&&void 0===A[e]&&J(e)},[y.projectId,N,A,J]);const K=A[y.projectId]??{},O=void 0!==A[y.projectId],[W,z]=useState({}),B=useCallback(e=>{z(t=>({...t,...e}))},[]),[G,H]=useState(""),Q=useCallback(e=>{H(e)},[]),[U,V]=useState(""),X=useCallback(()=>{z({}),H(""),V("")},[]),Z=useMemo(()=>({schemas:c,projectAssetRefs:n,defaultMethodId:o,schemasLoaded:l,state:_,selection:y,setSelection:x,existingProjects:R,projectKnown:N,refreshProjects:q,markProjectJustCreated:L,projectFields:K,projectFieldsLoaded:O,loadProjectFields:J,setProjectFields:Y,stagedConfig:W,setStagedConfig:B,clearStagedConfig:X,defaultsAppliedForMethod:G,markDefaultsAppliedForMethod:Q,configurationName:U,setConfigurationName:V,fetchRuns:k,fetchRun:E,runCache:M}),[c,n,o,l,_,y,x,R,N,q,L,K,O,J,Y,W,B,X,G,Q,U,V,k,E,M]);return _jsx(TisContext.Provider,{value:Z,children:e})};export const useTis=()=>useContext(TisContext);export const useTisSchemas=()=>useContext(TisContext).schemas;export const useTisState=()=>useContext(TisContext).state;export const useTisSelection=()=>{const{selection:e,setSelection:t}=useContext(TisContext);return[e,t]};export const useTisRuns=(e,t)=>{const{fetchRuns:s}=useContext(TisContext),[a,r]=useState([]),[c,d]=useState(!1),n=useCallback(async()=>{if(e){d(!0);try{r(await s(e,t))}finally{d(!1)}}else r([])},[e,t,s]);return useEffect(()=>{n()},[n]),{runs:a,loading:c,refresh:n}};export const useTisRun=e=>{const{selection:t,fetchRun:s,runCache:a}=useContext(TisContext),[r,c]=useState(!1),d=e??t.runId;useEffect(()=>{if(!d)return;if(a[d]?.meta)return;const e=t.projectId,r=t.methodId;e&&r&&(c(!0),s(e,r,d).finally(()=>c(!1)))},[d,t.projectId,t.methodId,s,a]);const n=d?a[d]:null;return{meta:n?.meta??null,cycles:n?.cycles??[],results:n?.results??{},rawData:n?.rawData??{},loading:r}};export{TisContext};
1
+ import{jsx as _jsx}from"react/jsx-runtime";import React,{createContext,useCallback,useContext,useEffect,useMemo,useReducer,useRef,useState}from"react";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";const EMPTY_STATE={staged:!1,stagedProjectId:"",stagedMethodId:"",stagedSampleId:"",active:!1,activeProjectId:"",activeMethodId:"",activeSampleId:"",activeRunId:"",lastStartError:""},EMPTY_SELECTION={projectId:"",methodId:"",sampleId:"",runId:""},EMPTY_CYCLE_CONFIG={projectId:"",methodId:"",scope:"run",promptBeforeCycle:!1,values:{},latched:null,ready:!0,missing:[],promptDue:!1},cycleConfigFromPayload=e=>({projectId:String(e?.project_id??""),methodId:String(e?.method_id??""),scope:"cycle"===e?.scope?"cycle":"run",promptBeforeCycle:!!e?.prompt_before_cycle,values:e?.values&&"object"==typeof e.values?e.values:{},latched:e?.latched&&"object"==typeof e.latched?e.latched:null,ready:!1!==e?.ready,missing:Array.isArray(e?.missing)?e.missing:[],promptDue:!!e?.prompt_due}),TisContext=createContext({schemas:{},projectAssetRefs:[],defaultMethodId:"",schemasLoaded:!1,state:EMPTY_STATE,selection:EMPTY_SELECTION,setSelection:()=>{},existingProjects:[],projectKnown:()=>!1,refreshProjects:async()=>{},markProjectJustCreated:()=>{},projectFields:{},projectFieldsLoaded:!1,loadProjectFields:async()=>null,setProjectFields:()=>{},stagedConfig:{},setStagedConfig:()=>{},clearStagedConfig:()=>{},defaultsAppliedForMethod:"",markDefaultsAppliedForMethod:()=>{},configurationName:"",setConfigurationName:()=>{},cycleConfig:EMPTY_CYCLE_CONFIG,cycleConfigFields:[],setCycleConfigValues:async()=>{},clearCycleConfig:async()=>{},refreshCycleConfig:async()=>{},fetchRuns:async()=>[],fetchRun:async()=>null,runCache:{}});function liveReducer(e,t){switch(t.kind){case"staged":return{...e,staged:t.value};case"staged_project_id":return{...e,stagedProjectId:t.value};case"staged_method_id":return{...e,stagedMethodId:t.value};case"staged_sample_id":return{...e,stagedSampleId:t.value};case"active":return{...e,active:t.value};case"active_project_id":return{...e,activeProjectId:t.value};case"active_method_id":return{...e,activeMethodId:t.value};case"active_sample_id":return{...e,activeSampleId:t.value};case"active_run_id":return{...e,activeRunId:t.value};case"last_start_error":return{...e,lastStartError:t.value}}}export const TisProvider=({children:e,defaultMethodId:t})=>{const{invoke:s,subscribe:c,unsubscribe:a}=useContext(EventEmitterContext),[r,d]=useState({}),[o,n]=useState([]),[i,u]=useState(t??""),[l,p]=useState(!1),[f,_]=useReducer(liveReducer,EMPTY_STATE),[m,C]=useState({projectId:null,methodId:null,sampleId:null,runId:null});useEffect(()=>{let e=!1;return(async()=>{try{const c=await s("tis.list_schemas",MessageType.Request,{});if(e)return;if(c?.success&&c.data){const e=c.data.test_methods??{},s=c.data.default_method_id??"",a=Array.isArray(c.data.asset_refs)?c.data.asset_refs:[];d(e),n(a),!t&&s&&u(s),p(!0)}}catch(e){}})(),()=>{e=!0}},[]),useEffect(()=>{let e=!1;return(async()=>{try{const t=await s("gnv.ux.selected_project_id",MessageType.Read,{});if(e)return;const c=t?.success?t.data??"":"";"string"==typeof c&&c.length>0&&C(e=>null===e.projectId?{...e,projectId:c}:e)}catch{}})(),()=>{e=!0}},[]),useEffect(()=>{const e=[c("tis.staged",e=>_({kind:"staged",value:!!e})),c("tis.staged_project_id",e=>_({kind:"staged_project_id",value:String(e??"")})),c("tis.staged_method_id",e=>_({kind:"staged_method_id",value:String(e??"")})),c("tis.staged_sample_id",e=>_({kind:"staged_sample_id",value:String(e??"")})),c("tis.active",e=>_({kind:"active",value:!!e})),c("tis.active_project_id",e=>_({kind:"active_project_id",value:String(e??"")})),c("tis.active_method_id",e=>_({kind:"active_method_id",value:String(e??"")})),c("tis.active_sample_id",e=>_({kind:"active_sample_id",value:String(e??"")})),c("tis.active_run_id",e=>_({kind:"active_run_id",value:String(e??"")})),c("tis.last_start_error",e=>_({kind:"last_start_error",value:String(e??"")}))];return()=>{e.forEach(a)}},[c,a]);const I=useRef({}),[y,g]=useState(0),h=useCallback(()=>g(e=>e+1),[]),j=useCallback((e,t)=>{if(!e)return;const s=I.current[e]??{meta:null,cycles:[],results:{},rawData:{}};I.current[e]={...s,cycles:[...s.cycles,t]},h()},[h]),v=useCallback((e,t)=>{if(!e)return;const s=I.current[e]??{meta:null,cycles:[],results:{},rawData:{}};I.current[e]={...s,results:t},h()},[h]);useEffect(()=>{const e=c("tis.cycle_added",e=>{e?.run_id&&e.cycle&&j(e.run_id,e.cycle)}),t=c("tis.results_updated",e=>{e?.run_id&&v(e.run_id,e.results??{})});return()=>{a(e),a(t)}},[c,a,j,v]);const S=useMemo(()=>({projectId:m.projectId??f.activeProjectId,methodId:m.methodId??(f.activeMethodId||i),sampleId:m.sampleId??f.activeSampleId,runId:m.runId??f.activeRunId}),[m,f,i]),T=useCallback(e=>{C(t=>({projectId:void 0===e.projectId?t.projectId:e.projectId,methodId:void 0===e.methodId?t.methodId:e.methodId,sampleId:void 0===e.sampleId?t.sampleId:e.sampleId,runId:void 0===e.runId?t.runId:e.runId})),void 0!==e.projectId&&s("gnv.ux.selected_project_id",MessageType.Write,e.projectId??"").catch(e=>{})},[s]),E=useRef("");useEffect(()=>{const e=f.activeRunId;e&&e!==E.current&&(E.current=e,C({projectId:null,methodId:null,sampleId:null,runId:null}))},[f.activeRunId]);const x=useCallback(async(e,t)=>{if(!e)return[];const c={project_id:e};t&&(c.method_id=t);try{const e=await s("tis.list_tests",MessageType.Request,c);if(e?.success&&e.data?.tests)return e.data.tests}catch(e){}return[]},[s]),M=useCallback(async(e,t,c)=>{if(!e||!t||!c)return null;try{const a=await s("tis.read_test",MessageType.Request,{project_id:e,method_id:t,run_id:c}),r=await s("tis.read_cycles",MessageType.Request,{project_id:e,method_id:t,run_id:c,offset:0,limit:1e3,order:"asc"});if(!a?.success)return null;const d={meta:a.data??null,cycles:r?.success?r.data?.cycles??[]:[],results:a.data?.results??{},rawData:I.current[c]?.rawData??{}};return I.current[c]=d,h(),d}catch(e){return null}},[s,h]),k=useMemo(()=>({...I.current}),[y]),[R,P]=useState([]),b=useRef(new Set),[F,w]=useState(0),[Y,A]=useState({}),L=useCallback(async()=>{try{const e=await s("tis.list_projects",MessageType.Request,{});e?.success&&e.data?.projects&&P(e.data.projects)}catch(e){}},[s]),N=useCallback(e=>{e&&(b.current.has(e)||(b.current.add(e),w(e=>e+1)))},[]),D=useCallback(e=>!!e&&(!!b.current.has(e)||R.includes(e)),[R,F]),q=useCallback((e,t)=>{e&&A(s=>({...s,[e]:t}))},[]),O=useCallback(async e=>{if(!e)return null;try{const t=await s("tis.read_project",MessageType.Request,{project_id:e});if(t?.success){const s=t.data?.project_fields??{};return A(t=>({...t,[e]:s})),s}}catch(e){}return A(t=>({...t,[e]:{}})),{}},[s]);useEffect(()=>{L()},[L]),useEffect(()=>{const e=c("tis.project_created",()=>{L()}),t=c("tis.project_updated",e=>{const t="string"==typeof e?.project_id?e.project_id:"";t&&O(t)});return()=>{a(e),a(t)}},[c,a,L,O]),useEffect(()=>{const e=S.projectId;e&&D(e)&&void 0===Y[e]&&O(e)},[S.projectId,D,Y,O]);const G=Y[S.projectId]??{},V=void 0!==Y[S.projectId],[B,J]=useState({}),K=useCallback(e=>{J(t=>({...t,...e}))},[]),[W,z]=useState(""),H=useCallback(e=>{z(e)},[]),[Q,U]=useState(""),X=useCallback(()=>{J({}),z(""),U("")},[]),[Z,$]=useState(EMPTY_CYCLE_CONFIG),ee=useMemo(()=>{const e=S.methodId?r[S.methodId]?.cycle_config_fields:void 0;return Array.isArray(e)?e:[]},[r,S.methodId]),te=useCallback(async()=>{const e=S.projectId,t=S.methodId;if(t)try{const c=await s("tis.cycle_config",MessageType.Request,{project_id:e,method_id:t});if(c?.success)return void $(cycleConfigFromPayload(c.data));$(EMPTY_CYCLE_CONFIG)}catch(e){$(EMPTY_CYCLE_CONFIG)}else $(EMPTY_CYCLE_CONFIG)},[s,S.projectId,S.methodId]),se=useCallback(async e=>{const t=S.projectId,c=S.methodId;if(c)try{const a=await s("tis.set_cycle_config",MessageType.Request,{project_id:t,method_id:c,values:e});a?.success&&$(cycleConfigFromPayload(a.data))}catch(e){}},[s,S.projectId,S.methodId]),ce=useCallback(async()=>{const e=S.projectId,t=S.methodId;if(t)try{const c=await s("tis.clear_cycle_config",MessageType.Request,{project_id:e,method_id:t});c?.success&&$(cycleConfigFromPayload(c.data))}catch(e){}},[s,S.projectId,S.methodId]);useEffect(()=>{te()},[te]),useEffect(()=>{const e=c("tis.cycle_config",e=>{const t=String(e?.method_id??"");t&&S.methodId&&t!==S.methodId||$(cycleConfigFromPayload(e))});return()=>{a(e)}},[c,a,S.methodId]);const ae=useMemo(()=>({schemas:r,projectAssetRefs:o,defaultMethodId:i,schemasLoaded:l,state:f,selection:S,setSelection:T,existingProjects:R,projectKnown:D,refreshProjects:L,markProjectJustCreated:N,projectFields:G,projectFieldsLoaded:V,loadProjectFields:O,setProjectFields:q,stagedConfig:B,setStagedConfig:K,clearStagedConfig:X,defaultsAppliedForMethod:W,markDefaultsAppliedForMethod:H,configurationName:Q,setConfigurationName:U,cycleConfig:Z,cycleConfigFields:ee,setCycleConfigValues:se,clearCycleConfig:ce,refreshCycleConfig:te,fetchRuns:x,fetchRun:M,runCache:k}),[r,o,i,l,f,S,T,R,D,L,N,G,V,O,q,B,K,X,W,H,Q,U,Z,ee,se,ce,te,x,M,k]);return _jsx(TisContext.Provider,{value:ae,children:e})};export const useTis=()=>useContext(TisContext);export const useTisSchemas=()=>useContext(TisContext).schemas;export const useTisState=()=>useContext(TisContext).state;export const useTisCycleConfig=()=>{const{cycleConfig:e,cycleConfigFields:t,setCycleConfigValues:s,clearCycleConfig:c}=useContext(TisContext);return{...e,fields:t,setValues:s,clear:c}};export const useTisSelection=()=>{const{selection:e,setSelection:t}=useContext(TisContext);return[e,t]};export const useTisRuns=(e,t)=>{const{fetchRuns:s}=useContext(TisContext),[c,a]=useState([]),[r,d]=useState(!1),o=useCallback(async()=>{if(e){d(!0);try{a(await s(e,t))}finally{d(!1)}}else a([])},[e,t,s]);return useEffect(()=>{o()},[o]),{runs:c,loading:r,refresh:o}};export const useTisRun=e=>{const{selection:t,fetchRun:s,runCache:c}=useContext(TisContext),[a,r]=useState(!1),d=e??t.runId;useEffect(()=>{if(!d)return;if(c[d]?.meta)return;const e=t.projectId,a=t.methodId;e&&a&&(r(!0),s(e,a,d).finally(()=>r(!1)))},[d,t.projectId,t.methodId,s,c]);const o=d?c[d]:null;return{meta:o?.meta??null,cycles:o?.cycles??[],results:o?.results??{},rawData:o?.rawData??{},loading:a}};export{TisContext};
@@ -1 +1 @@
1
- {"version":3,"file":"TisConfigEditor.d.ts","sourceRoot":"","sources":["../../../src/components/tis-editor/TisConfigEditor.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAWH,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAM5E,OAAO,uBAAuB,CAAC;AAE/B,MAAM,WAAW,oBAAoB;IACjC;+EAC2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,OAAO,CAAC,EAAE,aAAa,CAAC;CAC3B;AAkBD,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAyQ1D,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"TisConfigEditor.d.ts","sourceRoot":"","sources":["../../../src/components/tis-editor/TisConfigEditor.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAWH,OAAO,EAAgB,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAM5E,OAAO,uBAAuB,CAAC;AAE/B,MAAM,WAAW,oBAAoB;IACjC;+EAC2E;IAC3E,SAAS,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,OAAO,CAAC,EAAE,aAAa,CAAC;CAC3B;AAmBD,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAyQ1D,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -1 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useEffect,useMemo,useState}from"react";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Button}from"primereact/button";import{InputText}from"primereact/inputtext";import{Dialog}from"primereact/dialog";import{useContext}from"react";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";import{useTisConfig}from"../../hooks/useTisConfig";import{useAmsAssetTypes}from"../../hooks/useAmsAssetTypes";import{MethodFormEditor}from"./editor/MethodFormEditor";import{SaveDiffDialog}from"./editor/SaveDiffDialog";import"./TisConfigEditor.css";const EMPTY_METHOD={label:"",description:"",project_fields:[],config_fields:[],cycle_fields:[],results_fields:[],views:{},asset_refs:[]};export const TisConfigEditor=({projectId:e,invoker:t})=>{const s=useContext(EventEmitterContext),i=useMemo(()=>t??(async(e,t)=>await s.invoke(e,MessageType.Request,t)),[t,s]),o=useTisConfig(e,{invoker:i}),a=useAmsAssetTypes({invoker:i}),[n,r]=useState(null),[l,d]=useState(null),[c,m]=useState(!1),[f,p]=useState(!1),[h,u]=useState(!1),[_,g]=useState(""),x=useMemo(()=>o.config?Object.entries(o.config.methods).map(([e,t])=>({id:e,label:t?.label??""})):[],[o.config]);useEffect(()=>{if(!n&&o.config){const e=o.config.defaultMethodId||Object.keys(o.config.methods)[0]||null;r(e)}},[o.config,n]),useEffect(()=>{d(null)},[n]);return _jsxs("div",{className:"tis-editor",children:[_jsxs("header",{className:"tis-editor__header",children:[_jsxs("h2",{children:["Test Methods"," ",o.config?.dirty&&_jsx("span",{className:"tis-editor__dirty-pill",children:"unsaved"})]}),_jsxs("div",{className:"tis-editor__header-actions",children:[_jsx(Button,{label:"Save…",icon:"pi pi-save",disabled:c||!o.config?.dirty,onClick:()=>p(!0)}),_jsx(Button,{label:"Revert",icon:"pi pi-undo",className:"p-button-secondary",disabled:c||!o.config?.dirty,onClick:async()=>{if(window.confirm("Discard all in-progress edits? This cannot be undone.")){m(!0);try{await o.revert()}catch(e){d(String(e?.message??e))}finally{m(!1)}}}})]})]}),o.error&&_jsxs("div",{className:"tis-editor__error",children:[_jsx("strong",{children:"Error:"})," ",_jsx("pre",{children:o.error})]}),_jsxs("div",{className:"tis-editor__body",children:[_jsxs("aside",{className:"tis-editor__sidebar",children:[_jsxs("div",{className:"tis-editor__sidebar-actions",children:[_jsx(Button,{label:"New",icon:"pi pi-plus",disabled:c,onClick:()=>u(!0)}),_jsx(Button,{label:"Duplicate",icon:"pi pi-clone",className:"p-button-secondary",disabled:c||!n,onClick:async()=>{if(!n||!o.config)return;const e=o.config.methods[n];if(!e)return;let t=`${n}_copy`,s=2;for(;o.config.methods[t];)t=`${n}_copy_${s++}`;m(!0);try{await o.putMethod(t,JSON.parse(JSON.stringify(e))),r(t)}catch(e){d(String(e?.message??e))}finally{m(!1)}}}),_jsx(Button,{label:"Delete",icon:"pi pi-trash",className:"p-button-danger",disabled:c||!n,onClick:async()=>{if(n&&window.confirm(`Remove method "${n}"? This is staged — Save persists it.`)){m(!0);try{await o.removeMethod(n),r(null)}catch(e){d(String(e?.message??e))}finally{m(!1)}}}})]}),_jsxs(DataTable,{value:x,selection:x.find(e=>e.id===n)??null,onSelectionChange:e=>r(e.value?.id??null),selectionMode:"single",dataKey:"id",scrollable:!0,scrollHeight:"flex",emptyMessage:o.loading?"Loading…":"No test methods defined.",children:[_jsx(Column,{field:"id",header:"Method ID"}),_jsx(Column,{field:"label",header:"Label"})]})]}),_jsx("section",{className:"tis-editor__detail",children:n&&o.config?.methods[n]?_jsxs(_Fragment,{children:[l&&_jsx("div",{className:"tis-editor__error",children:_jsx("pre",{children:l})}),_jsx(MethodFormEditor,{methodId:n,method:o.config.methods[n],onApply:async e=>{if(n){m(!0);try{await o.putMethod(n,e),d(null)}catch(e){d(String(e?.message??e))}finally{m(!1)}}},busy:c,knownAssetTypes:a.types})]}):_jsx("div",{className:"tis-editor__empty",children:"Select a test method on the left, or create a new one."})})]}),_jsxs(Dialog,{header:"New Test Method",visible:h,onHide:()=>u(!1),style:{width:"24rem"},children:[_jsxs("label",{className:"tis-editor__new-method-label",children:["Method ID",_jsx(InputText,{value:_,onChange:e=>g(e.target.value),placeholder:"e.g. translational_traction",autoFocus:!0})]}),_jsx("small",{children:"Canonical key — appears in wire payloads, on-disk paths, and generated code."}),_jsxs("div",{style:{display:"flex",gap:"0.5rem",justifyContent:"flex-end",marginTop:"1rem"},children:[_jsx(Button,{label:"Cancel",className:"p-button-text",onClick:()=>u(!1)}),_jsx(Button,{label:"Create",disabled:!_.trim()||c,onClick:async()=>{const e=_.trim();if(e)if(o.config?.methods[e])d(`A method named "${e}" already exists.`);else{m(!0);try{await o.putMethod(e,EMPTY_METHOD),r(e),u(!1),g("")}catch(e){d(String(e?.message??e))}finally{m(!1)}}}})]})]}),_jsx(SaveDiffDialog,{visible:f,staged:o.config?.methods??{},invoker:i,onConfirm:async()=>{m(!0);try{await o.save(),p(!1)}catch(e){d(String(e?.message??e))}finally{m(!1)}},onCancel:()=>p(!1)})]})};export default TisConfigEditor;
1
+ import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useEffect,useMemo,useState}from"react";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Button}from"primereact/button";import{InputText}from"primereact/inputtext";import{Dialog}from"primereact/dialog";import{useContext}from"react";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";import{useTisConfig}from"../../hooks/useTisConfig";import{useAmsAssetTypes}from"../../hooks/useAmsAssetTypes";import{MethodFormEditor}from"./editor/MethodFormEditor";import{SaveDiffDialog}from"./editor/SaveDiffDialog";import"./TisConfigEditor.css";const EMPTY_METHOD={label:"",description:"",project_fields:[],config_fields:[],cycle_config_fields:[],cycle_fields:[],results_fields:[],views:{},asset_refs:[]};export const TisConfigEditor=({projectId:e,invoker:t})=>{const s=useContext(EventEmitterContext),i=useMemo(()=>t??(async(e,t)=>await s.invoke(e,MessageType.Request,t)),[t,s]),o=useTisConfig(e,{invoker:i}),a=useAmsAssetTypes({invoker:i}),[n,r]=useState(null),[l,d]=useState(null),[c,m]=useState(!1),[f,p]=useState(!1),[h,u]=useState(!1),[_,g]=useState(""),x=useMemo(()=>o.config?Object.entries(o.config.methods).map(([e,t])=>({id:e,label:t?.label??""})):[],[o.config]);useEffect(()=>{if(!n&&o.config){const e=o.config.defaultMethodId||Object.keys(o.config.methods)[0]||null;r(e)}},[o.config,n]),useEffect(()=>{d(null)},[n]);return _jsxs("div",{className:"tis-editor",children:[_jsxs("header",{className:"tis-editor__header",children:[_jsxs("h2",{children:["Test Methods"," ",o.config?.dirty&&_jsx("span",{className:"tis-editor__dirty-pill",children:"unsaved"})]}),_jsxs("div",{className:"tis-editor__header-actions",children:[_jsx(Button,{label:"Save…",icon:"pi pi-save",disabled:c||!o.config?.dirty,onClick:()=>p(!0)}),_jsx(Button,{label:"Revert",icon:"pi pi-undo",className:"p-button-secondary",disabled:c||!o.config?.dirty,onClick:async()=>{if(window.confirm("Discard all in-progress edits? This cannot be undone.")){m(!0);try{await o.revert()}catch(e){d(String(e?.message??e))}finally{m(!1)}}}})]})]}),o.error&&_jsxs("div",{className:"tis-editor__error",children:[_jsx("strong",{children:"Error:"})," ",_jsx("pre",{children:o.error})]}),_jsxs("div",{className:"tis-editor__body",children:[_jsxs("aside",{className:"tis-editor__sidebar",children:[_jsxs("div",{className:"tis-editor__sidebar-actions",children:[_jsx(Button,{label:"New",icon:"pi pi-plus",disabled:c,onClick:()=>u(!0)}),_jsx(Button,{label:"Duplicate",icon:"pi pi-clone",className:"p-button-secondary",disabled:c||!n,onClick:async()=>{if(!n||!o.config)return;const e=o.config.methods[n];if(!e)return;let t=`${n}_copy`,s=2;for(;o.config.methods[t];)t=`${n}_copy_${s++}`;m(!0);try{await o.putMethod(t,JSON.parse(JSON.stringify(e))),r(t)}catch(e){d(String(e?.message??e))}finally{m(!1)}}}),_jsx(Button,{label:"Delete",icon:"pi pi-trash",className:"p-button-danger",disabled:c||!n,onClick:async()=>{if(n&&window.confirm(`Remove method "${n}"? This is staged — Save persists it.`)){m(!0);try{await o.removeMethod(n),r(null)}catch(e){d(String(e?.message??e))}finally{m(!1)}}}})]}),_jsxs(DataTable,{value:x,selection:x.find(e=>e.id===n)??null,onSelectionChange:e=>r(e.value?.id??null),selectionMode:"single",dataKey:"id",scrollable:!0,scrollHeight:"flex",emptyMessage:o.loading?"Loading…":"No test methods defined.",children:[_jsx(Column,{field:"id",header:"Method ID"}),_jsx(Column,{field:"label",header:"Label"})]})]}),_jsx("section",{className:"tis-editor__detail",children:n&&o.config?.methods[n]?_jsxs(_Fragment,{children:[l&&_jsx("div",{className:"tis-editor__error",children:_jsx("pre",{children:l})}),_jsx(MethodFormEditor,{methodId:n,method:o.config.methods[n],onApply:async e=>{if(n){m(!0);try{await o.putMethod(n,e),d(null)}catch(e){d(String(e?.message??e))}finally{m(!1)}}},busy:c,knownAssetTypes:a.types})]}):_jsx("div",{className:"tis-editor__empty",children:"Select a test method on the left, or create a new one."})})]}),_jsxs(Dialog,{header:"New Test Method",visible:h,onHide:()=>u(!1),style:{width:"24rem"},children:[_jsxs("label",{className:"tis-editor__new-method-label",children:["Method ID",_jsx(InputText,{value:_,onChange:e=>g(e.target.value),placeholder:"e.g. translational_traction",autoFocus:!0})]}),_jsx("small",{children:"Canonical key — appears in wire payloads, on-disk paths, and generated code."}),_jsxs("div",{style:{display:"flex",gap:"0.5rem",justifyContent:"flex-end",marginTop:"1rem"},children:[_jsx(Button,{label:"Cancel",className:"p-button-text",onClick:()=>u(!1)}),_jsx(Button,{label:"Create",disabled:!_.trim()||c,onClick:async()=>{const e=_.trim();if(e)if(o.config?.methods[e])d(`A method named "${e}" already exists.`);else{m(!0);try{await o.putMethod(e,EMPTY_METHOD),r(e),u(!1),g("")}catch(e){d(String(e?.message??e))}finally{m(!1)}}}})]})]}),_jsx(SaveDiffDialog,{visible:f,staged:o.config?.methods??{},invoker:i,onConfirm:async()=>{m(!0);try{await o.save(),p(!1)}catch(e){d(String(e?.message??e))}finally{m(!1)}},onCancel:()=>p(!1)})]})};export default TisConfigEditor;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * CycleConfigSection — the two method-level settings that decide how the
3
+ * operator's per-row annotation behaves. The columns themselves are
4
+ * authored in the Fields tab; this is the state machine around them.
5
+ *
6
+ * The scope choice is the load-bearing one. A machine that records many
7
+ * cycles from one Start is not pausing for input between them, so its
8
+ * annotation has to latch once per Start ('run'). A machine the operator
9
+ * drives one cycle at a time — the six-position sweep — annotates each row
10
+ * separately ('cycle'), which means the gate re-arms between cycles and
11
+ * the control program must wait on `tis.cycle_ready`.
12
+ */
13
+ import type { TestMethod } from '../types';
14
+ export interface CycleConfigSectionProps {
15
+ method: TestMethod;
16
+ onChange: (next: TestMethod) => void;
17
+ }
18
+ export declare const CycleConfigSection: React.FC<CycleConfigSectionProps>;
19
+ //# sourceMappingURL=CycleConfigSection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CycleConfigSection.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/CycleConfigSection.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EAA+B,UAAU,EAAE,MAAM,UAAU,CAAC;AAOxE,MAAM,WAAW,uBAAuB;IACpC,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;CACxC;AAED,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CA+DhE,CAAC"}
@@ -0,0 +1 @@
1
+ import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import{Dropdown}from"primereact/dropdown";import{Checkbox}from"primereact/checkbox";import{FormSection}from"../../forms/FormSection";import{FormRow}from"../../forms/FormRow";const SCOPES=[{label:"run — one entry per Start, stamped on every cycle of the run",value:"run"},{label:"cycle — one entry per cycle, re-entered between cycles",value:"cycle"}];export const CycleConfigSection=({method:e,onChange:o})=>{const t=e.cycle_config_fields??[],r="cycle"===e.cycle_config_scope?"cycle":"run",n=(t,r,n)=>{const c={...e};n?delete c[t]:c[t]=r,o(c)};return _jsxs(FormSection,{title:"Cycle Configuration",description:'How the operator-entered annotation columns behave. Add the columns themselves on the Fields tab under "Cycle Configuration Fields".',children:[0===t.length&&_jsx("div",{style:{color:"#6b7280",marginBottom:"0.75rem"},children:"This method declares no Cycle Configuration fields, so these settings have no effect."}),_jsx(FormRow,{label:"Scope",hint:'When the operator\'s entry is latched and cleared. Choose "cycle" only if the machine actually waits between cycles — under "cycle" the gate drops after every row and the control program must hold until tis.cycle_ready comes back true.',children:_jsx(Dropdown,{value:r,options:SCOPES,onChange:e=>n("cycle_config_scope",e.value,"run"===e.value)})}),_jsx(FormRow,{label:"Prompt before cycle",hint:"Open the entry dialog at the boundary instead of relying on the operator having filled the Cycle tab. Costs a confirmation each time — worth it when a mislabelled row means re-running the sample, not worth it for a non-critical note.",children:_jsx(Checkbox,{checked:!!e.prompt_before_cycle,onChange:e=>n("prompt_before_cycle",!!e.checked,!e.checked)})})]})};
@@ -1 +1 @@
1
- {"version":3,"file":"FieldArrayEditor.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/FieldArrayEditor.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAa,UAAU,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAuBrE,MAAM,WAAW,qBAAqB;IAClC,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;CACxC;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAgH5D,CAAC"}
1
+ {"version":3,"file":"FieldArrayEditor.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/FieldArrayEditor.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAa,UAAU,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAyBrE,MAAM,WAAW,qBAAqB;IAClC,QAAQ,EAAE,aAAa,CAAC;IACxB,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;CACxC;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAwH5D,CAAC"}
@@ -1 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useState}from"react";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Button}from"primereact/button";import{FormSection}from"../../forms/FormSection";import{TestFieldDialog}from"./TestFieldDialog";const TITLES={project_fields:"Project Fields",config_fields:"Config Fields",cycle_fields:"Cycle Fields",results_fields:"Results Fields"},DESCRIPTIONS={project_fields:"System-level fields (operator, station). Filled by the HMI but not cycled.",config_fields:"Operator-input config (speeds, loads). Snapshotted into test.json on start.",cycle_fields:"Per-cycle capture. One row appended to cycles.jsonl per cycle.",results_fields:"Post-test summary (min/max/avg, pass/fail). Written once at finish. A field can also be a cross-test Aggregate (avg/min/max/stddev/count over tests sharing a sample_id)."},aggregateLabel=e=>{const t=e.aggregate;return t?"count"===t.fn?"count":`${t.fn}(${t.of??"?"})`:""};export const FieldArrayEditor=({arrayKey:e,method:t,onChange:s})=>{const l=t[e]??[],[i,o]=useState(!1),[n,a]=useState(null),r=(i,o)=>{const n=i+o;if(n<0||n>=l.length)return;const a=[...l];[a[i],a[n]]=[a[n],a[i]],s({...t,[e]:a})};return _jsxs(_Fragment,{children:[_jsx(FormSection,{title:TITLES[e],description:DESCRIPTIONS[e],actions:_jsx(Button,{label:"Add field",icon:"pi pi-plus",size:"small",onClick:()=>{a(null),o(!0)}}),children:_jsxs(DataTable,{value:l,dataKey:"name",emptyMessage:"No fields defined.",children:[_jsx(Column,{field:"name",header:"Name"}),_jsx(Column,{field:"type",header:"Type",style:{width:"6rem"}}),_jsx(Column,{field:"units",header:"Units",style:{width:"6rem"}}),_jsx(Column,{header:"Req",body:e=>e.required?"✓":"",style:{width:"4rem"}}),_jsx(Column,{field:"label",header:"Label"}),"results_fields"===e&&_jsx(Column,{header:"Aggregate",body:e=>aggregateLabel(e),style:{width:"8rem"}}),_jsx(Column,{header:"",body:(i,n)=>_jsxs("div",{style:{display:"flex",gap:"0.25rem"},children:[_jsx(Button,{icon:"pi pi-arrow-up",className:"p-button-text p-button-sm",disabled:0===n.rowIndex,onClick:()=>r(n.rowIndex,-1),"aria-label":"Move up"}),_jsx(Button,{icon:"pi pi-arrow-down",className:"p-button-text p-button-sm",disabled:n.rowIndex===l.length-1,onClick:()=>r(n.rowIndex,1),"aria-label":"Move down"}),_jsx(Button,{icon:"pi pi-pencil",className:"p-button-text p-button-sm",onClick:()=>{return e=n.rowIndex,a(e),void o(!0);var e},"aria-label":"Edit"}),_jsx(Button,{icon:"pi pi-trash",className:"p-button-text p-button-danger p-button-sm",onClick:()=>(i=>{const o=l[i];if(!o)return;if(!window.confirm(`Remove field "${o.name}"?`))return;const n=l.filter((e,t)=>t!==i);s({...t,[e]:n})})(n.rowIndex),"aria-label":"Remove"})]}),style:{width:"10rem"}})]})}),_jsx(TestFieldDialog,{visible:i,initial:null!==n?l[n]??null:null,siblingNames:l.map(e=>e.name),isResultsField:"results_fields"===e,cycleFieldNames:(t.cycle_fields??[]).map(e=>e.name).filter(Boolean),resultsFieldNames:(t.results_fields??[]).map(e=>e.name).filter(Boolean),onCancel:()=>o(!1),onSave:i=>{const a=[...l];null===n?a.push(i):a[n]=i,s({...t,[e]:a}),o(!1)}})]})};
1
+ import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useState}from"react";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Button}from"primereact/button";import{FormSection}from"../../forms/FormSection";import{TestFieldDialog}from"./TestFieldDialog";const TITLES={project_fields:"Project Fields",config_fields:"Config Fields",cycle_config_fields:"Cycle Configuration Fields",cycle_fields:"Cycle Fields",results_fields:"Results Fields"},DESCRIPTIONS={project_fields:"System-level fields (operator, station). Filled by the HMI but not cycled.",config_fields:"Operator-input config (speeds, loads). Snapshotted into test.json on start.",cycle_config_fields:"Operator-entered annotation recorded onto every cycle row (e.g. which of six positions this is). Shown on the HMI Cycle tab and written as the first Cycle Data columns after the row index. Mark them Required so the machine cannot start unannotated, and Autoclear so the next row cannot inherit a stale value.",cycle_fields:"Per-cycle capture. One row appended to cycles.jsonl per cycle.",results_fields:"Post-test summary (min/max/avg, pass/fail). Written once at finish. A field can also be a cross-test Aggregate (avg/min/max/stddev/count over tests sharing a sample_id)."},aggregateLabel=e=>{const t=e.aggregate;return t?"count"===t.fn?"count":`${t.fn}(${t.of??"?"})`:""};export const FieldArrayEditor=({arrayKey:e,method:t,onChange:s})=>{const l=t[e]??[],[o,i]=useState(!1),[n,a]=useState(null),r=(o,i)=>{const n=o+i;if(n<0||n>=l.length)return;const a=[...l];[a[o],a[n]]=[a[n],a[o]],s({...t,[e]:a})};return _jsxs(_Fragment,{children:[_jsx(FormSection,{title:TITLES[e],description:DESCRIPTIONS[e],actions:_jsx(Button,{label:"Add field",icon:"pi pi-plus",size:"small",onClick:()=>{a(null),i(!0)}}),children:_jsxs(DataTable,{value:l,dataKey:"name",emptyMessage:"No fields defined.",children:[_jsx(Column,{field:"name",header:"Name"}),_jsx(Column,{field:"type",header:"Type",style:{width:"6rem"}}),_jsx(Column,{field:"units",header:"Units",style:{width:"6rem"}}),_jsx(Column,{header:"Req",body:e=>e.required?"✓":"",style:{width:"4rem"}}),_jsx(Column,{field:"label",header:"Label"}),"results_fields"===e&&_jsx(Column,{header:"Aggregate",body:e=>aggregateLabel(e),style:{width:"8rem"}}),"cycle_config_fields"===e&&_jsx(Column,{header:"Autoclear",body:e=>e.autoclear?"✓":"",style:{width:"6rem"}}),_jsx(Column,{header:"",body:(o,n)=>_jsxs("div",{style:{display:"flex",gap:"0.25rem"},children:[_jsx(Button,{icon:"pi pi-arrow-up",className:"p-button-text p-button-sm",disabled:0===n.rowIndex,onClick:()=>r(n.rowIndex,-1),"aria-label":"Move up"}),_jsx(Button,{icon:"pi pi-arrow-down",className:"p-button-text p-button-sm",disabled:n.rowIndex===l.length-1,onClick:()=>r(n.rowIndex,1),"aria-label":"Move down"}),_jsx(Button,{icon:"pi pi-pencil",className:"p-button-text p-button-sm",onClick:()=>{return e=n.rowIndex,a(e),void i(!0);var e},"aria-label":"Edit"}),_jsx(Button,{icon:"pi pi-trash",className:"p-button-text p-button-danger p-button-sm",onClick:()=>(o=>{const i=l[o];if(!i)return;if(!window.confirm(`Remove field "${i.name}"?`))return;const n=l.filter((e,t)=>t!==o);s({...t,[e]:n})})(n.rowIndex),"aria-label":"Remove"})]}),style:{width:"10rem"}})]})}),_jsx(TestFieldDialog,{visible:o,initial:null!==n?l[n]??null:null,siblingNames:l.map(e=>e.name),isResultsField:"results_fields"===e,isCycleConfigField:"cycle_config_fields"===e,cycleFieldNames:(t.cycle_fields??[]).map(e=>e.name).filter(Boolean),resultsFieldNames:(t.results_fields??[]).map(e=>e.name).filter(Boolean),onCancel:()=>i(!1),onSave:o=>{const a=[...l];null===n?a.push(o):a[n]=o,s({...t,[e]:a}),i(!1)}})]})};
@@ -1 +1 @@
1
- {"version":3,"file":"MethodFormEditor.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/MethodFormEditor.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAcH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,WAAW,qBAAqB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,6EAA6E;IAC7E,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACpD,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,IAAI,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAkJ5D,CAAC"}
1
+ {"version":3,"file":"MethodFormEditor.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/MethodFormEditor.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAeH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,WAAW,qBAAqB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,6EAA6E;IAC7E,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACpD,4EAA4E;IAC5E,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,IAAI,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,gBAAgB,EAAE,KAAK,CAAC,EAAE,CAAC,qBAAqB,CAuJ5D,CAAC"}
@@ -1 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import{useEffect,useMemo,useState}from"react";import{TabView,TabPanel}from"primereact/tabview";import{Button}from"primereact/button";import{CodeEditor}from"../../CodeEditor";import{IdentitySection}from"./IdentitySection";import{FieldArrayEditor}from"./FieldArrayEditor";import{ConfigurationsEditor}from"./ConfigurationsEditor";import{ViewsEditor}from"./ViewsEditor";import{RawDataEditor}from"./RawDataEditor";import{AssetRefsEditor}from"./AssetRefsEditor";import{AnalysisEditor}from"./AnalysisEditor";import{validateMethod}from"../validation";export const MethodFormEditor=({methodId:e,method:t,onApply:r,knownAssetTypes:i=[],busy:o})=>{const[s,n]=useState(t),[a,d]=useState(""),[l,h]=useState(null),[m,c]=useState(0);useEffect(()=>{n(t),d(JSON.stringify(t,null,2)),h(null)},[t,e]);const f=useMemo(()=>{try{return JSON.stringify(s)!==JSON.stringify(t)}catch{return!0}},[s,t]),y=useMemo(()=>validateMethod(e,s),[s,e]),g=e=>{n(e),d(JSON.stringify(e,null,2))};return _jsxs("div",{style:{display:"flex",flexDirection:"column",height:"100%",minHeight:0},children:[_jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"0.5rem 1rem",borderBottom:"1px solid var(--surface-d, #e2e8f0)",background:"var(--surface-b, #f8fafc)"},children:[_jsxs("strong",{children:[e,f&&_jsx("span",{style:{marginLeft:"0.5rem",color:"#ea580c"},children:"•"}),y.length>0&&_jsxs("span",{title:y.map(e=>e.message).join("\n"),style:{marginLeft:"0.5rem",background:"#fef2f2",color:"#991b1b",padding:"0.1rem 0.5rem",borderRadius:4,fontSize:"0.75rem",fontWeight:600,cursor:"help"},children:[y.length," issue",1===y.length?"":"s"]})]}),_jsx("div",{style:{display:"flex",gap:"0.5rem"},children:_jsx(Button,{label:"Apply",icon:"pi pi-check",disabled:o||!f||!!l||y.length>0,onClick:async()=>{l||await r(s)}})})]}),_jsx("div",{style:{flex:1,minHeight:0,overflow:"auto"},children:_jsxs(TabView,{activeIndex:m,onTabChange:e=>c(e.index),children:[_jsx(TabPanel,{header:"Identity",children:_jsx(IdentitySection,{method:s,onChange:g})}),_jsxs(TabPanel,{header:"Fields",children:[_jsx(FieldArrayEditor,{arrayKey:"project_fields",method:s,onChange:g}),_jsx(FieldArrayEditor,{arrayKey:"config_fields",method:s,onChange:g}),_jsx(FieldArrayEditor,{arrayKey:"cycle_fields",method:s,onChange:g}),_jsx(FieldArrayEditor,{arrayKey:"results_fields",method:s,onChange:g})]}),_jsx(TabPanel,{header:"Configurations",children:_jsx(ConfigurationsEditor,{method:s,onChange:g})}),_jsx(TabPanel,{header:"Views",children:_jsx(ViewsEditor,{method:s,onChange:g})}),_jsx(TabPanel,{header:"Raw Data",children:_jsx(RawDataEditor,{method:s,onChange:g})}),_jsx(TabPanel,{header:"Assets",children:_jsx(AssetRefsEditor,{method:s,onChange:g,knownAssetTypes:i})}),_jsx(TabPanel,{header:"Analysis",children:_jsx(AnalysisEditor,{method:s,onChange:g})}),_jsxs(TabPanel,{header:"JSON",children:[l&&_jsx("div",{style:{color:"#dc2626",marginBottom:"0.5rem",fontFamily:"monospace"},children:l}),_jsx("div",{style:{height:"60vh"},children:_jsx(CodeEditor,{code:a,language:"json",theme:"vs-dark",readOnly:!1,cursorStyle:"line",onCodeChanged:e=>{const t=e??"";d(t);try{const e=JSON.parse(t);n(e),h(null)}catch(e){h(String(e?.message??e))}}})})]})]})})]})};
1
+ import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import{useEffect,useMemo,useState}from"react";import{TabView,TabPanel}from"primereact/tabview";import{Button}from"primereact/button";import{CodeEditor}from"../../CodeEditor";import{IdentitySection}from"./IdentitySection";import{FieldArrayEditor}from"./FieldArrayEditor";import{CycleConfigSection}from"./CycleConfigSection";import{ConfigurationsEditor}from"./ConfigurationsEditor";import{ViewsEditor}from"./ViewsEditor";import{RawDataEditor}from"./RawDataEditor";import{AssetRefsEditor}from"./AssetRefsEditor";import{AnalysisEditor}from"./AnalysisEditor";import{validateMethod}from"../validation";export const MethodFormEditor=({methodId:e,method:t,onApply:r,knownAssetTypes:i=[],busy:o})=>{const[n,s]=useState(t),[a,d]=useState(""),[l,h]=useState(null),[c,m]=useState(0);useEffect(()=>{s(t),d(JSON.stringify(t,null,2)),h(null)},[t,e]);const f=useMemo(()=>{try{return JSON.stringify(n)!==JSON.stringify(t)}catch{return!0}},[n,t]),y=useMemo(()=>validateMethod(e,n),[n,e]),g=e=>{s(e),d(JSON.stringify(e,null,2))};return _jsxs("div",{style:{display:"flex",flexDirection:"column",height:"100%",minHeight:0},children:[_jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"0.5rem 1rem",borderBottom:"1px solid var(--surface-d, #e2e8f0)",background:"var(--surface-b, #f8fafc)"},children:[_jsxs("strong",{children:[e,f&&_jsx("span",{style:{marginLeft:"0.5rem",color:"#ea580c"},children:"•"}),y.length>0&&_jsxs("span",{title:y.map(e=>e.message).join("\n"),style:{marginLeft:"0.5rem",background:"#fef2f2",color:"#991b1b",padding:"0.1rem 0.5rem",borderRadius:4,fontSize:"0.75rem",fontWeight:600,cursor:"help"},children:[y.length," issue",1===y.length?"":"s"]})]}),_jsx("div",{style:{display:"flex",gap:"0.5rem"},children:_jsx(Button,{label:"Apply",icon:"pi pi-check",disabled:o||!f||!!l||y.length>0,onClick:async()=>{l||await r(n)}})})]}),_jsx("div",{style:{flex:1,minHeight:0,overflow:"auto"},children:_jsxs(TabView,{activeIndex:c,onTabChange:e=>m(e.index),children:[_jsx(TabPanel,{header:"Identity",children:_jsx(IdentitySection,{method:n,onChange:g})}),_jsxs(TabPanel,{header:"Fields",children:[_jsx(FieldArrayEditor,{arrayKey:"project_fields",method:n,onChange:g}),_jsx(FieldArrayEditor,{arrayKey:"config_fields",method:n,onChange:g}),_jsx(FieldArrayEditor,{arrayKey:"cycle_config_fields",method:n,onChange:g}),_jsx(CycleConfigSection,{method:n,onChange:g}),_jsx(FieldArrayEditor,{arrayKey:"cycle_fields",method:n,onChange:g}),_jsx(FieldArrayEditor,{arrayKey:"results_fields",method:n,onChange:g})]}),_jsx(TabPanel,{header:"Configurations",children:_jsx(ConfigurationsEditor,{method:n,onChange:g})}),_jsx(TabPanel,{header:"Views",children:_jsx(ViewsEditor,{method:n,onChange:g})}),_jsx(TabPanel,{header:"Raw Data",children:_jsx(RawDataEditor,{method:n,onChange:g})}),_jsx(TabPanel,{header:"Assets",children:_jsx(AssetRefsEditor,{method:n,onChange:g,knownAssetTypes:i})}),_jsx(TabPanel,{header:"Analysis",children:_jsx(AnalysisEditor,{method:n,onChange:g})}),_jsxs(TabPanel,{header:"JSON",children:[l&&_jsx("div",{style:{color:"#dc2626",marginBottom:"0.5rem",fontFamily:"monospace"},children:l}),_jsx("div",{style:{height:"60vh"},children:_jsx(CodeEditor,{code:a,language:"json",theme:"vs-dark",readOnly:!1,cursorStyle:"line",onCodeChanged:e=>{const t=e??"";d(t);try{const e=JSON.parse(t);s(e),h(null)}catch(e){h(String(e?.message??e))}}})})]})]})})]})};
@@ -9,6 +9,9 @@ export interface TestFieldDialogProps {
9
9
  /** True when this field belongs to `results_fields` — only then is the
10
10
  * cross-test Aggregate section shown. */
11
11
  isResultsField?: boolean;
12
+ /** True when this field belongs to `cycle_config_fields` — only then is
13
+ * the Autoclear option shown, and only there does it do anything. */
14
+ isCycleConfigField?: boolean;
12
15
  /** Candidate target field names for an aggregate's `of`, by source. */
13
16
  cycleFieldNames?: string[];
14
17
  resultsFieldNames?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"TestFieldDialog.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/TestFieldDialog.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAiC,MAAM,UAAU,CAAC;AA4BzE,MAAM,WAAW,oBAAoB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,SAAS,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACnC,8EAA8E;IAC9E,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB;8CAC0C;IAC1C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,uEAAuE;IACvE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAID,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAuN1D,CAAC"}
1
+ {"version":3,"file":"TestFieldDialog.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/TestFieldDialog.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAiC,MAAM,UAAU,CAAC;AA4BzE,MAAM,WAAW,oBAAoB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,SAAS,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACnC,8EAA8E;IAC9E,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB;8CAC0C;IAC1C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;0EACsE;IACtE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,uEAAuE;IACvE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAID,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAwO1D,CAAC"}
@@ -1 +1 @@
1
- import{jsx as _jsx,Fragment as _Fragment,jsxs as _jsxs}from"react/jsx-runtime";import{useEffect,useState}from"react";import{Dialog}from"primereact/dialog";import{Button}from"primereact/button";import{InputText}from"primereact/inputtext";import{InputTextarea}from"primereact/inputtextarea";import{Dropdown}from"primereact/dropdown";import{Checkbox}from"primereact/checkbox";import{InputNumber}from"primereact/inputnumber";import{FormRow}from"../../forms/FormRow";const FIELD_TYPES=[{label:"string",value:"string"},{label:"i32",value:"i32"},{label:"i64",value:"i64"},{label:"u32",value:"u32"},{label:"u64",value:"u64"},{label:"f32",value:"f32"},{label:"f64",value:"f64"},{label:"bool",value:"bool"}],AGG_FNS=[{label:"— none (analysis-fed) —",value:""},{label:"avg",value:"avg"},{label:"min",value:"min"},{label:"max",value:"max"},{label:"stddev",value:"stddev"},{label:"count (# included tests)",value:"count"}],AGG_FROMS=[{label:"cycle field (every cycle, every test)",value:"cycle_fields"},{label:"result (one value per test)",value:"results"}],blank={name:"",type:"f32"};export const TestFieldDialog=({visible:e,initial:a,onCancel:t,onSave:l,siblingNames:r,isResultsField:n=!1,cycleFieldNames:i=[],resultsFieldNames:o=[]})=>{const[s,u]=useState(blank),[m,d]=useState(""),[c,g]=useState(null);useEffect(()=>{e&&(u(a?{...a}:{...blank}),d(null==a?.default?"":String(a.default)),g(null))},[e,a]);const p=s.aggregate,f=p?.from??"cycle_fields",v=("results"===f?o:i).filter(e=>e&&e!==s.name).map(e=>({label:e,value:e}));return _jsxs(Dialog,{header:a?`Edit field: ${a.name}`:"New field",visible:e,onHide:t,style:{width:"36rem"},children:[c&&_jsx("div",{style:{color:"#dc2626",marginBottom:"0.75rem"},children:c}),_jsx(FormRow,{label:"Name",required:!0,hint:"Wire-format key. Also the column name in CSV exports.",children:_jsx(InputText,{value:s.name,onChange:e=>u({...s,name:e.target.value})})}),_jsx(FormRow,{label:"Type",required:!0,children:_jsx(Dropdown,{value:s.type,options:FIELD_TYPES,onChange:e=>u({...s,type:e.value}),editable:!0})}),_jsx(FormRow,{label:"Units",hint:"Display label, appended to form labels (e.g. m/s).",children:_jsx(InputText,{value:s.units??"",onChange:e=>u({...s,units:e.target.value||void 0})})}),_jsx(FormRow,{label:"Label",hint:"Pretty form label. Falls back to name when empty.",children:_jsx(InputText,{value:s.label??"",onChange:e=>u({...s,label:e.target.value||void 0})})}),_jsx(FormRow,{label:"Required",children:_jsx(Checkbox,{checked:!!s.required,onChange:e=>u({...s,required:!!e.checked})})}),_jsx(FormRow,{label:"Source",hint:"Optional gm.* variable to bind this field to.",children:_jsx(InputText,{value:s.source??"",onChange:e=>u({...s,source:e.target.value||void 0}),placeholder:"gm.<variable_name>"})}),_jsx(FormRow,{label:"Scale",hint:"display = raw × scale. Leave blank for 1.0 (no conversion).",children:_jsx(InputNumber,{value:s.scale??null,onValueChange:e=>u({...s,scale:"number"==typeof e.value?e.value:void 0}),mode:"decimal",minFractionDigits:0,maxFractionDigits:9})}),_jsx(FormRow,{label:"Min",hint:"Optional lower bound the operator can enter (display units). Blank = no minimum.",children:_jsx(InputNumber,{value:s.min??null,onValueChange:e=>u({...s,min:"number"==typeof e.value?e.value:void 0}),mode:"decimal",minFractionDigits:0,maxFractionDigits:9})}),_jsx(FormRow,{label:"Max",hint:"Optional upper bound the operator can enter (display units). Blank = no maximum.",children:_jsx(InputNumber,{value:s.max??null,onValueChange:e=>u({...s,max:"number"==typeof e.value?e.value:void 0}),mode:"decimal",minFractionDigits:0,maxFractionDigits:9})}),_jsx(FormRow,{label:"Default",hint:"Applied every time the method loads (operator can override). A literal (display units) like 5, or an FQDN token like ${gm.safe_speed} that snapshots that value on load.",children:_jsx(InputText,{value:m,onChange:e=>d(e.target.value),placeholder:"e.g. 5 or ${gm.safe_speed}"})}),n&&_jsxs(_Fragment,{children:[_jsx("div",{style:{borderTop:"1px solid #e5e7eb",margin:"0.75rem 0 0.25rem"}}),_jsx(FormRow,{label:"Aggregate",hint:"Cross-test rollup. When set, this result is derived server-side over the non-excluded tests sharing the same sample_id — the analysis script does not supply it.",children:_jsx(Dropdown,{value:p?.fn??"",options:AGG_FNS,onChange:e=>(e=>{if(""===e)return void u({...s,aggregate:void 0});if("count"===e)return void u({...s,aggregate:{fn:"count"}});const a={fn:e,of:s.aggregate?.of,from:s.aggregate?.from??"cycle_fields"};u({...s,aggregate:a})})(e.value)})}),p&&"count"!==p.fn&&_jsxs(_Fragment,{children:[_jsx(FormRow,{label:"Aggregate from",hint:"Which per-test source the target field is read from.",children:_jsx(Dropdown,{value:f,options:AGG_FROMS,onChange:e=>u({...s,aggregate:{...p,from:e.value}})})}),_jsx(FormRow,{label:"Aggregate of",required:!0,hint:"The field to reduce across tests.",children:_jsx(Dropdown,{value:p.of??null,options:v,onChange:e=>u({...s,aggregate:{...p,of:e.value||void 0}}),editable:!0,placeholder:v.length?"Select a field":`No ${f} defined yet`})})]})]}),_jsx(FormRow,{label:"Description",hint:"Hover tooltip in the form.",children:_jsx(InputTextarea,{rows:2,value:s.description??"",onChange:e=>u({...s,description:e.target.value||void 0})})}),_jsxs("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.5rem",marginTop:"1rem"},children:[_jsx(Button,{label:"Cancel",className:"p-button-text",onClick:t}),_jsx(Button,{label:"Save",onClick:()=>{const e={...s},t=(e=>{const a=e.trim();if(""!==a){if(a.startsWith("${"))return a;if("true"===a)return!0;if("false"===a)return!1;if(/^-?\d*\.?\d+$/.test(a)){const e=Number(a);if(Number.isFinite(e))return e}return a}})(m);void 0===t?delete e.default:e.default=t;const n=(i=e).name.trim()?/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(i.name)?r.some(e=>e===i.name&&e!==a?.name)?`A field named "${i.name}" already exists in this array.`:"number"==typeof i.min&&"number"==typeof i.max&&i.min>i.max?`Min (${i.min}) cannot be greater than Max (${i.max}).`:i.aggregate&&"count"!==i.aggregate.fn&&!i.aggregate.of?.trim()?`Aggregate "${i.aggregate.fn}" needs a target field (the field to ${i.aggregate.fn} across tests).`:null:"Name must be a valid identifier (letters, digits, underscore; cannot start with a digit).":"Field name is required.";var i;n?g(n):l(e)}})]})]})};
1
+ import{jsx as _jsx,Fragment as _Fragment,jsxs as _jsxs}from"react/jsx-runtime";import{useEffect,useState}from"react";import{Dialog}from"primereact/dialog";import{Button}from"primereact/button";import{InputText}from"primereact/inputtext";import{InputTextarea}from"primereact/inputtextarea";import{Dropdown}from"primereact/dropdown";import{Checkbox}from"primereact/checkbox";import{InputNumber}from"primereact/inputnumber";import{FormRow}from"../../forms/FormRow";const FIELD_TYPES=[{label:"string",value:"string"},{label:"i32",value:"i32"},{label:"i64",value:"i64"},{label:"u32",value:"u32"},{label:"u64",value:"u64"},{label:"f32",value:"f32"},{label:"f64",value:"f64"},{label:"bool",value:"bool"}],AGG_FNS=[{label:"— none (analysis-fed) —",value:""},{label:"avg",value:"avg"},{label:"min",value:"min"},{label:"max",value:"max"},{label:"stddev",value:"stddev"},{label:"count (# included tests)",value:"count"}],AGG_FROMS=[{label:"cycle field (every cycle, every test)",value:"cycle_fields"},{label:"result (one value per test)",value:"results"}],blank={name:"",type:"f32"};export const TestFieldDialog=({visible:e,initial:a,onCancel:t,onSave:l,siblingNames:r,isResultsField:n=!1,isCycleConfigField:i=!1,cycleFieldNames:o=[],resultsFieldNames:s=[]})=>{const[u,d]=useState(blank),[m,c]=useState(""),[g,h]=useState(null);useEffect(()=>{e&&(d(a?{...a}:{...blank}),c(null==a?.default?"":String(a.default)),h(null))},[e,a]);const p=u.aggregate,f=p?.from??"cycle_fields",v=("results"===f?s:o).filter(e=>e&&e!==u.name).map(e=>({label:e,value:e}));return _jsxs(Dialog,{header:a?`Edit field: ${a.name}`:"New field",visible:e,onHide:t,style:{width:"36rem"},children:[g&&_jsx("div",{style:{color:"#dc2626",marginBottom:"0.75rem"},children:g}),_jsx(FormRow,{label:"Name",required:!0,hint:"Wire-format key. Also the column name in CSV exports.",children:_jsx(InputText,{value:u.name,onChange:e=>d({...u,name:e.target.value})})}),_jsx(FormRow,{label:"Type",required:!0,children:_jsx(Dropdown,{value:u.type,options:FIELD_TYPES,onChange:e=>d({...u,type:e.value}),editable:!0})}),_jsx(FormRow,{label:"Units",hint:"Display label, appended to form labels (e.g. m/s).",children:_jsx(InputText,{value:u.units??"",onChange:e=>d({...u,units:e.target.value||void 0})})}),_jsx(FormRow,{label:"Label",hint:"Pretty form label. Falls back to name when empty.",children:_jsx(InputText,{value:u.label??"",onChange:e=>d({...u,label:e.target.value||void 0})})}),_jsx(FormRow,{label:"Required",hint:i?"Blocks the machine until the operator fills this in — tis.cycle_ready stays false and start_test is refused. This is what stops an unannotated row from being recorded.":void 0,children:_jsx(Checkbox,{checked:!!u.required,onChange:e=>d({...u,required:!!e.checked})})}),i&&_jsx(FormRow,{label:"Autoclear",hint:"Clear the operator's entry once a row has recorded it, so the next row can't inherit a stale value. Fires after each cycle under 'cycle' scope, at the end of the run under 'run' scope. Pair with Required — on its own it turns stale data into silently missing data.",children:_jsx(Checkbox,{checked:!!u.autoclear,onChange:e=>d({...u,autoclear:!!e.checked})})}),_jsx(FormRow,{label:"Source",hint:"Optional gm.* variable to bind this field to.",children:_jsx(InputText,{value:u.source??"",onChange:e=>d({...u,source:e.target.value||void 0}),placeholder:"gm.<variable_name>"})}),_jsx(FormRow,{label:"Scale",hint:"display = raw × scale. Leave blank for 1.0 (no conversion).",children:_jsx(InputNumber,{value:u.scale??null,onValueChange:e=>d({...u,scale:"number"==typeof e.value?e.value:void 0}),mode:"decimal",minFractionDigits:0,maxFractionDigits:9})}),_jsx(FormRow,{label:"Min",hint:"Optional lower bound the operator can enter (display units). Blank = no minimum.",children:_jsx(InputNumber,{value:u.min??null,onValueChange:e=>d({...u,min:"number"==typeof e.value?e.value:void 0}),mode:"decimal",minFractionDigits:0,maxFractionDigits:9})}),_jsx(FormRow,{label:"Max",hint:"Optional upper bound the operator can enter (display units). Blank = no maximum.",children:_jsx(InputNumber,{value:u.max??null,onValueChange:e=>d({...u,max:"number"==typeof e.value?e.value:void 0}),mode:"decimal",minFractionDigits:0,maxFractionDigits:9})}),_jsx(FormRow,{label:"Default",hint:"Applied every time the method loads (operator can override). A literal (display units) like 5, or an FQDN token like ${gm.safe_speed} that snapshots that value on load.",children:_jsx(InputText,{value:m,onChange:e=>c(e.target.value),placeholder:"e.g. 5 or ${gm.safe_speed}"})}),n&&_jsxs(_Fragment,{children:[_jsx("div",{style:{borderTop:"1px solid #e5e7eb",margin:"0.75rem 0 0.25rem"}}),_jsx(FormRow,{label:"Aggregate",hint:"Cross-test rollup. When set, this result is derived server-side over the non-excluded tests sharing the same sample_id — the analysis script does not supply it.",children:_jsx(Dropdown,{value:p?.fn??"",options:AGG_FNS,onChange:e=>(e=>{if(""===e)return void d({...u,aggregate:void 0});if("count"===e)return void d({...u,aggregate:{fn:"count"}});const a={fn:e,of:u.aggregate?.of,from:u.aggregate?.from??"cycle_fields"};d({...u,aggregate:a})})(e.value)})}),p&&"count"!==p.fn&&_jsxs(_Fragment,{children:[_jsx(FormRow,{label:"Aggregate from",hint:"Which per-test source the target field is read from.",children:_jsx(Dropdown,{value:f,options:AGG_FROMS,onChange:e=>d({...u,aggregate:{...p,from:e.value}})})}),_jsx(FormRow,{label:"Aggregate of",required:!0,hint:"The field to reduce across tests.",children:_jsx(Dropdown,{value:p.of??null,options:v,onChange:e=>d({...u,aggregate:{...p,of:e.value||void 0}}),editable:!0,placeholder:v.length?"Select a field":`No ${f} defined yet`})})]})]}),_jsx(FormRow,{label:"Description",hint:"Hover tooltip in the form.",children:_jsx(InputTextarea,{rows:2,value:u.description??"",onChange:e=>d({...u,description:e.target.value||void 0})})}),_jsxs("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.5rem",marginTop:"1rem"},children:[_jsx(Button,{label:"Cancel",className:"p-button-text",onClick:t}),_jsx(Button,{label:"Save",onClick:()=>{const e={...u},t=(e=>{const a=e.trim();if(""!==a){if(a.startsWith("${"))return a;if("true"===a)return!0;if("false"===a)return!1;if(/^-?\d*\.?\d+$/.test(a)){const e=Number(a);if(Number.isFinite(e))return e}return a}})(m);void 0===t?delete e.default:e.default=t;const n=(i=e).name.trim()?/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(i.name)?r.some(e=>e===i.name&&e!==a?.name)?`A field named "${i.name}" already exists in this array.`:"number"==typeof i.min&&"number"==typeof i.max&&i.min>i.max?`Min (${i.min}) cannot be greater than Max (${i.max}).`:i.aggregate&&"count"!==i.aggregate.fn&&!i.aggregate.of?.trim()?`Aggregate "${i.aggregate.fn}" needs a target field (the field to ${i.aggregate.fn} across tests).`:null:"Name must be a valid identifier (letters, digits, underscore; cannot start with a digit).":"Field name is required.";var i;n?h(n):l(e)}})]})]})};
@@ -1 +1 @@
1
- {"version":3,"file":"ViewsEditor.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/ViewsEditor.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAwB,UAAU,EAAE,MAAM,UAAU,CAAC;AAEjE,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;CACxC;AAQD,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAiFlD,CAAC"}
1
+ {"version":3,"file":"ViewsEditor.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/ViewsEditor.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAwB,UAAU,EAAE,MAAM,UAAU,CAAC;AAEjE,MAAM,WAAW,gBAAgB;IAC7B,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;CACxC;AAQD,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAoFlD,CAAC"}
@@ -1 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useState}from"react";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Button}from"primereact/button";import{FormSection}from"../../forms/FormSection";import{ChartViewDialog}from"./ChartViewDialog";export const ViewsEditor=({method:e,onChange:t})=>{const i=e.views??{},a=Object.entries(i).map(([e,t])=>({id:e,title:t.title??"",type:t.type??""})),[s,n]=useState(!1),[o,r]=useState(null),l=[...(e.project_fields??[]).map(e=>e.name),...(e.config_fields??[]).map(e=>e.name),...(e.cycle_fields??[]).map(e=>e.name),...(e.results_fields??[]).map(e=>e.name),...Object.keys(e.raw_data?.columns??{})];return _jsxs(_Fragment,{children:[_jsx(FormSection,{title:"Views",description:"Named chart definitions rendered by <TestDataView> (cycle_scatter) and <TestRawDataView> (raw_trace).",actions:_jsx(Button,{label:"Add view",icon:"pi pi-plus",size:"small",onClick:()=>{r(null),n(!0)}}),children:_jsxs(DataTable,{value:a,dataKey:"id",emptyMessage:"No views defined.",children:[_jsx(Column,{field:"id",header:"View ID"}),_jsx(Column,{field:"title",header:"Title"}),_jsx(Column,{field:"type",header:"Type",style:{width:"8rem"}}),_jsx(Column,{header:"",body:a=>_jsxs("div",{style:{display:"flex",gap:"0.25rem"},children:[_jsx(Button,{icon:"pi pi-pencil",className:"p-button-text p-button-sm",onClick:()=>{return e=a.id,r(e),void n(!0);var e},"aria-label":"Edit"}),_jsx(Button,{icon:"pi pi-trash",className:"p-button-text p-button-danger p-button-sm",onClick:()=>(a=>{if(!window.confirm(`Remove view "${a}"?`))return;const s={...i};delete s[a],t({...e,views:s})})(a.id),"aria-label":"Remove"})]}),style:{width:"6rem"}})]})}),_jsx(ChartViewDialog,{visible:s,initial:o?{viewId:o,view:i[o]}:null,knownKeys:l,siblingIds:Object.keys(i),onCancel:()=>n(!1),onSave:(a,s)=>{const r={...i};o&&o!==a&&delete r[o],r[a]=s,t({...e,views:r}),n(!1)}})]})};
1
+ import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useState}from"react";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Button}from"primereact/button";import{FormSection}from"../../forms/FormSection";import{ChartViewDialog}from"./ChartViewDialog";export const ViewsEditor=({method:e,onChange:t})=>{const i=e.views??{},a=Object.entries(i).map(([e,t])=>({id:e,title:t.title??"",type:t.type??""})),[s,n]=useState(!1),[o,r]=useState(null),l=[...(e.project_fields??[]).map(e=>e.name),...(e.config_fields??[]).map(e=>e.name),...(e.cycle_config_fields??[]).map(e=>e.name),...(e.cycle_fields??[]).map(e=>e.name),...(e.results_fields??[]).map(e=>e.name),...Object.keys(e.raw_data?.columns??{})];return _jsxs(_Fragment,{children:[_jsx(FormSection,{title:"Views",description:"Named chart definitions rendered by <TestDataView> (cycle_scatter) and <TestRawDataView> (raw_trace).",actions:_jsx(Button,{label:"Add view",icon:"pi pi-plus",size:"small",onClick:()=>{r(null),n(!0)}}),children:_jsxs(DataTable,{value:a,dataKey:"id",emptyMessage:"No views defined.",children:[_jsx(Column,{field:"id",header:"View ID"}),_jsx(Column,{field:"title",header:"Title"}),_jsx(Column,{field:"type",header:"Type",style:{width:"8rem"}}),_jsx(Column,{header:"",body:a=>_jsxs("div",{style:{display:"flex",gap:"0.25rem"},children:[_jsx(Button,{icon:"pi pi-pencil",className:"p-button-text p-button-sm",onClick:()=>{return e=a.id,r(e),void n(!0);var e},"aria-label":"Edit"}),_jsx(Button,{icon:"pi pi-trash",className:"p-button-text p-button-danger p-button-sm",onClick:()=>(a=>{if(!window.confirm(`Remove view "${a}"?`))return;const s={...i};delete s[a],t({...e,views:s})})(a.id),"aria-label":"Remove"})]}),style:{width:"6rem"}})]})}),_jsx(ChartViewDialog,{visible:s,initial:o?{viewId:o,view:i[o]}:null,knownKeys:l,siblingIds:Object.keys(i),onCancel:()=>n(!1),onSave:(a,s)=>{const r={...i};o&&o!==a&&delete r[o],r[a]=s,t({...e,views:r}),n(!1)}})]})};