Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion msc-wis2node-ui/.prettierrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"singleQuote": true,
"printWidth": 100
"printWidth": 100,
"bracketSpacing": true,
"arrowParens": "always"
}
941 changes: 780 additions & 161 deletions msc-wis2node-ui/package-lock.json

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion msc-wis2node-ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "msc-wis2node-ui",
"version": "0.0.0",
"version": "0.1.0",
"private": true,
"type": "module",
"engines": {
Expand All @@ -16,15 +16,24 @@
"format": "prettier --write --experimental-cli src/"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^7.2.0",
"@fortawesome/free-brands-svg-icons": "^7.2.0",
"@fortawesome/free-regular-svg-icons": "^7.2.0",
"@fortawesome/free-solid-svg-icons": "^7.2.0",
"@fortawesome/vue-fontawesome": "^3.1.3",
"echarts": "^6.0.0",
"luxon": "^3.7.2",
"mqtt": "^5.14.1",
"pinia": "^3.0.4",
"pretty-print-json": "^3.0.7",
"vue": "^3.5.25",
"vue-echarts": "^8.0.1",
"vue-router": "^4.6.3"
},
"devDependencies": {
"@eslint/js": "^9.39.1",
"@playwright/test": "^1.57.0",
"@types/luxon": "^3.7.1",
"@vicons/ionicons5": "^0.13.0",
"@vitejs/plugin-vue": "^6.0.2",
"@vitest/eslint-plugin": "^1.5.0",
Expand Down
9 changes: 8 additions & 1 deletion msc-wis2node-ui/sample.env
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
VITE_DATA_DISTRIBUTION_METRICS_URL=https://example.com/msc-wis2node/data-distribution-metrics/
VITE_DATA_DISTRIBUTION_METRICS_URL=https://example.com/msc-wis2node/data-distribution-metrics/

VITE_BROKER_URL=wss://globalbroker.meteo.fr:443/mqtt
VITE_BROKER_USERNAME=everyone
VITE_BROKER_PASSWORD=everyone

VITE_TOPIC_NOTIFICATION='origin/a/wis2/ca-eccc-msc/#'
VITE_TOPIC_ERRORS='monitor/a/wis2/ca-eccc-msc/#'
135 changes: 62 additions & 73 deletions msc-wis2node-ui/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,42 +1,84 @@
<script setup>
// Naive UI
import { NConfigProvider, darkTheme } from 'naive-ui'
import { NLayout, NLayoutContent } from 'naive-ui'
import { NGrid, NGridItem, NText, NCard } from 'naive-ui'
import { NLayout } from 'naive-ui'
import { useRouter, useRoute } from 'vue-router'

import { storeToRefs } from 'pinia'

// Vue
import { ref, computed } from 'vue'
import { ref, computed, onMounted, h } from 'vue'
import { useDataDistributionMetrics } from './stores/useDataDistributionMetrics'
import { useNotifMsg } from './stores/useNotifMsg'
import { useErrorMsg } from './stores/useErrorMsg'
import { useDarkTheme } from './stores/useDarkTheme'

// Local components
import AppSidebar from './components/AppSidebar.vue'
import AppHeader from './components/AppHeader.vue'
import ActivityCharts from './components/ActivityCharts.vue'

import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { faHouse } from '@fortawesome/free-solid-svg-icons'
import { faEye } from '@fortawesome/free-solid-svg-icons'

const metricsStore = useDataDistributionMetrics()
const { availableDatasets, files, selectedFile } = storeToRefs(metricsStore)
const { fetchFileList, fetchFile } = metricsStore
const router = useRouter()

useNotifMsg()
useErrorMsg()

// Dark mode handling
const isDark = ref(true)
const darkThemeStore = useDarkTheme()
const { isDark } = storeToRefs(darkThemeStore)
const { toggleTheme } = darkThemeStore

const themeSwitcher = computed(() => (isDark.value ? darkTheme : null))
const toggleTheme = () => {
isDark.value = !isDark.value
}

// sidebar menu options for MQTT dashboard
const menuOptions = [
{ label: 'Overview', key: 'overview' },
{ label: 'Topics', key: 'topics' },
{ label: 'Clients', key: 'clients' },
{ label: 'QoS & Retained', key: 'qos-retained' },
{ label: 'Errors & Drops', key: 'errors' },
{
label: 'Overview',
key: '/',
icon: () => h(FontAwesomeIcon, { icon: faHouse }),
},
{
label: 'Monitoring',
key: '/monitoring',
icon: () => h(FontAwesomeIcon, { icon: faEye }),
},
]
const activeKey = ref('overview')
const route = useRoute()

const activeKey = ref('')

function nav() {
router.push(activeKey.value)
}

const dataDistributionMetricsUrl = import.meta.env.VITE_DATA_DISTRIBUTION_METRICS_URL
onMounted(async () => {
if (availableDatasets.value.length === 0) {
await fetchFileList()
}
if (files.value.length > 0 && !selectedFile.value) {
const latest = files.value[files.value.length - 1]
await fetchFile(latest)
}
activeKey.value = route.path
})
</script>

<template>
<n-config-provider :theme="themeSwitcher">
<n-layout has-sider class="app-layout">
<!-- SIDEBAR COMPONENT -->
<AppSidebar v-model:value="activeKey" :options="menuOptions" title="msc-wis2node-ui" />
<AppSidebar
v-model:value="activeKey"
:options="menuOptions"
title="msc-wis2node-ui"
@update:value="nav()"
/>

<!-- MAIN AREA -->
<n-layout>
Expand All @@ -48,62 +90,9 @@ const dataDistributionMetricsUrl = import.meta.env.VITE_DATA_DISTRIBUTION_METRIC
@toggle-theme="toggleTheme"
/>

<!-- CONTENT -->
<n-layout-content class="app-content">
<!-- Top metrics -->
<n-grid cols="1 600:2 1000:4" x-gap="16" y-gap="16">
<!-- Topic name -->
<n-grid-item>
<n-card>
<n-text depth="3">MQTT Topic</n-text>
<div class="stat-value">wis2/obs/temperature</div>
<n-text depth="3">Current subscription</n-text>
</n-card>
</n-grid-item>

<!-- Messages per second -->
<n-grid-item>
<n-card>
<n-text depth="3">Messages / Second</n-text>
<div class="stat-value">152</div>
<n-text depth="3">Averaged over last 60s</n-text>
</n-card>
</n-grid-item>

<!-- Last message / latency -->
<n-grid-item>
<n-card>
<n-text depth="3">Last Message</n-text>
<div class="stat-value">0.42 s</div>
<n-text depth="3">Age since last payload</n-text>
</n-card>
</n-grid-item>

<!-- Errors / drops -->
<n-grid-item>
<n-card>
<n-text depth="3">Dropped / Error Messages</n-text>
<div class="stat-value">3</div>
<n-text type="error">in the last 5 minutes</n-text>
</n-card>
</n-grid-item>
</n-grid>

<!-- Activity / charts -->
<div class="activity-card">
<n-card title="Activity" size="large">
<p class="activity-text">
This is where you can put charts, tables or detailed analytics.
</p>
<p>
dataDistributionMetricsUrl:
<code>{{ dataDistributionMetricsUrl }}</code>
</p>

<ActivityCharts />
</n-card>
</div>
</n-layout-content>
<main>
<RouterView />
</main>
</n-layout>
</n-layout>
</n-config-provider>
Expand All @@ -119,7 +108,7 @@ const dataDistributionMetricsUrl = import.meta.env.VITE_DATA_DISTRIBUTION_METRIC
/* Only keep minimal presentation styles that Naive UI
doesn't provide out of the box */
.app-content {
padding: 16px;
padding: 8px;
}

/* Stat cards */
Expand Down
Loading
Loading