@adcops/autocore-react 3.3.83 → 3.3.84

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.
@@ -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,EA8QnC,CAAC"}
1
+ {"version":3,"file":"AssetDetailView.d.ts","sourceRoot":"","sources":["../../../src/components/ams/AssetDetailView.tsx"],"names":[],"mappings":"AAQA,OAAO,KAA0C,MAAM,OAAO,CAAC;AAW/D,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EA4SnC,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: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)})})]})})]})};
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";import{AssetEditDialog}from"./AssetEditDialog";export const AssetDetailView=()=>{const{selection:e,schemas:t,roles:s,readAsset:i,listCalibrations:a,readCalibration:r,readUsage:l,refreshAssets:n,setSelection:o}=useAms(),{invoke:d}=useContext(EventEmitterContext),[c,m]=useState(null),[p,u]=useState([]),[_,h]=useState(null),[x,f]=useState(!1),[y,j]=useState(null),[b,g]=useState(!1),[v,C]=useState(!1),S=async()=>{if(!e.assetId)return m(null),u([]),void h(null);const t=await i(e.assetId);m(t);const s=await a(e.assetId),n=await Promise.all(s.map(t=>r(e.assetId,t)));u(n.filter(Boolean));const o=await l(e.assetId);h(o)};if(useEffect(()=>{S()},[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 w=t[c.asset_type]?.label??c.asset_type,A=s[c.asset_type]?.find(e=>e.location===c.location),D=A?A.label??A.location:c.location,E=Array.isArray(t[c.asset_type]?.fields)?t[c.asset_type].fields:[],k=c.custom??{},I=A?.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:w}),_jsx("strong",{children:"Serial"})," ",_jsx("span",{children:c.serial||_jsx("em",{style:{color:"#9ca3af"},children:"(none)"})}),_jsx("strong",{children:"Role"})," ",_jsx("span",{children:D||_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:_?.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."]}),E.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:E.map(e=>{const t=k[e.name],s=null!=t&&""!==t,i=e.label??e.name,a=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:a})]},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:[_jsx(Button,{label:"Edit",icon:"pi pi-pencil",outlined:!0,onClick:()=>C(!0),tooltip:"Edit role, nameplate, and per-axis values. Type, serial, and install date are immutable.",tooltipOptions:{position:"left"}}),"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 S())}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:()=>{S()}}),_jsx(AssetEditDialog,{visible:v,asset:c,onHide:()=>C(!1),onSaved:async()=>{await n(),await S()}})]})};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)})})]})})]})};
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ export interface AssetEditDialogProps {
3
+ visible: boolean;
4
+ /** Full asset record as returned by `ams.read_asset`. The dialog
5
+ * pre-seeds every editable input from this; on submit it pins
6
+ * asset_id from here onto the update_asset call. */
7
+ asset: any | null;
8
+ onHide: () => void;
9
+ /** Fires after a successful update so the parent can refresh. */
10
+ onSaved?: () => void;
11
+ }
12
+ export declare const AssetEditDialog: React.FC<AssetEditDialogProps>;
13
+ //# sourceMappingURL=AssetEditDialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssetEditDialog.d.ts","sourceRoot":"","sources":["../../../src/components/ams/AssetEditDialog.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAmD,MAAM,OAAO,CAAC;AA+BxE,MAAM,WAAW,oBAAoB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB;;yDAEqD;IACrD,KAAK,EAAE,GAAG,GAAG,IAAI,CAAC;IAClB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AA0CD,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAyW1D,CAAC"}
@@ -0,0 +1 @@
1
+ import{jsx as _jsx,Fragment as _Fragment,jsxs as _jsxs}from"react/jsx-runtime";import React,{useContext,useEffect,useMemo,useState}from"react";import{Button}from"primereact/button";import{Dialog}from"primereact/dialog";import{Dropdown}from"primereact/dropdown";import{InputText}from"primereact/inputtext";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";import{useAms}from"./AmsProvider";const ROLE_OTHER="__other__";function fieldsFor(e,t){const s=e?.[t]?.fields;return Array.isArray(s)?s:[]}function subLocationsFor(e,t){const s=e?.[t]?.sub_locations;return s&&Array.isArray(s.keys)&&Array.isArray(s.fields)?s:null}function coerceField(e,t){if(void 0!==t&&""!==t)switch(e.type){case"f32":case"f64":case"i8":case"i16":case"i32":case"i64":case"u8":case"u16":case"u32":case"u64":{const e=Number(t);return Number.isFinite(e)?e:void 0}case"bool":return"true"===t||"1"===t;default:return t}}function seedFromCustom(e,t){if(!e)return"";const s=e[t];return null==s?"":String(s)}export const AssetEditDialog=({visible:e,asset:t,onHide:s,onSaved:r})=>{const{schemas:o,roles:n}=useAms(),{invoke:a}=useContext(EventEmitterContext),i=t?.asset_type??"",l=useMemo(()=>fieldsFor(o,i),[o,i]),c=useMemo(()=>subLocationsFor(o,i),[o,i]),d=useMemo(()=>i?n[i]??[]:[],[i,n]),m=d.length>0,u=useMemo(()=>{const e=d.map(e=>({label:e.label??e.location,value:e.location}));return e.push({label:"Other (advanced — type a custom role)",value:ROLE_OTHER}),e},[d]),[p,_]=useState(""),[f,h]=useState(""),[x,g]=useState({}),[y,j]=useState({}),[b,v]=useState(!1),[E,C]=useState(null);useEffect(()=>{if(!e||!t)return;C(null),v(!1);const s="string"==typeof t.location?t.location:"",r=d.find(e=>e.location===s);""===s?(_(""),h("")):r?(_(s),h(s)):(_(ROLE_OTHER),h(s));const o={};for(const e of l)o[e.name]=seedFromCustom(t.custom,e.name);g(o);const n={};if(c){const e=t.sub_locations&&"object"==typeof t.sub_locations?t.sub_locations:{};for(const t of c.keys){const s=e[t]&&"object"==typeof e[t]?e[t]:{},r={};for(const e of c.fields){const t=s[e.name];r[e.name]=null==t?"":String(t)}n[t]=r}}j(n)},[e,t,l,c,d]);const S=!t?.asset_id||b||m&&(""===p||p===ROLE_OTHER&&!f.trim())||l.some(e=>e.required&&!x[e.name]?.toString().trim())||(c?.keys.some(e=>c.fields.some(t=>t.required&&!y[e]?.[t.name]?.toString().trim()))??!1);if(!t)return null;const R=o[i]?.label??i;return _jsxs(Dialog,{header:`Edit Asset — ${t.asset_id}`,visible:e,style:{width:"32rem"},onHide:()=>{b||s()},footer:_jsxs(_Fragment,{children:[_jsx(Button,{label:"Cancel",severity:"secondary",onClick:s,disabled:b}),_jsx(Button,{label:"Save",icon:"pi pi-check",onClick:async()=>{if(!t?.asset_id)return;v(!0),C(null);const e={};for(const t of l){const s=coerceField(t,x[t.name]??"");void 0!==s&&(e[t.name]=s)}let o;if(c){o={};for(const e of c.keys){const t={};for(const s of c.fields){const r=coerceField(s,y[e]?.[s.name]??"");void 0!==r&&(t[s.name]=r)}o[e]=t}}const n={asset_id:t.asset_id,location:f,custom:e};o&&(n.sub_locations=o);try{const e=await a("ams.update_asset",MessageType.Request,n);e?.success?(r?.(),s()):C(e?.error_message??"update_asset failed")}catch(e){C(String(e?.message??e))}finally{v(!1)}},loading:b,disabled:S})]}),children:[_jsxs("div",{style:{display:"grid",gridTemplateColumns:"auto 1fr",gap:"0.25rem 1rem",fontSize:"0.875rem",color:"var(--text-secondary-color)",marginBottom:"0.75rem"},children:[_jsx("span",{children:"Type"})," ",_jsx("span",{children:R}),_jsx("span",{children:"Serial"})," ",_jsx("span",{children:t.serial||_jsx("em",{children:"(none)"})}),t.install_date&&_jsxs(_Fragment,{children:[_jsx("span",{children:"Installed"}),_jsx("span",{children:new Date(t.install_date).toLocaleString()})]})]}),_jsxs("div",{style:{display:"grid",gridTemplateColumns:"auto 1fr",gap:"0.5rem 1rem",alignItems:"center"},children:[m&&_jsxs(_Fragment,{children:[_jsx("label",{children:"Role *"}),_jsx(Dropdown,{value:p,options:u,onChange:e=>{var t;(t=e.value)===ROLE_OTHER?_(ROLE_OTHER):(_(t),h(t))},placeholder:"Choose where this asset is mounted"}),p===ROLE_OTHER&&_jsxs(_Fragment,{children:[_jsx("label",{children:"Custom role"}),_jsx(InputText,{value:f,placeholder:"e.g. tsdr_secondary",onChange:e=>h(e.target.value)})]})]}),l.map(e=>{const t=e.label??e.name,s=e.units?`${t} [${e.units}]${e.required?" *":""}`:`${t}${e.required?" *":""}`,r="string"!==e.type&&"bool"!==e.type;return _jsxs(React.Fragment,{children:[_jsx("label",{title:e.description??void 0,children:s}),"bool"===e.type?_jsx(Dropdown,{value:x[e.name]??"",options:[{label:"true",value:"true"},{label:"false",value:"false"}],onChange:t=>g(s=>({...s,[e.name]:t.value})),placeholder:"—"}):_jsx(InputText,{value:x[e.name]??"",keyfilter:r?"num":void 0,placeholder:e.description??void 0,onChange:t=>g(s=>({...s,[e.name]:t.target.value}))})]},e.name)})]}),c&&_jsxs("div",{style:{marginTop:"1.5rem"},children:[_jsx("h4",{style:{margin:"0 0 0.5rem 0"},children:c.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:c.key_label??"Key"}),c.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.required?" *":""]},e.name))]})}),_jsx("tbody",{children:c.keys.map(e=>_jsxs("tr",{children:[_jsx("td",{style:{padding:"0.25rem 0.5rem",fontWeight:600},children:e}),c.fields.map(t=>{const s=y[e]?.[t.name]??"",r="string"!==t.type&&"bool"!==t.type;return _jsx("td",{style:{padding:"0.25rem 0.5rem"},children:_jsx(InputText,{value:s,keyfilter:r?"num":void 0,onChange:s=>{const r=s.target.value;j(s=>({...s,[e]:{...s[e]??{},[t.name]:r}}))},style:{width:"100%"}})},t.name)})]},e))})]})})]}),E&&_jsx("div",{style:{marginTop:"1rem",color:"#ef4444"},children:E})]})};
@@ -3,6 +3,7 @@ export type { AmsRole, AmsRoleRegistry } from './AmsProvider';
3
3
  export { AssetRegistryTable } from './AssetRegistryTable';
4
4
  export { AssetDetailView } from './AssetDetailView';
5
5
  export { CalibrationEntryDialog } from './CalibrationEntryDialog';
6
+ export { AssetEditDialog } from './AssetEditDialog';
6
7
  export { SubLocationPicker } from './SubLocationPicker';
7
8
  export { PlaceholderHealthPanel } from './PlaceholderHealthPanel';
8
9
  export { MissingAssetsBanner } from './MissingAssetsBanner';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/ams/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC7H,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/ams/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC7H,YAAY,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -1 +1 @@
1
- export{AmsProvider,useAms,useAmsSchemas,useAmsRoles,useAmsAlerts,useAmsAssets,useAmsSelection}from"./AmsProvider";export{AssetRegistryTable}from"./AssetRegistryTable";export{AssetDetailView}from"./AssetDetailView";export{CalibrationEntryDialog}from"./CalibrationEntryDialog";export{SubLocationPicker}from"./SubLocationPicker";export{PlaceholderHealthPanel}from"./PlaceholderHealthPanel";export{MissingAssetsBanner}from"./MissingAssetsBanner";
1
+ export{AmsProvider,useAms,useAmsSchemas,useAmsRoles,useAmsAlerts,useAmsAssets,useAmsSelection}from"./AmsProvider";export{AssetRegistryTable}from"./AssetRegistryTable";export{AssetDetailView}from"./AssetDetailView";export{CalibrationEntryDialog}from"./CalibrationEntryDialog";export{AssetEditDialog}from"./AssetEditDialog";export{SubLocationPicker}from"./SubLocationPicker";export{PlaceholderHealthPanel}from"./PlaceholderHealthPanel";export{MissingAssetsBanner}from"./MissingAssetsBanner";
@@ -1 +1 @@
1
- {"version":3,"file":"NetworkPanel.d.ts","sourceRoot":"","sources":["../../../src/components/network/NetworkPanel.tsx"],"names":[],"mappings":"AAeA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAuBnD,MAAM,WAAW,iBAAiB;IAC9B,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA6TpD,CAAC;AAEF,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"NetworkPanel.d.ts","sourceRoot":"","sources":["../../../src/components/network/NetworkPanel.tsx"],"names":[],"mappings":"AAeA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAuBnD,MAAM,WAAW,iBAAiB;IAC9B,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAyUpD,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -1 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import React,{useEffect,useState}from"react";import{Button}from"primereact/button";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Dialog}from"primereact/dialog";import{Password}from"primereact/password";import{InputSwitch}from"primereact/inputswitch";import{ConfirmDialog,confirmDialog}from"primereact/confirmdialog";import{useNetwork}from"./NetworkProvider";const signalBars=e=>e>=75?"▰▰▰▰":e>=50?"▰▰▰▱":e>=25?"▰▰▱▱":e>0?"▰▱▱▱":"▱▱▱▱",isSecured=e=>{const i=(e??"").trim();return i.length>0&&"--"!==i};export const NetworkPanel=({className:e})=>{const i=useNetwork(),[s,t]=useState(null),[r,n]=useState(""),[a,l]=useState(!1);useEffect(()=>{0!==i.aps.length||i.scanning||i.scanWifi()},[]);const o=i.status.interfaces.filter(e=>"ethernet"===e.type),d=i.status.interfaces.filter(e=>"wifi"===e.type),c=d.find(e=>"connected"===e.state)??d[0];return _jsxs("div",{className:e,style:{width:"100%",maxWidth:"100%",boxSizing:"border-box"},children:[_jsx(ConfirmDialog,{}),_jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:"1rem",gap:"0.5rem",flexWrap:"wrap"},children:[_jsxs("div",{children:[_jsx("h3",{style:{margin:0},children:"Network"}),c?_jsxs("div",{style:{fontSize:"0.875rem",color:"#9ca3af",marginTop:"0.25rem"},children:["connected"===c.state?_jsxs(_Fragment,{children:["Connected to ",_jsx("code",{children:c.connection||"(unnamed)"})," on ",_jsx("code",{children:c.device})]}):_jsxs(_Fragment,{children:["WiFi: ",c.state]}),c.ip4.addresses.length>0&&_jsxs(_Fragment,{children:[" · ",c.ip4.addresses[0]]})]}):_jsx("div",{style:{fontSize:"0.875rem",color:"#9ca3af",marginTop:"0.25rem"},children:"No WiFi device detected."})]}),_jsxs("div",{style:{display:"flex",gap:"0.75rem",alignItems:"center",flexWrap:"wrap"},children:[_jsxs("label",{style:{display:"inline-flex",alignItems:"center",gap:"0.4rem"},children:[_jsx("span",{style:{fontSize:"0.875rem"},children:"WiFi radio"}),_jsx(InputSwitch,{checked:!!i.status.wifi_radio,onChange:e=>{i.setRadio(!!e.value)},disabled:null===i.status.wifi_radio})]}),_jsx(Button,{icon:"pi pi-refresh",label:"Refresh",size:"small",onClick:()=>{i.refreshStatus()}})]})]}),i.lastError&&_jsx("div",{style:{background:"#7f1d1d",color:"white",padding:"0.5rem 0.75rem",borderRadius:4,marginBottom:"1rem",fontSize:"0.875rem"},children:i.lastError}),_jsxs("div",{style:{marginBottom:"1.5rem",padding:"0.75rem 1rem",border:"1px solid #2a2a2a",borderRadius:4,background:"#161616"},children:[_jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:"0.75rem"},children:[_jsx("strong",{children:"WiFi Networks"}),_jsx(Button,{icon:i.scanning?"pi pi-spin pi-spinner":"pi pi-search",label:i.scanning?"Scanning…":"Scan",size:"small",outlined:!0,disabled:i.scanning||!1===i.status.wifi_radio,onClick:()=>{i.scanWifi()}})]}),_jsxs(DataTable,{value:i.aps,emptyMessage:!1===i.status.wifi_radio?"WiFi radio is off.":"No networks found yet. Click Scan to refresh.",scrollable:!0,scrollHeight:"20rem",tableStyle:{minWidth:0},style:{width:"100%"},children:[_jsx(Column,{header:"SSID",body:e=>_jsxs("span",{children:[e.in_use&&_jsx("i",{className:"pi pi-check",style:{marginRight:"0.4rem",color:"#22c55e"}}),e.ssid]}),style:{minWidth:"10rem"}}),_jsx(Column,{header:"Signal",body:e=>_jsxs("span",{style:{fontFamily:"monospace"},children:[signalBars(e.signal)," ",_jsxs("span",{style:{color:"#9ca3af"},children:[e.signal,"%"]})]}),style:{width:"10rem"},sortable:!0,sortField:"signal"}),_jsx(Column,{header:"Security",body:e=>_jsx("span",{children:isSecured(e.security)?e.security:"Open"}),style:{width:"8rem"}}),_jsx(Column,{header:"Action",style:{width:"9rem"},body:e=>_jsx(Button,{icon:"pi pi-link",label:e.in_use?"Reconnect":"Connect",size:"small",outlined:!0,disabled:!!i.staged,onClick:()=>(e=>{t(e),n("")})(e)})})]})]}),i.status.connections.length>0&&_jsxs("div",{style:{marginBottom:"1.5rem",padding:"0.75rem 1rem",border:"1px solid #2a2a2a",borderRadius:4,background:"#161616"},children:[_jsx("strong",{style:{display:"block",marginBottom:"0.75rem"},children:"Saved Connections"}),_jsxs(DataTable,{value:i.status.connections,emptyMessage:"No saved profiles.",tableStyle:{minWidth:0},style:{width:"100%"},children:[_jsx(Column,{field:"name",header:"Name",style:{minWidth:"10rem"}}),_jsx(Column,{field:"type",header:"Type",style:{width:"8rem"}}),_jsx(Column,{field:"device",header:"Device",style:{minWidth:"8rem"}}),_jsx(Column,{header:"Active",style:{width:"6rem"},body:e=>e.active?_jsx("i",{className:"pi pi-check",style:{color:"#22c55e"}}):null}),_jsx(Column,{header:"Action",style:{width:"8rem"},body:e=>"wifi"===e.type?_jsx(Button,{icon:"pi pi-trash",label:"Forget",size:"small",severity:"danger",outlined:!0,onClick:()=>{var s;confirmDialog({header:"Forget network",icon:"pi pi-exclamation-triangle",acceptLabel:"Forget",rejectLabel:"Cancel",acceptClassName:"p-button-danger",message:_jsxs("div",{children:[_jsxs("p",{style:{marginTop:0},children:["Remove saved profile ",_jsx("code",{children:(s=e).name}),"?"]}),_jsx("p",{style:{marginBottom:0,fontSize:"0.875rem",color:"#9ca3af"},children:"The password is deleted. You'll need to re-enter it next time you connect to this network."})]}),accept:()=>{i.forgetConnection(s.uuid)}})}}):null})]})]}),_jsxs("div",{style:{padding:"0.75rem 1rem",border:"1px solid #2a2a2a",borderRadius:4,background:"#161616"},children:[_jsx("strong",{style:{display:"block",marginBottom:"0.25rem"},children:"Wired Interfaces"}),_jsx("div",{style:{fontSize:"0.75rem",color:"#6b7280",marginBottom:"0.75rem"},children:"Wired configuration is managed by netplan and is read-only here."}),_jsxs(DataTable,{value:o,emptyMessage:"No wired interfaces.",tableStyle:{minWidth:0},style:{width:"100%"},children:[_jsx(Column,{field:"device",header:"Device",style:{minWidth:"8rem"}}),_jsx(Column,{field:"state",header:"State",style:{width:"8rem"}}),_jsx(Column,{header:"IP Address",body:e=>e.ip4.addresses.length>0?e.ip4.addresses.join(", "):_jsx("span",{style:{color:"#6b7280"},children:"—"}),style:{minWidth:"10rem"}}),_jsx(Column,{header:"Gateway",body:e=>e.ip4.gateway||_jsx("span",{style:{color:"#6b7280"},children:"—"}),style:{minWidth:"8rem"}}),_jsx(Column,{header:"DNS",body:e=>e.ip4.dns.length>0?e.ip4.dns.join(", "):_jsx("span",{style:{color:"#6b7280"},children:"—"}),style:{minWidth:"8rem"}})]})]}),_jsx(Dialog,{header:s?`Connect to ${s.ssid}`:"Connect",visible:!!s,style:{width:"24rem"},onHide:()=>{a||t(null)},modal:!0,draggable:!1,children:s&&_jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"0.75rem"},children:[_jsxs("div",{style:{fontSize:"0.875rem",color:"#9ca3af"},children:[_jsxs("div",{children:["Signal: ",s.signal,"%"]}),_jsxs("div",{children:["Security: ",isSecured(s.security)?s.security:"Open"]})]}),isSecured(s.security)?_jsxs(_Fragment,{children:[_jsx("label",{htmlFor:"nw-pw",style:{fontSize:"0.875rem"},children:"Password"}),_jsx(Password,{inputId:"nw-pw",value:r,onChange:e=>n(e.target.value),feedback:!1,toggleMask:!0,autoFocus:!0})]}):_jsx("div",{style:{fontSize:"0.875rem"},children:"This is an open network. No password required."}),_jsx("div",{style:{background:"#78350f",color:"white",padding:"0.5rem 0.75rem",borderRadius:4,fontSize:"0.875rem"},children:"After connecting, you have 60 seconds to confirm the change. If you don't, the server will auto-revert to the previous network — that way a bad password from a remote session can't lock you out."}),_jsxs("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.4rem"},children:[_jsx(Button,{label:"Cancel",outlined:!0,onClick:()=>t(null),disabled:a}),_jsx(Button,{label:a?"Connecting…":"Connect",icon:a?"pi pi-spin pi-spinner":"pi pi-link",onClick:()=>{(async()=>{if(s){l(!0);try{const e=isSecured(s.security)?r:void 0;await i.wifiConnect(s.ssid,e)&&(t(null),n(""))}finally{l(!1)}}})()},disabled:a||isSecured(s.security)&&0===r.length})]})]})})]})};export default NetworkPanel;
1
+ import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import React,{useEffect,useState}from"react";import{Button}from"primereact/button";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Dialog}from"primereact/dialog";import{Password}from"primereact/password";import{InputSwitch}from"primereact/inputswitch";import{ConfirmDialog,confirmDialog}from"primereact/confirmdialog";import{useNetwork}from"./NetworkProvider";const signalBars=e=>e>=75?"▰▰▰▰":e>=50?"▰▰▰▱":e>=25?"▰▰▱▱":e>0?"▰▱▱▱":"▱▱▱▱",isSecured=e=>{const i=(e??"").trim();return i.length>0&&"--"!==i};export const NetworkPanel=({className:e})=>{const i=useNetwork(),[s,t]=useState(null),[r,n]=useState(""),[a,l]=useState(!1);useEffect(()=>{0!==i.aps.length||i.scanning||i.scanWifi()},[]);const o=i.status.interfaces.filter(e=>"ethernet"===e.type),d=i.status.interfaces.filter(e=>"wifi"===e.type),c=d.find(e=>"connected"===e.state)??d[0];return _jsxs("div",{className:e,style:{width:"100%",maxWidth:"100%",boxSizing:"border-box"},children:[_jsx(ConfirmDialog,{}),_jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:"1rem",gap:"0.5rem",flexWrap:"wrap"},children:[_jsxs("div",{children:[_jsx("h3",{style:{margin:0},children:"Network"}),i.statusLoaded?c?_jsxs("div",{style:{fontSize:"0.875rem",color:"#9ca3af",marginTop:"0.25rem"},children:["connected"===c.state?_jsxs(_Fragment,{children:["Connected to ",_jsx("code",{children:c.connection||"(unnamed)"})," on ",_jsx("code",{children:c.device})]}):_jsxs(_Fragment,{children:["WiFi: ",c.state]}),c.ip4.addresses.length>0&&_jsxs(_Fragment,{children:[" · ",c.ip4.addresses[0]]})]}):_jsx("div",{style:{fontSize:"0.875rem",color:"#9ca3af",marginTop:"0.25rem"},children:"No WiFi device detected."}):_jsx("div",{style:{fontSize:"0.875rem",color:"#9ca3af",marginTop:"0.25rem"},children:"Loading network status…"})]}),_jsxs("div",{style:{display:"flex",gap:"0.75rem",alignItems:"center",flexWrap:"wrap"},children:[_jsxs("label",{style:{display:"inline-flex",alignItems:"center",gap:"0.4rem"},children:[_jsx("span",{style:{fontSize:"0.875rem"},children:"WiFi radio"}),_jsx(InputSwitch,{checked:!!i.status.wifi_radio,onChange:e=>{i.setRadio(!!e.value)},disabled:null===i.status.wifi_radio})]}),_jsx(Button,{icon:"pi pi-refresh",label:"Refresh",size:"small",onClick:()=>{i.refreshStatus()}})]})]}),i.lastError&&_jsx("div",{style:{background:"#7f1d1d",color:"white",padding:"0.5rem 0.75rem",borderRadius:4,marginBottom:"1rem",fontSize:"0.875rem"},children:i.lastError}),_jsxs("div",{style:{marginBottom:"1.5rem",padding:"0.75rem 1rem",border:"1px solid #2a2a2a",borderRadius:4,background:"#161616"},children:[_jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:"0.75rem"},children:[_jsx("strong",{children:"WiFi Networks"}),_jsx(Button,{icon:i.scanning?"pi pi-spin pi-spinner":"pi pi-search",label:i.scanning?"Scanning…":"Scan",size:"small",outlined:!0,disabled:i.scanning||!1===i.status.wifi_radio,onClick:()=>{i.scanWifi()}})]}),_jsxs(DataTable,{value:i.aps,emptyMessage:!1===i.status.wifi_radio?"WiFi radio is off.":"No networks found yet. Click Scan to refresh.",scrollable:!0,scrollHeight:"20rem",tableStyle:{minWidth:0},style:{width:"100%"},children:[_jsx(Column,{header:"SSID",body:e=>_jsxs("span",{children:[e.in_use&&_jsx("i",{className:"pi pi-check",style:{marginRight:"0.4rem",color:"#22c55e"}}),e.ssid]}),style:{minWidth:"10rem"}}),_jsx(Column,{header:"Signal",body:e=>_jsxs("span",{style:{fontFamily:"monospace"},children:[signalBars(e.signal)," ",_jsxs("span",{style:{color:"#9ca3af"},children:[e.signal,"%"]})]}),style:{width:"10rem"},sortable:!0,sortField:"signal"}),_jsx(Column,{header:"Security",body:e=>_jsx("span",{children:isSecured(e.security)?e.security:"Open"}),style:{width:"8rem"}}),_jsx(Column,{header:"Action",style:{width:"9rem"},body:e=>_jsx(Button,{icon:"pi pi-link",label:e.in_use?"Reconnect":"Connect",size:"small",outlined:!0,disabled:!!i.staged,onClick:()=>(e=>{t(e),n("")})(e)})})]})]}),i.status.connections.length>0&&_jsxs("div",{style:{marginBottom:"1.5rem",padding:"0.75rem 1rem",border:"1px solid #2a2a2a",borderRadius:4,background:"#161616"},children:[_jsx("strong",{style:{display:"block",marginBottom:"0.75rem"},children:"Saved Connections"}),_jsxs(DataTable,{value:i.status.connections,emptyMessage:"No saved profiles.",tableStyle:{minWidth:0},style:{width:"100%"},children:[_jsx(Column,{field:"name",header:"Name",style:{minWidth:"10rem"}}),_jsx(Column,{field:"type",header:"Type",style:{width:"8rem"}}),_jsx(Column,{field:"device",header:"Device",style:{minWidth:"8rem"}}),_jsx(Column,{header:"Active",style:{width:"6rem"},body:e=>e.active?_jsx("i",{className:"pi pi-check",style:{color:"#22c55e"}}):null}),_jsx(Column,{header:"Action",style:{width:"8rem"},body:e=>"wifi"===e.type?_jsx(Button,{icon:"pi pi-trash",label:"Forget",size:"small",severity:"danger",outlined:!0,onClick:()=>{var s;confirmDialog({header:"Forget network",icon:"pi pi-exclamation-triangle",acceptLabel:"Forget",rejectLabel:"Cancel",acceptClassName:"p-button-danger",message:_jsxs("div",{children:[_jsxs("p",{style:{marginTop:0},children:["Remove saved profile ",_jsx("code",{children:(s=e).name}),"?"]}),_jsx("p",{style:{marginBottom:0,fontSize:"0.875rem",color:"#9ca3af"},children:"The password is deleted. You'll need to re-enter it next time you connect to this network."})]}),accept:()=>{i.forgetConnection(s.uuid)}})}}):null})]})]}),_jsxs("div",{style:{padding:"0.75rem 1rem",border:"1px solid #2a2a2a",borderRadius:4,background:"#161616"},children:[_jsx("strong",{style:{display:"block",marginBottom:"0.25rem"},children:"Wired Interfaces"}),_jsx("div",{style:{fontSize:"0.75rem",color:"#6b7280",marginBottom:"0.75rem"},children:"Wired configuration is managed by netplan and is read-only here."}),_jsxs(DataTable,{value:o,emptyMessage:"No wired interfaces.",tableStyle:{minWidth:0},style:{width:"100%"},children:[_jsx(Column,{field:"device",header:"Device",style:{minWidth:"8rem"}}),_jsx(Column,{field:"state",header:"State",style:{width:"8rem"}}),_jsx(Column,{header:"IP Address",body:e=>e.ip4.addresses.length>0?e.ip4.addresses.join(", "):_jsx("span",{style:{color:"#6b7280"},children:"—"}),style:{minWidth:"10rem"}}),_jsx(Column,{header:"Gateway",body:e=>e.ip4.gateway||_jsx("span",{style:{color:"#6b7280"},children:"—"}),style:{minWidth:"8rem"}}),_jsx(Column,{header:"DNS",body:e=>e.ip4.dns.length>0?e.ip4.dns.join(", "):_jsx("span",{style:{color:"#6b7280"},children:"—"}),style:{minWidth:"8rem"}})]})]}),_jsx(Dialog,{header:s?`Connect to ${s.ssid}`:"Connect",visible:!!s,style:{width:"24rem"},onHide:()=>{a||t(null)},modal:!0,draggable:!1,children:s&&_jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"0.75rem"},children:[_jsxs("div",{style:{fontSize:"0.875rem",color:"#9ca3af"},children:[_jsxs("div",{children:["Signal: ",s.signal,"%"]}),_jsxs("div",{children:["Security: ",isSecured(s.security)?s.security:"Open"]})]}),isSecured(s.security)?_jsxs(_Fragment,{children:[_jsx("label",{htmlFor:"nw-pw",style:{fontSize:"0.875rem"},children:"Password"}),_jsx(Password,{inputId:"nw-pw",value:r,onChange:e=>n(e.target.value),feedback:!1,toggleMask:!0,autoFocus:!0})]}):_jsx("div",{style:{fontSize:"0.875rem"},children:"This is an open network. No password required."}),_jsx("div",{style:{background:"#78350f",color:"white",padding:"0.5rem 0.75rem",borderRadius:4,fontSize:"0.875rem"},children:"After connecting, you have 60 seconds to confirm the change. If you don't, the server will auto-revert to the previous network — that way a bad password from a remote session can't lock you out."}),_jsxs("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.4rem"},children:[_jsx(Button,{label:"Cancel",outlined:!0,onClick:()=>t(null),disabled:a}),_jsx(Button,{label:a?"Connecting…":"Connect",icon:a?"pi pi-spin pi-spinner":"pi pi-link",onClick:()=>{(async()=>{if(s){l(!0);try{const e=isSecured(s.security)?r:void 0;await i.wifiConnect(s.ssid,e)&&(t(null),n(""))}finally{l(!1)}}})()},disabled:a||isSecured(s.security)&&0===r.length})]})]})})]})};export default NetworkPanel;
@@ -5,6 +5,11 @@ export interface TestFieldDef {
5
5
  units?: string;
6
6
  required?: boolean;
7
7
  source?: string;
8
+ /** Optional display-time scale multiplier. `display = raw * scale`,
9
+ * default 1.0 = no conversion. Cycle Data and Results panels
10
+ * apply this when formatting numeric cells. Charts plot raw
11
+ * values (axis labels already carry units). Storage stays raw. */
12
+ scale?: number;
8
13
  }
