@adcops/autocore-react 3.3.112 → 3.3.115
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.
- package/dist/components/ams/AmsProvider.d.ts.map +1 -1
- package/dist/components/ams/AmsProvider.js +1 -1
- package/dist/components/ams-editor/AssetFieldArray.d.ts +21 -0
- package/dist/components/ams-editor/AssetFieldArray.d.ts.map +1 -0
- package/dist/components/ams-editor/AssetFieldArray.js +1 -0
- package/dist/components/ams-editor/AssetTypeEditor.css +122 -0
- package/dist/components/ams-editor/AssetTypeEditor.d.ts +26 -0
- package/dist/components/ams-editor/AssetTypeEditor.d.ts.map +1 -0
- package/dist/components/ams-editor/AssetTypeEditor.js +1 -0
- package/dist/components/ams-editor/AssetTypeFormEditor.d.ts +21 -0
- package/dist/components/ams-editor/AssetTypeFormEditor.d.ts.map +1 -0
- package/dist/components/ams-editor/AssetTypeFormEditor.js +1 -0
- package/dist/components/ams-editor/SubLocationsEditor.d.ts +17 -0
- package/dist/components/ams-editor/SubLocationsEditor.d.ts.map +1 -0
- package/dist/components/ams-editor/SubLocationsEditor.js +1 -0
- package/dist/components/ams-editor/index.d.ts +7 -0
- package/dist/components/ams-editor/index.d.ts.map +1 -0
- package/dist/components/ams-editor/index.js +1 -0
- package/dist/components/ams-editor/types.d.ts +34 -0
- package/dist/components/ams-editor/types.d.ts.map +1 -0
- package/dist/components/ams-editor/types.js +1 -0
- package/dist/components/tis/ResultHistoryTable.d.ts.map +1 -1
- package/dist/components/tis/ResultHistoryTable.js +1 -1
- package/dist/components/tis/ScienceTable.d.ts +15 -0
- package/dist/components/tis/ScienceTable.d.ts.map +1 -1
- package/dist/components/tis/ScienceTable.js +1 -1
- package/dist/components/tis/TestDataView.d.ts +10 -4
- package/dist/components/tis/TestDataView.d.ts.map +1 -1
- package/dist/components/tis/TestDataView.js +1 -1
- package/dist/components/tis-editor/TisConfigEditor.d.ts +1 -1
- package/dist/components/tis-editor/editor/SaveDiffDialog.d.ts.map +1 -1
- package/dist/components/tis-editor/editor/SaveDiffDialog.js +1 -1
- package/dist/hooks/useAmsSchemaConfig.d.ts +39 -0
- package/dist/hooks/useAmsSchemaConfig.d.ts.map +1 -0
- package/dist/hooks/useAmsSchemaConfig.js +1 -0
- package/dist/hooks/useTisConfig.d.ts +1 -1
- package/package.json +110 -110
- package/src/components/ams/AmsProvider.tsx +20 -15
- package/src/components/ams-editor/AssetFieldArray.tsx +103 -0
- package/src/components/ams-editor/AssetTypeEditor.css +122 -0
- package/src/components/ams-editor/AssetTypeEditor.tsx +295 -0
- package/src/components/ams-editor/AssetTypeFormEditor.tsx +178 -0
- package/src/components/ams-editor/SubLocationsEditor.tsx +151 -0
- package/src/components/ams-editor/index.ts +16 -0
- package/src/components/ams-editor/types.ts +40 -0
- package/src/components/tis/ResultHistoryTable.tsx +141 -1
- package/src/components/tis/ScienceTable.tsx +71 -12
- package/src/components/tis/TestDataView.tsx +65 -6
- package/src/components/tis-editor/TisConfigEditor.tsx +1 -1
- package/src/components/tis-editor/editor/SaveDiffDialog.tsx +4 -3
- package/src/hooks/useAmsSchemaConfig.ts +142 -0
- package/src/hooks/useTisConfig.ts +1 -1
|
@@ -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 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";import{ScienceTable}from"./ScienceTable";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,n=e.runId??t.selection.runId,l=e.schema??(a?t.schemas[a]:void 0),{throttleMs:o=100,cycleTableHeight:s="400px",chartHeight:i="320px"}=e,{invoke:c,subscribe:d,unsubscribe:u}=useContext(EventEmitterContext),[m,p]=useState(null),[y,f]=useState([]),[h,g]=useState({}),[_,x]=useState({}),[b,j]=useState(!1),[v,w]=useState(!1),C=useRef(null),[S,T]=useState(null),[R,M]=useState(null),[L,I]=useState(!1),[E,O]=useState(null),[A,D]=useState(null),[N,k]=useState(!1),[F,H]=useState([]),[z,P]=useState(null),B=useMemo(()=>{const e=[];for(const[t,r]of Object.entries(l?.views??{}))e.push({name:t,view:r});return e},[l]),[V,$]=useState(B.length>0?B[0].name:null);useEffect(()=>{if(0===B.length)return;null!==V&&B.some(e=>e.name===V)||$(B[0].name)},[B,V]);const G=B.find(e=>e.name===V)?.view,W="raw_trace"===G?.type,q=useRef([]),U=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=[],f(t=>[...e.slice().reverse(),...t])}U.current&&(g(U.current),U.current=null)},o))};useEffect(()=>{if(!r||!a||!n)return p(null),f([]),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:n});!e&&t?.success&&(p(t.data),g(t.data.results??{}));const l=await c("tis.read_cycles",MessageType.Request,{project_id:r,method_id:a,run_id:n,offset:0,limit:200,order:"desc"});!e&&l?.success&&f(l.data.cycles??[])}catch(e){}})(),()=>{e=!0}},[r,a,n,c]),useEffect(()=>{const e=e=>e?.project_id===r&&e?.method_id===a&&e?.run_id===n,t=d("tis.cycle_added",t=>{e(t)&&t.cycle&&(q.current.push(t.cycle),Y())}),l=d("tis.results_updated",t=>{e(t)&&(U.current=t.results??{},Y())});return()=>{u(t),u(l),J.current&&(clearTimeout(J.current),J.current=null)}},[r,a,n,o]);const Z="string"==typeof m?.sample_id&&m.sample_id?m.sample_id:"string"==typeof m?.config?.sample_id?m.config.sample_id:"";useEffect(()=>{if(!r||!a||!Z)return void x({});let e=!1;(async()=>{try{const t=await c("tis.sample_summary",MessageType.Request,{project_id:r,method_id:a,sample_id:Z});!e&&t?.success&&x(t.data?.summary??{})}catch(e){}})();const t=d("tis.sample_summary_updated",e=>{e?.project_id===r&&e?.method_id===a&&e?.sample_id===Z&&x(e.summary??{})});return()=>{e=!0,u(t)}},[r,a,Z,c,d,u]);const K=useRawCycleData({projectId:r,methodId:a,runId:n,blobName:l?.raw_data?.blob_name??"trace",source:G?.source??"raw",enabled:W}),Q=useMemo(()=>{if(!G)return null;if("cycle_scatter"===G.type){const e=G.x.field;if(!e)return null;const t=[...y].reverse();return{labels:t.map(t=>t[e]),datasets:G.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"===G.type){if(!K.raw)return null;const e=G.x.column;if(!e)return null;const t=K.raw[e]??[],r=!1!==G.smooth;return{datasets:G.y.map((e,a)=>{const n=K.raw[e.column]??[],l=r?smoothTraceForDisplay(t,n,G.smoothWindow):n.map((e,r)=>({x:t[r],y:e}));return{label:e.label??e.column,data:l,yAxisID:"right"===e.y_axis?"y1":"y",borderColor:palette(a),backgroundColor:palette(a),pointRadius:0,borderWidth:1.5,showLine:!0,...r?{cubicInterpolationMode:"monotone"}:{}}})}}return null},[G,y,K.raw]),X=G?.y.some(e=>"right"===e.y_axis)??!1,ee=useMemo(()=>{if(!W)return[];const e=K.envelope,t=e?.regions??e?.context?.regions;return Array.isArray(t)?t:[]},[W,K.envelope]),te=useMemo(()=>[...G?.regions??[],...ee],[G,ee]),re=useMemo(()=>{const e="raw_trace"===G?.type;return{responsive:!0,maintainAspectRatio:!1,parsing:!e&&void 0,scales:{x:e?{type:"linear",title:{display:!!G?.x.label,text:G?.x.label}}:{title:{display:!!G?.x.label,text:G?.x.label}},y:{position:"left",title:{display:!0,text:leftAxisLabel(G)}},...X?{y1:{position:"right",grid:{drawOnChartArea:!1},title:{display:!0,text:rightAxisLabel(G)}}}:{}},plugins:{legend:{display:!0},zoom:{pan:{enabled:!0,mode:"xy"},zoom:{wheel:{enabled:!0},pinch:{enabled:!0},mode:"xy"}},annotation:{annotations:buildRegionAnnotations(te)}}}},[G,X,te]),ae=l?.raw_data?.blob_name??"trace",ne=useRef(""),le=useCallback(async()=>{if(!r||!a||!n)return[];try{const e=await c("tis.list_raw",MessageType.Request,{project_id:r,method_id:a,run_id:n});if(!e?.success)return[];const t=e.data?.cycles??[];return t.filter(e=>e?.name===ae&&"number"==typeof e?.cycle_index).map(e=>e.cycle_index).sort((e,t)=>e-t)}catch{return[]}},[r,a,n,ae,c]),oe=useCallback(async e=>{if(!r||!a||!n)return;const t=`${r}|${a}|${n}|${ae}|${e??"latest"}`;if(ne.current===t)return;ne.current=t;const l={project_id:r,method_id:a,run_id:n,name:ae};null!=e&&(l.cycle_index=e),I(!0),M(null),T(null);try{const e=await c("tis.read_raw",MessageType.Request,l);e?.success?T(e.data??{}):M(e?.error_message??"No raw data on disk for this run.")}catch(e){M(String(e?.message??e))}finally{I(!1)}k(!0),D(null),O(null);try{const e=await c("tis.read_filtered",MessageType.Request,l);e?.success?O(e.data??{}):D(e?.error_message??"No filtered data on disk for this run.")}catch(e){D(String(e?.message??e))}finally{k(!1)}},[r,a,n,ae,c]);useEffect(()=>{ne.current="",H([]),P(null)},[r,a,n,ae]);return useEffect(()=>{b&&null!=z&&oe(z)},[b,z,oe]),r&&a&&n&&l?_jsxs("div",{className:"vblock",style:{display:"flex",flexDirection:"column",gap:"1rem"},children:[_jsx(Header,{meta:m,config:m?.config,runId:n,projectId:r,methodId:a,canViewRaw:!!l.raw_data,onViewRaw:async()=>{j(!0);let e=F;0===e.length&&(e=await le(),H(e));const t=e.length>0?e[e.length-1]:null,r=z??t;z!==r&&P(r),await oe(r)},onShowConfig:()=>w(!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:V,options:B.map(e=>({label:e.view.title??e.name,value:e.name})),onChange:e=>$(e.value),placeholder:0===B.length?"No view defined":"Select a view",disabled:0===B.length}),_jsx("h3",{style:{margin:0},children:G?.title??""}),W&&K.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:K.selectedCycle,options:K.cycles.map(e=>({label:`Cycle ${e}`,value:e})),onChange:e=>K.setSelectedCycle(Number(e.value)),style:{minWidth:"8rem"}}),_jsxs("span",{style:{color:"var(--text-secondary-color)"},children:["of ",K.cycles.length]})]}),_jsx("div",{style:{flex:1}}),_jsx(Button,{icon:"pi pi-th-large",outlined:!0,rounded:!0,size:"small",onClick:()=>C.current?.resetZoom?.(),disabled:!Q,tooltip:"Reset chart zoom",tooltipOptions:{position:"left"},"aria-label":"Reset chart zoom"})]}),_jsxs("div",{style:{height:i,position:"relative"},children:[W&&K.loading&&_jsx(ChartOverlay,{children:"Loading raw data…"}),W&&K.error&&_jsx(ChartOverlay,{children:K.error}),Q&&_jsx(Line,{ref:C,data:Q,options:re})]})]}),(()=>{const e=y.length>CYCLE_VIRTUAL_THRESHOLD,t=l.cycle_fields.map(e=>({field:e.name,label:e.name,units:e.units,body:t=>formatCell(t[e.name],e.type,e.scale)}));return _jsx(ScienceTable,{title:`Cycle Data (${y.length})`,columns:t,rows:y,scrollable:e,scrollHeight:e?s:void 0,emptyMessage:"No cycles yet."})})(),(()=>{const e={...h,..._},t=Object.keys(e).length>0,r=l.results_fields.map(e=>({field:e.name,label:e.name,units:e.units,body:t=>formatCell(t[e.name],e.type,e.scale)}));return _jsx(ScienceTable,{title:"Results",columns:r,rows:t?[e]:[],emptyMessage:"No results yet."})})(),l.raw_data&&_jsxs(Dialog,{visible:b,onHide:()=>j(!1),header:`Run Data — ${n}`,style:{width:"90vw",height:"80vh"},maximizable:!0,children:[_jsx(CyclePickerBar,{cycles:F,selected:z,onChange:P}),_jsx(RawEnvelopeHeader,{envelope:S}),_jsxs(TabView,{style:{height:"100%"},children:[_jsx(TabPanel,{header:"Raw Data",children:_jsx(DataBlobTable,{blob:unwrapEnvelope(S),loading:L,error:R,rawData:l.raw_data})}),_jsx(TabPanel,{header:"Filtered Data",children:_jsx(DataBlobTable,{blob:unwrapEnvelope(E),loading:N,error:A,rawData:l.raw_data,emptyMessage:"Filtered data is written by post-processing — none on disk for this run yet."})})]})]}),_jsx(Dialog,{visible:v,onHide:()=>w(!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:n,canViewRaw:l,onViewRaw:o,onShowConfig:s})=>{const i="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:[i||r,c&&_jsx(Button,{icon:"pi pi-info-circle",type:"button",rounded:!0,text:!0,onClick:s,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: ",n," · run: ",r,e?.start_time&&_jsxs(_Fragment,{children:[" · started: ",new Date(e.start_time).toLocaleString()]})]})]}),l&&_jsx(Button,{icon:"pi pi-table",label:"View Raw Data",onClick:o,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 n=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:n(r)}),a&&_jsx("div",{style:{marginTop:"0.25rem"},children:n(a)})]})},DataBlobTable=({blob:e,loading:t,error:r,rawData:a,emptyMessage:n})=>{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:n??r});if(!e||"object"!=typeof e)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:"No data."});const l=Object.keys(a.columns??{}),o=Object.keys(e).filter(t=>Array.isArray(e[t])),s=[];for(const t of l)Array.isArray(e[t])&&s.push(t);for(const e of o)s.includes(e)||s.push(e);if(0===s.length)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:n??"No columnar data in this blob."});const i=s.reduce((t,r)=>Math.min(t,e[r].length),Number.POSITIVE_INFINITY),c=Number.isFinite(i)?i:0,d=Array.from({length:c},(t,r)=>{const a={__i:r};for(const t of s)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:n??"No data.",size:"small",stripedRows:!0,children:[_jsx(Column,{field:"__i",header:"#",style:{width:"5rem",textAlign:"right"},bodyStyle:{fontVariantNumeric:"tabular-nums",textAlign:"right"}}),s.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),CYCLE_VIRTUAL_THRESHOLD=30,CHART_COLORS=["#4ea8de","#f59e0b","#22c55e","#a855f7","#ef4444","#14b8a6","#eab308","#ec4899"],palette=e=>CHART_COLORS[e%CHART_COLORS.length],SG_ORDER=3,SMOOTH_TARGET_POINTS=800;function invertMatrix(e){const t=e.length,r=e.map((e,r)=>[...e,...Array.from({length:t},(e,t)=>r===t?1:0)]);for(let e=0;e<t;e++){let a=e;for(let n=e+1;n<t;n++)Math.abs(r[n][e])>Math.abs(r[a][e])&&(a=n);if(Math.abs(r[a][e])<1e-12)return null;[r[e],r[a]]=[r[a],r[e]];const n=r[e][e];for(let a=0;a<2*t;a++)r[e][a]/=n;for(let a=0;a<t;a++){if(a===e)continue;const n=r[a][e];if(0!==n)for(let l=0;l<2*t;l++)r[a][l]-=n*r[e][l]}}return r.map(e=>e.slice(t))}function sgWeights(e,t){const r=2*e+1,a=Math.min(t,r-1),n=[];for(let t=-e;t<=e;t++){const e=[];let r=1;for(let n=0;n<=a;n++)e.push(r),r*=t;n.push(e)}const l=a+1,o=Array.from({length:l},()=>new Array(l).fill(0));for(let e=0;e<l;e++)for(let t=0;t<l;t++){let a=0;for(let l=0;l<r;l++)a+=n[l][e]*n[l][t];o[e][t]=a}const s=invertMatrix(o);if(!s)return new Array(r).fill(1/r);const i=new Array(r);for(let e=0;e<r;e++){let t=0;for(let r=0;r<l;r++)t+=s[0][r]*n[e][r];i[e]=t}return i}function savitzkyGolay(e,t){const r=e.length;if(0===r||t<1)return e.slice();const a=new Map,n=e=>{let t=a.get(e);return t||(t=sgWeights(e,3),a.set(e,t)),t},l=new Array(r);for(let a=0;a<r;a++){const o=Math.min(t,a,r-1-a);if(o<1){l[a]=e[a];continue}const s=n(o);let i=0;for(let t=-o;t<=o;t++)i+=s[t+o]*e[a+t];l[a]=i}return l}function smoothTraceForDisplay(e,t,r){const a=Math.min(e.length,t.length);if(0===a)return[];const n=r&&r>1?Math.max(1,Math.floor((Math.min(r,a)-1)/2)):Math.max(2,Math.min(40,Math.round(a/250))),l=savitzkyGolay(t.slice(0,a),n),o=Math.max(1,Math.ceil(a/SMOOTH_TARGET_POINTS)),s=[];for(let t=0;t<a;t+=o)s.push({x:e[t],y:l[t]});return(a-1)%o!=0&&s.push({x:e[a-1],y:l[a-1]}),s}const 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));
|
|
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";import{ScienceTable}from"./ScienceTable";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,n=e.runId??t.selection.runId,l=e.schema??(a?t.schemas[a]:void 0),{throttleMs:o=100,cycleTableHeight:s="400px",chartHeight:i="320px"}=e,{invoke:c,subscribe:d,unsubscribe:u}=useContext(EventEmitterContext),[m,p]=useState(null),[y,f]=useState([]),[h,g]=useState({}),[_,x]=useState({}),[b,j]=useState(!1),[v,w]=useState(!1),C=useCallback(async(e,t,l)=>{const o=e?.cycle_index;if(r&&a&&n&&o)try{const e=await c("tis.update_cycle",MessageType.Request,{project_id:r,method_id:a,run_id:n,cycle_index:o,fields:{[t]:l}});if(!1===e?.success)return;f(e=>e.map(e=>e?.cycle_index===o?{...e,[t]:l}:e))}catch(e){}},[c,r,a,n]),S=useRef(null),[T,R]=useState(null),[M,L]=useState(null),[E,I]=useState(!1),[O,A]=useState(null),[D,N]=useState(null),[k,F]=useState(!1),[H,z]=useState([]),[P,B]=useState(null),V=useMemo(()=>{const e=[];for(const[t,r]of Object.entries(l?.views??{}))e.push({name:t,view:r});return e},[l]),[$,G]=useState(V.length>0?V[0].name:null);useEffect(()=>{if(0===V.length)return;null!==$&&V.some(e=>e.name===$)||G(V[0].name)},[V,$]);const W=V.find(e=>e.name===$)?.view,q="raw_trace"===W?.type,U=useRef([]),J=useRef(null),Y=useRef(null),Z=()=>{Y.current||(Y.current=setTimeout(()=>{if(Y.current=null,U.current.length>0){const e=U.current;U.current=[],f(t=>[...e.slice().reverse(),...t])}J.current&&(g(J.current),J.current=null)},o))};useEffect(()=>{if(!r||!a||!n)return p(null),f([]),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:n});!e&&t?.success&&(p(t.data),g(t.data.results??{}));const l=await c("tis.read_cycles",MessageType.Request,{project_id:r,method_id:a,run_id:n,offset:0,limit:200,order:"desc"});!e&&l?.success&&f(l.data.cycles??[])}catch(e){}})(),()=>{e=!0}},[r,a,n,c]),useEffect(()=>{const e=e=>e?.project_id===r&&e?.method_id===a&&e?.run_id===n,t=d("tis.cycle_added",t=>{e(t)&&t.cycle&&(U.current.push(t.cycle),Z())}),l=d("tis.results_updated",t=>{e(t)&&(J.current=t.results??{},Z())});return()=>{u(t),u(l),Y.current&&(clearTimeout(Y.current),Y.current=null)}},[r,a,n,o]);const K="string"==typeof m?.sample_id&&m.sample_id?m.sample_id:"string"==typeof m?.config?.sample_id?m.config.sample_id:"";useEffect(()=>{if(!r||!a||!K)return void x({});let e=!1;(async()=>{try{const t=await c("tis.sample_summary",MessageType.Request,{project_id:r,method_id:a,sample_id:K});!e&&t?.success&&x(t.data?.summary??{})}catch(e){}})();const t=d("tis.sample_summary_updated",e=>{e?.project_id===r&&e?.method_id===a&&e?.sample_id===K&&x(e.summary??{})});return()=>{e=!0,u(t)}},[r,a,K,c,d,u]);const Q=useRawCycleData({projectId:r,methodId:a,runId:n,blobName:l?.raw_data?.blob_name??"trace",source:W?.source??"raw",enabled:q}),X=useMemo(()=>{if(!W)return null;if("cycle_scatter"===W.type){const e=W.x.field;if(!e)return null;const t=[...y].reverse();return{labels:t.map(t=>t[e]),datasets:W.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"===W.type){if(!Q.raw)return null;const e=W.x.column;if(!e)return null;const t=Q.raw[e]??[],r=!1!==W.smooth;return{datasets:W.y.map((e,a)=>{const n=Q.raw[e.column]??[],l=r?smoothTraceForDisplay(t,n,W.smoothWindow):n.map((e,r)=>({x:t[r],y:e}));return{label:e.label??e.column,data:l,yAxisID:"right"===e.y_axis?"y1":"y",borderColor:palette(a),backgroundColor:palette(a),pointRadius:0,borderWidth:1.5,showLine:!0,...r?{cubicInterpolationMode:"monotone"}:{}}})}}return null},[W,y,Q.raw]),ee=W?.y.some(e=>"right"===e.y_axis)??!1,te=useMemo(()=>{if(!q)return[];const e=Q.envelope,t=e?.regions??e?.context?.regions;return Array.isArray(t)?t:[]},[q,Q.envelope]),re=useMemo(()=>[...W?.regions??[],...te],[W,te]),ae=useMemo(()=>{const e="raw_trace"===W?.type;return{responsive:!0,maintainAspectRatio:!1,parsing:!e&&void 0,scales:{x:e?{type:"linear",title:{display:!!W?.x.label,text:W?.x.label}}:{title:{display:!!W?.x.label,text:W?.x.label}},y:{position:"left",title:{display:!0,text:leftAxisLabel(W)}},...ee?{y1:{position:"right",grid:{drawOnChartArea:!1},title:{display:!0,text:rightAxisLabel(W)}}}:{}},plugins:{legend:{display:!0},zoom:{pan:{enabled:!0,mode:"xy"},zoom:{wheel:{enabled:!0},pinch:{enabled:!0},mode:"xy"}},annotation:{annotations:buildRegionAnnotations(re)}}}},[W,ee,re]),ne=l?.raw_data?.blob_name??"trace",le=useRef(""),oe=useCallback(async()=>{if(!r||!a||!n)return[];try{const e=await c("tis.list_raw",MessageType.Request,{project_id:r,method_id:a,run_id:n});if(!e?.success)return[];const t=e.data?.cycles??[];return t.filter(e=>e?.name===ne&&"number"==typeof e?.cycle_index).map(e=>e.cycle_index).sort((e,t)=>e-t)}catch{return[]}},[r,a,n,ne,c]),se=useCallback(async e=>{if(!r||!a||!n)return;const t=`${r}|${a}|${n}|${ne}|${e??"latest"}`;if(le.current===t)return;le.current=t;const l={project_id:r,method_id:a,run_id:n,name:ne};null!=e&&(l.cycle_index=e),I(!0),L(null),R(null);try{const e=await c("tis.read_raw",MessageType.Request,l);e?.success?R(e.data??{}):L(e?.error_message??"No raw data on disk for this run.")}catch(e){L(String(e?.message??e))}finally{I(!1)}F(!0),N(null),A(null);try{const e=await c("tis.read_filtered",MessageType.Request,l);e?.success?A(e.data??{}):N(e?.error_message??"No filtered data on disk for this run.")}catch(e){N(String(e?.message??e))}finally{F(!1)}},[r,a,n,ne,c]);useEffect(()=>{le.current="",z([]),B(null)},[r,a,n,ne]);return useEffect(()=>{b&&null!=P&&se(P)},[b,P,se]),r&&a&&n&&l?_jsxs("div",{className:"vblock",style:{display:"flex",flexDirection:"column",gap:"1rem"},children:[_jsx(Header,{meta:m,config:m?.config,runId:n,projectId:r,methodId:a,canViewRaw:!!l.raw_data,onViewRaw:async()=>{j(!0);let e=H;0===e.length&&(e=await oe(),z(e));const t=e.length>0?e[e.length-1]:null,r=P??t;P!==r&&B(r),await se(r)},onShowConfig:()=>w(!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:$,options:V.map(e=>({label:e.view.title??e.name,value:e.name})),onChange:e=>G(e.value),placeholder:0===V.length?"No view defined":"Select a view",disabled:0===V.length}),_jsx("h3",{style:{margin:0},children:W?.title??""}),q&&Q.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:Q.selectedCycle,options:Q.cycles.map(e=>({label:`Cycle ${e}`,value:e})),onChange:e=>Q.setSelectedCycle(Number(e.value)),style:{minWidth:"8rem"}}),_jsxs("span",{style:{color:"var(--text-secondary-color)"},children:["of ",Q.cycles.length]})]}),_jsx("div",{style:{flex:1}}),_jsx(Button,{icon:"pi pi-th-large",outlined:!0,rounded:!0,size:"small",onClick:()=>S.current?.resetZoom?.(),disabled:!X,tooltip:"Reset chart zoom",tooltipOptions:{position:"left"},"aria-label":"Reset chart zoom"})]}),_jsxs("div",{style:{height:i,position:"relative"},children:[q&&Q.loading&&_jsx(ChartOverlay,{children:"Loading raw data…"}),q&&Q.error&&_jsx(ChartOverlay,{children:Q.error}),X&&_jsx(Line,{ref:S,data:X,options:ae})]})]}),(()=>{const e=y.length>CYCLE_VIRTUAL_THRESHOLD,t=(l.cycle_fields??[]).map(e=>({field:e.name,label:e.name,units:e.units,editable:e.editable,body:t=>formatCell(t[e.name],e.type,e.scale)}));return _jsx(ScienceTable,{title:`Cycle Data (${y.length})`,columns:t,rows:y,scrollable:e,scrollHeight:e?s:void 0,emptyMessage:"No cycles yet.",onCellEdit:C})})(),(()=>{const e={...h,..._},t=Object.keys(e).length>0,r=(l.results_fields??[]).map(e=>({field:e.name,label:e.name,units:e.units,body:t=>formatCell(t[e.name],e.type,e.scale)}));return _jsx(ScienceTable,{title:"Results",columns:r,rows:t?[e]:[],emptyMessage:"No results yet."})})(),l.raw_data&&_jsxs(Dialog,{visible:b,onHide:()=>j(!1),header:`Run Data — ${n}`,style:{width:"90vw",height:"80vh"},maximizable:!0,children:[_jsx(CyclePickerBar,{cycles:H,selected:P,onChange:B}),_jsx(RawEnvelopeHeader,{envelope:T}),_jsxs(TabView,{style:{height:"100%"},children:[_jsx(TabPanel,{header:"Raw Data",children:_jsx(DataBlobTable,{blob:unwrapEnvelope(T),loading:E,error:M,rawData:l.raw_data})}),_jsx(TabPanel,{header:"Filtered Data",children:_jsx(DataBlobTable,{blob:unwrapEnvelope(O),loading:k,error:D,rawData:l.raw_data,emptyMessage:"Filtered data is written by post-processing — none on disk for this run yet."})})]})]}),_jsx(Dialog,{visible:v,onHide:()=>w(!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:n,canViewRaw:l,onViewRaw:o,onShowConfig:s})=>{const i="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:[i||r,c&&_jsx(Button,{icon:"pi pi-info-circle",type:"button",rounded:!0,text:!0,onClick:s,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: ",n," · run: ",r,e?.start_time&&_jsxs(_Fragment,{children:[" · started: ",new Date(e.start_time).toLocaleString()]})]})]}),l&&_jsx(Button,{icon:"pi pi-table",label:"View Raw Data",onClick:o,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 n=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:n(r)}),a&&_jsx("div",{style:{marginTop:"0.25rem"},children:n(a)})]})},DataBlobTable=({blob:e,loading:t,error:r,rawData:a,emptyMessage:n})=>{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:n??r});if(!e||"object"!=typeof e)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:"No data."});const l=Object.keys(a.columns??{}),o=Object.keys(e).filter(t=>Array.isArray(e[t])),s=[];for(const t of l)Array.isArray(e[t])&&s.push(t);for(const e of o)s.includes(e)||s.push(e);if(0===s.length)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:n??"No columnar data in this blob."});const i=s.reduce((t,r)=>Math.min(t,e[r].length),Number.POSITIVE_INFINITY),c=Number.isFinite(i)?i:0,d=Array.from({length:c},(t,r)=>{const a={__i:r};for(const t of s)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:n??"No data.",size:"small",stripedRows:!0,children:[_jsx(Column,{field:"__i",header:"#",style:{width:"5rem",textAlign:"right"},bodyStyle:{fontVariantNumeric:"tabular-nums",textAlign:"right"}}),s.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),CYCLE_VIRTUAL_THRESHOLD=30,CHART_COLORS=["#4ea8de","#f59e0b","#22c55e","#a855f7","#ef4444","#14b8a6","#eab308","#ec4899"],palette=e=>CHART_COLORS[e%CHART_COLORS.length],SG_ORDER=3,SMOOTH_TARGET_POINTS=800;function invertMatrix(e){const t=e.length,r=e.map((e,r)=>[...e,...Array.from({length:t},(e,t)=>r===t?1:0)]);for(let e=0;e<t;e++){let a=e;for(let n=e+1;n<t;n++)Math.abs(r[n][e])>Math.abs(r[a][e])&&(a=n);if(Math.abs(r[a][e])<1e-12)return null;[r[e],r[a]]=[r[a],r[e]];const n=r[e][e];for(let a=0;a<2*t;a++)r[e][a]/=n;for(let a=0;a<t;a++){if(a===e)continue;const n=r[a][e];if(0!==n)for(let l=0;l<2*t;l++)r[a][l]-=n*r[e][l]}}return r.map(e=>e.slice(t))}function sgWeights(e,t){const r=2*e+1,a=Math.min(t,r-1),n=[];for(let t=-e;t<=e;t++){const e=[];let r=1;for(let n=0;n<=a;n++)e.push(r),r*=t;n.push(e)}const l=a+1,o=Array.from({length:l},()=>new Array(l).fill(0));for(let e=0;e<l;e++)for(let t=0;t<l;t++){let a=0;for(let l=0;l<r;l++)a+=n[l][e]*n[l][t];o[e][t]=a}const s=invertMatrix(o);if(!s)return new Array(r).fill(1/r);const i=new Array(r);for(let e=0;e<r;e++){let t=0;for(let r=0;r<l;r++)t+=s[0][r]*n[e][r];i[e]=t}return i}function savitzkyGolay(e,t){const r=e.length;if(0===r||t<1)return e.slice();const a=new Map,n=e=>{let t=a.get(e);return t||(t=sgWeights(e,3),a.set(e,t)),t},l=new Array(r);for(let a=0;a<r;a++){const o=Math.min(t,a,r-1-a);if(o<1){l[a]=e[a];continue}const s=n(o);let i=0;for(let t=-o;t<=o;t++)i+=s[t+o]*e[a+t];l[a]=i}return l}function smoothTraceForDisplay(e,t,r){const a=Math.min(e.length,t.length);if(0===a)return[];const n=r&&r>1?Math.max(1,Math.floor((Math.min(r,a)-1)/2)):Math.max(2,Math.min(40,Math.round(a/250))),l=savitzkyGolay(t.slice(0,a),n),o=Math.max(1,Math.ceil(a/SMOOTH_TARGET_POINTS)),s=[];for(let t=0;t<a;t+=o)s.push({x:e[t],y:l[t]});return(a-1)%o!=0&&s.push({x:e[a-1],y:l[a-1]}),s}const 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));
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Action bar: New / Duplicate / Delete / Apply / Save / Revert.
|
|
8
8
|
*
|
|
9
9
|
* "Apply" pushes the local Monaco buffer to the server-side stage
|
|
10
|
-
* (`tis.put_method`). "Save" persists the entire stage to
|
|
10
|
+
* (`tis.put_method`). "Save" persists the entire stage to test_methods.json
|
|
11
11
|
* (`tis.save_config`). "Revert" drops the stage (`tis.discard_config_changes`).
|
|
12
12
|
*
|
|
13
13
|
* Save is disabled (with a tooltip) when active tests are open against
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SaveDiffDialog.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/SaveDiffDialog.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACtC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAQD,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,
|
|
1
|
+
{"version":3,"file":"SaveDiffDialog.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/SaveDiffDialog.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACtC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAQD,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAiGxD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useEffect,useState}from"react";import{Dialog}from"primereact/dialog";import{Button}from"primereact/button";export const SaveDiffDialog=({visible:e,staged:i,invoker:
|
|
1
|
+
import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useEffect,useState}from"react";import{Dialog}from"primereact/dialog";import{Button}from"primereact/button";export const SaveDiffDialog=({visible:e,staged:i,invoker:t,onConfirm:s,onCancel:r})=>{const[d,o]=useState(null),[n,l]=useState(null),[a,c]=useState(!1);useEffect(()=>{if(!e)return;let s=!1;return(async()=>{l(null);try{const e=await t("tis.list_schemas",{});if(s)return;if(!e.success)return void l(e.error_message??"tis.list_schemas failed");const r=e.data?.test_methods??{};o(buildDiff(r,i))}catch(e){l(String(e?.message??e))}})(),()=>{s=!0}},[e,i,t]);return _jsxs(Dialog,{header:"Save Test Methods to Disk",visible:e,onHide:r,style:{width:"60rem",maxWidth:"90vw"},children:[n&&_jsx("div",{style:{color:"#dc2626",marginBottom:"0.5rem"},children:n}),!d&&!n&&_jsx("small",{children:"Computing diff…"}),d&&_jsxs("div",{children:[_jsxs("p",{children:["About to save the test methods to"," ",_jsx("code",{children:"test_methods.json"}),". A backup of the previous file will be written to"," ",_jsx("code",{children:"test_methods.json.bak"}),"."]}),0===d.added.length&&0===d.removed.length&&0===d.modified.length&&_jsx("p",{children:_jsx("em",{children:"No changes detected."})}),d.added.length>0&&_jsxs("p",{children:[_jsx("strong",{children:"Added:"})," ",d.added.join(", ")]}),d.removed.length>0&&_jsxs("p",{children:[_jsx("strong",{children:"Removed:"})," ",d.removed.join(", ")]}),d.modified.length>0&&_jsxs(_Fragment,{children:[_jsxs("p",{children:[_jsx("strong",{children:"Modified:"})," ",d.modified.map(e=>e.id).join(", ")]}),_jsxs("details",{children:[_jsx("summary",{children:"Show before / after"}),d.modified.map(e=>_jsxs("div",{style:{marginTop:"0.75rem"},children:[_jsx("strong",{children:e.id}),_jsxs("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"0.5rem",marginTop:"0.25rem"},children:[_jsx("pre",{style:preStyle("before"),children:JSON.stringify(e.before,null,2)}),_jsx("pre",{style:preStyle("after"),children:JSON.stringify(e.after,null,2)})]})]},e.id))]})]})]}),_jsxs("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.5rem",marginTop:"1rem"},children:[_jsx(Button,{label:"Cancel",className:"p-button-text",onClick:r,disabled:a}),_jsx(Button,{label:"Save",icon:"pi pi-save",disabled:a||!d,onClick:async()=>{c(!0);try{await s()}finally{c(!1)}}})]})]})};function preStyle(e){return{background:"before"===e?"#fef2f2":"#f0fdf4",color:"#0f172a",padding:"0.5rem",fontSize:"0.75rem",margin:0,maxHeight:"20rem",overflow:"auto",border:"1px solid "+("before"===e?"#fecaca":"#bbf7d0"),borderRadius:4}}function buildDiff(e,i){const t={added:[],removed:[],modified:[]},s=new Set([...Object.keys(e),...Object.keys(i)]);for(const r of s){const s=e[r],d=i[r];void 0===s&&void 0!==d?t.added.push(r):void 0!==s&&void 0===d?t.removed.push(r):void 0!==s&&void 0!==d&&JSON.stringify(s)!==JSON.stringify(d)&&t.modified.push({id:r,before:s,after:d})}return t}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useAmsSchemaConfig — load and edit the AMS custom `asset_types` catalog.
|
|
3
|
+
*
|
|
4
|
+
* Thin wrapper around the `ams.show_schema_config` / `ams.put_asset_type` /
|
|
5
|
+
* `ams.remove_asset_type` / `ams.save_schema_config` /
|
|
6
|
+
* `ams.discard_schema_changes` IPC surface. Mirrors the server's
|
|
7
|
+
* staged-then-saved model (and the TIS `useTisConfig` hook exactly): edits
|
|
8
|
+
* land in the AMS module's in-memory buffer (dirty = true) until `save()`
|
|
9
|
+
* persists them to the `asset_management.json` sidecar next to project.json.
|
|
10
|
+
*
|
|
11
|
+
* Only the project's CUSTOM asset types are editable here. The three
|
|
12
|
+
* built-in types (load_cell/linear_encoder/spring) are code-defined and
|
|
13
|
+
* surfaced read-only via `ams.list_schemas` (see `useAmsAssetTypes`).
|
|
14
|
+
*
|
|
15
|
+
* Pass an `invoker` override to mock the IPC layer (playground / tests).
|
|
16
|
+
* When omitted, the hook reads `EventEmitterContext`.
|
|
17
|
+
*/
|
|
18
|
+
import type { TisIpcInvoker } from './useTisConfig';
|
|
19
|
+
import type { AssetTypeDef } from '../components/ams-editor/types';
|
|
20
|
+
export interface AmsSchemaConfig {
|
|
21
|
+
assetTypes: Record<string, AssetTypeDef>;
|
|
22
|
+
dirty: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface UseAmsSchemaConfigResult {
|
|
25
|
+
config: AmsSchemaConfig | null;
|
|
26
|
+
loading: boolean;
|
|
27
|
+
/** Last server-reported error from a mutation. Cleared on next success. */
|
|
28
|
+
error: string | null;
|
|
29
|
+
refresh: () => Promise<void>;
|
|
30
|
+
putType: (typeId: string, def: AssetTypeDef) => Promise<void>;
|
|
31
|
+
removeType: (typeId: string) => Promise<void>;
|
|
32
|
+
save: () => Promise<void>;
|
|
33
|
+
revert: () => Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
export interface UseAmsSchemaConfigOptions {
|
|
36
|
+
invoker?: TisIpcInvoker;
|
|
37
|
+
}
|
|
38
|
+
export declare function useAmsSchemaConfig(options?: UseAmsSchemaConfigOptions): UseAmsSchemaConfigResult;
|
|
39
|
+
//# sourceMappingURL=useAmsSchemaConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAmsSchemaConfig.d.ts","sourceRoot":"","sources":["../../src/hooks/useAmsSchemaConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,MAAM,WAAW,eAAe;IAC5B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACzC,KAAK,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACrC,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,yBAAyB;IACtC,OAAO,CAAC,EAAE,aAAa,CAAC;CAC3B;AAED,wBAAgB,kBAAkB,CAC9B,OAAO,CAAC,EAAE,yBAAyB,GACpC,wBAAwB,CA8F1B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{useCallback,useContext,useEffect,useRef,useState}from"react";import{EventEmitterContext}from"../core/EventEmitterContext";import{MessageType}from"../hub/CommandMessage";export function useAmsSchemaConfig(e){const s=useContext(EventEmitterContext),t=e?.invoker??(async(e,t)=>await s.invoke(e,MessageType.Request,t)),a=useRef(t);a.current=t;const[r,n]=useState(null),[c,o]=useState(!0),[i,u]=useState(null),l=useCallback(async()=>{o(!0);try{const e=await a.current("ams.show_schema_config",{});if(!e.success)return u(e.error_message??"ams.show_schema_config failed"),void n(null);const s=e.data??{};n({assetTypes:s.asset_types??{},dirty:!!s.dirty}),u(null)}catch(e){u(String(e?.message??e)),n(null)}finally{o(!1)}},[]);useEffect(()=>{l()},[l]);const m=useCallback(async(e,s)=>{const t=await a.current("ams.put_asset_type",{type_id:e,definition:s});if(!t.success){const e=t.error_message??"ams.put_asset_type failed";throw u(e),new Error(e)}u(null),await l()},[l]),f=useCallback(async e=>{const s=await a.current("ams.remove_asset_type",{type_id:e});if(!s.success){const e=s.error_message??"ams.remove_asset_type failed";throw u(e),new Error(e)}u(null),await l()},[l]),_=useCallback(async()=>{const e=await a.current("ams.save_schema_config",{});if(!e.success){const s=e.error_message??"ams.save_schema_config failed";throw u(s),new Error(s)}u(null),await l()},[l]),y=useCallback(async()=>{const e=await a.current("ams.discard_schema_changes",{});if(!e.success){const s=e.error_message??"ams.discard_schema_changes failed";throw u(s),new Error(s)}u(null),await l()},[l]);return{config:r,loading:c,error:i,refresh:l,putType:m,removeType:f,save:_,revert:y}}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* `tis.remove_method` / `tis.save_config` / `tis.discard_config_changes`
|
|
6
6
|
* IPC surface. Mirrors the server's staged-then-saved model: edits land in
|
|
7
7
|
* the server's in-memory copy (dirty = true) until `save()` persists them
|
|
8
|
-
* to project.json.
|
|
8
|
+
* to the `test_methods.json` sidecar next to project.json.
|
|
9
9
|
*
|
|
10
10
|
* Today the server hosts exactly one project (the one configured at
|
|
11
11
|
* startup), so `projectId` is informational; the hook still threads it
|
package/package.json
CHANGED
|
@@ -1,118 +1,118 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
2
|
+
"name": "@adcops/autocore-react",
|
|
3
|
+
"version": "3.3.115",
|
|
4
|
+
"description": "A React component library for industrial user interfaces.",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"clean": "rimraf ./dist",
|
|
9
|
+
"minify": "node ./tools/minify.cjs",
|
|
10
|
+
"tsc": "tsc",
|
|
11
|
+
"build-themes": "node ./tools/build-themes.cjs",
|
|
12
|
+
"generate-docs": "typedoc",
|
|
13
|
+
"build": "npm run clean && tsc && node ./tools/copy-distribution-files.cjs && npm run build-themes && npm run minify",
|
|
14
|
+
"build:dev": "npm run clean && tsc && node ./tools/copy-distribution-files.cjs",
|
|
15
|
+
"dev": "vite",
|
|
16
|
+
"playground": "vite",
|
|
17
|
+
"playground:build": "vite build",
|
|
18
|
+
"prestart": "tsc",
|
|
19
|
+
"publish-package": "npm run build && npm version patch && npm publish",
|
|
20
|
+
"prepublishOnly": "npm run build",
|
|
21
|
+
"release:patch": "npm version patch && npm publish",
|
|
22
|
+
"release:minor": "npm version minor && npm publish",
|
|
23
|
+
"release:major": "npm version major && npm publish",
|
|
24
|
+
"convert-assets": "npx @svgr/cli --typescript --out-dir src/assets/ src/assets/svg/ --jsx-runtime automatic --ignore-existing"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://adc360.visualstudio.com/AutoCore-React/_git/autocore-react"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"react",
|
|
32
|
+
"autocore",
|
|
33
|
+
"component-library",
|
|
34
|
+
"industrial-ui",
|
|
35
|
+
"typescript",
|
|
36
|
+
"primereact"
|
|
37
|
+
],
|
|
38
|
+
"author": "Automated Design Corp.",
|
|
39
|
+
"license": "ISC",
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://automateddesign.com"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://automateddesign.com",
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@types/react": "^18",
|
|
46
|
+
"primereact": ">=10.9.7",
|
|
47
|
+
"react": "^18",
|
|
48
|
+
"react-dom": "^18"
|
|
49
|
+
},
|
|
50
|
+
"peerDependenciesMeta": {
|
|
51
|
+
"@types/react": {
|
|
52
|
+
"optional": true
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@monaco-editor/react": "^4.7.0",
|
|
57
|
+
"@tauri-apps/api": "^2.9.1",
|
|
58
|
+
"chart.js": "^4.5.1",
|
|
59
|
+
"chartjs-plugin-annotation": "^3.1.0",
|
|
60
|
+
"chartjs-plugin-zoom": "^2.2.0",
|
|
61
|
+
"clsx": "^2.1.1",
|
|
62
|
+
"numerable": "^0.3.15",
|
|
63
|
+
"react-blockly": "^8.1.2",
|
|
64
|
+
"react-chartjs-2": "^5.3.0",
|
|
65
|
+
"react-number-format": "^5.4.5",
|
|
66
|
+
"react-simple-keyboard": "^3.8.120",
|
|
67
|
+
"react-transition-group": "^4.4.5",
|
|
68
|
+
"sass": "^1.92.1",
|
|
69
|
+
"socket.io-client": "^4.8.1",
|
|
70
|
+
"use-fit-text": "^2.4.0"
|
|
71
|
+
},
|
|
72
|
+
"sideEffects": [
|
|
73
|
+
"**/*.css",
|
|
74
|
+
"**/*.scss"
|
|
75
|
+
],
|
|
76
|
+
"engines": {
|
|
77
|
+
"node": ">=20.0.0"
|
|
78
|
+
},
|
|
79
|
+
"devDependencies": {
|
|
80
|
+
"@svgr/cli": "^8.1.0",
|
|
81
|
+
"@types/react": "^18.2.15",
|
|
82
|
+
"@types/react-dom": "^18.2.7",
|
|
83
|
+
"@types/react-transition-group": "^4.4.12",
|
|
84
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
85
|
+
"primereact": "^10.9.7",
|
|
86
|
+
"rimraf": "^6.0.1",
|
|
87
|
+
"terser": "^5.44.0",
|
|
88
|
+
"typedoc": "^0.28.12",
|
|
89
|
+
"typescript": "^5.9.2",
|
|
90
|
+
"vite": "^8.0.13"
|
|
91
|
+
},
|
|
92
|
+
"exports": {
|
|
93
|
+
"./core/*": {
|
|
94
|
+
"import": "./dist/core/*",
|
|
95
|
+
"types": "./dist/core/*.d.ts"
|
|
49
96
|
},
|
|
50
|
-
"
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
97
|
+
"./components/*": {
|
|
98
|
+
"import": "./dist/components/*",
|
|
99
|
+
"types": "./dist/components/*.d.ts"
|
|
54
100
|
},
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
"chart.js": "^4.5.1",
|
|
59
|
-
"chartjs-plugin-annotation": "^3.1.0",
|
|
60
|
-
"chartjs-plugin-zoom": "^2.2.0",
|
|
61
|
-
"clsx": "^2.1.1",
|
|
62
|
-
"numerable": "^0.3.15",
|
|
63
|
-
"react-blockly": "^8.1.2",
|
|
64
|
-
"react-chartjs-2": "^5.3.0",
|
|
65
|
-
"react-number-format": "^5.4.5",
|
|
66
|
-
"react-simple-keyboard": "^3.8.120",
|
|
67
|
-
"react-transition-group": "^4.4.5",
|
|
68
|
-
"sass": "^1.92.1",
|
|
69
|
-
"socket.io-client": "^4.8.1",
|
|
70
|
-
"use-fit-text": "^2.4.0"
|
|
101
|
+
"./assets/*": {
|
|
102
|
+
"import": "./dist/assets/*",
|
|
103
|
+
"types": "./dist/assets/*.d.ts"
|
|
71
104
|
},
|
|
72
|
-
"
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
],
|
|
76
|
-
"engines": {
|
|
77
|
-
"node": ">=20.0.0"
|
|
105
|
+
"./hooks/*": {
|
|
106
|
+
"import": "./dist/hooks/*",
|
|
107
|
+
"types": "./dist/hooks/*.d.ts"
|
|
78
108
|
},
|
|
79
|
-
"
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
"@types/react-dom": "^18.2.7",
|
|
83
|
-
"@types/react-transition-group": "^4.4.12",
|
|
84
|
-
"@vitejs/plugin-react": "^6.0.2",
|
|
85
|
-
"primereact": "^10.9.7",
|
|
86
|
-
"rimraf": "^6.0.1",
|
|
87
|
-
"terser": "^5.44.0",
|
|
88
|
-
"typedoc": "^0.28.12",
|
|
89
|
-
"typescript": "^5.9.2",
|
|
90
|
-
"vite": "^8.0.13"
|
|
109
|
+
"./themes/*": {
|
|
110
|
+
"import": "./dist/themes/*",
|
|
111
|
+
"types": "./dist/themes/*.d.ts"
|
|
91
112
|
},
|
|
92
|
-
"
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
"types": "./dist/core/*.d.ts"
|
|
96
|
-
},
|
|
97
|
-
"./components/*": {
|
|
98
|
-
"import": "./dist/components/*",
|
|
99
|
-
"types": "./dist/components/*.d.ts"
|
|
100
|
-
},
|
|
101
|
-
"./assets/*": {
|
|
102
|
-
"import": "./dist/assets/*",
|
|
103
|
-
"types": "./dist/assets/*.d.ts"
|
|
104
|
-
},
|
|
105
|
-
"./hooks/*": {
|
|
106
|
-
"import": "./dist/hooks/*",
|
|
107
|
-
"types": "./dist/hooks/*.d.ts"
|
|
108
|
-
},
|
|
109
|
-
"./themes/*": {
|
|
110
|
-
"import": "./dist/themes/*",
|
|
111
|
-
"types": "./dist/themes/*.d.ts"
|
|
112
|
-
},
|
|
113
|
-
"./hub/*": {
|
|
114
|
-
"import": "./dist/hub/*",
|
|
115
|
-
"types": "./dist/hub/*.d.ts"
|
|
116
|
-
}
|
|
113
|
+
"./hub/*": {
|
|
114
|
+
"import": "./dist/hub/*",
|
|
115
|
+
"types": "./dist/hub/*.d.ts"
|
|
117
116
|
}
|
|
117
|
+
}
|
|
118
118
|
}
|
|
@@ -153,24 +153,28 @@ export const AmsProvider: React.FC<AmsProviderProps> = ({ children }) => {
|
|
|
153
153
|
}, []);
|
|
154
154
|
|
|
155
155
|
// -----------------------------------------------------------------
|
|
156
|
+
// Re-fetch the schema catalog. Called once on mount and again whenever
|
|
157
|
+
// the asset-TYPE editor broadcasts `ams.schema_changed` after a save, so
|
|
158
|
+
// open pickers/forms pick up new or edited types without a reload.
|
|
159
|
+
const loadSchemas = useCallback(async () => {
|
|
160
|
+
try {
|
|
161
|
+
const resp: any = await invoke('ams.list_schemas' as any, MessageType.Request, {} as any);
|
|
162
|
+
if (resp?.success && resp.data) {
|
|
163
|
+
setSchemas((resp.data.asset_types ?? {}) as AmsSchemaRegistry);
|
|
164
|
+
setSchemasLoaded(true);
|
|
165
|
+
} else {
|
|
166
|
+
console.warn('[AmsProvider] ams.list_schemas failed:', resp?.error_message);
|
|
167
|
+
}
|
|
168
|
+
} catch (e) {
|
|
169
|
+
console.error('[AmsProvider] ams.list_schemas threw:', e);
|
|
170
|
+
}
|
|
171
|
+
}, [invoke]);
|
|
172
|
+
|
|
156
173
|
// Schema + role load — once on mount.
|
|
157
174
|
// -----------------------------------------------------------------
|
|
158
175
|
useEffect(() => {
|
|
159
176
|
let cancelled = false;
|
|
160
|
-
|
|
161
|
-
try {
|
|
162
|
-
const resp: any = await invoke('ams.list_schemas' as any, MessageType.Request, {} as any);
|
|
163
|
-
if (cancelled) return;
|
|
164
|
-
if (resp?.success && resp.data) {
|
|
165
|
-
setSchemas((resp.data.asset_types ?? {}) as AmsSchemaRegistry);
|
|
166
|
-
setSchemasLoaded(true);
|
|
167
|
-
} else {
|
|
168
|
-
console.warn('[AmsProvider] ams.list_schemas failed:', resp?.error_message);
|
|
169
|
-
}
|
|
170
|
-
} catch (e) {
|
|
171
|
-
console.error('[AmsProvider] ams.list_schemas threw:', e);
|
|
172
|
-
}
|
|
173
|
-
})();
|
|
177
|
+
void loadSchemas();
|
|
174
178
|
(async () => {
|
|
175
179
|
try {
|
|
176
180
|
const resp: any = await invoke('ams.list_roles' as any, MessageType.Request, {} as any);
|
|
@@ -214,12 +218,13 @@ export const AmsProvider: React.FC<AmsProviderProps> = ({ children }) => {
|
|
|
214
218
|
subscribe('ams.asset_changed', () => { refreshAssets(); }),
|
|
215
219
|
subscribe('ams.calibration_added', () => { refreshAssets(); }),
|
|
216
220
|
subscribe('ams.calibration_updated', () => { refreshAssets(); }),
|
|
221
|
+
subscribe('ams.schema_changed', () => { loadSchemas(); }),
|
|
217
222
|
subscribe('ams.asset_count', (v: any) => setAlerts(a => ({ ...a, assetCount: Number(v) || 0 }))),
|
|
218
223
|
subscribe('ams.alert_calibration_overdue', (v: any) => setAlerts(a => ({ ...a, calibrationOverdue: Number(v) || 0 }))),
|
|
219
224
|
subscribe('ams.alert_lane_unavailable', (v: any) => setAlerts(a => ({ ...a, laneUnavailable: Number(v) || 0 }))),
|
|
220
225
|
];
|
|
221
226
|
return () => { subs.forEach(unsubscribe); };
|
|
222
|
-
}, [subscribe, unsubscribe, refreshAssets]);
|
|
227
|
+
}, [subscribe, unsubscribe, refreshAssets, loadSchemas]);
|
|
223
228
|
|
|
224
229
|
// -----------------------------------------------------------------
|
|
225
230
|
// RPC helpers
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AssetFieldArray — add/remove/reorder editor for a plain `TestField[]`.
|
|
3
|
+
*
|
|
4
|
+
* A decoupled sibling of the TIS `FieldArrayEditor` (which is bound to a
|
|
5
|
+
* `TestMethod` keyed by `FieldArrayKey`). Asset types carry bare field
|
|
6
|
+
* lists (`fields`, `calibration_fields`, and the per-key lists inside a
|
|
7
|
+
* keyed `sub_locations`), so this operates directly on an array + onChange.
|
|
8
|
+
*
|
|
9
|
+
* Reuses the TIS `TestFieldDialog` for the per-field modal — asset fields
|
|
10
|
+
* have no cross-test aggregate concept, so `isResultsField` is left false.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { useState } from 'react';
|
|
14
|
+
import { DataTable } from 'primereact/datatable';
|
|
15
|
+
import { Column } from 'primereact/column';
|
|
16
|
+
import { Button } from 'primereact/button';
|
|
17
|
+
import { FormSection } from '../forms/FormSection';
|
|
18
|
+
import { TestFieldDialog } from '../tis-editor/editor/TestFieldDialog';
|
|
19
|
+
import type { TestField } from '../tis-editor/types';
|
|
20
|
+
|
|
21
|
+
export interface AssetFieldArrayProps {
|
|
22
|
+
title: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
fields: TestField[];
|
|
25
|
+
onChange: (next: TestField[]) => void;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const AssetFieldArray: React.FC<AssetFieldArrayProps> = ({
|
|
29
|
+
title, description, fields, onChange,
|
|
30
|
+
}) => {
|
|
31
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
32
|
+
const [editingIdx, setEditingIdx] = useState<number | null>(null);
|
|
33
|
+
|
|
34
|
+
const handleAdd = () => { setEditingIdx(null); setDialogOpen(true); };
|
|
35
|
+
const handleEdit = (idx: number) => { setEditingIdx(idx); setDialogOpen(true); };
|
|
36
|
+
|
|
37
|
+
const handleSaveField = (f: TestField) => {
|
|
38
|
+
const next = [...fields];
|
|
39
|
+
if (editingIdx === null) next.push(f);
|
|
40
|
+
else next[editingIdx] = f;
|
|
41
|
+
onChange(next);
|
|
42
|
+
setDialogOpen(false);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const handleRemove = (idx: number) => {
|
|
46
|
+
const target = fields[idx];
|
|
47
|
+
if (!target) return;
|
|
48
|
+
if (!window.confirm(`Remove field "${target.name}"?`)) return;
|
|
49
|
+
onChange(fields.filter((_, i) => i !== idx));
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const handleMove = (idx: number, dir: -1 | 1) => {
|
|
53
|
+
const j = idx + dir;
|
|
54
|
+
if (j < 0 || j >= fields.length) return;
|
|
55
|
+
const next = [...fields];
|
|
56
|
+
[next[idx], next[j]] = [next[j], next[idx]];
|
|
57
|
+
onChange(next);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const rowActions = (_row: TestField, opts: { rowIndex: number }) => (
|
|
61
|
+
<div style={{ display: 'flex', gap: '0.25rem' }}>
|
|
62
|
+
<Button icon="pi pi-arrow-up" className="p-button-text p-button-sm"
|
|
63
|
+
disabled={opts.rowIndex === 0}
|
|
64
|
+
onClick={() => handleMove(opts.rowIndex, -1)} aria-label="Move up" />
|
|
65
|
+
<Button icon="pi pi-arrow-down" className="p-button-text p-button-sm"
|
|
66
|
+
disabled={opts.rowIndex === fields.length - 1}
|
|
67
|
+
onClick={() => handleMove(opts.rowIndex, 1)} aria-label="Move down" />
|
|
68
|
+
<Button icon="pi pi-pencil" className="p-button-text p-button-sm"
|
|
69
|
+
onClick={() => handleEdit(opts.rowIndex)} aria-label="Edit" />
|
|
70
|
+
<Button icon="pi pi-trash" className="p-button-text p-button-danger p-button-sm"
|
|
71
|
+
onClick={() => handleRemove(opts.rowIndex)} aria-label="Remove" />
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<>
|
|
77
|
+
<FormSection
|
|
78
|
+
title={title}
|
|
79
|
+
description={description}
|
|
80
|
+
actions={<Button label="Add field" icon="pi pi-plus" size="small" onClick={handleAdd} />}
|
|
81
|
+
>
|
|
82
|
+
<DataTable value={fields} dataKey="name" emptyMessage="No fields defined.">
|
|
83
|
+
<Column field="name" header="Name" />
|
|
84
|
+
<Column field="type" header="Type" style={{ width: '6rem' }} />
|
|
85
|
+
<Column field="units" header="Units" style={{ width: '6rem' }} />
|
|
86
|
+
<Column header="Req" body={(r: TestField) => r.required ? '✓' : ''} style={{ width: '4rem' }} />
|
|
87
|
+
<Column field="label" header="Label" />
|
|
88
|
+
<Column header="" body={rowActions} style={{ width: '9rem' }} />
|
|
89
|
+
</DataTable>
|
|
90
|
+
</FormSection>
|
|
91
|
+
<TestFieldDialog
|
|
92
|
+
visible={dialogOpen}
|
|
93
|
+
initial={editingIdx !== null ? (fields[editingIdx] ?? null) : null}
|
|
94
|
+
siblingNames={fields.map(f => f.name)}
|
|
95
|
+
isResultsField={false}
|
|
96
|
+
onCancel={() => setDialogOpen(false)}
|
|
97
|
+
onSave={handleSaveField}
|
|
98
|
+
/>
|
|
99
|
+
</>
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export default AssetFieldArray;
|