@adcops/autocore-react 3.5.3 → 3.5.7

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.
Files changed (37) hide show
  1. package/dist/components/ams/AmsProvider.d.ts +14 -0
  2. package/dist/components/ams/AmsProvider.d.ts.map +1 -1
  3. package/dist/components/ams/AmsProvider.js +1 -1
  4. package/dist/components/ams/AssetDetailView.d.ts.map +1 -1
  5. package/dist/components/ams/AssetDetailView.js +1 -1
  6. package/dist/components/ams/AssetNameplateGrid.d.ts +13 -0
  7. package/dist/components/ams/AssetNameplateGrid.d.ts.map +1 -0
  8. package/dist/components/ams/AssetNameplateGrid.js +1 -0
  9. package/dist/components/ams/AssetRegistryTable.d.ts.map +1 -1
  10. package/dist/components/ams/AssetRegistryTable.js +1 -1
  11. package/dist/components/ams/InstalledAssetPicker.d.ts +35 -0
  12. package/dist/components/ams/InstalledAssetPicker.d.ts.map +1 -0
  13. package/dist/components/ams/InstalledAssetPicker.js +1 -0
  14. package/dist/components/ams/index.d.ts +5 -0
  15. package/dist/components/ams/index.d.ts.map +1 -1
  16. package/dist/components/ams/index.js +1 -1
  17. package/dist/components/ams/useInstalledAsset.d.ts +26 -0
  18. package/dist/components/ams/useInstalledAsset.d.ts.map +1 -0
  19. package/dist/components/ams/useInstalledAsset.js +1 -0
  20. package/dist/components/tis/TestFieldRow.d.ts +19 -0
  21. package/dist/components/tis/TestFieldRow.d.ts.map +1 -1
  22. package/dist/components/tis/TestFieldRow.js +1 -1
  23. package/dist/components/tis-editor/editor/TestFieldDialog.d.ts.map +1 -1
  24. package/dist/components/tis-editor/editor/TestFieldDialog.js +1 -1
  25. package/dist/components/tis-editor/types.d.ts +11 -2
  26. package/dist/components/tis-editor/types.d.ts.map +1 -1
  27. package/package.json +1 -1
  28. package/src/components/ams/AmsProvider.tsx +36 -1
  29. package/src/components/ams/AssetDetailView.tsx +2 -20
  30. package/src/components/ams/AssetNameplateGrid.tsx +62 -0
  31. package/src/components/ams/AssetRegistryTable.tsx +40 -9
  32. package/src/components/ams/InstalledAssetPicker.tsx +209 -0
  33. package/src/components/ams/index.ts +7 -0
  34. package/src/components/ams/useInstalledAsset.ts +108 -0
  35. package/src/components/tis/TestFieldRow.tsx +31 -0
  36. package/src/components/tis-editor/editor/TestFieldDialog.tsx +59 -20
  37. package/src/components/tis-editor/types.ts +11 -2
@@ -1,4 +1,4 @@
1
- import { useEffect, useState } from 'react';
1
+ import { useContext, useEffect, useMemo, useState } from 'react';
2
2
  import { Dialog } from 'primereact/dialog';
3
3
  import { Button } from 'primereact/button';
4
4
  import { InputText } from 'primereact/inputtext';
@@ -8,6 +8,11 @@ import { Checkbox } from 'primereact/checkbox';
8
8
  import { InputNumber } from 'primereact/inputnumber';
9
9
  import { FormRow } from '../../forms/FormRow';
10
10
  import type { TestField, AggFn, AggFrom, AggScope, AggregateSpec } from '../types';
11
+ // One home for bound parsing/formatting — shared with the operator-facing
12
+ // form so the editor and the runtime can never disagree on what a bound is.
13
+ import { boundText, parseBound, boundInvalid } from '../../tis/TestFieldRow';
14
+ import { AutoCoreTagContext } from '../../../core/AutoCoreTagContext';
15
+
11
16
 
