@adcops/autocore-react 3.3.87 → 3.3.89

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.
@@ -0,0 +1,4 @@
1
+ import type { SVGProps } from "react";
2
+ export declare const SvgAxisC: (props: SVGProps<SVGSVGElement>) => import("react/jsx-runtime").JSX.Element;
3
+ export default SvgAxisC;
4
+ //# sourceMappingURL=AxisC.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AxisC.d.ts","sourceRoot":"","sources":["../../src/assets/AxisC.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEtC,eAAO,MAAM,QAAQ,GAAI,OAAO,QAAQ,CAAC,aAAa,CAAC,4CAuBtD,CAAC;AAEF,eAAe,QAAQ,CAAC"}
@@ -0,0 +1 @@
1
+ import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";export const SvgAxisC=s=>_jsxs("svg",{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",...s,children:[_jsx("path",{d:"M15 10a4 4 1 1 0 0 6"}),_jsx("path",{d:"M12 2a10 10 0 1 0 9.5 7"}),_jsx("path",{d:"m11 5 4-5-4-4",transform:"translate(0 2)"})]});export default SvgAxisC;
@@ -1 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";export const SvgAxisX=s=>_jsxs("svg",{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",...s,children:[_jsx("path",{d:"M5 2l14 12M18 2l-13 12"}),_jsx("path",{d:"M2 22h20M2 22l3-3M2 22l3 3M22 22l-3-3M22 22l-3 3"})]});export default SvgAxisX;
1
+ import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";export const SvgAxisX=s=>_jsxs("svg",{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",...s,children:[_jsx("path",{d:"M4 18h16M4 18l4-4M4 18l4 4M20 18l-4-4M20 18l-4 4"}),_jsx("path",{d:"M9 2l6 6m0-6l-6 6"})]});export default SvgAxisX;
@@ -1 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";export const SvgAxisY=s=>_jsxs("svg",{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",...s,children:[_jsx("g",{transform:"rotate(-45 12 12)",children:_jsx("path",{d:"M0 8h22M0 8l4-4M0 8l4 4M23 8l-4-4M23 8l-4 4"})}),_jsx("path",{d:"M13 12l5 7v7M23 12l-5 6"})]});export default SvgAxisY;
1
+ import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";export const SvgAxisY=s=>_jsxs("svg",{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",...s,children:[_jsx("g",{transform:"rotate(-45 6 12)",children:_jsx("path",{d:"M4 12h16M4 12l4-4M4 12l4 4M20 12l-4-4M20 12l-4 4"})}),_jsx("path",{d:"M15 12l3 4v5 M21 12l-3 4"})]});export default SvgAxisY;
@@ -1 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";export const SvgAxisZ=s=>_jsxs("svg",{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",...s,children:[_jsx("path",{d:"M18 2v20M15 5l3-3 3 3M15 19l3 3 3-3"}),_jsx("path",{d:"M1 4h10l-10 15h10"})]});export default SvgAxisZ;
1
+ import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";export const SvgAxisZ=s=>_jsxs("svg",{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",...s,children:[_jsx("path",{d:"M18 4v16M14 8l4-4 4 4M14 16l4 4 4-4"}),_jsx("path",{d:"M2 10h6l-6 6h6"})]});export default SvgAxisZ;
@@ -25,6 +25,16 @@ export interface AmsRole {
25
25
  * feeding NI bridge channel calibration). Empty for purely
26
26
  * test-method-driven roles. */
27
27
  used_by_modules: string[];
28
+ /** Optional per-field seed values declared on the asset_ref's
29
+ * `defaults` map. AIS uses these to pre-fill nameplate inputs when
30
+ * the operator creates a new asset for this role — the operator
31
+ * confirms or edits, the values are never enforced. Absent when no
32
+ * asset_ref declared defaults (the common case, since built-in
33
+ * load_cell etc. didn't support defaults before the feature
34
+ * landed). Keys are field names from the asset_type's schema;
35
+ * values are untyped JSON (numbers, strings, booleans) matching
36
+ * the field's declared `type`. */
37
+ defaults?: Record<string, any>;
28
38
  }
