harden CSV import reconciliation and preserve saved model selections
Default fuzzy reconcile threshold to 0 and refuse merges when digit runs differ so numbered labels like Bay 311 never bind to Bay 310. Keep edit forms from clearing saved model fields when category filtering hides untyped rows. Includes in-progress computers policy and endpoints command feature work.
This commit is contained in:
@@ -98,6 +98,37 @@ export function badges(panel) {
|
||||
return list.map((b) => ({ label: b.label, color: b.color }))
|
||||
}
|
||||
|
||||
// Header status rollup for a card. A panel opts in with
|
||||
// status: { key, levels: { <rowvalue>: 'critical'|'warning'|'ok'|'unknown' } }.
|
||||
// The card header then shows a single green/yellow/red dot reflecting the worst
|
||||
// row: any critical -> red, else any warning -> yellow, else ok -> green. A
|
||||
// value not in levels is treated as unknown and does not raise the level.
|
||||
export const STATUS_RANK = { ok: 1, unknown: 1, warning: 2, critical: 3 }
|
||||
export const STATUS_COLOR = {
|
||||
ok: '#2dce89',
|
||||
warning: '#ffc107',
|
||||
critical: '#f5365c',
|
||||
unknown: '#6c757d',
|
||||
}
|
||||
|
||||
export function rollupStatus(panel) {
|
||||
const status = panel && panel.status
|
||||
if (!status || !status.key) return null
|
||||
const items = rows(panel)
|
||||
if (!items.length) return null
|
||||
const levels = status.levels || {}
|
||||
let worst = 'ok'
|
||||
let sawKnown = false
|
||||
for (const item of items) {
|
||||
const level = levels[item[status.key]]
|
||||
if (!level) continue
|
||||
sawKnown = true
|
||||
if (STATUS_RANK[level] > STATUS_RANK[worst]) worst = level
|
||||
}
|
||||
if (!sawKnown) return null
|
||||
return { level: worst, color: STATUS_COLOR[worst] || STATUS_COLOR.unknown }
|
||||
}
|
||||
|
||||
// tabs: [{ label, fields: [{label, value, mono}] }]. One section is shown at a
|
||||
// time, so a panel with 30 values stays the height of its largest section
|
||||
// instead of dragging one multicol column far past the other.
|
||||
@@ -114,6 +145,30 @@ export function tabSections(panel) {
|
||||
.filter((s) => s.fields.length > 0)
|
||||
}
|
||||
|
||||
// Inline row action for a list panel. A monitored services/processes row may
|
||||
// carry action: { running, start, stop, status } when it has a registered
|
||||
// command (e.g. MTConnect). The card renders a single toggle on that row: when
|
||||
// running it offers Stop, otherwise Start. Rows with no action are badge-only.
|
||||
// commandendpoint on the panel is where the enqueue POST goes.
|
||||
export function rowAction(panel, item) {
|
||||
const action = item && item.action
|
||||
if (!action || (!action.start && !action.stop)) return null
|
||||
const running = action.running === true
|
||||
return {
|
||||
running,
|
||||
// The action name to enqueue for the primary toggle button.
|
||||
toggle: running ? action.stop : action.start,
|
||||
toggleLabel: running ? 'Stop' : 'Start',
|
||||
// status is a non-mutating probe, always available when declared.
|
||||
status: action.status || null,
|
||||
// Generic actions (service_*/process_*) carry the subject to act on; fixed
|
||||
// actions (mtconnect_*) leave this null.
|
||||
target: action.target || null,
|
||||
// The declared enqueue endpoint template; the caller substitutes assetid.
|
||||
endpoint: panel.commandendpoint || null,
|
||||
}
|
||||
}
|
||||
|
||||
export function panelVisible(panel) {
|
||||
if (panel.render === 'tabs') return tabSections(panel).length > 0
|
||||
if (panel.render === 'list') return rows(panel).length > 0 || !!panel.empty
|
||||
|
||||
Reference in New Issue
Block a user