12
17
  const FIELD_TYPES: { label: string; value: string }[] = [
13
18
  { label: 'string', value: 'string' },
@@ -83,6 +88,17 @@ export const TestFieldDialog: React.FC<TestFieldDialogProps> = ({
83
88
  const [defaultText, setDefaultText] = useState<string>('');
84
89
  const [error, setError] = useState<string | null>(null);
85
90
 
91
+ // The quantity names this machine actually declares, from the resolved
92
+ // units table. Offered as a dropdown so an author picks a row that exists
93
+ // instead of typing one that silently never resolves — but `editable`, so a
94
+ // method can still be authored against a quantity the bench machine has yet
95
+ // to declare.
96
+ const { scales } = useContext(AutoCoreTagContext);
97
+ const quantityOptions = useMemo(
98
+ () => Object.keys(scales ?? {}).sort().map(q => ({ label: q, value: q })),
99
+ [scales],
100
+ );
101
+
86
102
  useEffect(() => {
87
103
  if (visible) {
88
104
  setDraft(initial ? { ...initial } : { ...blank });
@@ -116,6 +132,13 @@ export const TestFieldDialog: React.FC<TestFieldDialogProps> = ({
116
132
  if (typeof f.min === 'number' && typeof f.max === 'number' && f.min > f.max) {
117
133
  return `Min (${f.min}) cannot be greater than Max (${f.max}).`;
118
134
  }
135
+ // A bound that is neither a number nor a well-formed token resolves to
136
+ // NO bound at run time — silently unlimited. Refuse the save instead.
137
+ for (const which of ['min', 'max'] as const) {
138
+ if (boundInvalid(f[which])) {
139
+ return `${which === 'min' ? 'Min' : 'Max'} must be a number or an FQDN token like \${gm.some_var} — "${f[which]}" is neither.`;
140
+ }
141
+ }
119
142
  if (f.aggregate && f.aggregate.fn !== 'count' && !f.aggregate.of?.trim()) {
120
143
  return `Aggregate "${f.aggregate.fn}" needs a target field (the field to ${f.aggregate.fn} across tests).`;
121
144
  }
@@ -189,7 +212,23 @@ export const TestFieldDialog: React.FC<TestFieldDialogProps> = ({
189
212
  editable
190
213
  />
191
214
  </FormRow>
192
- <FormRow label="Units" hint="Display label, appended to form labels (e.g. m/s).">
215
+ <FormRow
216
+ label="Quantity"
217
+ hint="Ties this field to a row of the machine's units.json, so it follows the operator's Metric/Imperial choice and takes its unit label from the active system. Set this in preference to Units/Scale, which are fixed."
218
+ >
219
+ <Dropdown
220
+ value={draft.quantity ?? ''}
221
+ options={quantityOptions}
222
+ onChange={(e) => setDraft({ ...draft, quantity: e.value || undefined })}
223
+ placeholder="— none (fixed Units/Scale below) —"
224
+ showClear
225
+ editable
226
+ />
227
+ </FormRow>
228
+ <FormRow
229
+ label="Units"
230
+ hint="Fixed display label appended to form labels (e.g. m/s). Ignored when Quantity is set — the active unit system supplies the label."
231
+ >
193
232
  <InputText
194
233
  value={draft.units ?? ''}
195
234
  onChange={(e) => setDraft({ ...draft, units: e.target.value || undefined })}
@@ -241,26 +280,26 @@ export const TestFieldDialog: React.FC<TestFieldDialogProps> = ({
241
280
  maxFractionDigits={9}
242
281
  />
243
282
  </FormRow>
244
- <FormRow label="Min" hint="Optional lower bound the operator can enter (display units). Blank = no minimum.">
245
- <InputNumber
246
- value={draft.min ?? null}
247
- onValueChange={(e) =>
248
- setDraft({ ...draft, min: typeof e.value === 'number' ? e.value : undefined })
249
- }
250
- mode="decimal"
251
- minFractionDigits={0}
252
- maxFractionDigits={9}
283
+ <FormRow
284
+ label="Min"
285
+ hint="Optional lower bound the operator can enter (display units). Blank = no minimum. Or bind it to the machine with an FQDN token like ${gm.press_force_min_n} for a bound that moves, e.g. a load-cell force ceiling."
286
+ >
287
+ <InputText
288
+ value={boundText(draft.min)}
289
+ onChange={(e) => setDraft({ ...draft, min: parseBound(e.target.value) })}
290
+ className={boundInvalid(draft.min) ? 'p-invalid' : ''}
291
+ placeholder="number or ${gm.…}"
253
292
  />
254
293
  </FormRow>
255
- <FormRow label="Max" hint="Optional upper bound the operator can enter (display units). Blank = no maximum.">
256
- <InputNumber
257
- value={draft.max ?? null}
258
- onValueChange={(e) =>
259
- setDraft({ ...draft, max: typeof e.value === 'number' ? e.value : undefined })
260
- }
261
- mode="decimal"
262
- minFractionDigits={0}
263
- maxFractionDigits={9}
294
+ <FormRow
295
+ label="Max"
296
+ hint="Optional upper bound the operator can enter (display units). Blank = no maximum. Or bind it to the machine with an FQDN token like ${gm.press_force_max_n}."
297
+ >
298
+ <InputText
299
+ value={boundText(draft.max)}
300
+ onChange={(e) => setDraft({ ...draft, max: parseBound(e.target.value) })}
301
+ className={boundInvalid(draft.max) ? 'p-invalid' : ''}
302
+ placeholder="number or ${gm.…}"
264
303
  />
265
304
  </FormRow>
266
305
  <FormRow label="Default" hint="Applied every time the method loads (operator can override). A literal (display units) like 5, or an FQDN token like ${gm.safe_speed} that snapshots that value on load.">
@@ -15,6 +15,11 @@ export type FieldType =
15
15
  export interface TestField {
16
16
  name: string;
17
17
  type: FieldType | string;
18
+ /** Scale-group name from the machine's `units.json` ("force", "position",
19
+ * …). What makes this field follow the operator's Metric/Imperial choice;
20
+ * supersedes `units` / `scale`. Declared here so the editor round-trips it
21
+ * — see `TestFieldDialog`'s Quantity row. */
22
+ quantity?: string;
18
23
  units?: string;
19
24
  required?: boolean;
20
25
  source?: string;
@@ -25,8 +30,12 @@ export interface TestField {
25
30
  /** Optional inclusive bounds for the operator's numeric value, authored
26
31
  * in display units (same convention as `default`/`scale`). The form's
27
32
  * numeric input rejects values outside `[min, max]`. */
28
- min?: number;
29
- max?: number;
33
+ /** Inclusive bounds. A number is in display units; a string is an FQDN
34
+ * token `"${gm.<var>}"` resolved live from the machine — see
35
+ * `TestFieldRow.resolveBound`. The editor must preserve a token it does
36
+ * not understand rather than coercing it to a number. */
37
+ min?: number | string;
38
+ max?: number | string;
30
39
  /** results_fields only: a cross-test rollup derived server-side over the
31
40
  * non-excluded runs sharing this test's sample_id. When set, the field
32
41
  * is NOT supplied by the analysis script — the server computes it. */