Compare commits
8 commits
Author | SHA1 | Date | |
---|---|---|---|
|
4091020069 | ||
|
c569423e65 | ||
|
807de742a0 | ||
|
b2b8f18bf3 | ||
|
88f6644518 | ||
|
9413862186 | ||
|
a0dc1d5715 | ||
|
c7c69a8c90 |
9 changed files with 1730 additions and 0 deletions
1
pizzicore/.gitignore
vendored
1
pizzicore/.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
pizzicore.env
|
||||
.*.sw[po]
|
||||
*.dbm.db
|
||||
|
|
23
pizzicore/pages/spa.html
Normal file
23
pizzicore/pages/spa.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Eliminacode</title>
|
||||
<style>
|
||||
.counter {
|
||||
border: 1px solid #ccc;
|
||||
margin: 1em 0;
|
||||
padding: 1em 0.2em;
|
||||
font-size: 8vw;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body data-cid="0">
|
||||
<div id="app">
|
||||
</div>
|
||||
<script type="module" src="/static/js/spa.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
@ -214,3 +214,7 @@ async def root_page():
|
|||
@app.get("/prenota")
|
||||
async def prenota_page():
|
||||
return await get_page("pages/prenotati.html")
|
||||
|
||||
@app.get("/new")
|
||||
async def prenota_page():
|
||||
return await get_page("pages/spa.html")
|
||||
|
|
41
pizzicore/static/js/spa.js
Normal file
41
pizzicore/static/js/spa.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { createApp, defineAsyncComponent } from '/static/js/vue.esm-browser.min.js'
|
||||
import { createStore } from '/static/js/vuex.esm-browser.js'
|
||||
|
||||
|
||||
|
||||
// Create a new store instance.
|
||||
const store = createStore({
|
||||
state () {
|
||||
return {
|
||||
loggedIn: false,
|
||||
username: '',
|
||||
password: '',
|
||||
}
|
||||
},
|
||||
mutations: {
|
||||
logIn(state, payload) {
|
||||
state.loggedIn = true
|
||||
state.username = payload.username
|
||||
state.password = payload.password
|
||||
},
|
||||
logOut(state) {
|
||||
state.loggedIn = false
|
||||
}
|
||||
},
|
||||
|
||||
devtools: false,
|
||||
|
||||
})
|
||||
|
||||
var app = createApp({
|
||||
components: {
|
||||
'CounterList': defineAsyncComponent( () => import('/static/vue/counterlist.js') ),
|
||||
'UserLogin': defineAsyncComponent( () => import('/static/vue/userlogin.js') ),
|
||||
},
|
||||
|
||||
template: ` <CounterList></CounterList>
|
||||
<UserLogin></UserLogin>
|
||||
`
|
||||
})
|
||||
app.use(store)
|
||||
app.mount('#app')
|
15
pizzicore/static/js/vue.esm-browser.min.js
vendored
Normal file
15
pizzicore/static/js/vue.esm-browser.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1461
pizzicore/static/js/vuex.esm-browser.js
Normal file
1461
pizzicore/static/js/vuex.esm-browser.js
Normal file
File diff suppressed because it is too large
Load diff
81
pizzicore/static/vue/counter.js
Normal file
81
pizzicore/static/vue/counter.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
const template = `
|
||||
<div class="counter">Fila {{queue_desc}}: <div class="counter-number">{{counter}}</div>
|
||||
<div v-if="loggedIn"><button @click="callNext">Prossimo</button></div></div>
|
||||
`
|
||||
|
||||
|
||||
export default {
|
||||
props: ["queue_num"],
|
||||
data() {
|
||||
var me = this;
|
||||
do_websocket(parseInt(me.queue_num, 10), function(new_value) {
|
||||
me.counter = new_value
|
||||
})
|
||||
return { counter: -1 }
|
||||
},
|
||||
template: template,
|
||||
computed: {
|
||||
queue_desc: function() {
|
||||
return printToLetter(parseInt(this.queue_num, 10) + 1)
|
||||
},
|
||||
|
||||
loggedIn() {
|
||||
return this.$store.state.loggedIn
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
callNext() {
|
||||
let headers = new Headers()
|
||||
let username = this.$store.state.username
|
||||
let password = this.$store.state.password
|
||||
headers.set('Authorization', 'Basic ' + btoa(username + ":" + password))
|
||||
fetch('/v1/counter/' + this.queue_num + '/increment', {method: 'POST', headers: headers})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function get_url(cid)
|
||||
{
|
||||
var url = "";
|
||||
if(window.location.protocol == "http:") {
|
||||
url += "ws://"
|
||||
} else {
|
||||
url = "wss://"
|
||||
}
|
||||
url += window.location.host + '/v1/ws/counter/';
|
||||
url += cid
|
||||
return url;
|
||||
}
|
||||
|
||||
function do_websocket(cid, cb_on_new_value)
|
||||
{
|
||||
const socket = new WebSocket(get_url(cid));
|
||||
socket.onmessage = function(evt) {
|
||||
var msg = JSON.parse(evt.data)
|
||||
cb_on_new_value(msg.value)
|
||||
}
|
||||
socket.onclose = function() {
|
||||
setTimeout(do_websocket, 3000)
|
||||
}
|
||||
socket.onerror = socket.onclose
|
||||
}
|
||||
|
||||
/* thanks, https://stackoverflow.com/a/60033403 */
|
||||
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
function printToLetter(number, recursive){
|
||||
number--
|
||||
if(number < 0) {
|
||||
if(recursive === true) {
|
||||
return alphabet[alphabet.length - 1]
|
||||
} else {
|
||||
return 'ø'
|
||||
}
|
||||
}
|
||||
if(number < alphabet.length) {
|
||||
return alphabet[number]
|
||||
}
|
||||
var remainder = number % alphabet.length
|
||||
var quotient = Math.floor((number-1)/alphabet.length)
|
||||
return printToLetter(quotient, true) + printToLetter(remainder, true)
|
||||
}
|
52
pizzicore/static/vue/counterlist.js
Normal file
52
pizzicore/static/vue/counterlist.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
import { defineAsyncComponent } from '/static/js/vue.esm-browser.min.js'
|
||||
|
||||
const template = `
|
||||
<div class="counters">
|
||||
<CounterController v-for="q in queues" :queue_num="q"></CounterController>
|
||||
</div>
|
||||
`
|
||||
|
||||
export default {
|
||||
template: template,
|
||||
|
||||
components: {
|
||||
'CounterController': defineAsyncComponent( () => import('/static/vue/counter.js') ),
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
queue_count: 0,
|
||||
}
|
||||
},
|
||||
|
||||
mounted: function () {
|
||||
this.updateGlobalProperties()
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateGlobalProperties() {
|
||||
var me = this
|
||||
fetch('/v1/counter/')
|
||||
.then((response) => response.json())
|
||||
.then(function(data) {
|
||||
me.queue_count = data.counters
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
queues() {
|
||||
return range(0, this.queue_count-1)
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
function range(start, end) {
|
||||
var ans = [];
|
||||
for (let i = start; i <= end; i++) {
|
||||
ans.push(i);
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
52
pizzicore/static/vue/userlogin.js
Normal file
52
pizzicore/static/vue/userlogin.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
const template = `
|
||||
<div v-if="!loggedIn">
|
||||
<input name="username" :value="username" />
|
||||
<input name="password" type="password" :value="password" />
|
||||
<button @click="checkLogin" >Login</button>
|
||||
</div>
|
||||
<div v-else>
|
||||
<button @click="logout">Logout</button>
|
||||
</div>
|
||||
`
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return { }
|
||||
},
|
||||
|
||||
computed: {
|
||||
loggedIn(){ return this.$store.state.loggedIn },
|
||||
username(){ return this.$store.state.username },
|
||||
password(){ return this.$store.state.password },
|
||||
},
|
||||
|
||||
template: template,
|
||||
|
||||
methods: {
|
||||
checkLogin: function() {
|
||||
var username = document.querySelector('input[name=username]', this.$el).value
|
||||
var password = document.querySelector('input[name=password]', this.$el).value
|
||||
let $store = this.$root.$store
|
||||
let headers = new Headers()
|
||||
headers.set('Authorization', 'Basic ' + btoa(username + ":" + password))
|
||||
fetch('/v1/whoami', {method: 'GET', headers: headers})
|
||||
.then(function(response) {
|
||||
if(response.ok) {
|
||||
console.info("Login OK")
|
||||
$store.commit({
|
||||
type: 'logIn',
|
||||
username: username,
|
||||
password: password
|
||||
})
|
||||
} else {
|
||||
console.warn("Login error")
|
||||
// XXX: animate some form of error
|
||||
}
|
||||
})
|
||||
},
|
||||
logout: function() {
|
||||
this.$root.$store.commit('logOut')
|
||||
},
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue