WIP: Add maintenance status

This commit is contained in:
Louis Lam 2022-10-11 01:45:30 +08:00
parent c1ccaa7a9f
commit c84de4d259
6 changed files with 108 additions and 48 deletions

View File

@ -2,13 +2,14 @@ const { BeanModel } = require("redbean-node/dist/bean-model");
const { parseTimeObject, parseTimeFromTimeObject, utcToLocal, localToUTC } = require("../../src/util"); const { parseTimeObject, parseTimeFromTimeObject, utcToLocal, localToUTC } = require("../../src/util");
const { isArray } = require("chart.js/helpers"); const { isArray } = require("chart.js/helpers");
const { timeObjectToUTC, timeObjectToLocal } = require("../util-server"); const { timeObjectToUTC, timeObjectToLocal } = require("../util-server");
const { R } = require("redbean-node");
const dayjs = require("dayjs");
class Maintenance extends BeanModel { class Maintenance extends BeanModel {
/** /**
* Return an object that ready to parse to JSON for public * Return an object that ready to parse to JSON for public
* Only show necessary data to public * Only show necessary data to public
* @param {string} timezone If not specified, the timeRange will be in UTC
* @returns {Object} * @returns {Object}
*/ */
async toPublicJSON() { async toPublicJSON() {
@ -38,6 +39,7 @@ class Maintenance extends BeanModel {
timeRange: timeRange, timeRange: timeRange,
weekdays: (this.weekdays) ? JSON.parse(this.weekdays) : [], weekdays: (this.weekdays) ? JSON.parse(this.weekdays) : [],
daysOfMonth: (this.days_of_month) ? JSON.parse(this.days_of_month) : [], daysOfMonth: (this.days_of_month) ? JSON.parse(this.days_of_month) : [],
timeslotList: await this.getTimeslotList(),
}; };
if (!isArray(obj.weekdays)) { if (!isArray(obj.weekdays)) {
@ -48,9 +50,45 @@ class Maintenance extends BeanModel {
obj.daysOfMonth = []; 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; 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 * Return an object that ready to parse to JSON
* @param {string} timezone If not specified, the timeRange will be in UTC * @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; module.exports = Maintenance;

View File

@ -1109,10 +1109,10 @@ class Monitor extends BeanModel {
FROM monitor_maintenance mm FROM monitor_maintenance mm
JOIN maintenance JOIN maintenance
ON mm.maintenance_id = maintenance.id ON mm.maintenance_id = maintenance.id
JOIN maintenance_timeslot AND mm.monitor_id = ?
LEFT JOIN maintenance_timeslot
ON maintenance_timeslot.maintenance_id = maintenance.id ON maintenance_timeslot.maintenance_id = maintenance.id
WHERE mm.monitor_id = ? WHERE ${activeCondition}
AND ${activeCondition}
LIMIT 1`, [ monitorID ]); LIMIT 1`, [ monitorID ]);
return maintenance.count !== 0; return maintenance.count !== 0;
} }

View File

@ -269,6 +269,20 @@ optgroup {
color: white; 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 { .btn-warning {
color: $dark-font-color2; color: $dark-font-color2;

View File

@ -58,7 +58,7 @@
</li> </li>
<li> <li>
<router-link to="/settings" class="dropdown-item" :class="{ active: $route.path.includes('settings') }"> <router-link to="/settings/general" class="dropdown-item" :class="{ active: $route.path.includes('settings') }">
<font-awesome-icon icon="cog" /> {{ $t("Settings") }} <font-awesome-icon icon="cog" /> {{ $t("Settings") }}
</router-link> </router-link>
</li> </li>

View File

@ -12,10 +12,6 @@ export default {
}, },
methods: { methods: {
isActiveMaintenance(endDate) {
return (dayjs.utc(endDate).unix() >= dayjs.utc().unix());
},
toUTC(value) { toUTC(value) {
return dayjs.tz(value, this.timezone).utc().format(); return dayjs.tz(value, this.timezone).utc().format();
}, },

View File

@ -20,7 +20,7 @@
v-for="(item, index) in sortedMaintenanceList" v-for="(item, index) in sortedMaintenanceList"
:key="index" :key="index"
class="item" class="item"
:class="{ 'ended': !$root.isActiveMaintenance(item.end_date) }" :class="item.status"
> >
<div class="left-part"> <div class="left-part">
<div <div
@ -35,7 +35,7 @@
<div class="buttons"> <div class="buttons">
<router-link v-if="false" :to="maintenanceURL(item.id)" class="btn btn-light">{{ $t("Details") }}</router-link> <router-link v-if="false" :to="maintenanceURL(item.id)" class="btn btn-light">{{ $t("Details") }}</router-link>
<button v-if="item.active" class="btn btn-light" @click="pauseDialog"> <button v-if="item.active" class="btn btn-normal" @click="pauseDialog">
<font-awesome-icon icon="pause" /> {{ $t("Pause") }} <font-awesome-icon icon="pause" /> {{ $t("Pause") }}
</button> </button>
@ -43,7 +43,7 @@
<font-awesome-icon icon="play" /> {{ $t("Resume") }} <font-awesome-icon icon="play" /> {{ $t("Resume") }}
</button> </button>
<router-link :to="'/maintenance/edit/' + item.id" class="btn btn-secondary"> <router-link :to="'/maintenance/edit/' + item.id" class="btn btn-normal">
<font-awesome-icon icon="edit" /> {{ $t("Edit") }} <font-awesome-icon icon="edit" /> {{ $t("Edit") }}
</router-link> </router-link>
@ -90,36 +90,6 @@ export default {
let result = Object.values(this.$root.maintenanceList); let result = Object.values(this.$root.maintenanceList);
result.sort((m1, m2) => { result.sort((m1, m2) => {
if (this.$root.isActiveMaintenance(m1.end_date) !== this.$root.isActiveMaintenance(m2.end_date)) {
if (!this.$root.isActiveMaintenance(m2.end_date)) {
return -1;
}
if (!this.$root.isActiveMaintenance(m1.end_date)) {
return 1;
}
}
if (this.$root.isActiveMaintenance(m1.end_date) && this.$root.isActiveMaintenance(m2.end_date)) {
if (Date.parse(m1.end_date) < Date.parse(m2.end_date)) {
return -1;
}
if (Date.parse(m2.end_date) < Date.parse(m1.end_date)) {
return 1;
}
}
if (!this.$root.isActiveMaintenance(m1.end_date) && !this.$root.isActiveMaintenance(m2.end_date)) {
if (Date.parse(m1.end_date) < Date.parse(m2.end_date)) {
return 1;
}
if (Date.parse(m2.end_date) < Date.parse(m1.end_date)) {
return -1;
}
}
return m1.title.localeCompare(m2.title); return m1.title.localeCompare(m2.title);
}); });
@ -173,7 +143,7 @@ export default {
/** /**
* Pause maintenance * Pause maintenance
*/ */
pauseMonitor() { pauseMaintenance() {
return; return;
this.$root.getSocket().emit("pauseMaintenance", selectedMaintenanceID, (res) => { this.$root.getSocket().emit("pauseMaintenance", selectedMaintenanceID, (res) => {
this.$root.toastRes(res); this.$root.toastRes(res);
@ -211,14 +181,44 @@ export default {
background-color: $highlight-white; background-color: $highlight-white;
} }
&.under-maintenance {
background-color: rgba(23, 71, 245, 0.16);
&:hover {
background-color: rgba(23, 71, 245, 0.3) !important;
}
.circle {
background-color: $maintenance;
}
}
&.scheduled {
.circle {
background-color: $primary;
}
}
&.inactive {
.circle {
background-color: $danger;
}
}
&.ended { &.ended {
.left-part { .left-part {
opacity: 0.5; opacity: 0.3;
}
.circle { .circle {
background-color: $dark-font-color; background-color: $dark-font-color;
} }
} }
&.unknown {
.circle {
background-color: $dark-font-color;
}
} }
.left-part { .left-part {
@ -230,7 +230,6 @@ export default {
width: 25px; width: 25px;
height: 25px; height: 25px;
border-radius: 50rem; border-radius: 50rem;
background-color: $maintenance;
} }
.info { .info {