29
39
  export type AmsRoleRegistry = {
30
40
  [assetType: string]: AmsRole[];
@@ -1 +1 @@
1
- {"version":3,"file":"AmsProvider.d.ts","sourceRoot":"","sources":["../../../src/components/ams/AmsProvider.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,EAOV,KAAK,SAAS,EACjB,MAAM,OAAO,CAAC;AAQf,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC;AAChC,MAAM,MAAM,iBAAiB,GAAG;IAAE,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,CAAA;CAAE,CAAC;AAEvE;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACpB,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,2EAA2E;IAC3E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;;;oCAKgC;IAChC,eAAe,EAAE,MAAM,EAAE,CAAC;CAC7B;AACD,MAAM,MAAM,eAAe,GAAG;IAAE,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,EAAE,CAAA;CAAE,CAAC;AAEjE,MAAM,WAAW,SAAS;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,iBAAiB,CAAC;IACjD,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;CACzC;AAED,MAAM,WAAW,YAAY;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC5B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,KAAK,EAAE,eAAe,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IACpD,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACzD,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IACzE,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IACpD,SAAS,EAAE,YAAY,CAAC;IACxB,YAAY,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;CACxD;AA0BD,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,EAAE,SAAS,CAAC;CACvB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAoIlD,CAAC;AAMF,eAAO,MAAM,MAAM,uBAAwC,CAAC;AAC5D,eAAO,MAAM,aAAa,yBAAyC,CAAC;AACpE,eAAO,MAAM,WAAW,uBAAyC,CAAC;AAClE,eAAO,MAAM,YAAY,iBAAyC,CAAC;AACnE,eAAO,MAAM,YAAY,uBAAyC,CAAC;AACnE,eAAO,MAAM,eAAe,wCA9KF,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAiLvD,CAAC"}
1
+ {"version":3,"file":"AmsProvider.d.ts","sourceRoot":"","sources":["../../../src/components/ams/AmsProvider.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,EAOV,KAAK,SAAS,EACjB,MAAM,OAAO,CAAC;AAQf,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC;AAChC,MAAM,MAAM,iBAAiB,GAAG;IAAE,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,CAAA;CAAE,CAAC;AAEvE;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACpB,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;IACjB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,4DAA4D;IAC5D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,2EAA2E;IAC3E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;;;oCAKgC;IAChC,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B;;;;;;;;uCAQmC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAClC;AACD,MAAM,MAAM,eAAe,GAAG;IAAE,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,EAAE,CAAA;CAAE,CAAC;AAEjE,MAAM,WAAW,SAAS;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,iBAAiB,CAAC;IACjD,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;CACzC;AAED,MAAM,WAAW,YAAY;IACzB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC5B,OAAO,EAAE,iBAAiB,CAAC;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,KAAK,EAAE,eAAe,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IACpD,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACzD,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IACzE,SAAS,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IACpD,SAAS,EAAE,YAAY,CAAC;IACxB,YAAY,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;CACxD;AA0BD,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,EAAE,SAAS,CAAC;CACvB;AAED,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAoIlD,CAAC;AAMF,eAAO,MAAM,MAAM,uBAAwC,CAAC;AAC5D,eAAO,MAAM,aAAa,yBAAyC,CAAC;AACpE,eAAO,MAAM,WAAW,uBAAyC,CAAC;AAClE,eAAO,MAAM,YAAY,iBAAyC,CAAC;AACnE,eAAO,MAAM,YAAY,uBAAyC,CAAC;AACnE,eAAO,MAAM,eAAe,wCA9KF,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAiLvD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"AssetRegistryTable.d.ts","sourceRoot":"","sources":["../../../src/components/ams/AssetRegistryTable.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAmD,MAAM,OAAO,CAAC;AA6HxE,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAqftC,CAAC"}
1
+ {"version":3,"file":"AssetRegistryTable.d.ts","sourceRoot":"","sources":["../../../src/components/ams/AssetRegistryTable.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAuJxE,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAwgBtC,CAAC"}
@@ -1 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import React,{useContext,useEffect,useMemo,useState}from"react";import{Button}from"primereact/button";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Dropdown}from"primereact/dropdown";import{InputText}from"primereact/inputtext";import{Dialog}from"primereact/dialog";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";import{useAms}from"./AmsProvider";const ROLE_OTHER="__other__",EMPTY_ADD={open:!1,assetType:"",serial:"",roleSelection:"",location:"",customFields:{},subLocationFields:{}};function roleLabel(e){return e.label??e.location}function roleUsageSummary(e){const s=[];return e.used_by.length>0&&s.push(`Used by test method${1===e.used_by.length?"":"s"}: ${e.used_by.join(", ")}`),e.used_by_modules.length>0&&s.push(`Used by module${1===e.used_by_modules.length?"":"s"}: ${e.used_by_modules.join(", ")}`),0===s.length?"Not referenced by any test method or module.":s.join(" • ")}function fieldsFor(e,s){const t=e?.[s]?.fields;return Array.isArray(t)?t:[]}function subLocationsFor(e,s){const t=e?.[s]?.sub_locations;return t&&Array.isArray(t.keys)&&Array.isArray(t.fields)?t:null}function coerceField(e,s){if(void 0!==s&&""!==s)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(s);return Number.isFinite(e)?e:void 0}case"bool":return"true"===s||"1"===s;default:return s}}export const AssetRegistryTable=()=>{const{schemas:e,roles:s,assets:t,refreshAssets:o,setSelection:l,selection:a}=useAms(),{invoke:r}=useContext(EventEmitterContext),[i,n]=useState(null),[c,d]=useState(null),[u,m]=useState(EMPTY_ADD);useEffect(()=>{const e=e=>{const s=e.detail,t=s?.assetType??"",o=s?.location??"";t&&m({...EMPTY_ADD,open:!0,assetType:t,roleSelection:o,location:o})};return window.addEventListener("ams:prefill-add",e),()=>window.removeEventListener("ams:prefill-add",e)},[]);const p=useMemo(()=>Object.keys(e).map(s=>({label:e[s]?.label??s,value:s})),[e]),y=useMemo(()=>u.assetType?s[u.assetType]??[]:[],[u.assetType,s]),_=y.length>0,h=useMemo(()=>{const e=y.map(e=>({label:roleLabel(e),value:e.location}));return e.push({label:"Other (advanced — type a custom role)",value:ROLE_OTHER}),e},[y]),b=useMemo(()=>u.roleSelection===ROLE_OTHER?null:y.find(e=>e.location===u.roleSelection)??null,[u.roleSelection,y]),f=useMemo(()=>t.filter(e=>(!i||e.asset_type===i)&&(!c||e.status===c)),[t,i,c]),x=useMemo(()=>fieldsFor(e,u.assetType),[e,u.assetType]),g=useMemo(()=>subLocationsFor(e,u.assetType),[e,u.assetType]),j=!u.assetType||_&&(""===u.roleSelection||u.roleSelection===ROLE_OTHER&&!u.location.trim())||x.some(e=>e.required&&!u.customFields[e.name]?.toString().trim())||(g?.keys.some(e=>g.fields.some(s=>s.required&&!u.subLocationFields[e]?.[s.name]?.toString().trim()))??!1);return _jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"0.5rem"},children:[_jsxs("div",{style:{display:"flex",gap:"0.75rem",alignItems:"center"},children:[_jsx(Dropdown,{value:i,options:[{label:"All Types",value:null},...p],onChange:e=>n(e.value),placeholder:"Filter by type"}),_jsx(Dropdown,{value:c,options:[{label:"All Statuses",value:null},{label:"Active",value:"active"},{label:"Out for Service",value:"out_for_service"},{label:"Retired",value:"retired"}],onChange:e=>d(e.value),placeholder:"Filter by status"}),_jsx("span",{style:{marginLeft:"auto"},children:_jsx(Button,{icon:"pi pi-plus",label:"Add Asset",onClick:()=>m(e=>({...e,open:!0})),disabled:0===p.length})})]}),_jsxs(DataTable,{value:f,selectionMode:"single",selection:f.find(e=>e.asset_id===a.assetId)??null,onSelectionChange:e=>{const s=e.value;s&&l({assetType:s.asset_type,assetId:s.asset_id})},dataKey:"asset_id",emptyMessage:0===p.length?"AMS not enabled in this project (no asset_types declared).":"No assets registered yet.",size:"small",stripedRows:!0,children:[_jsx(Column,{field:"asset_id",header:"Asset ID"}),_jsx(Column,{field:"asset_type",header:"Type",body:s=>e[s.asset_type]?.label??s.asset_type}),_jsx(Column,{field:"serial",header:"Serial"}),_jsx(Column,{field:"location",header:"Role",body:e=>e.location?((e,t)=>{const o=(s[e]??[]).find(e=>e.location===t);return o?roleLabel(o):t})(e.asset_type,e.location):_jsx("span",{style:{color:"#9ca3af"},children:"—"})}),_jsx(Column,{field:"status",header:"Status"}),_jsx(Column,{header:"Calibration",body:e=>e.current_calibration_id?_jsx("span",{title:e.current_calibration_id,children:"✓"}):_jsx("span",{style:{color:"#f59e0b"},children:"none"})})]}),_jsxs(Dialog,{header:"Add New Asset",visible:u.open,style:{width:"32rem"},onHide:()=>m(EMPTY_ADD),footer:_jsxs(_Fragment,{children:[_jsx(Button,{label:"Cancel",severity:"secondary",onClick:()=>m(EMPTY_ADD)}),_jsx(Button,{label:"Create",icon:"pi pi-check",onClick:async()=>{if(!u.assetType)return;const e={};for(const s of x){const t=u.customFields[s.name];if(void 0!==t&&""!==t)switch(s.type){case"f32":case"f64":case"i8":case"i16":case"i32":case"i64":case"u8":case"u16":case"u32":case"u64":{const o=Number(t);Number.isFinite(o)&&(e[s.name]=o);break}case"bool":e[s.name]="true"===t||"1"===t;break;default:e[s.name]=t}}const s={};if(g)for(const e of g.keys){const t={};for(const s of g.fields){const o=coerceField(s,u.subLocationFields[e]?.[s.name]??"");void 0!==o&&(t[s.name]=o)}s[e]=t}const t={asset_type:u.assetType,serial:u.serial,location:u.location,custom:e};g&&(t.sub_locations=s);try{const e=await r("ams.create_asset",MessageType.Request,t);e?.success&&(m(EMPTY_ADD),await o(),e.data?.asset_id&&l({assetType:u.assetType,assetId:e.data.asset_id}))}catch(e){}},disabled:j})]}),children:[_jsxs("div",{style:{display:"grid",gridTemplateColumns:"auto 1fr",gap:"0.5rem 1rem",alignItems:"center"},children:[_jsx("label",{children:"Type *"}),_jsx(Dropdown,{value:u.assetType,options:p,onChange:e=>(e=>{const t=s[e]??[],o=1===t.length?{roleSelection:t[0].location,location:t[0].location}:{roleSelection:"",location:""};m(s=>({...s,assetType:e,...o,customFields:{},subLocationFields:{}}))})(e.value),placeholder:"Choose asset type"}),_jsx("label",{children:"Serial"}),_jsx(InputText,{value:u.serial,onChange:e=>m(s=>({...s,serial:e.target.value}))}),_&&_jsxs(_Fragment,{children:[_jsx("label",{children:"Role *"}),_jsx(Dropdown,{value:u.roleSelection,options:h,onChange:e=>{return s=e.value,void m(s===ROLE_OTHER?e=>({...e,roleSelection:ROLE_OTHER,location:""}):e=>({...e,roleSelection:s,location:s}));var s},placeholder:"Choose where this asset is mounted"}),u.roleSelection===ROLE_OTHER&&_jsxs(_Fragment,{children:[_jsx("label",{children:"Custom role"}),_jsx(InputText,{value:u.location,placeholder:"e.g. tsdr_secondary",onChange:e=>m(s=>({...s,location:e.target.value}))})]})]}),x.map(e=>{const s=e.label??e.name,t=e.units?`${s} [${e.units}]${e.required?" *":""}`:`${s}${e.required?" *":""}`,o="string"!==e.type&&"bool"!==e.type;return _jsxs(React.Fragment,{children:[_jsx("label",{title:e.description??void 0,children:t}),"bool"===e.type?_jsx(Dropdown,{value:u.customFields[e.name]??"",options:[{label:"true",value:"true"},{label:"false",value:"false"}],onChange:s=>m(t=>({...t,customFields:{...t.customFields,[e.name]:s.value}})),placeholder:"—"}):_jsx(InputText,{value:u.customFields[e.name]??"",keyfilter:o?"num":void 0,placeholder:e.description??void 0,onChange:s=>m(t=>({...t,customFields:{...t.customFields,[e.name]:s.target.value}}))})]},e.name)})]}),g&&_jsxs("div",{style:{marginTop:"1.5rem"},children:[_jsx("h4",{style:{margin:"0 0 0.5rem 0"},children:g.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:g.key_label??"Key"}),g.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:g.keys.map(e=>_jsxs("tr",{children:[_jsx("td",{style:{padding:"0.25rem 0.5rem",fontWeight:600},children:e}),g.fields.map(s=>{const t=u.subLocationFields[e]?.[s.name]??"",o="string"!==s.type&&"bool"!==s.type;return _jsx("td",{style:{padding:"0.25rem 0.5rem"},children:_jsx(InputText,{value:t,keyfilter:o?"num":void 0,onChange:t=>{const o=t.target.value;m(t=>({...t,subLocationFields:{...t.subLocationFields,[e]:{...t.subLocationFields[e]??{},[s.name]:o}}}))},style:{width:"100%"}})},s.name)})]},e))})]})})]}),_jsxs("div",{style:{fontSize:"0.875rem",color:"#9ca3af",marginTop:"1rem"},children:[_&&b&&_jsxs(_Fragment,{children:[b.description&&_jsx("p",{style:{margin:"0 0 0.5rem 0"},children:b.description}),_jsxs("p",{style:{margin:"0 0 0.5rem 0",color:"#34d399"},children:["✓ ",roleUsageSummary(b)]})]}),_&&u.roleSelection===ROLE_OTHER&&_jsx("p",{style:{margin:"0 0 0.5rem 0",color:"#f59e0b"},children:"⚠ No test method on this machine references this custom role yet. The asset will be registered but won't be picked up at start_test unless you add an asset_ref to a test method's project.json."}),!_&&u.assetType&&_jsx("p",{style:{margin:"0 0 0.5rem 0"},children:"This asset type isn't selected by physical role on this machine — test methods reference it by ID instead. Just register the asset and pick it by ID on the Test Setup form when running a test."}),_jsxs("p",{style:{margin:0},children:["The asset_id is generated by the server (format:"," ",_jsxs("code",{children:[u.assetType?e[u.assetType]?.id_prefix??"A-":"A-","YYYYMMDDTHHMMSS"]}),"). Manufacturer serial is recorded for traceability but is not used as the unique key."]})]})]})]})};
1
+ import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import React,{useContext,useEffect,useMemo,useState}from"react";import{Button}from"primereact/button";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Dropdown}from"primereact/dropdown";import{InputText}from"primereact/inputtext";import{Dialog}from"primereact/dialog";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";import{useAms}from"./AmsProvider";const ROLE_OTHER="__other__",EMPTY_ADD={open:!1,assetType:"",serial:"",roleSelection:"",location:"",customFields:{},subLocationFields:{}};function roleLabel(e){return e.label??e.location}function roleUsageSummary(e){const s=[];return e.used_by.length>0&&s.push(`Used by test method${1===e.used_by.length?"":"s"}: ${e.used_by.join(", ")}`),e.used_by_modules.length>0&&s.push(`Used by module${1===e.used_by_modules.length?"":"s"}: ${e.used_by_modules.join(", ")}`),0===s.length?"Not referenced by any test method or module.":s.join(" • ")}function fieldsFor(e,s){const t=e?.[s]?.fields;return Array.isArray(t)?t:[]}function subLocationsFor(e,s){const t=e?.[s]?.sub_locations;return t&&Array.isArray(t.keys)&&Array.isArray(t.fields)?t:null}function seedCustomFromRoleDefaults(e,s){const t={};if(!e||!e.defaults)return t;const o=new Set(s.map(e=>e.name));for(const[s,l]of Object.entries(e.defaults))o.has(s)&&null!=l&&(t[s]=String(l));return t}function coerceField(e,s){if(void 0!==s&&""!==s)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(s);return Number.isFinite(e)?e:void 0}case"bool":return"true"===s||"1"===s;default:return s}}export const AssetRegistryTable=()=>{const{schemas:e,roles:s,assets:t,refreshAssets:o,setSelection:l,selection:a}=useAms(),{invoke:n}=useContext(EventEmitterContext),[r,i]=useState(null),[c,d]=useState(null),[u,m]=useState(EMPTY_ADD);useEffect(()=>{const t=t=>{const o=t.detail,l=o?.assetType??"",a=o?.location??"";if(!l)return;const n=(s[l]??[]).find(e=>e.location===a),r=fieldsFor(e,l);m({...EMPTY_ADD,open:!0,assetType:l,roleSelection:a,location:a,customFields:seedCustomFromRoleDefaults(n,r)})};return window.addEventListener("ams:prefill-add",t),()=>window.removeEventListener("ams:prefill-add",t)},[s,e]);const p=useMemo(()=>Object.keys(e).map(s=>({label:e[s]?.label??s,value:s})),[e]),y=useMemo(()=>u.assetType?s[u.assetType]??[]:[],[u.assetType,s]),_=y.length>0,h=useMemo(()=>{const e=y.map(e=>({label:roleLabel(e),value:e.location}));return e.push({label:"Other (advanced — type a custom role)",value:ROLE_OTHER}),e},[y]),f=useMemo(()=>u.roleSelection===ROLE_OTHER?null:y.find(e=>e.location===u.roleSelection)??null,[u.roleSelection,y]),b=useMemo(()=>t.filter(e=>(!r||e.asset_type===r)&&(!c||e.status===c)),[t,r,c]),x=useMemo(()=>fieldsFor(e,u.assetType),[e,u.assetType]),g=useMemo(()=>subLocationsFor(e,u.assetType),[e,u.assetType]),j=!u.assetType||_&&(""===u.roleSelection||u.roleSelection===ROLE_OTHER&&!u.location.trim())||x.some(e=>e.required&&!u.customFields[e.name]?.toString().trim())||(g?.keys.some(e=>g.fields.some(s=>s.required&&!u.subLocationFields[e]?.[s.name]?.toString().trim()))??!1);return _jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"0.5rem"},children:[_jsxs("div",{style:{display:"flex",gap:"0.75rem",alignItems:"center"},children:[_jsx(Dropdown,{value:r,options:[{label:"All Types",value:null},...p],onChange:e=>i(e.value),placeholder:"Filter by type"}),_jsx(Dropdown,{value:c,options:[{label:"All Statuses",value:null},{label:"Active",value:"active"},{label:"Out for Service",value:"out_for_service"},{label:"Retired",value:"retired"}],onChange:e=>d(e.value),placeholder:"Filter by status"}),_jsx("span",{style:{marginLeft:"auto"},children:_jsx(Button,{icon:"pi pi-plus",label:"Add Asset",onClick:()=>m(e=>({...e,open:!0})),disabled:0===p.length})})]}),_jsxs(DataTable,{value:b,selectionMode:"single",selection:b.find(e=>e.asset_id===a.assetId)??null,onSelectionChange:e=>{const s=e.value;s&&l({assetType:s.asset_type,assetId:s.asset_id})},dataKey:"asset_id",emptyMessage:0===p.length?"AMS not enabled in this project (no asset_types declared).":"No assets registered yet.",size:"small",stripedRows:!0,children:[_jsx(Column,{field:"asset_id",header:"Asset ID"}),_jsx(Column,{field:"asset_type",header:"Type",body:s=>e[s.asset_type]?.label??s.asset_type}),_jsx(Column,{field:"serial",header:"Serial"}),_jsx(Column,{field:"location",header:"Role",body:e=>e.location?((e,t)=>{const o=(s[e]??[]).find(e=>e.location===t);return o?roleLabel(o):t})(e.asset_type,e.location):_jsx("span",{style:{color:"#9ca3af"},children:"—"})}),_jsx(Column,{field:"status",header:"Status"}),_jsx(Column,{header:"Calibration",body:e=>e.current_calibration_id?_jsx("span",{title:e.current_calibration_id,children:"✓"}):_jsx("span",{style:{color:"#f59e0b"},children:"none"})})]}),_jsxs(Dialog,{header:"Add New Asset",visible:u.open,style:{width:"32rem"},onHide:()=>m(EMPTY_ADD),footer:_jsxs(_Fragment,{children:[_jsx(Button,{label:"Cancel",severity:"secondary",onClick:()=>m(EMPTY_ADD)}),_jsx(Button,{label:"Create",icon:"pi pi-check",onClick:async()=>{if(!u.assetType)return;const e={};for(const s of x){const t=u.customFields[s.name];if(void 0!==t&&""!==t)switch(s.type){case"f32":case"f64":case"i8":case"i16":case"i32":case"i64":case"u8":case"u16":case"u32":case"u64":{const o=Number(t);Number.isFinite(o)&&(e[s.name]=o);break}case"bool":e[s.name]="true"===t||"1"===t;break;default:e[s.name]=t}}const s={};if(g)for(const e of g.keys){const t={};for(const s of g.fields){const o=coerceField(s,u.subLocationFields[e]?.[s.name]??"");void 0!==o&&(t[s.name]=o)}s[e]=t}const t={asset_type:u.assetType,serial:u.serial,location:u.location,custom:e};g&&(t.sub_locations=s);try{const e=await n("ams.create_asset",MessageType.Request,t);e?.success&&(m(EMPTY_ADD),await o(),e.data?.asset_id&&l({assetType:u.assetType,assetId:e.data.asset_id}))}catch(e){}},disabled:j})]}),children:[_jsxs("div",{style:{display:"grid",gridTemplateColumns:"auto 1fr",gap:"0.5rem 1rem",alignItems:"center"},children:[_jsx("label",{children:"Type *"}),_jsx(Dropdown,{value:u.assetType,options:p,onChange:t=>(t=>{const o=s[t]??[],l=1===o.length?o[0]:void 0,a=l?{roleSelection:l.location,location:l.location}:{roleSelection:"",location:""},n=fieldsFor(e,t);m(e=>({...e,assetType:t,...a,customFields:seedCustomFromRoleDefaults(l,n),subLocationFields:{}}))})(t.value),placeholder:"Choose asset type"}),_jsx("label",{children:"Serial"}),_jsx(InputText,{value:u.serial,onChange:e=>m(s=>({...s,serial:e.target.value}))}),_&&_jsxs(_Fragment,{children:[_jsx("label",{children:"Role *"}),_jsx(Dropdown,{value:u.roleSelection,options:h,onChange:e=>(e=>{if(e===ROLE_OTHER)return void m(e=>({...e,roleSelection:ROLE_OTHER,location:""}));const s=y.find(s=>s.location===e);m(t=>({...t,roleSelection:e,location:e,customFields:seedCustomFromRoleDefaults(s,x)}))})(e.value),placeholder:"Choose where this asset is mounted"}),u.roleSelection===ROLE_OTHER&&_jsxs(_Fragment,{children:[_jsx("label",{children:"Custom role"}),_jsx(InputText,{value:u.location,placeholder:"e.g. tsdr_secondary",onChange:e=>m(s=>({...s,location:e.target.value}))})]})]}),x.map(e=>{const s=e.label??e.name,t=e.units?`${s} [${e.units}]${e.required?" *":""}`:`${s}${e.required?" *":""}`,o="string"!==e.type&&"bool"!==e.type;return _jsxs(React.Fragment,{children:[_jsx("label",{title:e.description??void 0,children:t}),"bool"===e.type?_jsx(Dropdown,{value:u.customFields[e.name]??"",options:[{label:"true",value:"true"},{label:"false",value:"false"}],onChange:s=>m(t=>({...t,customFields:{...t.customFields,[e.name]:s.value}})),placeholder:"—"}):_jsx(InputText,{value:u.customFields[e.name]??"",keyfilter:o?"num":void 0,placeholder:e.description??void 0,onChange:s=>m(t=>({...t,customFields:{...t.customFields,[e.name]:s.target.value}}))})]},e.name)})]}),g&&_jsxs("div",{style:{marginTop:"1.5rem"},children:[_jsx("h4",{style:{margin:"0 0 0.5rem 0"},children:g.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:g.key_label??"Key"}),g.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:g.keys.map(e=>_jsxs("tr",{children:[_jsx("td",{style:{padding:"0.25rem 0.5rem",fontWeight:600},children:e}),g.fields.map(s=>{const t=u.subLocationFields[e]?.[s.name]??"",o="string"!==s.type&&"bool"!==s.type;return _jsx("td",{style:{padding:"0.25rem 0.5rem"},children:_jsx(InputText,{value:t,keyfilter:o?"num":void 0,onChange:t=>{const o=t.target.value;m(t=>({...t,subLocationFields:{...t.subLocationFields,[e]:{...t.subLocationFields[e]??{},[s.name]:o}}}))},style:{width:"100%"}})},s.name)})]},e))})]})})]}),_jsxs("div",{style:{fontSize:"0.875rem",color:"#9ca3af",marginTop:"1rem"},children:[_&&f&&_jsxs(_Fragment,{children:[f.description&&_jsx("p",{style:{margin:"0 0 0.5rem 0"},children:f.description}),_jsxs("p",{style:{margin:"0 0 0.5rem 0",color:"#34d399"},children:["✓ ",roleUsageSummary(f)]})]}),_&&u.roleSelection===ROLE_OTHER&&_jsx("p",{style:{margin:"0 0 0.5rem 0",color:"#f59e0b"},children:"⚠ No test method on this machine references this custom role yet. The asset will be registered but won't be picked up at start_test unless you add an asset_ref to a test method's project.json."}),!_&&u.assetType&&_jsx("p",{style:{margin:"0 0 0.5rem 0"},children:"This asset type isn't selected by physical role on this machine — test methods reference it by ID instead. Just register the asset and pick it by ID on the Test Setup form when running a test."}),_jsxs("p",{style:{margin:0},children:["The asset_id is generated by the server (format:"," ",_jsxs("code",{children:[u.assetType?e[u.assetType]?.id_prefix??"A-":"A-","YYYYMMDDTHHMMSS"]}),"). Manufacturer serial is recorded for traceability but is not used as the unique key."]})]})]})]})};
@@ -1 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import*as React from"react";import"./forms.css";export const FormRow=({label:r,required:s,hint:a,error:e,htmlFor:o,children:l})=>_jsxs("div",{className:"ac-form-row"+(e?" ac-form-row--error":""),children:[_jsxs("label",{className:"ac-form-row__label",htmlFor:o,children:[r,s&&_jsx("span",{className:"ac-form-row__required","aria-hidden":!0,children:" *"}),a&&_jsx("small",{className:"ac-form-row__hint",children:a})]}),_jsxs("div",{className:"ac-form-row__field",children:[l,e&&_jsx("small",{className:"ac-form-row__error",children:e})]})]});export default FormRow;
1
+ import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import*as React from"react";import"./forms.css";export const FormRow=({label:r,required:s,hint:a,error:e,htmlFor:o,children:l})=>_jsxs("div",{className:"ac-formrow"+(e?" ac-formrow--error":""),children:[_jsxs("label",{className:"ac-formrow__label",htmlFor:o,children:[r,s&&_jsx("span",{className:"ac-formrow__required","aria-hidden":!0,children:" *"}),a&&_jsx("small",{className:"ac-formrow__hint",children:a})]}),_jsxs("div",{className:"ac-formrow__field",children:[l,e&&_jsx("small",{className:"ac-formrow__error",children:e})]})]});export default FormRow;
@@ -1 +1 @@
1
- import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import*as React from"react";import"./forms.css";export const FormSection=({title:s,description:c,actions:e,children:a})=>_jsxs("section",{className:"ac-form-section",children:[(s||e)&&_jsxs("header",{className:"ac-form-section__header",children:[_jsxs("div",{children:[s&&_jsx("h3",{className:"ac-form-section__title",children:s}),c&&_jsx("small",{className:"ac-form-section__desc",children:c})]}),e&&_jsx("div",{className:"ac-form-section__actions",children:e})]}),_jsx("div",{className:"ac-form-section__body",children:a})]});export default FormSection;
1
+ import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import*as React from"react";import"./forms.css";export const FormSection=({title:s,description:c,actions:e,children:a})=>_jsxs("section",{className:"ac-formsection",children:[(s||e)&&_jsxs("header",{className:"ac-formsection__header",children:[_jsxs("div",{children:[s&&_jsx("h3",{className:"ac-formsection__title",children:s}),c&&_jsx("small",{className:"ac-formsection__desc",children:c})]}),e&&_jsx("div",{className:"ac-formsection__actions",children:e})]}),_jsx("div",{className:"ac-formsection__body",children:a})]});export default FormSection;
@@ -1,11 +1,11 @@
1
- .ac-form-section {
1
+ .ac-formsection {
2
2
  border: 1px solid var(--surface-d, #e2e8f0);
3
3
  border-radius: 6px;
4
4
  background: var(--surface-card, #fff);
5
5
  margin-bottom: 1rem;
6
6
  }
7
7
 
8
- .ac-form-section__header {
8
+ .ac-formsection__header {
9
9
  display: flex;
10
10
  align-items: flex-start;
11
11
  justify-content: space-between;
@@ -16,74 +16,74 @@
16
16
  border-top-right-radius: 6px;
17
17
  }
18
18
 
19
- .ac-form-section__title {
19
+ .ac-formsection__title {
20
20
  margin: 0;
21
21
  font-size: 0.95rem;
22
22
  font-weight: 600;
23
23
  }
24
24
 
25
- .ac-form-section__desc {
25
+ .ac-formsection__desc {
26
26
  color: var(--text-color-secondary, #64748b);
27
27
  display: block;
28
28
  margin-top: 0.15rem;
29
29
  }
30
30
 
31
- .ac-form-section__actions {
31
+ .ac-formsection__actions {
32
32
  display: flex;
33
33
  gap: 0.5rem;
34
34
  }
35
35
 
36
- .ac-form-section__body {
36
+ .ac-formsection__body {
37
37
  padding: 0.75rem 1rem;
38
38
  display: flex;
39
39
  flex-direction: column;
40
40
  gap: 0.5rem;
41
41
  }
42
42
 
43
- .ac-form-row {
43
+ .ac-formrow {
44
44
  display: grid;
45
45
  grid-template-columns: minmax(8rem, 14rem) 1fr;
46
46
  gap: 0.5rem 1rem;
47
47
  align-items: start;
48
48
  }
49
49
 
50
- .ac-form-row--error .ac-form-row__field input,
51
- .ac-form-row--error .ac-form-row__field .p-inputtext {
50
+ .ac-formrow--error .ac-formrow__field input,
51
+ .ac-formrow--error .ac-formrow__field .p-inputtext {
52
52
  border-color: #dc2626;
53
53
  }
54
54
 
55
- .ac-form-row__label {
55
+ .ac-formrow__label {
56
56
  font-weight: 500;
57
57
  padding-top: 0.4rem;
58
58
  display: flex;
59
59
  flex-direction: column;
60
60
  }
61
61
 
62
- .ac-form-row__required {
62
+ .ac-formrow__required {
63
63
  color: #dc2626;
64
64
  }
65
65
 
66
- .ac-form-row__hint {
66
+ .ac-formrow__hint {
67
67
  color: var(--text-color-secondary, #64748b);
68
68
  font-weight: 400;
69
69
  font-size: 0.75rem;
70
70
  margin-top: 0.15rem;
71
71
  }
72
72
 
73
- .ac-form-row__field {
73
+ .ac-formrow__field {
74
74
  display: flex;
75
75
  flex-direction: column;
76
76
  gap: 0.25rem;
77
77
  }
78
78
 
79
- .ac-form-row__field > input,
80
- .ac-form-row__field > .p-inputtext,
81
- .ac-form-row__field > .p-dropdown,
82
- .ac-form-row__field > .p-inputtextarea {
79
+ .ac-formrow__field > input,
80
+ .ac-formrow__field > .p-inputtext,
81
+ .ac-formrow__field > .p-dropdown,
82
+ .ac-formrow__field > .p-inputtextarea {
83
83
  width: 100%;
84
84
  }
85
85
 
86
- .ac-form-row__error {
86
+ .ac-formrow__error {
87
87
  color: #dc2626;
88
88
  font-size: 0.75rem;
89
89
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adcops/autocore-react",
3
- "version": "3.3.87",
3
+ "version": "3.3.89",
4
4
  "description": "A React component library for industrial user interfaces.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -0,0 +1,38 @@
1
+ /*
2
+ * Copyright (C) 2025 Automated Design Corp. All Rights Reserved.
3
+ * Created Date: 2025-08-28 11:27:20
4
+ * -----
5
+ * Last Modified: 2025-08-28 11:38:53
6
+ * -----
7
+ *
8
+ */
9
+
10
+
11
+ import type { SVGProps } from "react";
12
+
13
+ export const SvgAxisC = (props: SVGProps<SVGSVGElement>) => (
14
+
15
+ <svg
16
+ xmlns="http://www.w3.org/2000/svg"
17
+ width="24"
18
+ height="24"
19
+ viewBox="0 0 24 24"
20
+ fill="none"
21
+ stroke="currentColor"
22
+ strokeWidth="2"
23
+ strokeLinecap="round"
24
+ strokeLinejoin="round"
25
+ {...props}
26
+ >
27
+ {/* The Letter C - Centered slightly left */}
28
+ <path d="M15 10a4 4 1 1 0 0 6" />
29
+
30
+ {/* The Circular Arrow - Arc ends at (12, 2.1) */}
31
+ <path d="M12 2a10 10 0 1 0 9.5 7" />
32
+
33
+ {/* The Arrowhead - Pointing specifically to (12, 2) */}
34
+ <path d="m11 5 4-5-4-4" transform="translate(0 2)" />
35
+ </svg>
36
+ );
37
+
38
+ export default SvgAxisC;
@@ -1,33 +1,33 @@
1
- /*
2
- * Copyright (C) 2025 Automated Design Corp. All Rights Reserved.
3
- * Created Date: 2025-08-28 11:27:20
4
- * -----
5
- * Last Modified: 2025-08-28 11:38:53
6
- * -----
7
- *
8
- */
9
-
10
-
11
- import type { SVGProps } from "react";
12
-
13
- export const SvgAxisX = (props: SVGProps<SVGSVGElement>) => (
14
- <svg
15
- xmlns="http://www.w3.org/2000/svg"
16
- width="24"
17
- height="24"
18
- viewBox="0 0 24 24"
19
- fill="none"
20
- stroke="currentColor"
21
- strokeWidth="2"
22
- strokeLinecap="round"
23
- strokeLinejoin="round"
24
- {...props}
25
- >
26
- {/* Letter X */}
27
- <path d="M5 2l14 12M18 2l-13 12" />
28
- {/* Bi-directional horizontal arrow */}
29
- <path d="M2 22h20M2 22l3-3M2 22l3 3M22 22l-3-3M22 22l-3 3" />
30
- </svg>
31
- );
32
-
1
+ /*
2
+ * Copyright (C) 2025 Automated Design Corp. All Rights Reserved.
3
+ * Created Date: 2025-08-28 11:27:20
4
+ * -----
5
+ * Last Modified: 2025-08-28 11:38:53
6
+ * -----
7
+ *
8
+ */
9
+
10
+
11
+ import type { SVGProps } from "react";
12
+
13
+ export const SvgAxisX = (props: SVGProps<SVGSVGElement>) => (
14
+ <svg
15
+ xmlns="http://www.w3.org/2000/svg"
16
+ width="24"
17
+ height="24"
18
+ viewBox="0 0 24 24"
19
+ fill="none"
20
+ stroke="currentColor"
21
+ strokeWidth="2"
22
+ strokeLinecap="round"
23
+ strokeLinejoin="round"
24
+ {...props}
25
+ >
26
+ {/* Bi-directional horizontal arrow */}
27
+ <path d="M4 18h16M4 18l4-4M4 18l4 4M20 18l-4-4M20 18l-4 4" />
28
+ {/* Larger Letter X */}
29
+ <path d="M9 2l6 6m0-6l-6 6" />
30
+ </svg>
31
+ );
32
+
33
33
  export default SvgAxisX;
@@ -1,35 +1,35 @@
1
- /*
2
- * Copyright (C) 2025 Automated Design Corp. All Rights Reserved.
3
- * Created Date: 2025-08-28 11:27:59
4
- * -----
5
- * Last Modified: 2025-08-28 11:42:59
6
- * -----
7
- *
8
- */
9
-
10
-
11
- import type { SVGProps } from "react";
12
-
13
- export const SvgAxisY = (props: SVGProps<SVGSVGElement>) => (
14
- <svg
15
- xmlns="http://www.w3.org/2000/svg"
16
- width="24"
17
- height="24"
18
- viewBox="0 0 24 24"
19
- fill="none"
20
- stroke="currentColor"
21
- strokeWidth="2"
22
- strokeLinecap="round"
23
- strokeLinejoin="round"
24
- {...props}
25
- >
26
- {/* Bi-directional diagonal arrow (rotated across the full viewBox) */}
27
- <g transform="rotate(-45 12 12)">
28
- <path d="M0 8h22M0 8l4-4M0 8l4 4M23 8l-4-4M23 8l-4 4" />
29
- </g>
30
- {/* Letter Y (bottom-right corner, below the diagonal) */}
31
- <path d="M13 12l5 7v7M23 12l-5 6" />
32
- </svg>
33
- );
34
-
1
+ /*
2
+ * Copyright (C) 2025 Automated Design Corp. All Rights Reserved.
3
+ * Created Date: 2025-08-28 11:27:59
4
+ * -----
5
+ * Last Modified: 2025-08-28 11:42:59
6
+ * -----
7
+ *
8
+ */
9
+
10
+
11
+ import type { SVGProps } from "react";
12
+
13
+ export const SvgAxisY = (props: SVGProps<SVGSVGElement>) => (
14
+ <svg
15
+ xmlns="http://www.w3.org/2000/svg"
16
+ width="24"
17
+ height="24"
18
+ viewBox="0 0 24 24"
19
+ fill="none"
20
+ stroke="currentColor"
21
+ strokeWidth="2"
22
+ strokeLinecap="round"
23
+ strokeLinejoin="round"
24
+ {...props}
25
+ >
26
+ {/* Bi-directional diagonal arrow (rotated) */}
27
+ <g transform="rotate(-45 6 12)">
28
+ <path d="M4 12h16M4 12l4-4M4 12l4 4M20 12l-4-4M20 12l-4 4" />
29
+ </g>
30
+ {/* Larger Letter Y */}
31
+ <path d="M15 12l3 4v5 M21 12l-3 4" />
32
+ </svg>
33
+ );
34
+
35
35
  export default SvgAxisY;
@@ -1,32 +1,32 @@
1
- /*
2
- * Copyright (C) 2025 Automated Design Corp. All Rights Reserved.
3
- * Created Date: 2025-08-28 11:28:14
4
- * -----
5
- * Last Modified: 2025-08-28 11:39:53
6
- * -----
7
- *
8
- */
9
-
10
- import type { SVGProps } from "react";
11
-
12
- export const SvgAxisZ = (props: SVGProps<SVGSVGElement>) => (
13
- <svg
14
- xmlns="http://www.w3.org/2000/svg"
15
- width="24"
16
- height="24"
17
- viewBox="0 0 24 24"
18
- fill="none"
19
- stroke="currentColor"
20
- strokeWidth="2"
21
- strokeLinecap="round"
22
- strokeLinejoin="round"
23
- {...props}
24
- >
25
- {/* Bi-directional vertical arrow */}
26
- <path d="M18 2v20M15 5l3-3 3 3M15 19l3 3 3-3" />
27
- {/* Letter Z */}
28
- <path d="M1 4h10l-10 15h10" />
29
- </svg>
30
- );
31
-
1
+ /*
2
+ * Copyright (C) 2025 Automated Design Corp. All Rights Reserved.
3
+ * Created Date: 2025-08-28 11:28:14
4
+ * -----
5
+ * Last Modified: 2025-08-28 11:39:53
6
+ * -----
7
+ *
8
+ */
9
+
10
+ import type { SVGProps } from "react";
11
+
12
+ export const SvgAxisZ = (props: SVGProps<SVGSVGElement>) => (
13
+ <svg
14
+ xmlns="http://www.w3.org/2000/svg"
15
+ width="24"
16
+ height="24"
17
+ viewBox="0 0 24 24"
18
+ fill="none"
19
+ stroke="currentColor"
20
+ strokeWidth="2"
21
+ strokeLinecap="round"
22
+ strokeLinejoin="round"
23
+ {...props}
24
+ >
25
+ {/* Bi-directional vertical arrow */}
26
+ <path d="M18 4v16M14 8l4-4 4 4M14 16l4 4 4-4" />
27
+ {/* Larger Letter Z */}
28
+ <path d="M2 10h6l-6 6h6" />
29
+ </svg>
30
+ );
31
+
32
32
  export default SvgAxisZ;
@@ -55,6 +55,16 @@ export interface AmsRole {
55
55
  * feeding NI bridge channel calibration). Empty for purely
56
56
  * test-method-driven roles. */
57
57
  used_by_modules: string[];
58
+ /** Optional per-field seed values declared on the asset_ref's
59
+ * `defaults` map. AIS uses these to pre-fill nameplate inputs when
60
+ * the operator creates a new asset for this role — the operator
61
+ * confirms or edits, the values are never enforced. Absent when no
62
+ * asset_ref declared defaults (the common case, since built-in
63
+ * load_cell etc. didn't support defaults before the feature
64
+ * landed). Keys are field names from the asset_type's schema;
65
+ * values are untyped JSON (numbers, strings, booleans) matching
66
+ * the field's declared `type`. */
67
+ defaults?: Record<string, any>;
58
68
  }
59
69
  export type AmsRoleRegistry = { [assetType: string]: AmsRole[] };
60
70
 
@@ -111,6 +111,32 @@ function subLocationsFor(schemas: any, assetType: string): SubLocationsSchema |
111
111
  return sl as SubLocationsSchema;
112
112
  }
113
113
 
114
+ /** Build a fresh `customFields` seed from a role's `defaults` map,
115
+ * narrowed to fields the asset_type schema actually declares. Server
116
+ * validation rejects unknown default keys at project load, so in
117
+ * normal operation every key will match a declared field; the
118
+ * narrowing here just protects against schema/defaults drift while a
119
+ * project is being edited.
120
+ *
121
+ * Returns string-typed values because the dialog's inputs are text-
122
+ * based; the existing `coerceField` helper converts back on submit.
123
+ * Empty / missing defaults are skipped (rather than emitting `""`) so
124
+ * required-field gating still fires for fields the role didn't seed. */
125
+ function seedCustomFromRoleDefaults(
126
+ role: AmsRole | undefined,
127
+ fields: SchemaField[],
128
+ ): Record<string, string> {
129
+ const out: Record<string, string> = {};
130
+ if (!role || !role.defaults) return out;
131
+ const declared = new Set(fields.map(f => f.name));
132
+ for (const [k, v] of Object.entries(role.defaults)) {
133
+ if (!declared.has(k)) continue;
134
+ if (v === undefined || v === null) continue;
135
+ out[k] = String(v);
136
+ }
137
+ return out;
138
+ }
139
+
114
140
  /** Coerce a per-cell string from the matrix input back to its declared
115
141
  * type. Numeric strings → JSON numbers; booleans → bool; everything
116
142
  * else stays as a string. Matches the top-level fields coercion. */
@@ -142,6 +168,8 @@ export const AssetRegistryTable: React.FC = () => {
142
168
  // when an operator clicks Register on one of its rows. Catch it
143
169
  // here and open the Add dialog pre-populated with the asset_type
144
170
  // and role — the operator just fills in the nameplate values.
171
+ // If the matching role declares `defaults`, those seed the
172
+ // nameplate inputs too (same path as onRoleChange).
145
173
  useEffect(() => {
146
174
  const handler = (e: Event) => {
147
175
  const detail = (e as CustomEvent).detail as
@@ -149,17 +177,20 @@ export const AssetRegistryTable: React.FC = () => {
149
177
  const assetType = detail?.assetType ?? '';
150
178
  const location = detail?.location ?? '';
151
179
  if (!assetType) return;
180
+ const role = (roles[assetType] ?? []).find(r => r.location === location);
181
+ const declaredFields = fieldsFor(schemas, assetType);
152
182
  setAddState({
153
183
  ...EMPTY_ADD,
154
184
  open: true,
155
185
  assetType,
156
186
  roleSelection: location,
157
187
  location,
188
+ customFields: seedCustomFromRoleDefaults(role, declaredFields),
158
189
  });
159
190
  };
160
191
  window.addEventListener('ams:prefill-add', handler);
161
192
  return () => window.removeEventListener('ams:prefill-add', handler);
162
- }, []);
193
+ }, [roles, schemas]);
163
194
 
164
195
  const typeOptions = useMemo(
165
196
  () => Object.keys(schemas).map(k => ({ label: schemas[k]?.label ?? k, value: k })),
@@ -285,15 +316,20 @@ export const AssetRegistryTable: React.FC = () => {
285
316
  * hardware), or leave it empty so they make an explicit choice
286
317
  * when there are multiple. Hide the field entirely when none.
287
318
  * Also resets the nameplate-field map — fields are type-specific
288
- * and the previous type's inputs would not be relevant. */
319
+ * and the previous type's inputs would not be relevant. When the
320
+ * auto-selected role declares `defaults`, seed those into the
321
+ * nameplate inputs so the operator only has to confirm or edit. */
289
322
  const onAssetTypeChange = (newType: string) => {
290
323
  const list = roles[newType] ?? [];
291
- const base = list.length === 1
292
- ? { roleSelection: list[0].location, location: list[0].location }
324
+ const autoRole = list.length === 1 ? list[0] : undefined;
325
+ const base = autoRole
326
+ ? { roleSelection: autoRole.location, location: autoRole.location }
293
327
  : { roleSelection: '', location: '' };
328
+ const newFields = fieldsFor(schemas, newType);
294
329
  setAddState(s => ({
295
330
  ...s, assetType: newType, ...base,
296
- customFields: {}, subLocationFields: {},
331
+ customFields: seedCustomFromRoleDefaults(autoRole, newFields),
332
+ subLocationFields: {},
297
333
  }));
298
334
  };
299
335
 
@@ -315,13 +351,22 @@ export const AssetRegistryTable: React.FC = () => {
315
351
  );
316
352
 
317
353
  /** Role dropdown change handler. ROLE_OTHER puts us into free-form
318
- * mode where the operator types into a text field. */
354
+ * mode where the operator types into a text field. When picking a
355
+ * known role with declared `defaults`, seed the nameplate inputs
356
+ * from those values so the operator only has to confirm or edit.
357
+ * Switching role is treated as an explicit reseed; values typed
358
+ * into the previous role's inputs are replaced — switching role
359
+ * is a discrete intent, not a continuous edit. */
319
360
  const onRoleChange = (value: string) => {
320
361
  if (value === ROLE_OTHER) {
321
362
  setAddState(s => ({ ...s, roleSelection: ROLE_OTHER, location: '' }));
322
- } else {
323
- setAddState(s => ({ ...s, roleSelection: value, location: value }));
363
+ return;
324
364
  }
365
+ const role = rolesForType.find(r => r.location === value);
366
+ setAddState(s => ({
367
+ ...s, roleSelection: value, location: value,
368
+ customFields: seedCustomFromRoleDefaults(role, fieldsForType),
369
+ }));
325
370
  };
326
371
 
327
372
  /** Disable Create until the operator has picked a role (or supplied
@@ -20,15 +20,15 @@ export interface FormRowProps {
20
20
 
21
21
  export const FormRow: React.FC<FormRowProps> = ({ label, required, hint, error, htmlFor, children }) => {
22
22
  return (
23
- <div className={`ac-form-row${error ? ' ac-form-row--error' : ''}`}>
24
- <label className="ac-form-row__label" htmlFor={htmlFor}>
23
+ <div className={`ac-formrow${error ? ' ac-formrow--error' : ''}`}>
24
+ <label className="ac-formrow__label" htmlFor={htmlFor}>
25
25
  {label}
26
- {required && <span className="ac-form-row__required" aria-hidden> *</span>}
27
- {hint && <small className="ac-form-row__hint">{hint}</small>}
26
+ {required && <span className="ac-formrow__required" aria-hidden> *</span>}
27
+ {hint && <small className="ac-formrow__hint">{hint}</small>}
28
28
  </label>
29
- <div className="ac-form-row__field">
29
+ <div className="ac-formrow__field">
30
30
  {children}
31
- {error && <small className="ac-form-row__error">{error}</small>}
31
+ {error && <small className="ac-formrow__error">{error}</small>}
32
32
  </div>
33
33
  </div>
34
34
  );
@@ -19,17 +19,17 @@ export interface FormSectionProps {
19
19
 
20
20
  export const FormSection: React.FC<FormSectionProps> = ({ title, description, actions, children }) => {
21
21
  return (
22
- <section className="ac-form-section">
22
+ <section className="ac-formsection">
23
23
  {(title || actions) && (
24
- <header className="ac-form-section__header">
24
+ <header className="ac-formsection__header">
25
25
  <div>
26
- {title && <h3 className="ac-form-section__title">{title}</h3>}
27
- {description && <small className="ac-form-section__desc">{description}</small>}
26
+ {title && <h3 className="ac-formsection__title">{title}</h3>}
27
+ {description && <small className="ac-formsection__desc">{description}</small>}
28
28
  </div>
29
- {actions && <div className="ac-form-section__actions">{actions}</div>}
29
+ {actions && <div className="ac-formsection__actions">{actions}</div>}
30
30
  </header>
31
31
  )}
32
- <div className="ac-form-section__body">
32
+ <div className="ac-formsection__body">
33
33
  {children}
34
34
  </div>
35
35
  </section>
@@ -1,11 +1,11 @@
1
- .ac-form-section {
1
+ .ac-formsection {
2
2
  border: 1px solid var(--surface-d, #e2e8f0);
3
3
  border-radius: 6px;
4
4
  background: var(--surface-card, #fff);
5
5
  margin-bottom: 1rem;
6
6
  }
7
7
 
8
- .ac-form-section__header {
8
+ .ac-formsection__header {
9
9
  display: flex;
10
10
  align-items: flex-start;
11
11
  justify-content: space-between;
@@ -16,74 +16,74 @@
16
16
  border-top-right-radius: 6px;
17
17
  }
18
18
 
19
- .ac-form-section__title {
19
+ .ac-formsection__title {
20
20
  margin: 0;
21
21
  font-size: 0.95rem;
22
22
  font-weight: 600;
23
23
  }
24
24
 
25
- .ac-form-section__desc {
25
+ .ac-formsection__desc {
26
26
  color: var(--text-color-secondary, #64748b);
27
27
  display: block;
28
28
  margin-top: 0.15rem;
29
29
  }
30
30
 
31
- .ac-form-section__actions {
31
+ .ac-formsection__actions {
32
32
  display: flex;
33
33
  gap: 0.5rem;
34
34
  }
35
35
 
36
- .ac-form-section__body {
36
+ .ac-formsection__body {
37
37
  padding: 0.75rem 1rem;
38
38
  display: flex;
39
39
  flex-direction: column;
40
40
  gap: 0.5rem;
41
41
  }
42
42
 
43
- .ac-form-row {
43
+ .ac-formrow {
44
44
  display: grid;
45
45
  grid-template-columns: minmax(8rem, 14rem) 1fr;
46
46
  gap: 0.5rem 1rem;
47
47
  align-items: start;
48
48
  }
49
49
 
50
- .ac-form-row--error .ac-form-row__field input,
51
- .ac-form-row--error .ac-form-row__field .p-inputtext {
50
+ .ac-formrow--error .ac-formrow__field input,
51
+ .ac-formrow--error .ac-formrow__field .p-inputtext {
52
52
  border-color: #dc2626;
53
53
  }
54
54
 
55
- .ac-form-row__label {
55
+ .ac-formrow__label {
56
56
  font-weight: 500;
57
57
  padding-top: 0.4rem;
58
58
  display: flex;
59
59
  flex-direction: column;
60
60
  }
61
61
 
62
- .ac-form-row__required {
62
+ .ac-formrow__required {
63
63
  color: #dc2626;
64
64
  }
65
65
 
66
- .ac-form-row__hint {
66
+ .ac-formrow__hint {
67
67
  color: var(--text-color-secondary, #64748b);
68
68
  font-weight: 400;
69
69
  font-size: 0.75rem;
70
70
  margin-top: 0.15rem;
71
71
  }
72
72
 
73
- .ac-form-row__field {
73
+ .ac-formrow__field {
74
74
  display: flex;
75
75
  flex-direction: column;
76
76
  gap: 0.25rem;
77
77
  }
78
78
 
79
- .ac-form-row__field > input,
80
- .ac-form-row__field > .p-inputtext,
81
- .ac-form-row__field > .p-dropdown,
82
- .ac-form-row__field > .p-inputtextarea {
79
+ .ac-formrow__field > input,
80
+ .ac-formrow__field > .p-inputtext,
81
+ .ac-formrow__field > .p-dropdown,
82
+ .ac-formrow__field > .p-inputtextarea {
83
83
  width: 100%;
84
84
  }
85
85
 
86
- .ac-form-row__error {
86
+ .ac-formrow__error {
87
87
  color: #dc2626;
88
88
  font-size: 0.75rem;
89
89
  }