diff --git a/.gitignore b/.gitignore
index 59dce8cf..5b458bcc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,4 +41,5 @@ tmp
/frontend/cypress/videos/
services.yml
statping.wiki
-assets/
\ No newline at end of file
+assets/
+.vscode/settings.json
diff --git a/frontend/.gitignore b/frontend/.gitignore
index a0dddc6f..b4a193e4 100644
--- a/frontend/.gitignore
+++ b/frontend/.gitignore
@@ -19,3 +19,6 @@ yarn-error.log*
*.njsproj
*.sln
*.sw?
+
+# Package lock file
+package-lock.json
diff --git a/frontend/src/API.js b/frontend/src/API.js
index e6f3fc7d..2646a0e7 100644
--- a/frontend/src/API.js
+++ b/frontend/src/API.js
@@ -282,6 +282,27 @@ class Api {
await axios.all([all])
}
+ async downtimes ({ serviceId, start, end, skip, count, subStatus }) {
+ return axios.get('api/downtimes', {
+ params: { service_id: serviceId, start, end, skip, count, sub_status: subStatus }
+ }).then((response) => response.data);
+ }
+
+ async downtime (id) {
+ return axios.get(`api/downtimes/${id}`).then((response) => response.data);
+ }
+
+ async downtime_create (data) {
+ return axios.post('/api/downtimes', data).then((response) => response.data);
+ }
+
+ async downtime_update ({ id, data }) {
+ return axios.patch(`/api/downtimes/${id}`, data).then((response) => response.data);
+ }
+
+ async downtime_delete (id) {
+ return axios.delete(`/api/downtimes/${id}`).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 59b82352..dc6da19b 100644
--- a/frontend/src/assets/scss/layout.scss
+++ b/frontend/src/assets/scss/layout.scss
@@ -85,6 +85,10 @@ A {
.nav-link {
color: $navbar-color;
+
+ &.router-link-exact-active {
+ font-weight: bold;
+ }
}
.form-control {
diff --git a/frontend/src/components/Dashboard/DashboardDowntimes.vue b/frontend/src/components/Dashboard/DashboardDowntimes.vue
new file mode 100644
index 00000000..adf38bdc
--- /dev/null
+++ b/frontend/src/components/Dashboard/DashboardDowntimes.vue
@@ -0,0 +1,176 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading Downtimes
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/Dashboard/DowntimesList.vue b/frontend/src/components/Dashboard/DowntimesList.vue
new file mode 100644
index 00000000..ecdcf23e
--- /dev/null
+++ b/frontend/src/components/Dashboard/DowntimesList.vue
@@ -0,0 +1,172 @@
+
+
+
+ You currently don't have any downtimes for this services!
+
+
+
+
+
+
+ {{ $t('name') }}
+
+
+ {{ $t('start_time') }}
+
+
+ {{ $t('end_time') }}
+
+
+ {{ $t('status') }}
+
+
+ {{ $t('failures') }}
+
+
+ {{ $t('actions') }}
+
+
+
+
+
+
+ {{ downtime.service.name }}
+
+
+
+
+ {{ niceDateWithYear(downtime.start) }}
+
+
+
+
+ {{ downtime.end ? niceDateWithYear(downtime.end) : 'Ongoing' }}
+
+
+
+
+ {{ downtime.sub_status }}
+
+
+
+
+ {{ downtime.failures }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/Dashboard/EditDowntime.vue b/frontend/src/components/Dashboard/EditDowntime.vue
new file mode 100644
index 00000000..4bcc8549
--- /dev/null
+++ b/frontend/src/components/Dashboard/EditDowntime.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+ Loading Downtime
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/Dashboard/TopNav.vue b/frontend/src/components/Dashboard/TopNav.vue
index 8136fff8..45a1394b 100644
--- a/frontend/src/components/Dashboard/TopNav.vue
+++ b/frontend/src/components/Dashboard/TopNav.vue
@@ -14,6 +14,9 @@
{{ $t('services') }}
+
+ {{'Downtimes'}}
+
{{ $t('users') }}
diff --git a/frontend/src/components/Elements/Pagination.vue b/frontend/src/components/Elements/Pagination.vue
new file mode 100644
index 00000000..5b4687f4
--- /dev/null
+++ b/frontend/src/components/Elements/Pagination.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/forms/Downtime.vue b/frontend/src/forms/Downtime.vue
new file mode 100644
index 00000000..a23cb788
--- /dev/null
+++ b/frontend/src/forms/Downtime.vue
@@ -0,0 +1,325 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/forms/DowntimeFilters.vue b/frontend/src/forms/DowntimeFilters.vue
new file mode 100644
index 00000000..e8317227
--- /dev/null
+++ b/frontend/src/forms/DowntimeFilters.vue
@@ -0,0 +1,173 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/icons.js b/frontend/src/icons.js
index 91e7c3bc..86024a5c 100644
--- a/frontend/src/icons.js
+++ b/frontend/src/icons.js
@@ -6,4 +6,4 @@ import Vue from "vue";
library.add(fas, fab)
-Vue.component('font-awesome-icon', FontAwesomeIcon)
+Vue.component('FontAwesomeIcon', FontAwesomeIcon);
diff --git a/frontend/src/languages/english.js b/frontend/src/languages/english.js
index db54f907..33d73f4e 100644
--- a/frontend/src/languages/english.js
+++ b/frontend/src/languages/english.js
@@ -140,6 +140,20 @@ const english = {
notify_all: "Notify All Changes",
service_update: "Update Service",
service_create: "Create Service",
+ start_time: 'Start Time',
+ end_time: "End Time",
+ actions: "Actions",
+ services: "Services",
+ downtimes: "Downtimes",
+ downtime_info: "Downtime Info",
+ downtime_status: "Downtime Status",
+ downtime_date_range: "Downtime Date Range",
+ downtime_create: "Create Downtime",
+ downtime_update: "Update Downtime",
+ filters: "Filters",
+ service: "Service",
+ clear: "Clear",
+ search: "Search",
};
export default english;
diff --git a/frontend/src/mixin.js b/frontend/src/mixin.js
index 373239e6..32776900 100644
--- a/frontend/src/mixin.js
+++ b/frontend/src/mixin.js
@@ -256,6 +256,9 @@ export default Vue.mixin({
},
addSeconds(date, amount) {
return addSeconds(date, amount)
- }
+ },
+ niceDateWithYear (val) {
+ return format(parseISO(val), 'do MMM, yyyy h:mma');
+ },
}
});
diff --git a/frontend/src/routes.js b/frontend/src/routes.js
index 2b2a82ad..2cebe256 100644
--- a/frontend/src/routes.js
+++ b/frontend/src/routes.js
@@ -16,6 +16,9 @@ const Checkins = () => import(/* webpackChunkName: "dashboard" */ '@/components/
const Failures = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/Failures')
const NotFound = () => import(/* webpackChunkName: "index" */ '@/pages/NotFound')
const Importer = () => import(/* webpackChunkName: "index" */ '@/components/Dashboard/Importer')
+const DashboardDowntimes = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/DashboardDowntimes')
+const EditDowntime = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/EditDowntime')
+
import VueRouter from "vue-router";
import Api from "./API";
@@ -137,6 +140,27 @@ const routes = [
requiresAuth: true,
title: 'Statping - Service Failures',
}
+ },{
+ path: 'downtimes',
+ component: DashboardDowntimes,
+ meta: {
+ requiresAuth: true,
+ title: 'Statping - Downtimes',
+ }
+ },{
+ path: 'create_downtime',
+ component: EditDowntime,
+ meta: {
+ requiresAuth: true,
+ title: 'Statping - Create Downtime',
+ }
+ },{
+ path: 'edit_downtime/:id',
+ component: EditDowntime,
+ meta: {
+ requiresAuth: true,
+ title: 'Statping - Update Downtime',
+ }
},{
path: 'messages',
component: DashboardMessages,
@@ -228,3 +252,4 @@ router.beforeEach((to, from, next) => {
});
export default router
+
diff --git a/frontend/src/store.js b/frontend/src/store.js
index 66d923e1..c8b328c0 100644
--- a/frontend/src/store.js
+++ b/frontend/src/store.js
@@ -23,6 +23,7 @@ export default new Vuex.Store({
oauth: {},
token: null,
services: [],
+ downtimes: [],
service: null,
groups: [],
messages: [],
@@ -152,12 +153,19 @@ export default new Vuex.Store({
setModal(state, modal) {
state.modal = modal
},
+ setDowntimes (state, downtimes) {
+ state.downtimes = downtimes;
+ }
},
actions: {
async getAllServices(context) {
const services = await Api.services()
context.commit("setServices", services);
},
+ async getDowntimes (context, { payload }) {
+ const { output } = await Api.downtimes(payload);
+ context.commit('setDowntimes', output ?? []);
+ },
async loadCore(context) {
const core = await Api.core()
const token = await Api.token()