aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md11
-rw-r--r--docs/development.md45
-rw-r--r--src/components/Service.vue5
-rw-r--r--src/components/services/AdGuardHome.vue60
-rw-r--r--src/components/services/Generic.vue33
-rw-r--r--src/components/services/Ping.vue49
6 files changed, 104 insertions, 99 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f29b7d8..de893b6 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -12,10 +12,6 @@ UX and usability. If you are looking for a full featured dashboard, there is ton
12- Configuration is stored in a simple config file, avoiding the need for a backend/database while making possible to use versioning or [config template](https://docs.ansible.com/ansible/latest/user_guide/playbooks_templating.html). 12- Configuration is stored in a simple config file, avoiding the need for a backend/database while making possible to use versioning or [config template](https://docs.ansible.com/ansible/latest/user_guide/playbooks_templating.html).
13- Only modern browsers are supported, feel free to use any JS features without any polyfill as soon as the latest version of the major browsers supports them. 13- Only modern browsers are supported, feel free to use any JS features without any polyfill as soon as the latest version of the major browsers supports them.
14 14
15### Roadmap
16
17If you want to know more about the project direction or looking for something to work on, checkout the [roadmap](https://github.com/bastienwirtz/homer#Roadmap)!
18Feel free to open an issue if you have any question.
19 15
20# Ground Rules 16# Ground Rules
21 17
@@ -40,8 +36,9 @@ feel free to open an issue to present your idea.
40### How to submit a contribution 36### How to submit a contribution
41 37
42The general process to submit a contribution is as follow: 38The general process to submit a contribution is as follow:
431. Create your own fork of the code 391. Take a look to the [development guideline](https://github.com/bastienwirtz/homer/blob/main/docs/development.md).
442. Do the changes in your fork 402. Create your own fork of the code
453. Make sure to fill the [pull request description](https://github.com/bastienwirtz/homer/blob/main/.github/PULL_REQUEST_TEMPLATE.md) properly. 413. Do the changes in your fork
424. Make sure to fill the [pull request description](https://github.com/bastienwirtz/homer/blob/main/.github/PULL_REQUEST_TEMPLATE.md) properly.
46 43
47### Happy coding :metal: 44### Happy coding :metal:
diff --git a/docs/development.md b/docs/development.md
index 5e432f1..a22ae0b 100644
--- a/docs/development.md
+++ b/docs/development.md
@@ -1,5 +1,7 @@
1# Development 1# Development
2 2
3If you want to contribute to Homer, please read the [contributing guidelines](https://github.com/bastienwirtz/homer/blob/main/CONTRIBUTING.md) first.
4
3```sh 5```sh
4# Using yarn (recommended) 6# Using yarn (recommended)
5yarn install 7yarn install
@@ -10,6 +12,49 @@ npm install
10npm run serve 12npm run serve
11``` 13```
12 14
15## Custom services
16
17Custom services are small VueJs component (see `src/components/services/`) that add little features to a classic, "static", dashboard item. It should be very simple.
18A dashboard can contain a lot of items, so performance is very important.
19
20The [`Generic`](https://github.com/bastienwirtz/homer/blob/main/src/components/services/Generic.vue) service provides a typical card layout which
21you can extend to add specific features. Unless you want a completely different design, extended the generic service is the recommended way. It gives you 3 [slots](https://vuejs.org/v2/guide/components-slots.html#Named-Slots) to extend: `icon`, `content` and `indicator`.
22Each one is **optional**, and will display the usual information if omitted.
23
24Each service must implement the `item` [property](https://vuejs.org/v2/guide/components-props.html) and bind it the Generic component if used.
25
26### Skeleton
27```Vue
28<template>
29 <Generic :item="item">
30 <template #icon>
31 <!-- left area containing the icon -->
32 </template>
33 <template #content>
34 <!-- main area containing the title, subtitle, ... -->
35 </template>
36 <template #indicator>
37 <!-- top right area, empty by default -->
38 </template>
39 </Generic>
40</template>
41
42<script>
43import Generic from "./Generic.vue";
44
45export default {
46 name: "MyNewService",
47 props: {
48 item: Object,
49 },
50 components: {
51 Generic,
52 }
53};
54</script>
55```
56
57
13## Themes 58## Themes
14 59
15Themes are meant to be simple customization (written in [scss](https://sass-lang.com/documentation/syntax)). 60Themes are meant to be simple customization (written in [scss](https://sass-lang.com/documentation/syntax)).
diff --git a/src/components/Service.vue b/src/components/Service.vue
index 8686759..39a9ac4 100644
--- a/src/components/Service.vue
+++ b/src/components/Service.vue
@@ -7,16 +7,13 @@ import Generic from "./services/Generic.vue";
7 7
8export default { 8export default {
9 name: "Service", 9 name: "Service",
10 components: {
11 Generic,
12 },
13 props: { 10 props: {
14 item: Object, 11 item: Object,
15 }, 12 },
16 computed: { 13 computed: {
17 component() { 14 component() {
18 const type = this.item.type || "Generic"; 15 const type = this.item.type || "Generic";
19 if (type == "Generic") { 16 if (type === "Generic") {
20 return Generic; 17 return Generic;
21 } 18 }
22 return () => import(`./services/${type}.vue`); 19 return () => import(`./services/${type}.vue`);
diff --git a/src/components/services/AdGuardHome.vue b/src/components/services/AdGuardHome.vue
index 61d4bed..16881fa 100644
--- a/src/components/services/AdGuardHome.vue
+++ b/src/components/services/AdGuardHome.vue
@@ -1,49 +1,35 @@
1<template> 1<template>
2 <div> 2 <Generic :item="item">
3 <div class="card" :class="item.class"> 3 <template #content>
4 <a :href="item.url" :target="item.target" rel="noreferrer"> 4 <p class="title is-4">{{ item.name }}</p>
5 <div class="card-content"> 5 <p class="subtitle is-6">
6 <div class="media"> 6 <template v-if="item.subtitle">
7 <div v-if="item.logo" class="media-left"> 7 {{ item.subtitle }}
8 <figure class="image is-48x48"> 8 </template>
9 <img :src="item.logo" :alt="`${item.name} logo`" /> 9 <template v-else-if="stats">
10 </figure> 10 {{ percentage }}&percnt; blocked
11 </div> 11 </template>
12 <div v-if="item.icon" class="media-left"> 12 </p>
13 <figure class="image is-48x48"> 13 </template>
14 <i style="font-size: 35px" :class="['fa-fw', item.icon]"></i> 14 <template #indicator>
15 </figure> 15 <div class="status" :class="protection">
16 </div> 16 {{ protection }}
17 <div class="media-content"> 17 </div>
18 <p class="title is-4">{{ item.name }}</p> 18 </template>
19 <p class="subtitle is-6"> 19 </Generic>
20 <template v-if="item.subtitle">
21 {{ item.subtitle }}
22 </template>
23 <template v-else-if="stats">
24 {{ percentage }}&percnt; blocked
25 </template>
26 </p>
27 </div>
28 <div class="status" :class="protection">
29 {{ protection }}
30 </div>
31 </div>
32 <div class="tag" :class="item.tagstyle" v-if="item.tag">
33 <strong class="tag-text">#{{ item.tag }}</strong>
34 </div>
35 </div>
36 </a>
37 </div>
38 </div>
39</template> 20</template>
40 21
41<script> 22<script>
23import Generic from "./Generic.vue";
24
42export default { 25export default {
43 name: "AdGuardHome", 26 name: "AdGuardHome",
44 props: { 27 props: {
45 item: Object, 28 item: Object,
46 }, 29 },
30 components: {
31 Generic,
32 },
47 data: () => { 33 data: () => {
48 return { 34 return {
49 status: null, 35 status: null,
diff --git a/src/components/services/Generic.vue b/src/components/services/Generic.vue
index 08bd3f6..af65a8c 100644
--- a/src/components/services/Generic.vue
+++ b/src/components/services/Generic.vue
@@ -8,22 +8,27 @@
8 <a :href="item.url" :target="item.target" rel="noreferrer"> 8 <a :href="item.url" :target="item.target" rel="noreferrer">
9 <div class="card-content"> 9 <div class="card-content">
10 <div :class="mediaClass"> 10 <div :class="mediaClass">
11 <div v-if="item.logo" class="media-left"> 11 <slot name="icon">
12 <figure class="image is-48x48"> 12 <div v-if="item.logo" class="media-left">
13 <img :src="item.logo" :alt="`${item.name} logo`" /> 13 <figure class="image is-48x48">
14 </figure> 14 <img :src="item.logo" :alt="`${item.name} logo`" />
15 </div> 15 </figure>
16 <div v-if="item.icon" class="media-left"> 16 </div>
17 <figure class="image is-48x48"> 17 <div v-if="item.icon" class="media-left">
18 <i style="font-size: 35px" :class="['fa-fw', item.icon]"></i> 18 <figure class="image is-48x48">
19 </figure> 19 <i style="font-size: 35px" :class="['fa-fw', item.icon]"></i>
20 </div> 20 </figure>
21 </div>
22 </slot>
21 <div class="media-content"> 23 <div class="media-content">
22 <p class="title is-4">{{ item.name }}</p> 24 <slot name="content">
23 <p class="subtitle is-6" v-if="item.subtitle"> 25 <p class="title is-4">{{ item.name }}</p>
24 {{ item.subtitle }} 26 <p class="subtitle is-6" v-if="item.subtitle">
25 </p> 27 {{ item.subtitle }}
28 </p>
29 </slot>
26 </div> 30 </div>
31 <slot name="indicator" class="indicator"></slot>
27 </div> 32 </div>
28 <div class="tag" :class="item.tagstyle" v-if="item.tag"> 33 <div class="tag" :class="item.tagstyle" v-if="item.tag">
29 <strong class="tag-text">#{{ item.tag }}</strong> 34 <strong class="tag-text">#{{ item.tag }}</strong>
diff --git a/src/components/services/Ping.vue b/src/components/services/Ping.vue
index e693af4..6fd3ec5 100644
--- a/src/components/services/Ping.vue
+++ b/src/components/services/Ping.vue
@@ -1,46 +1,24 @@
1<template> 1<template>
2 <div> 2 <Generic :item="item">
3 <div class="card" :class="item.class"> 3 <template #indicator>
4 <a :href="item.url" :target="item.target" rel="noreferrer"> 4 <div v-if="status" class="status" :class="status">
5 <div class="card-content"> 5 {{ status }}
6 <div class="media"> 6 </div>
7 <div v-if="item.logo" class="media-left"> 7 </template>
8 <figure class="image is-48x48"> 8 </Generic>
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">
20 <template v-if="item.subtitle">
21 {{ item.subtitle }}
22 </template>
23 </p>
24 </div>
25 <div v-if="status" class="status" :class="status">
26 {{ status }}
27 </div>
28 </div>
29 <div class="tag" :class="item.tagstyle" v-if="item.tag">
30 <strong class="tag-text">#{{ item.tag }}</strong>
31 </div>
32 </div>
33 </a>
34 </div>
35 </div>
36</template> 9</template>
37 10
38<script> 11<script>
12import Generic from "./Generic.vue";
13
39export default { 14export default {
40 name: "Ping", 15 name: "Ping",
41 props: { 16 props: {
42 item: Object, 17 item: Object,
43 }, 18 },
19 components: {
20 Generic,
21 },
44 data: () => ({ 22 data: () => ({
45 status: null, 23 status: null,
46 }), 24 }),
@@ -70,9 +48,6 @@ export default {
70</script> 48</script>
71 49
72<style scoped lang="scss"> 50<style scoped lang="scss">
73.media-left img {
74 max-height: 100%;
75}
76.status { 51.status {
77 font-size: 0.8rem; 52 font-size: 0.8rem;
78 color: var(--text-title); 53 color: var(--text-title);