UI changes

pull/791/head
hunterlong 2020-08-21 18:44:10 -07:00
parent 37303351d5
commit d26faec077
9 changed files with 130 additions and 70 deletions

View File

@ -233,10 +233,6 @@ func ParseQueries(r *http.Request, o isObject) (*GroupQuery, error) {
if endField == 0 {
query.End = utils.Now()
}
if query.End.After(utils.Now()) {
query.End = utils.Now()
}
if query.Limit != 0 {
q = q.Limit(query.Limit)
}

View File

@ -52,17 +52,17 @@ class Api {
return axios.post('api/services/' + data.id, data).then(response => (response.data))
}
async service_hits(id, start, end, group, fill=true) {
async service_hits(id, start, end, group, fill = true) {
return axios.get('api/services/' + id + '/hits_data?start=' + start + '&end=' + end + '&group=' + group + '&fill=' + fill).then(response => (response.data))
}
async service_ping(id, start, end, group, fill=true) {
return axios.get('api/services/' + id + '/ping_data?start=' + start + '&end=' + end + '&group=' + group + '&fill=' + fill).then(response => (response.data))
}
async service_ping(id, start, end, group, fill = true) {
return axios.get('api/services/' + id + '/ping_data?start=' + start + '&end=' + end + '&group=' + group + '&fill=' + fill).then(response => (response.data))
}
async service_failures_data(id, start, end, group, fill=true) {
return axios.get('api/services/' + id + '/failure_data?start=' + start + '&end=' + end + '&group=' + group + '&fill=' + fill).then(response => (response.data))
}
async service_failures_data(id, start, end, group, fill = true) {
return axios.get('api/services/' + id + '/failure_data?start=' + start + '&end=' + end + '&group=' + group + '&fill=' + fill).then(response => (response.data))
}
async service_uptime(id, start, end) {
return axios.get('api/services/' + id + '/uptime_data?start=' + start + '&end=' + end).then(response => (response.data))
@ -73,7 +73,7 @@ class Api {
}
async service_failures(id, start, end, limit = 999, offset = 0) {
return axios.get('api/services/' + id + '/failures?start=' + start + '&end=' + end + '&limit=' + limit+ '&offset=' + offset).then(response => (response.data))
return axios.get('api/services/' + id + '/failures?start=' + start + '&end=' + end + '&limit=' + limit + '&offset=' + offset).then(response => (response.data))
}
async service_failures_delete(service) {
@ -88,16 +88,16 @@ class Api {
return axios.post('api/reorder/services', data).then(response => (response.data))
}
async checkins() {
return axios.get('api/checkins').then(response => (response.data))
}
async checkins() {
return axios.get('api/checkins').then(response => (response.data))
}
async groups() {
return axios.get('api/groups').then(response => (response.data))
}
async groups_reorder(data) {
window.console.log('api/reorder/groups', data)
window.console.log('api/reorder/groups', data)
return axios.post('api/reorder/groups', data).then(response => (response.data))
}
@ -130,40 +130,40 @@ class Api {
}
async incident_updates(incident) {
return axios.get('api/incidents/'+incident.id+'/updates').then(response => (response.data))
return axios.get('api/incidents/' + incident.id + '/updates').then(response => (response.data))
}
async incident_update_create(update) {
return axios.post('api/incidents/'+update.incident+'/updates', update).then(response => (response.data))
return axios.post('api/incidents/' + update.incident + '/updates', update).then(response => (response.data))
}
async incident_update_delete(update) {
return axios.delete('api/incidents/'+update.incident+'/updates/'+update.id).then(response => (response.data))
return axios.delete('api/incidents/' + update.incident + '/updates/' + update.id).then(response => (response.data))
}
async incidents_service(id) {
return axios.get('api/services/'+id+'/incidents').then(response => (response.data))
}
async incidents_service(id) {
return axios.get('api/services/' + id + '/incidents').then(response => (response.data))
}
async incident_create(service_id, data) {
return axios.post('api/services/'+service_id+'/incidents', data).then(response => (response.data))
}
async incident_create(service_id, data) {
return axios.post('api/services/' + service_id + '/incidents', data).then(response => (response.data))
}
async incident_delete(incident) {
return axios.delete('api/incidents/'+incident.id).then(response => (response.data))
}
async incident_delete(incident) {
return axios.delete('api/incidents/' + incident.id).then(response => (response.data))
}
async checkin(api) {
return axios.get('api/checkins/'+api).then(response => (response.data))
return axios.get('api/checkins/' + api).then(response => (response.data))
}
async checkin_create(data) {
return axios.post('api/checkins', data).then(response => (response.data))
}
async checkin_create(data) {
return axios.post('api/checkins', data).then(response => (response.data))
}
async checkin_delete(checkin) {
return axios.delete('api/checkins/'+checkin.api_key).then(response => (response.data))
}
async checkin_delete(checkin) {
return axios.delete('api/checkins/' + checkin.api_key).then(response => (response.data))
}
async messages() {
return axios.get('api/messages').then(response => (response.data))

View File

@ -113,7 +113,7 @@ A:HOVER {
.list-group-item:HOVER {
background-color: lighten($group-list-background, 5%) !important;
box-shadow: #00000014 0px 4px 4px 2px;
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 6px 1px;
-webkit-transition-duration: 300ms;
-moz-transition-duration: 300ms;
-o-transition-duration: 300ms;

View File

@ -1,6 +1,5 @@
<template>
<div class="col-12 mt-4 mt-md-3">
<div class="row stats_area mb-5">
<div class="col-4">
<span class="font-6 font-weight-bold d-block">{{$store.getters.services.length}}</span>
@ -34,27 +33,26 @@
</div>
<div class="row">
<ServiceInfo v-for="(service, index) in services_no_group" v-bind:key="index" :service="service" />
<div v-for="(service, index) in services_no_group" class="col-12 col-md-4">
<ServiceInfo :service="service" />
</div>
</div>
<div class="row" v-for="group in groups">
<h4 class="h4 col-12 mb-3 mt-2 text-dim"><font-awesome-icon icon="minus" class="mr-3"/> {{group.name}}</h4>
<ServiceInfo v-if="group_services(group.id)" v-for="(service, index) in group_services(group.id)" v-bind:key="index" :service="service" />
<div v-for="group in groups">
<GroupedServices :group="group"/>
</div>
</div>
</template>
<script>
import isAfter from "date-fns/isAfter";
import parseISO from "date-fns/parseISO";
import isBefore from "date-fns/isBefore";
import GroupedServices from "@/components/Dashboard/GroupedServices";
const ServiceInfo = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/ServiceInfo')
export default {
name: 'DashboardIndex',
components: {
GroupedServices,
ServiceInfo
},
data() {
@ -77,9 +75,6 @@
},
},
methods: {
group_services(id) {
return this.$store.getters.servicesInGroup(id)
},
failuresLast24Hours() {
let total = 0;
this.services.map((s) => {

View File

@ -0,0 +1,53 @@
<template>
<div class="row">
<h5 v-if="group.name" class="h5 col-12 mb-3 mt-2 text-dim">
<font-awesome-icon @click="toggle" :icon="expanded ? 'minus' : 'plus'" class="pointer mr-3"/> {{group.name}}
<span class="badge badge-success text-uppercase float-right ml-2">{{services_online.length}} online</span>
<span v-if="services_online.services_offline > 0" class="badge badge-danger text-uppercase float-right">
{{services_offline.length}} offline
</span>
</h5>
<div class="col-12 col-md-4" v-if="expanded" v-for="service in group_services">
<ServiceInfo :service="service" />
</div>
</div>
</template>
<script>
const ServiceInfo = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/ServiceInfo')
export default {
name: "GroupedServices",
components: {
ServiceInfo
},
data() {
return {
expanded: true
}
},
props: {
group: {
required: true,
type: Object,
}
},
computed: {
services_online() {
return this.$store.getters.servicesInGroup(this.group.id).filter((s) => s.online)
},
services_offline() {
return this.$store.getters.servicesInGroup(this.group.id).filter((s) => !s.online)
},
group_services() {
return this.$store.getters.servicesInGroup(this.group.id)
},
},
methods: {
toggle() {
this.expanded = !this.expanded
}
}
}
</script>

View File

@ -1,26 +1,27 @@
<template>
<div>
<div class="row p-2">
<Loading :loading="!loaded"/>
<div v-if="loaded && last_failure && failureBefore" class="text-danger font-2 p-0 m-0 mb-2">
<div v-if="loaded && last_failure && failureBefore" class="col-12 text-danger font-2 m-0 mb-2">
<font-awesome-icon icon="exclamation" class="mr-1 text-danger" size="1x"/> Recent Failure<br>
<span class="font-italic d-inline-block text-dim" style="max-width: 270px">
Last failure was {{ago(service.last_error)}} ago. {{last_failure.issue}}
</span>
</div>
<div v-if="loaded" v-for="message in messages" class="font-2 p-0 m-0 mb-2">
<div v-if="loaded" v-for="message in messages" class="col-12 font-2 m-0 mb-2">
<font-awesome-icon icon="calendar" class="mr-1" size="1x"/> Upcoming Announcement<br>
<span class="font-italic font-weight-light text-dim">{{message.description}}</span>
<span class="font-0 text-dim float-right font-weight-light mt-1">@ <strong>{{niceDate(message.start_on)}}</strong>
</span>
</div>
<div v-if="loaded" v-for="incident in incidents" class="font-2 p-0 m-0 mb-2">
<div v-if="loaded" v-for="incident in incidents" class="col-12 font-2 m-0 mb-2">
<font-awesome-icon icon="bullhorn" class="mr-1" size="1x"/>Recent Incident<br>
<span class="font-italic d-inline-block text-dim" style="max-width: 270px">{{incident.title}} - {{incident.description}}</span>
<span class="font-0 text-dim float-right font-weight-light mt-1">@ <strong>{{niceDate(incident.created_at)}}</strong></span>
</div>
<div v-if="success_event && !failureBefore" class="font-2 p-0 m-0 mt-1 mb-3">
<div v-if="success_event && !failureBefore" class="col-12 font-2 m-0 mb-2">
<span class="text-success"><font-awesome-icon icon="check" class="mr-1" size="1x"/>No New Events</span>
<span class="font-italic d-inline-block text-truncate text-dim" style="max-width: 270px">
Last failure was {{ago(service.last_error)}} ago.

View File

@ -1,5 +1,4 @@
<template>
<div class="col-4">
<div class="dashboard_card card mb-4" :class="{'offline-card': !service.online}">
<div class="card-header pb-1">
<h6 v-observe-visibility="setVisible">
@ -11,25 +10,23 @@
</div>
<div class="card-body">
<transition name="fade">
<div v-if="loaded" class="row pl-2 pr-2">
<div class="col-md-6 col-sm-12 mt-2 mt-md-0 mb-3 pl-0 pr-0">
<div v-if="loaded" class="row pl-2">
<div class="col-md-6 col-sm-12 pl-2 mt-2 mt-md-0 mb-3">
<ServiceSparkLine :title="set2_name" subtitle="Latency Last 24 Hours" :series="set2"/>
</div>
<div class="col-md-6 col-sm-12 mt-4 mt-md-0 mb-3">
<div class="col-md-6 col-sm-12 pl-0 mt-4 mt-md-0 mb-3">
<ServiceSparkLine :title="set1_name" subtitle="Latency Last 7 Days" :series="set1"/>
</div>
<ServiceEvents :service="service"/>
</div>
<div v-else class="row mb-5 pt-5 pb-5">
<div class="col-6 text-center">
<font-awesome-icon icon="circle-notch" class="text-dim" size="3x" spin/>
<div v-else class="row mb-5">
<div class="col-12 col-md-6 text-center">
<font-awesome-icon icon="circle-notch" class="text-dim" size="2x" spin/>
</div>
<div class="col-6 text-center text-dim">
<font-awesome-icon icon="circle-notch" class="text-dim" size="3x" spin/>
<div class="col-12 col-md-6 text-center text-dim">
<font-awesome-icon icon="circle-notch" class="text-dim" size="2x" spin/>
</div>
</div>
</transition>
</div>
<div class="card-footer">
@ -62,7 +59,6 @@
</span>
</div>
</div>
</template>
<script>

View File

@ -2,7 +2,7 @@
<div>
<div v-observe-visibility="{callback: visibleChart, once: true}" v-if="!loaded" class="row">
<div class="col-12 text-center mt-3">
<font-awesome-icon icon="circle-notch" class="text-dim" size="1x" spin/>
<font-awesome-icon icon="circle-notch" class="text-dim" size="2x" spin/>
</div>
</div>
<transition name="fade">
@ -77,8 +77,9 @@ export default {
this.hover_text = `${e.date.toLocaleDateString()} - ${txt}`
},
async lastDaysFailures() {
const start = this.nowSubtract(86400 * 90)
const data = await Api.service_failures_data(this.service.id, this.toUnix(start), this.toUnix(this.now()), "24h", true)
const start = this.beginningOf('day', this.nowSubtract(86400 * 90))
const end = this.endOf('tomorrow')
const data = await Api.service_failures_data(this.service.id, this.toUnix(start), this.toUnix(end), "24h", true)
data.forEach((d) => {
let date = this.parseISO(d.timeframe)
this.failureData.push({month: date.getMonth(), day: date.getDate(), date: date, amount: d.amount})

View File

@ -58,7 +58,7 @@ export default Vue.mixin({
return roundToNearestMinutes(minutes)
},
endOf(method, val) {
switch (val) {
switch (method) {
case "day":
return endOfDay(val)
case "today":
@ -73,7 +73,7 @@ export default Vue.mixin({
return roundToNearestMinutes(val)
},
beginningOf(method, val) {
switch (val) {
switch (method) {
case "day":
return startOfDay(val)
case "today":
@ -106,6 +106,24 @@ export default Vue.mixin({
return `Service has been offline for ${this.ago(s.last_success)}`
}
},
round_time(frame, val) {
switch(frame) {
case "15m":
return roundToNearestMinutes(val, {nearestTo: 60 * 15})
case "30m":
return roundToNearestMinutes(val, {nearestTo: 60 * 30})
case "1h":
return roundToNearestMinutes(val, {nearestTo: 3600})
case "3h":
return roundToNearestMinutes(val, {nearestTo: 3600 * 3})
case "6h":
return roundToNearestMinutes(val, {nearestTo: 3600 * 6})
case "12h":
return roundToNearestMinutes(val, {nearestTo: 3600 * 12})
case "24h":
return roundToNearestMinutes(val, {nearestTo: 3600 * 24})
}
},
toUnix(val) {
return getUnixTime(val)
},