Phase 2: dashboard tabs + popups, chart fixes, shell relocation

Built via parallel Workflow (5 builders + adversarial verifiers), then
live-rendered and fixed in a headless browser against the running gateway.

Tabs (PrimeControls/Tabs/*):
- Overview: priority cards, KPI status row, alarm-rate timeline, insights, top-5
- Analysis: hour x day heatmap, priority donut vs ISA 80/15/5, MTTA/MTTR, floods
- BadActors: chattering/standing/fleeting tables, focus chip, AlarmDetail wiring
- Journal: paginated getJournalPage table, CSV export, EventDetail wiring

Popups (PrimeControls/Popups/*): HealthScore, ShiftReport, AlarmDetail, EventDetail

Chart fixes (verified rendering in-browser):
- Overview timeline: seeded dataSources.rate, switched to static fills, split
  flood bins into their own red stacked series (per-point deriveFieldsFromData
  fill was crashing render with React.cloneElement null).
- BadActors: removed the dual-axis Pareto XY chart (scaling was unusable);
  replaced with a ranked "Top Sources" table (share-of-total progress bar +
  cumulative %). Same insight, no axis scaling to break.

Structural / hygiene:
- Relocated Header + FilterBar from Dashboard/ to Shell/ and updated the two
  embed paths. A Perspective view cannot resolve sub-views nested inside another
  view's folder, so both were rendering as "view does not exist" placeholders.
- Set persistent:false on all view input params. persistent:true caused the
  gateway to bake the live bundle (with sim tag paths) into Tabs/Analysis on
  render; nulled that baked data and reset the session query range to 0.

lint_project.py: 0 failures / 0 warnings.

Verified in-browser: Overview, BadActors. NOT yet live-verified: Analysis,
Journal, and the four popups (schema-verified only). Header content is next
phase (renders now, but a benign cloneElement console warning remains).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-16 14:45:17 -05:00
parent ecb10b9b7b
commit 83b23431c6
30 changed files with 8678 additions and 312 deletions

View File

@@ -0,0 +1,10 @@
{
"scope": "G",
"version": 1,
"restricted": false,
"overridable": true,
"files": [
"view.json"
],
"attributes": {}
}

View File

@@ -0,0 +1,792 @@
{
"custom": {
"detail": null
},
"params": {
"label": null,
"source": null
},
"propConfig": {
"custom.detail": {
"binding": {
"config": {
"expression": "{view.params.source} + '|' + {session.custom.PrimeControls.query.startMs} + '|' + {session.custom.PrimeControls.query.endMs}"
},
"transforms": [
{
"code": "\ttry:\n\t\tsrc = self.view.params.source\n\texcept:\n\t\tsrc = None\n\tif not src:\n\t\treturn None\n\ttry:\n\t\tq = self.session.custom.PrimeControls.query\n\t\treturn PrimeControls.alarms.getSourceDetail(src, q.startMs, q.endMs)\n\texcept:\n\t\treturn None",
"type": "script"
}
],
"type": "expr"
}
},
"params.label": {
"paramDirection": "input",
"persistent": false
},
"params.source": {
"paramDirection": "input",
"persistent": false
}
},
"props": {
"defaultSize": {
"height": 660,
"width": 720
}
},
"root": {
"children": [
{
"children": [
{
"meta": {
"name": "lblTitle"
},
"propConfig": {
"props.text": {
"binding": {
"config": {
"path": "view.params.label"
},
"transforms": [
{
"code": "\tif value:\n\t\treturn value\n\ttry:\n\t\treturn self.view.params.source or 'Alarm Detail'\n\texcept:\n\t\treturn 'Alarm Detail'",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"style": {
"classes": "PrimeControls/Text/Big"
}
},
"type": "ia.display.label"
},
{
"meta": {
"name": "lblArea"
},
"propConfig": {
"props.text": {
"binding": {
"config": {
"path": "view.custom.detail"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn value['area'] or ''\n\texcept:\n\t\treturn ''",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"style": {
"classes": "PrimeControls/Text/Muted"
}
},
"type": "ia.display.label"
}
],
"meta": {
"name": "headerCol"
},
"position": {
"shrink": 0
},
"props": {
"direction": "column"
},
"type": "ia.container.flex"
},
{
"children": [
{
"children": [
{
"meta": {
"name": "kpiCount"
},
"position": {
"basis": "100px",
"grow": 1
},
"propConfig": {
"props.params.value": {
"binding": {
"config": {
"path": "view.custom.detail"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn PrimeControls.fmt.num(value['stats']['count'])\n\texcept:\n\t\treturn '--'",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"params": {
"label": "Activations"
},
"path": "PrimeControls/Components/KpiCard"
},
"type": "ia.display.view"
},
{
"meta": {
"name": "kpiTta"
},
"position": {
"basis": "100px",
"grow": 1
},
"propConfig": {
"props.params.value": {
"binding": {
"config": {
"path": "view.custom.detail"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn PrimeControls.fmt.dur(value['stats']['avg_tta_ms'])\n\texcept:\n\t\treturn '--'",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"params": {
"label": "Avg Time to Ack"
},
"path": "PrimeControls/Components/KpiCard"
},
"type": "ia.display.view"
},
{
"meta": {
"name": "kpiActive"
},
"position": {
"basis": "100px",
"grow": 1
},
"propConfig": {
"props.params.value": {
"binding": {
"config": {
"path": "view.custom.detail"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn PrimeControls.fmt.dur(value['stats']['avg_active_ms'])\n\texcept:\n\t\treturn '--'",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"params": {
"label": "Avg Active Time"
},
"path": "PrimeControls/Components/KpiCard"
},
"type": "ia.display.view"
},
{
"meta": {
"name": "kpiFleeting"
},
"position": {
"basis": "100px",
"grow": 1
},
"propConfig": {
"props.params.value": {
"binding": {
"config": {
"path": "view.custom.detail"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn PrimeControls.fmt.num(value['stats']['fleeting_count'])\n\texcept:\n\t\treturn '--'",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"params": {
"label": "Fleeting"
},
"path": "PrimeControls/Components/KpiCard"
},
"type": "ia.display.view"
}
],
"meta": {
"name": "statsRow"
},
"position": {
"basis": "88px",
"shrink": 0
},
"props": {
"alignItems": "stretch",
"style": {
"gap": "10px"
}
},
"type": "ia.container.flex"
},
{
"children": [
{
"meta": {
"name": "lblDailyTitle"
},
"position": {
"shrink": 0
},
"props": {
"style": {
"classes": "PrimeControls/CardTitle"
},
"text": "Daily activity"
},
"type": "ia.display.label"
},
{
"meta": {
"name": "repDaily"
},
"position": {
"basis": "30px",
"shrink": 0
},
"propConfig": {
"props.instances": {
"binding": {
"config": {
"path": "view.custom.detail"
},
"transforms": [
{
"code": "\ttry:\n\t\tdaily = list(value['daily'] or [])\n\texcept:\n\t\tdaily = []\n\tcounts = []\n\tfor d in daily:\n\t\ttry:\n\t\t\tcounts.append(int(d['count']))\n\t\texcept:\n\t\t\tcounts.append(0)\n\tif not counts:\n\t\treturn []\n\tm = max(counts)\n\treturn [{'count': c, 'max': m} for c in counts]",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"direction": "row",
"elementPosition": {
"basis": "12px",
"grow": 1,
"shrink": 1
},
"path": "PrimeControls/Components/HeatmapCell"
},
"type": "ia.display.flex-repeater"
},
{
"meta": {
"name": "lblDailyRange"
},
"position": {
"shrink": 0
},
"propConfig": {
"props.text": {
"binding": {
"config": {
"path": "view.custom.detail"
},
"transforms": [
{
"code": "\ttry:\n\t\tdaily = list(value['daily'] or [])\n\texcept:\n\t\tdaily = []\n\tif not daily:\n\t\treturn 'No activity in range'\n\ttry:\n\t\treturn 'Activations per day, %s - %s' % (PrimeControls.fmt.day_clock(daily[0]['t0']), PrimeControls.fmt.day_clock(daily[-1]['t0']))\n\texcept:\n\t\treturn 'Activations per day'",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"style": {
"classes": "PrimeControls/Text/Muted"
}
},
"type": "ia.display.label"
}
],
"meta": {
"name": "cardDaily"
},
"position": {
"shrink": 0
},
"props": {
"direction": "column",
"style": {
"classes": "PrimeControls/Card",
"gap": "6px"
}
},
"type": "ia.container.flex"
},
{
"children": [
{
"meta": {
"name": "lblAckTitle"
},
"position": {
"shrink": 0
},
"props": {
"style": {
"classes": "PrimeControls/CardTitle"
},
"text": "Top ack users"
},
"type": "ia.display.label"
},
{
"children": [
{
"meta": {
"name": "ack0"
},
"position": {
"shrink": 0
},
"propConfig": {
"position.display": {
"binding": {
"config": {
"path": "view.custom.detail"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn len(value['stats']['top_ack_users']) > 0\n\texcept:\n\t\treturn False",
"type": "script"
}
],
"type": "property"
}
},
"props.text": {
"binding": {
"config": {
"path": "view.custom.detail"
},
"transforms": [
{
"code": "\ttry:\n\t\tu = value['stats']['top_ack_users'][0]\n\t\treturn '%s (%d)' % (u['user'], u['count'])\n\texcept:\n\t\treturn ''",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"style": {
"classes": "PrimeControls/Chip/Flat",
"paddingBottom": "2px",
"paddingTop": "2px"
}
},
"type": "ia.display.label"
},
{
"meta": {
"name": "ack1"
},
"position": {
"shrink": 0
},
"propConfig": {
"position.display": {
"binding": {
"config": {
"path": "view.custom.detail"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn len(value['stats']['top_ack_users']) > 1\n\texcept:\n\t\treturn False",
"type": "script"
}
],
"type": "property"
}
},
"props.text": {
"binding": {
"config": {
"path": "view.custom.detail"
},
"transforms": [
{
"code": "\ttry:\n\t\tu = value['stats']['top_ack_users'][1]\n\t\treturn '%s (%d)' % (u['user'], u['count'])\n\texcept:\n\t\treturn ''",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"style": {
"classes": "PrimeControls/Chip/Flat",
"paddingBottom": "2px",
"paddingTop": "2px"
}
},
"type": "ia.display.label"
},
{
"meta": {
"name": "ack2"
},
"position": {
"shrink": 0
},
"propConfig": {
"position.display": {
"binding": {
"config": {
"path": "view.custom.detail"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn len(value['stats']['top_ack_users']) > 2\n\texcept:\n\t\treturn False",
"type": "script"
}
],
"type": "property"
}
},
"props.text": {
"binding": {
"config": {
"path": "view.custom.detail"
},
"transforms": [
{
"code": "\ttry:\n\t\tu = value['stats']['top_ack_users'][2]\n\t\treturn '%s (%d)' % (u['user'], u['count'])\n\texcept:\n\t\treturn ''",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"style": {
"classes": "PrimeControls/Chip/Flat",
"paddingBottom": "2px",
"paddingTop": "2px"
}
},
"type": "ia.display.label"
},
{
"meta": {
"name": "lblNoAcks"
},
"position": {
"shrink": 0
},
"propConfig": {
"position.display": {
"binding": {
"config": {
"path": "view.custom.detail"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn len(value['stats']['top_ack_users']) == 0\n\texcept:\n\t\treturn True",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"style": {
"classes": "PrimeControls/Text/Muted"
},
"text": "No acknowledgements in range"
},
"type": "ia.display.label"
}
],
"meta": {
"name": "ackRow"
},
"position": {
"shrink": 0
},
"props": {
"alignItems": "center",
"style": {
"gap": "6px"
},
"wrap": "wrap"
},
"type": "ia.container.flex"
}
],
"meta": {
"name": "cardAcks"
},
"position": {
"shrink": 0
},
"props": {
"direction": "column",
"style": {
"classes": "PrimeControls/Card",
"gap": "6px"
}
},
"type": "ia.container.flex"
},
{
"children": [
{
"meta": {
"name": "lblEvTitle"
},
"position": {
"shrink": 0
},
"props": {
"style": {
"classes": "PrimeControls/CardTitle"
},
"text": "Recent events"
},
"type": "ia.display.label"
},
{
"meta": {
"name": "tblEvents"
},
"position": {
"grow": 1
},
"propConfig": {
"props.data": {
"binding": {
"config": {
"path": "view.custom.detail"
},
"transforms": [
{
"code": "\tout = []\n\ttry:\n\t\trows = list(value['events'] or [])\n\texcept:\n\t\trows = []\n\tfor r in rows[:25]:\n\t\ttry:\n\t\t\tout.append({'time_label': r['time_label'], 'state_label': r['state_label'], 'ack_user': r['ack_user'] or ''})\n\t\texcept:\n\t\t\tpass\n\treturn out",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"columns": [
{
"field": "time_label",
"header": {
"title": "Time"
},
"render": "string",
"sortable": false,
"strictWidth": true,
"width": 160
},
{
"field": "state_label",
"header": {
"title": "State"
},
"render": "string",
"sortable": false,
"strictWidth": true,
"width": 110
},
{
"field": "ack_user",
"header": {
"title": "Ack by"
},
"render": "string",
"sortable": false
}
],
"emptyMessage": {
"noData": {
"text": "No events in range"
}
},
"pager": {
"bottom": false,
"top": false
}
},
"type": "ia.display.table"
}
],
"meta": {
"name": "cardEvents"
},
"position": {
"grow": 1
},
"props": {
"direction": "column",
"style": {
"classes": "PrimeControls/Card",
"gap": "6px"
}
},
"type": "ia.container.flex"
}
],
"meta": {
"name": "bodyCol"
},
"position": {
"grow": 1
},
"propConfig": {
"position.display": {
"binding": {
"config": {
"path": "view.custom.detail"
},
"transforms": [
{
"code": "\tif value is None:\n\t\treturn False\n\ttry:\n\t\treturn not value['error']\n\texcept:\n\t\treturn True",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"direction": "column",
"style": {
"gap": "10px"
}
},
"type": "ia.container.flex"
},
{
"meta": {
"name": "emptyWrap"
},
"position": {
"grow": 1
},
"propConfig": {
"position.display": {
"binding": {
"config": {
"path": "view.custom.detail"
},
"transforms": [
{
"code": "\tif value is None:\n\t\treturn True\n\ttry:\n\t\treturn bool(value['error'])\n\texcept:\n\t\treturn False",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"params": {
"icon": "notifications_off",
"message": "No journal data for this alarm in the selected range"
},
"path": "PrimeControls/Components/EmptyState"
},
"type": "ia.display.view"
},
{
"children": [
{
"events": {
"component": {
"onActionPerformed": {
"config": {
"script": "\tsystem.perspective.closePopup('PC_AlarmDetail')"
},
"scope": "G",
"type": "script"
}
}
},
"meta": {
"name": "btnClose"
},
"position": {
"basis": "90px",
"shrink": 0
},
"props": {
"primary": true,
"text": "Close"
},
"type": "ia.input.button"
}
],
"meta": {
"name": "footerRow"
},
"position": {
"shrink": 0
},
"props": {
"alignItems": "center",
"justify": "flex-end"
},
"type": "ia.container.flex"
}
],
"custom": {},
"meta": {
"name": "root"
},
"props": {
"direction": "column",
"style": {
"classes": "PrimeControls/PageBg",
"gap": "10px",
"padding": "16px"
}
},
"type": "ia.container.flex"
}
}

View File

@@ -0,0 +1,10 @@
{
"scope": "G",
"version": 1,
"restricted": false,
"overridable": true,
"files": [
"view.json"
],
"attributes": {}
}

View File

@@ -0,0 +1,660 @@
{
"custom": {
"props": []
},
"params": {
"event": null
},
"propConfig": {
"custom.props": {
"binding": {
"config": {
"path": "view.params.event"
},
"transforms": [
{
"code": "\tev = value\n\tif not ev:\n\t\treturn []\n\ttry:\n\t\tres = PrimeControls.alarms.getEventData(ev['id'], ev['time_ms'], ev['source'])\n\t\treturn res['props'] or []\n\texcept:\n\t\treturn []",
"type": "script"
}
],
"type": "property"
}
},
"params.event": {
"paramDirection": "input",
"persistent": false
}
},
"props": {
"defaultSize": {
"height": 580,
"width": 580
}
},
"root": {
"children": [
{
"children": [
{
"meta": {
"name": "lblTitle"
},
"position": {
"grow": 1
},
"propConfig": {
"props.text": {
"binding": {
"config": {
"path": "view.params.event"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn value['label'] or value['source'] or 'Event Detail'\n\texcept:\n\t\treturn 'Event Detail'",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"style": {
"classes": "PrimeControls/Text/Big"
}
},
"type": "ia.display.label"
},
{
"meta": {
"name": "chipState"
},
"position": {
"shrink": 0
},
"propConfig": {
"position.display": {
"binding": {
"config": {
"path": "view.params.event"
},
"transforms": [
{
"code": "\treturn value is not None",
"type": "script"
}
],
"type": "property"
}
},
"props.style.classes": {
"binding": {
"config": {
"path": "view.params.event"
},
"transforms": [
{
"code": "\ttry:\n\t\ts = value['state']\n\texcept:\n\t\ts = None\n\tif s == 'ack':\n\t\treturn 'PrimeControls/State/Acked'\n\tif s == 'clear':\n\t\treturn 'PrimeControls/State/Cleared'\n\treturn 'PrimeControls/State/Active'",
"type": "script"
}
],
"type": "property"
}
},
"props.text": {
"binding": {
"config": {
"path": "view.params.event"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn value['state_label'] or ''\n\texcept:\n\t\treturn ''",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"style": {
"borderRadius": "10px",
"fontSize": "11px",
"paddingBottom": "2px",
"paddingLeft": "8px",
"paddingRight": "8px",
"paddingTop": "2px"
}
},
"type": "ia.display.label"
},
{
"meta": {
"name": "chipPriority"
},
"position": {
"shrink": 0
},
"propConfig": {
"position.display": {
"binding": {
"config": {
"path": "view.params.event"
},
"transforms": [
{
"code": "\treturn value is not None",
"type": "script"
}
],
"type": "property"
}
},
"props.style.classes": {
"binding": {
"config": {
"path": "view.params.event"
},
"transforms": [
{
"code": "\ttry:\n\t\tp = value['priority_name']\n\texcept:\n\t\tp = None\n\tif p not in ['Diagnostic', 'Low', 'Medium', 'High', 'Critical']:\n\t\tp = 'Diagnostic'\n\treturn 'PrimeControls/Priority/' + p",
"type": "script"
}
],
"type": "property"
}
},
"props.text": {
"binding": {
"config": {
"path": "view.params.event"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn value['priority_name'] or ''\n\texcept:\n\t\treturn ''",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"style": {
"borderRadius": "10px",
"fontSize": "11px",
"paddingBottom": "2px",
"paddingLeft": "8px",
"paddingRight": "8px",
"paddingTop": "2px"
}
},
"type": "ia.display.label"
}
],
"meta": {
"name": "headerRow"
},
"position": {
"shrink": 0
},
"props": {
"alignItems": "center",
"style": {
"gap": "8px"
}
},
"type": "ia.container.flex"
},
{
"children": [
{
"children": [
{
"children": [
{
"meta": {
"name": "k"
},
"position": {
"basis": "90px",
"shrink": 0
},
"props": {
"style": {
"classes": "PrimeControls/Text/Muted"
},
"text": "Time"
},
"type": "ia.display.label"
},
{
"meta": {
"name": "v"
},
"position": {
"grow": 1
},
"propConfig": {
"props.text": {
"binding": {
"config": {
"path": "view.params.event"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn value['time_label'] or PrimeControls.fmt.day_clock(value['time_ms'])\n\texcept:\n\t\treturn '--'",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {},
"type": "ia.display.label"
}
],
"meta": {
"name": "rowTime"
},
"props": {
"style": {
"gap": "8px"
}
},
"type": "ia.container.flex"
},
{
"children": [
{
"meta": {
"name": "k"
},
"position": {
"basis": "90px",
"shrink": 0
},
"props": {
"style": {
"classes": "PrimeControls/Text/Muted"
},
"text": "Source"
},
"type": "ia.display.label"
},
{
"meta": {
"name": "v"
},
"position": {
"grow": 1
},
"propConfig": {
"props.text": {
"binding": {
"config": {
"path": "view.params.event"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn value['source'] or '--'\n\texcept:\n\t\treturn '--'",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"style": {
"overflowWrap": "anywhere"
}
},
"type": "ia.display.label"
}
],
"meta": {
"name": "rowSource"
},
"props": {
"style": {
"gap": "8px"
}
},
"type": "ia.container.flex"
},
{
"children": [
{
"meta": {
"name": "k"
},
"position": {
"basis": "90px",
"shrink": 0
},
"props": {
"style": {
"classes": "PrimeControls/Text/Muted"
},
"text": "Area"
},
"type": "ia.display.label"
},
{
"meta": {
"name": "v"
},
"position": {
"grow": 1
},
"propConfig": {
"props.text": {
"binding": {
"config": {
"path": "view.params.event"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn value['area'] or '--'\n\texcept:\n\t\treturn '--'",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {},
"type": "ia.display.label"
}
],
"meta": {
"name": "rowArea"
},
"props": {
"style": {
"gap": "8px"
}
},
"type": "ia.container.flex"
},
{
"children": [
{
"meta": {
"name": "k"
},
"position": {
"basis": "90px",
"shrink": 0
},
"props": {
"style": {
"classes": "PrimeControls/Text/Muted"
},
"text": "Ack by"
},
"type": "ia.display.label"
},
{
"meta": {
"name": "v"
},
"position": {
"grow": 1
},
"propConfig": {
"props.text": {
"binding": {
"config": {
"path": "view.params.event"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn value['ack_user'] or '--'\n\texcept:\n\t\treturn '--'",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {},
"type": "ia.display.label"
}
],
"meta": {
"name": "rowAck"
},
"props": {
"style": {
"gap": "8px"
}
},
"type": "ia.container.flex"
}
],
"meta": {
"name": "cardCore"
},
"position": {
"shrink": 0
},
"props": {
"direction": "column",
"style": {
"classes": "PrimeControls/Card",
"gap": "4px"
}
},
"type": "ia.container.flex"
},
{
"children": [
{
"meta": {
"name": "lblPropsTitle"
},
"position": {
"shrink": 0
},
"props": {
"style": {
"classes": "PrimeControls/CardTitle"
},
"text": "Event properties"
},
"type": "ia.display.label"
},
{
"meta": {
"name": "tblProps"
},
"position": {
"grow": 1
},
"propConfig": {
"props.data": {
"binding": {
"config": {
"path": "view.custom.props"
},
"transforms": [
{
"code": "\tout = []\n\tfor p in (value or []):\n\t\ttry:\n\t\t\tv = p['value']\n\t\t\tout.append({'name': '%s' % p['name'], 'value': '' if v is None else '%s' % v})\n\t\texcept:\n\t\t\tpass\n\treturn out",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"columns": [
{
"field": "name",
"header": {
"title": "Property"
},
"render": "string",
"sortable": false,
"strictWidth": true,
"width": 180
},
{
"field": "value",
"header": {
"title": "Value"
},
"render": "string",
"sortable": false
}
],
"emptyMessage": {
"noData": {
"text": "No extended properties for this event"
}
},
"pager": {
"bottom": false,
"top": false
}
},
"type": "ia.display.table"
}
],
"meta": {
"name": "cardProps"
},
"position": {
"grow": 1
},
"props": {
"direction": "column",
"style": {
"classes": "PrimeControls/Card",
"gap": "6px"
}
},
"type": "ia.container.flex"
}
],
"meta": {
"name": "bodyCol"
},
"position": {
"grow": 1
},
"propConfig": {
"position.display": {
"binding": {
"config": {
"path": "view.params.event"
},
"transforms": [
{
"code": "\treturn value is not None",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"direction": "column",
"style": {
"gap": "10px"
}
},
"type": "ia.container.flex"
},
{
"meta": {
"name": "emptyWrap"
},
"position": {
"grow": 1
},
"propConfig": {
"position.display": {
"binding": {
"config": {
"path": "view.params.event"
},
"transforms": [
{
"code": "\treturn value is None",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"params": {
"icon": "info",
"message": "No event selected"
},
"path": "PrimeControls/Components/EmptyState"
},
"type": "ia.display.view"
},
{
"children": [
{
"events": {
"component": {
"onActionPerformed": {
"config": {
"script": "\tsystem.perspective.closePopup('PC_EventDetail')"
},
"scope": "G",
"type": "script"
}
}
},
"meta": {
"name": "btnClose"
},
"position": {
"basis": "90px",
"shrink": 0
},
"props": {
"primary": true,
"text": "Close"
},
"type": "ia.input.button"
}
],
"meta": {
"name": "footerRow"
},
"position": {
"shrink": 0
},
"props": {
"alignItems": "center",
"justify": "flex-end"
},
"type": "ia.container.flex"
}
],
"custom": {},
"meta": {
"name": "root"
},
"props": {
"direction": "column",
"style": {
"classes": "PrimeControls/PageBg",
"gap": "10px",
"padding": "16px"
}
},
"type": "ia.container.flex"
}
}

View File

@@ -0,0 +1,10 @@
{
"scope": "G",
"version": 1,
"restricted": false,
"overridable": true,
"files": [
"view.json"
],
"attributes": {}
}

View File

@@ -0,0 +1,316 @@
{
"custom": {},
"params": {
"health": null
},
"propConfig": {
"params.health": {
"paramDirection": "input",
"persistent": false
}
},
"props": {
"defaultSize": {
"height": 540,
"width": 540
}
},
"root": {
"children": [
{
"children": [
{
"meta": {
"name": "gradeChip"
},
"position": {
"shrink": 0
},
"propConfig": {
"props.style.classes": {
"binding": {
"config": {
"path": "view.params.health"
},
"transforms": [
{
"code": "\ttry:\n\t\tg = value['grade']\n\texcept:\n\t\tg = None\n\treturn 'PrimeControls/Grade/' + (g if g in ['A', 'B', 'C', 'D', 'F'] else 'NA')",
"type": "script"
}
],
"type": "property"
}
},
"props.text": {
"binding": {
"config": {
"path": "view.params.health"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn value['grade'] or 'N/A'\n\texcept:\n\t\treturn 'N/A'",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"style": {
"fontSize": "38px",
"paddingBottom": "4px",
"paddingLeft": "18px",
"paddingRight": "18px",
"paddingTop": "4px",
"textAlign": "center"
}
},
"type": "ia.display.label"
},
{
"children": [
{
"meta": {
"name": "lblTitle"
},
"props": {
"style": {
"classes": "PrimeControls/Text/Big"
},
"text": "Alarm Health Score"
},
"type": "ia.display.label"
},
{
"meta": {
"name": "lblScore"
},
"propConfig": {
"props.text": {
"binding": {
"config": {
"path": "view.params.health"
},
"transforms": [
{
"code": "\ttry:\n\t\ts = value['score']\n\texcept:\n\t\ts = None\n\tif s is None:\n\t\treturn 'No data in selected range'\n\treturn 'Overall score %s / 100' % PrimeControls.fmt.num(s, 1)",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"style": {
"classes": "PrimeControls/Text/Muted"
}
},
"type": "ia.display.label"
}
],
"meta": {
"name": "titleCol"
},
"position": {
"grow": 1
},
"props": {
"direction": "column",
"justify": "center"
},
"type": "ia.container.flex"
}
],
"meta": {
"name": "headerRow"
},
"position": {
"shrink": 0
},
"props": {
"alignItems": "center",
"style": {
"gap": "14px"
}
},
"type": "ia.container.flex"
},
{
"children": [
{
"meta": {
"name": "lblSubsTitle"
},
"position": {
"shrink": 0
},
"props": {
"style": {
"classes": "PrimeControls/CardTitle"
},
"text": "Component scores"
},
"type": "ia.display.label"
},
{
"meta": {
"name": "repSubs"
},
"position": {
"grow": 1
},
"propConfig": {
"props.instances": {
"binding": {
"config": {
"path": "view.params.health"
},
"transforms": [
{
"code": "\ttry:\n\t\tsubs = value['subs'] or []\n\texcept:\n\t\tsubs = []\n\tout = []\n\tfor s in subs:\n\t\ttry:\n\t\t\tout.append({'key': s['key'], 'label': s['label'], 'score': s['score'], 'weight': s['weight'], 'detail': s['detail']})\n\t\texcept:\n\t\t\tpass\n\treturn out",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"direction": "column",
"elementPosition": {
"basis": "64px",
"grow": 0,
"shrink": 0
},
"path": "PrimeControls/Components/ScoreBar",
"style": {
"overflowY": "auto"
}
},
"type": "ia.display.flex-repeater"
},
{
"meta": {
"name": "emptyWrap"
},
"position": {
"grow": 1
},
"propConfig": {
"position.display": {
"binding": {
"config": {
"path": "view.params.health"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn not (value and value['subs'])\n\texcept:\n\t\treturn True",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"params": {
"icon": "assessment",
"message": "No health data in the selected range"
},
"path": "PrimeControls/Components/EmptyState"
},
"type": "ia.display.view"
}
],
"meta": {
"name": "cardSubs"
},
"position": {
"grow": 1
},
"props": {
"direction": "column",
"style": {
"classes": "PrimeControls/Card",
"gap": "6px"
}
},
"type": "ia.container.flex"
},
{
"children": [
{
"meta": {
"name": "lblCaption"
},
"position": {
"grow": 1,
"shrink": 1
},
"props": {
"style": {
"classes": "PrimeControls/Text/Muted"
},
"text": "Weighted ISA-18.2 composite: rate, flood, chattering, standing, priority mix"
},
"type": "ia.display.label"
},
{
"events": {
"component": {
"onActionPerformed": {
"config": {
"script": "\tsystem.perspective.closePopup('PC_HealthScore')"
},
"scope": "G",
"type": "script"
}
}
},
"meta": {
"name": "btnClose"
},
"position": {
"basis": "90px",
"shrink": 0
},
"props": {
"primary": true,
"text": "Close"
},
"type": "ia.input.button"
}
],
"meta": {
"name": "footerRow"
},
"position": {
"shrink": 0
},
"props": {
"alignItems": "center",
"justify": "space-between",
"style": {
"gap": "12px"
}
},
"type": "ia.container.flex"
}
],
"custom": {},
"meta": {
"name": "root"
},
"props": {
"direction": "column",
"style": {
"classes": "PrimeControls/PageBg",
"gap": "12px",
"padding": "16px"
}
},
"type": "ia.container.flex"
}
}

View File

@@ -0,0 +1,10 @@
{
"scope": "G",
"version": 1,
"restricted": false,
"overridable": true,
"files": [
"view.json"
],
"attributes": {}
}

View File

@@ -0,0 +1,204 @@
{
"custom": {
"report": null
},
"params": {},
"propConfig": {
"custom.report": {
"binding": {
"config": {
"expression": "{session.custom.PrimeControls.query.refreshToken}"
},
"transforms": [
{
"code": "\ttry:\n\t\tr = PrimeControls.alarms.getShiftReportText()\n\t\tif r and r['text']:\n\t\t\treturn r\n\texcept:\n\t\tpass\n\treturn {'text': 'Report unavailable', 'start_ms': 0, 'end_ms': 0, 'label': ''}",
"type": "script"
}
],
"type": "expr"
}
}
},
"props": {
"defaultSize": {
"height": 560,
"width": 620
}
},
"root": {
"children": [
{
"children": [
{
"meta": {
"name": "lblTitle"
},
"propConfig": {
"props.text": {
"binding": {
"config": {
"path": "view.custom.report"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn value['label'] or 'Shift Report'\n\texcept:\n\t\treturn 'Shift Report'",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"style": {
"classes": "PrimeControls/Text/Big"
}
},
"type": "ia.display.label"
},
{
"meta": {
"name": "lblWindow"
},
"propConfig": {
"props.text": {
"binding": {
"config": {
"path": "view.custom.report"
},
"transforms": [
{
"code": "\ttry:\n\t\ts = value['start_ms']\n\t\te = value['end_ms']\n\texcept:\n\t\treturn ''\n\tif not s or not e:\n\t\treturn ''\n\treturn '%s - %s' % (PrimeControls.fmt.day_clock(s), PrimeControls.fmt.day_clock(e))",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"style": {
"classes": "PrimeControls/Text/Muted"
}
},
"type": "ia.display.label"
}
],
"meta": {
"name": "headerCol"
},
"position": {
"shrink": 0
},
"props": {
"direction": "column"
},
"type": "ia.container.flex"
},
{
"children": [
{
"meta": {
"name": "lblText"
},
"position": {
"shrink": 0
},
"propConfig": {
"props.text": {
"binding": {
"config": {
"path": "view.custom.report"
},
"transforms": [
{
"code": "\ttry:\n\t\treturn value['text'] or 'Report unavailable'\n\texcept:\n\t\treturn 'Report unavailable'",
"type": "script"
}
],
"type": "property"
}
}
},
"props": {
"style": {
"fontFamily": "monospace",
"fontSize": "12px",
"lineHeight": "1.5",
"whiteSpace": "pre-wrap"
}
},
"type": "ia.display.label"
}
],
"meta": {
"name": "cardText"
},
"position": {
"grow": 1
},
"props": {
"direction": "column",
"style": {
"classes": "PrimeControls/Card",
"overflowY": "auto"
}
},
"type": "ia.container.flex"
},
{
"children": [
{
"events": {
"component": {
"onActionPerformed": {
"config": {
"script": "\tsystem.perspective.closePopup('PC_ShiftReport')"
},
"scope": "G",
"type": "script"
}
}
},
"meta": {
"name": "btnClose"
},
"position": {
"basis": "90px",
"shrink": 0
},
"props": {
"primary": true,
"text": "Close"
},
"type": "ia.input.button"
}
],
"meta": {
"name": "footerRow"
},
"position": {
"shrink": 0
},
"props": {
"alignItems": "center",
"justify": "flex-end"
},
"type": "ia.container.flex"
}
],
"custom": {},
"meta": {
"name": "root"
},
"props": {
"direction": "column",
"style": {
"classes": "PrimeControls/PageBg",
"gap": "12px",
"padding": "16px"
}
},
"type": "ia.container.flex"
}
}