summaryrefslogtreecommitdiffstats
path: root/src/components/MonitorListItem.vue
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/MonitorListItem.vue')
-rw-r--r--src/components/MonitorListItem.vue256
1 files changed, 256 insertions, 0 deletions
diff --git a/src/components/MonitorListItem.vue b/src/components/MonitorListItem.vue
new file mode 100644
index 0000000..74ba483
--- /dev/null
+++ b/src/components/MonitorListItem.vue
@@ -0,0 +1,256 @@
+<template>
+ <div>
+ <div :style="depthMargin">
+ <!-- Checkbox -->
+ <div v-if="isSelectMode" class="select-input-wrapper">
+ <input
+ class="form-check-input select-input"
+ type="checkbox"
+ :aria-label="$t('Check/Uncheck')"
+ :checked="isSelected(monitor.id)"
+ @click.stop="toggleSelection"
+ />
+ </div>
+
+ <router-link :to="monitorURL(monitor.id)" class="item" :class="{ 'disabled': ! monitor.active }">
+ <div class="row">
+ <div class="col-9 col-md-8 small-padding" :class="{ 'monitor-item': $root.userHeartbeatBar == 'bottom' || $root.userHeartbeatBar == 'none' }">
+ <div class="info">
+ <Uptime :monitor="monitor" type="24" :pill="true" />
+ <span v-if="hasChildren" class="collapse-padding" @click.prevent="changeCollapsed">
+ <font-awesome-icon icon="chevron-down" class="animated" :class="{ collapsed: isCollapsed}" />
+ </span>
+ {{ monitor.name }}
+ </div>
+ <div v-if="monitor.tags.length > 0" class="tags">
+ <Tag v-for="tag in monitor.tags" :key="tag" :item="tag" :size="'sm'" />
+ </div>
+ </div>
+ <div v-show="$root.userHeartbeatBar == 'normal'" :key="$root.userHeartbeatBar" class="col-3 col-md-4">
+ <HeartbeatBar ref="heartbeatBar" size="small" :monitor-id="monitor.id" />
+ </div>
+ </div>
+
+ <div v-if="$root.userHeartbeatBar == 'bottom'" class="row">
+ <div class="col-12 bottom-style">
+ <HeartbeatBar ref="heartbeatBar" size="small" :monitor-id="monitor.id" />
+ </div>
+ </div>
+ </router-link>
+ </div>
+
+ <transition name="slide-fade-up">
+ <div v-if="!isCollapsed" class="childs">
+ <MonitorListItem
+ v-for="(item, index) in sortedChildMonitorList"
+ :key="index"
+ :monitor="item"
+ :isSelectMode="isSelectMode"
+ :isSelected="isSelected"
+ :select="select"
+ :deselect="deselect"
+ :depth="depth + 1"
+ :filter-func="filterFunc"
+ :sort-func="sortFunc"
+ />
+ </div>
+ </transition>
+ </div>
+</template>
+
+<script>
+import HeartbeatBar from "../components/HeartbeatBar.vue";
+import Tag from "../components/Tag.vue";
+import Uptime from "../components/Uptime.vue";
+import { getMonitorRelativeURL } from "../util.ts";
+
+export default {
+ name: "MonitorListItem",
+ components: {
+ Uptime,
+ HeartbeatBar,
+ Tag,
+ },
+ props: {
+ /** Monitor this represents */
+ monitor: {
+ type: Object,
+ default: null,
+ },
+ /** If the user is in select mode */
+ isSelectMode: {
+ type: Boolean,
+ default: false,
+ },
+ /** How many ancestors are above this monitor */
+ depth: {
+ type: Number,
+ default: 0,
+ },
+ /** Callback to determine if monitor is selected */
+ isSelected: {
+ type: Function,
+ default: () => {}
+ },
+ /** Callback fired when monitor is selected */
+ select: {
+ type: Function,
+ default: () => {}
+ },
+ /** Callback fired when monitor is deselected */
+ deselect: {
+ type: Function,
+ default: () => {}
+ },
+ /** Function to filter child monitors */
+ filterFunc: {
+ type: Function,
+ default: () => {}
+ },
+ /** Function to sort child monitors */
+ sortFunc: {
+ type: Function,
+ default: () => {},
+ }
+ },
+ data() {
+ return {
+ isCollapsed: true,
+ };
+ },
+ computed: {
+ sortedChildMonitorList() {
+ let result = Object.values(this.$root.monitorList);
+
+ // Get children
+ result = result.filter(childMonitor => childMonitor.parent === this.monitor.id);
+
+ // Run filter on children
+ result = result.filter(this.filterFunc);
+
+ result.sort(this.sortFunc);
+
+ return result;
+ },
+ hasChildren() {
+ return this.sortedChildMonitorList.length > 0;
+ },
+ depthMargin() {
+ return {
+ marginLeft: `${31 * this.depth}px`,
+ };
+ },
+ },
+ watch: {
+ isSelectMode() {
+ // TODO: Resize the heartbeat bar, but too slow
+ // this.$refs.heartbeatBar.resize();
+ }
+ },
+ beforeMount() {
+
+ // Always unfold if monitor is accessed directly
+ if (this.monitor.childrenIDs.includes(parseInt(this.$route.params.id))) {
+ this.isCollapsed = false;
+ return;
+ }
+
+ // Set collapsed value based on local storage
+ let storage = window.localStorage.getItem("monitorCollapsed");
+ if (storage === null) {
+ return;
+ }
+
+ let storageObject = JSON.parse(storage);
+ if (storageObject[`monitor_${this.monitor.id}`] == null) {
+ return;
+ }
+
+ this.isCollapsed = storageObject[`monitor_${this.monitor.id}`];
+ },
+ methods: {
+ /**
+ * Changes the collapsed value of the current monitor and saves
+ * it to local storage
+ * @returns {void}
+ */
+ changeCollapsed() {
+ this.isCollapsed = !this.isCollapsed;
+
+ // Save collapsed value into local storage
+ let storage = window.localStorage.getItem("monitorCollapsed");
+ let storageObject = {};
+ if (storage !== null) {
+ storageObject = JSON.parse(storage);
+ }
+ storageObject[`monitor_${this.monitor.id}`] = this.isCollapsed;
+
+ window.localStorage.setItem("monitorCollapsed", JSON.stringify(storageObject));
+ },
+ /**
+ * Get URL of monitor
+ * @param {number} id ID of monitor
+ * @returns {string} Relative URL of monitor
+ */
+ monitorURL(id) {
+ return getMonitorRelativeURL(id);
+ },
+ /**
+ * Toggle selection of monitor
+ * @returns {void}
+ */
+ toggleSelection() {
+ if (this.isSelected(this.monitor.id)) {
+ this.deselect(this.monitor.id);
+ } else {
+ this.select(this.monitor.id);
+ }
+ },
+ },
+};
+</script>
+
+<style lang="scss" scoped>
+@import "../assets/vars.scss";
+
+.small-padding {
+ padding-left: 5px !important;
+ padding-right: 5px !important;
+}
+
+.collapse-padding {
+ padding-left: 8px !important;
+ padding-right: 2px !important;
+}
+
+// .monitor-item {
+// width: 100%;
+// }
+
+.tags {
+ margin-top: 4px;
+ padding-left: 67px;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0;
+}
+
+.collapsed {
+ transform: rotate(-90deg);
+}
+
+.animated {
+ transition: all 0.2s $easing-in;
+}
+
+.select-input-wrapper {
+ float: left;
+ margin-top: 15px;
+ margin-left: 3px;
+ margin-right: 10px;
+ padding-left: 4px;
+ position: relative;
+ z-index: 15;
+}
+
+</style>