@adcops/autocore-react 3.3.87 → 3.3.90

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 (112) hide show
  1. package/dist/assets/AxisC.d.ts +4 -0
  2. package/dist/assets/AxisC.d.ts.map +1 -0
  3. package/dist/assets/AxisC.js +1 -0
  4. package/dist/assets/AxisX.js +1 -1
  5. package/dist/assets/AxisY.js +1 -1
  6. package/dist/assets/AxisZ.js +1 -1
  7. package/dist/assets/JogXNeg.d.ts +4 -0
  8. package/dist/assets/JogXNeg.d.ts.map +1 -0
  9. package/dist/assets/JogXNeg.js +1 -0
  10. package/dist/assets/JogXPos.d.ts +4 -0
  11. package/dist/assets/JogXPos.d.ts.map +1 -0
  12. package/dist/assets/JogXPos.js +1 -0
  13. package/dist/assets/JogYNeg.d.ts +4 -0
  14. package/dist/assets/JogYNeg.d.ts.map +1 -0
  15. package/dist/assets/JogYNeg.js +1 -0
  16. package/dist/assets/JogYPos.d.ts +4 -0
  17. package/dist/assets/JogYPos.d.ts.map +1 -0
  18. package/dist/assets/JogYPos.js +1 -0
  19. package/dist/assets/JogZNeg.d.ts +4 -0
  20. package/dist/assets/JogZNeg.d.ts.map +1 -0
  21. package/dist/assets/JogZNeg.js +1 -0
  22. package/dist/assets/JogZPos.d.ts +4 -0
  23. package/dist/assets/JogZPos.d.ts.map +1 -0
  24. package/dist/assets/JogZPos.js +1 -0
  25. package/dist/assets/Off.d.ts +4 -0
  26. package/dist/assets/Off.d.ts.map +1 -0
  27. package/dist/assets/Off.js +1 -0
  28. package/dist/assets/On.d.ts +4 -0
  29. package/dist/assets/On.d.ts.map +1 -0
  30. package/dist/assets/On.js +1 -0
  31. package/dist/assets/index.d.ts +6 -0
  32. package/dist/assets/index.d.ts.map +1 -1
  33. package/dist/assets/index.js +1 -1
  34. package/dist/assets/svg/off.svg +2 -0
  35. package/dist/assets/svg/on.svg +11 -0
  36. package/dist/components/JogPanel.d.ts +2 -2
  37. package/dist/components/JogPanel.d.ts.map +1 -1
  38. package/dist/components/JogPanel.js +1 -1
  39. package/dist/components/ams/AmsProvider.d.ts +10 -0
  40. package/dist/components/ams/AmsProvider.d.ts.map +1 -1
  41. package/dist/components/ams/AssetDetailView.js +1 -1
  42. package/dist/components/ams/AssetEditDialog.d.ts.map +1 -1
  43. package/dist/components/ams/AssetEditDialog.js +1 -1
  44. package/dist/components/ams/AssetRegistryTable.css +12 -0
  45. package/dist/components/ams/AssetRegistryTable.d.ts +1 -0
  46. package/dist/components/ams/AssetRegistryTable.d.ts.map +1 -1
  47. package/dist/components/ams/AssetRegistryTable.js +1 -1
  48. package/dist/components/forms/FormRow.js +1 -1
  49. package/dist/components/forms/FormSection.js +1 -1
  50. package/dist/components/forms/forms.css +18 -18
  51. package/dist/components/tis/ConfigurationDialog.d.ts +21 -0
  52. package/dist/components/tis/ConfigurationDialog.d.ts.map +1 -0
  53. package/dist/components/tis/ConfigurationDialog.js +1 -0
  54. package/dist/components/tis/ResultHistoryTable.js +1 -1
  55. package/dist/components/tis/TestDataView.d.ts +27 -0
  56. package/dist/components/tis/TestDataView.d.ts.map +1 -1
  57. package/dist/components/tis/TestDataView.js +1 -1
  58. package/dist/components/tis/TestSetupForm.d.ts +37 -0
  59. package/dist/components/tis/TestSetupForm.d.ts.map +1 -1
  60. package/dist/components/tis/TestSetupForm.js +1 -1
  61. package/dist/components/tis/TisProvider.d.ts +25 -0
  62. package/dist/components/tis/TisProvider.d.ts.map +1 -1
  63. package/dist/components/tis/TisProvider.js +1 -1
  64. package/dist/components/tis-editor/TisConfigEditor.css +20 -0
  65. package/dist/components/tis-editor/editor/ConfigurationsEditor.d.ts +19 -0
  66. package/dist/components/tis-editor/editor/ConfigurationsEditor.d.ts.map +1 -0
  67. package/dist/components/tis-editor/editor/ConfigurationsEditor.js +1 -0
  68. package/dist/components/tis-editor/editor/MethodFormEditor.d.ts.map +1 -1
  69. package/dist/components/tis-editor/editor/MethodFormEditor.js +1 -1
  70. package/dist/components/tis-editor/types.d.ts +13 -0
  71. package/dist/components/tis-editor/types.d.ts.map +1 -1
  72. package/dist/components/tis-editor/validation.d.ts.map +1 -1
  73. package/dist/components/tis-editor/validation.js +1 -1
  74. package/dist/themes/adc-dark/blue/theme.css +3 -2
  75. package/dist/themes/adc-dark/blue/theme.css.map +1 -1
  76. package/package.json +2 -1
  77. package/src/assets/AxisC.tsx +38 -0
  78. package/src/assets/AxisX.tsx +32 -32
  79. package/src/assets/AxisY.tsx +34 -34
  80. package/src/assets/AxisZ.tsx +31 -31
  81. package/src/assets/JogXNeg.tsx +30 -0
  82. package/src/assets/JogXPos.tsx +30 -0
  83. package/src/assets/JogYNeg.tsx +30 -0
  84. package/src/assets/JogYPos.tsx +30 -0
  85. package/src/assets/JogZNeg.tsx +30 -0
  86. package/src/assets/JogZPos.tsx +30 -0
  87. package/src/assets/Off.tsx +14 -0
  88. package/src/assets/On.tsx +26 -0
  89. package/src/assets/index.ts +6 -0
  90. package/src/assets/svg/off.svg +2 -0
  91. package/src/assets/svg/on.svg +11 -0
  92. package/src/components/JogPanel.tsx +18 -28
  93. package/src/components/ams/AmsProvider.tsx +10 -0
  94. package/src/components/ams/AssetDetailView.tsx +1 -1
  95. package/src/components/ams/AssetEditDialog.tsx +25 -10
  96. package/src/components/ams/AssetRegistryTable.css +12 -0
  97. package/src/components/ams/AssetRegistryTable.tsx +68 -12
  98. package/src/components/forms/FormRow.tsx +6 -6
  99. package/src/components/forms/FormSection.tsx +6 -6
  100. package/src/components/forms/forms.css +18 -18
  101. package/src/components/tis/ConfigurationDialog.tsx +128 -0
  102. package/src/components/tis/ResultHistoryTable.tsx +2 -2
  103. package/src/components/tis/TestDataView.tsx +83 -1
  104. package/src/components/tis/TestSetupForm.tsx +167 -10
  105. package/src/components/tis/TisProvider.tsx +53 -0
  106. package/src/components/tis-editor/TisConfigEditor.css +20 -0
  107. package/src/components/tis-editor/editor/ConfigurationsEditor.tsx +242 -0
  108. package/src/components/tis-editor/editor/MethodFormEditor.tsx +4 -0
  109. package/src/components/tis-editor/types.ts +14 -0
  110. package/src/components/tis-editor/validation.ts +29 -0
  111. package/src/themes/adc-dark/_extensions.scss +1 -0
  112. package/src/themes/theme-base/components/panel/_fieldset.scss +2 -2
@@ -1,11 +1,11 @@
1
- .ac-form-section {
1
+ .ac-formsection {
2
2
  border: 1px solid var(--surface-d, #e2e8f0);
3
3
  border-radius: 6px;
4
4
  background: var(--surface-card, #fff);
5
5
  margin-bottom: 1rem;
6
6
  }
7
7
 
8
- .ac-form-section__header {
8
+ .ac-formsection__header {
9
9
  display: flex;
10
10
  align-items: flex-start;
11
11
  justify-content: space-between;
@@ -16,74 +16,74 @@
16
16
  border-top-right-radius: 6px;
17
17
  }
18
18
 
19
- .ac-form-section__title {
19
+ .ac-formsection__title {
20
20
  margin: 0;
21
21
  font-size: 0.95rem;
22
22
  font-weight: 600;
23
23
  }
24
24
 
25
- .ac-form-section__desc {
25
+ .ac-formsection__desc {
26
26
  color: var(--text-color-secondary, #64748b);
27
27
  display: block;
28
28
  margin-top: 0.15rem;
29
29
  }
30
30
 
31
- .ac-form-section__actions {
31
+ .ac-formsection__actions {
32
32
  display: flex;
33
33
  gap: 0.5rem;
34
34
  }
35
35
 
36
- .ac-form-section__body {
36
+ .ac-formsection__body {
37
37
  padding: 0.75rem 1rem;
38
38
  display: flex;
39
39
  flex-direction: column;
40
40
  gap: 0.5rem;
41
41
  }
42
42
 
43
- .ac-form-row {
43
+ .ac-formrow {
44
44
  display: grid;
45
45
  grid-template-columns: minmax(8rem, 14rem) 1fr;
46
46
  gap: 0.5rem 1rem;
47
47
  align-items: start;
48
48
  }
49
49
 
50
- .ac-form-row--error .ac-form-row__field input,
51
- .ac-form-row--error .ac-form-row__field .p-inputtext {
50
+ .ac-formrow--error .ac-formrow__field input,
51
+ .ac-formrow--error .ac-formrow__field .p-inputtext {
52
52
  border-color: #dc2626;
53
53
  }
54
54
 
55
- .ac-form-row__label {
55
+ .ac-formrow__label {
56
56
  font-weight: 500;
57
57
  padding-top: 0.4rem;
58
58
  display: flex;
59
59
  flex-direction: column;
60
60
  }
61
61
 
62
- .ac-form-row__required {
62
+ .ac-formrow__required {
63
63
  color: #dc2626;
64
64
  }
65
65
 
66
- .ac-form-row__hint {
66
+ .ac-formrow__hint {
67
67
  color: var(--text-color-secondary, #64748b);
68
68
  font-weight: 400;
69
69
  font-size: 0.75rem;
70
70
  margin-top: 0.15rem;
71
71
  }
72
72
 
73
- .ac-form-row__field {
73
+ .ac-formrow__field {
74
74
  display: flex;
75
75
  flex-direction: column;
76
76
  gap: 0.25rem;
77
77
  }
78
78
 
79
- .ac-form-row__field > input,
80
- .ac-form-row__field > .p-inputtext,
81
- .ac-form-row__field > .p-dropdown,
82
- .ac-form-row__field > .p-inputtextarea {
79
+ .ac-formrow__field > input,
80
+ .ac-formrow__field > .p-inputtext,
81
+ .ac-formrow__field > .p-dropdown,
82
+ .ac-formrow__field > .p-inputtextarea {
83
83
  width: 100%;
84
84
  }
85
85
 
86
- .ac-form-row__error {
86
+ .ac-formrow__error {
87
87
  color: #dc2626;
88
88
  font-size: 0.75rem;
89
89
  }
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import type { TestConfiguration } from './TestSetupForm';
3
+ /** Display name for one configuration: prefer `label`, fall back to `name`. */
4
+ export declare const configLabelOf: (cfg: TestConfiguration | undefined) => string;
5
+ export interface ConfigurationDialogProps {
6
+ visible: boolean;
7
+ onHide: () => void;
8
+ /** Configurations declared on the active method. */
9
+ configurations: TestConfiguration[];
10
+ /** `name` of the configuration currently applied on the form. The
11
+ * dropdown opens pointing at this so the dialog reflects state. */
12
+ currentConfigName: string;
13
+ /**
14
+ * Called with the chosen configuration `name` when the operator
15
+ * clicks OK. Cancel does not fire this. The parent applies the
16
+ * configuration's defaults to the fields.
17
+ */
18
+ onSelected: (configName: string) => void;
19
+ }
20
+ export declare const ConfigurationDialog: React.FC<ConfigurationDialogProps>;
21
+ //# sourceMappingURL=ConfigurationDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigurationDialog.d.ts","sourceRoot":"","sources":["../../../src/components/tis/ConfigurationDialog.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAI5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,+EAA+E;AAC/E,eAAO,MAAM,aAAa,GAAI,KAAK,iBAAiB,GAAG,SAAS,KAAG,MACK,CAAC;AAEzE,MAAM,WAAW,wBAAwB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,oDAAoD;IACpD,cAAc,EAAE,iBAAiB,EAAE,CAAC;IACpC;wEACoE;IACpE,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,UAAU,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5C;AAED,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,wBAAwB,CAsFlE,CAAC"}
@@ -0,0 +1 @@
1
+ import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import React,{useEffect,useMemo,useState}from"react";import{Button}from"primereact/button";import{Dialog}from"primereact/dialog";import{Dropdown}from"primereact/dropdown";export const configLabelOf=e=>e?.label&&e.label.length>0?e.label:e?.name??"";export const ConfigurationDialog=({visible:e,onHide:o,configurations:i,currentConfigName:t,onSelected:n})=>{const[r,l]=useState(t);useEffect(()=>{e&&l(t)},[e,t]);const a=useMemo(()=>i.map(e=>({label:configLabelOf(e),value:e.name})),[i]),s=i.find(e=>e.name===r),c=s?.description&&s.description.length>0?s.description:null,d=_jsxs("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.5rem"},children:[_jsx(Button,{label:"Cancel",icon:"pi pi-times",onClick:o,text:!0}),_jsx(Button,{label:"OK",icon:"pi pi-check",onClick:()=>{r&&r!==t&&n(r),o()},disabled:!r})]});return _jsx(Dialog,{header:"Select Configuration",visible:e,onHide:o,footer:d,modal:!0,style:{width:"min(560px, 90vw)"},children:0===a.length?_jsx("p",{style:{color:"var(--text-secondary-color)"},children:"This test method declares no configurations."}):_jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"1rem"},children:[_jsxs("div",{style:{display:"flex",alignItems:"center",gap:"0.75rem"},children:[_jsx("label",{htmlFor:"acConfigurationDropdown",style:{flexShrink:0},children:"Configuration:"}),_jsx(Dropdown,{inputId:"acConfigurationDropdown",value:r,options:a,onChange:e=>l(e.value),placeholder:"Select a configuration",style:{flex:1}})]}),_jsx("div",{style:{padding:"0.75rem 1rem",background:"var(--surface-100)",borderRadius:"6px",minHeight:"4.5rem",color:c?"var(--text-color)":"var(--text-secondary-color)",fontStyle:c?"normal":"italic",whiteSpace:"pre-wrap"},children:c??"No description provided for this configuration."})]})})};
@@ -1 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import React,{useState,useEffect,useContext}from"react";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Button}from"primereact/button";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";import{useTis}from"./TisProvider";const downloadCsvBlob=(e,t)=>{const o=new Blob([t],{type:"text/csv;charset=utf-8;"}),r=URL.createObjectURL(o),i=document.createElement("a");i.href=r,i.download=e,document.body.appendChild(i),i.click(),i.remove(),URL.revokeObjectURL(r)};export const ResultHistoryTable=e=>{const t=useTis(),o=e.projectId??t.selection.projectId,r=e.methodId,[i,s]=useState([]),[a,n]=useState(!1),[l,d]=useState(null),[c,p]=useState(null),{invoke:m}=useContext(EventEmitterContext),u=async()=>{if(o){n(!0);try{const e={project_id:o};r&&(e.method_id=r);const t=await m("tis.list_tests",MessageType.Request,e);t.success&&t.data&&t.data.tests&&s(t.data.tests)}catch(e){}n(!1)}else s([])};useEffect(()=>{u()},[o,r,t.state.activeRunId]);const f=async(e,t)=>{const i=e?.run_id,s=e?.method_id??r;if(!i||!s||!o)return;const a="report"===t?"tis.export_test_csv":"tis.export_test_data_csv",n="report"===t?"test report":"test data";d({runId:i,kind:t});try{const e=await m(a,MessageType.Request,{project_id:o,method_id:s,run_id:i});if(!e?.success)return void alert(`Failed to build ${n} for ${i}`+(e?.error_message?`: ${e.error_message}`:""));const r="string"==typeof e.data?.csv?e.data.csv:"";if(!r)return void alert(`${n} for ${i} is empty.`);const l="string"==typeof e.data?.filename&&e.data.filename?e.data.filename:`${o}_${s}_${i}_${t}.csv`;downloadCsvBlob(l,r)}catch(e){alert(`Download failed: ${e instanceof Error?e.message:String(e)}`)}finally{d(null)}},_=async e=>{if(!o)return;const t="report"===e?"tis.export_project_csv":"tis.export_project_zip",r="report"===e?"project report":"project archive";p(e);try{const i=await m(t,MessageType.Request,{project_id:o});if(!i?.success)return void alert(`Failed to build ${r}`+(i?.error_message?`: ${i.error_message}`:""));if("report"===e){const e="string"==typeof i.data?.csv?i.data.csv:"";if(!e)return void alert(`Project ${o} has no tests to report.`);const t="string"==typeof i.data?.filename&&i.data.filename?i.data.filename:`${o}_project_report.csv`;downloadCsvBlob(t,e)}else{const e="string"==typeof i.data?.download_url?i.data.download_url:"";if(!e)return void alert(`Server did not return a download URL for ${r}.`);const t=document.createElement("a");t.href=e,t.download="string"==typeof i.data?.filename?i.data.filename:`${o}_project_archive.zip`,document.body.appendChild(t),t.click(),t.remove()}}catch(e){alert(`Download failed: ${e instanceof Error?e.message:String(e)}`)}finally{p(null)}},h=e=>{const t="string"==typeof e?.sample_id?e.sample_id:"";if(t)return t;const o=e?.config;return o&&"object"==typeof o&&"string"==typeof o.sample_id?o.sample_id:""};return _jsxs("div",{style:{width:"100%",maxWidth:"100%",overflow:"hidden",boxSizing:"border-box"},children:[_jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:"1rem",gap:"0.5rem",flexWrap:"wrap"},children:[_jsx("h3",{style:{margin:0},children:o?`Test History: ${o}${r?` / ${r}`:""}`:"Test History (no project selected)"}),_jsxs("div",{style:{display:"flex",gap:"0.4rem",alignItems:"center"},children:[_jsx(Button,{icon:"report"===c?"pi pi-spin pi-spinner":"pi pi-file",label:"Download Report",size:"small",outlined:!0,disabled:!o||null!==c,onClick:()=>_("report"),tooltip:"Download a CSV report of every test in this project",tooltipOptions:{position:"bottom"}}),_jsx(Button,{icon:"archive"===c?"pi pi-spin pi-spinner":"pi pi-box",label:"Download Archive",size:"small",outlined:!0,disabled:!o||null!==c,onClick:()=>_("archive"),tooltip:"Download a ZIP of the entire project directory (all tests, raw data, configs)",tooltipOptions:{position:"bottom"}}),_jsx(Button,{icon:"pi pi-refresh",label:"Refresh",size:"small",onClick:u,disabled:a})]})]}),_jsxs(DataTable,{value:i,loading:a,paginator:!0,rows:10,emptyMessage:"No tests found.",scrollable:!0,scrollHeight:"flex",tableStyle:{minWidth:0},style:{width:"100%"},selectionMode:"single",onSelectionChange:e=>{const o=e.value;o?.run_id&&t.setSelection({projectId:o.project_id??null,methodId:o.method_id??null,runId:o.run_id})},children:[_jsx(Column,{header:"Sample ID",sortable:!0,body:h,sortFunction:e=>{const t=[...e.data];return t.sort((t,o)=>h(t).localeCompare(h(o))*(e.order??1)),t},style:{minWidth:"8rem"}}),_jsx(Column,{field:"start_time",header:"Date/Time",sortable:!0,body:e=>{return(t=e.start_time)?new Date(t).toLocaleString():"";var t},style:{minWidth:"12rem"}}),_jsx(Column,{field:"method_id",header:"Test Method",sortable:!0,style:{minWidth:"10rem"}}),_jsx(Column,{field:"run_id",header:"Run ID",sortable:!0,style:{minWidth:"12rem"}}),_jsx(Column,{header:"Download",style:{width:"14rem"},body:e=>{const t=l?.runId===e.run_id&&"data"===l?.kind,o=l?.runId===e.run_id&&"report"===l?.kind,r=null!==l;return _jsxs("div",{style:{display:"flex",gap:"0.4rem"},children:[_jsx(Button,{icon:t?"pi pi-spin pi-spinner":"pi pi-download",label:"Data",size:"small",outlined:!0,disabled:r,onClick:()=>f(e,"data"),tooltip:"Download raw + filtered trace data as one CSV",tooltipOptions:{position:"left"}}),_jsx(Button,{icon:o?"pi pi-spin pi-spinner":"pi pi-file",label:"Report",size:"small",outlined:!0,disabled:r,onClick:()=>f(e,"report"),tooltip:"Download a CSV report (metadata + cycles + results) for this test",tooltipOptions:{position:"left"}})]})}})]})]})};
1
+ import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import React,{useState,useEffect,useContext}from"react";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Button}from"primereact/button";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";import{useTis}from"./TisProvider";const downloadCsvBlob=(e,t)=>{const o=new Blob([t],{type:"text/csv;charset=utf-8;"}),r=URL.createObjectURL(o),i=document.createElement("a");i.href=r,i.download=e,document.body.appendChild(i),i.click(),i.remove(),URL.revokeObjectURL(r)};export const ResultHistoryTable=e=>{const t=useTis(),o=e.projectId??t.selection.projectId,r=e.methodId,[i,s]=useState([]),[a,n]=useState(!1),[l,d]=useState(null),[c,p]=useState(null),{invoke:m}=useContext(EventEmitterContext),u=async()=>{if(o){n(!0);try{const e={project_id:o};r&&(e.method_id=r);const t=await m("tis.list_tests",MessageType.Request,e);t.success&&t.data&&t.data.tests&&s(t.data.tests)}catch(e){}n(!1)}else s([])};useEffect(()=>{u()},[o,r,t.state.activeRunId]);const f=async(e,t)=>{const i=e?.run_id,s=e?.method_id??r;if(!i||!s||!o)return;const a="report"===t?"tis.export_test_csv":"tis.export_test_data_csv",n="report"===t?"test report":"test data";d({runId:i,kind:t});try{const e=await m(a,MessageType.Request,{project_id:o,method_id:s,run_id:i});if(!e?.success)return void alert(`Failed to build ${n} for ${i}`+(e?.error_message?`: ${e.error_message}`:""));const r="string"==typeof e.data?.csv?e.data.csv:"";if(!r)return void alert(`${n} for ${i} is empty.`);const l="string"==typeof e.data?.filename&&e.data.filename?e.data.filename:`${o}_${s}_${i}_${t}.csv`;downloadCsvBlob(l,r)}catch(e){alert(`Download failed: ${e instanceof Error?e.message:String(e)}`)}finally{d(null)}},_=async e=>{if(!o)return;const t="report"===e?"tis.export_project_csv":"tis.export_project_zip",r="report"===e?"project report":"project archive";p(e);try{const i=await m(t,MessageType.Request,{project_id:o});if(!i?.success)return void alert(`Failed to build ${r}`+(i?.error_message?`: ${i.error_message}`:""));if("report"===e){const e="string"==typeof i.data?.csv?i.data.csv:"";if(!e)return void alert(`Project ${o} has no tests to report.`);const t="string"==typeof i.data?.filename&&i.data.filename?i.data.filename:`${o}_project_report.csv`;downloadCsvBlob(t,e)}else{const e="string"==typeof i.data?.download_url?i.data.download_url:"";if(!e)return void alert(`Server did not return a download URL for ${r}.`);const t=document.createElement("a");t.href=e,t.download="string"==typeof i.data?.filename?i.data.filename:`${o}_project_archive.zip`,document.body.appendChild(t),t.click(),t.remove()}}catch(e){alert(`Download failed: ${e instanceof Error?e.message:String(e)}`)}finally{p(null)}},h=e=>{const t="string"==typeof e?.sample_id?e.sample_id:"";if(t)return t;const o=e?.config;return o&&"object"==typeof o&&"string"==typeof o.sample_id?o.sample_id:""};return _jsxs("div",{style:{width:"100%",maxWidth:"100%",overflow:"hidden",boxSizing:"border-box"},children:[_jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:"1rem",gap:"0.5rem",flexWrap:"wrap"},children:[_jsx("h3",{style:{margin:0},children:o?`Test History: ${o}${r?` / ${r}`:""}`:"Test History (no project selected)"}),_jsxs("div",{style:{display:"flex",gap:"0.4rem",alignItems:"center"},children:[_jsx(Button,{icon:"report"===c?"pi pi-spin pi-spinner":"pi pi-file",label:"Download Report",size:"small",outlined:!0,disabled:!o||null!==c,onClick:()=>_("report"),tooltip:"Download a CSV report of every test in this project",tooltipOptions:{position:"bottom"}}),_jsx(Button,{icon:"archive"===c?"pi pi-spin pi-spinner":"pi pi-box",label:"Download Archive",size:"small",outlined:!0,disabled:!o||null!==c,onClick:()=>_("archive"),tooltip:"Download a ZIP of the entire project directory (all tests, raw data, configs)",tooltipOptions:{position:"bottom"}}),_jsx(Button,{icon:"pi pi-refresh",label:"Refresh",size:"small",onClick:u,disabled:a})]})]}),_jsxs(DataTable,{value:i,loading:a,paginator:!0,rows:10,emptyMessage:"No tests found.",scrollable:!0,scrollHeight:"flex",tableStyle:{minWidth:0},style:{width:"100%"},selectionMode:"single",onSelectionChange:e=>{const o=e.value;o?.run_id&&t.setSelection({projectId:o.project_id??null,methodId:o.method_id??null,runId:o.run_id})},children:[_jsx(Column,{header:"Sample ID",sortable:!0,body:h,sortFunction:e=>{const t=[...e.data];return t.sort((t,o)=>h(t).localeCompare(h(o))*(e.order??1)),t},style:{minWidth:"8rem"}}),_jsx(Column,{field:"start_time",header:"Date/Time",sortable:!0,body:e=>{return(t=e.start_time)?new Date(t).toLocaleString():"";var t},style:{minWidth:"12rem"}}),_jsx(Column,{field:"method_id",header:"Test Method",sortable:!0,style:{minWidth:"10rem"}}),_jsx(Column,{field:"run_id",header:"Run ID",sortable:!0,style:{minWidth:"12rem"}}),_jsx(Column,{header:"Download",style:{width:"14rem"},body:e=>{const t=l?.runId===e.run_id&&"data"===l?.kind,o=l?.runId===e.run_id&&"report"===l?.kind,r=null!==l;return _jsxs("div",{style:{display:"flex",gap:"0.4rem"},children:[_jsx(Button,{icon:t?"pi pi-spin pi-spinner":"pi pi-download",label:"Raw",size:"small",outlined:!0,disabled:r,onClick:()=>f(e,"data"),tooltip:"Download raw + filtered trace data as one CSV",tooltipOptions:{position:"left"}}),_jsx(Button,{icon:o?"pi pi-spin pi-spinner":"pi pi-file",label:"Results",size:"small",outlined:!0,disabled:r,onClick:()=>f(e,"report"),tooltip:"Download a CSV report (metadata + cycles + results) for this test",tooltipOptions:{position:"left"}})]})}})]})]})};
@@ -22,11 +22,38 @@ export interface ChartSeries {
22
22
  label?: string;
23
23
  y_axis?: 'left' | 'right';
24
24
  }
25
+ /**
26
+ * A shaded X-range band drawn over the plot (rendered as a
27
+ * chartjs-plugin-annotation `box` annotation spanning the full Y height).
28
+ * `xMin`/`xMax` are in the same units as the view's x data — for a
29
+ * `raw_trace` that's the x column's value, for a `cycle_scatter` it's the
30
+ * category index. Used to indicate a processed / region-of-interest span.
31
+ */
32
+ export interface ChartRegion {
33
+ /** Start of the band on the X axis. */
34
+ xMin: number;
35
+ /** End of the band on the X axis. */
36
+ xMax: number;
37
+ /** Optional caption drawn at the top-center of the band. */
38
+ label?: string;
39
+ /** Fill color (any CSS color). Default: translucent theme accent. */
40
+ color?: string;
41
+ /** Optional band-edge border color. Default: no border. */
42
+ borderColor?: string;
43
+ }
25
44
  export interface ChartView {
26
45
  title?: string;
27
46
  type: 'cycle_scatter' | 'raw_trace';
28
47
  x: ChartAxis;
29
48
  y: ChartSeries[];
49
+ /**
50
+ * Optional shaded X-range bands drawn over the plot. Declared per view
51
+ * in project.json (static). The dynamic case — bands computed at
52
+ * runtime from processed/results data — would feed the same
53
+ * `buildRegionAnnotations()` helper via a future prop; see its seam in
54
+ * the `chartOptions` memo.
55
+ */
56
+ regions?: ChartRegion[];
30
57
  }
31
58
  export interface RawColumn {
32
59
  source: string;
@@ -1 +1 @@
1
- {"version":3,"file":"TestDataView.d.ts","sourceRoot":"","sources":["../../../src/components/tis/TestDataView.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAwE,MAAM,OAAO,CAAC;AA8B7F,MAAM,WAAW,YAAY;IACzB,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;;;uEAGmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IAAI,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CAAE;AAChF,MAAM,WAAW,WAAW;IAAG,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAAE;AAC5G,MAAM,WAAW,SAAS;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,eAAe,GAAG,WAAW,CAAC;IACpC,CAAC,EAAE,SAAS,CAAC;IACb,CAAC,EAAE,WAAW,EAAE,CAAC;CACpB;AACD,MAAM,WAAW,SAAS;IAAG,MAAM,EAAE,MAAM,CAAC;CAAE;AAC9C,MAAM,WAAW,YAAY;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB;;8CAE0C;IAC1C,OAAO,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IACtC,KAAK,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;CACrC;AACD,MAAM,WAAW,UAAU;IACvB,cAAc,EAAG,YAAY,EAAE,CAAC;IAChC,aAAa,EAAI,YAAY,EAAE,CAAC;IAChC,YAAY,EAAK,YAAY,EAAE,CAAC;IAChC,cAAc,EAAG,YAAY,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAQ,YAAY,GAAG,IAAI,CAAC;IACrC,KAAK,CAAC,EAAW;QAAE,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC/C,wDAAwD;IACxD,KAAK,CAAC,EAAW,MAAM,CAAC;IACxB,qDAAqD;IACrD,WAAW,CAAC,EAAK,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAC9B,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,QAAQ,CAAC,EAAG,MAAM,CAAC;IACnB,gEAAgE;IAChE,KAAK,CAAC,EAAM,MAAM,CAAC;IACnB,kEAAkE;IAClE,MAAM,CAAC,EAAK,UAAU,CAAC;IACvB,8EAA8E;IAC9E,UAAU,CAAC,EAAG,MAAM,CAAC;IACrB,wDAAwD;IACxD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;uEACmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAID,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAgjBpD,CAAC"}
1
+ {"version":3,"file":"TestDataView.d.ts","sourceRoot":"","sources":["../../../src/components/tis/TestDataView.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAwE,MAAM,OAAO,CAAC;AA+B7F,MAAM,WAAW,YAAY;IACzB,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;;;uEAGmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IAAI,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CAAE;AAChF,MAAM,WAAW,WAAW;IAAG,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAAE;AAC5G;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IACxB,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AACD,MAAM,WAAW,SAAS;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,eAAe,GAAG,WAAW,CAAC;IACpC,CAAC,EAAE,SAAS,CAAC;IACb,CAAC,EAAE,WAAW,EAAE,CAAC;IACjB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;CAC3B;AACD,MAAM,WAAW,SAAS;IAAG,MAAM,EAAE,MAAM,CAAC;CAAE;AAC9C,MAAM,WAAW,YAAY;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB;;8CAE0C;IAC1C,OAAO,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IACtC,KAAK,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;CACrC;AACD,MAAM,WAAW,UAAU;IACvB,cAAc,EAAG,YAAY,EAAE,CAAC;IAChC,aAAa,EAAI,YAAY,EAAE,CAAC;IAChC,YAAY,EAAK,YAAY,EAAE,CAAC;IAChC,cAAc,EAAG,YAAY,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAQ,YAAY,GAAG,IAAI,CAAC;IACrC,KAAK,CAAC,EAAW;QAAE,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC/C,wDAAwD;IACxD,KAAK,CAAC,EAAW,MAAM,CAAC;IACxB,qDAAqD;IACrD,WAAW,CAAC,EAAK,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAC9B,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,QAAQ,CAAC,EAAG,MAAM,CAAC;IACnB,gEAAgE;IAChE,KAAK,CAAC,EAAM,MAAM,CAAC;IACnB,kEAAkE;IAClE,MAAM,CAAC,EAAK,UAAU,CAAC;IACvB,8EAA8E;IAC9E,UAAU,CAAC,EAAG,MAAM,CAAC;IACrB,wDAAwD;IACxD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;uEACmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAID,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAsjBpD,CAAC"}
@@ -1 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import React,{useCallback,useContext,useEffect,useMemo,useRef,useState}from"react";import{Button}from"primereact/button";import{Column}from"primereact/column";import{DataTable}from"primereact/datatable";import{Dialog}from"primereact/dialog";import{Dropdown}from"primereact/dropdown";import{TabView,TabPanel}from"primereact/tabview";import{Chart as ChartJS,CategoryScale,LinearScale,PointElement,LineElement,Title,Tooltip,Legend}from"chart.js";import zoomPlugin from"chartjs-plugin-zoom";import{Line}from"react-chartjs-2";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";import{useTis}from"./TisProvider";import{useRawCycleData}from"./useRawCycleData";ChartJS.register(CategoryScale,LinearScale,PointElement,LineElement,Title,Tooltip,Legend,zoomPlugin);export const TestDataView=e=>{const t=useTis(),r=e.projectId??t.selection.projectId,a=e.methodId??t.selection.methodId,l=e.runId??t.selection.runId,s=e.schema??(a?t.schemas[a]:void 0),{throttleMs:n=100,cycleTableHeight:i="400px",chartHeight:o="320px"}=e,{invoke:c,subscribe:d,unsubscribe:u}=useContext(EventEmitterContext),[m,p]=useState(null),[y,h]=useState([]),[f,g]=useState({}),[x,_]=useState(!1),[b,j]=useState(!1),v=useRef(null),[w,C]=useState(null),[S,R]=useState(null),[T,N]=useState(!1),[D,I]=useState(null),[L,E]=useState(null),[O,k]=useState(!1),[A,H]=useState([]),[M,z]=useState(null),F=useMemo(()=>{const e=[];for(const[t,r]of Object.entries(s?.views??{}))e.push({name:t,view:r});return e},[s]),[P,V]=useState(F.length>0?F[0].name:null);useEffect(()=>{if(0===F.length)return;null!==P&&F.some(e=>e.name===P)||V(F[0].name)},[F,P]);const $=F.find(e=>e.name===P)?.view,B="raw_trace"===$?.type,q=useRef([]),W=useRef(null),J=useRef(null),Y=()=>{J.current||(J.current=setTimeout(()=>{if(J.current=null,q.current.length>0){const e=q.current;q.current=[],h(t=>[...e.slice().reverse(),...t])}W.current&&(g(W.current),W.current=null)},n))};useEffect(()=>{if(!r||!a||!l)return p(null),h([]),void g({});let e=!1;return(async()=>{try{const t=await c("tis.read_test",MessageType.Request,{project_id:r,method_id:a,run_id:l});!e&&t?.success&&(p(t.data),g(t.data.results??{}));const s=await c("tis.read_cycles",MessageType.Request,{project_id:r,method_id:a,run_id:l,offset:0,limit:200,order:"desc"});!e&&s?.success&&h(s.data.cycles??[])}catch(e){}})(),()=>{e=!0}},[r,a,l,c]),useEffect(()=>{const e=e=>e?.project_id===r&&e?.method_id===a&&e?.run_id===l,t=d("tis.cycle_added",t=>{e(t)&&t.cycle&&(q.current.push(t.cycle),Y())}),s=d("tis.results_updated",t=>{e(t)&&(W.current=t.results??{},Y())});return()=>{u(t),u(s),J.current&&(clearTimeout(J.current),J.current=null)}},[r,a,l,n]);const G=useRawCycleData({projectId:r,methodId:a,runId:l,blobName:s?.raw_data?.blob_name??"trace",enabled:B}),U=useMemo(()=>{if(!$)return null;if("cycle_scatter"===$.type){const e=$.x.field;if(!e)return null;const t=[...y].reverse();return{labels:t.map(t=>t[e]),datasets:$.y.map((e,r)=>({label:e.label??e.field,data:t.map(t=>t[e.field]),yAxisID:"right"===e.y_axis?"y1":"y",borderColor:palette(r),backgroundColor:palette(r),tension:.1,pointRadius:2}))}}if("raw_trace"===$.type){if(!G.raw)return null;const e=$.x.column;if(!e)return null;const t=G.raw[e]??[];return{datasets:$.y.map((e,r)=>({label:e.label??e.column,data:(G.raw[e.column]??[]).map((e,r)=>({x:t[r],y:e})),yAxisID:"right"===e.y_axis?"y1":"y",borderColor:palette(r),backgroundColor:palette(r),pointRadius:0,borderWidth:1.5,showLine:!0}))}}return null},[$,y,G.raw]),Z=$?.y.some(e=>"right"===e.y_axis)??!1,K=useMemo(()=>{const e="raw_trace"===$?.type;return{responsive:!0,maintainAspectRatio:!1,parsing:!e&&void 0,scales:{x:e?{type:"linear",title:{display:!!$?.x.label,text:$?.x.label}}:{title:{display:!!$?.x.label,text:$?.x.label}},y:{position:"left",title:{display:!0,text:leftAxisLabel($)}},...Z?{y1:{position:"right",grid:{drawOnChartArea:!1},title:{display:!0,text:rightAxisLabel($)}}}:{}},plugins:{legend:{display:!0},zoom:{pan:{enabled:!0,mode:"xy"},zoom:{wheel:{enabled:!0},pinch:{enabled:!0},mode:"xy"}}}}},[$,Z]),Q=s?.raw_data?.blob_name??"trace",X=useRef(""),ee=useCallback(async()=>{if(!r||!a||!l)return[];try{const e=await c("tis.list_raw",MessageType.Request,{project_id:r,method_id:a,run_id:l});if(!e?.success)return[];const t=e.data?.cycles??[];return t.filter(e=>e?.name===Q&&"number"==typeof e?.cycle_index).map(e=>e.cycle_index).sort((e,t)=>e-t)}catch{return[]}},[r,a,l,Q,c]),te=useCallback(async e=>{if(!r||!a||!l)return;const t=`${r}|${a}|${l}|${Q}|${e??"latest"}`;if(X.current===t)return;X.current=t;const s={project_id:r,method_id:a,run_id:l,name:Q};null!=e&&(s.cycle_index=e),N(!0),R(null),C(null);try{const e=await c("tis.read_raw",MessageType.Request,s);e?.success?C(e.data??{}):R(e?.error_message??"No raw data on disk for this run.")}catch(e){R(String(e?.message??e))}finally{N(!1)}k(!0),E(null),I(null);try{const e=await c("tis.read_filtered",MessageType.Request,{project_id:r,method_id:a,run_id:l,name:Q});e?.success?I(e.data??{}):E(e?.error_message??"No filtered data on disk for this run.")}catch(e){E(String(e?.message??e))}finally{k(!1)}},[r,a,l,Q,c]);useEffect(()=>{X.current="",H([]),z(null)},[r,a,l,Q]);return useEffect(()=>{x&&null!=M&&te(M)},[x,M,te]),r&&a&&l&&s?_jsxs("div",{className:"vblock",style:{display:"flex",flexDirection:"column",gap:"1rem"},children:[_jsx(Header,{meta:m,config:m?.config,runId:l,projectId:r,methodId:a,canViewRaw:!!s.raw_data,onViewRaw:async()=>{_(!0);let e=A;0===e.length&&(e=await ee(),H(e));const t=e.length>0?e[e.length-1]:null,r=M??t;M!==r&&z(r),await te(r)},onShowConfig:()=>j(!0)}),_jsxs("div",{className:"p-card",style:{padding:"1rem"},children:[_jsxs("div",{className:"flex",style:{gap:"1rem",alignItems:"center",marginBottom:"0.5rem",flexWrap:"wrap"},children:[_jsx(Dropdown,{value:P,options:F.map(e=>({label:e.view.title??e.name,value:e.name})),onChange:e=>V(e.value),placeholder:0===F.length?"No view defined":"Select a view",disabled:0===F.length}),_jsx("h3",{style:{margin:0},children:$?.title??""}),B&&G.cycles.length>1&&_jsxs(_Fragment,{children:[_jsx("label",{htmlFor:"chart-cycle-picker",style:{color:"var(--text-secondary-color)"},children:"Cycle:"}),_jsx(Dropdown,{inputId:"chart-cycle-picker",value:G.selectedCycle,options:G.cycles.map(e=>({label:`Cycle ${e}`,value:e})),onChange:e=>G.setSelectedCycle(Number(e.value)),style:{minWidth:"8rem"}}),_jsxs("span",{style:{color:"var(--text-secondary-color)"},children:["of ",G.cycles.length]})]}),_jsx("div",{style:{flex:1}}),_jsx(Button,{icon:"pi pi-th-large",outlined:!0,rounded:!0,size:"small",onClick:()=>v.current?.resetZoom?.(),disabled:!U,tooltip:"Reset chart zoom",tooltipOptions:{position:"left"},"aria-label":"Reset chart zoom"})]}),_jsxs("div",{style:{height:o,position:"relative"},children:[B&&G.loading&&_jsx(ChartOverlay,{children:"Loading raw data…"}),B&&G.error&&_jsx(ChartOverlay,{children:G.error}),U&&_jsx(Line,{ref:v,data:U,options:K})]})]}),_jsxs("div",{className:"p-card",style:{padding:"1rem"},children:[_jsxs("h3",{style:{marginTop:0},children:["Cycle Data (",y.length,")"]}),(()=>{const e=y.length>CYCLE_VIRTUAL_THRESHOLD;return _jsx(DataTable,{value:y,scrollable:e,scrollHeight:e?i:void 0,virtualScrollerOptions:e?{itemSize:38}:void 0,emptyMessage:"No cycles yet.",children:s.cycle_fields.map(e=>_jsx(Column,{field:e.name,header:e.units?`${e.name} (${e.units})`:e.name,body:t=>formatCell(t[e.name],e.type,e.scale)},e.name))})})()]}),_jsxs("div",{className:"p-card",style:{padding:"1rem"},children:[_jsx("h3",{style:{marginTop:0},children:"Results"}),_jsx(ResultsGrid,{schema:s.results_fields,values:f})]}),s.raw_data&&_jsxs(Dialog,{visible:x,onHide:()=>_(!1),header:`Run Data — ${l}`,style:{width:"90vw",height:"80vh"},maximizable:!0,children:[_jsx(CyclePickerBar,{cycles:A,selected:M,onChange:z}),_jsx(RawEnvelopeHeader,{envelope:w}),_jsxs(TabView,{style:{height:"100%"},children:[_jsx(TabPanel,{header:"Raw Data",children:_jsx(DataBlobTable,{blob:unwrapEnvelope(w),loading:T,error:S,rawData:s.raw_data})}),_jsx(TabPanel,{header:"Filtered Data",children:_jsx(DataBlobTable,{blob:unwrapEnvelope(D),loading:O,error:L,rawData:s.raw_data,emptyMessage:"Filtered data is written by post-processing — none on disk for this run yet."})})]})]}),_jsx(Dialog,{visible:b,onHide:()=>j(!1),header:"Test Configuration",style:{width:"min(640px, 90vw)"},modal:!0,children:_jsx(ConfigList,{config:m?.config})})]}):_jsx("div",{className:"p-card",style:{padding:"1rem",color:"var(--text-secondary-color)"},children:"No test selected. Pick a row from the History tab or start a run."})};const Header=({meta:e,config:t,runId:r,projectId:a,methodId:l,canViewRaw:s,onViewRaw:n,onShowConfig:i})=>{const o="string"==typeof e?.sample_id&&e.sample_id||"string"==typeof e?.config?.sample_id&&e.config.sample_id||"",c=t&&"object"==typeof t&&Object.entries(t).some(([e])=>"sample_id"!==e);return _jsx("div",{className:"p-card",style:{padding:"1rem"},children:_jsxs("div",{className:"flex",style:{justifyContent:"space-between",alignItems:"flex-start",gap:"1rem"},children:[_jsxs("div",{children:[_jsxs("h2",{style:{margin:0,display:"flex",alignItems:"center",gap:"0.5rem"},children:[o||r,c&&_jsx(Button,{icon:"pi pi-info-circle",type:"button",rounded:!0,text:!0,onClick:i,tooltip:"Show test configuration",tooltipOptions:{position:"top"},style:{width:"2rem",height:"2rem",padding:0},"aria-label":"Show test configuration"})]}),_jsxs("div",{style:{color:"var(--text-secondary-color)",fontSize:"0.85em"},children:["project: ",a," · method: ",l," · run: ",r,e?.start_time&&_jsxs(_Fragment,{children:[" · started: ",new Date(e.start_time).toLocaleString()]})]})]}),s&&_jsx(Button,{icon:"pi pi-table",label:"View Raw Data",onClick:n,outlined:!0})]})})},ConfigList=({config:e})=>{const t=e&&"object"==typeof e?Object.entries(e).filter(([e])=>"sample_id"!==e):[];return 0===t.length?_jsx("div",{style:{color:"var(--text-secondary-color)"},children:"No configuration recorded for this run."}):_jsx("div",{style:{display:"grid",gridTemplateColumns:"auto 1fr",gap:"0.5rem 1rem",fontSize:"0.95em"},children:t.map(([e,t])=>_jsxs(React.Fragment,{children:[_jsx("div",{style:{color:"var(--text-secondary-color)"},children:e}),_jsx("div",{children:formatCell(t,"string")})]},e))})},unwrapEnvelope=e=>e&&"object"==typeof e&&"data"in e&&e.data&&"object"==typeof e.data&&Object.values(e.data).some(e=>Array.isArray(e))?e.data:e,CyclePickerBar=({cycles:e,selected:t,onChange:r})=>e.length<=1?null:_jsxs("div",{style:{display:"flex",alignItems:"center",gap:"0.5rem",padding:"0.25rem 0.5rem 0.5rem"},children:[_jsx("label",{htmlFor:"raw-cycle-picker",style:{color:"var(--text-secondary-color)"},children:"Cycle:"}),_jsx(Dropdown,{inputId:"raw-cycle-picker",value:t,options:e.map(e=>({label:`Cycle ${e}`,value:e})),onChange:e=>r(Number(e.value)),style:{minWidth:"8rem"}}),_jsxs("span",{style:{color:"var(--text-secondary-color)"},children:["(",e.length," cycles recorded)"]})]}),RawEnvelopeHeader=({envelope:e})=>{if(!e||"object"!=typeof e)return null;const t=e.cycle_index,r=e.cycle_fields,a=e.context;if(null==t&&!r&&!a)return null;const l=e=>{if(!e||"object"!=typeof e)return null;const t=Object.entries(e).filter(([,e])=>null!==e&&"object"!=typeof e);return 0===t.length?null:t.map(([e,t])=>_jsxs("span",{style:{marginRight:"1rem"},children:[_jsxs("span",{style:{color:"var(--text-secondary-color)"},children:[e,": "]}),_jsx("span",{children:String(t)})]},e))};return _jsxs("div",{style:{padding:"0.5rem",borderBottom:"1px solid var(--surface-border)",fontSize:"0.9rem"},children:[null!=t&&_jsx("div",{children:_jsxs("strong",{children:["Cycle ",t]})}),r&&_jsx("div",{style:{marginTop:"0.25rem"},children:l(r)}),a&&_jsx("div",{style:{marginTop:"0.25rem"},children:l(a)})]})},DataBlobTable=({blob:e,loading:t,error:r,rawData:a,emptyMessage:l})=>{if(t)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:"Loading…"});if(r)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:l??r});if(!e||"object"!=typeof e)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:"No data."});const s=Object.keys(a.columns??{}),n=Object.keys(e).filter(t=>Array.isArray(e[t])),i=[];for(const t of s)Array.isArray(e[t])&&i.push(t);for(const e of n)i.includes(e)||i.push(e);if(0===i.length)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:l??"No columnar data in this blob."});const o=i.reduce((t,r)=>Math.min(t,e[r].length),Number.POSITIVE_INFINITY),c=Number.isFinite(o)?o:0,d=Array.from({length:c},(t,r)=>{const a={__i:r};for(const t of i)a[t]=e[t][r];return a}),u=e=>{const t=a.units?.[e];return t?`${e} [${t}]`:e};return _jsxs(DataTable,{value:d,scrollable:!0,scrollHeight:"60vh",virtualScrollerOptions:{itemSize:32},emptyMessage:l??"No data.",size:"small",stripedRows:!0,children:[_jsx(Column,{field:"__i",header:"#",style:{width:"5rem",textAlign:"right"},bodyStyle:{fontVariantNumeric:"tabular-nums",textAlign:"right"}}),i.map(e=>_jsx(Column,{field:e,header:u(e),style:{minWidth:"8rem"},bodyStyle:{fontVariantNumeric:"tabular-nums",textAlign:"right"},body:t=>formatNumeric(t[e])},e))]})},formatNumeric=e=>null==e?"":"number"==typeof e&&Number.isFinite(e)?Number.parseFloat(e.toPrecision(6)).toString():String(e),ResultsGrid=({schema:e,values:t})=>t&&0!==Object.keys(t).length?_jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(220px, 1fr))",gap:"0.5rem 1rem"},children:e.map(e=>_jsxs("div",{children:[_jsxs("div",{style:{fontSize:"0.8em",color:"var(--text-secondary-color)"},children:[e.name,e.units?` (${e.units})`:""]}),_jsx("div",{children:formatCell(t[e.name],e.type,e.scale)})]},e.name))}):_jsx("div",{style:{color:"var(--text-secondary-color)"},children:"No results yet."}),CYCLE_VIRTUAL_THRESHOLD=30,CHART_COLORS=["#4ea8de","#f59e0b","#22c55e","#a855f7","#ef4444","#14b8a6","#eab308","#ec4899"],palette=e=>CHART_COLORS[e%CHART_COLORS.length],ChartOverlay=({children:e})=>_jsx("div",{style:{position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",color:"var(--text-secondary-color)",pointerEvents:"none"},children:e}),seriesLabel=e=>e.label??e.field??e.column??"",leftAxisLabel=e=>e?.y.filter(e=>"right"!==e.y_axis).map(seriesLabel).join(" / ")??"",rightAxisLabel=e=>e?.y.filter(e=>"right"===e.y_axis).map(seriesLabel).join(" / ")??"",formatCell=(e,t,r)=>null==e?"":(r&&1!==r&&"number"==typeof e&&Number.isFinite(e)&&(e*=r),"f32"===t||"f64"===t?"number"==typeof e?e.toFixed(4):String(e):"object"==typeof e?JSON.stringify(e):String(e));
1
+ import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import React,{useCallback,useContext,useEffect,useMemo,useRef,useState}from"react";import{Button}from"primereact/button";import{Column}from"primereact/column";import{DataTable}from"primereact/datatable";import{Dialog}from"primereact/dialog";import{Dropdown}from"primereact/dropdown";import{TabView,TabPanel}from"primereact/tabview";import{Chart as ChartJS,CategoryScale,LinearScale,PointElement,LineElement,Title,Tooltip,Legend}from"chart.js";import zoomPlugin from"chartjs-plugin-zoom";import annotationPlugin from"chartjs-plugin-annotation";import{Line}from"react-chartjs-2";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";import{useTis}from"./TisProvider";import{useRawCycleData}from"./useRawCycleData";ChartJS.register(CategoryScale,LinearScale,PointElement,LineElement,Title,Tooltip,Legend,zoomPlugin,annotationPlugin);export const TestDataView=e=>{const t=useTis(),r=e.projectId??t.selection.projectId,a=e.methodId??t.selection.methodId,l=e.runId??t.selection.runId,n=e.schema??(a?t.schemas[a]:void 0),{throttleMs:s=100,cycleTableHeight:i="400px",chartHeight:o="320px"}=e,{invoke:c,subscribe:d,unsubscribe:u}=useContext(EventEmitterContext),[m,p]=useState(null),[y,h]=useState([]),[g,f]=useState({}),[x,_]=useState(!1),[b,j]=useState(!1),v=useRef(null),[w,C]=useState(null),[R,S]=useState(null),[T,L]=useState(!1),[I,N]=useState(null),[D,E]=useState(null),[O,A]=useState(!1),[k,M]=useState([]),[F,H]=useState(null),z=useMemo(()=>{const e=[];for(const[t,r]of Object.entries(n?.views??{}))e.push({name:t,view:r});return e},[n]),[P,$]=useState(z.length>0?z[0].name:null);useEffect(()=>{if(0===z.length)return;null!==P&&z.some(e=>e.name===P)||$(z[0].name)},[z,P]);const B=z.find(e=>e.name===P)?.view,V="raw_trace"===B?.type,G=useRef([]),W=useRef(null),q=useRef(null),U=()=>{q.current||(q.current=setTimeout(()=>{if(q.current=null,G.current.length>0){const e=G.current;G.current=[],h(t=>[...e.slice().reverse(),...t])}W.current&&(f(W.current),W.current=null)},s))};useEffect(()=>{if(!r||!a||!l)return p(null),h([]),void f({});let e=!1;return(async()=>{try{const t=await c("tis.read_test",MessageType.Request,{project_id:r,method_id:a,run_id:l});!e&&t?.success&&(p(t.data),f(t.data.results??{}));const n=await c("tis.read_cycles",MessageType.Request,{project_id:r,method_id:a,run_id:l,offset:0,limit:200,order:"desc"});!e&&n?.success&&h(n.data.cycles??[])}catch(e){}})(),()=>{e=!0}},[r,a,l,c]),useEffect(()=>{const e=e=>e?.project_id===r&&e?.method_id===a&&e?.run_id===l,t=d("tis.cycle_added",t=>{e(t)&&t.cycle&&(G.current.push(t.cycle),U())}),n=d("tis.results_updated",t=>{e(t)&&(W.current=t.results??{},U())});return()=>{u(t),u(n),q.current&&(clearTimeout(q.current),q.current=null)}},[r,a,l,s]);const J=useRawCycleData({projectId:r,methodId:a,runId:l,blobName:n?.raw_data?.blob_name??"trace",enabled:V}),Y=useMemo(()=>{if(!B)return null;if("cycle_scatter"===B.type){const e=B.x.field;if(!e)return null;const t=[...y].reverse();return{labels:t.map(t=>t[e]),datasets:B.y.map((e,r)=>({label:e.label??e.field,data:t.map(t=>t[e.field]),yAxisID:"right"===e.y_axis?"y1":"y",borderColor:palette(r),backgroundColor:palette(r),tension:.1,pointRadius:2}))}}if("raw_trace"===B.type){if(!J.raw)return null;const e=B.x.column;if(!e)return null;const t=J.raw[e]??[];return{datasets:B.y.map((e,r)=>({label:e.label??e.column,data:(J.raw[e.column]??[]).map((e,r)=>({x:t[r],y:e})),yAxisID:"right"===e.y_axis?"y1":"y",borderColor:palette(r),backgroundColor:palette(r),pointRadius:0,borderWidth:1.5,showLine:!0}))}}return null},[B,y,J.raw]),Z=B?.y.some(e=>"right"===e.y_axis)??!1,K=useMemo(()=>{const e="raw_trace"===B?.type;return{responsive:!0,maintainAspectRatio:!1,parsing:!e&&void 0,scales:{x:e?{type:"linear",title:{display:!!B?.x.label,text:B?.x.label}}:{title:{display:!!B?.x.label,text:B?.x.label}},y:{position:"left",title:{display:!0,text:leftAxisLabel(B)}},...Z?{y1:{position:"right",grid:{drawOnChartArea:!1},title:{display:!0,text:rightAxisLabel(B)}}}:{}},plugins:{legend:{display:!0},zoom:{pan:{enabled:!0,mode:"xy"},zoom:{wheel:{enabled:!0},pinch:{enabled:!0},mode:"xy"}},annotation:{annotations:buildRegionAnnotations(B?.regions)}}}},[B,Z]),Q=n?.raw_data?.blob_name??"trace",X=useRef(""),ee=useCallback(async()=>{if(!r||!a||!l)return[];try{const e=await c("tis.list_raw",MessageType.Request,{project_id:r,method_id:a,run_id:l});if(!e?.success)return[];const t=e.data?.cycles??[];return t.filter(e=>e?.name===Q&&"number"==typeof e?.cycle_index).map(e=>e.cycle_index).sort((e,t)=>e-t)}catch{return[]}},[r,a,l,Q,c]),te=useCallback(async e=>{if(!r||!a||!l)return;const t=`${r}|${a}|${l}|${Q}|${e??"latest"}`;if(X.current===t)return;X.current=t;const n={project_id:r,method_id:a,run_id:l,name:Q};null!=e&&(n.cycle_index=e),L(!0),S(null),C(null);try{const e=await c("tis.read_raw",MessageType.Request,n);e?.success?C(e.data??{}):S(e?.error_message??"No raw data on disk for this run.")}catch(e){S(String(e?.message??e))}finally{L(!1)}A(!0),E(null),N(null);try{const e=await c("tis.read_filtered",MessageType.Request,{project_id:r,method_id:a,run_id:l,name:Q});e?.success?N(e.data??{}):E(e?.error_message??"No filtered data on disk for this run.")}catch(e){E(String(e?.message??e))}finally{A(!1)}},[r,a,l,Q,c]);useEffect(()=>{X.current="",M([]),H(null)},[r,a,l,Q]);return useEffect(()=>{x&&null!=F&&te(F)},[x,F,te]),r&&a&&l&&n?_jsxs("div",{className:"vblock",style:{display:"flex",flexDirection:"column",gap:"1rem"},children:[_jsx(Header,{meta:m,config:m?.config,runId:l,projectId:r,methodId:a,canViewRaw:!!n.raw_data,onViewRaw:async()=>{_(!0);let e=k;0===e.length&&(e=await ee(),M(e));const t=e.length>0?e[e.length-1]:null,r=F??t;F!==r&&H(r),await te(r)},onShowConfig:()=>j(!0)}),_jsxs("div",{className:"p-card",style:{padding:"1rem"},children:[_jsxs("div",{className:"flex",style:{gap:"1rem",alignItems:"center",marginBottom:"0.5rem",flexWrap:"wrap"},children:[_jsx(Dropdown,{value:P,options:z.map(e=>({label:e.view.title??e.name,value:e.name})),onChange:e=>$(e.value),placeholder:0===z.length?"No view defined":"Select a view",disabled:0===z.length}),_jsx("h3",{style:{margin:0},children:B?.title??""}),V&&J.cycles.length>1&&_jsxs(_Fragment,{children:[_jsx("label",{htmlFor:"chart-cycle-picker",style:{color:"var(--text-secondary-color)"},children:"Cycle:"}),_jsx(Dropdown,{inputId:"chart-cycle-picker",value:J.selectedCycle,options:J.cycles.map(e=>({label:`Cycle ${e}`,value:e})),onChange:e=>J.setSelectedCycle(Number(e.value)),style:{minWidth:"8rem"}}),_jsxs("span",{style:{color:"var(--text-secondary-color)"},children:["of ",J.cycles.length]})]}),_jsx("div",{style:{flex:1}}),_jsx(Button,{icon:"pi pi-th-large",outlined:!0,rounded:!0,size:"small",onClick:()=>v.current?.resetZoom?.(),disabled:!Y,tooltip:"Reset chart zoom",tooltipOptions:{position:"left"},"aria-label":"Reset chart zoom"})]}),_jsxs("div",{style:{height:o,position:"relative"},children:[V&&J.loading&&_jsx(ChartOverlay,{children:"Loading raw data…"}),V&&J.error&&_jsx(ChartOverlay,{children:J.error}),Y&&_jsx(Line,{ref:v,data:Y,options:K})]})]}),_jsxs("div",{className:"p-card",style:{padding:"1rem"},children:[_jsxs("h3",{style:{marginTop:0},children:["Cycle Data (",y.length,")"]}),(()=>{const e=y.length>CYCLE_VIRTUAL_THRESHOLD;return _jsx(DataTable,{value:y,scrollable:e,scrollHeight:e?i:void 0,virtualScrollerOptions:e?{itemSize:38}:void 0,emptyMessage:"No cycles yet.",children:n.cycle_fields.map(e=>_jsx(Column,{field:e.name,header:e.units?`${e.name} (${e.units})`:e.name,body:t=>formatCell(t[e.name],e.type,e.scale)},e.name))})})()]}),_jsxs("div",{className:"p-card",style:{padding:"1rem"},children:[_jsx("h3",{style:{marginTop:0},children:"Results"}),_jsx(ResultsGrid,{schema:n.results_fields,values:g})]}),n.raw_data&&_jsxs(Dialog,{visible:x,onHide:()=>_(!1),header:`Run Data — ${l}`,style:{width:"90vw",height:"80vh"},maximizable:!0,children:[_jsx(CyclePickerBar,{cycles:k,selected:F,onChange:H}),_jsx(RawEnvelopeHeader,{envelope:w}),_jsxs(TabView,{style:{height:"100%"},children:[_jsx(TabPanel,{header:"Raw Data",children:_jsx(DataBlobTable,{blob:unwrapEnvelope(w),loading:T,error:R,rawData:n.raw_data})}),_jsx(TabPanel,{header:"Filtered Data",children:_jsx(DataBlobTable,{blob:unwrapEnvelope(I),loading:O,error:D,rawData:n.raw_data,emptyMessage:"Filtered data is written by post-processing — none on disk for this run yet."})})]})]}),_jsx(Dialog,{visible:b,onHide:()=>j(!1),header:"Test Configuration",style:{width:"min(640px, 90vw)"},modal:!0,children:_jsx(ConfigList,{config:m?.config})})]}):_jsx("div",{className:"p-card",style:{padding:"1rem",color:"var(--text-secondary-color)"},children:"No test selected. Pick a row from the History tab or start a run."})};const Header=({meta:e,config:t,runId:r,projectId:a,methodId:l,canViewRaw:n,onViewRaw:s,onShowConfig:i})=>{const o="string"==typeof e?.sample_id&&e.sample_id||"string"==typeof e?.config?.sample_id&&e.config.sample_id||"",c=t&&"object"==typeof t&&Object.entries(t).some(([e])=>"sample_id"!==e);return _jsx("div",{className:"p-card",style:{padding:"1rem"},children:_jsxs("div",{className:"flex",style:{justifyContent:"space-between",alignItems:"flex-start",gap:"1rem"},children:[_jsxs("div",{children:[_jsxs("h2",{style:{margin:0,display:"flex",alignItems:"center",gap:"0.5rem"},children:[o||r,c&&_jsx(Button,{icon:"pi pi-info-circle",type:"button",rounded:!0,text:!0,onClick:i,tooltip:"Show test configuration",tooltipOptions:{position:"top"},style:{width:"2rem",height:"2rem",padding:0},"aria-label":"Show test configuration"})]}),_jsxs("div",{style:{color:"var(--text-secondary-color)",fontSize:"0.85em"},children:["project: ",a," · method: ",l," · run: ",r,e?.start_time&&_jsxs(_Fragment,{children:[" · started: ",new Date(e.start_time).toLocaleString()]})]})]}),n&&_jsx(Button,{icon:"pi pi-table",label:"View Raw Data",onClick:s,outlined:!0})]})})},ConfigList=({config:e})=>{const t=e&&"object"==typeof e?Object.entries(e).filter(([e])=>"sample_id"!==e):[];return 0===t.length?_jsx("div",{style:{color:"var(--text-secondary-color)"},children:"No configuration recorded for this run."}):_jsx("div",{style:{display:"grid",gridTemplateColumns:"auto 1fr",gap:"0.5rem 1rem",fontSize:"0.95em"},children:t.map(([e,t])=>_jsxs(React.Fragment,{children:[_jsx("div",{style:{color:"var(--text-secondary-color)"},children:e}),_jsx("div",{children:formatCell(t,"string")})]},e))})},unwrapEnvelope=e=>e&&"object"==typeof e&&"data"in e&&e.data&&"object"==typeof e.data&&Object.values(e.data).some(e=>Array.isArray(e))?e.data:e,CyclePickerBar=({cycles:e,selected:t,onChange:r})=>e.length<=1?null:_jsxs("div",{style:{display:"flex",alignItems:"center",gap:"0.5rem",padding:"0.25rem 0.5rem 0.5rem"},children:[_jsx("label",{htmlFor:"raw-cycle-picker",style:{color:"var(--text-secondary-color)"},children:"Cycle:"}),_jsx(Dropdown,{inputId:"raw-cycle-picker",value:t,options:e.map(e=>({label:`Cycle ${e}`,value:e})),onChange:e=>r(Number(e.value)),style:{minWidth:"8rem"}}),_jsxs("span",{style:{color:"var(--text-secondary-color)"},children:["(",e.length," cycles recorded)"]})]}),RawEnvelopeHeader=({envelope:e})=>{if(!e||"object"!=typeof e)return null;const t=e.cycle_index,r=e.cycle_fields,a=e.context;if(null==t&&!r&&!a)return null;const l=e=>{if(!e||"object"!=typeof e)return null;const t=Object.entries(e).filter(([,e])=>null!==e&&"object"!=typeof e);return 0===t.length?null:t.map(([e,t])=>_jsxs("span",{style:{marginRight:"1rem"},children:[_jsxs("span",{style:{color:"var(--text-secondary-color)"},children:[e,": "]}),_jsx("span",{children:String(t)})]},e))};return _jsxs("div",{style:{padding:"0.5rem",borderBottom:"1px solid var(--surface-border)",fontSize:"0.9rem"},children:[null!=t&&_jsx("div",{children:_jsxs("strong",{children:["Cycle ",t]})}),r&&_jsx("div",{style:{marginTop:"0.25rem"},children:l(r)}),a&&_jsx("div",{style:{marginTop:"0.25rem"},children:l(a)})]})},DataBlobTable=({blob:e,loading:t,error:r,rawData:a,emptyMessage:l})=>{if(t)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:"Loading…"});if(r)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:l??r});if(!e||"object"!=typeof e)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:"No data."});const n=Object.keys(a.columns??{}),s=Object.keys(e).filter(t=>Array.isArray(e[t])),i=[];for(const t of n)Array.isArray(e[t])&&i.push(t);for(const e of s)i.includes(e)||i.push(e);if(0===i.length)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:l??"No columnar data in this blob."});const o=i.reduce((t,r)=>Math.min(t,e[r].length),Number.POSITIVE_INFINITY),c=Number.isFinite(o)?o:0,d=Array.from({length:c},(t,r)=>{const a={__i:r};for(const t of i)a[t]=e[t][r];return a}),u=e=>{const t=a.units?.[e];return t?`${e} [${t}]`:e};return _jsxs(DataTable,{value:d,scrollable:!0,scrollHeight:"60vh",virtualScrollerOptions:{itemSize:32},emptyMessage:l??"No data.",size:"small",stripedRows:!0,children:[_jsx(Column,{field:"__i",header:"#",style:{width:"5rem",textAlign:"right"},bodyStyle:{fontVariantNumeric:"tabular-nums",textAlign:"right"}}),i.map(e=>_jsx(Column,{field:e,header:u(e),style:{minWidth:"8rem"},bodyStyle:{fontVariantNumeric:"tabular-nums",textAlign:"right"},body:t=>formatNumeric(t[e])},e))]})},formatNumeric=e=>null==e?"":"number"==typeof e&&Number.isFinite(e)?Number.parseFloat(e.toPrecision(6)).toString():String(e),ResultsGrid=({schema:e,values:t})=>t&&0!==Object.keys(t).length?_jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(220px, 1fr))",gap:"0.5rem 1rem"},children:e.map(e=>_jsxs("div",{children:[_jsxs("div",{style:{fontSize:"0.8em",color:"var(--text-secondary-color)"},children:[e.name,e.units?` (${e.units})`:""]}),_jsx("div",{children:formatCell(t[e.name],e.type,e.scale)})]},e.name))}):_jsx("div",{style:{color:"var(--text-secondary-color)"},children:"No results yet."}),CYCLE_VIRTUAL_THRESHOLD=30,CHART_COLORS=["#4ea8de","#f59e0b","#22c55e","#a855f7","#ef4444","#14b8a6","#eab308","#ec4899"],palette=e=>CHART_COLORS[e%CHART_COLORS.length],DEFAULT_REGION_FILL="rgba(78, 168, 222, 0.15)",REGION_LABEL_COLOR="rgba(226, 232, 240, 0.85)";function buildRegionAnnotations(e){if(!e||0===e.length)return{};const t={};return e.forEach((e,r)=>{const a=!!e.borderColor;t[`region-${r}`]={type:"box",xScaleID:"x",xMin:e.xMin,xMax:e.xMax,backgroundColor:e.color??DEFAULT_REGION_FILL,borderColor:a?e.borderColor:"transparent",borderWidth:a?1:0,drawTime:"beforeDatasetsDraw",...e.label?{label:{display:!0,content:e.label,position:{x:"center",y:"start"},color:REGION_LABEL_COLOR,font:{size:11}}}:{}}}),t}const ChartOverlay=({children:e})=>_jsx("div",{style:{position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",color:"var(--text-secondary-color)",pointerEvents:"none"},children:e}),seriesLabel=e=>e.label??e.field??e.column??"",leftAxisLabel=e=>e?.y.filter(e=>"right"!==e.y_axis).map(seriesLabel).join(" / ")??"",rightAxisLabel=e=>e?.y.filter(e=>"right"===e.y_axis).map(seriesLabel).join(" / ")??"",formatCell=(e,t,r)=>null==e?"":(r&&1!==r&&"number"==typeof e&&Number.isFinite(e)&&(e*=r),"f32"===t||"f64"===t?"number"==typeof e?e.toFixed(4):String(e):"object"==typeof e?JSON.stringify(e):String(e));
@@ -45,6 +45,17 @@ export interface TestFieldDef {
45
45
  * Cycle and results values are scaled by the corresponding paths
46
46
  * in TestDataView; the server scales CSV exports too. */
47
47
  scale?: number;
48
+ /** Optional fixed set of choices. When present, the field renders
49
+ * as a dropdown and the operator must pick one of the declared
50
+ * values rather than typing freely. Each entry is either a bare
51
+ * scalar (label === value) or an explicit `{ label, value }` pair
52
+ * when the displayed text should differ from the stored value.
53
+ * Works with any `type`; like `default`, values are authored in
54
+ * display units when `scale` is set. */
55
+ options?: Array<string | number | boolean | {
56
+ label?: string;
57
+ value: string | number | boolean;
58
+ }>;
48
59
  }
49
60
  export interface TestMethod {
50
61
  project_fields: TestFieldDef[];
@@ -75,6 +86,32 @@ export interface TestMethod {
75
86
  * as `analysis` — the form ignores them; the type just has to
76
87
  * accept them so generated schemas typecheck. */
77
88
  views?: Record<string, any>;
89
+ /**
90
+ * Optional named configurations for this method. When one or more
91
+ * are declared, the form renders a Configuration selector beneath
92
+ * the Test Method picker. Selecting (or accepting) a configuration
93
+ * writes its `defaults` into the matching config_fields. Each
94
+ * configuration only needs to declare the fields whose value is
95
+ * specific to it — unlisted fields keep the method's base default.
96
+ */
97
+ configurations?: TestConfiguration[];
98
+ }
99
+ /**
100
+ * One named, ready-to-use set of config_field overrides for a method —
101
+ * e.g. translational_traction's "Plaque" vs "Shoe". `defaults` is a
102
+ * sparse map of `config_field.name → value`; values are authored in
103
+ * display units (the form converts via the field's `scale` before
104
+ * writing to stagedConfig / GM, same convention as `TestFieldDef.default`).
105
+ */
106
+ export interface TestConfiguration {
107
+ /** Canonical key — unique within the method. */
108
+ name: string;
109
+ /** Pretty label shown in the Configuration picker. Falls back to `name`. */
110
+ label?: string;
111
+ /** Long-form guidance shown beside the dropdown in the picker dialog. */
112
+ description?: string;
113
+ /** Sparse `config_field.name → value` overrides applied on selection. */
114
+ defaults?: Record<string, any>;
78
115
  }
79
116
  /**
80
117
  * Test-setup form. Renders Sample ID, Test Method picker, and Test
@@ -1 +1 @@
1
- {"version":3,"file":"TestSetupForm.d.ts","sourceRoot":"","sources":["../../../src/components/tis/TestSetupForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2D,MAAM,OAAO,CAAC;AAchF;;;;;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;CAClB;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;CAC/B;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;AAiGD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAsftD,CAAC"}
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;;;;;;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;AA+GD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA4lBtD,CAAC"}
@@ -1 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import React,{useState,useEffect,useContext,useMemo,useRef}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";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,methodLabelOf=(e,t)=>t?.label&&t.label.length>0?t.label:e,AssetIdPicker=({assetType:e,value:t,onChange:s,invalid:i})=>{const a=useAmsAssets(),n=useAmsRoles(),o=useMemo(()=>a.filter(t=>t.asset_type===e&&"active"===t.status).map(e=>{const t=n[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}}),[a,n,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:i?"p-invalid":"",filter:!0,showClear:!0,disabled:0===o.length})};export const TestSetupForm=({schema:e,defaultMethodId:t,onMethodChange:s,onValidationChange:i})=>{const a=useTis(),{invoke:n,write:o}=useContext(EventEmitterContext),{rawValues:r,findTagByFqdn:l}=useContext(AutoCoreTagContext),c=useMemo(()=>Object.keys(a.schemas),[a.schemas]),d=a.selection.projectId,m=""!==d.trim()&&a.projectKnown(d.trim()),[p,u]=useState(a.selection.methodId||t||a.defaultMethodId||""),[f,h]=useState(a.selection.sampleId||""),g=a.stagedConfig,x=e=>{const t="function"==typeof e?e(a.stagedConfig):e;t!==a.stagedConfig&&a.setStagedConfig(t)},_=e??(p?a.schemas[p]:void 0);useEffect(()=>{a.selection.methodId!==p&&p&&a.setSelection({methodId:p}),s&&s(p)},[p]),useEffect(()=>{a.selection.sampleId!==f&&a.setSelection({sampleId:f})},[f]),useEffect(()=>{a.state.stagedSampleId&&a.state.stagedSampleId!==f&&h(a.state.stagedSampleId)},[a.state.stagedSampleId]),useEffect(()=>{a.selection.methodId&&a.selection.methodId!==p&&u(a.selection.methodId)},[a.selection.methodId]);const[j,y]=useState(!1),[b,v]=useState(!1),[T,I]=useState({open:!1,title:"",body:null}),C=useRef("");useEffect(()=>{_&&p&&C.current!==p&&(C.current=p,x(e=>{let t=e;for(const s of _.config_fields){if("sample_id"===s.name)continue;if(void 0===s.default||null===s.default)continue;t===e&&(t={...e});const i=displayToRaw(s.default,s.scale);t[s.name]=i,s.source&&Promise.resolve().then(()=>o(s.source,i)).catch(e=>{})}return t}))},[_,p,o]),useEffect(()=>{_&&x(e=>{let t=e;for(const s of _.config_fields){if("sample_id"===s.name)continue;if(!s.source)continue;const i=l(s.source);if(!i)continue;const a=r[i.tagName];null!=a&&(t[s.name]!==a&&(t===e&&(t={...e}),t[s.name]=a))}return t})},[_,r,l]),useEffect(()=>{if(!_)return void y(!1);let e=!0;m||(e=!1),p.trim()||(e=!1),f.trim()||(e=!1),e&&!a.projectFieldsLoaded&&(e=!1);for(const t of _.config_fields)if("sample_id"!==t.name&&t.required){const s=g[t.name];if(void 0===s||""===s||null===s){e=!1;break}}if(y(e),i&&i(e,g),e){const{sample_id:e,...t}=g??{},s={...a.projectFields,...t};n("tis.stage_test",MessageType.Request,{project_id:d,method_id:p,sample_id:f,config:s}).catch(e=>{})}},[g,_,d,p,f,m,a.projectFields,a.projectFieldsLoaded,i,n]);const S=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:a.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 N=j&&!a.state.lastStartError;return _jsxs("div",{className:"ac-form-grid",style:{padding:"1.25rem",gridTemplateColumns:"auto 1fr 1.75rem 1.75rem"},children:[_jsxs("h3",{className:"ac-form-section",style:{display:"flex",alignItems:"center",gap:"10px"},children:["Test Setup",_jsx(Button,{icon:N?"pi pi-check-circle":"pi pi-exclamation-circle",severity:N?"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."),a.projectFieldsLoaded||e.push("Project fields are still loading."),p.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;if(!t.required)continue;const s=g[t.name];void 0!==s&&""!==s&&null!==s||e.push(`Required field "${labelOf(t)}" is empty.`)}return e})(),t=a.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."}),I({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(p,_),readOnly:!0,style:{flex:1},tabIndex:-1}),_jsx(Button,{icon:"pi pi-pencil",type:"button",onClick:()=>v(!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"})})]}),_jsx("h3",{className:"ac-form-section",style:{marginTop:"1rem"},children:"Test Configuration"}),_.config_fields.map(e=>{if("sample_id"===e.name)return null;const t=(e=>{if(!e.required)return!0;const t=g[e.name];return void 0!==t&&""!==t&&null!==t})(e),s=(e=>{const t=_?.asset_refs??[],s=a.projectAssetRefs??[],i=`config.${e.name}`,n=t.find(e=>"by_id_field"===e.select&&e.from===i);if(n)return n.asset_type;const o=s.find(e=>"by_id_field"===e.select&&e.from===i);return o?o.asset_type:null})(e),i=!s&&"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=>S(e,t),invalid:!t}):i?_jsx(ValueInput,{label:void 0,value:null!=g[e.name]?Number(rawToDisplay(Number(g[e.name]),e.scale)):null,onValueChanged:t=>S(e,t),className:t?"":"p-invalid"}):_jsx(TextInput,{label:void 0,value:null!=g[e.name]?String(g[e.name]):"",onValueChanged:t=>S(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:()=>I({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:b,onHide:()=>v(!1),currentMethodId:p,onSelected:e=>u(e)}),_jsx(Dialog,{header:T.title,visible:T.open,onHide:()=>I({open:!1,title:"",body:null}),style:{width:"32rem",maxWidth:"90vw"},modal:!0,dismissableMask:!0,children:T.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{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,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 o=useAmsAssets(),n=useAmsRoles(),a=useMemo(()=>o.filter(t=>t.asset_type===e&&"active"===t.status).map(e=>{const t=n[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}}),[o,n,e]);return _jsx(Dropdown,{value:t,options:a,onChange:e=>s(e.value??""),placeholder:0===a.length?`No active ${e} assets registered — add one in Settings → Assets`:`Select ${e}…`,className:i?"p-invalid":"",filter:!0,showClear:!0,disabled:0===a.length})};export const TestSetupForm=({schema:e,defaultMethodId:t,onMethodChange:s,onValidationChange:i})=>{const o=useTis(),{invoke:n,write:a}=useContext(EventEmitterContext),{rawValues:r,findTagByFqdn:l}=useContext(AutoCoreTagContext),c=useMemo(()=>Object.keys(o.schemas),[o.schemas]),d=o.selection.projectId,m=""!==d.trim()&&o.projectKnown(d.trim()),[p,u]=useState(o.selection.methodId||t||o.defaultMethodId||""),[f,h]=useState(o.selection.sampleId||""),g=o.stagedConfig,x=e=>{const t="function"==typeof e?e(o.stagedConfig):e;t!==o.stagedConfig&&o.setStagedConfig(t)},_=e??(p?o.schemas[p]: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),o=displayToRaw(i,e?.scale);t[s]=o,e?.source&&Promise.resolve().then(()=>a(e.source,o)).catch(e=>{})}return t};useEffect(()=>{o.selection.methodId!==p&&p&&o.setSelection({methodId:p}),s&&s(p)},[p]),useEffect(()=>{o.selection.sampleId!==f&&o.setSelection({sampleId:f})},[f]),useEffect(()=>{o.state.stagedSampleId&&o.state.stagedSampleId!==f&&h(o.state.stagedSampleId)},[o.state.stagedSampleId]),useEffect(()=>{o.selection.methodId&&o.selection.methodId!==p&&u(o.selection.methodId)},[o.selection.methodId]);const[y,b]=useState(!1),[v,C]=useState(!1),[I,T]=useState(!1),N=_?.configurations??[],S=N.find(e=>e.name===o.configurationName),[w,A]=useState({open:!1,title:"",body:null});useEffect(()=>{if(!_||!p)return;if(o.defaultsAppliedForMethod===p)return;o.markDefaultsAppliedForMethod(p);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;s===t&&(s={...t});const i=displayToRaw(e.default,e.scale);s[e.name]=i,e.source&&Promise.resolve().then(()=>a(e.source,i)).catch(e=>{})}return e&&(s===t&&(s={...t}),s=j(e,s)),s}),o.setConfigurationName(e?e.name:"")},[_,p,a,o.defaultsAppliedForMethod,o.markDefaultsAppliedForMethod,o.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=l(s.source);if(!i)continue;const o=r[i.tagName];null!=o&&(t[s.name]!==o&&(t===e&&(t={...e}),t[s.name]=o))}return t})},[_,r,l]),useEffect(()=>{if(!_)return void b(!1);let e=!0;m||(e=!1),p.trim()||(e=!1),f.trim()||(e=!1),e&&!o.projectFieldsLoaded&&(e=!1);for(const t of _.config_fields)if("sample_id"!==t.name&&t.required){const s=g[t.name];if(void 0===s||""===s||null===s){e=!1;break}}if(b(e),i&&i(e,g),e){const{sample_id:e,...t}=g??{},s={...o.projectFields,...t};n("tis.stage_test",MessageType.Request,{project_id:d,method_id:p,sample_id:f,config:s}).catch(e=>{})}},[g,_,d,p,f,m,o.projectFields,o.projectFieldsLoaded,i,n]);const k=async(e,t)=>{const s=displayToRaw(t,e.scale);if(x({...g,[e.name]:s}),e.source)try{await a(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:o.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=y&&!o.state.lastStartError;return _jsxs("div",{className:"ac-form-grid",style:{padding:"1.25rem",gridTemplateColumns:"auto 1fr 1.75rem 1.75rem"},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."),o.projectFieldsLoaded||e.push("Project fields are still loading."),p.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;if(!t.required)continue;const s=g[t.name];void 0!==s&&""!==s&&null!==s||e.push(`Required field "${labelOf(t)}" is empty.`)}return e})(),t=o.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."}),A({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(p,_),readOnly:!0,style:{flex:1},tabIndex:-1}),_jsx(Button,{icon:"pi pi-folder",type:"button",onClick:()=>C(!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:()=>T(!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("h3",{className:"ac-form-section",style:{marginTop:"1rem"},children:"Test Configuration"}),_.config_fields.map(e=>{if("sample_id"===e.name)return null;const t=(e=>{if(!e.required)return!0;const t=g[e.name];return void 0!==t&&""!==t&&null!==t})(e),s=(e=>{const t=_?.asset_refs??[],s=o.projectAssetRefs??[],i=`config.${e.name}`,n=t.find(e=>"by_id_field"===e.select&&e.from===i);if(n)return n.asset_type;const a=s.find(e=>"by_id_field"===e.select&&e.from===i);return a?a.asset_type:null})(e),i=!s&&Array.isArray(e.options)&&e.options.length>0?normalizeOptions(e.options):null,n=!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=>k(e,t),invalid:!t}):i?_jsx(Dropdown,{value:g[e.name]??null,options:i,onChange:t=>k(e,t.value),placeholder:`Select ${e.label??e.name}…`,className:t?"":"p-invalid",showClear:!e.required}):n?_jsx(ValueInput,{label:void 0,value:null!=g[e.name]?Number(rawToDisplay(Number(g[e.name]),e.scale)):null,onValueChanged:t=>k(e,t),className:t?"":"p-invalid"}):_jsx(TextInput,{label:void 0,value:null!=g[e.name]?String(g[e.name]):"",onValueChanged:t=>k(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:()=>A({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:v,onHide:()=>C(!1),currentMethodId:p,onSelected:e=>u(e)}),_jsx(ConfigurationDialog,{visible:I,onHide:()=>T(!1),configurations:N,currentConfigName:o.configurationName,onSelected:e=>{const t=N.find(t=>t.name===e);t&&(x(e=>j(t,{...e})),o.setConfigurationName(e))}}),_jsx(Dialog,{header:w.title,visible:w.open,onHide:()=>A({open:!1,title:"",body:null}),style:{width:"32rem",maxWidth:"90vw"},modal:!0,dismissableMask:!0,children:w.body})]})};
@@ -126,6 +126,31 @@ export interface TisContextValue {
126
126
  /** Reset `stagedConfig` to `{}`. Called by the form on a fresh
127
127
  * method selection or after a run completes. */
128
128
  clearStagedConfig: () => void;
129
+ /** methodId whose schema defaults have already been seeded into
130
+ * `stagedConfig` (and written to GM for source-bound fields) for
131
+ * the current session. `<TestSetupForm>` gates its one-shot
132
+ * "apply defaults" effect on this value so it only fires on an
133
+ * actual method change — not on form remount when the operator
134
+ * switches tabs. Empty string means "no method seeded yet."
135
+ * Reset to `''` by `clearStagedConfig()` so the next staged test
136
+ * re-seeds correctly. */
137
+ defaultsAppliedForMethod: string;
138
+ /** Mark the given methodId as having been seeded. The form calls
139
+ * this immediately after writing the defaults, so subsequent
140
+ * remounts skip re-application. */
141
+ markDefaultsAppliedForMethod: (methodId: string) => void;
142
+ /** `name` of the method configuration the operator currently has
143
+ * selected (see `TestMethod.configurations`). Empty string when the
144
+ * active method declares no configurations, or none is chosen yet.
145
+ * Held on the provider — like `stagedConfig` — so the selection
146
+ * survives `<TestSetupForm>` remounting on a tab switch. HMI-only:
147
+ * it scopes which override set is shown; the override *values* it
148
+ * applies are what actually get recorded. */
149
+ configurationName: string;
150
+ /** Set the selected configuration name. The form calls this when the
151
+ * seeding effect auto-applies the first configuration and when the
152
+ * operator accepts a different one in the picker dialog. */
153
+ setConfigurationName: (name: string) => void;
129
154
  /** Fetch the run list for a (project, method?) pair. Method may be
130
155
  * omitted to aggregate runs across every method in the project —
131
156
  * the History tab uses this. */
@@ -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:()=>{},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({}),[j,h]=useState(0),g=useCallback(()=>h(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]),y=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&&y(e.run_id,e.results??{})});return()=>{r(e),r(t)}},[a,r,C,y]);const S=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 E=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]),R=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]),k=useMemo(()=>({...v.current}),[j]),[M,P]=useState([]),b=useRef(new Set),[w,F]=useState(0),[A,q]=useState({}),D=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)))},[]),Y=useCallback(e=>!!e&&(!!b.current.has(e)||M.includes(e)),[M,w]),J=useCallback((e,t)=>{e&&q(s=>({...s,[e]:t}))},[]),K=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 q(t=>({...t,[e]:s})),s}}catch(e){}return q(t=>({...t,[e]:{}})),{}},[s]);useEffect(()=>{D()},[D]),useEffect(()=>{const e=a("tis.project_created",()=>{D()}),t=a("tis.project_updated",e=>{const t="string"==typeof e?.project_id?e.project_id:"";t&&K(t)});return()=>{r(e),r(t)}},[a,r,D,K]),useEffect(()=>{const e=S.projectId;e&&Y(e)&&void 0===A[e]&&K(e)},[S.projectId,Y,A,K]);const N=A[S.projectId]??{},O=void 0!==A[S.projectId],[W,z]=useState({}),B=useCallback(e=>{z(t=>({...t,...e}))},[]),G=useCallback(()=>{z({})},[]),H=useMemo(()=>({schemas:c,projectAssetRefs:n,defaultMethodId:o,schemasLoaded:l,state:_,selection:S,setSelection:x,existingProjects:M,projectKnown:Y,refreshProjects:D,markProjectJustCreated:L,projectFields:N,projectFieldsLoaded:O,loadProjectFields:K,setProjectFields:J,stagedConfig:W,setStagedConfig:B,clearStagedConfig:G,fetchRuns:E,fetchRun:R,runCache:k}),[c,n,o,l,_,S,x,M,Y,D,L,N,O,K,J,W,B,G,E,R,k]);return _jsx(TisContext.Provider,{value:H,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:""},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};
@@ -119,3 +119,23 @@
119
119
  gap: 0.25rem;
