[status page] checkpoint

This commit is contained in:
LouisLam 2021-09-13 19:21:39 +08:00
parent e205adfd7b
commit 35aca46b68
6 changed files with 203 additions and 57 deletions

View file

@ -301,6 +301,20 @@ h2 {
opacity: 0; opacity: 0;
} }
.slide-fade-right-enter-active {
transition: all 0.2s $easing-in;
}
.slide-fade-right-leave-active {
transition: all 0.2s $easing-in;
}
.slide-fade-right-enter-from,
.slide-fade-right-leave-to {
transform: translateX(50px);
opacity: 0;
}
.monitor-list { .monitor-list {
&.scrollbar { &.scrollbar {
min-height: calc(100vh - 240px); min-height: calc(100vh - 240px);

View file

@ -0,0 +1,96 @@
<template>
<!-- Group List -->
<Draggable
v-model="$root.publicGroupList"
:disabled="!editMode"
item-key="id"
:animation="100"
>
<template #item="{ element }">
<div>
<h2 class="mt-5">{{ element.name }}</h2>
<div class="shadow-box monitor-list mt-4 position-relative">
<div v-if="element.monitorList.length === 0" class="text-center no-monitor-msg">
{{ $t("No Monitors") }}
</div>
<!-- Monitor List -->
<Draggable
v-model="element.monitorList"
class="monitor-list"
group="same-group"
:disabled="!editMode"
:animation="100"
>
<template #item="{ element }">
<div class="item">
<div class="row">
<div class="col-6 col-md-8 small-padding">
<div class="info">
<Uptime :monitor="element" type="24" :pill="true" />
{{ element.name }}
</div>
</div>
<div :key="$root.userHeartbeatBar" class="col-6 col-md-4">
<HeartbeatBar size="small" :monitor-id="element.id" />
</div>
</div>
</div>
</template>
</Draggable>
</div>
</div>
</template>
</Draggable>
</template>
<script>
import Draggable from "vuedraggable";
import HeartbeatBar from "./HeartbeatBar.vue";
import Uptime from "./Uptime.vue";
export default {
name: "GroupList",
components: {
Draggable,
HeartbeatBar,
Uptime,
},
props: {
editMode: {
type: Boolean,
required: true,
},
},
data() {
return {
}
},
created() {
}
}
</script>
<style lang="scss" scoped>
.no-monitor-msg {
position: absolute;
width: 100%;
top: 20px;
left: 0;
}
.monitor-list {
min-height: 46px;
}
.flip-list-move {
transition: transform 0.5s;
}
.no-move {
transition: transform 0s;
}
</style>

View file

@ -1,10 +1,10 @@
import { library } from "@fortawesome/fontawesome-svg-core" import { library } from "@fortawesome/fontawesome-svg-core"
import { faCog, faEdit, faPlus, faPause, faPlay, faTachometerAlt, faTrash, faList, faArrowAltCircleUp, faEye, faEyeSlash, faCheckCircle, faStream } from "@fortawesome/free-solid-svg-icons" import { faCog, faEdit, faPlus, faPause, faPlay, faTachometerAlt, faTrash, faList, faArrowAltCircleUp, faEye, faEyeSlash, faCheckCircle, faStream, faSave } from "@fortawesome/free-solid-svg-icons"
//import { fa } from '@fortawesome/free-regular-svg-icons' //import { fa } from '@fortawesome/free-regular-svg-icons'
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome" import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"
// Add Free Font Awesome Icons here // Add Free Font Awesome Icons here
// https://fontawesome.com/v5.15/icons?d=gallery&p=2&s=solid&m=free // https://fontawesome.com/v5.15/icons?d=gallery&p=2&s=solid&m=free
library.add(faCog, faEdit, faPlus, faPause, faPlay, faTachometerAlt, faTrash, faList, faArrowAltCircleUp, faEye, faEyeSlash, faCheckCircle, faStream); library.add(faCog, faEdit, faPlus, faPause, faPlay, faTachometerAlt, faTrash, faList, faArrowAltCircleUp, faEye, faEyeSlash, faCheckCircle, faStream, faSave);
export { FontAwesomeIcon } export { FontAwesomeIcon }

View file

@ -13,6 +13,8 @@ import socket from "./mixins/socket";
import theme from "./mixins/theme"; import theme from "./mixins/theme";
import mobile from "./mixins/mobile"; import mobile from "./mixins/mobile";
import datetime from "./mixins/datetime"; import datetime from "./mixins/datetime";
import publicMixin from "./mixins/public";
import Dashboard from "./pages/Dashboard.vue"; import Dashboard from "./pages/Dashboard.vue";
import DashboardHome from "./pages/DashboardHome.vue"; import DashboardHome from "./pages/DashboardHome.vue";
import Details from "./pages/Details.vue"; import Details from "./pages/Details.vue";
@ -140,7 +142,8 @@ const app = createApp({
socket, socket,
theme, theme,
mobile, mobile,
datetime datetime,
publicMixin,
], ],
data() { data() {
return { return {

7
src/mixins/public.js Normal file
View file

@ -0,0 +1,7 @@
export default {
data() {
return {
publicGroupList: [],
}
}
}

View file

@ -2,58 +2,61 @@
<div class="container mt-3"> <div class="container mt-3">
<h1><img src="/icon.svg" alt /> Uptime Kuma</h1> <h1><img src="/icon.svg" alt /> Uptime Kuma</h1>
<div v-if="hasToken" class="mt-3"> <div v-if="hasToken" class="mt-3" style="height: 38px;">
<button v-if="!enableEditMode" class="btn btn-info me-2" @click="edit"> <div v-if="!enableEditMode">
<font-awesome-icon icon="edit" /> <button class="btn btn-info me-2" @click="edit">
Edit Status Page <font-awesome-icon icon="edit" />
</button> Edit Status Page
</button>
<button v-if="enableEditMode" class="btn btn-dark me-2" @click="leaveEditMode"> <router-link to="/dashboard" class="btn btn-info">
<font-awesome-icon icon="" /> <font-awesome-icon icon="tachometer-alt" />
Leave Edit Mode Go to Dashboard
</button> </router-link>
</div>
<router-link v-if="!enableEditMode" to="/dashboard" class="btn btn-info"> <div v-else>
<font-awesome-icon icon="tachometer-alt" /> <button class="btn btn-success me-2" @click="leaveEditMode">
Go to Dashboard <font-awesome-icon icon="save" />
</router-link> {{ $t("Save") }}
</button>
<button class="btn btn-danger me-2" @click="">
<font-awesome-icon icon="save" />
{{ $t("Discard") }}
</button>
<!-- Set Default Language -->
<!-- Set theme -->
</div>
</div> </div>
<div class="shadow-box list p-4 overall-status mt-4"> <div class="shadow-box list p-4 overall-status mt-4">
<font-awesome-icon icon="check-circle" class="ok" /> All Systems Operational <font-awesome-icon icon="check-circle" class="ok" /> All Systems Operational
</div> </div>
<div v-if="showEditFeature" class="mt-4"> <div v-if="showEditFeature" class="row mt-4" style="height: 43px;">
<VueMultiselect <div class="col-2">
v-model="selectedMonitor" <button class="btn btn-primary btn-add-group" @click="addGroup">Add Group</button>
:options="allMonitorList"
:custom-label="monitorSelectorLabel"
:searchable="true"
@select="onSelectedMonitor"
></VueMultiselect>
</div>
<div class="shadow-box monitor-list mt-4">
<div v-if="Object.keys(monitorList).length === 0" class="text-center my-3">
{{ $t("No Monitors") }}
</div> </div>
<div class="col-10">
<div v-for="(item, index) in monitorList" :key="index" class="item"> <div>
<div class="row"> <VueMultiselect
<div class="col-6 col-md-8 small-padding"> v-model="selectedMonitor"
<div class="info"> :options="allMonitorList"
<Uptime :monitor="item" type="24" :pill="true" /> :custom-label="monitorSelectorLabel"
{{ item.name }} :searchable="true"
</div> placeholder="Add a monitor"
</div> ></VueMultiselect>
<div :key="$root.userHeartbeatBar" class="col-6 col-md-4">
<HeartbeatBar size="small" :monitor-id="item.id" />
</div>
</div> </div>
</div> </div>
</div> </div>
<footer class="mt-4"> <div>
<GroupList :edit-mode="enableEditMode" />
</div>
<footer class="my-4">
Powered by <a target="_blank" href="https://github.com/louislam/uptime-kuma">Uptime Kuma</a> Powered by <a target="_blank" href="https://github.com/louislam/uptime-kuma">Uptime Kuma</a>
</footer> </footer>
</div> </div>
@ -61,12 +64,8 @@
<script> <script>
import VueMultiselect from "vue-multiselect" import VueMultiselect from "vue-multiselect"
import { useToast } from "vue-toastification";
const toast = useToast();
import axios from "axios"; import axios from "axios";
import HeartbeatBar from "../components/HeartbeatBar.vue"; import GroupList from "../components/GroupList.vue";
import Uptime from "../components/Uptime.vue";
const env = process.env.NODE_ENV || "production"; const env = process.env.NODE_ENV || "production";
@ -77,8 +76,7 @@ if (env === "development" || localStorage.dev === "dev") {
export default { export default {
components: { components: {
HeartbeatBar, GroupList,
Uptime,
VueMultiselect, VueMultiselect,
}, },
data() { data() {
@ -91,8 +89,18 @@ export default {
} }
}, },
computed: { computed: {
allMonitorList() { allMonitorList() {
return Object.values(this.$root.monitorList); let result = [];
for (let id in this.$root.monitorList) {
if (this.$root.monitorList[id] && ! (id in this.monitorList)) {
let monitor = this.$root.monitorList[id];
result.push(monitor);
}
}
return result;
}, },
showEditFeature() { showEditFeature() {
return this.enableEditMode && this.$root.socket.connected; return this.enableEditMode && this.$root.socket.connected;
@ -100,6 +108,21 @@ export default {
}, },
watch: { watch: {
/**
* Selected a monitor and add to the list.
*/
selectedMonitor(monitor) {
if (monitor) {
if (this.$root.publicGroupList.length === 0) {
this.addGroup();
}
const firstGroup = this.$root.publicGroupList[0];
firstGroup.monitorList.push(monitor);
this.selectedMonitor = null;
}
}
}, },
async created() { async created() {
this.hasToken = ("token" in localStorage); this.hasToken = ("token" in localStorage);
@ -126,14 +149,12 @@ export default {
return `${monitor.name}`; return `${monitor.name}`;
}, },
onSelectedMonitor(selectedOption, id) { addGroup() {
console.log(selectedOption); this.$root.publicGroupList.push({
console.log(id); name: "Untitled Group",
monitorList: [],
// TODO: emit socket cmd to set it public })
// TODO: Reload monitor or add to list
} }
}, },
} }
</script> </script>
@ -164,4 +185,9 @@ footer {
text-align: center; text-align: center;
font-size: 14px; font-size: 14px;
} }
.btn-add-group {
height: 100%;
width: 100%;
}
</style> </style>