Added changeable stops & improved web GUI

This commit is contained in:
Filip Znachor 2022-12-20 17:25:59 +01:00
parent ac129a77fa
commit 2f982dd29a
8 changed files with 52 additions and 20 deletions

View file

@ -70,6 +70,15 @@ class API:
d.clear()
return {'success': True}
@route('/admin/devices/change_stop', method='POST')
def admin_devices_change_stop():
if main.config["admin"]["secret"] != request.forms.get('secret'):
return {'error': "unauthorized"}
for i, d in enumerate(main.controller.devices):
if str(i) == request.forms.get('index'):
d.set_stop(request.forms.get('stop_id'))
return {'success': True}
@error(404)
def error404(err):
return ''

View file

@ -32,7 +32,7 @@ class Departure:
remove = []
for did in Departure.storage:
d = Departure.storage[did]
if d.get_departure() < -3.5:
if d.get_departure() < -1:
Departure.id_pool[d.stop_id].remove(d.id)
remove.append(did)
for did in remove:

View file

@ -54,6 +54,10 @@ class LoraDevice:
self.port = 1
self.clear()
def set_stop(self, stop_id: str):
self.clear()
self.stop_id = stop_id
def get_updated_departures(self):
self.send_time()
for d in Departure.get(self.stop_id):

View file

@ -1,12 +1,15 @@
.devices {display: flex; flex-direction: column; gap: 10px;}
.devices > * {background-color: var(--alt-bg); border-radius: 8px; box-shadow: 0 2px 10px 0 #0005; border: 1px solid var(--border-color); overflow: hidden;}
.cards {display: flex; flex-direction: column; gap: 10px;}
.cards > * {background-color: var(--alt-bg); border-radius: 8px; box-shadow: 0 2px 10px 0 #0005; border: 1px solid var(--border-color); overflow: hidden;}
.devices .header {cursor: pointer; padding: 15px; display: grid; grid-template-columns: 1fr max-content; user-select: none;}
.devices .id {font-weight: 700;}
.devices .settings {padding: 15px; border-top: 1px solid var(--border-color2); background: var(--alt-bg2); display: none;}
.devices .settings.visible {display: block;}
.cards .header {padding: 15px; display: grid; grid-template-columns: 1fr max-content; user-select: none;}
.cards .first {font-weight: 700;}
.cards .settings {padding: 15px; border-top: 1px solid var(--border-color2); background: var(--alt-bg2); display: none;}
.cards .settings.visible {display: block;}
.devices .actions {display: flex; gap: 10px; flex-wrap: wrap; align-items: center;}
.devices .actions::before {content: "Rychlé akce:"; font-weight: 500; font-size: 90%; opacity: .5; text-transform: uppercase;}
.cards .actions {display: flex; gap: 10px; flex-wrap: wrap; align-items: center;}
.cards .actions::before {content: "Akce:"; font-weight: 500; font-size: 90%; opacity: .5; text-transform: uppercase;}
@media screen and (max-width: 500px) {
.cards .actions::before {display: none;}
}
.login form input {margin-bottom: 15px; display: block;}

View file

@ -11,20 +11,30 @@
<div class="container" id="app">
<div v-if="logged">
<h2>Zařízení</h2>
<div class="devices">
<div class="cards">
<div v-for="d, i in devices">
<div class="header" @click="d.visible = !d.visible">
<div class="id">{{ d.id }}</div>
<div class="stop">{{ stops[d.stop_id] ? stops[d.stop_id].name : "Nenastaveno" }}</div>
<div class="header clickable" @click="d.visible = !d.visible">
<div class="first">{{ d.id }}</div>
<div class="second">{{ stops[d.stop_id] ? stops[d.stop_id].name : "Nenastaveno" }}</div>
</div>
<div class="settings" :class="{ visible: d.visible }">
<div class="actions">
<button @click="clear(i)">CLEAR</button>
<button @click="resend(i)">RESEND</button>
<button @click="device_clear(i)">Vymazat</button>
<button @click="device_resend(i)">Přeposlat</button>
<button @click="device_change_stop(i)">Změnit zastávku</button>
</div>
</div>
</div>
</div>
<h2>Zastávky</h2>
<div class="cards">
<div v-for="s, id in stops">
<div class="header">
<div class="first">{{ s.name }}</div>
<div class="second">{{ id }}</div>
</div>
</div>
</div>
</div>
<div v-else class="login">
<h2>Přihlašte se</h2>

View file

@ -23,11 +23,17 @@ let app = createApp({
this.stops = stops.stops;
this.logged = true;
},
async clear(index) {
async device_clear(index) {
await api("/admin/devices/clear", {index, secret: this.secret});
},
async resend(index) {
async device_resend(index) {
await api("/admin/devices/resend", {index, secret: this.secret});
},
async device_change_stop(index) {
let stop_id = prompt("Zadejte ID zastávky:");
if(!stop_id || stop_id.trim() == "") return;
this.devices[index].stop_id = stop_id;
await api("/admin/devices/change_stop", {index, stop_id, secret: this.secret});
}
}
}).mount('#app');

View file

@ -21,7 +21,7 @@
<option v-for="stop, id in stops" :value="id">{{ stop.name }}</option>
</select>
</div>
<div :title="filter ? 'Zobrazit všechny' : 'Zobrazit pouze nejbližší'" class="filter-toggle" @click="filter = !filter">
<div :title="filter ? 'Zobrazit všechny' : 'Zobrazit pouze nejbližší'" class="clickable" @click="filter = !filter">
<svg class="icon" v-if="filter" viewBox="0 0 24 24">
<path d="M15,19.88C15.04,20.18 14.94,20.5 14.71,20.71C14.32,21.1 13.69,21.1 13.3,20.71L9.29,16.7C9.06,16.47 8.96,16.16 9,15.87V10.75L4.21,4.62C3.87,4.19 3.95,3.56 4.38,3.22C4.57,3.08 4.78,3 5,3V3H19V3C19.22,3 19.43,3.08 19.62,3.22C20.05,3.56 20.13,4.19 19.79,4.62L15,10.75V19.88M7.04,5L11,10.06V15.58L13,17.58V10.05L16.96,5H7.04Z" />
</svg>

View file

@ -3,7 +3,7 @@
--alt-bg: #303030;
--alt-bg2: #242424;
--button-bg: #454545;
--button-hover-bg: #3E3E3E;
--button-hover-bg: #505050;
--border-color: #3E3E3E;
--border-color2: #1f1f1f;
}
@ -29,7 +29,7 @@ body {background-color: var(--site-bg); color: #fffd; margin: 0; margin-top: 35p
.icon {fill: #fff; width: 24px; height: 24px;}
.icon.icon-dropdown {fill: #fff5;}
.filter-toggle {cursor: pointer;}
.clickable {cursor: pointer;}
.nav {position: fixed; top: 0; left: 0; width: 100%; z-index: 10;}
.nav .inner {background: linear-gradient(to bottom, var(--site-bg) 70%, transparent 100%); padding: 20px 25px; margin: auto; max-width: 700px; padding-bottom: 30px; transition: padding .1s; display: grid; grid-template-columns: 1fr max-content; gap: 15px; align-items: center;}