diff --git a/server/model/maintenance.js b/server/model/maintenance.js index d46b9d4b4..2ab2a5bb4 100644 --- a/server/model/maintenance.js +++ b/server/model/maintenance.js @@ -2,13 +2,14 @@ const { BeanModel } = require("redbean-node/dist/bean-model"); const { parseTimeObject, parseTimeFromTimeObject, utcToLocal, localToUTC } = require("../../src/util"); const { isArray } = require("chart.js/helpers"); const { timeObjectToUTC, timeObjectToLocal } = require("../util-server"); +const { R } = require("redbean-node"); +const dayjs = require("dayjs"); class Maintenance extends BeanModel { /** * Return an object that ready to parse to JSON for public * Only show necessary data to public - * @param {string} timezone If not specified, the timeRange will be in UTC * @returns {Object} */ async toPublicJSON() { @@ -38,6 +39,7 @@ class Maintenance extends BeanModel { timeRange: timeRange, weekdays: (this.weekdays) ? JSON.parse(this.weekdays) : [], daysOfMonth: (this.days_of_month) ? JSON.parse(this.days_of_month) : [], + timeslotList: await this.getTimeslotList(), }; if (!isArray(obj.weekdays)) { @@ -48,9 +50,45 @@ class Maintenance extends BeanModel { obj.daysOfMonth = []; } + // Maintenance Status + if (!obj.active) { + obj.status = "inactive"; + } else if (obj.strategy === "manual" || obj.timeslotList.length > 0) { + for (let timeslot of obj.timeslotList) { + if (dayjs.utc(timeslot.start_date) <= dayjs.utc() && dayjs.utc(timeslot.end_date) >= dayjs.utc()) { + obj.status = "under-maintenance"; + break; + } + } + + if (!obj.status) { + obj.status = "scheduled"; + } + } else if (obj.timeslotList.length === 0) { + obj.status = "ended"; + } else { + obj.status = "unknown"; + } + return obj; } + /** + * Only get future or current timeslots only + * @returns {Promise<[]>} + */ + async getTimeslotList() { + return await R.getAll(` + SELECT maintenance_timeslot.* + FROM maintenance_timeslot, maintenance + WHERE maintenance_timeslot.maintenance_id = maintenance.id + AND maintenance.id = ? + AND ${Maintenance.getActiveAndFutureMaintenanceSQLCondition()} + `, [ + this.id + ]); + } + /** * Return an object that ready to parse to JSON * @param {string} timezone If not specified, the timeRange will be in UTC @@ -111,6 +149,19 @@ class Maintenance extends BeanModel { `; } + + /** + * SQL conditions for active and future maintenance + * @returns {string} + */ + static getActiveAndFutureMaintenanceSQLCondition() { + return ` + (maintenance_timeslot.end_date >= DATETIME('now') + AND maintenance.active = 1) + OR + (maintenance.strategy = 'manual' AND active = 1) + `; + } } module.exports = Maintenance; diff --git a/server/model/monitor.js b/server/model/monitor.js index 4c51d2209..d77c55297 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -1109,10 +1109,10 @@ class Monitor extends BeanModel { FROM monitor_maintenance mm JOIN maintenance ON mm.maintenance_id = maintenance.id - JOIN maintenance_timeslot + AND mm.monitor_id = ? + LEFT JOIN maintenance_timeslot ON maintenance_timeslot.maintenance_id = maintenance.id - WHERE mm.monitor_id = ? - AND ${activeCondition} + WHERE ${activeCondition} LIMIT 1`, [ monitorID ]); return maintenance.count !== 0; } diff --git a/src/assets/app.scss b/src/assets/app.scss index 81cf77245..be324afdf 100644 --- a/src/assets/app.scss +++ b/src/assets/app.scss @@ -269,6 +269,20 @@ optgroup { color: white; } + .btn-normal { + $bg-color: $dark-header-bg; + + color: $dark-font-color; + background-color: $bg-color; + border-color: $bg-color; + + &:hover { + $hover-color: darken($bg-color, 3%); + background-color: $hover-color; + border-color: $hover-color; + } + } + .btn-warning { color: $dark-font-color2; diff --git a/src/layouts/Layout.vue b/src/layouts/Layout.vue index 7ece4982e..acd9446c1 100644 --- a/src/layouts/Layout.vue +++ b/src/layouts/Layout.vue @@ -58,7 +58,7 @@