Skip to content

Conversation

muzaffarmhd
Copy link
Contributor

PR Checklist

If you have any questions, you can refer to the Contributing Guide


What is the current behavior?

Currently, MQTTX does not provide a dashboard feature for visualizing MQTT message data in real time or historically. Users cannot create, configure, or manage dashboards and widgets for monitoring numeric or structured payloads. There is no support for widget-based visualization (Big Number, Gauge, Line), time range selection, or schema-aware extraction from Protobuf/Avro.

Issue Number

None

What is the new behavior?

This PR introduces a comprehensive dashboard feature, including:

  • Dashboard Management: Create, edit, delete, and reorder dashboards (DashboardsList.vue, index.vue).
  • Widget System: Add, configure, and remove widgets (Big Number, Gauge, Line) per dashboard (WidgetConfig.vue, WidgetRenderer.vue, widgetRegistry.ts).
  • Data Extraction: Extracts values from JSON, Protobuf, or Avro payloads, with fallback and schema validation (DashboardView.vue).
  • Visualization:
    • Big Number (BigNumber.vue): Displays latest value, supports thresholds, units, and field names.
    • Gauge (Gauge.vue): Visualizes value with thresholds and dynamic coloring.
    • Line Chart (LineChart.vue): Plots historical or live data with area/smooth options.
  • Time Range Selection: Supports live/static time ranges and quick shortcuts (TimeRangeSelect.vue).
  • Widget Options: Each widget type has configurable options (BigNumberConfig.vue, GaugeConfig.vue, LineConfig.vue).
  • Persistence: Dashboard and widget layouts, settings, and time ranges are persisted.
  • Schema Support: Widgets can extract fields from Protobuf/Avro using stored schemas.
  • Non-numeric Handling: Non-numeric values in value fields are replaced by fallback values for numeric widgets; only numeric data is visualized.

Screenshots:
image
image
image

Does this PR introduce a breaking change?

  • Yes
  • No

Specific Instructions

Testing on the following lines:

  • Widget data extraction and fallback logic for non-numeric values.
  • Widget configuration and preview flows.
  • Dashboard and widget CRUD operations and layout persistence.
  • Schema extraction and error handling.
  • For detailed code, see:
    • DashboardView.vue : Here most of the message processing takes place
    • WidgetRenderer.vue
    • BigNumber.vue, Gauge.vue, LineChart.vue
    • WidgetConfig.vue
    • widgetRegistry.ts

Other information

  • This PR is a foundational feature for MQTTX analytics and visualization.
  • Further enhancements (e.g., support for non-numeric widgets, aggregation, or custom visualizations) can be built on this structure.
  • No breaking changes to existing features; all dashboard code is additive and modular.

@ysfscream ysfscream added feature This pr is a feature desktop MQTTX Desktop labels Sep 30, 2025
@ysfscream ysfscream moved this to In Progress in MQTTX Sep 30, 2025
@ysfscream ysfscream added this to the v1.13.0 milestone Sep 30, 2025
hu: 'Irányítópultok elrejtése',
},
noWidgetsTitle: {
zh: '开始你的新仪表盘,添加一个可视化',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
zh: '开始你的新仪表盘,添加一个可视化',
zh: '添加可视化组件,开始使用仪表盘',

hu: 'Válasszon ki egy adatforrást, majd lekérdezze és vizualizálja adatait diagramokkal, statisztikákkal és egyéb widgetekkel',
},
addVisualization: {
zh: '添加可视化',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
zh: '添加可视化',
zh: '添加可视化组件',

Comment on lines +385 to +391
connection: {
zh: '连接',
en: 'Connection',
tr: 'Bağlantı',
ja: '接続',
hu: 'Kapcsolat',
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a duplicate

hu: 'Téma minta',
},
valueField: {
zh: '值字段',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
zh: '值字段',
zh: '字段',

name: 'Viewer',
component: () => import('@/views/viewer/index.vue'),
redirect: '/viewer/topic_tree',
redirect: '/viewer/dashboard',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer the dashboard to the second tab, and the topic tree is the first default.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll move it to the second tab, thank you.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a huge file and a lot of code; I recommend splitting it into components.

</div>
<div class="viewer-view-tabs">
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
<el-tab-pane :label="$t('viewer.dashboard')" name="Dashboard"></el-tab-pane>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move under the topic tree.

Comment on lines +57 to +63
let STORE_PATH: string
try {
STORE_PATH = getUnifiedAppDataPath('MQTTX')
} catch (e) {
const home = process.env.HOME || require('os').homedir()
STORE_PATH = process.env.MQTTX_DATA_PATH || join(home, '.mqttx')
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What issue was fixed here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TypeORM CLI commands such as yarn db:migration:show and the rest fail with:
TypeError: Cannot read properties of undefined (reading 'getPath'), because there's no electron instance while running TypeORM CLI commands, while I now think it might be better to add error handling here in appDataPath.ts

export const getUnifiedAppDataPath = (subDir?: string): string => {
  // Handle both main and renderer process
  const isRenderer = process.type === 'renderer'
  const electronApp = isRenderer ? require('@electron/remote').app : app

  // Use Electron's native API which handles Windows %APPDATA% redirection correctly
  const userDataPath = electronApp.getPath('userData')

  if (subDir) {
    // For backward compatibility, if subDir is 'MQTTX' and it's already in the path, don't duplicate it
    if (subDir === 'MQTTX' && userDataPath.includes('MQTTX')) {
      return userDataPath
    }
    return path.join(userDataPath, subDir)
  }

  return userDataPath
}

Perhaps we could use the getLegacyDataPath function as a fallback instead of using the home directory which i used for development purposes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this change is mainly to ensure we can operate the database even when the Electron app isn’t running, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

<template>
<div class="gauge-container">
<div v-if="hasData" :id="id" style="width: 100%; height: 100%"></div>
<div v-else class="no-data">No Data</div>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i18n

<!-- @ts-ignore -->
<component :is="widgetComponent" v-bind="widgetProps" @error="onError" />
<div v-if="!widgetComponent" class="widget-error">
<div class="error-message">Unknown widget type: {{ widget.type }}</div>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i18n

class="big-stat-panel"
>
<div v-if="hasData" :id="id" class="chart-container"></div>
<div v-else class="no-data-background">No Data</div>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i18n

<template>
<div class="line-chart-container">
<div :id="id" class="chart-container"></div>
<div v-if="!hasData" class="no-data-overlay">No Data</div>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i18n

Comment on lines +118 to +132
.color-picker-item {
.el-form-item__content {
display: flex !important;
align-items: center !important;
line-height: normal !important;
height: 43px !important;
justify-content: flex-start !important;
}
.el-color-picker {
vertical-align: middle !important;
margin: 0 !important;
align-self: center !important;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like there's a lot of duplication here, and all of them have strict import keywords.

@@ -0,0 +1,133 @@
<template>
<div class="key-value-editor">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this new component created because the built-in key value editor couldn't satisfy the needs?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

desktop MQTTX Desktop feature This pr is a feature

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

2 participants