diff --git a/nodes/config/ui_page.js b/nodes/config/ui_page.js
index e8dc63d99..3dca563f0 100644
--- a/nodes/config/ui_page.js
+++ b/nodes/config/ui_page.js
@@ -21,12 +21,18 @@ module.exports = function (RED) {
node.register = function (group, widgetNode, widgetConfig, widgetEvents) {
const ui = RED.nodes.getNode(config.ui)
const page = config
- ui.register(page, group, widgetNode, widgetConfig, widgetEvents)
+ if (ui) {
+ ui.register(page, group, widgetNode, widgetConfig, widgetEvents)
+ } else {
+ node.error(`Error registering Widget - ${widgetNode.name || widgetNode.id}. No parent ui-base node found for ui-page node: ${(page.name || page.id)}`)
+ }
}
node.deregister = function (group, widgetNode) {
const ui = RED.nodes.getNode(config.ui)
const page = config
- ui.deregister(page, group, widgetNode)
+ if (ui) {
+ ui.deregister(page, group, widgetNode)
+ }
}
}
RED.nodes.registerType('ui-page', UIPageNode)
diff --git a/ui/public/favicon.ico b/ui/public/favicon.ico
index df36fcfb7..14bf2fb78 100644
Binary files a/ui/public/favicon.ico and b/ui/public/favicon.ico differ
diff --git a/ui/src/App.vue b/ui/src/App.vue
index ae3597a61..2fb278040 100644
--- a/ui/src/App.vue
+++ b/ui/src/App.vue
@@ -1,6 +1,14 @@
-
+
+
+
+
+
Node-RED Dashboard 2.0
+
+
+
+
@@ -17,7 +25,84 @@ export default {
name: 'App',
inject: ['$socket'],
computed: {
- ...mapState('ui', ['dashboards', 'pages', 'widgets'])
+ ...mapState('ui', ['dashboards', 'pages', 'widgets']),
+ status: function () {
+ if (this.dashboards) {
+ const dashboards = Object.keys(this.dashboards)
+ if (!dashboards || dashboards.length === 0) {
+ return {
+ type: 'info',
+ msg: 'Please add some Dashboard 2.0 nodes to your flow and re-deploy.'
+ }
+ } else if (dashboards.length > 1) {
+ return {
+ type: 'warning',
+ msg: 'We currently do not support multiple ui-base
nodes in a single flow.
Please remove all but one (in the "config" menu on the right-side of Node-RED) and re-deploy.
'
+ }
+ } else {
+ const pages = Object.values(this.pages)
+ console.log(dashboards)
+ console.log(pages)
+ let msg = null
+ for (let i = 0; i < pages.length; i++) {
+ const page = pages[i]
+ console.log(page)
+ if (!dashboards.includes(page.ui)) {
+ // Catch instances of multiple Dashboards, or pages not bound to a Dashboard we know about
+ msg = {
+ type: 'warning',
+ msg: 'You have at least one ui-page
that is mapped to a ui-base
that we can\'t find. We currently do not support multiple ui-base
nodes in a single flow, so can only import one at a time.Please remove all but one (in the "config" menu on the right-side of Node-RED) and re-deploy.
'
+ }
+ break
+ }
+ }
+ if (msg) {
+ return msg
+ }
+ }
+ // check pages overlapping with URL
+ if (this.pages) {
+ const endpoints = {}
+ const duplicates = {}
+ Object.values(this.pages).forEach(page => {
+ const route = this.dashboards[page.ui].path + page.path
+ if (endpoints[route]) {
+ if (!duplicates[route]) {
+ // our first instance of a duplicate
+ duplicates[route] = {
+ pages: [endpoints[route]],
+ route
+ }
+ }
+ duplicates[route].pages.push(page)
+ } else {
+ endpoints[route] = page
+ }
+ })
+ if (Object.keys(duplicates).length > 0) {
+ let msg = 'Warning: You have multiple pages configured with the same URL.'
+ let list = ''
+ Object.values(duplicates).forEach((d) => {
+ d.pages.forEach((p) => {
+ list += `
${p.name} (path: ${p.path})
`
+ })
+ })
+ list += '
'
+ msg += list
+ return {
+ type: 'warning',
+ msg
+ }
+ }
+ }
+ return null
+ } else {
+ return {
+ type: 'info',
+ msg: 'Please add and configure your first Dashboard 2.0 node to get started.'
+ }
+ }
+ }
},
created () {
this.$socket.on('ui-config', (topic, payload) => {
@@ -25,23 +110,26 @@ export default {
// loop over pages, add them to vue router
Object.values(payload.pages).forEach(page => {
- const route = payload.dashboards[page.ui].path + page.path
- const routeName = 'Page:' + page.name
- console.log('adding route', route)
- this.$router?.addRoute({
- path: route,
- name: routeName,
- component: layouts[page.layout],
- meta: {
- title: page.name, // the page name
- id: page.id, // the pages id
- dashboard: page.ui // the dashboard id - to simplify determining which dashboard we're on
+ // check that the page's bound UI is also in our config
+ if (payload.dashboards[page.ui]) {
+ console.log('adding route for page', page)
+ const route = payload.dashboards[page.ui].path + page.path
+ const routeName = 'Page:' + page.name
+ this.$router?.addRoute({
+ path: route,
+ name: routeName,
+ component: layouts[page.layout],
+ meta: {
+ title: page.name, // the page name
+ id: page.id, // the pages id
+ dashboard: page.ui // the dashboard id - to simplify determining which dashboard we're on
+ }
+ })
+ // store data on the "page" object so it's easy for us to map in the navigation drawer
+ page.route = {
+ path: route,
+ name: routeName
}
- })
- // store data on the "page" object so it's easy for us to map in the navigation drawer
- page.route = {
- path: route,
- name: routeName
}
})
diff --git a/ui/src/assets/logo.png b/ui/src/assets/logo.png
index f3d2503fc..cc75e13ff 100644
Binary files a/ui/src/assets/logo.png and b/ui/src/assets/logo.png differ
diff --git a/ui/src/main.js b/ui/src/main.js
index 65a7cad41..0ba6f8a8c 100644
--- a/ui/src/main.js
+++ b/ui/src/main.js
@@ -19,7 +19,7 @@ import * as directives from 'vuetify/directives'
const theme = {
dark: false,
colors: {
- background: '#0000ff',
+ background: '#fff',
'group-background': '#ffffff',
primary: '#0000ff',
accent: '#ff6b99',
diff --git a/ui/src/store/ui.js b/ui/src/store/ui.js
index c8901d5a5..166342373 100644
--- a/ui/src/store/ui.js
+++ b/ui/src/store/ui.js
@@ -4,6 +4,7 @@
// initial state
const state = () => ({
+ dashboards: null,
pages: null,
groups: null,
themes: null,
@@ -12,6 +13,9 @@ const state = () => ({
// getters
const getters = {
+ dashboards (state) {
+ return state.dashboards
+ },
pages (state) {
return state.pages
},
@@ -74,6 +78,12 @@ const getters = {
}
const mutations = {
+ dashboards (state, dashboards) {
+ state.dashboards = {
+ ...state.dashboards,
+ ...dashboards
+ }
+ },
pages (state, pages) {
state.pages = pages
},
diff --git a/ui/src/stylesheets/common.css b/ui/src/stylesheets/common.css
index d2e20df04..56a3d4f74 100644
--- a/ui/src/stylesheets/common.css
+++ b/ui/src/stylesheets/common.css
@@ -29,6 +29,55 @@ main {
padding: var(--nrdb-main-padding);
}
+/**
+* Placeholder
+*/
+
+.nrdb-placeholder-container {
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: #eee;
+}
+
+.nrdb-placeholder {
+ max-width: 480px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ color: #222;
+ text-align: center;
+ padding: 1.5rem 2rem;
+ border-radius: 6px;
+ border: 1px solid #d1d1d1;
+ background-color: #fff;
+}
+
+.nrdb-placeholder img {
+ width: 150px;
+}
+
+.nrdb-placeholder p {
+ margin: 0.5rem 0 0;
+}
+
+.nrdb-placeholder .status-info {
+ font-weight: 600;
+}
+
+.nrdb-placeholder .status-warning {
+ color: #bb2020;
+ font-weight: 600;
+}
+
+.nrdb-placeholder .status-duplicates {
+ font-weight: 400;
+ color: #222;
+ margin-top: 0.5rem;
+}
+
/**
* Common Widget Styling
*/