diff --git a/frontend/package.json b/frontend/package.json index 760c5140..0d02522a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -28,6 +28,7 @@ "querystring": "^0.2.0", "sass": "^1.26.10", "semver": "^7.3.2", + "v-tooltip": "^2.1.3", "vue": "^2.6.11", "vue-apexcharts": "^1.6.0", "vue-clipboard2": "^0.3.1", diff --git a/frontend/src/API.js b/frontend/src/API.js index 2646a0e7..c286fd4a 100644 --- a/frontend/src/API.js +++ b/frontend/src/API.js @@ -303,6 +303,10 @@ class Api { async downtime_delete (id) { return axios.delete(`/api/downtimes/${id}`).then((response) => response.data); } + + async service_status (sec) { + return axios.get(`/api/services/status${sec && `?time=${sec}`}`).then((response) => response.data); + } } const api = new Api() export default api diff --git a/frontend/src/assets/scss/layout.scss b/frontend/src/assets/scss/layout.scss index dc6da19b..f73d481f 100644 --- a/frontend/src/assets/scss/layout.scss +++ b/frontend/src/assets/scss/layout.scss @@ -232,3 +232,113 @@ A { top:0; background: darken($card-background, 10%); } + +.tooltip { + display: block !important; + z-index: 10000; + + .tooltip-inner { + background: black; + color: white; + border-radius: 5px; + padding: 5px 10px 4px; + } + + .tooltip-arrow { + width: 0; + height: 0; + border-style: solid; + position: absolute; + margin: 5px; + border-color: black; + z-index: 1; + } + + &[x-placement^="top"] { + margin-bottom: 5px; + + .tooltip-arrow { + border-width: 5px 5px 0 5px; + border-left-color: transparent !important; + border-right-color: transparent !important; + border-bottom-color: transparent !important; + bottom: -5px; + left: calc(50% - 5px); + margin-top: 0; + margin-bottom: 0; + } + } + + &[x-placement^="bottom"] { + margin-top: 5px; + + .tooltip-arrow { + border-width: 0 5px 5px 5px; + border-left-color: transparent !important; + border-right-color: transparent !important; + border-top-color: transparent !important; + top: -5px; + left: calc(50% - 5px); + margin-top: 0; + margin-bottom: 0; + } + } + + &[x-placement^="right"] { + margin-left: 5px; + + .tooltip-arrow { + border-width: 5px 5px 5px 0; + border-left-color: transparent !important; + border-top-color: transparent !important; + border-bottom-color: transparent !important; + left: -5px; + top: calc(50% - 5px); + margin-left: 0; + margin-right: 0; + } + } + + &[x-placement^="left"] { + margin-right: 5px; + + .tooltip-arrow { + border-width: 5px 0 5px 5px; + border-top-color: transparent !important; + border-right-color: transparent !important; + border-bottom-color: transparent !important; + right: -5px; + top: calc(50% - 5px); + margin-left: 0; + margin-right: 0; + } + } + + &.popover { + $color: #f9f9f9; + + .popover-inner { + background: $color; + color: black; + padding: 24px; + border-radius: 5px; + box-shadow: 0 5px 30px rgba(black, .1); + } + + .popover-arrow { + border-color: $color; + } + } + + &[aria-hidden='true'] { + visibility: hidden; + opacity: 0; + transition: opacity .15s, visibility .15s; + } + + &[aria-hidden='false'] { + visibility: visible; + opacity: 1; + transition: opacity .15s; + } +} diff --git a/frontend/src/components/Dashboard/DashboardServices.vue b/frontend/src/components/Dashboard/DashboardServices.vue index a029e46b..d25fb8a4 100644 --- a/frontend/src/components/Dashboard/DashboardServices.vue +++ b/frontend/src/components/Dashboard/DashboardServices.vue @@ -1,5 +1,13 @@ + + \ No newline at end of file diff --git a/frontend/src/components/Elements/TreeItem.vue b/frontend/src/components/Elements/TreeItem.vue new file mode 100644 index 00000000..c94c037d --- /dev/null +++ b/frontend/src/components/Elements/TreeItem.vue @@ -0,0 +1,156 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/main.js b/frontend/src/main.js index 8cf0c965..08a73dc2 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -5,6 +5,7 @@ import VueObserveVisibility from 'vue-observe-visibility' import VueClipboard from 'vue-clipboard2' import VueCookies from 'vue-cookies' import VueI18n from 'vue-i18n' +import VTooltip from 'v-tooltip' import router from './routes' import "./mixin" import "./icons" @@ -20,6 +21,7 @@ Vue.use(VueRouter); Vue.use(VueObserveVisibility); Vue.use(VueCookies); Vue.use(VueI18n); +Vue.use(VTooltip); const i18n = new VueI18n({ fallbackLocale: "en", diff --git a/frontend/src/mixin.js b/frontend/src/mixin.js index 32776900..73bbcf67 100644 --- a/frontend/src/mixin.js +++ b/frontend/src/mixin.js @@ -258,7 +258,14 @@ export default Vue.mixin({ return addSeconds(date, amount) }, niceDateWithYear (val) { + if(!val) { + return ''; + } + return format(parseISO(val), 'do MMM, yyyy h:mma'); }, + convertDateObjToSec (val) { + return Math.floor((new Date(val).getTime())/1000); + } } }); diff --git a/frontend/src/store.js b/frontend/src/store.js index c8b328c0..083801b2 100644 --- a/frontend/src/store.js +++ b/frontend/src/store.js @@ -33,6 +33,7 @@ export default new Vuex.Store({ admin: false, user: false, loggedIn: false, + serviceStatus: [], modal: { visible: false, title: "Modal Header", @@ -155,6 +156,9 @@ export default new Vuex.Store({ }, setDowntimes (state, downtimes) { state.downtimes = downtimes; + }, + setServiceStatus (state, serviceStatus) { + state.serviceStatus = serviceStatus; } }, actions: { @@ -166,6 +170,10 @@ export default new Vuex.Store({ const { output } = await Api.downtimes(payload); context.commit('setDowntimes', output ?? []); }, + async getServiceStatus (context, { payload }) { + const { output } = await Api.service_status(payload); + context.commit("setServiceStatus", output ?? []); + }, async loadCore(context) { const core = await Api.core() const token = await Api.token()