@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.
- package/dist/assets/AxisC.d.ts +4 -0
- package/dist/assets/AxisC.d.ts.map +1 -0
- package/dist/assets/AxisC.js +1 -0
- package/dist/assets/AxisX.js +1 -1
- package/dist/assets/AxisY.js +1 -1
- package/dist/assets/AxisZ.js +1 -1
- package/dist/components/ams/AmsProvider.d.ts +10 -0
- package/dist/components/ams/AmsProvider.d.ts.map +1 -1
- package/dist/components/ams/AssetRegistryTable.d.ts.map +1 -1
- package/dist/components/ams/AssetRegistryTable.js +1 -1
- package/dist/components/forms/FormRow.js +1 -1
- package/dist/components/forms/FormSection.js +1 -1
- package/dist/components/forms/forms.css +18 -18
- package/package.json +1 -1
- package/src/assets/AxisC.tsx +38 -0
- package/src/assets/AxisX.tsx +32 -32
- package/src/assets/AxisY.tsx +34 -34
- package/src/assets/AxisZ.tsx +31 -31
- package/src/components/ams/AmsProvider.tsx +10 -0
- package/src/components/ams/AssetRegistryTable.tsx +53 -8
- package/src/components/forms/FormRow.tsx +6 -6
- package/src/components/forms/FormSection.tsx +6 -6
- package/src/components/forms/forms.css +18 -18
|
@@ -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;
|
package/dist/assets/AxisX.js
CHANGED
|
@@ -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:"
|
|
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;
|
package/dist/assets/AxisY.js
CHANGED
|
@@ -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
|
|
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;
|
package/dist/assets/AxisZ.js
CHANGED
|
@@ -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
|
|
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;
|
|
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;
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
31
|
+
.ac-formsection__actions {
|
|
32
32
|
display: flex;
|
|
33
33
|
gap: 0.5rem;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
.ac-
|
|
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-
|
|
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-
|
|
51
|
-
.ac-
|
|
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-
|
|
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-
|
|
62
|
+
.ac-formrow__required {
|
|
63
63
|
color: #dc2626;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
.ac-
|
|
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-
|
|
73
|
+
.ac-formrow__field {
|
|
74
74
|
display: flex;
|
|
75
75
|
flex-direction: column;
|
|
76
76
|
gap: 0.25rem;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
.ac-
|
|
80
|
-
.ac-
|
|
81
|
-
.ac-
|
|
82
|
-
.ac-
|
|
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-
|
|
86
|
+
.ac-formrow__error {
|
|
87
87
|
color: #dc2626;
|
|
88
88
|
font-size: 0.75rem;
|
|
89
89
|
}
|
package/package.json
CHANGED
|
@@ -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;
|
package/src/assets/AxisX.tsx
CHANGED
|
@@ -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
|
-
{/*
|
|
27
|
-
<path d="
|
|
28
|
-
{/*
|
|
29
|
-
<path d="
|
|
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;
|
package/src/assets/AxisY.tsx
CHANGED
|
@@ -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
|
|
27
|
-
<g transform="rotate(-45
|
|
28
|
-
<path d="
|
|
29
|
-
</g>
|
|
30
|
-
{/* Letter Y
|
|
31
|
-
<path d="
|
|
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;
|
package/src/assets/AxisZ.tsx
CHANGED
|
@@ -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
|
|
27
|
-
{/* Letter Z */}
|
|
28
|
-
<path d="
|
|
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
|
|
292
|
-
|
|
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:
|
|
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
|
-
|
|
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-
|
|
24
|
-
<label className="ac-
|
|
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-
|
|
27
|
-
{hint && <small className="ac-
|
|
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-
|
|
29
|
+
<div className="ac-formrow__field">
|
|
30
30
|
{children}
|
|
31
|
-
{error && <small className="ac-
|
|
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-
|
|
22
|
+
<section className="ac-formsection">
|
|
23
23
|
{(title || actions) && (
|
|
24
|
-
<header className="ac-
|
|
24
|
+
<header className="ac-formsection__header">
|
|
25
25
|
<div>
|
|
26
|
-
{title && <h3 className="ac-
|
|
27
|
-
{description && <small className="ac-
|
|
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-
|
|
29
|
+
{actions && <div className="ac-formsection__actions">{actions}</div>}
|
|
30
30
|
</header>
|
|
31
31
|
)}
|
|
32
|
-
<div className="ac-
|
|
32
|
+
<div className="ac-formsection__body">
|
|
33
33
|
{children}
|
|
34
34
|
</div>
|
|
35
35
|
</section>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
.ac-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
31
|
+
.ac-formsection__actions {
|
|
32
32
|
display: flex;
|
|
33
33
|
gap: 0.5rem;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
.ac-
|
|
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-
|
|
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-
|
|
51
|
-
.ac-
|
|
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-
|
|
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-
|
|
62
|
+
.ac-formrow__required {
|
|
63
63
|
color: #dc2626;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
.ac-
|
|
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-
|
|
73
|
+
.ac-formrow__field {
|
|
74
74
|
display: flex;
|
|
75
75
|
flex-direction: column;
|
|
76
76
|
gap: 0.25rem;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
.ac-
|
|
80
|
-
.ac-
|
|
81
|
-
.ac-
|
|
82
|
-
.ac-
|
|
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-
|
|
86
|
+
.ac-formrow__error {
|
|
87
87
|
color: #dc2626;
|
|
88
88
|
font-size: 0.75rem;
|
|
89
89
|
}
|