aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBastien Wirtz <bastien.wirtz@gmail.com>2020-10-23 18:29:14 -0700
committerGitHub <noreply@github.com>2020-10-23 18:29:14 -0700
commit13071ae3d1457bd677665769e83535a3d1f94b14 (patch)
treed4137914a4bf76bd60ff4af940dc0fd33fee04f1
parent37dfd2a132d883482b330b22a7c19d86948a391c (diff)
parent5b727eee020b9d734f60fd37f840ea47f83fb23e (diff)
downloadhomer-13071ae3d1457bd677665769e83535a3d1f94b14.tar.gz
homer-13071ae3d1457bd677665769e83535a3d1f94b14.tar.zst
homer-13071ae3d1457bd677665769e83535a3d1f94b14.zip
Merge pull request #140 from Genymobile/dynamic-services
Custom service components
-rw-r--r--docs/configuration.md7
-rw-r--r--src/components/Service.vue48
-rw-r--r--src/components/services/Generic.vue56
-rw-r--r--src/components/services/PiHole.vue88
4 files changed, 163 insertions, 36 deletions
diff --git a/docs/configuration.md b/docs/configuration.md
index 4bd4677..7df5651 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -107,11 +107,12 @@ services:
107 - name: "Other group" 107 - name: "Other group"
108 icon: "fas fa-heartbeat" 108 icon: "fas fa-heartbeat"
109 items: 109 items:
110 - name: "Another app" 110 - name: "Pi-hole"
111 logo: "assets/tools/sample.png" 111 logo: "assets/tools/sample.png"
112 subtitle: "Another example" 112 subtitle: "Network-wide Ad Blocking"
113 tag: "other" 113 tag: "other"
114 url: "https://www.reddit.com/r/selfhosted/" 114 url: "http://192.168.0.151/admin"
115 type: "PiHole" # optional, loads a specific component that provides extra features. MUST MATCH a file name (without file extension) available in `src/components/services`
115 target: "_blank" # optional html a tag target attribute 116 target: "_blank" # optional html a tag target attribute
116 # class: "green" # optional custom CSS class for card, useful with custom stylesheet 117 # class: "green" # optional custom CSS class for card, useful with custom stylesheet
117``` 118```
diff --git a/src/components/Service.vue b/src/components/Service.vue
index 4d3702b..8686759 100644
--- a/src/components/Service.vue
+++ b/src/components/Service.vue
@@ -1,44 +1,26 @@
1<template> 1<template>
2 <div> 2 <component v-bind:is="component" :item="item"></component>
3 <div class="card" :class="item.class">
4 <a :href="item.url" :target="item.target" rel="noreferrer">
5 <div class="card-content">
6 <div class="media">
7 <div v-if="item.logo" class="media-left">
8 <figure class="image is-48x48">
9 <img :src="item.logo" :alt="`${item.name} logo`" />
10 </figure>
11 </div>
12 <div v-if="item.icon" class="media-left">
13 <figure class="image is-48x48">
14 <i style="font-size: 35px" :class="['fa-fw', item.icon]"></i>
15 </figure>
16 </div>
17 <div class="media-content">
18 <p class="title is-4">{{ item.name }}</p>
19 <p class="subtitle is-6">{{ item.subtitle }}</p>
20 </div>
21 </div>
22 <div class="tag" :class="item.tagstyle" v-if="item.tag">
23 <strong class="tag-text">#{{ item.tag }}</strong>
24 </div>
25 </div>
26 </a>
27 </div>
28 </div>
29</template> 3</template>
30 4
31<script> 5<script>
6import Generic from "./services/Generic.vue";
7
32export default { 8export default {
33 name: "Service", 9 name: "Service",
10 components: {
11 Generic,
12 },
34 props: { 13 props: {
35 item: Object, 14 item: Object,
36 }, 15 },
16 computed: {
17 component() {
18 const type = this.item.type || "Generic";
19 if (type == "Generic") {
20 return Generic;
21 }
22 return () => import(`./services/${type}.vue`);
23 },
24 },
37}; 25};
38</script> 26</script>
39
40<style scoped lang="scss">
41.media-left img {
42 max-height: 100%;
43}
44</style>
diff --git a/src/components/services/Generic.vue b/src/components/services/Generic.vue
new file mode 100644
index 0000000..f19ee18
--- /dev/null
+++ b/src/components/services/Generic.vue
@@ -0,0 +1,56 @@
1<script>
2export default {};
3</script>
4
5<style></style> */
6
7<script>
8export default {};
9</script>
10
11<style></style>
12
13<template>
14 <div>
15 <div class="card" :class="item.class">
16 <a :href="item.url" :target="item.target" rel="noreferrer">
17 <div class="card-content">
18 <div class="media">
19 <div v-if="item.logo" class="media-left">
20 <figure class="image is-48x48">
21 <img :src="item.logo" :alt="`${item.name} logo`" />
22 </figure>
23 </div>
24 <div v-if="item.icon" class="media-left">
25 <figure class="image is-48x48">
26 <i style="font-size: 35px;" :class="['fa-fw', item.icon]"></i>
27 </figure>
28 </div>
29 <div class="media-content">
30 <p class="title is-4">{{ item.name }}</p>
31 <p class="subtitle is-6">{{ item.subtitle }}</p>
32 </div>
33 </div>
34 <div class="tag" :class="item.tagstyle" v-if="item.tag">
35 <strong class="tag-text">#{{ item.tag }}</strong>
36 </div>
37 </div>
38 </a>
39 </div>
40 </div>
41</template>
42
43<script>
44export default {
45 name: "Generic",
46 props: {
47 item: Object,
48 },
49};
50</script>
51
52<style scoped lang="scss">
53.media-left img {
54 max-height: 100%;
55}
56</style>
diff --git a/src/components/services/PiHole.vue b/src/components/services/PiHole.vue
new file mode 100644
index 0000000..c08e1fa
--- /dev/null
+++ b/src/components/services/PiHole.vue
@@ -0,0 +1,88 @@
1<template>
2 <div>
3 <div class="card" :class="item.class">
4 <a :href="item.url" :target="item.target" rel="noreferrer">
5 <div class="card-content">
6 <div class="media">
7 <div v-if="item.logo" class="media-left">
8 <figure class="image is-48x48">
9 <img :src="item.logo" :alt="`${item.name} logo`" />
10 </figure>
11 </div>
12 <div v-if="item.icon" class="media-left">
13 <figure class="image is-48x48">
14 <i style="font-size: 35px;" :class="['fa-fw', item.icon]"></i>
15 </figure>
16 </div>
17 <div class="media-content">
18 <p class="title is-4">{{ item.name }}</p>
19 <p class="subtitle is-6">{{ item.subtitle }}</p>
20 </div>
21 <div v-if="status" class="status" :class="status.status">
22 {{ status.status }}
23 </div>
24 </div>
25 <div class="tag" :class="item.tagstyle" v-if="item.tag">
26 <strong class="tag-text">#{{ item.tag }}</strong>
27 </div>
28 </div>
29 </a>
30 </div>
31 </div>
32</template>
33
34<script>
35export default {
36 name: "PiHole",
37 props: {
38 item: Object,
39 },
40 data: () => {
41 return {
42 status: null,
43 };
44 },
45 created: function () {
46 this.fetchStatus();
47 },
48 methods: {
49 fetchStatus: async function () {
50 this.status = await fetch(`${this.item.url}/api.php`).then((response) =>
51 response.json()
52 );
53 },
54 },
55};
56</script>
57
58<style scoped lang="scss">
59.media-left img {
60 max-height: 100%;
61}
62.status {
63 font-size: 0.8rem;
64 color: var(--text-title);
65
66 &.enabled:before {
67 background-color: #94e185;
68 border-color: #78d965;
69 box-shadow: 0px 0px 4px 1px #94e185;
70 }
71
72 &.disabled:before {
73 background-color: #c9404d;
74 border-color: #c42c3b;
75 box-shadow: 0px 0px 4px 1px #c9404d;
76 }
77
78 &:before {
79 content: " ";
80 display: inline-block;
81 width: 7px;
82 height: 7px;
83 margin-right: 10px;
84 border: 1px solid #000;
85 border-radius: 7px;
86 }
87}
88</style>