9
14
  export interface ChartAxis {
10
15
  field?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"TestDataView.d.ts","sourceRoot":"","sources":["../../../src/components/tis/TestDataView.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAwE,MAAM,OAAO,CAAC;AA8B7F,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IAAI,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CAAE;AAChF,MAAM,WAAW,WAAW;IAAG,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAAE;AAC5G,MAAM,WAAW,SAAS;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,eAAe,GAAG,WAAW,CAAC;IACpC,CAAC,EAAE,SAAS,CAAC;IACb,CAAC,EAAE,WAAW,EAAE,CAAC;CACpB;AACD,MAAM,WAAW,SAAS;IAAG,MAAM,EAAE,MAAM,CAAC;CAAE;AAC9C,MAAM,WAAW,YAAY;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB;;8CAE0C;IAC1C,OAAO,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IACtC,KAAK,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;CACrC;AACD,MAAM,WAAW,UAAU;IACvB,cAAc,EAAG,YAAY,EAAE,CAAC;IAChC,aAAa,EAAI,YAAY,EAAE,CAAC;IAChC,YAAY,EAAK,YAAY,EAAE,CAAC;IAChC,cAAc,EAAG,YAAY,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAQ,YAAY,GAAG,IAAI,CAAC;IACrC,KAAK,CAAC,EAAW;QAAE,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC/C,wDAAwD;IACxD,KAAK,CAAC,EAAW,MAAM,CAAC;IACxB,qDAAqD;IACrD,WAAW,CAAC,EAAK,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAC9B,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,QAAQ,CAAC,EAAG,MAAM,CAAC;IACnB,gEAAgE;IAChE,KAAK,CAAC,EAAM,MAAM,CAAC;IACnB,kEAAkE;IAClE,MAAM,CAAC,EAAK,UAAU,CAAC;IACvB,8EAA8E;IAC9E,UAAU,CAAC,EAAG,MAAM,CAAC;IACrB,wDAAwD;IACxD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;uEACmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAID,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAygBpD,CAAC"}
1
+ {"version":3,"file":"TestDataView.d.ts","sourceRoot":"","sources":["../../../src/components/tis/TestDataView.tsx"],"names":[],"mappings":"AA0BA,OAAO,KAAwE,MAAM,OAAO,CAAC;AA8B7F,MAAM,WAAW,YAAY;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;uEAGmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IAAI,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CAAE;AAChF,MAAM,WAAW,WAAW;IAAG,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAAE;AAC5G,MAAM,WAAW,SAAS;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,eAAe,GAAG,WAAW,CAAC;IACpC,CAAC,EAAE,SAAS,CAAC;IACb,CAAC,EAAE,WAAW,EAAE,CAAC;CACpB;AACD,MAAM,WAAW,SAAS;IAAG,MAAM,EAAE,MAAM,CAAC;CAAE;AAC9C,MAAM,WAAW,YAAY;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB;;8CAE0C;IAC1C,OAAO,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IACtC,KAAK,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;CACrC;AACD,MAAM,WAAW,UAAU;IACvB,cAAc,EAAG,YAAY,EAAE,CAAC;IAChC,aAAa,EAAI,YAAY,EAAE,CAAC;IAChC,YAAY,EAAK,YAAY,EAAE,CAAC;IAChC,cAAc,EAAG,YAAY,EAAE,CAAC;IAChC,QAAQ,CAAC,EAAQ,YAAY,GAAG,IAAI,CAAC;IACrC,KAAK,CAAC,EAAW;QAAE,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC/C,wDAAwD;IACxD,KAAK,CAAC,EAAW,MAAM,CAAC;IACxB,qDAAqD;IACrD,WAAW,CAAC,EAAK,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAC9B,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,QAAQ,CAAC,EAAG,MAAM,CAAC;IACnB,gEAAgE;IAChE,KAAK,CAAC,EAAM,MAAM,CAAC;IACnB,kEAAkE;IAClE,MAAM,CAAC,EAAK,UAAU,CAAC;IACvB,8EAA8E;IAC9E,UAAU,CAAC,EAAG,MAAM,CAAC;IACrB,wDAAwD;IACxD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;uEACmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAID,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAgjBpD,CAAC"}
@@ -1 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import React,{useCallback,useContext,useEffect,useMemo,useRef,useState}from"react";import{Button}from"primereact/button";import{Column}from"primereact/column";import{DataTable}from"primereact/datatable";import{Dialog}from"primereact/dialog";import{Dropdown}from"primereact/dropdown";import{TabView,TabPanel}from"primereact/tabview";import{Chart as ChartJS,CategoryScale,LinearScale,PointElement,LineElement,Title,Tooltip,Legend}from"chart.js";import zoomPlugin from"chartjs-plugin-zoom";import{Line}from"react-chartjs-2";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";import{useTis}from"./TisProvider";import{useRawCycleData}from"./useRawCycleData";ChartJS.register(CategoryScale,LinearScale,PointElement,LineElement,Title,Tooltip,Legend,zoomPlugin);export const TestDataView=e=>{const t=useTis(),r=e.projectId??t.selection.projectId,a=e.methodId??t.selection.methodId,l=e.runId??t.selection.runId,s=e.schema??(a?t.schemas[a]:void 0),{throttleMs:n=100,cycleTableHeight:i="400px",chartHeight:o="320px"}=e,{invoke:c,subscribe:d,unsubscribe:u}=useContext(EventEmitterContext),[m,p]=useState(null),[y,h]=useState([]),[f,g]=useState({}),[x,_]=useState(!1),[b,j]=useState(!1),[v,w]=useState(null),[C,S]=useState(null),[T,R]=useState(!1),[N,D]=useState(null),[I,L]=useState(null),[E,O]=useState(!1),[k,A]=useState([]),[M,F]=useState(null),H=useMemo(()=>{const e=[];for(const[t,r]of Object.entries(s?.views??{}))e.push({name:t,view:r});return e},[s]),[z,P]=useState(H.length>0?H[0].name:null);useEffect(()=>{if(0===H.length)return;null!==z&&H.some(e=>e.name===z)||P(H[0].name)},[H,z]);const $=H.find(e=>e.name===z)?.view,V="raw_trace"===$?.type,B=useRef([]),q=useRef(null),W=useRef(null),J=()=>{W.current||(W.current=setTimeout(()=>{if(W.current=null,B.current.length>0){const e=B.current;B.current=[],h(t=>[...e.slice().reverse(),...t])}q.current&&(g(q.current),q.current=null)},n))};useEffect(()=>{if(!r||!a||!l)return p(null),h([]),void g({});let e=!1;return(async()=>{try{const t=await c("tis.read_test",MessageType.Request,{project_id:r,method_id:a,run_id:l});!e&&t?.success&&(p(t.data),g(t.data.results??{}));const s=await c("tis.read_cycles",MessageType.Request,{project_id:r,method_id:a,run_id:l,offset:0,limit:200,order:"desc"});!e&&s?.success&&h(s.data.cycles??[])}catch(e){}})(),()=>{e=!0}},[r,a,l,c]),useEffect(()=>{const e=e=>e?.project_id===r&&e?.method_id===a&&e?.run_id===l,t=d("tis.cycle_added",t=>{e(t)&&t.cycle&&(B.current.push(t.cycle),J())}),s=d("tis.results_updated",t=>{e(t)&&(q.current=t.results??{},J())});return()=>{u(t),u(s),W.current&&(clearTimeout(W.current),W.current=null)}},[r,a,l,n]);const G=useRawCycleData({projectId:r,methodId:a,runId:l,blobName:s?.raw_data?.blob_name??"trace",enabled:V}),Y=useMemo(()=>{if(!$)return null;if("cycle_scatter"===$.type){const e=$.x.field;if(!e)return null;const t=[...y].reverse();return{labels:t.map(t=>t[e]),datasets:$.y.map((e,r)=>({label:e.label??e.field,data:t.map(t=>t[e.field]),yAxisID:"right"===e.y_axis?"y1":"y",borderColor:palette(r),backgroundColor:palette(r),tension:.1,pointRadius:2}))}}if("raw_trace"===$.type){if(!G.raw)return null;const e=$.x.column;if(!e)return null;const t=G.raw[e]??[];return{datasets:$.y.map((e,r)=>({label:e.label??e.column,data:(G.raw[e.column]??[]).map((e,r)=>({x:t[r],y:e})),yAxisID:"right"===e.y_axis?"y1":"y",borderColor:palette(r),backgroundColor:palette(r),pointRadius:0,borderWidth:1.5,showLine:!0}))}}return null},[$,y,G.raw]),K=$?.y.some(e=>"right"===e.y_axis)??!1,Q=useMemo(()=>{const e="raw_trace"===$?.type;return{responsive:!0,maintainAspectRatio:!1,parsing:!e&&void 0,scales:{x:e?{type:"linear",title:{display:!!$?.x.label,text:$?.x.label}}:{title:{display:!!$?.x.label,text:$?.x.label}},y:{position:"left",title:{display:!0,text:leftAxisLabel($)}},...K?{y1:{position:"right",grid:{drawOnChartArea:!1},title:{display:!0,text:rightAxisLabel($)}}}:{}},plugins:{legend:{display:!0},zoom:{pan:{enabled:!0,mode:"xy"},zoom:{wheel:{enabled:!0},pinch:{enabled:!0},mode:"xy"}}}}},[$,K]),U=s?.raw_data?.blob_name??"trace",X=useRef(""),Z=useCallback(async()=>{if(!r||!a||!l)return[];try{const e=await c("tis.list_raw",MessageType.Request,{project_id:r,method_id:a,run_id:l});if(!e?.success)return[];const t=e.data?.cycles??[];return t.filter(e=>e?.name===U&&"number"==typeof e?.cycle_index).map(e=>e.cycle_index).sort((e,t)=>e-t)}catch{return[]}},[r,a,l,U,c]),ee=useCallback(async e=>{if(!r||!a||!l)return;const t=`${r}|${a}|${l}|${U}|${e??"latest"}`;if(X.current===t)return;X.current=t;const s={project_id:r,method_id:a,run_id:l,name:U};null!=e&&(s.cycle_index=e),R(!0),S(null),w(null);try{const e=await c("tis.read_raw",MessageType.Request,s);e?.success?w(e.data??{}):S(e?.error_message??"No raw data on disk for this run.")}catch(e){S(String(e?.message??e))}finally{R(!1)}O(!0),L(null),D(null);try{const e=await c("tis.read_filtered",MessageType.Request,{project_id:r,method_id:a,run_id:l,name:U});e?.success?D(e.data??{}):L(e?.error_message??"No filtered data on disk for this run.")}catch(e){L(String(e?.message??e))}finally{O(!1)}},[r,a,l,U,c]);useEffect(()=>{X.current="",A([]),F(null)},[r,a,l,U]);return useEffect(()=>{x&&null!=M&&ee(M)},[x,M,ee]),r&&a&&l&&s?_jsxs("div",{className:"vblock",style:{display:"flex",flexDirection:"column",gap:"1rem"},children:[_jsx(Header,{meta:m,config:m?.config,runId:l,projectId:r,methodId:a,canViewRaw:!!s.raw_data,onViewRaw:async()=>{_(!0);let e=k;0===e.length&&(e=await Z(),A(e));const t=e.length>0?e[e.length-1]:null,r=M??t;M!==r&&F(r),await ee(r)},onShowConfig:()=>j(!0)}),_jsxs("div",{className:"p-card",style:{padding:"1rem"},children:[_jsxs("div",{className:"flex",style:{gap:"1rem",alignItems:"center",marginBottom:"0.5rem",flexWrap:"wrap"},children:[_jsx(Dropdown,{value:z,options:H.map(e=>({label:e.view.title??e.name,value:e.name})),onChange:e=>P(e.value),placeholder:0===H.length?"No view defined":"Select a view",disabled:0===H.length}),_jsx("h3",{style:{margin:0},children:$?.title??""}),V&&G.cycles.length>1&&_jsxs(_Fragment,{children:[_jsx("label",{htmlFor:"chart-cycle-picker",style:{color:"var(--text-secondary-color)"},children:"Cycle:"}),_jsx(Dropdown,{inputId:"chart-cycle-picker",value:G.selectedCycle,options:G.cycles.map(e=>({label:`Cycle ${e}`,value:e})),onChange:e=>G.setSelectedCycle(Number(e.value)),style:{minWidth:"8rem"}}),_jsxs("span",{style:{color:"var(--text-secondary-color)"},children:["of ",G.cycles.length]})]})]}),_jsxs("div",{style:{height:o,position:"relative"},children:[V&&G.loading&&_jsx(ChartOverlay,{children:"Loading raw data…"}),V&&G.error&&_jsx(ChartOverlay,{children:G.error}),Y&&_jsx(Line,{data:Y,options:Q})]})]}),_jsxs("div",{className:"p-card",style:{padding:"1rem"},children:[_jsxs("h3",{style:{marginTop:0},children:["Cycle Data (",y.length,")"]}),_jsx(DataTable,{value:y,scrollable:!0,scrollHeight:i,virtualScrollerOptions:{itemSize:38},emptyMessage:"No cycles yet.",children:s.cycle_fields.map(e=>_jsx(Column,{field:e.name,header:e.units?`${e.name} (${e.units})`:e.name,body:t=>formatCell(t[e.name],e.type)},e.name))})]}),_jsxs("div",{className:"p-card",style:{padding:"1rem"},children:[_jsx("h3",{style:{marginTop:0},children:"Results"}),_jsx(ResultsGrid,{schema:s.results_fields,values:f})]}),s.raw_data&&_jsxs(Dialog,{visible:x,onHide:()=>_(!1),header:`Run Data — ${l}`,style:{width:"90vw",height:"80vh"},maximizable:!0,children:[_jsx(CyclePickerBar,{cycles:k,selected:M,onChange:F}),_jsx(RawEnvelopeHeader,{envelope:v}),_jsxs(TabView,{style:{height:"100%"},children:[_jsx(TabPanel,{header:"Raw Data",children:_jsx(DataBlobTable,{blob:unwrapEnvelope(v),loading:T,error:C,rawData:s.raw_data})}),_jsx(TabPanel,{header:"Filtered Data",children:_jsx(DataBlobTable,{blob:unwrapEnvelope(N),loading:E,error:I,rawData:s.raw_data,emptyMessage:"Filtered data is written by post-processing — none on disk for this run yet."})})]})]}),_jsx(Dialog,{visible:b,onHide:()=>j(!1),header:"Test Configuration",style:{width:"min(640px, 90vw)"},modal:!0,children:_jsx(ConfigList,{config:m?.config})})]}):_jsx("div",{className:"p-card",style:{padding:"1rem",color:"var(--text-secondary-color)"},children:"No test selected. Pick a row from the History tab or start a run."})};const Header=({meta:e,config:t,runId:r,projectId:a,methodId:l,canViewRaw:s,onViewRaw:n,onShowConfig:i})=>{const o="string"==typeof e?.sample_id&&e.sample_id||"string"==typeof e?.config?.sample_id&&e.config.sample_id||"",c=t&&"object"==typeof t&&Object.entries(t).some(([e])=>"sample_id"!==e);return _jsx("div",{className:"p-card",style:{padding:"1rem"},children:_jsxs("div",{className:"flex",style:{justifyContent:"space-between",alignItems:"flex-start",gap:"1rem"},children:[_jsxs("div",{children:[_jsxs("h2",{style:{margin:0,display:"flex",alignItems:"center",gap:"0.5rem"},children:[o||r,c&&_jsx(Button,{icon:"pi pi-info-circle",type:"button",rounded:!0,text:!0,onClick:i,tooltip:"Show test configuration",tooltipOptions:{position:"top"},style:{width:"2rem",height:"2rem",padding:0},"aria-label":"Show test configuration"})]}),_jsxs("div",{style:{color:"var(--text-secondary-color)",fontSize:"0.85em"},children:["project: ",a," · method: ",l," · run: ",r,e?.start_time&&_jsxs(_Fragment,{children:[" · started: ",new Date(e.start_time).toLocaleString()]})]})]}),s&&_jsx(Button,{icon:"pi pi-table",label:"View Raw Data",onClick:n,outlined:!0})]})})},ConfigList=({config:e})=>{const t=e&&"object"==typeof e?Object.entries(e).filter(([e])=>"sample_id"!==e):[];return 0===t.length?_jsx("div",{style:{color:"var(--text-secondary-color)"},children:"No configuration recorded for this run."}):_jsx("div",{style:{display:"grid",gridTemplateColumns:"auto 1fr",gap:"0.5rem 1rem",fontSize:"0.95em"},children:t.map(([e,t])=>_jsxs(React.Fragment,{children:[_jsx("div",{style:{color:"var(--text-secondary-color)"},children:e}),_jsx("div",{children:formatCell(t,"string")})]},e))})},unwrapEnvelope=e=>e&&"object"==typeof e&&"data"in e&&e.data&&"object"==typeof e.data&&Object.values(e.data).some(e=>Array.isArray(e))?e.data:e,CyclePickerBar=({cycles:e,selected:t,onChange:r})=>e.length<=1?null:_jsxs("div",{style:{display:"flex",alignItems:"center",gap:"0.5rem",padding:"0.25rem 0.5rem 0.5rem"},children:[_jsx("label",{htmlFor:"raw-cycle-picker",style:{color:"var(--text-secondary-color)"},children:"Cycle:"}),_jsx(Dropdown,{inputId:"raw-cycle-picker",value:t,options:e.map(e=>({label:`Cycle ${e}`,value:e})),onChange:e=>r(Number(e.value)),style:{minWidth:"8rem"}}),_jsxs("span",{style:{color:"var(--text-secondary-color)"},children:["(",e.length," cycles recorded)"]})]}),RawEnvelopeHeader=({envelope:e})=>{if(!e||"object"!=typeof e)return null;const t=e.cycle_index,r=e.cycle_fields,a=e.context;if(null==t&&!r&&!a)return null;const l=e=>{if(!e||"object"!=typeof e)return null;const t=Object.entries(e).filter(([,e])=>null!==e&&"object"!=typeof e);return 0===t.length?null:t.map(([e,t])=>_jsxs("span",{style:{marginRight:"1rem"},children:[_jsxs("span",{style:{color:"var(--text-secondary-color)"},children:[e,": "]}),_jsx("span",{children:String(t)})]},e))};return _jsxs("div",{style:{padding:"0.5rem",borderBottom:"1px solid var(--surface-border)",fontSize:"0.9rem"},children:[null!=t&&_jsx("div",{children:_jsxs("strong",{children:["Cycle ",t]})}),r&&_jsx("div",{style:{marginTop:"0.25rem"},children:l(r)}),a&&_jsx("div",{style:{marginTop:"0.25rem"},children:l(a)})]})},DataBlobTable=({blob:e,loading:t,error:r,rawData:a,emptyMessage:l})=>{if(t)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:"Loading…"});if(r)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:l??r});if(!e||"object"!=typeof e)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:"No data."});const s=Object.keys(a.columns??{}),n=Object.keys(e).filter(t=>Array.isArray(e[t])),i=[];for(const t of s)Array.isArray(e[t])&&i.push(t);for(const e of n)i.includes(e)||i.push(e);if(0===i.length)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:l??"No columnar data in this blob."});const o=i.reduce((t,r)=>Math.min(t,e[r].length),Number.POSITIVE_INFINITY),c=Number.isFinite(o)?o:0,d=Array.from({length:c},(t,r)=>{const a={__i:r};for(const t of i)a[t]=e[t][r];return a}),u=e=>{const t=a.units?.[e];return t?`${e} [${t}]`:e};return _jsxs(DataTable,{value:d,scrollable:!0,scrollHeight:"60vh",virtualScrollerOptions:{itemSize:32},emptyMessage:l??"No data.",size:"small",stripedRows:!0,children:[_jsx(Column,{field:"__i",header:"#",style:{width:"5rem",textAlign:"right"},bodyStyle:{fontVariantNumeric:"tabular-nums",textAlign:"right"}}),i.map(e=>_jsx(Column,{field:e,header:u(e),style:{minWidth:"8rem"},bodyStyle:{fontVariantNumeric:"tabular-nums",textAlign:"right"},body:t=>formatNumeric(t[e])},e))]})},formatNumeric=e=>null==e?"":"number"==typeof e&&Number.isFinite(e)?Number.parseFloat(e.toPrecision(6)).toString():String(e),ResultsGrid=({schema:e,values:t})=>t&&0!==Object.keys(t).length?_jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(220px, 1fr))",gap:"0.5rem 1rem"},children:e.map(e=>_jsxs("div",{children:[_jsxs("div",{style:{fontSize:"0.8em",color:"var(--text-secondary-color)"},children:[e.name,e.units?` (${e.units})`:""]}),_jsx("div",{children:formatCell(t[e.name],e.type)})]},e.name))}):_jsx("div",{style:{color:"var(--text-secondary-color)"},children:"No results yet."}),CHART_COLORS=["#4ea8de","#f59e0b","#22c55e","#a855f7","#ef4444","#14b8a6","#eab308","#ec4899"],palette=e=>CHART_COLORS[e%CHART_COLORS.length],ChartOverlay=({children:e})=>_jsx("div",{style:{position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",color:"var(--text-secondary-color)",pointerEvents:"none"},children:e}),seriesLabel=e=>e.label??e.field??e.column??"",leftAxisLabel=e=>e?.y.filter(e=>"right"!==e.y_axis).map(seriesLabel).join(" / ")??"",rightAxisLabel=e=>e?.y.filter(e=>"right"===e.y_axis).map(seriesLabel).join(" / ")??"",formatCell=(e,t)=>null==e?"":"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{Line}from"react-chartjs-2";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";import{useTis}from"./TisProvider";import{useRawCycleData}from"./useRawCycleData";ChartJS.register(CategoryScale,LinearScale,PointElement,LineElement,Title,Tooltip,Legend,zoomPlugin);export const TestDataView=e=>{const t=useTis(),r=e.projectId??t.selection.projectId,a=e.methodId??t.selection.methodId,l=e.runId??t.selection.runId,s=e.schema??(a?t.schemas[a]:void 0),{throttleMs:n=100,cycleTableHeight:i="400px",chartHeight:o="320px"}=e,{invoke:c,subscribe:d,unsubscribe:u}=useContext(EventEmitterContext),[m,p]=useState(null),[y,h]=useState([]),[f,g]=useState({}),[x,_]=useState(!1),[b,j]=useState(!1),v=useRef(null),[w,C]=useState(null),[S,R]=useState(null),[T,N]=useState(!1),[D,I]=useState(null),[L,E]=useState(null),[O,k]=useState(!1),[A,H]=useState([]),[M,z]=useState(null),F=useMemo(()=>{const e=[];for(const[t,r]of Object.entries(s?.views??{}))e.push({name:t,view:r});return e},[s]),[P,V]=useState(F.length>0?F[0].name:null);useEffect(()=>{if(0===F.length)return;null!==P&&F.some(e=>e.name===P)||V(F[0].name)},[F,P]);const $=F.find(e=>e.name===P)?.view,B="raw_trace"===$?.type,q=useRef([]),W=useRef(null),J=useRef(null),Y=()=>{J.current||(J.current=setTimeout(()=>{if(J.current=null,q.current.length>0){const e=q.current;q.current=[],h(t=>[...e.slice().reverse(),...t])}W.current&&(g(W.current),W.current=null)},n))};useEffect(()=>{if(!r||!a||!l)return p(null),h([]),void g({});let e=!1;return(async()=>{try{const t=await c("tis.read_test",MessageType.Request,{project_id:r,method_id:a,run_id:l});!e&&t?.success&&(p(t.data),g(t.data.results??{}));const s=await c("tis.read_cycles",MessageType.Request,{project_id:r,method_id:a,run_id:l,offset:0,limit:200,order:"desc"});!e&&s?.success&&h(s.data.cycles??[])}catch(e){}})(),()=>{e=!0}},[r,a,l,c]),useEffect(()=>{const e=e=>e?.project_id===r&&e?.method_id===a&&e?.run_id===l,t=d("tis.cycle_added",t=>{e(t)&&t.cycle&&(q.current.push(t.cycle),Y())}),s=d("tis.results_updated",t=>{e(t)&&(W.current=t.results??{},Y())});return()=>{u(t),u(s),J.current&&(clearTimeout(J.current),J.current=null)}},[r,a,l,n]);const G=useRawCycleData({projectId:r,methodId:a,runId:l,blobName:s?.raw_data?.blob_name??"trace",enabled:B}),U=useMemo(()=>{if(!$)return null;if("cycle_scatter"===$.type){const e=$.x.field;if(!e)return null;const t=[...y].reverse();return{labels:t.map(t=>t[e]),datasets:$.y.map((e,r)=>({label:e.label??e.field,data:t.map(t=>t[e.field]),yAxisID:"right"===e.y_axis?"y1":"y",borderColor:palette(r),backgroundColor:palette(r),tension:.1,pointRadius:2}))}}if("raw_trace"===$.type){if(!G.raw)return null;const e=$.x.column;if(!e)return null;const t=G.raw[e]??[];return{datasets:$.y.map((e,r)=>({label:e.label??e.column,data:(G.raw[e.column]??[]).map((e,r)=>({x:t[r],y:e})),yAxisID:"right"===e.y_axis?"y1":"y",borderColor:palette(r),backgroundColor:palette(r),pointRadius:0,borderWidth:1.5,showLine:!0}))}}return null},[$,y,G.raw]),Z=$?.y.some(e=>"right"===e.y_axis)??!1,K=useMemo(()=>{const e="raw_trace"===$?.type;return{responsive:!0,maintainAspectRatio:!1,parsing:!e&&void 0,scales:{x:e?{type:"linear",title:{display:!!$?.x.label,text:$?.x.label}}:{title:{display:!!$?.x.label,text:$?.x.label}},y:{position:"left",title:{display:!0,text:leftAxisLabel($)}},...Z?{y1:{position:"right",grid:{drawOnChartArea:!1},title:{display:!0,text:rightAxisLabel($)}}}:{}},plugins:{legend:{display:!0},zoom:{pan:{enabled:!0,mode:"xy"},zoom:{wheel:{enabled:!0},pinch:{enabled:!0},mode:"xy"}}}}},[$,Z]),Q=s?.raw_data?.blob_name??"trace",X=useRef(""),ee=useCallback(async()=>{if(!r||!a||!l)return[];try{const e=await c("tis.list_raw",MessageType.Request,{project_id:r,method_id:a,run_id:l});if(!e?.success)return[];const t=e.data?.cycles??[];return t.filter(e=>e?.name===Q&&"number"==typeof e?.cycle_index).map(e=>e.cycle_index).sort((e,t)=>e-t)}catch{return[]}},[r,a,l,Q,c]),te=useCallback(async e=>{if(!r||!a||!l)return;const t=`${r}|${a}|${l}|${Q}|${e??"latest"}`;if(X.current===t)return;X.current=t;const s={project_id:r,method_id:a,run_id:l,name:Q};null!=e&&(s.cycle_index=e),N(!0),R(null),C(null);try{const e=await c("tis.read_raw",MessageType.Request,s);e?.success?C(e.data??{}):R(e?.error_message??"No raw data on disk for this run.")}catch(e){R(String(e?.message??e))}finally{N(!1)}k(!0),E(null),I(null);try{const e=await c("tis.read_filtered",MessageType.Request,{project_id:r,method_id:a,run_id:l,name:Q});e?.success?I(e.data??{}):E(e?.error_message??"No filtered data on disk for this run.")}catch(e){E(String(e?.message??e))}finally{k(!1)}},[r,a,l,Q,c]);useEffect(()=>{X.current="",H([]),z(null)},[r,a,l,Q]);return useEffect(()=>{x&&null!=M&&te(M)},[x,M,te]),r&&a&&l&&s?_jsxs("div",{className:"vblock",style:{display:"flex",flexDirection:"column",gap:"1rem"},children:[_jsx(Header,{meta:m,config:m?.config,runId:l,projectId:r,methodId:a,canViewRaw:!!s.raw_data,onViewRaw:async()=>{_(!0);let e=A;0===e.length&&(e=await ee(),H(e));const t=e.length>0?e[e.length-1]:null,r=M??t;M!==r&&z(r),await te(r)},onShowConfig:()=>j(!0)}),_jsxs("div",{className:"p-card",style:{padding:"1rem"},children:[_jsxs("div",{className:"flex",style:{gap:"1rem",alignItems:"center",marginBottom:"0.5rem",flexWrap:"wrap"},children:[_jsx(Dropdown,{value:P,options:F.map(e=>({label:e.view.title??e.name,value:e.name})),onChange:e=>V(e.value),placeholder:0===F.length?"No view defined":"Select a view",disabled:0===F.length}),_jsx("h3",{style:{margin:0},children:$?.title??""}),B&&G.cycles.length>1&&_jsxs(_Fragment,{children:[_jsx("label",{htmlFor:"chart-cycle-picker",style:{color:"var(--text-secondary-color)"},children:"Cycle:"}),_jsx(Dropdown,{inputId:"chart-cycle-picker",value:G.selectedCycle,options:G.cycles.map(e=>({label:`Cycle ${e}`,value:e})),onChange:e=>G.setSelectedCycle(Number(e.value)),style:{minWidth:"8rem"}}),_jsxs("span",{style:{color:"var(--text-secondary-color)"},children:["of ",G.cycles.length]})]}),_jsx("div",{style:{flex:1}}),_jsx(Button,{icon:"pi pi-th-large",outlined:!0,rounded:!0,size:"small",onClick:()=>v.current?.resetZoom?.(),disabled:!U,tooltip:"Reset chart zoom",tooltipOptions:{position:"left"},"aria-label":"Reset chart zoom"})]}),_jsxs("div",{style:{height:o,position:"relative"},children:[B&&G.loading&&_jsx(ChartOverlay,{children:"Loading raw data…"}),B&&G.error&&_jsx(ChartOverlay,{children:G.error}),U&&_jsx(Line,{ref:v,data:U,options:K})]})]}),_jsxs("div",{className:"p-card",style:{padding:"1rem"},children:[_jsxs("h3",{style:{marginTop:0},children:["Cycle Data (",y.length,")"]}),(()=>{const e=y.length>CYCLE_VIRTUAL_THRESHOLD;return _jsx(DataTable,{value:y,scrollable:e,scrollHeight:e?i:void 0,virtualScrollerOptions:e?{itemSize:38}:void 0,emptyMessage:"No cycles yet.",children:s.cycle_fields.map(e=>_jsx(Column,{field:e.name,header:e.units?`${e.name} (${e.units})`:e.name,body:t=>formatCell(t[e.name],e.type,e.scale)},e.name))})})()]}),_jsxs("div",{className:"p-card",style:{padding:"1rem"},children:[_jsx("h3",{style:{marginTop:0},children:"Results"}),_jsx(ResultsGrid,{schema:s.results_fields,values:f})]}),s.raw_data&&_jsxs(Dialog,{visible:x,onHide:()=>_(!1),header:`Run Data — ${l}`,style:{width:"90vw",height:"80vh"},maximizable:!0,children:[_jsx(CyclePickerBar,{cycles:A,selected:M,onChange:z}),_jsx(RawEnvelopeHeader,{envelope:w}),_jsxs(TabView,{style:{height:"100%"},children:[_jsx(TabPanel,{header:"Raw Data",children:_jsx(DataBlobTable,{blob:unwrapEnvelope(w),loading:T,error:S,rawData:s.raw_data})}),_jsx(TabPanel,{header:"Filtered Data",children:_jsx(DataBlobTable,{blob:unwrapEnvelope(D),loading:O,error:L,rawData:s.raw_data,emptyMessage:"Filtered data is written by post-processing — none on disk for this run yet."})})]})]}),_jsx(Dialog,{visible:b,onHide:()=>j(!1),header:"Test Configuration",style:{width:"min(640px, 90vw)"},modal:!0,children:_jsx(ConfigList,{config:m?.config})})]}):_jsx("div",{className:"p-card",style:{padding:"1rem",color:"var(--text-secondary-color)"},children:"No test selected. Pick a row from the History tab or start a run."})};const Header=({meta:e,config:t,runId:r,projectId:a,methodId:l,canViewRaw:s,onViewRaw:n,onShowConfig:i})=>{const o="string"==typeof e?.sample_id&&e.sample_id||"string"==typeof e?.config?.sample_id&&e.config.sample_id||"",c=t&&"object"==typeof t&&Object.entries(t).some(([e])=>"sample_id"!==e);return _jsx("div",{className:"p-card",style:{padding:"1rem"},children:_jsxs("div",{className:"flex",style:{justifyContent:"space-between",alignItems:"flex-start",gap:"1rem"},children:[_jsxs("div",{children:[_jsxs("h2",{style:{margin:0,display:"flex",alignItems:"center",gap:"0.5rem"},children:[o||r,c&&_jsx(Button,{icon:"pi pi-info-circle",type:"button",rounded:!0,text:!0,onClick:i,tooltip:"Show test configuration",tooltipOptions:{position:"top"},style:{width:"2rem",height:"2rem",padding:0},"aria-label":"Show test configuration"})]}),_jsxs("div",{style:{color:"var(--text-secondary-color)",fontSize:"0.85em"},children:["project: ",a," · method: ",l," · run: ",r,e?.start_time&&_jsxs(_Fragment,{children:[" · started: ",new Date(e.start_time).toLocaleString()]})]})]}),s&&_jsx(Button,{icon:"pi pi-table",label:"View Raw Data",onClick:n,outlined:!0})]})})},ConfigList=({config:e})=>{const t=e&&"object"==typeof e?Object.entries(e).filter(([e])=>"sample_id"!==e):[];return 0===t.length?_jsx("div",{style:{color:"var(--text-secondary-color)"},children:"No configuration recorded for this run."}):_jsx("div",{style:{display:"grid",gridTemplateColumns:"auto 1fr",gap:"0.5rem 1rem",fontSize:"0.95em"},children:t.map(([e,t])=>_jsxs(React.Fragment,{children:[_jsx("div",{style:{color:"var(--text-secondary-color)"},children:e}),_jsx("div",{children:formatCell(t,"string")})]},e))})},unwrapEnvelope=e=>e&&"object"==typeof e&&"data"in e&&e.data&&"object"==typeof e.data&&Object.values(e.data).some(e=>Array.isArray(e))?e.data:e,CyclePickerBar=({cycles:e,selected:t,onChange:r})=>e.length<=1?null:_jsxs("div",{style:{display:"flex",alignItems:"center",gap:"0.5rem",padding:"0.25rem 0.5rem 0.5rem"},children:[_jsx("label",{htmlFor:"raw-cycle-picker",style:{color:"var(--text-secondary-color)"},children:"Cycle:"}),_jsx(Dropdown,{inputId:"raw-cycle-picker",value:t,options:e.map(e=>({label:`Cycle ${e}`,value:e})),onChange:e=>r(Number(e.value)),style:{minWidth:"8rem"}}),_jsxs("span",{style:{color:"var(--text-secondary-color)"},children:["(",e.length," cycles recorded)"]})]}),RawEnvelopeHeader=({envelope:e})=>{if(!e||"object"!=typeof e)return null;const t=e.cycle_index,r=e.cycle_fields,a=e.context;if(null==t&&!r&&!a)return null;const l=e=>{if(!e||"object"!=typeof e)return null;const t=Object.entries(e).filter(([,e])=>null!==e&&"object"!=typeof e);return 0===t.length?null:t.map(([e,t])=>_jsxs("span",{style:{marginRight:"1rem"},children:[_jsxs("span",{style:{color:"var(--text-secondary-color)"},children:[e,": "]}),_jsx("span",{children:String(t)})]},e))};return _jsxs("div",{style:{padding:"0.5rem",borderBottom:"1px solid var(--surface-border)",fontSize:"0.9rem"},children:[null!=t&&_jsx("div",{children:_jsxs("strong",{children:["Cycle ",t]})}),r&&_jsx("div",{style:{marginTop:"0.25rem"},children:l(r)}),a&&_jsx("div",{style:{marginTop:"0.25rem"},children:l(a)})]})},DataBlobTable=({blob:e,loading:t,error:r,rawData:a,emptyMessage:l})=>{if(t)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:"Loading…"});if(r)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:l??r});if(!e||"object"!=typeof e)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:"No data."});const s=Object.keys(a.columns??{}),n=Object.keys(e).filter(t=>Array.isArray(e[t])),i=[];for(const t of s)Array.isArray(e[t])&&i.push(t);for(const e of n)i.includes(e)||i.push(e);if(0===i.length)return _jsx("div",{style:{padding:"1rem",color:"var(--text-secondary-color)"},children:l??"No columnar data in this blob."});const o=i.reduce((t,r)=>Math.min(t,e[r].length),Number.POSITIVE_INFINITY),c=Number.isFinite(o)?o:0,d=Array.from({length:c},(t,r)=>{const a={__i:r};for(const t of i)a[t]=e[t][r];return a}),u=e=>{const t=a.units?.[e];return t?`${e} [${t}]`:e};return _jsxs(DataTable,{value:d,scrollable:!0,scrollHeight:"60vh",virtualScrollerOptions:{itemSize:32},emptyMessage:l??"No data.",size:"small",stripedRows:!0,children:[_jsx(Column,{field:"__i",header:"#",style:{width:"5rem",textAlign:"right"},bodyStyle:{fontVariantNumeric:"tabular-nums",textAlign:"right"}}),i.map(e=>_jsx(Column,{field:e,header:u(e),style:{minWidth:"8rem"},bodyStyle:{fontVariantNumeric:"tabular-nums",textAlign:"right"},body:t=>formatNumeric(t[e])},e))]})},formatNumeric=e=>null==e?"":"number"==typeof e&&Number.isFinite(e)?Number.parseFloat(e.toPrecision(6)).toString():String(e),ResultsGrid=({schema:e,values:t})=>t&&0!==Object.keys(t).length?_jsx("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fill, minmax(220px, 1fr))",gap:"0.5rem 1rem"},children:e.map(e=>_jsxs("div",{children:[_jsxs("div",{style:{fontSize:"0.8em",color:"var(--text-secondary-color)"},children:[e.name,e.units?` (${e.units})`:""]}),_jsx("div",{children:formatCell(t[e.name],e.type,e.scale)})]},e.name))}):_jsx("div",{style:{color:"var(--text-secondary-color)"},children:"No results yet."}),CYCLE_VIRTUAL_THRESHOLD=30,CHART_COLORS=["#4ea8de","#f59e0b","#22c55e","#a855f7","#ef4444","#14b8a6","#eab308","#ec4899"],palette=e=>CHART_COLORS[e%CHART_COLORS.length],ChartOverlay=({children:e})=>_jsx("div",{style:{position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",color:"var(--text-secondary-color)",pointerEvents:"none"},children:e}),seriesLabel=e=>e.label??e.field??e.column??"",leftAxisLabel=e=>e?.y.filter(e=>"right"!==e.y_axis).map(seriesLabel).join(" / ")??"",rightAxisLabel=e=>e?.y.filter(e=>"right"===e.y_axis).map(seriesLabel).join(" / ")??"",formatCell=(e,t,r)=>null==e?"":(r&&1!==r&&"number"==typeof e&&Number.isFinite(e)&&(e*=r),"f32"===t||"f64"===t?"number"==typeof e?e.toFixed(4):String(e):"object"==typeof e?JSON.stringify(e):String(e));
@@ -29,8 +29,22 @@ export interface TestFieldDef {
29
29
  * the control program sees it; for non-source fields it's stashed
30
30
  * straight into stagedConfig. Operator edits override per-stage,
31
31
  * but the schema default never mutates — re-selecting the method
32
- * re-applies the default. */
32
+ * re-applies the default. **Authored in display units** when
33
+ * `scale` is set — the form divides by `scale` before writing to
34
+ * GM / stagedConfig so the underlying storage stays raw. */
33
35
  default?: any;
36
+ /** Optional unit-conversion multiplier. Mirrors AutoCoreTagContext:
37
+ * ```
38
+ * display = raw * scale
39
+ * raw = display / scale
40
+ * ```
41
+ * Example: backend stores degrees, operator enters revolutions →
42
+ * `scale: 0.00277778`. None / 1.0 = no conversion.
43
+ *
44
+ * Storage stays raw — the form scales only on input and display.
45
+ * Cycle and results values are scaled by the corresponding paths
46
+ * in TestDataView; the server scales CSV exports too. */
47
+ scale?: number;
34
48
  }
35
49
  export interface TestMethod {
36
50
  project_fields: TestFieldDef[];
@@ -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,CAsetD,CAAC"}
1
+ {"version":3,"file":"TestSetupForm.d.ts","sourceRoot":"","sources":["../../../src/components/tis/TestSetupForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2D,MAAM,OAAO,CAAC;AAchF;;;;;GAKG;AACH,UAAU,WAAW;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,aAAa,GAAG,aAAa,CAAC;IACtC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IACzB,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;0EACsE;IACtE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;;iEAO6D;IAC7D,OAAO,CAAC,EAAE,GAAG,CAAC;IACd;;;;;;;;;;8DAU0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACvB,cAAc,EAAE,YAAY,EAAE,CAAC;IAC/B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,cAAc,EAAE,YAAY,EAAE,CAAC;IAC/B;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,WAAW,EAAE,CAAC;IAC3B;0CACsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;2CACuC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;gEAG4D;IAC5D,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf;;sDAEkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,kBAAkB;IAC/B,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;CAChE;AAiGD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAsftD,CAAC"}
@@ -1 +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(),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})]})};
1
+ import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import React,{useState,useEffect,useContext,useMemo,useRef}from"react";import{Button}from"primereact/button";import{InputText}from"primereact/inputtext";import{Dropdown}from"primereact/dropdown";import{Dialog}from"primereact/dialog";import{EventEmitterContext}from"../../core/EventEmitterContext";import{AutoCoreTagContext}from"../../core/AutoCoreTagContext";import{MessageType}from"../../hub/CommandMessage";import{ValueInput}from"../ValueInput";import{TextInput}from"../TextInput";import{useTis}from"./TisProvider";import{useAmsAssets,useAmsRoles}from"../ams/AmsProvider";import{TestMethodDialog}from"./TestMethodDialog";const labelOf=e=>{const t=e.label&&e.label.length>0?e.label:e.name;return e.units?`${t} [${e.units}]`:t},rawToDisplay=(e,t)=>t&&1!==t&&"number"==typeof e&&Number.isFinite(e)?e*t:e,displayToRaw=(e,t)=>t&&1!==t&&"number"==typeof e&&Number.isFinite(e)?e/t:e,hasDescription=e=>"string"==typeof e.description&&e.description.length>0,methodLabelOf=(e,t)=>t?.label&&t.label.length>0?t.label:e,AssetIdPicker=({assetType:e,value:t,onChange:s,invalid:i})=>{const a=useAmsAssets(),n=useAmsRoles(),o=useMemo(()=>a.filter(t=>t.asset_type===e&&"active"===t.status).map(e=>{const t=n[e.asset_type]?.find(t=>t.location===e.location)?.label,s=[e.asset_id];return t?s.push(`— ${t}`):e.location&&s.push(`— ${e.location}`),e.serial&&s.push(`(s/n ${e.serial})`),{label:s.join(" "),value:e.asset_id}}),[a,n,e]);return _jsx(Dropdown,{value:t,options:o,onChange:e=>s(e.value??""),placeholder:0===o.length?`No active ${e} assets registered — add one in Settings → Assets`:`Select ${e}…`,className:i?"p-invalid":"",filter:!0,showClear:!0,disabled:0===o.length})};export const TestSetupForm=({schema:e,defaultMethodId:t,onMethodChange:s,onValidationChange:i})=>{const a=useTis(),{invoke:n,write:o}=useContext(EventEmitterContext),{rawValues:r,findTagByFqdn:l}=useContext(AutoCoreTagContext),c=useMemo(()=>Object.keys(a.schemas),[a.schemas]),d=a.selection.projectId,m=""!==d.trim()&&a.projectKnown(d.trim()),[p,u]=useState(a.selection.methodId||t||a.defaultMethodId||""),[f,h]=useState(a.selection.sampleId||""),g=a.stagedConfig,x=e=>{const t="function"==typeof e?e(a.stagedConfig):e;t!==a.stagedConfig&&a.setStagedConfig(t)},_=e??(p?a.schemas[p]:void 0);useEffect(()=>{a.selection.methodId!==p&&p&&a.setSelection({methodId:p}),s&&s(p)},[p]),useEffect(()=>{a.selection.sampleId!==f&&a.setSelection({sampleId:f})},[f]),useEffect(()=>{a.state.stagedSampleId&&a.state.stagedSampleId!==f&&h(a.state.stagedSampleId)},[a.state.stagedSampleId]),useEffect(()=>{a.selection.methodId&&a.selection.methodId!==p&&u(a.selection.methodId)},[a.selection.methodId]);const[j,y]=useState(!1),[b,v]=useState(!1),[T,I]=useState({open:!1,title:"",body:null}),C=useRef("");useEffect(()=>{_&&p&&C.current!==p&&(C.current=p,x(e=>{let t=e;for(const s of _.config_fields){if("sample_id"===s.name)continue;if(void 0===s.default||null===s.default)continue;t===e&&(t={...e});const i=displayToRaw(s.default,s.scale);t[s.name]=i,s.source&&Promise.resolve().then(()=>o(s.source,i)).catch(e=>{})}return t}))},[_,p,o]),useEffect(()=>{_&&x(e=>{let t=e;for(const s of _.config_fields){if("sample_id"===s.name)continue;if(!s.source)continue;const i=l(s.source);if(!i)continue;const a=r[i.tagName];null!=a&&(t[s.name]!==a&&(t===e&&(t={...e}),t[s.name]=a))}return t})},[_,r,l]),useEffect(()=>{if(!_)return void y(!1);let e=!0;m||(e=!1),p.trim()||(e=!1),f.trim()||(e=!1),e&&!a.projectFieldsLoaded&&(e=!1);for(const t of _.config_fields)if("sample_id"!==t.name&&t.required){const s=g[t.name];if(void 0===s||""===s||null===s){e=!1;break}}if(y(e),i&&i(e,g),e){const{sample_id:e,...t}=g??{},s={...a.projectFields,...t};n("tis.stage_test",MessageType.Request,{project_id:d,method_id:p,sample_id:f,config:s}).catch(e=>{})}},[g,_,d,p,f,m,a.projectFields,a.projectFieldsLoaded,i,n]);const S=async(e,t)=>{const s=displayToRaw(t,e.scale);if(x({...g,[e.name]:s}),e.source)try{await o(e.source,s)}catch(e){}};if(!_)return _jsx("div",{className:"ac-form-grid",style:{padding:"1.25rem"},children:_jsx("h3",{className:"ac-form-section",children:a.schemasLoaded?"No Test Method Selected":"Loading test methods…"})});if(!m)return _jsxs("div",{style:{padding:"1.25rem",maxWidth:"600px"},children:[_jsx("h3",{className:"ac-form-section",children:"No project selected"}),_jsxs("p",{style:{color:"var(--text-secondary-color)",marginTop:"0.5rem"},children:["Pick a project on the ",_jsx("strong",{children:"Project"})," tab first",""!==d.trim()&&` (or click + there to create "${d.trim()}")`,"."]})]});const N=j&&!a.state.lastStartError;return _jsxs("div",{className:"ac-form-grid",style:{padding:"1.25rem",gridTemplateColumns:"auto 1fr 1.75rem 1.75rem"},children:[_jsxs("h3",{className:"ac-form-section",style:{display:"flex",alignItems:"center",gap:"10px"},children:["Test Setup",_jsx(Button,{icon:N?"pi pi-check-circle":"pi pi-exclamation-circle",severity:N?"success":"danger",text:!0,rounded:!0,"aria-label":"Test Setup status",onClick:()=>{const e=(()=>{const e=[];if(m||e.push("No project is selected. Pick or create one on the Project tab."),a.projectFieldsLoaded||e.push("Project fields are still loading."),p.trim()||e.push("No test method selected."),f.trim()||e.push("Sample ID is required."),_)for(const t of _.config_fields){if("sample_id"===t.name)continue;if(!t.required)continue;const s=g[t.name];void 0!==s&&""!==s&&null!==s||e.push(`Required field "${labelOf(t)}" is empty.`)}return e})(),t=a.state.lastStartError?.trim()??"";let s;s=0!==e.length||t?_jsxs("div",{children:[e.length>0&&_jsxs(_Fragment,{children:[_jsx("p",{style:{margin:"0 0 0.5rem 0"},children:"The form is incomplete:"}),_jsx("ul",{style:{margin:"0 0 1rem 1.25rem"},children:e.map((e,t)=>_jsx("li",{children:e},t))})]}),t&&_jsxs(_Fragment,{children:[_jsx("p",{style:{margin:"0 0 0.25rem 0",fontWeight:600},children:"Last start_test error from the server:"}),_jsx("pre",{style:{margin:0,padding:"0.5rem",background:"rgba(0,0,0,0.25)",borderRadius:"4px",whiteSpace:"pre-wrap",fontSize:"0.875rem"},children:t})]})]}):_jsx("p",{style:{margin:0},children:"All required fields are complete. The test is staged and ready to start."}),I({open:!0,title:"Test Setup Status",body:s})}}),_jsxs("span",{style:{fontSize:"0.85em",color:"var(--text-secondary-color)",fontWeight:"normal",marginLeft:"0.25rem"},children:["project: ",_jsx("strong",{children:d})]})]}),_jsx("span",{className:"ac-form-label",children:"Sample ID"}),_jsx(TextInput,{label:void 0,value:f,onValueChanged:e=>{h(e)},className:f.trim()?"":"p-invalid"}),_jsx("span",{"aria-hidden":"true"}),_jsx("span",{style:{color:f.trim()?"var(--green-500)":"var(--red-500)",display:"flex",alignItems:"center"},children:_jsx("i",{className:f.trim()?"pi pi-check":"pi pi-times"})}),c.length>0&&_jsxs(_Fragment,{children:[_jsx("span",{className:"ac-form-label",children:"Test Method"}),_jsxs("div",{className:"p-inputgroup",style:{flex:1},children:[_jsx(InputText,{value:methodLabelOf(p,_),readOnly:!0,style:{flex:1},tabIndex:-1}),_jsx(Button,{icon:"pi pi-pencil",type:"button",onClick:()=>v(!0),tooltip:c.length>1?"Change test method":"View test method details",tooltipOptions:{position:"top"}})]}),_jsx("span",{"aria-hidden":"true"}),_jsx("span",{style:{color:p?"var(--green-500)":"var(--red-500)",display:"flex",alignItems:"center"},children:_jsx("i",{className:p?"pi pi-check":"pi pi-times"})})]}),_jsx("h3",{className:"ac-form-section",style:{marginTop:"1rem"},children:"Test Configuration"}),_.config_fields.map(e=>{if("sample_id"===e.name)return null;const t=(e=>{if(!e.required)return!0;const t=g[e.name];return void 0!==t&&""!==t&&null!==t})(e),s=(e=>{const t=_?.asset_refs??[],s=a.projectAssetRefs??[],i=`config.${e.name}`,n=t.find(e=>"by_id_field"===e.select&&e.from===i);if(n)return n.asset_type;const o=s.find(e=>"by_id_field"===e.select&&e.from===i);return o?o.asset_type:null})(e),i=!s&&"string"!==e.type&&"bool"!==e.type;return _jsxs(React.Fragment,{children:[_jsx("span",{className:"ac-form-label",children:labelOf(e)}),s?_jsx(AssetIdPicker,{assetType:s,value:null!=g[e.name]?String(g[e.name]):"",onChange:t=>S(e,t),invalid:!t}):i?_jsx(ValueInput,{label:void 0,value:null!=g[e.name]?Number(rawToDisplay(Number(g[e.name]),e.scale)):null,onValueChanged:t=>S(e,t),className:t?"":"p-invalid"}):_jsx(TextInput,{label:void 0,value:null!=g[e.name]?String(g[e.name]):"",onValueChanged:t=>S(e,t),className:t?"":"p-invalid"}),hasDescription(e)?_jsx(Button,{icon:"pi pi-info-circle",text:!0,rounded:!0,"aria-label":`About ${labelOf(e)}`,onClick:()=>I({open:!0,title:labelOf(e),body:_jsx("p",{style:{margin:0,whiteSpace:"pre-wrap"},children:e.description})})}):_jsx("span",{"aria-hidden":"true"}),_jsx("span",{style:{color:t?"var(--green-500)":"var(--red-500)",display:"flex",alignItems:"center"},children:_jsx("i",{className:t?"pi pi-check":"pi pi-times"})})]},e.name)}),_jsx(TestMethodDialog,{visible:b,onHide:()=>v(!1),currentMethodId:p,onSelected:e=>u(e)}),_jsx(Dialog,{header:T.title,visible:T.open,onHide:()=>I({open:!1,title:"",body:null}),style:{width:"32rem",maxWidth:"90vw"},modal:!0,dismissableMask:!0,children:T.body})]})};
@@ -1 +1 @@
1
- {"version":3,"file":"useRawCycleData.d.ts","sourceRoot":"","sources":["../../../src/components/tis/useRawCycleData.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,sBAAsB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAG,MAAM,CAAC;IACnB,KAAK,CAAC,EAAM,MAAM,CAAC;IACnB,mEAAmE;IACnE,QAAQ,EAAI,MAAM,CAAC;IACnB;;sDAEkD;IAClD,OAAO,EAAK,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IAClC;kEAC8D;IAC9D,MAAM,EAAY,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAK,MAAM,GAAG,IAAI,CAAC;IAChC,gBAAgB,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC7C;4EACwE;IACxE,GAAG,EAAe,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC;IAClD;yEACqE;IACrE,QAAQ,EAAU,GAAG,GAAG,IAAI,CAAC;IAC7B,OAAO,EAAW,OAAO,CAAC;IAC1B,KAAK,EAAa,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,qBAAqB,CAuFnF;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAOlE"}
1
+ {"version":3,"file":"useRawCycleData.d.ts","sourceRoot":"","sources":["../../../src/components/tis/useRawCycleData.ts"],"names":[],"mappings":"AAqCA,MAAM,WAAW,sBAAsB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAG,MAAM,CAAC;IACnB,KAAK,CAAC,EAAM,MAAM,CAAC;IACnB,mEAAmE;IACnE,QAAQ,EAAI,MAAM,CAAC;IACnB;;sDAEkD;IAClD,OAAO,EAAK,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IAClC;kEAC8D;IAC9D,MAAM,EAAY,MAAM,EAAE,CAAC;IAC3B,aAAa,EAAK,MAAM,GAAG,IAAI,CAAC;IAChC,gBAAgB,EAAE,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC7C;4EACwE;IACxE,GAAG,EAAe,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC;IAClD;yEACqE;IACrE,QAAQ,EAAU,GAAG,GAAG,IAAI,CAAC;IAC7B,OAAO,EAAW,OAAO,CAAC;IAC1B,KAAK,EAAa,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,qBAAqB,CA6KnF;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAOlE"}
@@ -1 +1 @@
1
- import{useContext,useEffect,useState}from"react";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";export function useRawCycleData(e){const{projectId:t,methodId:n,runId:a,blobName:r,enabled:s}=e,{invoke:l}=useContext(EventEmitterContext),[u,o]=useState([]),[c,i]=useState(null),[d,f]=useState(null),[m,y]=useState(null),[p,E]=useState(!1),[_,g]=useState(null);return useEffect(()=>{o([]),i(null),f(null),y(null),g(null)},[t,n,a,r]),useEffect(()=>{if(!(s&&t&&n&&a))return;let e=!1;return(async()=>{try{const s=await l("tis.list_raw",MessageType.Request,{project_id:t,method_id:n,run_id:a});if(e||!s?.success)return;const u=(s.data?.cycles??[]).filter(e=>e?.name===r&&"number"==typeof e?.cycle_index).map(e=>e.cycle_index).sort((e,t)=>e-t);o(u),u.length>0&&i(e=>e??u[u.length-1])}catch{}})(),()=>{e=!0}},[s,t,n,a,r,l]),useEffect(()=>{if(!(s&&t&&n&&a))return f(null),y(null),E(!1),void g(null);let e=!1;return E(!0),g(null),(async()=>{try{const s={project_id:t,method_id:n,run_id:a,name:r};null!=c&&(s.cycle_index=c);const u=await l("tis.read_raw",MessageType.Request,s);if(e)return;if(u?.success){const e=u.data??{};y(e),f(unwrapEnvelope(e))}else g(u?.error_message??"Failed to read raw data")}catch(t){e||g(String(t?.message??t))}finally{e||E(!1)}})(),()=>{e=!0}},[s,t,n,a,r,c,l]),{cycles:u,selectedCycle:c,setSelectedCycle:i,raw:d,envelope:m,loading:p,error:_}}export function unwrapEnvelope(e){return 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:{}}
1
+ import{useCallback,useContext,useEffect,useRef,useState}from"react";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";export function useRawCycleData(e){const{projectId:t,methodId:r,runId:n,blobName:u,enabled:a}=e,{invoke:l,subscribe:s,unsubscribe:c}=useContext(EventEmitterContext),[o,i]=useState([]),[d,f]=useState(null),[m,y]=useState(null),[p,_]=useState(null),[b,g]=useState(!1),[h,E]=useState(null),w=useRef(!1),C=useCallback(async()=>{if(!t||!r||!n)return[];try{const e=await l("tis.list_raw",MessageType.Request,{project_id:t,method_id:r,run_id:n});if(!e?.success)return[];return(e.data?.cycles??[]).filter(e=>e?.name===u&&"number"==typeof e?.cycle_index).map(e=>e.cycle_index).sort((e,t)=>e-t)}catch{return[]}},[t,r,n,u,l]);useEffect(()=>{i([]),f(null),y(null),_(null),E(null),w.current=!1},[t,r,n,u]),useEffect(()=>{if(!(a&&t&&r&&n))return;let e=!1;return(async()=>{const t=await C();e||(i(t),t.length>0&&f(e=>e??t[t.length-1]))})(),()=>{e=!0}},[a,t,r,n,u,C]);const v=useRef(o);v.current=o;const x=useRef(d);x.current=d,useEffect(()=>{if(!(a&&t&&r&&n))return;const e=s("tis.raw_data_added",async e=>{if(e?.project_id!==t)return;if(e?.method_id!==r)return;if(e?.run_id!==n)return;if(e?.name!==u)return;const a=await C();if(0===a.length)return;i(a);const l=v.current.length>0?v.current[v.current.length-1]:null,s=a[a.length-1];!w.current&&(null===x.current||x.current===l)&&s!==x.current&&f(s)});return()=>{c(e)}},[a,t,r,n,u,C,s,c]),useEffect(()=>{if(!(a&&t&&r&&n))return y(null),_(null),g(!1),void E(null);if(null==d)return y(null),_(null),g(!1),void E(null);let e=!1;return g(!0),E(null),(async()=>{try{const a=await l("tis.read_raw",MessageType.Request,{project_id:t,method_id:r,run_id:n,name:u,cycle_index:d});if(e)return;if(a?.success){const e=a.data??{};_(e),y(unwrapEnvelope(e))}else E(a?.error_message??"Failed to read raw data")}catch(t){e||E(String(t?.message??t))}finally{e||g(!1)}})(),()=>{e=!0}},[a,t,r,n,u,d,l]);const S=useCallback(e=>{w.current=null!=e,f(e)},[]);return{cycles:o,selectedCycle:d,setSelectedCycle:S,raw:m,envelope:p,loading:b,error:h}}export function unwrapEnvelope(e){return 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:{}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adcops/autocore-react",
3
- "version": "3.3.83",
3
+ "version": "3.3.84",
4
4
  "description": "A React component library for industrial user interfaces.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -15,6 +15,7 @@ import { EventEmitterContext } from '../../core/EventEmitterContext';
15
15
  import { MessageType } from '../../hub/CommandMessage';
16
16
  import { useAms } from './AmsProvider';
17
17
  import { CalibrationEntryDialog } from './CalibrationEntryDialog';
18
+ import { AssetEditDialog } from './AssetEditDialog';
18
19
 
19
20
  export const AssetDetailView: React.FC = () => {
20
21
  const { selection, schemas, roles, readAsset, listCalibrations, readCalibration, readUsage,
@@ -30,6 +31,9 @@ export const AssetDetailView: React.FC = () => {
30
31
  * row, so this is always either null or the current cal. */
31
32
  const [editingCal, setEditingCal] = useState<any | null>(null);
32
33
  const [deleting, setDeleting] = useState(false);
34
+ /** Open state for the asset-edit dialog. The dialog seeds itself
35
+ * from `asset` whenever this flips true. */
36
+ const [editDialogOpen, setEditDialogOpen] = useState(false);
33
37
 
34
38
  /** Two-step asset retirement: flip status to retired via update_asset.
35
39
  * The hard delete (delete_asset) is a separate action, only visible
@@ -221,6 +225,19 @@ export const AssetDetailView: React.FC = () => {
221
225
  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
222
226
  <h4 style={{ margin: 0 }}>Calibration History</h4>
223
227
  <div style={{ display: 'flex', gap: '0.5rem' }}>
228
+ {/* Asset-mutation actions (Edit / Retire / Delete)
229
+ sit alongside the calibration add — they're all
230
+ the "do something to this asset" affordances and
231
+ deserve to live in one row rather than scattered
232
+ across the panel. Edit covers role + nameplate
233
+ + per-axis values; Retire / Delete cover the
234
+ lifecycle transitions. */}
235
+ <Button label="Edit" icon="pi pi-pencil"
236
+ outlined
237
+ onClick={() => setEditDialogOpen(true)}
238
+ tooltip="Edit role, nameplate, and per-axis values. Type, serial, and install date are immutable."
239
+ tooltipOptions={{ position: 'left' }}
240
+ />
224
241
  {asset.status === 'active' && (
225
242
  <Button label="Retire" icon="pi pi-power-off"
226
243
  severity="warning" outlined
@@ -284,6 +301,20 @@ export const AssetDetailView: React.FC = () => {
284
301
  onHide={() => { setCalDialogOpen(false); setEditingCal(null); }}
285
302
  onAdded={() => { refresh(); }}
286
303
  />
304
+
305
+ <AssetEditDialog
306
+ visible={editDialogOpen}
307
+ asset={asset}
308
+ onHide={() => setEditDialogOpen(false)}
309
+ onSaved={async () => {
310
+ // The server broadcasts ams.asset_changed which the
311
+ // provider already listens to, but we also need to
312
+ // re-pull this view's local copy so the nameplate
313
+ // and role re-render immediately.
314
+ await refreshAssets();
315
+ await refresh();
316
+ }}
317
+ />
287
318
  </div>
288
319
  );
289
320
  };