120
120
  margin-bottom: 0.25rem;
121
121
  }
122
+
123
+ /* Portrait displays (e.g. wall-mounted HMIs rotated to portrait) don't
124
+ have the horizontal room for a fixed 320px selection pane beside the
125
+ content pane — the content pane gets squeezed and cut off. Stack the
126
+ two panes instead: the method-selection list becomes a capped,
127
+ scrollable band on top, and the editor takes the remaining height. */
128
+ @media (orientation: portrait) {
129
+ .tis-editor__body {
130
+ flex-direction: column;
131
+ }
132
+
133
+ .tis-editor__sidebar {
134
+ /* Cap the list at ~35% of the body height (it scrolls internally
135
+ via the DataTable's scrollHeight="flex"); the editor below gets
136
+ the rest. Drop the fixed 320px width — full width when stacked. */
137
+ flex: 0 1 35%;
138
+ border-right: none;
139
+ border-bottom: 1px solid var(--surface-d, #e2e8f0);
140
+ }
141
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * ConfigurationsEditor — manages a method's named `configurations`
3
+ * (e.g. translational_traction's "Plaque" vs "Shoe"). Each configuration
4
+ * carries a sparse set of config_field overrides; only the fields whose
5
+ * value is specific to that configuration need to be listed.
6
+ *
7
+ * Mirrors <AssetRefsEditor>: a DataTable of the declared configurations
8
+ * plus an add/edit Dialog. The override sub-editor is a small key/value
9
+ * table whose key is a dropdown of the method's own config_fields, so the
10
+ * author can only target fields that actually exist, and values are
11
+ * coerced to the field's declared type on save.
12
+ */
13
+ import type { TestMethod } from '../types';
14
+ export interface ConfigurationsEditorProps {
15
+ method: TestMethod;
16
+ onChange: (next: TestMethod) => void;
17
+ }
18
+ export declare const ConfigurationsEditor: React.FC<ConfigurationsEditorProps>;
19
+ //# sourceMappingURL=ConfigurationsEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConfigurationsEditor.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/ConfigurationsEditor.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAYH,OAAO,KAAK,EAAgC,UAAU,EAAE,MAAM,UAAU,CAAC;AAEzE,MAAM,WAAW,yBAAyB;IACtC,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;CACxC;AAuBD,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,yBAAyB,CA8LpE,CAAC"}
@@ -0,0 +1 @@
1
+ import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useMemo,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{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{FormSection}from"../../forms/FormSection";import{FormRow}from"../../forms/FormRow";const NUMERIC_TYPES=new Set(["i32","i64","u32","u64","f32","f64"]),coerceValue=(e,t)=>{const i=e?.type??"string";if("bool"===i)return"true"===t||"1"===t;if(NUMERIC_TYPES.has(i)){const e=Number(t);return""!==t.trim()&&Number.isFinite(e)?e:t}return t},blank=()=>({name:"",defaults:{}}),toRows=e=>Object.entries(e??{}).map(([e,t])=>({field:e,value:String(t)}));export const ConfigurationsEditor=({method:e,onChange:t})=>{const i=e.configurations??[],n=e.config_fields??[],[o,a]=useState(!1),[l,r]=useState(null),[s,u]=useState(blank()),[d,m]=useState([]),[c,p]=useState(null),f=useMemo(()=>n.filter(e=>e.name&&"sample_id"!==e.name).map(e=>({label:e.units?`${e.name} [${e.units}]`:e.name,value:e.name})),[n]),h=useMemo(()=>{const e=new Map;return n.forEach(t=>e.set(t.name,t)),e},[n]),x=(e,t)=>m(d.map((i,n)=>n===e?{...i,...t}:i));return _jsxs(_Fragment,{children:[_jsx(FormSection,{title:"Configurations",description:"Named, ready-to-use override sets (e.g. Plaque vs Shoe). Selecting one in Test Setup writes its values into the matching config fields; the method's first configuration is applied automatically.",actions:_jsx(Button,{label:"Add configuration",icon:"pi pi-plus",size:"small",onClick:()=>{r(null),u(blank()),m([]),p(null),a(!0)}}),children:_jsxs(DataTable,{value:i,dataKey:"name",emptyMessage:"No configurations declared.",children:[_jsx(Column,{field:"name",header:"Name",style:{width:"10rem"}}),_jsx(Column,{header:"Label",body:e=>e.label??"—",style:{width:"10rem"}}),_jsx(Column,{header:"Overrides",body:e=>{const t=Object.entries(e.defaults??{});return 0===t.length?"—":t.map(([e,t])=>`${e}=${String(t)}`).join(", ")}}),_jsx(Column,{header:"",body:(n,o)=>_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=o.rowIndex,r(e),u({...i[e]}),m(toRows(i[e].defaults)),p(null),void a(!0);var e}}),_jsx(Button,{icon:"pi pi-trash",className:"p-button-text p-button-danger p-button-sm",onClick:()=>{return n=o.rowIndex,void(window.confirm(`Remove configuration "${i[n].name}"?`)&&t({...e,configurations:i.filter((e,t)=>t!==n)}));var n}})]}),style:{width:"6rem"}})]})}),_jsxs(Dialog,{header:null===l?"New configuration":`Edit configuration: ${i[l??0]?.name}`,visible:o,onHide:()=>a(!1),style:{width:"44rem"},children:[c&&_jsx("div",{style:{color:"#dc2626",marginBottom:"0.5rem"},children:c}),_jsx(FormRow,{label:"Name",required:!0,hint:"Canonical key — unique within the method.",children:_jsx(InputText,{value:s.name,onChange:e=>u({...s,name:e.target.value}),placeholder:"e.g. plaque"})}),_jsx(FormRow,{label:"Label",hint:"Shown in the Configuration picker. Falls back to Name.",children:_jsx(InputText,{value:s.label??"",onChange:e=>u({...s,label:e.target.value||void 0}),placeholder:"e.g. Plaque"})}),_jsx(FormRow,{label:"Description",hint:"Shown beside the dropdown in the picker dialog.",children:_jsx(InputTextarea,{rows:2,value:s.description??"",onChange:e=>u({...s,description:e.target.value||void 0})})}),_jsx(FormSection,{title:"Field overrides",description:"Only list fields whose value is specific to this configuration. Values are authored in display units, same as a field's default.",actions:_jsx(Button,{label:"Add override",icon:"pi pi-plus",size:"small",onClick:()=>m([...d,{field:"",value:""}]),disabled:0===f.length}),children:0===f.length?_jsx("small",{style:{color:"var(--text-secondary-color)"},children:"This method has no config_fields to override yet — add some on the Fields tab first."}):0===d.length?_jsx("small",{style:{color:"var(--text-secondary-color)"},children:"No overrides — this configuration uses every field's base default."}):_jsx("div",{style:{display:"flex",flexDirection:"column",gap:"0.5rem"},children:d.map((e,t)=>{const i=h.get(e.field);return _jsxs("div",{style:{display:"flex",gap:"0.5rem",alignItems:"center"},children:[_jsx(Dropdown,{value:e.field||null,options:f,onChange:e=>x(t,{field:e.value}),placeholder:"Field",style:{flex:"0 0 16rem"}}),"bool"===i?.type?_jsx(Dropdown,{value:"true"===e.value||"1"===e.value?"true":"false",options:[{label:"true",value:"true"},{label:"false",value:"false"}],onChange:e=>x(t,{value:e.value}),style:{flex:1}}):_jsx(InputText,{value:e.value,onChange:e=>x(t,{value:e.target.value}),placeholder:i?.type&&NUMERIC_TYPES.has(i.type)?"number":"value",style:{flex:1}}),_jsx(Button,{icon:"pi pi-trash",className:"p-button-text p-button-danger p-button-sm",onClick:()=>(e=>m(d.filter((t,i)=>i!==e)))(t)})]},t)})})}),_jsxs("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.5rem",marginTop:"1rem"},children:[_jsx(Button,{label:"Cancel",className:"p-button-text",onClick:()=>a(!1)}),_jsx(Button,{label:"Save",onClick:()=>{const n=(e=>{if(!e.name.trim())return"Name is required.";if(i.some((t,i)=>t.name===e.name&&i!==l))return`A configuration named "${e.name}" already exists.`;const t=new Set;for(const e of d){if(!e.field)return"Every override row must pick a field.";if(t.has(e.field))return`Field "${e.field}" is overridden more than once.`;if(t.add(e.field),!h.has(e.field))return`"${e.field}" is not a config_field on this method.`}return null})(s);if(n)return void p(n);const o={};for(const e of d)o[e.field]=coerceValue(h.get(e.field),e.value);const r={name:s.name.trim(),...s.label?.trim()?{label:s.label.trim()}:{},...s.description?.trim()?{description:s.description.trim()}:{},defaults:o},u=[...i];null===l?u.push(r):u[l]=r,t({...e,configurations:u}),a(!1)}})]})]})]})};
@@ -1 +1 @@
1
- {"version":3,"file":"MethodFormEditor.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/MethodFormEditor.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAaH,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,CA+I5D,CAAC"}
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"}