@adcops/autocore-react 3.3.77 → 3.3.82
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/Indicator.d.ts.map +1 -1
- package/dist/components/Indicator.js +1 -1
- package/dist/components/ams/AmsProvider.d.ts.map +1 -1
- package/dist/components/ams/AmsProvider.js +1 -1
- package/dist/components/ams/AssetDetailView.d.ts.map +1 -1
- package/dist/components/ams/AssetDetailView.js +1 -1
- package/dist/components/ams/CalibrationEntryDialog.d.ts +15 -0
- package/dist/components/ams/CalibrationEntryDialog.d.ts.map +1 -1
- package/dist/components/ams/CalibrationEntryDialog.js +1 -1
- package/dist/components/tis/ProjectManager.d.ts.map +1 -1
- package/dist/components/tis/ProjectManager.js +1 -1
- package/dist/components/tis/TestSetupForm.d.ts.map +1 -1
- package/dist/components/tis/TestSetupForm.js +1 -1
- package/dist/components/tis/TisProvider.d.ts +24 -0
- package/dist/components/tis/TisProvider.d.ts.map +1 -1
- package/dist/components/tis/TisProvider.js +1 -1
- package/package.json +1 -1
- package/src/components/Indicator.tsx +17 -6
- package/src/components/ams/AmsProvider.tsx +4 -3
- package/src/components/ams/AssetDetailView.tsx +24 -2
- package/src/components/ams/CalibrationEntryDialog.tsx +75 -13
- package/src/components/tis/ProjectManager.tsx +3 -2
- package/src/components/tis/TestSetupForm.tsx +25 -9
- package/src/components/tis/TisProvider.tsx +33 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Indicator.d.ts","sourceRoot":"","sources":["../../src/components/Indicator.tsx"],"names":[],"mappings":"AAmCA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAG/D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B,MAAM,WAAW,cAAc;IAC3B;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8FAA8F;IAC9F,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,gDAAgD;IAChD,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAE5B;;;;OAIG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAExB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC9B;AAiBD,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,
|
|
1
|
+
{"version":3,"file":"Indicator.d.ts","sourceRoot":"","sources":["../../src/components/Indicator.tsx"],"names":[],"mappings":"AAmCA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAG/D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B,MAAM,WAAW,cAAc;IAC3B;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8FAA8F;IAC9F,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,gDAAgD;IAChD,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;IAE5B;;;;OAIG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAExB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC9B;AAiBD,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CAyG9C,CAAC;AAEF,eAAe,SAAS,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import React,{useContext,useEffect,useState}from"react";import clsx from"clsx";import{EventEmitterContext}from"../core/EventEmitterContext";import{IndicatorColor}from"../core/IndicatorColor";export{IndicatorColor};const pickColor=(o,t,r,e)=>!0===o?t:!1===o?r:e;export const Indicator=({value:o,onColor:t=IndicatorColor.IndicatorGreen,offColor:r=IndicatorColor.IndicatorOff,invalidColor:e=IndicatorColor.IndicatorInvalid,topic:n,className:i,style:s,label:a,children:c})=>{const{subscribe:l,unsubscribe:d}=useContext(EventEmitterContext),[
|
|
1
|
+
import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import React,{useContext,useEffect,useState}from"react";import clsx from"clsx";import{EventEmitterContext}from"../core/EventEmitterContext";import{IndicatorColor}from"../core/IndicatorColor";export{IndicatorColor};const pickColor=(o,t,r,e)=>!0===o?t:!1===o?r:e;export const Indicator=({value:o,onColor:t=IndicatorColor.IndicatorGreen,offColor:r=IndicatorColor.IndicatorOff,invalidColor:e=IndicatorColor.IndicatorInvalid,topic:n,className:i,style:s,label:a,children:c})=>{const{subscribe:l,unsubscribe:d}=useContext(EventEmitterContext),[x,p]=useState(void 0);useEffect(()=>{if(!n||!l)return;const o=l(n,o=>{p(null==o?void 0:!!o)});return()=>{try{d&&void 0!==o&&d(o)}catch{}}},[n,l,d]);const u=pickColor(void 0!==o?o:x,t,r,e);if(null!=c){const o={backgroundColor:u,color:"white",display:"inline-flex",alignItems:"center",justifyContent:"center",borderRadius:0,boxShadow:"inset 2px 2px 3px rgba(0, 0, 0, 0.55), inset -1px -1px 2px rgba(255, 255, 255, 0.20)",boxSizing:"border-box",...s};return _jsx("span",{className:clsx("indicator",i),style:o,role:"status","aria-label":"string"==typeof a?a:void 0,children:c})}const m={alignItems:"center",width:"auto",flex:"0 0 auto",...s},f={backgroundColor:u,width:"22px",borderRadius:0,boxShadow:"inset 1px 1px 2px rgba(0, 0, 0, 0.5), inset -1px -1px 1px rgba(255, 255, 255, 0.18)"};return _jsxs("div",{className:clsx("p-inputgroup indicator",i),style:m,role:"status",children:[_jsx("span",{className:"p-inputgroup-addon",style:f,children:" "}),void 0!==a&&_jsx("span",{className:"p-inputgroup-addon",children:a})]})};export default Indicator;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AmsProvider.d.ts","sourceRoot":"","sources":["../../../src/components/ams/AmsProvider.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,EAOV,KAAK,SAAS,EACjB,MAAM,OAAO,CAAC;AAQf,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC;AAChC,MAAM,MAAM,iBAAiB,GAAG;IAAE,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,CAAA;CAAE,CAAC;AAEvE;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACpB,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,2EAA2E;IAC3E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;;;oCAKgC;IAChC,eAAe,EAAE,MAAM,EAAE,CAAC;CAC7B;AACD,MAAM,MAAM,eAAe,GAAG;IAAE,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,EAAE,CAAA;CAAE,CAAC;AAEjE,MAAM,WAAW,SAAS;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,iBAAiB,CAAC;IACjD,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;CACzC;AAED,MAAM,WAAW,YAAY;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC5B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,KAAK,EAAE,eAAe,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IACpD,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACzD,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IACzE,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IACpD,SAAS,EAAE,YAAY,CAAC;IACxB,YAAY,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;CACxD;AA0BD,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,EAAE,SAAS,CAAC;CACvB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,
|
|
1
|
+
{"version":3,"file":"AmsProvider.d.ts","sourceRoot":"","sources":["../../../src/components/ams/AmsProvider.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,EAOV,KAAK,SAAS,EACjB,MAAM,OAAO,CAAC;AAQf,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC;AAChC,MAAM,MAAM,iBAAiB,GAAG;IAAE,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,CAAA;CAAE,CAAC;AAEvE;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACpB,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,2EAA2E;IAC3E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;;;oCAKgC;IAChC,eAAe,EAAE,MAAM,EAAE,CAAC;CAC7B;AACD,MAAM,MAAM,eAAe,GAAG;IAAE,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,EAAE,CAAA;CAAE,CAAC;AAEjE,MAAM,WAAW,SAAS;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,iBAAiB,CAAC;IACjD,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;CACzC;AAED,MAAM,WAAW,YAAY;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC5B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,KAAK,EAAE,eAAe,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IACpD,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACzD,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IACzE,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IACpD,SAAS,EAAE,YAAY,CAAC;IACxB,YAAY,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;CACxD;AA0BD,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,EAAE,SAAS,CAAC;CACvB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAoIlD,CAAC;AAMF,eAAO,MAAM,MAAM,uBAAwC,CAAC;AAC5D,eAAO,MAAM,aAAa,yBAAyC,CAAC;AACpE,eAAO,MAAM,WAAW,uBAAyC,CAAC;AAClE,eAAO,MAAM,YAAY,iBAAyC,CAAC;AACnE,eAAO,MAAM,YAAY,uBAAyC,CAAC;AACnE,eAAO,MAAM,eAAe,wCA9KF,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAiLvD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as _jsx}from"react/jsx-runtime";import React,{createContext,useCallback,useContext,useEffect,useMemo,useState}from"react";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";const EMPTY_ALERTS={assetCount:0,calibrationOverdue:0,laneUnavailable:0},AmsContext=createContext({schemas:{},schemasLoaded:!1,roles:{},rolesLoaded:!1,alerts:EMPTY_ALERTS,assets:[],refreshAssets:async()=>{},readAsset:async()=>null,listCalibrations:async()=>[],readCalibration:async()=>null,readUsage:async()=>null,selection:{assetType:null,assetId:null},setSelection:()=>{}});export const AmsProvider=({children:e})=>{const{invoke:s,subscribe:t,unsubscribe:a}=useContext(EventEmitterContext),[n,r]=useState({}),[c,o]=useState(!1),[u,l]=useState({}),[i,m]=useState(!1),[d,C]=useState(EMPTY_ALERTS),[x,y]=useState([]),[b,_]=useState({assetType:null,assetId:null}),A=useCallback(e=>{_(s=>({...s,...e}))},[]);useEffect(()=>{let e=!1;return(async()=>{try{const t=await s("ams.list_schemas",MessageType.Request,{});if(e)return;t?.success&&t.data&&(r(t.data.asset_types??{}),o(!0))}catch(e){}})(),(async()=>{try{const t=await s("ams.list_roles",MessageType.Request,{});if(e)return;t?.success&&t.data?(l(t.data.roles??{}),m(!0)):m(!0)}catch(e){m(!0)}})(),()=>{e=!0}},[]);const p=useCallback(async()=>{try{const e=await s("ams.list_assets",MessageType.Request,{include_retired:!0});e?.success&&y(e.data.assets??[])}catch(e){}},[s]);useEffect(()=>{p()},[p]),useEffect(()=>{const e=[t("ams.asset_changed",()=>{p()}),t("ams.calibration_added",()=>{p()}),t("ams.asset_count",e=>C(s=>({...s,assetCount:Number(e)||0}))),t("ams.alert_calibration_overdue",e=>C(s=>({...s,calibrationOverdue:Number(e)||0}))),t("ams.alert_lane_unavailable",e=>C(s=>({...s,laneUnavailable:Number(e)||0})))];return()=>{e.forEach(a)}},[t,a,p]);const h=useCallback(async e=>{try{const t=await s("ams.read_asset",MessageType.Request,{asset_id:e});return t?.success?t.data:null}catch{return null}},[s]),f=useCallback(async e=>{try{const t=await s("ams.list_calibrations",MessageType.Request,{asset_id:e});return t?.success?t.data.cal_ids??[]:[]}catch{return[]}},[s]),E=useCallback(async(e,t)=>{try{const a=await s("ams.read_calibration",MessageType.Request,{asset_id:e,cal_id:t});return a?.success?a.data:null}catch{return null}},[s]),S=useCallback(async e=>{try{const t=await s("ams.read_usage",MessageType.Request,{asset_id:e});return t?.success?t.data:null}catch{return null}},[s]),T=useMemo(()=>({schemas:n,schemasLoaded:c,roles:u,rolesLoaded:i,alerts:d,assets:x,refreshAssets:p,readAsset:h,listCalibrations:f,readCalibration:E,readUsage:S,selection:b,setSelection:A}),[n,c,u,i,d,x,p,h,f,E,S,b,A]);return _jsx(AmsContext.Provider,{value:T,children:e})};export const useAms=()=>useContext(AmsContext);export const useAmsSchemas=()=>useContext(AmsContext).schemas;export const useAmsRoles=()=>useContext(AmsContext).roles;export const useAmsAlerts=()=>useContext(AmsContext).alerts;export const useAmsAssets=()=>useContext(AmsContext).assets;export const useAmsSelection=()=>{const e=useContext(AmsContext);return[e.selection,e.setSelection]};
|
|
1
|
+
import{jsx as _jsx}from"react/jsx-runtime";import React,{createContext,useCallback,useContext,useEffect,useMemo,useState}from"react";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";const EMPTY_ALERTS={assetCount:0,calibrationOverdue:0,laneUnavailable:0},AmsContext=createContext({schemas:{},schemasLoaded:!1,roles:{},rolesLoaded:!1,alerts:EMPTY_ALERTS,assets:[],refreshAssets:async()=>{},readAsset:async()=>null,listCalibrations:async()=>[],readCalibration:async()=>null,readUsage:async()=>null,selection:{assetType:null,assetId:null},setSelection:()=>{}});export const AmsProvider=({children:e})=>{const{invoke:s,subscribe:t,unsubscribe:a}=useContext(EventEmitterContext),[n,r]=useState({}),[c,o]=useState(!1),[u,l]=useState({}),[i,m]=useState(!1),[d,C]=useState(EMPTY_ALERTS),[x,y]=useState([]),[b,_]=useState({assetType:null,assetId:null}),A=useCallback(e=>{_(s=>({...s,...e}))},[]);useEffect(()=>{let e=!1;return(async()=>{try{const t=await s("ams.list_schemas",MessageType.Request,{});if(e)return;t?.success&&t.data&&(r(t.data.asset_types??{}),o(!0))}catch(e){}})(),(async()=>{try{const t=await s("ams.list_roles",MessageType.Request,{});if(e)return;t?.success&&t.data?(l(t.data.roles??{}),m(!0)):m(!0)}catch(e){m(!0)}})(),()=>{e=!0}},[]);const p=useCallback(async()=>{try{const e=await s("ams.list_assets",MessageType.Request,{include_retired:!0});e?.success&&y(e.data.assets??[])}catch(e){}},[s]);useEffect(()=>{p()},[p]),useEffect(()=>{const e=[t("ams.asset_changed",()=>{p()}),t("ams.calibration_added",()=>{p()}),t("ams.calibration_updated",()=>{p()}),t("ams.asset_count",e=>C(s=>({...s,assetCount:Number(e)||0}))),t("ams.alert_calibration_overdue",e=>C(s=>({...s,calibrationOverdue:Number(e)||0}))),t("ams.alert_lane_unavailable",e=>C(s=>({...s,laneUnavailable:Number(e)||0})))];return()=>{e.forEach(a)}},[t,a,p]);const h=useCallback(async e=>{try{const t=await s("ams.read_asset",MessageType.Request,{asset_id:e});return t?.success?t.data:null}catch{return null}},[s]),f=useCallback(async e=>{try{const t=await s("ams.list_calibrations",MessageType.Request,{asset_id:e});return t?.success?t.data.cal_ids??[]:[]}catch{return[]}},[s]),E=useCallback(async(e,t)=>{try{const a=await s("ams.read_calibration",MessageType.Request,{asset_id:e,cal_id:t});return a?.success?a.data:null}catch{return null}},[s]),S=useCallback(async e=>{try{const t=await s("ams.read_usage",MessageType.Request,{asset_id:e});return t?.success?t.data:null}catch{return null}},[s]),T=useMemo(()=>({schemas:n,schemasLoaded:c,roles:u,rolesLoaded:i,alerts:d,assets:x,refreshAssets:p,readAsset:h,listCalibrations:f,readCalibration:E,readUsage:S,selection:b,setSelection:A}),[n,c,u,i,d,x,p,h,f,E,S,b,A]);return _jsx(AmsContext.Provider,{value:T,children:e})};export const useAms=()=>useContext(AmsContext);export const useAmsSchemas=()=>useContext(AmsContext).schemas;export const useAmsRoles=()=>useContext(AmsContext).roles;export const useAmsAlerts=()=>useContext(AmsContext).alerts;export const useAmsAssets=()=>useContext(AmsContext).assets;export const useAmsSelection=()=>{const e=useContext(AmsContext);return[e.selection,e.setSelection]};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssetDetailView.d.ts","sourceRoot":"","sources":["../../../src/components/ams/AssetDetailView.tsx"],"names":[],"mappings":"AAQA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAU/D,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"AssetDetailView.d.ts","sourceRoot":"","sources":["../../../src/components/ams/AssetDetailView.tsx"],"names":[],"mappings":"AAQA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAU/D,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EA8QnC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import React,{useContext,useEffect,useState}from"react";import{Button}from"primereact/button";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{ConfirmDialog,confirmDialog}from"primereact/confirmdialog";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";import{useAms}from"./AmsProvider";import{CalibrationEntryDialog}from"./CalibrationEntryDialog";export const AssetDetailView=()=>{const{selection:e,schemas:t,roles:s,readAsset:
|
|
1
|
+
import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import React,{useContext,useEffect,useState}from"react";import{Button}from"primereact/button";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{ConfirmDialog,confirmDialog}from"primereact/confirmdialog";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";import{useAms}from"./AmsProvider";import{CalibrationEntryDialog}from"./CalibrationEntryDialog";export const AssetDetailView=()=>{const{selection:e,schemas:t,roles:s,readAsset:i,listCalibrations:r,readCalibration:a,readUsage:l,refreshAssets:n,setSelection:o}=useAms(),{invoke:d}=useContext(EventEmitterContext),[c,m]=useState(null),[p,_]=useState([]),[u,h]=useState(null),[x,f]=useState(!1),[y,j]=useState(null),[b,g]=useState(!1),v=async()=>{if(!e.assetId)return m(null),_([]),void h(null);const t=await i(e.assetId);m(t);const s=await r(e.assetId),n=await Promise.all(s.map(t=>a(e.assetId,t)));_(n.filter(Boolean));const o=await l(e.assetId);h(o)};if(useEffect(()=>{v()},[e.assetId]),!e.assetId)return _jsx("div",{style:{padding:"1rem",color:"#9ca3af"},children:"Select an asset from the registry to see its details."});if(!c)return _jsx("div",{style:{padding:"1rem"},children:"Loading…"});const C=t[c.asset_type]?.label??c.asset_type,S=s[c.asset_type]?.find(e=>e.location===c.location),w=S?S.label??S.location:c.location,A=Array.isArray(t[c.asset_type]?.fields)?t[c.asset_type].fields:[],D=c.custom??{},I=S?.used_by_modules??[];return _jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"1rem"},children:[_jsxs("div",{style:{display:"grid",gridTemplateColumns:"auto 1fr auto 1fr",gap:"0.5rem 1.5rem",alignItems:"baseline"},children:[_jsx("strong",{children:"Asset ID"})," ",_jsx("span",{children:c.asset_id}),_jsx("strong",{children:"Type"})," ",_jsx("span",{children:C}),_jsx("strong",{children:"Serial"})," ",_jsx("span",{children:c.serial||_jsx("em",{style:{color:"#9ca3af"},children:"(none)"})}),_jsx("strong",{children:"Role"})," ",_jsx("span",{children:w||_jsx("em",{style:{color:"#9ca3af"},children:"(none)"})}),_jsx("strong",{children:"Status"})," ",_jsx("span",{children:c.status}),_jsx("strong",{children:"Installed"}),_jsx("span",{children:c.install_date?new Date(c.install_date).toLocaleString():"—"}),_jsx("strong",{children:"Current Cal"}),_jsx("span",{children:c.current_calibration_id??_jsx("em",{style:{color:"#f59e0b"},children:"none"})}),_jsx("strong",{children:"Cycles"})," ",_jsx("span",{children:u?.cycles??0})]}),I.length>0&&_jsxs("div",{style:{fontSize:"0.875rem",color:"#34d399"},children:["✓ Feeds module ",1===I.length?"config":"configs",":"," ",_jsx("strong",{children:I.join(", ")})," — ","their ",_jsxs("code",{children:["$","${ams.by_location."+c.location+".*}"]})," ","placeholders will resolve to the values below."]}),A.length>0&&_jsxs("div",{children:[_jsx("h4",{style:{margin:"0 0 0.5rem 0"},children:"Nameplate"}),_jsx("div",{style:{display:"grid",gridTemplateColumns:"auto 1fr",gap:"0.25rem 1rem",alignItems:"baseline",fontSize:"0.9rem"},children:A.map(e=>{const t=D[e.name],s=null!=t&&""!==t,i=e.label??e.name,r=s?e.units?`${t} ${e.units}`:String(t):"(not set)";return _jsxs(React.Fragment,{children:[_jsx("strong",{title:e.description??void 0,children:i}),_jsx("span",{style:{color:s?void 0:"#f59e0b"},children:r})]},e.name)})})]}),_jsx(SubLocationsPanel,{schema:t[c.asset_type]?.sub_locations,values:c.sub_locations}),_jsx(SubLocationsCalibrationPanel,{schema:t[c.asset_type]?.sub_locations,currentCalibration:p.find(e=>e.cal_id===c.current_calibration_id)}),_jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[_jsx("h4",{style:{margin:0},children:"Calibration History"}),_jsxs("div",{style:{display:"flex",gap:"0.5rem"},children:["active"===c.status&&_jsx(Button,{label:"Retire",icon:"pi pi-power-off",severity:"warning",outlined:!0,onClick:async()=>{if(c)try{const e=await d("ams.update_asset",MessageType.Request,{asset_id:c.asset_id,status:"retired"});e?.success&&(await n(),await v())}catch(e){}},tooltip:"Mark inactive. The asset stays on disk; deletion is a separate action available after retirement.",tooltipOptions:{position:"left"}}),"retired"===c.status&&_jsx(Button,{label:"Delete",icon:"pi pi-trash",severity:"danger",outlined:!0,loading:b,onClick:()=>{c&&confirmDialog({message:_jsxs("div",{children:[_jsxs("p",{style:{marginTop:0},children:["Permanently delete asset ",_jsx("code",{children:c.asset_id}),"?"]}),_jsx("p",{style:{marginBottom:0,fontSize:"0.875rem",color:"#9ca3af"},children:"This removes the AMS record (nameplate, calibration history, usage counters). Historical test records that referenced this asset are unaffected — they carry the full asset and calibration data inline."})]}),header:"Delete asset",icon:"pi pi-exclamation-triangle",acceptLabel:"Delete",rejectLabel:"Cancel",acceptClassName:"p-button-danger",accept:()=>{(async()=>{if(c){g(!0);try{const e=await d("ams.delete_asset",MessageType.Request,{asset_id:c.asset_id});e?.success?(o({assetType:null,assetId:null}),await n()):alert(`Delete failed: ${e?.error_message??"unknown error"}`)}catch(e){alert(`Delete threw: ${e?.message??e}`)}finally{g(!1)}}})()}})},tooltip:"Permanent. Removes the AMS record; historical test snapshots are preserved inline.",tooltipOptions:{position:"left"}}),_jsx(Button,{label:"+ Calibration",icon:"pi pi-plus",onClick:()=>{j(null),f(!0)}})]})]}),_jsx(ConfirmDialog,{}),_jsxs(DataTable,{value:p,size:"small",stripedRows:!0,emptyMessage:"No calibrations recorded for this asset.",children:[_jsx(Column,{field:"cal_id",header:"Cal ID"}),_jsx(Column,{field:"performed_at",header:"Performed",body:e=>e.performed_at?new Date(e.performed_at).toLocaleString():"—"}),_jsx(Column,{field:"performed_by",header:"By"}),_jsx(Column,{field:"expires_at",header:"Expires",body:e=>e.expires_at?new Date(e.expires_at).toLocaleDateString():"—"}),_jsx(Column,{field:"cert_ref",header:"Cert"}),_jsx(Column,{header:"Values",body:e=>_jsx("code",{style:{fontSize:"0.75rem"},children:JSON.stringify(e.values)})}),_jsx(Column,{header:"",style:{width:"5rem"},body:e=>e.cal_id===c.current_calibration_id?_jsx(Button,{icon:"pi pi-pencil",text:!0,rounded:!0,size:"small",tooltip:"Edit this calibration (fixes a typo in place).",tooltipOptions:{position:"left"},onClick:()=>{j(e),f(!0)}}):null})]}),_jsx(CalibrationEntryDialog,{visible:x,assetId:c.asset_id,assetType:c.asset_type,editing:y,onHide:()=>{f(!1),j(null)},onAdded:()=>{v()}})]})};const SubLocationsPanel=({schema:e,values:t})=>{if(!e||!Array.isArray(e.keys)||!Array.isArray(e.fields))return null;const s=t&&"object"==typeof t?t:{};return _jsxs("div",{children:[_jsx("h4",{style:{margin:"0 0 0.5rem 0"},children:e.label??"Sub-locations"}),_jsx("div",{style:{overflowX:"auto"},children:_jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:"0.875rem"},children:[_jsx("thead",{children:_jsxs("tr",{children:[_jsx("th",{style:{textAlign:"left",padding:"0.25rem 0.5rem",borderBottom:"1px solid var(--surface-border)"},children:e.key_label??"Key"}),e.fields.map(e=>_jsxs("th",{title:e.description??void 0,style:{textAlign:"left",padding:"0.25rem 0.5rem",borderBottom:"1px solid var(--surface-border)"},children:[e.label??e.name,e.units?` [${e.units}]`:""]},e.name))]})}),_jsx("tbody",{children:e.keys.map(t=>{const i=s[t]??{};return _jsxs("tr",{children:[_jsx("td",{style:{padding:"0.25rem 0.5rem",fontWeight:600},children:t}),e.fields.map(e=>{const t=i[e.name],s=null!=t&&""!==t;return _jsx("td",{style:{padding:"0.25rem 0.5rem",color:s?void 0:"#f59e0b"},children:s?String(t):"(not set)"},e.name)})]},t)})})]})})]})},SubLocationsCalibrationPanel=({schema:e,currentCalibration:t})=>{if(!e||!Array.isArray(e.keys)||!Array.isArray(e.calibration_fields)||0===e.calibration_fields.length)return null;if(!t)return null;const s=t.values&&"object"==typeof t.values?t.values:{};return _jsxs("div",{children:[_jsx("h4",{style:{margin:"0 0 0.5rem 0"},children:"Current Calibration — Per-axis Values"}),_jsx("div",{style:{overflowX:"auto"},children:_jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:"0.875rem"},children:[_jsx("thead",{children:_jsxs("tr",{children:[_jsx("th",{style:{textAlign:"left",padding:"0.25rem 0.5rem",borderBottom:"1px solid var(--surface-border)"},children:e.key_label??"Key"}),e.calibration_fields.map(e=>_jsxs("th",{style:{textAlign:"left",padding:"0.25rem 0.5rem",borderBottom:"1px solid var(--surface-border)"},children:[e.label??e.name,e.units?` [${e.units}]`:""]},e.name))]})}),_jsx("tbody",{children:e.keys.map(t=>{const i=s[t]??{};return _jsxs("tr",{children:[_jsx("td",{style:{padding:"0.25rem 0.5rem",fontWeight:600},children:t}),e.calibration_fields.map(e=>{const t=i[e.name],s=null!=t&&""!==t;return _jsx("td",{style:{padding:"0.25rem 0.5rem",color:s?void 0:"#f59e0b"},children:s?String(t):"(not set)"},e.name)})]},t)})})]})})]})};
|
|
@@ -4,7 +4,22 @@ export interface CalibrationEntryDialogProps {
|
|
|
4
4
|
assetId: string;
|
|
5
5
|
assetType: string;
|
|
6
6
|
onHide: () => void;
|
|
7
|
+
/** Fires after a successful add OR edit. The cal_id is the same as
|
|
8
|
+
* the editing record on edit, or a freshly minted id on add. */
|
|
7
9
|
onAdded?: (calId: string) => void;
|
|
10
|
+
/** When set, the dialog opens in edit mode pre-seeded from this
|
|
11
|
+
* Calibration record. cal_id is preserved through the round-trip.
|
|
12
|
+
* Caller is responsible for only passing the asset's *current*
|
|
13
|
+
* calibration — the server rejects edits to historical records. */
|
|
14
|
+
editing?: {
|
|
15
|
+
cal_id: string;
|
|
16
|
+
performed_at?: string;
|
|
17
|
+
performed_by?: string;
|
|
18
|
+
expires_at?: string | null;
|
|
19
|
+
cert_ref?: string;
|
|
20
|
+
notes?: string;
|
|
21
|
+
values: any;
|
|
22
|
+
} | null;
|
|
8
23
|
}
|
|
9
24
|
export declare const CalibrationEntryDialog: React.FC<CalibrationEntryDialogProps>;
|
|
10
25
|
//# sourceMappingURL=CalibrationEntryDialog.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CalibrationEntryDialog.d.ts","sourceRoot":"","sources":["../../../src/components/ams/CalibrationEntryDialog.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CalibrationEntryDialog.d.ts","sourceRoot":"","sources":["../../../src/components/ams/CalibrationEntryDialog.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAW/D,MAAM,WAAW,2BAA2B;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB;qEACiE;IACjE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC;;;wEAGoE;IACpE,OAAO,CAAC,EAAE;QACN,MAAM,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,GAAG,CAAC;KACf,GAAG,IAAI,CAAC;CACZ;AAwBD,eAAO,MAAM,sBAAsB,EAAE,KAAK,CAAC,EAAE,CAAC,2BAA2B,CA+TxE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import React,{useContext,useEffect,useState}from"react";import{Button}from"primereact/button";import{Dialog}from"primereact/dialog";import{InputText}from"primereact/inputtext";import{InputNumber}from"primereact/inputnumber";import{Calendar}from"primereact/calendar";import{Dropdown}from"primereact/dropdown";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";import{useAmsSchemas}from"./AmsProvider";export const CalibrationEntryDialog=({visible:e,assetId:t,assetType:a,onHide:r,onAdded:n})=>{const
|
|
1
|
+
import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import React,{useContext,useEffect,useState}from"react";import{Button}from"primereact/button";import{Dialog}from"primereact/dialog";import{InputText}from"primereact/inputtext";import{InputNumber}from"primereact/inputnumber";import{Calendar}from"primereact/calendar";import{Dropdown}from"primereact/dropdown";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";import{useAmsSchemas}from"./AmsProvider";export const CalibrationEntryDialog=({visible:e,assetId:t,assetType:a,onHide:r,onAdded:l,editing:n})=>{const s=!!n,i=useAmsSchemas(),{invoke:o}=useContext(EventEmitterContext),u=i[a]?.calibration_fields??[],d=(()=>{const e=i[a]?.sub_locations;return e&&Array.isArray(e.keys)?e:null})(),m=d?.calibration_fields??[],c=m.length>0,[p,x]=useState({}),[h,_]=useState({}),[g,f]=useState(""),[v,b]=useState(null),[j,y]=useState(""),[C,I]=useState(""),[S,w]=useState(!1),[T,$]=useState(null);useEffect(()=>{e&&($(null),n?(c?(x({}),_(n.values&&"object"==typeof n.values?n.values:{})):(x(n.values&&"object"==typeof n.values?n.values:{}),_({})),f(n.performed_by??""),b(n.expires_at?new Date(n.expires_at):null),y(n.cert_ref??""),I(n.notes??"")):(x({}),_({}),f(""),b(null),y(""),I("")))},[e,n,c]);return _jsxs(Dialog,{header:s?`Edit Calibration ${n.cal_id} — ${t}`:`Add Calibration — ${t}`,visible:e,onHide:r,style:{width:"40rem"},footer:_jsxs(_Fragment,{children:[_jsx(Button,{label:"Cancel",severity:"secondary",onClick:r,disabled:S}),_jsx(Button,{label:"Save",icon:"pi pi-check",onClick:async()=>{if(c&&d)for(const e of d.keys)for(const t of m){if(!t.required)continue;const a=h[e]?.[t.name];if(null==a||""===a)return void $(`Field "${t.label??t.name}" on axis "${e}" is required.`)}else for(const e of u)if(e.required&&(void 0===p[e.name]||null===p[e.name]||""===p[e.name]))return void $(`Field "${e.label??e.name}" is required.`);const e=c?h:p;w(!0),$(null);try{const a=s?"ams.update_calibration":"ams.add_calibration",i={asset_id:t,performed_by:g,expires_at:v?v.toISOString():null,cert_ref:j,notes:C,values:e};s&&n&&(i.cal_id=n.cal_id);const u=await o(a,MessageType.Request,i);u?.success?(l?.(u.data.cal_id),r()):$(u?.error_message??`${a} failed`)}catch(e){$(String(e?.message??e))}finally{w(!1)}},loading:S})]}),children:[_jsxs("div",c&&d?{children:[_jsx("h4",{style:{margin:"0 0 0.5rem 0"},children:d.label??"Per-axis calibration"}),_jsx("div",{style:{overflowX:"auto",marginBottom:"1rem"},children:_jsxs("table",{style:{width:"100%",borderCollapse:"collapse",fontSize:"0.875rem"},children:[_jsx("thead",{children:_jsxs("tr",{children:[_jsx("th",{style:{textAlign:"left",padding:"0.25rem 0.5rem",borderBottom:"1px solid var(--surface-border)"},children:d.key_label??"Key"}),m.map(e=>_jsxs("th",{title:e.description??void 0,style:{textAlign:"left",padding:"0.25rem 0.5rem",borderBottom:"1px solid var(--surface-border)"},children:[e.label??e.name,e.units?` [${e.units}]`:"",e.required?" *":""]},e.name))]})}),_jsx("tbody",{children:d.keys.map(e=>_jsxs("tr",{children:[_jsx("td",{style:{padding:"0.25rem 0.5rem",fontWeight:600},children:e}),m.map(t=>{const a=h[e]?.[t.name],r="string"!==t.type&&"bool"!==t.type;return _jsx("td",{style:{padding:"0.25rem 0.5rem"},children:r?_jsx(InputNumber,{value:a??null,onValueChange:a=>{_(r=>({...r,[e]:{...r[e]??{},[t.name]:a.value}}))},useGrouping:!1,maxFractionDigits:t.type.startsWith("f")?9:0,inputStyle:{width:"100%"}}):_jsx(InputText,{value:a??"",onChange:a=>{_(r=>({...r,[e]:{...r[e]??{},[t.name]:a.target.value}}))},style:{width:"100%"}})},t.name)})]},e))})]})}),_jsxs("div",{style:{display:"grid",gridTemplateColumns:"auto 1fr",gap:"0.5rem 1rem",alignItems:"center"},children:[_jsx("label",{children:"Performed by"}),_jsx(InputText,{value:g,onChange:e=>f(e.target.value)}),_jsx("label",{children:"Expires at"}),_jsx(Calendar,{value:v,onChange:e=>b(e.value??null),showIcon:!0,dateFormat:"yy-mm-dd"}),_jsx("label",{children:"Certificate ref"}),_jsx(InputText,{value:j,onChange:e=>y(e.target.value)}),_jsx("label",{children:"Notes"}),_jsx(InputText,{value:C,onChange:e=>I(e.target.value)})]})]}:{style:{display:"grid",gridTemplateColumns:"auto 1fr",gap:"0.5rem 1rem",alignItems:"center"},children:[u.flatMap((e,t)=>{const[a,r]=(e=>{const t=`${e.label??e.name}${e.units?` [${e.units}]`:""}${e.required?" *":""}`,a=_jsx("label",{title:e.description??"",children:t}),r=t=>x(a=>({...a,[e.name]:t}));let l;switch(e.type){case"string":default:l=_jsx(InputText,{value:p[e.name]??"",onChange:e=>r(e.target.value)});break;case"enum":l=_jsx(Dropdown,{value:p[e.name]??null,options:(e.values??[]).map(e=>({label:e,value:e})),onChange:e=>r(e.value),placeholder:"Choose…"});break;case"bool":l=_jsx(Dropdown,{value:p[e.name]??null,options:[{label:"true",value:!0},{label:"false",value:!1}],onChange:e=>r(e.value)});break;case"u8":case"u16":case"u32":case"u64":case"i8":case"i16":case"i32":case"i64":case"f32":case"f64":l=_jsx(InputNumber,{value:p[e.name]??null,onValueChange:e=>r(e.value),useGrouping:!1,maxFractionDigits:e.type.startsWith("f")?9:0})}return[a,l]})(e);return[_jsx(React.Fragment,{children:a},`l-${t}`),_jsx(React.Fragment,{children:r},`i-${t}`)]}),_jsx("hr",{style:{gridColumn:"1 / span 2",width:"100%"}}),_jsx("label",{children:"Performed by"}),_jsx(InputText,{value:g,onChange:e=>f(e.target.value)}),_jsx("label",{children:"Expires at"}),_jsx(Calendar,{value:v,onChange:e=>b(e.value??null),showIcon:!0,dateFormat:"yy-mm-dd"}),_jsx("label",{children:"Certificate ref"}),_jsx(InputText,{value:j,onChange:e=>y(e.target.value)}),_jsx("label",{children:"Notes"}),_jsx(InputText,{value:C,onChange:e=>I(e.target.value)})]}),T&&_jsx("div",{style:{marginTop:"1rem",color:"#ef4444"},children:T})]})};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectManager.d.ts","sourceRoot":"","sources":["../../../src/components/tis/ProjectManager.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ProjectManager.d.ts","sourceRoot":"","sources":["../../../src/components/tis/ProjectManager.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAuD,MAAM,OAAO,CAAC;AAU5E,MAAM,WAAW,mBAAmB;IAChC,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAyBD,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CA+UxD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as _jsx,Fragment as _Fragment,jsxs as _jsxs}from"react/jsx-runtime";import React,{useCallback,useContext,useEffect,useState}from"react";import{Button}from"primereact/button";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{
|
|
1
|
+
import{jsx as _jsx,Fragment as _Fragment,jsxs as _jsxs}from"react/jsx-runtime";import React,{useCallback,useContext,useEffect,useState}from"react";import{Button}from"primereact/button";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{confirmDialog}from"primereact/confirmdialog";import{ProgressBar}from"primereact/progressbar";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";import{useTis}from"./TisProvider";const formatBytes=e=>{if(!Number.isFinite(e)||e<=0)return"0 B";const t=["B","KiB","MiB","GiB","TiB","PiB"];let r=0,s=e;for(;s>=1024&&r<t.length-1;)s/=1024,r++;return`${s.toFixed(s>=100?0:s>=10?1:2)} ${t[r]}`},formatDate=e=>{if(!e)return"";const t=new Date(e);return Number.isNaN(t.getTime())?e:t.toLocaleString()};export const ProjectManager=e=>{const t=useTis(),r=e.projectId??t.selection.projectId,{invoke:s}=useContext(EventEmitterContext),[a,i]=useState([]),[o,n]=useState(!1),[l,c]=useState(null),[d,m]=useState(!1),[p,u]=useState(!1),[f,h]=useState(null),[g,_]=useState(""),y=useCallback(async()=>{if(r){n(!0);try{const e=await s("tis.list_tests",MessageType.Request,{project_id:r});e?.success&&e.data?.tests?i(e.data.tests):i([])}catch(e){i([])}finally{n(!1)}}else i([])},[r,s]),j=useCallback(async()=>{try{const e=await s("tis.disk_usage",MessageType.Request,{});e?.success&&e.data?(h(e.data),_("")):(h(null),_(e?.error_message??"disk_usage failed"))}catch(e){h(null),_(e instanceof Error?e.message:String(e))}},[s]);useEffect(()=>{y()},[y,t.state.activeRunId]),useEffect(()=>{j()},[j]);const b=e=>{const a=e?.run_id??"",i=e?.sample_id??"";confirmDialog({header:"Delete test",icon:"pi pi-exclamation-triangle",acceptLabel:"Delete",rejectLabel:"Cancel",acceptClassName:"p-button-danger",message:_jsxs("div",{children:[_jsxs("p",{style:{marginTop:0},children:["Permanently delete run ",_jsx("code",{children:a}),i?_jsxs(_Fragment,{children:[" (sample ",_jsx("code",{children:i}),")"]}):null,"?"]}),_jsxs("p",{style:{marginBottom:0,fontSize:"0.875rem",color:"#9ca3af"},children:["Removes ",_jsx("code",{children:"test.json"}),", cycles, raw and filtered data for this run. This action cannot be undone."]})]}),accept:()=>{(async e=>{const a=e?.run_id,i=e?.method_id;if(r&&i&&a){c(a);try{const e=await s("tis.delete_test",MessageType.Request,{project_id:r,method_id:i,run_id:a});if(!e?.success)return void alert(`Failed to delete test ${a}`+(e?.error_message?`: ${e.error_message}`:""));await y(),t.selection.runId===a&&t.setSelection({runId:null})}catch(e){alert(`Delete failed: ${e instanceof Error?e.message:String(e)}`)}finally{c(null)}}})(e)}})},x=e=>{const t="string"==typeof e?.sample_id?e.sample_id:"";if(t)return t;const r=e?.config;return r&&"object"==typeof r&&"string"==typeof r.sample_id?r.sample_id:""},v=f&&f.total_bytes>0?Math.min(100,Math.round(f.used_bytes/f.total_bytes*100)):0;return _jsxs("div",{style:{width:"100%",maxWidth:"100%",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:r?`Project Manager: ${r}`:"Project Manager (no project selected)"}),_jsxs("div",{style:{display:"flex",gap:"0.4rem",alignItems:"center",flexWrap:"wrap"},children:[_jsx(Button,{icon:p?"pi pi-spin pi-spinner":"pi pi-box",label:"Download Archive",size:"small",outlined:!0,disabled:!r||p||d,onClick:async()=>{if(r){u(!0);try{const e=await s("tis.export_project_zip",MessageType.Request,{project_id:r});if(!e?.success)return void alert("Failed to build project archive"+(e?.error_message?`: ${e.error_message}`:""));const t="string"==typeof e.data?.download_url?e.data.download_url:"";if(!t)return void alert("Server did not return a download URL for the archive.");const a=document.createElement("a");a.href=t,a.download="string"==typeof e.data?.filename?e.data.filename:`${r}_project_archive.zip`,document.body.appendChild(a),a.click(),a.remove()}catch(e){alert(`Download failed: ${e instanceof Error?e.message:String(e)}`)}finally{u(!1)}}},tooltip:"Download a ZIP of the entire project directory",tooltipOptions:{position:"bottom"}}),_jsx(Button,{icon:d?"pi pi-spin pi-spinner":"pi pi-trash",label:"Delete Project",size:"small",severity:"danger",disabled:!r||d||p,onClick:()=>{if(!r)return;const e=a.length;confirmDialog({header:"Delete project",icon:"pi pi-exclamation-triangle",acceptLabel:"Delete Project",rejectLabel:"Cancel",acceptClassName:"p-button-danger",message:_jsxs("div",{children:[_jsxs("p",{style:{marginTop:0},children:["Permanently delete project ",_jsx("code",{children:r})," and all ",_jsx("strong",{children:e})," test",1===e?"":"s"," inside it?"]}),_jsx("p",{style:{marginBottom:0,fontSize:"0.875rem",color:"#9ca3af"},children:"This removes every method, run, cycle, and raw/filtered blob under the project. Consider downloading the archive first. This action cannot be undone."})]}),accept:()=>{(async()=>{if(r){m(!0);try{const e=await s("tis.delete_project",MessageType.Request,{project_id:r});if(!e?.success)return void alert(`Failed to delete project ${r}`+(e?.error_message?`: ${e.error_message}`:""));t.setSelection({projectId:null,methodId:null,sampleId:null,runId:null}),await t.refreshProjects(),await j(),i([])}catch(e){alert(`Delete failed: ${e instanceof Error?e.message:String(e)}`)}finally{m(!1)}}})()}})},tooltip:"Permanently delete the project and every test inside it",tooltipOptions:{position:"bottom"}}),_jsx(Button,{icon:"pi pi-refresh",label:"Refresh",size:"small",onClick:()=>{y(),j()},disabled:o})]})]}),_jsxs("div",{style:{marginBottom:"1rem",padding:"0.75rem 1rem",border:"1px solid #2a2a2a",borderRadius:4,background:"#161616"},children:[_jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"baseline",flexWrap:"wrap",gap:"0.5rem"},children:[_jsx("strong",{children:"Server Disk Space"}),f?_jsxs("span",{style:{fontSize:"0.875rem",color:"#9ca3af"},children:[formatBytes(f.available_bytes)," free of ",formatBytes(f.total_bytes)," ","(",100-v,"% available)"]}):_jsx("span",g?{style:{fontSize:"0.875rem",color:"#f87171"},children:g}:{style:{fontSize:"0.875rem",color:"#9ca3af"},children:"Loading…"})]}),f&&_jsxs(_Fragment,{children:[_jsx(ProgressBar,{value:v,showValue:!1,style:{height:"0.5rem",marginTop:"0.5rem"},color:v>=90?"#dc2626":v>=75?"#f59e0b":void 0}),_jsx("div",{style:{fontSize:"0.75rem",color:"#6b7280",marginTop:"0.4rem"},children:_jsx("code",{children:f.base_directory})})]})]}),_jsxs(DataTable,{value:a,loading:o,paginator:!0,rows:10,emptyMessage:r?"No tests in this project.":"Select a project to manage.",scrollable:!0,scrollHeight:"flex",tableStyle:{minWidth:0},style:{width:"100%"},children:[_jsx(Column,{header:"Sample ID",body:x,sortable:!0,sortFunction:e=>{const t=[...e.data];return t.sort((t,r)=>x(t).localeCompare(x(r))*(e.order??1)),t},style:{minWidth:"8rem"}}),_jsx(Column,{field:"start_time",header:"Date/Time",sortable:!0,body:e=>formatDate(e.start_time),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:"Action",style:{width:"8rem"},body:e=>{const t=l===e.run_id;return _jsx(Button,{icon:t?"pi pi-spin pi-spinner":"pi pi-trash",label:"Delete",size:"small",severity:"danger",outlined:!0,disabled:null!==l||d,onClick:()=>b(e),tooltip:"Permanently delete this test",tooltipOptions:{position:"left"}})}})]})]})};
|
|
@@ -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;;;;;kCAK8B;IAC9B,OAAO,CAAC,EAAE,GAAG,CAAC;CACjB;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;AAyED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,
|
|
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;;;;;kCAK8B;IAC9B,OAAO,CAAC,EAAE,GAAG,CAAC;CACjB;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;AAyED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAsetD,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},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(),
|
|
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},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(),r=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:r,onChange:e=>s(e.value??""),placeholder:0===r.length?`No active ${e} assets registered — add one in Settings → Assets`:`Select ${e}…`,className:i?"p-invalid":"",filter:!0,showClear:!0,disabled:0===r.length})};export const TestSetupForm=({schema:e,defaultMethodId:t,onMethodChange:s,onValidationChange:i})=>{const a=useTis(),{invoke:n,write:r}=useContext(EventEmitterContext),{rawValues:o,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),[v,b]=useState(!1),[I,C]=useState({open:!1,title:"",body:null}),S=useRef("");useEffect(()=>{_&&p&&S.current!==p&&(S.current=p,x(e=>{let t=e;for(const s of _.config_fields)"sample_id"!==s.name&&void 0!==s.default&&null!==s.default&&(t===e&&(t={...e}),t[s.name]=s.default,s.source&&Promise.resolve().then(()=>r(s.source,s.default)).catch(e=>{}));return t}))},[_,p,r]),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=o[i.tagName];null!=a&&(t[s.name]!==a&&(t===e&&(t={...e}),t[s.name]=a))}return t})},[_,o,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 T=async(e,t)=>{if(x({...g,[e.name]:t}),e.source)try{await r(e.source,t)}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."}),C({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:()=>b(!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 r=s.find(e=>"by_id_field"===e.select&&e.from===i);return r?r.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=>T(e,t),invalid:!t}):i?_jsx(ValueInput,{label:void 0,value:null!=g[e.name]?Number(g[e.name]):null,onValueChanged:t=>T(e,t),className:t?"":"p-invalid"}):_jsx(TextInput,{label:void 0,value:null!=g[e.name]?String(g[e.name]):"",onValueChanged:t=>T(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:()=>C({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:()=>b(!1),currentMethodId:p,onSelected:e=>u(e)}),_jsx(Dialog,{header:I.title,visible:I.open,onHide:()=>C({open:!1,title:"",body:null}),style:{width:"32rem",maxWidth:"90vw"},modal:!0,dismissableMask:!0,children:I.body})]})};
|
|
@@ -52,8 +52,32 @@ export interface TisRunCacheEntry {
|
|
|
52
52
|
[blobName: string]: any;
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* One project-level asset_ref entry, in the wire shape returned by
|
|
57
|
+
* `tis.list_schemas`. Mirrors `Project.asset_refs` on the server.
|
|
58
|
+
* Carries only the fields the form's by_id_field lookup needs;
|
|
59
|
+
* other fields (label, description, calibration_required) are
|
|
60
|
+
* ignored but pass through untouched.
|
|
61
|
+
*/
|
|
62
|
+
export interface TisProjectAssetRef {
|
|
63
|
+
field: string;
|
|
64
|
+
asset_type: string;
|
|
65
|
+
select: 'by_location' | 'by_id_field';
|
|
66
|
+
/** Set when `select=by_id_field` — the dotted config path
|
|
67
|
+
* (`config.<field_name>`) whose value picks the asset. */
|
|
68
|
+
from?: string;
|
|
69
|
+
/** Set when `select=by_location` — the role/location key. */
|
|
70
|
+
location?: string;
|
|
71
|
+
}
|
|
55
72
|
export interface TisContextValue {
|
|
56
73
|
schemas: SchemaRegistry;
|
|
74
|
+
/** Project-level asset_refs from `tis.list_schemas`. Empty array
|
|
75
|
+
* when the project declares none. The HMI form merges these with
|
|
76
|
+
* the active method's own `asset_refs` before deciding whether to
|
|
77
|
+
* render a config field as a dropdown (by_id_field) or a free-form
|
|
78
|
+
* input. Method-level refs win on `field`-name collision, mirroring
|
|
79
|
+
* the server resolver. */
|
|
80
|
+
projectAssetRefs: TisProjectAssetRef[];
|
|
57
81
|
defaultMethodId: string;
|
|
58
82
|
schemasLoaded: boolean;
|
|
59
83
|
state: TisLiveState;
|
|
@@ -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:{},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:
|
|
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};
|
package/package.json
CHANGED
|
@@ -134,7 +134,7 @@ export const Indicator: React.FC<IndicatorProps> = ({
|
|
|
134
134
|
const displayValue = value !== undefined ? value : subscribedValue;
|
|
135
135
|
const color = pickColor(displayValue, onColor, offColor, invalidColor);
|
|
136
136
|
|
|
137
|
-
// Children mode: square, filled,
|
|
137
|
+
// Children mode: square, filled, sunken look. Sized by the caller's
|
|
138
138
|
// className (e.g. `ac-toolbar-icon-btn`) so it lines up with sibling
|
|
139
139
|
// IndicatorButtons in the same toolbar.
|
|
140
140
|
if (children !== undefined && children !== null) {
|
|
@@ -147,10 +147,17 @@ export const Indicator: React.FC<IndicatorProps> = ({
|
|
|
147
147
|
// Square corners distinguish a passive indicator from an
|
|
148
148
|
// interactive button (PrimeReact buttons round to ~6px).
|
|
149
149
|
borderRadius: 0,
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
|
|
150
|
+
// Classic Win95-style chiseled-in bevel: dark shadow on
|
|
151
|
+
// the top + left (the "wall" above the recess casts a
|
|
152
|
+
// shadow into it) plus a lighter highlight on the bottom +
|
|
153
|
+
// right (light bouncing back out). The previous single
|
|
154
|
+
// top-only shadow read as raised because the bottom edge
|
|
155
|
+
// had nothing to contrast it. The shadow opacities are
|
|
156
|
+
// chosen to remain legible against every IndicatorColor
|
|
157
|
+
// — green, red, orange, blue, gray, black.
|
|
158
|
+
boxShadow:
|
|
159
|
+
'inset 2px 2px 3px rgba(0, 0, 0, 0.55), ' +
|
|
160
|
+
'inset -1px -1px 2px rgba(255, 255, 255, 0.20)',
|
|
154
161
|
boxSizing: 'border-box',
|
|
155
162
|
...style,
|
|
156
163
|
};
|
|
@@ -180,7 +187,11 @@ export const Indicator: React.FC<IndicatorProps> = ({
|
|
|
180
187
|
backgroundColor: color,
|
|
181
188
|
width: '22px',
|
|
182
189
|
borderRadius: 0,
|
|
183
|
-
|
|
190
|
+
// Same Win95-sunken treatment as the children-mode box, dialed
|
|
191
|
+
// down a touch since the swatch is only 22px wide.
|
|
192
|
+
boxShadow:
|
|
193
|
+
'inset 1px 1px 2px rgba(0, 0, 0, 0.5), ' +
|
|
194
|
+
'inset -1px -1px 1px rgba(255, 255, 255, 0.18)',
|
|
184
195
|
};
|
|
185
196
|
|
|
186
197
|
return (
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 1. Schema registry (loaded once via `ams.list_schemas`).
|
|
8
8
|
* 2. Live alert scalars (`ams.asset_count`, `ams.alert_*`).
|
|
9
9
|
* 3. Asset registry cache, refreshed on `ams.asset_changed` /
|
|
10
|
-
* `ams.calibration_added` broadcasts.
|
|
10
|
+
* `ams.calibration_added` / `ams.calibration_updated` broadcasts.
|
|
11
11
|
* 4. Selection state (assetType, assetId) — pinned by user click.
|
|
12
12
|
*
|
|
13
13
|
* Drop once at the top of the HMI; the AMS components below it read
|
|
@@ -201,8 +201,9 @@ export const AmsProvider: React.FC<AmsProviderProps> = ({ children }) => {
|
|
|
201
201
|
|
|
202
202
|
useEffect(() => {
|
|
203
203
|
const subs = [
|
|
204
|
-
subscribe('ams.asset_changed',
|
|
205
|
-
subscribe('ams.calibration_added',
|
|
204
|
+
subscribe('ams.asset_changed', () => { refreshAssets(); }),
|
|
205
|
+
subscribe('ams.calibration_added', () => { refreshAssets(); }),
|
|
206
|
+
subscribe('ams.calibration_updated', () => { refreshAssets(); }),
|
|
206
207
|
subscribe('ams.asset_count', (v: any) => setAlerts(a => ({ ...a, assetCount: Number(v) || 0 }))),
|
|
207
208
|
subscribe('ams.alert_calibration_overdue', (v: any) => setAlerts(a => ({ ...a, calibrationOverdue: Number(v) || 0 }))),
|
|
208
209
|
subscribe('ams.alert_lane_unavailable', (v: any) => setAlerts(a => ({ ...a, laneUnavailable: Number(v) || 0 }))),
|
|
@@ -24,6 +24,11 @@ export const AssetDetailView: React.FC = () => {
|
|
|
24
24
|
const [cals, setCals] = useState<any[]>([]);
|
|
25
25
|
const [usage, setUsage] = useState<any | null>(null);
|
|
26
26
|
const [calDialogOpen, setCalDialogOpen] = useState(false);
|
|
27
|
+
/** When non-null, the dialog opens in edit mode pre-seeded from this
|
|
28
|
+
* Calibration record. Only the asset's current calibration is
|
|
29
|
+
* editable (server enforces); the Edit button only renders on that
|
|
30
|
+
* row, so this is always either null or the current cal. */
|
|
31
|
+
const [editingCal, setEditingCal] = useState<any | null>(null);
|
|
27
32
|
const [deleting, setDeleting] = useState(false);
|
|
28
33
|
|
|
29
34
|
/** Two-step asset retirement: flip status to retired via update_asset.
|
|
@@ -234,7 +239,7 @@ export const AssetDetailView: React.FC = () => {
|
|
|
234
239
|
/>
|
|
235
240
|
)}
|
|
236
241
|
<Button label="+ Calibration" icon="pi pi-plus"
|
|
237
|
-
onClick={() => setCalDialogOpen(true)} />
|
|
242
|
+
onClick={() => { setEditingCal(null); setCalDialogOpen(true); }} />
|
|
238
243
|
</div>
|
|
239
244
|
</div>
|
|
240
245
|
<ConfirmDialog />
|
|
@@ -253,13 +258,30 @@ export const AssetDetailView: React.FC = () => {
|
|
|
253
258
|
<Column header="Values"
|
|
254
259
|
body={(r) => <code style={{ fontSize: '0.75rem' }}>{JSON.stringify(r.values)}</code>}
|
|
255
260
|
/>
|
|
261
|
+
<Column header="" style={{ width: '5rem' }}
|
|
262
|
+
body={(r) => (
|
|
263
|
+
// Edit is only offered for the asset's current
|
|
264
|
+
// calibration — historical records are frozen
|
|
265
|
+
// (server enforces too). Customers asked for
|
|
266
|
+
// this so typos can be fixed without minting a
|
|
267
|
+
// new cal_id every time.
|
|
268
|
+
r.cal_id === asset.current_calibration_id ? (
|
|
269
|
+
<Button icon="pi pi-pencil" text rounded size="small"
|
|
270
|
+
tooltip="Edit this calibration (fixes a typo in place)."
|
|
271
|
+
tooltipOptions={{ position: 'left' }}
|
|
272
|
+
onClick={() => { setEditingCal(r); setCalDialogOpen(true); }}
|
|
273
|
+
/>
|
|
274
|
+
) : null
|
|
275
|
+
)}
|
|
276
|
+
/>
|
|
256
277
|
</DataTable>
|
|
257
278
|
|
|
258
279
|
<CalibrationEntryDialog
|
|
259
280
|
visible={calDialogOpen}
|
|
260
281
|
assetId={asset.asset_id}
|
|
261
282
|
assetType={asset.asset_type}
|
|
262
|
-
|
|
283
|
+
editing={editingCal}
|
|
284
|
+
onHide={() => { setCalDialogOpen(false); setEditingCal(null); }}
|
|
263
285
|
onAdded={() => { refresh(); }}
|
|
264
286
|
/>
|
|
265
287
|
</div>
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* <CalibrationEntryDialog> — form for adding a calibration
|
|
3
|
-
*
|
|
2
|
+
* <CalibrationEntryDialog> — form for adding *or editing* a calibration
|
|
3
|
+
* record. Renders a dynamic field set built from the asset_type's
|
|
4
4
|
* `calibration_fields` schema, plus a few standard meta fields.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
6
|
+
* Two modes:
|
|
7
|
+
* - Add: `editing` prop absent. Submits via `ams.add_calibration`,
|
|
8
|
+
* which mints a new cal_id and bumps the asset's current_calibration_id.
|
|
9
|
+
* - Edit: `editing` prop carries the existing Calibration record. Form
|
|
10
|
+
* fields are pre-seeded from it; submit hits `ams.update_calibration`
|
|
11
|
+
* which overwrites the file in place (cal_id and the rest of the
|
|
12
|
+
* history are preserved). The server enforces that only the asset's
|
|
13
|
+
* *current* calibration is editable, so the parent should only open
|
|
14
|
+
* the dialog in edit mode for the current row.
|
|
9
15
|
*/
|
|
10
16
|
|
|
11
17
|
import React, { useContext, useEffect, useState } from 'react';
|
|
@@ -24,7 +30,22 @@ export interface CalibrationEntryDialogProps {
|
|
|
24
30
|
assetId: string;
|
|
25
31
|
assetType: string;
|
|
26
32
|
onHide: () => void;
|
|
33
|
+
/** Fires after a successful add OR edit. The cal_id is the same as
|
|
34
|
+
* the editing record on edit, or a freshly minted id on add. */
|
|
27
35
|
onAdded?: (calId: string) => void;
|
|
36
|
+
/** When set, the dialog opens in edit mode pre-seeded from this
|
|
37
|
+
* Calibration record. cal_id is preserved through the round-trip.
|
|
38
|
+
* Caller is responsible for only passing the asset's *current*
|
|
39
|
+
* calibration — the server rejects edits to historical records. */
|
|
40
|
+
editing?: {
|
|
41
|
+
cal_id: string;
|
|
42
|
+
performed_at?: string;
|
|
43
|
+
performed_by?: string;
|
|
44
|
+
expires_at?: string | null;
|
|
45
|
+
cert_ref?: string;
|
|
46
|
+
notes?: string;
|
|
47
|
+
values: any;
|
|
48
|
+
} | null;
|
|
28
49
|
}
|
|
29
50
|
|
|
30
51
|
interface FieldSpec {
|
|
@@ -50,8 +71,9 @@ interface SubLocationsSchema {
|
|
|
50
71
|
}
|
|
51
72
|
|
|
52
73
|
export const CalibrationEntryDialog: React.FC<CalibrationEntryDialogProps> = ({
|
|
53
|
-
visible, assetId, assetType, onHide, onAdded,
|
|
74
|
+
visible, assetId, assetType, onHide, onAdded, editing,
|
|
54
75
|
}) => {
|
|
76
|
+
const isEdit = !!editing;
|
|
55
77
|
const schemas = useAmsSchemas();
|
|
56
78
|
const { invoke } = useContext(EventEmitterContext);
|
|
57
79
|
|
|
@@ -81,17 +103,46 @@ export const CalibrationEntryDialog: React.FC<CalibrationEntryDialogProps> = ({
|
|
|
81
103
|
const [submitting, setSubmitting] = useState(false);
|
|
82
104
|
const [error, setError] = useState<string | null>(null);
|
|
83
105
|
|
|
106
|
+
// Reset / pre-seed when the dialog opens. In edit mode the form
|
|
107
|
+
// mirrors the existing record so the operator only has to change
|
|
108
|
+
// what's wrong; in add mode the form starts blank.
|
|
84
109
|
useEffect(() => {
|
|
85
|
-
if (visible)
|
|
110
|
+
if (!visible) return;
|
|
111
|
+
setError(null);
|
|
112
|
+
if (editing) {
|
|
113
|
+
// The values payload shape mirrors what the server stores:
|
|
114
|
+
// per-axis types use `{ <key>: { <field>: ... } }`; flat
|
|
115
|
+
// types use `{ <field>: ... }`. Route into the right slot
|
|
116
|
+
// based on the schema, not the payload, so a corrupt or
|
|
117
|
+
// legacy shape doesn't crosstalk into the wrong UI mode.
|
|
118
|
+
if (isPerAxis) {
|
|
119
|
+
setValues({});
|
|
120
|
+
setPerAxisValues(
|
|
121
|
+
(editing.values && typeof editing.values === 'object')
|
|
122
|
+
? editing.values as { [k: string]: { [f: string]: any } }
|
|
123
|
+
: {}
|
|
124
|
+
);
|
|
125
|
+
} else {
|
|
126
|
+
setValues(
|
|
127
|
+
(editing.values && typeof editing.values === 'object')
|
|
128
|
+
? editing.values as { [k: string]: any }
|
|
129
|
+
: {}
|
|
130
|
+
);
|
|
131
|
+
setPerAxisValues({});
|
|
132
|
+
}
|
|
133
|
+
setPerformedBy(editing.performed_by ?? '');
|
|
134
|
+
setExpiresAt(editing.expires_at ? new Date(editing.expires_at) : null);
|
|
135
|
+
setCertRef(editing.cert_ref ?? '');
|
|
136
|
+
setNotes(editing.notes ?? '');
|
|
137
|
+
} else {
|
|
86
138
|
setValues({});
|
|
87
139
|
setPerAxisValues({});
|
|
88
140
|
setPerformedBy('');
|
|
89
141
|
setExpiresAt(null);
|
|
90
142
|
setCertRef('');
|
|
91
143
|
setNotes('');
|
|
92
|
-
setError(null);
|
|
93
144
|
}
|
|
94
|
-
}, [visible]);
|
|
145
|
+
}, [visible, editing, isPerAxis]);
|
|
95
146
|
|
|
96
147
|
const renderField = (f: FieldSpec) => {
|
|
97
148
|
const labelText = `${f.label ?? f.name}${f.units ? ` [${f.units}]` : ''}${f.required ? ' *' : ''}`;
|
|
@@ -176,19 +227,28 @@ export const CalibrationEntryDialog: React.FC<CalibrationEntryDialogProps> = ({
|
|
|
176
227
|
setSubmitting(true);
|
|
177
228
|
setError(null);
|
|
178
229
|
try {
|
|
179
|
-
|
|
230
|
+
// Edit and add share the same payload shape; the topic
|
|
231
|
+
// differentiates them. On edit we also pin the cal_id so
|
|
232
|
+
// the server overwrites the right record rather than
|
|
233
|
+
// minting a new one.
|
|
234
|
+
const topic = isEdit ? 'ams.update_calibration' : 'ams.add_calibration';
|
|
235
|
+
const payload: any = {
|
|
180
236
|
asset_id: assetId,
|
|
181
237
|
performed_by: performedBy,
|
|
182
238
|
expires_at: expiresAt ? expiresAt.toISOString() : null,
|
|
183
239
|
cert_ref: certRef,
|
|
184
240
|
notes,
|
|
185
241
|
values: valuesPayload,
|
|
186
|
-
}
|
|
242
|
+
};
|
|
243
|
+
if (isEdit && editing) {
|
|
244
|
+
payload.cal_id = editing.cal_id;
|
|
245
|
+
}
|
|
246
|
+
const resp: any = await invoke(topic as any, MessageType.Request, payload);
|
|
187
247
|
if (resp?.success) {
|
|
188
248
|
onAdded?.(resp.data.cal_id);
|
|
189
249
|
onHide();
|
|
190
250
|
} else {
|
|
191
|
-
setError(resp?.error_message ??
|
|
251
|
+
setError(resp?.error_message ?? `${topic} failed`);
|
|
192
252
|
}
|
|
193
253
|
} catch (e: any) {
|
|
194
254
|
setError(String(e?.message ?? e));
|
|
@@ -199,7 +259,9 @@ export const CalibrationEntryDialog: React.FC<CalibrationEntryDialogProps> = ({
|
|
|
199
259
|
|
|
200
260
|
return (
|
|
201
261
|
<Dialog
|
|
202
|
-
header={
|
|
262
|
+
header={isEdit
|
|
263
|
+
? `Edit Calibration ${editing!.cal_id} — ${assetId}`
|
|
264
|
+
: `Add Calibration — ${assetId}`}
|
|
203
265
|
visible={visible}
|
|
204
266
|
onHide={onHide}
|
|
205
267
|
style={{ width: '40rem' }}
|
|
@@ -13,13 +13,15 @@
|
|
|
13
13
|
* Mounts under the operator-facing Project tab, alongside <ProjectSelector>.
|
|
14
14
|
* Reads `selection.projectId` from <TisProvider> so it follows whatever
|
|
15
15
|
* project the operator has picked.
|
|
16
|
+
*
|
|
17
|
+
* Requires that a <ConfirmDialog> instance be registered in web app somewhere.
|
|
16
18
|
*/
|
|
17
19
|
|
|
18
20
|
import React, { useCallback, useContext, useEffect, useState } from 'react';
|
|
19
21
|
import { Button } from 'primereact/button';
|
|
20
22
|
import { DataTable } from 'primereact/datatable';
|
|
21
23
|
import { Column } from 'primereact/column';
|
|
22
|
-
import {
|
|
24
|
+
import { confirmDialog } from 'primereact/confirmdialog';
|
|
23
25
|
import { ProgressBar } from 'primereact/progressbar';
|
|
24
26
|
import { EventEmitterContext } from '../../core/EventEmitterContext';
|
|
25
27
|
import { MessageType } from '../../hub/CommandMessage';
|
|
@@ -256,7 +258,6 @@ export const ProjectManager: React.FC<ProjectManagerProps> = (props) => {
|
|
|
256
258
|
|
|
257
259
|
return (
|
|
258
260
|
<div style={{ width: '100%', maxWidth: '100%', boxSizing: 'border-box' }}>
|
|
259
|
-
<ConfirmDialog />
|
|
260
261
|
|
|
261
262
|
<div style={{
|
|
262
263
|
display: 'flex',
|
|
@@ -368,19 +368,35 @@ export const TestSetupForm: React.FC<TestSetupFormProps> = ({
|
|
|
368
368
|
};
|
|
369
369
|
|
|
370
370
|
/**
|
|
371
|
-
* If
|
|
372
|
-
*
|
|
373
|
-
* should pick from. The form renders that
|
|
374
|
-
* AMS assets instead of a free-form text
|
|
371
|
+
* If any `asset_ref` declares `select=by_id_field` with
|
|
372
|
+
* `from: "config.<field_name>"` for this field, return the
|
|
373
|
+
* asset_type the field should pick from. The form renders that
|
|
374
|
+
* field as a dropdown of AMS assets instead of a free-form text
|
|
375
|
+
* input.
|
|
375
376
|
*
|
|
376
|
-
*
|
|
377
|
-
*
|
|
377
|
+
* Refs are sourced from two places, matching what the server's
|
|
378
|
+
* resolver does at stage time:
|
|
379
|
+
* 1. Method-level (`schema.asset_refs`) — method-specific
|
|
380
|
+
* accessories.
|
|
381
|
+
* 2. Project-level (`tis.projectAssetRefs`) — system hardware
|
|
382
|
+
* that applies to every test method (e.g. the test surface
|
|
383
|
+
* picked per-test by id).
|
|
384
|
+
* Method-level wins on `field`-name collision, so a method can
|
|
385
|
+
* override the project default.
|
|
378
386
|
*/
|
|
379
387
|
const assetTypeForField = (field: TestFieldDef): string | null => {
|
|
380
|
-
const
|
|
388
|
+
const methodRefs = (schema?.asset_refs ?? []) as TisAssetRef[];
|
|
389
|
+
const projectRefs = (tis.projectAssetRefs ?? []) as TisAssetRef[];
|
|
381
390
|
const expectedFrom = `config.${field.name}`;
|
|
382
|
-
|
|
383
|
-
|
|
391
|
+
// Method wins on collision — iterate method refs first.
|
|
392
|
+
const methodHit = methodRefs.find(
|
|
393
|
+
r => r.select === 'by_id_field' && r.from === expectedFrom,
|
|
394
|
+
);
|
|
395
|
+
if (methodHit) return methodHit.asset_type;
|
|
396
|
+
const projectHit = projectRefs.find(
|
|
397
|
+
r => r.select === 'by_id_field' && r.from === expectedFrom,
|
|
398
|
+
);
|
|
399
|
+
return projectHit ? projectHit.asset_type : null;
|
|
384
400
|
};
|
|
385
401
|
|
|
386
402
|
const renderConfigField = (field: TestFieldDef) => {
|
|
@@ -91,8 +91,33 @@ export interface TisRunCacheEntry {
|
|
|
91
91
|
rawData: { [blobName: string]: any };
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
/**
|
|
95
|
+
* One project-level asset_ref entry, in the wire shape returned by
|
|
96
|
+
* `tis.list_schemas`. Mirrors `Project.asset_refs` on the server.
|
|
97
|
+
* Carries only the fields the form's by_id_field lookup needs;
|
|
98
|
+
* other fields (label, description, calibration_required) are
|
|
99
|
+
* ignored but pass through untouched.
|
|
100
|
+
*/
|
|
101
|
+
export interface TisProjectAssetRef {
|
|
102
|
+
field: string;
|
|
103
|
+
asset_type: string;
|
|
104
|
+
select: 'by_location' | 'by_id_field';
|
|
105
|
+
/** Set when `select=by_id_field` — the dotted config path
|
|
106
|
+
* (`config.<field_name>`) whose value picks the asset. */
|
|
107
|
+
from?: string;
|
|
108
|
+
/** Set when `select=by_location` — the role/location key. */
|
|
109
|
+
location?: string;
|
|
110
|
+
}
|
|
111
|
+
|
|
94
112
|
export interface TisContextValue {
|
|
95
113
|
schemas: SchemaRegistry;
|
|
114
|
+
/** Project-level asset_refs from `tis.list_schemas`. Empty array
|
|
115
|
+
* when the project declares none. The HMI form merges these with
|
|
116
|
+
* the active method's own `asset_refs` before deciding whether to
|
|
117
|
+
* render a config field as a dropdown (by_id_field) or a free-form
|
|
118
|
+
* input. Method-level refs win on `field`-name collision, mirroring
|
|
119
|
+
* the server resolver. */
|
|
120
|
+
projectAssetRefs: TisProjectAssetRef[];
|
|
96
121
|
defaultMethodId: string;
|
|
97
122
|
schemasLoaded: boolean;
|
|
98
123
|
|
|
@@ -182,6 +207,7 @@ const EMPTY_SELECTION: TisSelection = {
|
|
|
182
207
|
|
|
183
208
|
const TisContext = createContext<TisContextValue>({
|
|
184
209
|
schemas: {},
|
|
210
|
+
projectAssetRefs: [],
|
|
185
211
|
defaultMethodId: '',
|
|
186
212
|
schemasLoaded: false,
|
|
187
213
|
state: EMPTY_STATE,
|
|
@@ -252,6 +278,7 @@ export const TisProvider: React.FC<TisProviderProps> = ({ children, defaultMetho
|
|
|
252
278
|
const { invoke, subscribe, unsubscribe } = useContext(EventEmitterContext);
|
|
253
279
|
|
|
254
280
|
const [schemas, setSchemas] = useState<SchemaRegistry>({});
|
|
281
|
+
const [projectAssetRefs, setProjectAssetRefs] = useState<TisProjectAssetRef[]>([]);
|
|
255
282
|
const [defaultMethodId, setDefaultMethodId] = useState<string>(initialDefault ?? '');
|
|
256
283
|
const [schemasLoaded, setSchemasLoaded] = useState(false);
|
|
257
284
|
|
|
@@ -284,7 +311,11 @@ export const TisProvider: React.FC<TisProviderProps> = ({ children, defaultMetho
|
|
|
284
311
|
if (resp?.success && resp.data) {
|
|
285
312
|
const methods = (resp.data.test_methods ?? {}) as SchemaRegistry;
|
|
286
313
|
const dflt = (resp.data.default_method_id ?? '') as string;
|
|
314
|
+
const refs = Array.isArray(resp.data.asset_refs)
|
|
315
|
+
? (resp.data.asset_refs as TisProjectAssetRef[])
|
|
316
|
+
: [];
|
|
287
317
|
setSchemas(methods);
|
|
318
|
+
setProjectAssetRefs(refs);
|
|
288
319
|
if (!initialDefault && dflt) setDefaultMethodId(dflt);
|
|
289
320
|
setSchemasLoaded(true);
|
|
290
321
|
} else {
|
|
@@ -620,14 +651,14 @@ export const TisProvider: React.FC<TisProviderProps> = ({ children, defaultMetho
|
|
|
620
651
|
}, []);
|
|
621
652
|
|
|
622
653
|
const value: TisContextValue = useMemo(() => ({
|
|
623
|
-
schemas, defaultMethodId, schemasLoaded,
|
|
654
|
+
schemas, projectAssetRefs, defaultMethodId, schemasLoaded,
|
|
624
655
|
state, selection, setSelection,
|
|
625
656
|
existingProjects, projectKnown, refreshProjects, markProjectJustCreated,
|
|
626
657
|
projectFields, projectFieldsLoaded, loadProjectFields, setProjectFields,
|
|
627
658
|
stagedConfig, setStagedConfig, clearStagedConfig,
|
|
628
659
|
fetchRuns, fetchRun, runCache,
|
|
629
660
|
}), [
|
|
630
|
-
schemas, defaultMethodId, schemasLoaded,
|
|
661
|
+
schemas, projectAssetRefs, defaultMethodId, schemasLoaded,
|
|
631
662
|
state, selection, setSelection,
|
|
632
663
|
existingProjects, projectKnown, refreshProjects, markProjectJustCreated,
|
|
633
664
|
projectFields, projectFieldsLoaded, loadProjectFields, setProjectFields,
|