aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--docs/configuration.md5
-rw-r--r--public/assets/additionnal-page.yml.dist35
-rw-r--r--public/assets/config.yml.dist5
-rw-r--r--src/App.vue57
-rw-r--r--src/components/Message.vue25
5 files changed, 99 insertions, 28 deletions
diff --git a/docs/configuration.md b/docs/configuration.md
index 3ff49fa..a472b41 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -92,6 +92,11 @@ links:
92 - name: "link 2" 92 - name: "link 2"
93 icon: "fas fa-book" 93 icon: "fas fa-book"
94 url: "https://github.com/bastienwirtz/homer" 94 url: "https://github.com/bastienwirtz/homer"
95 # this will link to a second homer page that will load config from page2.yml and keep default config values as in config.yml file
96 # see url field and assets/page.yml used in this example:
97 - name: "Second Page"
98 icon: "fas fa-file-alt"
99 url: "#page2"
95 100
96# Services 101# Services
97# First level array represents a group. 102# First level array represents a group.
diff --git a/public/assets/additionnal-page.yml.dist b/public/assets/additionnal-page.yml.dist
new file mode 100644
index 0000000..f918dc1
--- /dev/null
+++ b/public/assets/additionnal-page.yml.dist
@@ -0,0 +1,35 @@
1---
2# Additionnal page configuration
3
4# Additionnal configurations are loaded using its file name, minus the extension, as an anchor (https://<mydashboad>#<config>).
5# `config.yml` is still used as a base configuration, and all values here will overwrite it, so you don't have to re-defined everything
6
7
8subtitle: "this is another dashboard page"
9
10# This overwrites message config. Setting it to empty to remove message from this page and keep it only in the main one:
11message: ~
12
13# as we want to include a differente link here (so we can get back to home page), we need to replicate all links or they will be revome when overwriting the links field:
14links:
15 - name: "Home"
16 icon: "fas fa-home"
17 url: "#"
18 - name: "Contribute"
19 icon: "fab fa-github"
20 url: "https://github.com/bastienwirtz/homer"
21 target: "_blank" # optional html a tag target attribute
22 - name: "Wiki"
23 icon: "fas fa-book"
24 url: "https://www.wikipedia.org/"
25
26services:
27 - name: "More applications on another page!"
28 icon: "fas fa-cloud"
29 items:
30 - name: "Awesome app on a second page!"
31 logo: "assets/tools/sample.png"
32 subtitle: "Bookmark example"
33 tag: "app"
34 url: "https://www.reddit.com/r/selfhosted/"
35 target: "_blank"
diff --git a/public/assets/config.yml.dist b/public/assets/config.yml.dist
index 85478ec..65c5098 100644
--- a/public/assets/config.yml.dist
+++ b/public/assets/config.yml.dist
@@ -56,6 +56,11 @@ links:
56 - name: "Wiki" 56 - name: "Wiki"
57 icon: "fas fa-book" 57 icon: "fas fa-book"
58 url: "https://www.wikipedia.org/" 58 url: "https://www.wikipedia.org/"
59 # this will link to a second homer page that will load config from additionnal-page.yml and keep default config values as in config.yml file
60 # see url field and assets/additionnal-page.yml.dist used in this example:
61 - name: "another page!"
62 icon: "fas fa-file-alt"
63 url: "#additionnal-page"
59 64
60# Services 65# Services
61# First level array represent a group. 66# First level array represent a group.
diff --git a/src/App.vue b/src/App.vue
index 486ef03..1f4f509 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -13,7 +13,9 @@
13 <section v-if="config.header" class="first-line"> 13 <section v-if="config.header" class="first-line">
14 <div v-cloak class="container"> 14 <div v-cloak class="container">
15 <div class="logo"> 15 <div class="logo">
16 <img v-if="config.logo" :src="config.logo" alt="dashboard logo" /> 16 <a href="#">
17 <img v-if="config.logo" :src="config.logo" alt="dashboard logo" />
18 </a>
17 <i v-if="config.icon" :class="config.icon"></i> 19 <i v-if="config.icon" :class="config.icon"></i>
18 </div> 20 </div>
19 <div class="dashboard-title"> 21 <div class="dashboard-title">
@@ -159,28 +161,41 @@ export default {
159 }; 161 };
160 }, 162 },
161 created: async function () { 163 created: async function () {
162 const defaults = jsyaml.load(defaultConfig); 164 this.buildDashboard();
163 let config; 165 window.onhashchange = this.buildDashboard;
164 try {
165 config = await this.getConfig();
166 } catch (error) {
167 console.log(error);
168 config = this.handleErrors("⚠️ Error loading configuration", error);
169 }
170 this.config = merge(defaults, config);
171 this.services = this.config.services;
172 document.title =
173 this.config.documentTitle ||
174 `${this.config.title} | ${this.config.subtitle}`;
175 if (this.config.stylesheet) {
176 let stylesheet = "";
177 for (const file of this.config.stylesheet) {
178 stylesheet += `@import "${file}";`;
179 }
180 this.createStylesheet(stylesheet);
181 }
182 }, 166 },
183 methods: { 167 methods: {
168 buildDashboard: async function () {
169 const defaults = jsyaml.load(defaultConfig);
170 let config;
171 try {
172 config = await this.getConfig();
173 const path =
174 window.location.hash.substring(1) != ""
175 ? window.location.hash.substring(1)
176 : null;
177
178 if (path) {
179 let pathConfig = await this.getConfig(`assets/${path}.yml`); // the slash (/) is included in the pathname
180 config = Object.assign(config, pathConfig);
181 }
182 } catch (error) {
183 console.log(error);
184 config = this.handleErrors("⚠️ Error loading configuration", error);
185 }
186 this.config = merge(defaults, config);
187 this.services = this.config.services;
188 document.title =
189 this.config.documentTitle ||
190 `${this.config.title} | ${this.config.subtitle}`;
191 if (this.config.stylesheet) {
192 let stylesheet = "";
193 for (const file of this.config.stylesheet) {
194 stylesheet += `@import "${file}";`;
195 }
196 this.createStylesheet(stylesheet);
197 }
198 },
184 getConfig: function (path = "assets/config.yml") { 199 getConfig: function (path = "assets/config.yml") {
185 return fetch(path).then((response) => { 200 return fetch(path).then((response) => {
186 if (response.redirected) { 201 if (response.redirected) {
diff --git a/src/components/Message.vue b/src/components/Message.vue
index 2f71f3f..7c6acdd 100644
--- a/src/components/Message.vue
+++ b/src/components/Message.vue
@@ -22,7 +22,6 @@ export default {
22 }, 22 },
23 data: function () { 23 data: function () {
24 return { 24 return {
25 show: false,
26 message: {}, 25 message: {},
27 }; 26 };
28 }, 27 },
@@ -30,14 +29,23 @@ export default {
30 // Look for a new message if an endpoint is provided. 29 // Look for a new message if an endpoint is provided.
31 this.message = Object.assign({}, this.item); 30 this.message = Object.assign({}, this.item);
32 await this.getMessage(); 31 await this.getMessage();
33 this.show = this.message.title || this.message.content;
34 }, 32 },
35 33 computed: {
34 show: function () {
35 return this.message.title || this.message.content;
36 },
37 },
38 watch: {
39 item: function (item) {
40 this.message = Object.assign({}, item);
41 },
42 },
36 methods: { 43 methods: {
37 getMessage: async function() { 44 getMessage: async function () {
38 if (this.item && this.item.url) { 45 if (this.item && this.item.url) {
39 let fetchedMessage = await this.downloadMessage(this.item.url); 46 let fetchedMessage = await this.downloadMessage(this.item.url);
40 if (this.item.mapping) fetchedMessage = this.mapRemoteMessage(fetchedMessage); 47 if (this.item.mapping)
48 fetchedMessage = this.mapRemoteMessage(fetchedMessage);
41 // keep the original config value if no value is provided by the endpoint 49 // keep the original config value if no value is provided by the endpoint
42 for (const prop of ["title", "style", "content"]) { 50 for (const prop of ["title", "style", "content"]) {
43 if (prop in fetchedMessage && fetchedMessage[prop] !== null) { 51 if (prop in fetchedMessage && fetchedMessage[prop] !== null) {
@@ -45,7 +53,8 @@ export default {
45 } 53 }
46 } 54 }
47 } 55 }
48 if (this.item.refreshInterval) setTimeout(this.getMessage, this.item.refreshInterval); 56 if (this.item.refreshInterval)
57 setTimeout(this.getMessage, this.item.refreshInterval);
49 }, 58 },
50 59
51 downloadMessage: function (url) { 60 downloadMessage: function (url) {
@@ -60,7 +69,9 @@ export default {
60 mapRemoteMessage: function (message) { 69 mapRemoteMessage: function (message) {
61 let mapped = {}; 70 let mapped = {};
62 // map property from message into mapped according to mapping config (only if field has a value): 71 // map property from message into mapped according to mapping config (only if field has a value):
63 for (const prop in this.item.mapping) if (message[this.item.mapping[prop]]) mapped[prop] = message[this.item.mapping[prop]]; 72 for (const prop in this.item.mapping)
73 if (message[this.item.mapping[prop]])
74 mapped[prop] = message[this.item.mapping[prop]];
64 return mapped; 75 return mapped;
65 }, 76 },
66 }, 77 },