Introduction of JS based pages
This commit is contained in:
parent
c4cb9ff458
commit
7d64788154
24 changed files with 24446 additions and 30 deletions
39
hal-core/resources/web/js/vue/components/AlertComponent.js
vendored
Normal file
39
hal-core/resources/web/js/vue/components/AlertComponent.js
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
export default {
|
||||
props: [
|
||||
"level", // ERROR, WARNING, SUCCESS, INFO
|
||||
"message"
|
||||
],
|
||||
data() {
|
||||
return { }
|
||||
},
|
||||
computed: {
|
||||
levelClass() {
|
||||
switch(this.level) {
|
||||
case "ERROR": return "alert-danger";
|
||||
case "WARNING": return "alert-warning";
|
||||
case "SUCCESS": return "alert-success";
|
||||
case "INFO":
|
||||
default: return "alert-info";
|
||||
}
|
||||
},
|
||||
levelIcon() {
|
||||
switch(this.level) {
|
||||
case "ERROR": return "glyphicon-minus-sign";
|
||||
case "WARNING": return "glyphicon-warning-sign";
|
||||
case "SUCCESS": return "glyphicon-ok-circle";
|
||||
case "INFO":
|
||||
default: return "glyphicon-info-sign";
|
||||
}
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div id="" data-alert-id="" class="alert alert-dismissible fade in" :class="levelClass">
|
||||
<button type="button" class="close" data-dismiss="alert">
|
||||
<span>×</span>
|
||||
</button>
|
||||
<span class="glyphicon" :class="levelIcon"></span>
|
||||
<strong class="alert-title">{{message}}</strong>
|
||||
<span class="alert-description"></span>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
29
hal-core/resources/web/js/vue/components/AlertListComponent.js
vendored
Normal file
29
hal-core/resources/web/js/vue/components/AlertListComponent.js
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import Alert from 'AlertComponent'
|
||||
import { alertStore } from 'AlertStore'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
alertStore
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Alert,
|
||||
},
|
||||
mounted() {
|
||||
alertStore.enableAutoLoad(true);
|
||||
},
|
||||
template: `
|
||||
<div v-if="alertStore.loading" class="modal fade in" tabindex="-1" role="dialog" aria-hidden="true" data-backdrop="false">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content modal-body">
|
||||
<i class="glyphicon glyphicon-refresh glyphicon-refresh-animate"></i>
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="alert-container">
|
||||
<Alert v-for="a in alertStore.alerts" v-bind="a" />
|
||||
</div>`
|
||||
}
|
||||
17
hal-core/resources/web/js/vue/components/App.js
vendored
Normal file
17
hal-core/resources/web/js/vue/components/App.js
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import AlertList from 'AlertListComponent'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
components: {
|
||||
AlertList,
|
||||
},
|
||||
template: `
|
||||
<div class="container col-md-12">
|
||||
<AlertList />
|
||||
|
||||
<router-view></router-view>
|
||||
</div>`
|
||||
}
|
||||
35
hal-core/resources/web/js/vue/components/EventActionComponent.js
vendored
Normal file
35
hal-core/resources/web/js/vue/components/EventActionComponent.js
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import {eventStore} from 'EventStore'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
'id': {default: 0},
|
||||
},
|
||||
data() {
|
||||
let internalEvent = eventStore.getEvent(this.id);
|
||||
return {
|
||||
event: internalEvent,
|
||||
checkboxChecked: internalEvent.data?.valueStr == 'ON',
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<form method="POST">
|
||||
<input type="hidden" name="action" value="modify">
|
||||
<input type="hidden" name="action-id" :value="event.id">
|
||||
|
||||
<div class="btn-toolbar pull-right">
|
||||
<template v-if="event.dataType === 'ColorEventData'">
|
||||
<input type="hidden" name="type" value="color">
|
||||
<input type="color" name="data" onchange="this.form.submit()" :value="event.data?.valueStr">
|
||||
</template>
|
||||
<template v-else-if="event.dataType === 'LevelEventData'">
|
||||
<input type="hidden" name="type" value="level">
|
||||
<input class="styled-slider slider-progress" type="range" name="data" min="0" max="100" step="10" onchange="this.form.submit()" :value="event.data?.value * 100">
|
||||
</template>
|
||||
<template v-else-if="event.dataType === 'OnOffEventData'">
|
||||
<input type="hidden" name="type" value="on-off">
|
||||
<input class="switch" type="checkbox" name="data" onchange="this.form.submit()" v-model="checkboxChecked">
|
||||
</template>
|
||||
</div>
|
||||
</form>
|
||||
`
|
||||
}
|
||||
88
hal-core/resources/web/js/vue/components/EventDetailPageComponent.js
vendored
Normal file
88
hal-core/resources/web/js/vue/components/EventDetailPageComponent.js
vendored
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import {eventStore} from 'EventStore'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
var id = this.$route.params.id;
|
||||
return {
|
||||
id: id,
|
||||
event: eventStore.getEvent(id),
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<h1 class="page-header">Details for <a href="?id={{event.id}}">{{event.name}}</a></h1>
|
||||
|
||||
<div class="col-md-5">
|
||||
<div class="panel panel-default drop-shadow">
|
||||
<div class="panel-heading">Configuration</div>
|
||||
<div class="panel-body">
|
||||
<table class="table table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-right">Event ID:</th>
|
||||
<th>{{event.id}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right">Name:</th>
|
||||
<th>{{event.name}}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr>
|
||||
<th class="text-right">Type:</th>
|
||||
<td>{{event.configType}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right">Owner:</th>
|
||||
<td>{{event.owner}} <p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="text-right">State:</th>
|
||||
<td>
|
||||
<form method="POST">
|
||||
<input type="hidden" name="action" value="modify">
|
||||
<input type="hidden" name="action-id" value="{{event.id}}">
|
||||
|
||||
<div class="btn-toolbar pull-left">
|
||||
<input class="toggle-switch" type="checkbox" name="enabled"
|
||||
data-on-color="danger">
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr v-for="(confName, confValue) in event.config">
|
||||
<th class="text-right">{{name}}:</th>
|
||||
<td>{{confValue}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-7">
|
||||
<div class="panel panel-default drop-shadow">
|
||||
<div class="panel-heading">History data</div>
|
||||
<div class="panel-body">
|
||||
<table class="table table-hover table-condensed">
|
||||
<thead>
|
||||
<th class="col-md-6">Timestamp</th>
|
||||
<th class="col-md-2">Raw Data</th>
|
||||
</thead>
|
||||
|
||||
<tr>
|
||||
<td><span class="timestamp">{{event.data.timestamp}}</span></td>
|
||||
<td>{{event.data.value}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$(function (){
|
||||
$(".toggle-switch").on("switchChange.bootstrapSwitch", function (event, state) {
|
||||
$(this).closest('form').submit();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
`
|
||||
}
|
||||
21
hal-core/resources/web/js/vue/components/EventOverviewPageComponent.js
vendored
Normal file
21
hal-core/resources/web/js/vue/components/EventOverviewPageComponent.js
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import EventTable from 'EventTableComponent'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
},
|
||||
components: {
|
||||
EventTable,
|
||||
},
|
||||
template: `
|
||||
<h1 class="page-header">Event Overview</h1>
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default drop-shadow">
|
||||
<div class="panel-heading">Local Events</div>
|
||||
<div class="panel-body">
|
||||
<EventTable />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
34
hal-core/resources/web/js/vue/components/EventTableComponent.js
vendored
Normal file
34
hal-core/resources/web/js/vue/components/EventTableComponent.js
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import {eventStore} from 'EventStore'
|
||||
import EventTableRow from 'EventTableRowComponent'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
eventStore,
|
||||
}
|
||||
},
|
||||
components: {
|
||||
EventTableRow,
|
||||
},
|
||||
mounted() {
|
||||
eventStore.enableAutoLoad(true);
|
||||
},
|
||||
unmounted() {
|
||||
eventStore.enableAutoLoad(false);
|
||||
},
|
||||
template: `
|
||||
<table id="event-device-table2" class="table table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-md-4">Name</th>
|
||||
<th class="col-md-3">Type</th>
|
||||
<th class="col-md-1">Data</th>
|
||||
<th class="col-md-2">Last Update</th>
|
||||
<th class="col-md-2 text-right">Actions</th>
|
||||
</tr>
|
||||
|
||||
<EventTableRow v-for="e in eventStore.events" :key="e.id" :id="e.id" />
|
||||
</thead>
|
||||
</table>
|
||||
`
|
||||
}
|
||||
29
hal-core/resources/web/js/vue/components/EventTableRowComponent.js
vendored
Normal file
29
hal-core/resources/web/js/vue/components/EventTableRowComponent.js
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import {eventStore} from 'EventStore'
|
||||
import EventAction from 'EventActionComponent'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
'id': {default: 0},
|
||||
},
|
||||
data() {
|
||||
let event = eventStore.getEvent(this.id)
|
||||
return {
|
||||
event: event,
|
||||
timestamp: getRelTimestamp(event.data.timestamp)
|
||||
}
|
||||
},
|
||||
components: {
|
||||
EventAction
|
||||
},
|
||||
template: `
|
||||
<tr :data-device-id="event.id">
|
||||
<td><a :href="'?id=' + event.id">{{ event.name }}</a></td>
|
||||
<td>{{ event.configType }}</td>
|
||||
<td>{{ event.data.valueStr }}</td>
|
||||
<td class="timestamp">{{ timestamp }}</td>
|
||||
<td>
|
||||
<EventAction :id="event.id"/>
|
||||
</td>
|
||||
</tr>
|
||||
`
|
||||
}
|
||||
46
hal-core/resources/web/js/vue/stores/AlertStore.js
vendored
Normal file
46
hal-core/resources/web/js/vue/stores/AlertStore.js
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
import { reactive } from 'vue'
|
||||
|
||||
export const alertStore = reactive({
|
||||
loading: false,
|
||||
alerts: [],
|
||||
pollTimer: null,
|
||||
|
||||
enableAutoLoad(enabled = true) {
|
||||
if (enabled && this.pollTimer !== null) {
|
||||
this.pollTimer = setInterval(function() {
|
||||
load();
|
||||
}, 3000);
|
||||
} else {
|
||||
clearInterval(this.timer);
|
||||
this.pollTimer == null;
|
||||
}
|
||||
},
|
||||
|
||||
load() {
|
||||
fetch('/api/alert?action=poll')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
data.forEach(alert => {
|
||||
alert.source = "server";
|
||||
alertStore.alerts.push(alert);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
setLoading(l = true) {
|
||||
this.loading = l;
|
||||
},
|
||||
|
||||
addAlertInfo(message) {
|
||||
this.alerts.push({source: "local", level: "info", message: message});
|
||||
},
|
||||
addAlertSuccess(message) {
|
||||
this.alerts.push({source: "local", level: "success", message: message});
|
||||
},
|
||||
addAlertWarning(message) {
|
||||
this.alerts.push({source: "local", level: "warning", message: message});
|
||||
},
|
||||
AddAlertError(message) {
|
||||
this.alerts.push({source: "local", level: "danger", message: message});
|
||||
},
|
||||
});
|
||||
49
hal-core/resources/web/js/vue/stores/EventStore.js
vendored
Normal file
49
hal-core/resources/web/js/vue/stores/EventStore.js
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import { reactive } from 'vue'
|
||||
import { alertStore } from 'AlertStore'
|
||||
|
||||
export const eventStore = reactive({
|
||||
events: [],
|
||||
pollTimerId: null,
|
||||
pollInterval: 10000, // 10 sec
|
||||
|
||||
enableAutoLoad(enabled = true) {
|
||||
if (enabled) {
|
||||
if (this.pollTimerId !== null)
|
||||
return; // Timer already initialized
|
||||
eventStore.load();
|
||||
|
||||
this.pollTimerId = setInterval(function() {
|
||||
eventStore.load();
|
||||
}, this.pollInterval);
|
||||
} else {
|
||||
clearInterval(this.pollTimerId);
|
||||
this.pollTimerId == null;
|
||||
}
|
||||
},
|
||||
|
||||
load() {
|
||||
alertStore.setLoading(true);
|
||||
|
||||
fetch('/api/event', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
if (json['error'] != null) {
|
||||
alertStore.alertError(json['error']);
|
||||
return;
|
||||
}
|
||||
|
||||
eventStore.events = json;
|
||||
alertStore.setLoading(false);
|
||||
})
|
||||
},
|
||||
|
||||
getEvent(id) {
|
||||
let event = eventStore.events.find(event => id == event.id);
|
||||
return event;
|
||||
}
|
||||
});
|
||||
44
hal-core/resources/web/js/vue/stores/SensorStore.js
vendored
Normal file
44
hal-core/resources/web/js/vue/stores/SensorStore.js
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import { reactive } from 'vue'
|
||||
import { alertStore } from 'AlertStore'
|
||||
|
||||
export const sensorStore = reactive({
|
||||
sensors: {},
|
||||
pollTimerId: null,
|
||||
pollInterval: 10000, // 10 sec
|
||||
|
||||
enableAutoLoad(enabled = true) {
|
||||
if (enabled) {
|
||||
if (this.pollTimerId !== null)
|
||||
return; // Timer already initialized
|
||||
sensorStore.load();
|
||||
|
||||
this.pollTimerId = setInterval(function() {
|
||||
sensorStore.load();
|
||||
}, this.pollInterval);
|
||||
} else {
|
||||
clearInterval(this.pollTimerId);
|
||||
this.pollTimerId == null;
|
||||
}
|
||||
},
|
||||
|
||||
load() {
|
||||
alertStore.setLoading(true);
|
||||
|
||||
fetch('/api/sensor', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
if (json['error'] != null) {
|
||||
alertStore.alertError(json['error']);
|
||||
return;
|
||||
}
|
||||
|
||||
sensorStore.sensors = json;
|
||||
alertStore.setLoading(false);
|
||||
})
|
||||
},
|
||||
});
|
||||
3615
hal-core/resources/web/js/vue/vue-router.esm-browser.js
vendored
Normal file
3615
hal-core/resources/web/js/vue/vue-router.esm-browser.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
3615
hal-core/resources/web/js/vue/vue-router.esm-browser.prod.js
vendored
Normal file
3615
hal-core/resources/web/js/vue/vue-router.esm-browser.prod.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
16633
hal-core/resources/web/js/vue/vue.esm-browser.js
vendored
Normal file
16633
hal-core/resources/web/js/vue/vue.esm-browser.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
11
hal-core/resources/web/js/vue/vue.esm-browser.prod.js
vendored
Normal file
11
hal-core/resources/web/js/vue/vue.esm-browser.prod.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue