@adcops/autocore-react 3.3.112 → 3.3.113
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ams/AmsProvider.d.ts.map +1 -1
- package/dist/components/ams/AmsProvider.js +1 -1
- package/dist/components/ams-editor/AssetFieldArray.d.ts +21 -0
- package/dist/components/ams-editor/AssetFieldArray.d.ts.map +1 -0
- package/dist/components/ams-editor/AssetFieldArray.js +1 -0
- package/dist/components/ams-editor/AssetTypeEditor.css +122 -0
- package/dist/components/ams-editor/AssetTypeEditor.d.ts +26 -0
- package/dist/components/ams-editor/AssetTypeEditor.d.ts.map +1 -0
- package/dist/components/ams-editor/AssetTypeEditor.js +1 -0
- package/dist/components/ams-editor/AssetTypeFormEditor.d.ts +21 -0
- package/dist/components/ams-editor/AssetTypeFormEditor.d.ts.map +1 -0
- package/dist/components/ams-editor/AssetTypeFormEditor.js +1 -0
- package/dist/components/ams-editor/SubLocationsEditor.d.ts +17 -0
- package/dist/components/ams-editor/SubLocationsEditor.d.ts.map +1 -0
- package/dist/components/ams-editor/SubLocationsEditor.js +1 -0
- package/dist/components/ams-editor/index.d.ts +7 -0
- package/dist/components/ams-editor/index.d.ts.map +1 -0
- package/dist/components/ams-editor/index.js +1 -0
- package/dist/components/ams-editor/types.d.ts +34 -0
- package/dist/components/ams-editor/types.d.ts.map +1 -0
- package/dist/components/ams-editor/types.js +1 -0
- package/dist/components/tis-editor/TisConfigEditor.d.ts +1 -1
- package/dist/components/tis-editor/editor/SaveDiffDialog.d.ts.map +1 -1
- package/dist/components/tis-editor/editor/SaveDiffDialog.js +1 -1
- package/dist/hooks/useAmsSchemaConfig.d.ts +39 -0
- package/dist/hooks/useAmsSchemaConfig.d.ts.map +1 -0
- package/dist/hooks/useAmsSchemaConfig.js +1 -0
- package/dist/hooks/useTisConfig.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/ams/AmsProvider.tsx +20 -15
- package/src/components/ams-editor/AssetFieldArray.tsx +103 -0
- package/src/components/ams-editor/AssetTypeEditor.css +122 -0
- package/src/components/ams-editor/AssetTypeEditor.tsx +295 -0
- package/src/components/ams-editor/AssetTypeFormEditor.tsx +178 -0
- package/src/components/ams-editor/SubLocationsEditor.tsx +151 -0
- package/src/components/ams-editor/index.ts +16 -0
- package/src/components/ams-editor/types.ts +40 -0
- package/src/components/tis-editor/TisConfigEditor.tsx +1 -1
- package/src/components/tis-editor/editor/SaveDiffDialog.tsx +4 -3
- package/src/hooks/useAmsSchemaConfig.ts +142 -0
- package/src/hooks/useTisConfig.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"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,
|
|
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,CAyIlD,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,wCAnLF,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAsLvD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as _jsx}from"react/jsx-runtime";import React,{createContext,useCallback,useContext,useEffect,useMemo,useState}from"react";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";const EMPTY_ALERTS={assetCount:0,calibrationOverdue:0,laneUnavailable:0},AmsContext=createContext({schemas:{},schemasLoaded:!1,roles:{},rolesLoaded:!1,alerts:EMPTY_ALERTS,assets:[],refreshAssets:async()=>{},readAsset:async()=>null,listCalibrations:async()=>[],readCalibration:async()=>null,readUsage:async()=>null,selection:{assetType:null,assetId:null},setSelection:()=>{}});export const AmsProvider=({children:e})=>{const{invoke:s,subscribe:t,unsubscribe:a}=useContext(EventEmitterContext),[n,
|
|
1
|
+
import{jsx as _jsx}from"react/jsx-runtime";import React,{createContext,useCallback,useContext,useEffect,useMemo,useState}from"react";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";const EMPTY_ALERTS={assetCount:0,calibrationOverdue:0,laneUnavailable:0},AmsContext=createContext({schemas:{},schemasLoaded:!1,roles:{},rolesLoaded:!1,alerts:EMPTY_ALERTS,assets:[],refreshAssets:async()=>{},readAsset:async()=>null,listCalibrations:async()=>[],readCalibration:async()=>null,readUsage:async()=>null,selection:{assetType:null,assetId:null},setSelection:()=>{}});export const AmsProvider=({children:e})=>{const{invoke:s,subscribe:t,unsubscribe:a}=useContext(EventEmitterContext),[n,c]=useState({}),[r,o]=useState(!1),[l,u]=useState({}),[i,m]=useState(!1),[d,C]=useState(EMPTY_ALERTS),[x,b]=useState([]),[y,_]=useState({assetType:null,assetId:null}),A=useCallback(e=>{_(s=>({...s,...e}))},[]),h=useCallback(async()=>{try{const e=await s("ams.list_schemas",MessageType.Request,{});e?.success&&e.data&&(c(e.data.asset_types??{}),o(!0))}catch(e){}},[s]);useEffect(()=>{let e=!1;return h(),(async()=>{try{const t=await s("ams.list_roles",MessageType.Request,{});if(e)return;t?.success&&t.data?(u(t.data.roles??{}),m(!0)):m(!0)}catch(e){m(!0)}})(),()=>{e=!0}},[]);const p=useCallback(async()=>{try{const e=await s("ams.list_assets",MessageType.Request,{include_retired:!0});e?.success&&b(e.data.assets??[])}catch(e){}},[s]);useEffect(()=>{p()},[p]),useEffect(()=>{const e=[t("ams.asset_changed",()=>{p()}),t("ams.calibration_added",()=>{p()}),t("ams.calibration_updated",()=>{p()}),t("ams.schema_changed",()=>{h()}),t("ams.asset_count",e=>C(s=>({...s,assetCount:Number(e)||0}))),t("ams.alert_calibration_overdue",e=>C(s=>({...s,calibrationOverdue:Number(e)||0}))),t("ams.alert_lane_unavailable",e=>C(s=>({...s,laneUnavailable:Number(e)||0})))];return()=>{e.forEach(a)}},[t,a,p,h]);const E=useCallback(async e=>{try{const t=await s("ams.read_asset",MessageType.Request,{asset_id:e});return t?.success?t.data:null}catch{return null}},[s]),f=useCallback(async e=>{try{const t=await s("ams.list_calibrations",MessageType.Request,{asset_id:e});return t?.success?t.data.cal_ids??[]:[]}catch{return[]}},[s]),S=useCallback(async(e,t)=>{try{const a=await s("ams.read_calibration",MessageType.Request,{asset_id:e,cal_id:t});return a?.success?a.data:null}catch{return null}},[s]),T=useCallback(async e=>{try{const t=await s("ams.read_usage",MessageType.Request,{asset_id:e});return t?.success?t.data:null}catch{return null}},[s]),g=useMemo(()=>({schemas:n,schemasLoaded:r,roles:l,rolesLoaded:i,alerts:d,assets:x,refreshAssets:p,readAsset:E,listCalibrations:f,readCalibration:S,readUsage:T,selection:y,setSelection:A}),[n,r,l,i,d,x,p,E,f,S,T,y,A]);return _jsx(AmsContext.Provider,{value:g,children:e})};export const useAms=()=>useContext(AmsContext);export const useAmsSchemas=()=>useContext(AmsContext).schemas;export const useAmsRoles=()=>useContext(AmsContext).roles;export const useAmsAlerts=()=>useContext(AmsContext).alerts;export const useAmsAssets=()=>useContext(AmsContext).assets;export const useAmsSelection=()=>{const e=useContext(AmsContext);return[e.selection,e.setSelection]};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AssetFieldArray — add/remove/reorder editor for a plain `TestField[]`.
|
|
3
|
+
*
|
|
4
|
+
* A decoupled sibling of the TIS `FieldArrayEditor` (which is bound to a
|
|
5
|
+
* `TestMethod` keyed by `FieldArrayKey`). Asset types carry bare field
|
|
6
|
+
* lists (`fields`, `calibration_fields`, and the per-key lists inside a
|
|
7
|
+
* keyed `sub_locations`), so this operates directly on an array + onChange.
|
|
8
|
+
*
|
|
9
|
+
* Reuses the TIS `TestFieldDialog` for the per-field modal — asset fields
|
|
10
|
+
* have no cross-test aggregate concept, so `isResultsField` is left false.
|
|
11
|
+
*/
|
|
12
|
+
import type { TestField } from '../tis-editor/types';
|
|
13
|
+
export interface AssetFieldArrayProps {
|
|
14
|
+
title: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
fields: TestField[];
|
|
17
|
+
onChange: (next: TestField[]) => void;
|
|
18
|
+
}
|
|
19
|
+
export declare const AssetFieldArray: React.FC<AssetFieldArrayProps>;
|
|
20
|
+
export default AssetFieldArray;
|
|
21
|
+
//# sourceMappingURL=AssetFieldArray.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssetFieldArray.d.ts","sourceRoot":"","sources":["../../../src/components/ams-editor/AssetFieldArray.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAQH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,MAAM,WAAW,oBAAoB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,IAAI,CAAC;CACzC;AAED,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAyE1D,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useState}from"react";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Button}from"primereact/button";import{FormSection}from"../forms/FormSection";import{TestFieldDialog}from"../tis-editor/editor/TestFieldDialog";export const AssetFieldArray=({title:e,description:t,fields:i,onChange:o})=>{const[l,n]=useState(!1),[s,a]=useState(null),r=(e,t)=>{const l=e+t;if(l<0||l>=i.length)return;const n=[...i];[n[e],n[l]]=[n[l],n[e]],o(n)};return _jsxs(_Fragment,{children:[_jsx(FormSection,{title:e,description:t,actions:_jsx(Button,{label:"Add field",icon:"pi pi-plus",size:"small",onClick:()=>{a(null),n(!0)}}),children:_jsxs(DataTable,{value:i,dataKey:"name",emptyMessage:"No fields defined.",children:[_jsx(Column,{field:"name",header:"Name"}),_jsx(Column,{field:"type",header:"Type",style:{width:"6rem"}}),_jsx(Column,{field:"units",header:"Units",style:{width:"6rem"}}),_jsx(Column,{header:"Req",body:e=>e.required?"✓":"",style:{width:"4rem"}}),_jsx(Column,{field:"label",header:"Label"}),_jsx(Column,{header:"",body:(e,t)=>_jsxs("div",{style:{display:"flex",gap:"0.25rem"},children:[_jsx(Button,{icon:"pi pi-arrow-up",className:"p-button-text p-button-sm",disabled:0===t.rowIndex,onClick:()=>r(t.rowIndex,-1),"aria-label":"Move up"}),_jsx(Button,{icon:"pi pi-arrow-down",className:"p-button-text p-button-sm",disabled:t.rowIndex===i.length-1,onClick:()=>r(t.rowIndex,1),"aria-label":"Move down"}),_jsx(Button,{icon:"pi pi-pencil",className:"p-button-text p-button-sm",onClick:()=>{return e=t.rowIndex,a(e),void n(!0);var e},"aria-label":"Edit"}),_jsx(Button,{icon:"pi pi-trash",className:"p-button-text p-button-danger p-button-sm",onClick:()=>(e=>{const t=i[e];t&&window.confirm(`Remove field "${t.name}"?`)&&o(i.filter((t,i)=>i!==e))})(t.rowIndex),"aria-label":"Remove"})]}),style:{width:"9rem"}})]})}),_jsx(TestFieldDialog,{visible:l,initial:null!==s?i[s]??null:null,siblingNames:i.map(e=>e.name),isResultsField:!1,onCancel:()=>n(!1),onSave:e=>{const t=[...i];null===s?t.push(e):t[s]=e,o(t),n(!1)}})]})};export default AssetFieldArray;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
.ams-typeeditor {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
height: 100%;
|
|
5
|
+
min-height: 0;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.ams-typeeditor__header {
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
justify-content: space-between;
|
|
12
|
+
padding: 0.75rem 1rem;
|
|
13
|
+
border-bottom: 1px solid var(--surface-d, #e2e8f0);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.ams-typeeditor__header h2 {
|
|
17
|
+
margin: 0;
|
|
18
|
+
font-size: 1.125rem;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.ams-typeeditor__header-actions {
|
|
22
|
+
display: flex;
|
|
23
|
+
gap: 0.5rem;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.ams-typeeditor__dirty-pill {
|
|
27
|
+
display: inline-block;
|
|
28
|
+
background: #ea580c;
|
|
29
|
+
color: white;
|
|
30
|
+
font-size: 0.7rem;
|
|
31
|
+
font-weight: 600;
|
|
32
|
+
text-transform: uppercase;
|
|
33
|
+
letter-spacing: 0.05em;
|
|
34
|
+
padding: 0.15rem 0.5rem;
|
|
35
|
+
border-radius: 999px;
|
|
36
|
+
margin-left: 0.5rem;
|
|
37
|
+
vertical-align: middle;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.ams-typeeditor__body {
|
|
41
|
+
flex: 1;
|
|
42
|
+
display: flex;
|
|
43
|
+
min-height: 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.ams-typeeditor__sidebar {
|
|
47
|
+
flex: 0 0 320px;
|
|
48
|
+
border-right: 1px solid var(--surface-d, #e2e8f0);
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-direction: column;
|
|
51
|
+
min-height: 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.ams-typeeditor__sidebar-actions {
|
|
55
|
+
display: flex;
|
|
56
|
+
gap: 0.25rem;
|
|
57
|
+
padding: 0.5rem;
|
|
58
|
+
border-bottom: 1px solid var(--surface-d, #e2e8f0);
|
|
59
|
+
flex-wrap: wrap;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.ams-typeeditor__sidebar-actions .p-button {
|
|
63
|
+
flex: 1 1 auto;
|
|
64
|
+
min-width: 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.ams-typeeditor__builtins {
|
|
68
|
+
padding: 0.5rem;
|
|
69
|
+
border-top: 1px solid var(--surface-d, #e2e8f0);
|
|
70
|
+
color: var(--text-color-secondary, #64748b);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.ams-typeeditor__detail {
|
|
74
|
+
flex: 1;
|
|
75
|
+
display: flex;
|
|
76
|
+
flex-direction: column;
|
|
77
|
+
min-height: 0;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.ams-typeeditor__error {
|
|
81
|
+
background: #fef2f2;
|
|
82
|
+
color: #991b1b;
|
|
83
|
+
border-left: 3px solid #dc2626;
|
|
84
|
+
padding: 0.5rem 1rem;
|
|
85
|
+
margin: 0.5rem 1rem;
|
|
86
|
+
font-size: 0.875rem;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.ams-typeeditor__error pre {
|
|
90
|
+
white-space: pre-wrap;
|
|
91
|
+
margin: 0.25rem 0 0 0;
|
|
92
|
+
font-family: ui-monospace, monospace;
|
|
93
|
+
font-size: 0.8rem;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.ams-typeeditor__empty {
|
|
97
|
+
flex: 1;
|
|
98
|
+
display: flex;
|
|
99
|
+
align-items: center;
|
|
100
|
+
justify-content: center;
|
|
101
|
+
color: var(--text-color-secondary, #64748b);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.ams-typeeditor__new-label {
|
|
105
|
+
display: flex;
|
|
106
|
+
flex-direction: column;
|
|
107
|
+
gap: 0.25rem;
|
|
108
|
+
margin-bottom: 0.25rem;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* Portrait HMIs: stack the panes (see TisConfigEditor.css rationale). */
|
|
112
|
+
@media (orientation: portrait) {
|
|
113
|
+
.ams-typeeditor__body {
|
|
114
|
+
flex-direction: column;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.ams-typeeditor__sidebar {
|
|
118
|
+
flex: 0 1 35%;
|
|
119
|
+
border-right: none;
|
|
120
|
+
border-bottom: 1px solid var(--surface-d, #e2e8f0);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AssetTypeEditor — master-detail UI for authoring AMS custom asset TYPES
|
|
3
|
+
* from the operator HMI (the Instron-style "define your own asset type"
|
|
4
|
+
* capability). The instance-level UI (AssetRegistryTable / AssetEditDialog)
|
|
5
|
+
* edits asset records; this edits the type *schemas* those records conform
|
|
6
|
+
* to.
|
|
7
|
+
*
|
|
8
|
+
* Left: custom types (editable). Right: tabbed AssetTypeFormEditor.
|
|
9
|
+
* Action bar: New / Duplicate / Delete / Save / Revert.
|
|
10
|
+
*
|
|
11
|
+
* Staged-then-saved, mirroring the TIS test-method editor: New/Apply/Delete
|
|
12
|
+
* stage into the AMS module's in-memory buffer (dirty); Save persists to the
|
|
13
|
+
* `asset_management.json` sidecar (ams.save_schema_config) and broadcasts
|
|
14
|
+
* ams.schema_changed so open pickers/forms refresh. Built-in types
|
|
15
|
+
* (load_cell/linear_encoder/spring) are code-defined and shown read-only for
|
|
16
|
+
* reference — they can't be edited here.
|
|
17
|
+
*/
|
|
18
|
+
import type { TisIpcInvoker } from '../../hooks/useTisConfig';
|
|
19
|
+
import './AssetTypeEditor.css';
|
|
20
|
+
export interface AssetTypeEditorProps {
|
|
21
|
+
/** Optional invoker override — primarily for the playground / tests. */
|
|
22
|
+
invoker?: TisIpcInvoker;
|
|
23
|
+
}
|
|
24
|
+
export declare const AssetTypeEditor: React.FC<AssetTypeEditorProps>;
|
|
25
|
+
export default AssetTypeEditor;
|
|
26
|
+
//# sourceMappingURL=AssetTypeEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssetTypeEditor.d.ts","sourceRoot":"","sources":["../../../src/components/ams-editor/AssetTypeEditor.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAaH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAI9D,OAAO,uBAAuB,CAAC;AAE/B,MAAM,WAAW,oBAAoB;IACjC,wEAAwE;IACxE,OAAO,CAAC,EAAE,aAAa,CAAC;CAC3B;AAcD,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAgP1D,CAAC;AAEF,eAAe,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useEffect,useMemo,useState}from"react";import{useContext}from"react";import{DataTable}from"primereact/datatable";import{Column}from"primereact/column";import{Button}from"primereact/button";import{InputText}from"primereact/inputtext";import{Dialog}from"primereact/dialog";import{EventEmitterContext}from"../../core/EventEmitterContext";import{MessageType}from"../../hub/CommandMessage";import{useAmsSchemaConfig}from"../../hooks/useAmsSchemaConfig";import{useAmsAssetTypes}from"../../hooks/useAmsAssetTypes";import{AssetTypeFormEditor}from"./AssetTypeFormEditor";import"./AssetTypeEditor.css";const EMPTY_TYPE={label:"",description:"",fields:[],calibration_fields:[]};export const AssetTypeEditor=({invoker:e})=>{const s=useContext(EventEmitterContext),t=useMemo(()=>e??(async(e,t)=>await s.invoke(e,MessageType.Request,t)),[e,s]),i=useAmsSchemaConfig({invoker:t}),a=useAmsAssetTypes({invoker:t}),[n,o]=useState(null),[r,l]=useState(null),[c,d]=useState(!1),[m,p]=useState(!1),[y,u]=useState(""),f=useMemo(()=>Object.keys(i.config?.assetTypes??{}),[i.config]),_=useMemo(()=>a.types.filter(e=>!f.includes(e)),[a.types,f]),g=useMemo(()=>i.config?Object.entries(i.config.assetTypes).map(([e,s])=>({id:e,label:s?.label??""})):[],[i.config]);useEffect(()=>{!n&&i.config&&o(Object.keys(i.config.assetTypes)[0]??null)},[i.config,n]),useEffect(()=>{l(null)},[n]);return _jsxs("div",{className:"ams-typeeditor",children:[_jsxs("header",{className:"ams-typeeditor__header",children:[_jsxs("h2",{children:["Asset Types"," ",i.config?.dirty&&_jsx("span",{className:"ams-typeeditor__dirty-pill",children:"unsaved"})]}),_jsxs("div",{className:"ams-typeeditor__header-actions",children:[_jsx(Button,{label:"Save…",icon:"pi pi-save",disabled:c||!i.config?.dirty,onClick:async()=>{if(window.confirm("Save asset-type changes to disk?")){d(!0);try{await i.save()}catch(e){l(String(e?.message??e))}finally{d(!1)}}}}),_jsx(Button,{label:"Revert",icon:"pi pi-undo",className:"p-button-secondary",disabled:c||!i.config?.dirty,onClick:async()=>{if(window.confirm("Discard all in-progress edits? This cannot be undone.")){d(!0);try{await i.revert()}catch(e){l(String(e?.message??e))}finally{d(!1)}}}})]})]}),i.error&&_jsxs("div",{className:"ams-typeeditor__error",children:[_jsx("strong",{children:"Error:"})," ",_jsx("pre",{children:i.error})]}),_jsxs("div",{className:"ams-typeeditor__body",children:[_jsxs("aside",{className:"ams-typeeditor__sidebar",children:[_jsxs("div",{className:"ams-typeeditor__sidebar-actions",children:[_jsx(Button,{label:"New",icon:"pi pi-plus",disabled:c,onClick:()=>p(!0)}),_jsx(Button,{label:"Duplicate",icon:"pi pi-clone",className:"p-button-secondary",disabled:c||!n,onClick:async()=>{if(!n||!i.config)return;const e=i.config.assetTypes[n];if(!e)return;let s=`${n}_copy`,t=2;for(;i.config.assetTypes[s];)s=`${n}_copy_${t++}`;d(!0);try{await i.putType(s,JSON.parse(JSON.stringify(e))),o(s)}catch(e){l(String(e?.message??e))}finally{d(!1)}}}),_jsx(Button,{label:"Delete",icon:"pi pi-trash",className:"p-button-danger",disabled:c||!n,onClick:async()=>{if(n&&window.confirm(`Remove type "${n}"? This is staged — Save persists it.`)){d(!0);try{await i.removeType(n),o(null)}catch(e){l(String(e?.message??e))}finally{d(!1)}}}})]}),_jsxs(DataTable,{value:g,selection:g.find(e=>e.id===n)??null,onSelectionChange:e=>o(e.value?.id??null),selectionMode:"single",dataKey:"id",scrollable:!0,scrollHeight:"flex",emptyMessage:i.loading?"Loading…":"No custom asset types defined.",children:[_jsx(Column,{field:"id",header:"Type ID"}),_jsx(Column,{field:"label",header:"Label"})]}),_.length>0&&_jsx("div",{className:"ams-typeeditor__builtins",children:_jsxs("small",{children:["Built-in (read-only): ",_.join(", ")]})})]}),_jsx("section",{className:"ams-typeeditor__detail",children:n&&i.config?.assetTypes[n]?_jsxs(_Fragment,{children:[r&&_jsx("div",{className:"ams-typeeditor__error",children:_jsx("pre",{children:r})}),_jsx(AssetTypeFormEditor,{typeId:n,def:i.config.assetTypes[n],onApply:async e=>{if(n){d(!0);try{await i.putType(n,e),l(null)}catch(e){l(String(e?.message??e))}finally{d(!1)}}},busy:c,builtinTypes:_})]}):_jsx("div",{className:"ams-typeeditor__empty",children:"Select a custom asset type on the left, or create a new one."})})]}),_jsxs(Dialog,{header:"New Asset Type",visible:m,onHide:()=>p(!1),style:{width:"24rem"},children:[_jsxs("label",{className:"ams-typeeditor__new-label",children:["Type ID",_jsx(InputText,{value:y,onChange:e=>u(e.target.value),placeholder:"e.g. triaxial_transducer",autoFocus:!0})]}),_jsx("small",{children:"Canonical key — appears in wire payloads, on-disk paths, and generated code."}),_jsxs("div",{style:{display:"flex",gap:"0.5rem",justifyContent:"flex-end",marginTop:"1rem"},children:[_jsx(Button,{label:"Cancel",className:"p-button-text",onClick:()=>p(!1)}),_jsx(Button,{label:"Create",disabled:!y.trim()||c,onClick:async()=>{const e=y.trim();if(e)if(i.config?.assetTypes[e])l(`A custom type named "${e}" already exists.`);else if(!_.includes(e)||window.confirm(`"${e}" is a built-in type. Creating a custom type with this id overrides/extends the built-in. Continue?`)){d(!0);try{await i.putType(e,EMPTY_TYPE),o(e),p(!1),u("")}catch(e){l(String(e?.message??e))}finally{d(!1)}}}})]})]})]})};export default AssetTypeEditor;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AssetTypeFormEditor — tabbed shell for a single custom asset type,
|
|
3
|
+
* mirroring the TIS MethodFormEditor. Tabs: Identity, Fields, Calibration
|
|
4
|
+
* Fields, Sub-locations, and an always-available JSON escape hatch.
|
|
5
|
+
*
|
|
6
|
+
* Owns the local working copy; hands the changed copy back via onApply —
|
|
7
|
+
* the parent decides when to call ams.put_asset_type.
|
|
8
|
+
*/
|
|
9
|
+
import type { AssetTypeDef } from './types';
|
|
10
|
+
export interface AssetTypeFormEditorProps {
|
|
11
|
+
typeId: string;
|
|
12
|
+
def: AssetTypeDef;
|
|
13
|
+
/** Called on Apply with the working copy — parent forwards to put_asset_type. */
|
|
14
|
+
onApply: (next: AssetTypeDef) => Promise<void> | void;
|
|
15
|
+
/** Built-in type names, for the `extends` dropdown. */
|
|
16
|
+
builtinTypes?: string[];
|
|
17
|
+
busy?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare const AssetTypeFormEditor: React.FC<AssetTypeFormEditorProps>;
|
|
20
|
+
export default AssetTypeFormEditor;
|
|
21
|
+
//# sourceMappingURL=AssetTypeFormEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AssetTypeFormEditor.d.ts","sourceRoot":"","sources":["../../../src/components/ams-editor/AssetTypeFormEditor.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAaH,OAAO,KAAK,EAAE,YAAY,EAAa,MAAM,SAAS,CAAC;AAEvD,MAAM,WAAW,wBAAwB;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,YAAY,CAAC;IAClB,iFAAiF;IACjF,OAAO,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACtD,uDAAuD;IACvD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,IAAI,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,mBAAmB,EAAE,KAAK,CAAC,EAAE,CAAC,wBAAwB,CA+IlE,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as _jsx,jsxs as _jsxs}from"react/jsx-runtime";import{useEffect,useMemo,useState}from"react";import{TabView,TabPanel}from"primereact/tabview";import{Button}from"primereact/button";import{InputText}from"primereact/inputtext";import{InputTextarea}from"primereact/inputtextarea";import{Dropdown}from"primereact/dropdown";import{CodeEditor}from"../CodeEditor";import{FormSection}from"../forms/FormSection";import{FormRow}from"../forms/FormRow";import{AssetFieldArray}from"./AssetFieldArray";import{SubLocationsEditor}from"./SubLocationsEditor";export const AssetTypeFormEditor=({typeId:e,def:t,onApply:i,builtinTypes:r=[],busy:s})=>{const[a,o]=useState(t),[n,l]=useState(""),[d,c]=useState(null),[p,m]=useState(0);useEffect(()=>{o(t),l(JSON.stringify(t,null,2)),c(null)},[t,e]);const u=useMemo(()=>{try{return JSON.stringify(a)!==JSON.stringify(t)}catch{return!0}},[a,t]),h=e=>{o(e),l(JSON.stringify(e,null,2))},f=[{label:"— none —",value:""},...r.map(e=>({label:`builtin: ${e}`,value:e}))];return _jsxs("div",{style:{display:"flex",flexDirection:"column",height:"100%",minHeight:0},children:[_jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center",padding:"0.5rem 1rem",borderBottom:"1px solid var(--surface-d, #e2e8f0)",background:"var(--surface-b, #f8fafc)"},children:[_jsxs("strong",{children:[e,u&&_jsx("span",{style:{marginLeft:"0.5rem",color:"#ea580c"},children:"•"})]}),_jsx(Button,{label:"Apply",icon:"pi pi-check",disabled:s||!u||!!d,onClick:async()=>{d||await i(a)}})]}),_jsx("div",{style:{flex:1,minHeight:0,overflow:"auto"},children:_jsxs(TabView,{activeIndex:p,onTabChange:e=>m(e.index),children:[_jsx(TabPanel,{header:"Identity",children:_jsxs(FormSection,{title:"Identity",description:"How this type appears in the Add-Asset picker and how its asset IDs are prefixed.",children:[_jsx(FormRow,{label:"Label",hint:"Pretty name in the asset picker. Falls back to the type id.",children:_jsx(InputText,{value:a.label??"",onChange:e=>h({...a,label:e.target.value||void 0})})}),_jsx(FormRow,{label:"Description",children:_jsx(InputTextarea,{rows:2,value:a.description??"",onChange:e=>h({...a,description:e.target.value||void 0})})}),_jsx(FormRow,{label:"ID prefix",hint:"Asset IDs are minted as <prefix>001, 002, … Defaults to A- when blank.",children:_jsx(InputText,{value:a.id_prefix??"",placeholder:"e.g. TSDR-",onChange:e=>h({...a,id_prefix:e.target.value||void 0})})}),_jsx(FormRow,{label:"Extends",hint:"Optionally build on a built-in type's schema (merges its fields under yours).",children:_jsx(Dropdown,{value:a.extends??"",options:f,onChange:e=>h({...a,extends:e.value||void 0}),editable:!0})})]})}),_jsx(TabPanel,{header:"Fields",children:_jsx(AssetFieldArray,{title:"Nameplate Fields",description:"Per-asset fields captured when the asset is registered (e.g. capacity, material).",fields:a.fields??[],onChange:e=>h({...a,fields:e})})}),_jsx(TabPanel,{header:"Calibration Fields",children:_jsx(AssetFieldArray,{title:"Calibration Fields",description:"The calibration-record schema (e.g. scale, offset). Leave empty for types that don't calibrate.",fields:a.calibration_fields??[],onChange:e=>h({...a,calibration_fields:e})})}),_jsx(TabPanel,{header:"Sub-locations",children:_jsx(SubLocationsEditor,{def:a,onChange:h})}),_jsxs(TabPanel,{header:"JSON",children:[d&&_jsx("div",{style:{color:"#dc2626",marginBottom:"0.5rem",fontFamily:"monospace"},children:d}),_jsx("div",{style:{height:"60vh"},children:_jsx(CodeEditor,{code:n,language:"json",theme:"vs-dark",readOnly:!1,cursorStyle:"line",onCodeChanged:e=>{const t=e??"";l(t);try{o(JSON.parse(t)),c(null)}catch(e){c(String(e?.message??e))}}})})]})]})})]})};export default AssetTypeFormEditor;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SubLocationsEditor — edits the keyed-fields (multi-axis) `sub_locations`
|
|
3
|
+
* shape of an asset type: a fixed set of keys (e.g. x/y/z axes) plus
|
|
4
|
+
* per-key nameplate and calibration fields.
|
|
5
|
+
*
|
|
6
|
+
* The positional (surface-lanes) shape — `count` + `per_location_state` —
|
|
7
|
+
* is not editable here; it's left to the form's JSON tab. Assets without
|
|
8
|
+
* any sub_locations can add a keyed schema with one click.
|
|
9
|
+
*/
|
|
10
|
+
import { type AssetTypeDef } from './types';
|
|
11
|
+
export interface SubLocationsEditorProps {
|
|
12
|
+
def: AssetTypeDef;
|
|
13
|
+
onChange: (next: AssetTypeDef) => void;
|
|
14
|
+
}
|
|
15
|
+
export declare const SubLocationsEditor: React.FC<SubLocationsEditorProps>;
|
|
16
|
+
export default SubLocationsEditor;
|
|
17
|
+
//# sourceMappingURL=SubLocationsEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SubLocationsEditor.d.ts","sourceRoot":"","sources":["../../../src/components/ams-editor/SubLocationsEditor.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,OAAO,EAAuB,KAAK,YAAY,EAA0C,MAAM,SAAS,CAAC;AAEzG,MAAM,WAAW,uBAAuB;IACpC,GAAG,EAAE,YAAY,CAAC;IAClB,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;CAC1C;AAID,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CA4HhE,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{Button}from"primereact/button";import{InputText}from"primereact/inputtext";import{FormSection}from"../forms/FormSection";import{FormRow}from"../forms/FormRow";import{AssetFieldArray}from"./AssetFieldArray";import{isKeyedSubLocations}from"./types";const EMPTY_KEYED={keys:[],fields:[],calibration_fields:[]};export const SubLocationsEditor=({def:e,onChange:t})=>{const s=e.sub_locations;if(null==s)return _jsx(FormSection,{title:"Sub-locations",description:"Optional. For multi-axis assets (e.g. a triaxial transducer), declare a fixed set of keys with per-key fields and calibration.",children:_jsx(Button,{label:"Add multi-axis sub-locations",icon:"pi pi-plus",size:"small",onClick:()=>t({...e,sub_locations:{...EMPTY_KEYED}})})});if(!isKeyedSubLocations(s))return _jsx(FormSection,{title:"Sub-locations",children:_jsxs("p",{style:{color:"var(--text-color-secondary, #64748b)"},children:["This type uses the positional (surface-lanes) sub-locations shape, which isn't editable in the form. Use the ",_jsx("strong",{children:"JSON"})," tab to edit it."]})});const o=s,i=s=>t({...e,sub_locations:s}),a=(e,t)=>{const s=e+t;if(s<0||s>=o.keys.length)return;const a=[...o.keys];[a[e],a[s]]=[a[s],a[e]],i({...o,keys:a})};return _jsxs(_Fragment,{children:[_jsxs(FormSection,{title:"Sub-locations (multi-axis)",description:"A fixed, ordered set of keys. Each asset carries per-key values; calibration records carry a per-key sub-object.",actions:_jsx(Button,{label:"Remove sub-locations",icon:"pi pi-trash",className:"p-button-text p-button-danger",size:"small",onClick:()=>{if(window.confirm("Remove the sub-locations schema from this type?")){const s={...e};delete s.sub_locations,t(s)}}}),children:[_jsx(FormRow,{label:"Group label",hint:"Pretty label for the set (e.g. Axes).",children:_jsx(InputText,{value:o.label??"",onChange:e=>i({...o,label:e.target.value||void 0})})}),_jsx(FormRow,{label:"Key label",hint:"Pretty label for one key (e.g. Axis).",children:_jsx(InputText,{value:o.key_label??"",onChange:e=>i({...o,key_label:e.target.value||void 0})})}),_jsx(FormRow,{label:"Keys",hint:"Ordered. Each becomes a per-key column on assets & calibrations.",children:_jsxs("div",{style:{display:"flex",flexDirection:"column",gap:"0.35rem"},children:[o.keys.map((e,t)=>_jsxs("div",{style:{display:"flex",gap:"0.25rem",alignItems:"center"},children:[_jsx(InputText,{value:e,placeholder:"e.g. x",onChange:e=>((e,t)=>{const s=[...o.keys];s[e]=t,i({...o,keys:s})})(t,e.target.value),style:{flex:1}}),_jsx(Button,{icon:"pi pi-arrow-up",className:"p-button-text p-button-sm",disabled:0===t,onClick:()=>a(t,-1),"aria-label":"Move up"}),_jsx(Button,{icon:"pi pi-arrow-down",className:"p-button-text p-button-sm",disabled:t===o.keys.length-1,onClick:()=>a(t,1),"aria-label":"Move down"}),_jsx(Button,{icon:"pi pi-trash",className:"p-button-text p-button-danger p-button-sm",onClick:()=>{return e=t,i({...o,keys:o.keys.filter((t,s)=>s!==e)});var e},"aria-label":"Remove key"})]},t)),_jsx("div",{children:_jsx(Button,{label:"Add key",icon:"pi pi-plus",size:"small",className:"p-button-secondary",onClick:()=>i({...o,keys:[...o.keys,""]})})})]})})]}),_jsx(AssetFieldArray,{title:"Per-key fields",description:"Nameplate fields recorded once per key on each asset.",fields:o.fields??[],onChange:e=>i({...o,fields:e})}),_jsx(AssetFieldArray,{title:"Per-key calibration fields",description:"Calibration values captured per key. When present, each calibration record must carry a per-key sub-object.",fields:o.calibration_fields??[],onChange:e=>i({...o,calibration_fields:e})})]})};export default SubLocationsEditor;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { AssetTypeEditor } from './AssetTypeEditor';
|
|
2
|
+
export type { AssetTypeEditorProps } from './AssetTypeEditor';
|
|
3
|
+
export { AssetTypeFormEditor } from './AssetTypeFormEditor';
|
|
4
|
+
export { AssetFieldArray } from './AssetFieldArray';
|
|
5
|
+
export { SubLocationsEditor } from './SubLocationsEditor';
|
|
6
|
+
export type { AssetTypeDef, KeyedSubLocations, TestField } from './types';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/ams-editor/index.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,YAAY,EAAE,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{AssetTypeEditor}from"./AssetTypeEditor";export{AssetTypeFormEditor}from"./AssetTypeFormEditor";export{AssetFieldArray}from"./AssetFieldArray";export{SubLocationsEditor}from"./SubLocationsEditor";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side mirror of the autocore-ams `AssetTypeConfig` (see
|
|
3
|
+
* autocore-ams/src/config.rs). Kept loose so unknown server-side
|
|
4
|
+
* extensions don't trip the editor at parse time. Reuses the shared
|
|
5
|
+
* `TestField` shape from the TIS editor — the server reuses the same
|
|
6
|
+
* Rust `TestField` for both test methods and asset/calibration fields.
|
|
7
|
+
*/
|
|
8
|
+
import type { TestField } from '../tis-editor/types';
|
|
9
|
+
export type { TestField };
|
|
10
|
+
/** One custom asset type. `sub_locations` is deliberately untyped: two
|
|
11
|
+
* shapes exist (keyed-fields multi-axis, discriminated by a `keys` array;
|
|
12
|
+
* and positional surface-lanes). The form edits the keyed shape; the
|
|
13
|
+
* positional shape is left to the JSON tab. */
|
|
14
|
+
export interface AssetTypeDef {
|
|
15
|
+
extends?: string;
|
|
16
|
+
id_prefix?: string;
|
|
17
|
+
label?: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
fields?: TestField[];
|
|
20
|
+
calibration_fields?: TestField[];
|
|
21
|
+
sub_locations?: unknown;
|
|
22
|
+
}
|
|
23
|
+
/** The keyed-fields (multi-axis) `sub_locations` shape. */
|
|
24
|
+
export interface KeyedSubLocations {
|
|
25
|
+
label?: string;
|
|
26
|
+
key_label?: string;
|
|
27
|
+
keys: string[];
|
|
28
|
+
fields?: TestField[];
|
|
29
|
+
calibration_fields?: TestField[];
|
|
30
|
+
}
|
|
31
|
+
/** True when a `sub_locations` value is the keyed-fields (multi-axis)
|
|
32
|
+
* shape the form can edit — discriminated by a `keys` array. */
|
|
33
|
+
export declare function isKeyedSubLocations(v: unknown): v is KeyedSubLocations;
|
|
34
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/ams-editor/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,YAAY,EAAE,SAAS,EAAE,CAAC;AAE1B;;;gDAGgD;AAChD,MAAM,WAAW,YAAY;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;IACrB,kBAAkB,CAAC,EAAE,SAAS,EAAE,CAAC;IACjC,aAAa,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,2DAA2D;AAC3D,MAAM,WAAW,iBAAiB;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;IACrB,kBAAkB,CAAC,EAAE,SAAS,EAAE,CAAC;CACpC;AAED;iEACiE;AACjE,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,iBAAiB,CAEtE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function isKeyedSubLocations(e){return!!e&&"object"==typeof e&&Array.isArray(e.keys)}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Action bar: New / Duplicate / Delete / Apply / Save / Revert.
|
|
8
8
|
*
|
|
9
9
|
* "Apply" pushes the local Monaco buffer to the server-side stage
|
|
10
|
-
* (`tis.put_method`). "Save" persists the entire stage to
|
|
10
|
+
* (`tis.put_method`). "Save" persists the entire stage to test_methods.json
|
|
11
11
|
* (`tis.save_config`). "Revert" drops the stage (`tis.discard_config_changes`).
|
|
12
12
|
*
|
|
13
13
|
* Save is disabled (with a tooltip) when active tests are open against
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SaveDiffDialog.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/SaveDiffDialog.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACtC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAQD,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,
|
|
1
|
+
{"version":3,"file":"SaveDiffDialog.d.ts","sourceRoot":"","sources":["../../../../src/components/tis-editor/editor/SaveDiffDialog.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,WAAW,mBAAmB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACnC,OAAO,EAAE,aAAa,CAAC;IACvB,SAAS,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACtC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAQD,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAiGxD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useEffect,useState}from"react";import{Dialog}from"primereact/dialog";import{Button}from"primereact/button";export const SaveDiffDialog=({visible:e,staged:i,invoker:
|
|
1
|
+
import{jsx as _jsx,jsxs as _jsxs,Fragment as _Fragment}from"react/jsx-runtime";import{useEffect,useState}from"react";import{Dialog}from"primereact/dialog";import{Button}from"primereact/button";export const SaveDiffDialog=({visible:e,staged:i,invoker:t,onConfirm:s,onCancel:r})=>{const[d,o]=useState(null),[n,l]=useState(null),[a,c]=useState(!1);useEffect(()=>{if(!e)return;let s=!1;return(async()=>{l(null);try{const e=await t("tis.list_schemas",{});if(s)return;if(!e.success)return void l(e.error_message??"tis.list_schemas failed");const r=e.data?.test_methods??{};o(buildDiff(r,i))}catch(e){l(String(e?.message??e))}})(),()=>{s=!0}},[e,i,t]);return _jsxs(Dialog,{header:"Save Test Methods to Disk",visible:e,onHide:r,style:{width:"60rem",maxWidth:"90vw"},children:[n&&_jsx("div",{style:{color:"#dc2626",marginBottom:"0.5rem"},children:n}),!d&&!n&&_jsx("small",{children:"Computing diff…"}),d&&_jsxs("div",{children:[_jsxs("p",{children:["About to save the test methods to"," ",_jsx("code",{children:"test_methods.json"}),". A backup of the previous file will be written to"," ",_jsx("code",{children:"test_methods.json.bak"}),"."]}),0===d.added.length&&0===d.removed.length&&0===d.modified.length&&_jsx("p",{children:_jsx("em",{children:"No changes detected."})}),d.added.length>0&&_jsxs("p",{children:[_jsx("strong",{children:"Added:"})," ",d.added.join(", ")]}),d.removed.length>0&&_jsxs("p",{children:[_jsx("strong",{children:"Removed:"})," ",d.removed.join(", ")]}),d.modified.length>0&&_jsxs(_Fragment,{children:[_jsxs("p",{children:[_jsx("strong",{children:"Modified:"})," ",d.modified.map(e=>e.id).join(", ")]}),_jsxs("details",{children:[_jsx("summary",{children:"Show before / after"}),d.modified.map(e=>_jsxs("div",{style:{marginTop:"0.75rem"},children:[_jsx("strong",{children:e.id}),_jsxs("div",{style:{display:"grid",gridTemplateColumns:"1fr 1fr",gap:"0.5rem",marginTop:"0.25rem"},children:[_jsx("pre",{style:preStyle("before"),children:JSON.stringify(e.before,null,2)}),_jsx("pre",{style:preStyle("after"),children:JSON.stringify(e.after,null,2)})]})]},e.id))]})]})]}),_jsxs("div",{style:{display:"flex",justifyContent:"flex-end",gap:"0.5rem",marginTop:"1rem"},children:[_jsx(Button,{label:"Cancel",className:"p-button-text",onClick:r,disabled:a}),_jsx(Button,{label:"Save",icon:"pi pi-save",disabled:a||!d,onClick:async()=>{c(!0);try{await s()}finally{c(!1)}}})]})]})};function preStyle(e){return{background:"before"===e?"#fef2f2":"#f0fdf4",color:"#0f172a",padding:"0.5rem",fontSize:"0.75rem",margin:0,maxHeight:"20rem",overflow:"auto",border:"1px solid "+("before"===e?"#fecaca":"#bbf7d0"),borderRadius:4}}function buildDiff(e,i){const t={added:[],removed:[],modified:[]},s=new Set([...Object.keys(e),...Object.keys(i)]);for(const r of s){const s=e[r],d=i[r];void 0===s&&void 0!==d?t.added.push(r):void 0!==s&&void 0===d?t.removed.push(r):void 0!==s&&void 0!==d&&JSON.stringify(s)!==JSON.stringify(d)&&t.modified.push({id:r,before:s,after:d})}return t}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useAmsSchemaConfig — load and edit the AMS custom `asset_types` catalog.
|
|
3
|
+
*
|
|
4
|
+
* Thin wrapper around the `ams.show_schema_config` / `ams.put_asset_type` /
|
|
5
|
+
* `ams.remove_asset_type` / `ams.save_schema_config` /
|
|
6
|
+
* `ams.discard_schema_changes` IPC surface. Mirrors the server's
|
|
7
|
+
* staged-then-saved model (and the TIS `useTisConfig` hook exactly): edits
|
|
8
|
+
* land in the AMS module's in-memory buffer (dirty = true) until `save()`
|
|
9
|
+
* persists them to the `asset_management.json` sidecar next to project.json.
|
|
10
|
+
*
|
|
11
|
+
* Only the project's CUSTOM asset types are editable here. The three
|
|
12
|
+
* built-in types (load_cell/linear_encoder/spring) are code-defined and
|
|
13
|
+
* surfaced read-only via `ams.list_schemas` (see `useAmsAssetTypes`).
|
|
14
|
+
*
|
|
15
|
+
* Pass an `invoker` override to mock the IPC layer (playground / tests).
|
|
16
|
+
* When omitted, the hook reads `EventEmitterContext`.
|
|
17
|
+
*/
|
|
18
|
+
import type { TisIpcInvoker } from './useTisConfig';
|
|
19
|
+
import type { AssetTypeDef } from '../components/ams-editor/types';
|
|
20
|
+
export interface AmsSchemaConfig {
|
|
21
|
+
assetTypes: Record<string, AssetTypeDef>;
|
|
22
|
+
dirty: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface UseAmsSchemaConfigResult {
|
|
25
|
+
config: AmsSchemaConfig | null;
|
|
26
|
+
loading: boolean;
|
|
27
|
+
/** Last server-reported error from a mutation. Cleared on next success. */
|
|
28
|
+
error: string | null;
|
|
29
|
+
refresh: () => Promise<void>;
|
|
30
|
+
putType: (typeId: string, def: AssetTypeDef) => Promise<void>;
|
|
31
|
+
removeType: (typeId: string) => Promise<void>;
|
|
32
|
+
save: () => Promise<void>;
|
|
33
|
+
revert: () => Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
export interface UseAmsSchemaConfigOptions {
|
|
36
|
+
invoker?: TisIpcInvoker;
|
|
37
|
+
}
|
|
38
|
+
export declare function useAmsSchemaConfig(options?: UseAmsSchemaConfigOptions): UseAmsSchemaConfigResult;
|
|
39
|
+
//# sourceMappingURL=useAmsSchemaConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAmsSchemaConfig.d.ts","sourceRoot":"","sources":["../../src/hooks/useAmsSchemaConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,MAAM,WAAW,eAAe;IAC5B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACzC,KAAK,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACrC,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,yBAAyB;IACtC,OAAO,CAAC,EAAE,aAAa,CAAC;CAC3B;AAED,wBAAgB,kBAAkB,CAC9B,OAAO,CAAC,EAAE,yBAAyB,GACpC,wBAAwB,CA8F1B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{useCallback,useContext,useEffect,useRef,useState}from"react";import{EventEmitterContext}from"../core/EventEmitterContext";import{MessageType}from"../hub/CommandMessage";export function useAmsSchemaConfig(e){const s=useContext(EventEmitterContext),t=e?.invoker??(async(e,t)=>await s.invoke(e,MessageType.Request,t)),a=useRef(t);a.current=t;const[r,n]=useState(null),[c,o]=useState(!0),[i,u]=useState(null),l=useCallback(async()=>{o(!0);try{const e=await a.current("ams.show_schema_config",{});if(!e.success)return u(e.error_message??"ams.show_schema_config failed"),void n(null);const s=e.data??{};n({assetTypes:s.asset_types??{},dirty:!!s.dirty}),u(null)}catch(e){u(String(e?.message??e)),n(null)}finally{o(!1)}},[]);useEffect(()=>{l()},[l]);const m=useCallback(async(e,s)=>{const t=await a.current("ams.put_asset_type",{type_id:e,definition:s});if(!t.success){const e=t.error_message??"ams.put_asset_type failed";throw u(e),new Error(e)}u(null),await l()},[l]),f=useCallback(async e=>{const s=await a.current("ams.remove_asset_type",{type_id:e});if(!s.success){const e=s.error_message??"ams.remove_asset_type failed";throw u(e),new Error(e)}u(null),await l()},[l]),_=useCallback(async()=>{const e=await a.current("ams.save_schema_config",{});if(!e.success){const s=e.error_message??"ams.save_schema_config failed";throw u(s),new Error(s)}u(null),await l()},[l]),y=useCallback(async()=>{const e=await a.current("ams.discard_schema_changes",{});if(!e.success){const s=e.error_message??"ams.discard_schema_changes failed";throw u(s),new Error(s)}u(null),await l()},[l]);return{config:r,loading:c,error:i,refresh:l,putType:m,removeType:f,save:_,revert:y}}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* `tis.remove_method` / `tis.save_config` / `tis.discard_config_changes`
|
|
6
6
|
* IPC surface. Mirrors the server's staged-then-saved model: edits land in
|
|
7
7
|
* the server's in-memory copy (dirty = true) until `save()` persists them
|
|
8
|
-
* to project.json.
|
|
8
|
+
* to the `test_methods.json` sidecar next to project.json.
|
|
9
9
|
*
|
|
10
10
|
* Today the server hosts exactly one project (the one configured at
|
|
11
11
|
* startup), so `projectId` is informational; the hook still threads it
|
package/package.json
CHANGED
|
@@ -153,24 +153,28 @@ export const AmsProvider: React.FC<AmsProviderProps> = ({ children }) => {
|
|
|
153
153
|
}, []);
|
|
154
154
|
|
|
155
155
|
// -----------------------------------------------------------------
|
|
156
|
+
// Re-fetch the schema catalog. Called once on mount and again whenever
|
|
157
|
+
// the asset-TYPE editor broadcasts `ams.schema_changed` after a save, so
|
|
158
|
+
// open pickers/forms pick up new or edited types without a reload.
|
|
159
|
+
const loadSchemas = useCallback(async () => {
|
|
160
|
+
try {
|
|
161
|
+
const resp: any = await invoke('ams.list_schemas' as any, MessageType.Request, {} as any);
|
|
162
|
+
if (resp?.success && resp.data) {
|
|
163
|
+
setSchemas((resp.data.asset_types ?? {}) as AmsSchemaRegistry);
|
|
164
|
+
setSchemasLoaded(true);
|
|
165
|
+
} else {
|
|
166
|
+
console.warn('[AmsProvider] ams.list_schemas failed:', resp?.error_message);
|
|
167
|
+
}
|
|
168
|
+
} catch (e) {
|
|
169
|
+
console.error('[AmsProvider] ams.list_schemas threw:', e);
|
|
170
|
+
}
|
|
171
|
+
}, [invoke]);
|
|
172
|
+
|
|
156
173
|
// Schema + role load — once on mount.
|
|
157
174
|
// -----------------------------------------------------------------
|
|
158
175
|
useEffect(() => {
|
|
159
176
|
let cancelled = false;
|
|
160
|
-
|
|
161
|
-
try {
|
|
162
|
-
const resp: any = await invoke('ams.list_schemas' as any, MessageType.Request, {} as any);
|
|
163
|
-
if (cancelled) return;
|
|
164
|
-
if (resp?.success && resp.data) {
|
|
165
|
-
setSchemas((resp.data.asset_types ?? {}) as AmsSchemaRegistry);
|
|
166
|
-
setSchemasLoaded(true);
|
|
167
|
-
} else {
|
|
168
|
-
console.warn('[AmsProvider] ams.list_schemas failed:', resp?.error_message);
|
|
169
|
-
}
|
|
170
|
-
} catch (e) {
|
|
171
|
-
console.error('[AmsProvider] ams.list_schemas threw:', e);
|
|
172
|
-
}
|
|
173
|
-
})();
|
|
177
|
+
void loadSchemas();
|
|
174
178
|
(async () => {
|
|
175
179
|
try {
|
|
176
180
|
const resp: any = await invoke('ams.list_roles' as any, MessageType.Request, {} as any);
|
|
@@ -214,12 +218,13 @@ export const AmsProvider: React.FC<AmsProviderProps> = ({ children }) => {
|
|
|
214
218
|
subscribe('ams.asset_changed', () => { refreshAssets(); }),
|
|
215
219
|
subscribe('ams.calibration_added', () => { refreshAssets(); }),
|
|
216
220
|
subscribe('ams.calibration_updated', () => { refreshAssets(); }),
|
|
221
|
+
subscribe('ams.schema_changed', () => { loadSchemas(); }),
|
|
217
222
|
subscribe('ams.asset_count', (v: any) => setAlerts(a => ({ ...a, assetCount: Number(v) || 0 }))),
|
|
218
223
|
subscribe('ams.alert_calibration_overdue', (v: any) => setAlerts(a => ({ ...a, calibrationOverdue: Number(v) || 0 }))),
|
|
219
224
|
subscribe('ams.alert_lane_unavailable', (v: any) => setAlerts(a => ({ ...a, laneUnavailable: Number(v) || 0 }))),
|
|
220
225
|
];
|
|
221
226
|
return () => { subs.forEach(unsubscribe); };
|
|
222
|
-
}, [subscribe, unsubscribe, refreshAssets]);
|
|
227
|
+
}, [subscribe, unsubscribe, refreshAssets, loadSchemas]);
|
|
223
228
|
|
|
224
229
|
// -----------------------------------------------------------------
|
|
225
230
|
// RPC helpers
|