Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
32989cb
NMS-19851: Configurable system-wide dashboard (milestones 1-2 + persi…
joseanesONMS Jun 2, 2026
ae53494
NMS-19851: Add first real dashboard panels (notifications, status ove…
joseanesONMS Jun 2, 2026
1b295a3
NMS-19851: Status Overview outages donut + News Feed panel
joseanesONMS Jun 2, 2026
9248f35
NMS-19851: Add Pending Situations and Service Outages panels
joseanesONMS Jun 2, 2026
3110b4b
NMS-19851: Add Regional Status map panel
joseanesONMS Jun 3, 2026
5099b1d
NMS-19851: Wider middle column in default dashboard layout
joseanesONMS Jun 3, 2026
f1ec789
NMS-19851: Add Availability (24h) panel
joseanesONMS Jun 3, 2026
ce031da
NMS-19851: Add Business Services and Applications (with pending alarm…
joseanesONMS Jun 3, 2026
aaf9c93
NMS-19851: Search widgets, original-layout order, toolbar tooltips + …
joseanesONMS Jun 3, 2026
4263de1
NMS-19851: Dashboard panel polish (map, donuts, panel height, double …
joseanesONMS Jun 3, 2026
fd21bbe
NMS-19851: Fix fullscreen, footer overlap, overlay font, donut resize
joseanesONMS Jun 3, 2026
0a16e9e
NMS-19851: Per-panel options dialog, fixed/auto height, Notes + HTML …
joseanesONMS Jun 3, 2026
d618d94
NMS-19851: Fix panel overlap from auto-height (manual vertical compac…
joseanesONMS Jun 3, 2026
7402835
NMS-19851: Fix fixed-panel height chain, internal scroll vs footer, H…
joseanesONMS Jun 3, 2026
ec43475
NMS-19851: Auto-height panels shrink to content (min-h=1, no reserved…
joseanesONMS Jun 3, 2026
d9dc29d
NMS-19851: Add Top N panel (rank entities by a chosen KPI over the ti…
joseanesONMS Jun 3, 2026
78a0520
NMS-19851: TopN measurements use a normal-resolution step (fix NaN)
joseanesONMS Jun 3, 2026
baf08a7
NMS-19851: Fix auto-height measurement + dark-mode panel theming
joseanesONMS Jun 3, 2026
0d69eb4
NMS-19851: Tighten panel padding + compact columns on mount (less whi…
joseanesONMS Jun 3, 2026
3765408
NMS-19851: Real compaction (fresh refs), severity-shading option, leg…
joseanesONMS Jun 3, 2026
83140aa
NMS-19851: Auto-height actually applies (always reassign) + service-t…
joseanesONMS Jun 3, 2026
f16b44f
NMS-19851: Pixel-perfect panel heights, panel/category deep-links, de…
joseanesONMS Jun 3, 2026
e07f093
NMS-19851: BSM title deep-link + readable donut legend in dark mode
joseanesONMS Jun 3, 2026
d4ac2d5
NMS-19851: Metric Chart panel — line chart of one entity/metric over …
joseanesONMS Jun 11, 2026
fe94443
NMS-19851: Hide Sample Panel from the Add Panel picker
joseanesONMS Jun 11, 2026
7b1c65b
NMS-19851: 'Reset to default' in edit mode — restore the factory layout
joseanesONMS Jun 11, 2026
955b686
NMS-19851: Status Overview donut slices deep-link in context (legacy …
joseanesONMS Jun 11, 2026
8919f53
Merge remote-tracking branch 'origin/develop' into HEAD
joseanesONMS Jul 31, 2026
b748218
NMS-19851: adapt the dashboard to current develop
joseanesONMS Jul 31, 2026
5108243
NMS-19851: dashboard test suite and admin-only layout writes
joseanesONMS Jul 31, 2026
3139a2d
NMS-19851: fix adversarial review findings on the dashboard
joseanesONMS Jul 31, 2026
273054c
NMS-19851: move timeframeRange into the framework timeframe helper
joseanesONMS Jul 31, 2026
3be6fc5
NMS-19851: configurable dashboard framework (base)
joseanesONMS Jul 31, 2026
761dc9d
NMS-20127: Top-N and Metric Chart panels
joseanesONMS Jul 31, 2026
321cbbb
NMS-19851: resolve dashboard filter categories via the node search th…
joseanesONMS Jul 31, 2026
a5de37d
NMS-19851: drop accidentally committed ui/node_modules symlink
joseanesONMS Jul 31, 2026
a9c8277
NMS-19851: drop accidentally committed ui/node_modules symlink
joseanesONMS Jul 31, 2026
4a74b63
NMS-19851: onms-ui seam migration, auto-fit layout option, auto-heigh…
joseanesONMS Aug 3, 2026
626d9ea
Merge remote-tracking branch 'origin/develop' into jira/NMS-19851-das…
joseanesONMS Aug 4, 2026
4802bff
NMS-19851: satisfy arrow-parens lint on listPanelDefinitions
joseanesONMS Aug 4, 2026
44e0077
Merge remote-tracking branch 'origin/jira/NMS-19851-dashboard-base' i…
joseanesONMS Aug 4, 2026
c402f71
Merge develop into NMS-19851 (compat check)
joseanesONMS Aug 26, 2026
e3a0783
Merge develop-refreshed NMS-19851 base into NMS-20127
joseanesONMS Aug 26, 2026
0046272
NMS-19851: satisfy stylistic lint in the dashboard tests !smoke
joseanesONMS Aug 29, 2026
fb86adb
Merge remote-tracking branch 'origin/jira/NMS-19851-dashboard-base' i…
joseanesONMS Aug 29, 2026
0403855
NMS-20127: satisfy stylistic lint in the topn service test !smoke
joseanesONMS Aug 29, 2026
4c42767
Merge origin/develop into NMS-20127 after the base PR squash-merged
joseanesONMS Aug 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 113 additions & 2 deletions ui/src/components/Dashboard/PanelOptionsDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,71 @@ License.
</small>
</div>


<template v-if="panel.type === 'metric-chart'">
<div class="opts__field">
<label class="opts__label">Entity</label>
<OnmsSelect
v-model="chartEntity"
:options="entityOptions"
:loading="entitiesLoading"
filter
class="opts__control"
/>
<small class="opts__hint">Entities that have data for the selected metric.</small>
</div>
<div class="opts__field">
<label class="opts__label">Metric</label>
<OnmsSelect
v-model="chartMetric"
:options="kpiOptions"
option-label="label"
option-value="value"
class="opts__control"
/>
</div>
</template>

<template v-if="panel.type === 'topn'">
<div class="opts__field">
<label class="opts__label">Rank by (KPI)</label>
<OnmsSelect
v-model="topnKpi"
:options="kpiOptions"
option-label="label"
option-value="value"
class="opts__control"
/>
</div>
<fieldset class="opts__group">
<legend class="opts__legend">Order</legend>
<label class="opts__radio">
<input
v-model="topnDirection"
type="radio"
value="desc"
>
Descending (highest first)
</label>
<label class="opts__radio">
<input
v-model="topnDirection"
type="radio"
value="asc"
>
Ascending (lowest first)
</label>
</fieldset>
<div class="opts__field">
<label class="opts__label">How many (N)</label>
<input
v-model.number="topnN"
type="number"
min="1"
max="50"
class="opts__control opts__number"
>
</div>
</template>
</div>

<template #footer>
Expand All @@ -116,16 +180,20 @@ License.

<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { OnmsDialog, OnmsTextarea, OnmsInputText, OnmsButton } from '@opennms/onms-ui'
import { OnmsDialog, OnmsTextarea, OnmsInputText, OnmsButton, OnmsSelect } from '@opennms/onms-ui'
import type { DashboardPanel, PanelHeightMode } from '@/types/dashboard'
import { getPanelDefinition } from './registry'
import { useDashboardStore } from '@/stores/dashboardStore'
import { DEFAULT_TOPN_KPI, DEFAULT_TOPN_N, TOPN_KPIS } from '@/services/topnService'
import { DEFAULT_CHART_ENTITY, DEFAULT_CHART_METRIC, listMetricEntities } from '@/services/metricChartService'

const props = defineProps<{ panel: DashboardPanel; visible: boolean }>()
const emit = defineEmits<{ (e: 'update:visible', value: boolean): void }>()

const store = useDashboardStore()

const kpiOptions = TOPN_KPIS.map(k => ({ label: k.label, value: k.id }))

const visibleModel = computed({
get: () => props.visible,
set: v => emit('update:visible', v)
Expand All @@ -144,6 +212,32 @@ const htmlUrl = ref('')
const SHADEABLE = ['pending-situations', 'nodes-with-alarms', 'availability']
const supportsShade = computed(() => SHADEABLE.includes(props.panel.type))

const topnKpi = ref(DEFAULT_TOPN_KPI)
const topnN = ref(DEFAULT_TOPN_N)
const topnDirection = ref<'asc' | 'desc'>('desc')

// metric-chart: one entity x one metric (single-select each)
const chartEntity = ref(DEFAULT_CHART_ENTITY)
const chartMetric = ref(DEFAULT_CHART_METRIC)
const entityOptions = ref<string[]>([])
const entitiesLoading = ref(false)

const loadEntities = async () => {
entitiesLoading.value = true
const entities = await listMetricEntities(chartMetric.value)
// keep the current selection listed even if it has no data right now
if (chartEntity.value && !entities.includes(chartEntity.value)) {
entities.unshift(chartEntity.value)
}
entityOptions.value = entities
entitiesLoading.value = false
}

watch(chartMetric, () => {
if (props.panel.type === 'metric-chart' && props.visible) {
loadEntities()
}
})

// External URLs are blocked by the dashboard CSP (frame-src 'self'); validate
// up front so the user gets an explanation instead of a silent broken iframe.
Expand All @@ -169,6 +263,14 @@ const syncFromPanel = () => {
shade.value = !!props.panel.options?.shade
notesText.value = String(props.panel.options?.text ?? '')
htmlUrl.value = String(props.panel.options?.url ?? '')
topnKpi.value = String(props.panel.options?.kpi ?? DEFAULT_TOPN_KPI)
topnN.value = Number(props.panel.options?.n ?? DEFAULT_TOPN_N)
topnDirection.value = props.panel.options?.direction === 'asc' ? 'asc' : 'desc'
chartEntity.value = String(props.panel.options?.entity ?? DEFAULT_CHART_ENTITY)
chartMetric.value = String(props.panel.options?.metric ?? DEFAULT_CHART_METRIC)
if (props.panel.type === 'metric-chart') {
loadEntities()
}
}

watch(
Expand All @@ -195,6 +297,15 @@ const apply = () => {
if (props.panel.type === 'html-content') {
opts.url = htmlUrl.value.trim()
}
if (props.panel.type === 'topn') {
opts.kpi = topnKpi.value
opts.n = Math.min(50, Math.max(1, Number(topnN.value) || DEFAULT_TOPN_N))
opts.direction = topnDirection.value
}
if (props.panel.type === 'metric-chart') {
opts.entity = chartEntity.value
opts.metric = chartMetric.value
}
store.setPanelOptions(props.panel.id, opts)
visibleModel.value = false
}
Expand Down
Loading
Loading