Minor js riorganization and dev setup enhancements
This commit is contained in:
+7
-9
@@ -5,8 +5,6 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Posti DM</title>
|
||||
|
||||
<script type="module" src="./src/index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
@@ -15,18 +13,18 @@
|
||||
</div>
|
||||
<div class="nav-group center">
|
||||
<div class="nav-item">
|
||||
<a href=".">Posti DM</a>
|
||||
<a href="/">Posti DM</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav-group right">
|
||||
<div id="login-label" class="nav-item">
|
||||
<a href="./login.html">Login</a>
|
||||
<div id="nav-user" class="nav-group right">
|
||||
<div class="nav-item login-label">
|
||||
<a href="/login">Login</a>
|
||||
</div>
|
||||
<div id="logged-label" class="nav-item hidden">
|
||||
<div class="nav-item hidden logged-label">
|
||||
<!-- @username -->
|
||||
</div>
|
||||
<div id="logout-label" class="nav-item hidden">
|
||||
<button id="logout-button">Logout</button>
|
||||
<div class="nav-item hidden logout-label">
|
||||
<button class="logout-button">Logout</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
+2
-4
@@ -5,8 +5,6 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Posti DM</title>
|
||||
|
||||
<script type="module" src="./src/index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
@@ -15,7 +13,7 @@
|
||||
</div>
|
||||
<div class="nav-group center">
|
||||
<div class="nav-item">
|
||||
<a href=".">Posti DM</a>
|
||||
<a href="/">Posti DM</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -32,6 +30,6 @@
|
||||
<button id="button-login" class="fill-row gap-above-l">Login</button>
|
||||
</div>
|
||||
</main>
|
||||
<script type="module" src="./src/pages/login.js"></script>
|
||||
<script type="module" src="../src/pages/login.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Posti DM</title>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<div class="nav-group left">
|
||||
<div class="nav-item" id="clock">14:23</div>
|
||||
</div>
|
||||
<div class="nav-group center">
|
||||
<div class="nav-item">
|
||||
<a href="/">Posti DM</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav-group right">
|
||||
<div id="login-label" class="nav-item">
|
||||
<a href="/login">Login</a>
|
||||
</div>
|
||||
<div id="logged-label" class="nav-item hidden">
|
||||
<!-- @username -->
|
||||
</div>
|
||||
<div id="logout-label" class="nav-item hidden">
|
||||
<button id="logout-button">Logout</button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<main></main>
|
||||
<script type="module" src="../src/pages/orari.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,103 @@
|
||||
import './style.scss'
|
||||
|
||||
/**
|
||||
* `BASE_URL` string from environment file without final slash (for more readable interpolated strings)
|
||||
*/
|
||||
export const BASE_URL = import.meta.env.BASE_URL.replace(/\/$/, '')
|
||||
|
||||
const NotCached = Symbol('not cached')
|
||||
|
||||
export const User = {
|
||||
_user: NotCached,
|
||||
|
||||
async getLogged() {
|
||||
if (User._user === NotCached) {
|
||||
console.log('Caching user data...')
|
||||
|
||||
const res = await fetch(`${BASE_URL}/api/user`)
|
||||
const user = await res.json()
|
||||
|
||||
User._user = user
|
||||
}
|
||||
|
||||
return User._user
|
||||
},
|
||||
async login(username, password) {
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/api/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ username, password }),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
return { error: await response.text() }
|
||||
}
|
||||
|
||||
// If successful redirect to homepage
|
||||
location.href = `${BASE_URL}/`
|
||||
} catch (e) {
|
||||
return { error: e }
|
||||
}
|
||||
},
|
||||
async logout() {
|
||||
await fetch(`${BASE_URL}/api/logout`, { method: 'POST' })
|
||||
location.href = `${BASE_URL}/`
|
||||
},
|
||||
}
|
||||
|
||||
export function createRoomEventStream(roomId) {
|
||||
return new EventSource(`${BASE_URL}/api/room_events?id=${roomId}`)
|
||||
}
|
||||
|
||||
export const Database = {
|
||||
async getSeats(roomId) {
|
||||
const seatList = await (await fetch(`${BASE_URL}/api/room/seats?id=${roomId}`)).json()
|
||||
const seats = {}
|
||||
|
||||
seatList.forEach(seat => {
|
||||
seats[seat.id] = seat
|
||||
})
|
||||
|
||||
return seats
|
||||
},
|
||||
async occupySeat(seatId) {
|
||||
const response = await fetch(`${BASE_URL}/api/seat/occupy?id=${seatId}`, {
|
||||
method: 'POST',
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(await response.text())
|
||||
}
|
||||
|
||||
await response.json()
|
||||
},
|
||||
async leaveSeat(seatId) {
|
||||
const response = await fetch(`${BASE_URL}/api/seat/leave?id=${seatId}`, {
|
||||
method: 'POST',
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(await response.text())
|
||||
}
|
||||
|
||||
await response.json()
|
||||
},
|
||||
}
|
||||
|
||||
// Always append BASE_URL to all <a> links in the page.
|
||||
document.querySelectorAll('a').forEach($a => {
|
||||
const url = $a.getAttribute('href')
|
||||
let newUrl = BASE_URL + url
|
||||
|
||||
if (import.meta.env.MODE === 'development') {
|
||||
if (newUrl.endsWith('/')) newUrl += 'index'
|
||||
|
||||
newUrl += '.html'
|
||||
}
|
||||
|
||||
$a.href = newUrl
|
||||
})
|
||||
@@ -47,5 +47,5 @@ export function createGridLineCanvas($roomGrid) {
|
||||
const render = () => renderGridLinesCanvas($canvas, [rows, cols])
|
||||
|
||||
window.addEventListener('resize', render)
|
||||
render()
|
||||
requestAnimationFrame(render)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { User } from '../common.js'
|
||||
|
||||
function elements(el) {
|
||||
return {
|
||||
elLoggedLabel: el.querySelector('.logged-label'),
|
||||
elLoginLabel: el.querySelector('.login-label'),
|
||||
elLogoutLabel: el.querySelector('.logout-label'),
|
||||
elLogoutButton: el.querySelector('.logout-button'),
|
||||
}
|
||||
}
|
||||
|
||||
function setup(el) {
|
||||
const { elLogoutButton } = elements(el)
|
||||
|
||||
elLogoutButton.addEventListener('click', () => User.logout())
|
||||
}
|
||||
|
||||
function update(el, { user }) {
|
||||
const { elLoggedLabel, elLoginLabel, elLogoutLabel } = elements(el)
|
||||
|
||||
elLoginLabel.classList.toggle('hidden', user)
|
||||
|
||||
const roleString =
|
||||
user && user.permissions.length > 0 ? ` (${user.permissions.join(', ')})` : ''
|
||||
|
||||
elLoggedLabel.innerText = user ? `@${user.id}${roleString}` : ''
|
||||
|
||||
elLoggedLabel.classList.toggle('hidden', !user)
|
||||
elLogoutLabel.classList.toggle('hidden', !user)
|
||||
}
|
||||
|
||||
export function createNavUser(el) {
|
||||
setup(el)
|
||||
|
||||
// Load current user
|
||||
;(async () => {
|
||||
const user = await User.getLogged()
|
||||
update(el, { user })
|
||||
})()
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { BASE_URL, createRoomEventStream, Database, getLoggedUser } from '../index.js'
|
||||
import { addTooltipElementListener, resetTooltip, setTooltipText } from './tooltip.js'
|
||||
import { BASE_URL, createRoomEventStream, Database, User } from '../common'
|
||||
import { addTooltipElementListener, setTooltipText } from './tooltip'
|
||||
|
||||
async function renderWidget(elSeatMap, seats) {
|
||||
const user = await getLoggedUser()
|
||||
const user = await User.getLogged()
|
||||
|
||||
Object.values(seats).forEach(seat => {
|
||||
const { id, occupiedBy } = seat
|
||||
@@ -26,7 +26,7 @@ async function renderWidget(elSeatMap, seats) {
|
||||
}
|
||||
|
||||
export async function createSeatWidget($roomGrid, roomId) {
|
||||
const user = await getLoggedUser()
|
||||
const user = await User.getLogged()
|
||||
|
||||
const elSeats = [...$roomGrid.querySelectorAll('[data-seat-id]')]
|
||||
const elSeatMap = {}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import './style.scss'
|
||||
|
||||
export const BASE_URL = import.meta.env.BASE_URL.replace(/\/$/, '')
|
||||
|
||||
let USER = false
|
||||
export async function getLoggedUser() {
|
||||
if (USER === false) {
|
||||
console.log('Caching user data...')
|
||||
USER = await (await fetch(`${BASE_URL}/api/user`)).json()
|
||||
}
|
||||
|
||||
return USER
|
||||
}
|
||||
|
||||
export function createRoomEventStream(roomId) {
|
||||
return new EventSource(`${BASE_URL}/api/room_events?id=${roomId}`)
|
||||
}
|
||||
|
||||
export const Database = {
|
||||
async getSeats(roomId) {
|
||||
const seatList = await (await fetch(`${BASE_URL}/api/room/seats?id=${roomId}`)).json()
|
||||
const seats = {}
|
||||
|
||||
seatList.forEach(seat => {
|
||||
seats[seat.id] = seat
|
||||
})
|
||||
|
||||
return seats
|
||||
},
|
||||
async occupySeat(seatId) {
|
||||
const response = await fetch(`${BASE_URL}/api/seat/occupy?id=${seatId}`, {
|
||||
method: 'POST',
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(await response.text())
|
||||
}
|
||||
|
||||
await response.json()
|
||||
},
|
||||
async leaveSeat(seatId) {
|
||||
const response = await fetch(`${BASE_URL}/api/seat/leave?id=${seatId}`, {
|
||||
method: 'POST',
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(await response.text())
|
||||
}
|
||||
|
||||
await response.json()
|
||||
},
|
||||
}
|
||||
+11
-29
@@ -1,48 +1,30 @@
|
||||
import { createGridLineCanvas } from '../components/gridlines.js'
|
||||
import { BASE_URL, getLoggedUser } from '../index.js'
|
||||
import { createSeatWidget } from '../components/seats-widget.js'
|
||||
import { createClock } from '../components/clock.js'
|
||||
import { attachTooltip } from '../components/tooltip.js'
|
||||
import '../common'
|
||||
|
||||
import { createGridLineCanvas } from '../components/grid-lines'
|
||||
import { BASE_URL } from '../common'
|
||||
import { createSeatWidget } from '../components/seats-widget'
|
||||
import { createClock } from '../components/clock'
|
||||
import { attachTooltip } from '../components/tooltip'
|
||||
import { createNavUser } from '../components/nav-user.js'
|
||||
|
||||
const elClock = document.querySelector('#clock')
|
||||
|
||||
const elLoggedLabel = document.querySelector('#logged-label')
|
||||
const elLoginLabel = document.querySelector('#login-label')
|
||||
const elLogoutLabel = document.querySelector('#logout-label')
|
||||
|
||||
const elLogoutButton = document.querySelector('#logout-button')
|
||||
const elNavUser = document.querySelector('#nav-user')
|
||||
|
||||
const elRoomGrid = document.querySelector('.room-grid')
|
||||
|
||||
async function logout() {
|
||||
await fetch(`${BASE_URL}/api/logout`, { method: 'POST' })
|
||||
location.href = `${BASE_URL}`
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const urlSearchParams = new URLSearchParams(window.location.search)
|
||||
const params = Object.fromEntries(urlSearchParams.entries())
|
||||
console.log(params)
|
||||
|
||||
elLogoutButton.addEventListener('click', () => logout())
|
||||
|
||||
const user = await getLoggedUser()
|
||||
|
||||
if (user) {
|
||||
elLoginLabel.classList.add('hidden')
|
||||
|
||||
elLoggedLabel.innerText =
|
||||
'@' + user.id + (user.permissions.length > 0 ? ` (${user.permissions.join(', ')})` : '')
|
||||
elLoggedLabel.classList.remove('hidden')
|
||||
|
||||
elLogoutLabel.classList.remove('hidden')
|
||||
}
|
||||
|
||||
// Widgets
|
||||
// createClock(elClock)
|
||||
createGridLineCanvas(elRoomGrid)
|
||||
createSeatWidget(elRoomGrid, 'aula-stud')
|
||||
|
||||
createNavUser(elNavUser)
|
||||
|
||||
// Use tooltips only on desktop
|
||||
if (matchMedia('(pointer: fine)').matches) {
|
||||
attachTooltip()
|
||||
|
||||
+13
-29
@@ -1,4 +1,6 @@
|
||||
import { BASE_URL, getLoggedUser } from '../index.js'
|
||||
import '../common'
|
||||
|
||||
import { BASE_URL, User } from '../common'
|
||||
|
||||
//
|
||||
// Page Element
|
||||
@@ -16,8 +18,7 @@ const elLoginButton = document.querySelector('#button-login')
|
||||
//
|
||||
|
||||
let errorTimeoutHandle = null
|
||||
|
||||
function displayErrorString(e) {
|
||||
function displayFormErrorMessage(e) {
|
||||
if (errorTimeoutHandle) clearTimeout(errorTimeoutHandle)
|
||||
elErrorString.classList.toggle('hidden', false)
|
||||
elErrorString.innerText = e.toString()
|
||||
@@ -31,28 +32,10 @@ function displayErrorString(e) {
|
||||
// Handle Login Button
|
||||
//
|
||||
|
||||
async function login() {
|
||||
try {
|
||||
const response = await fetch(`${BASE_URL}/api/login`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: elLoginUsernameInput.value,
|
||||
password: elLoginPasswordInput.value,
|
||||
}),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
displayErrorString(await response.text())
|
||||
return
|
||||
}
|
||||
|
||||
location.href = `${BASE_URL}/`
|
||||
} catch (e) {
|
||||
displayErrorString(e)
|
||||
async function onLoginButtonClick() {
|
||||
const res = await User.login(elLoginUsernameInput.value, elLoginPasswordInput.value)
|
||||
if (res.error) {
|
||||
displayFormErrorMessage(res.error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,16 +44,17 @@ async function login() {
|
||||
//
|
||||
|
||||
async function main() {
|
||||
const user = await getLoggedUser()
|
||||
console.log(user)
|
||||
const user = await User.getLogged()
|
||||
if (user) {
|
||||
location.href = `${BASE_URL}/`
|
||||
}
|
||||
|
||||
elLoginButton.addEventListener('click', () => login())
|
||||
elLoginButton.addEventListener('click', () => {
|
||||
onLoginButtonClick()
|
||||
})
|
||||
|
||||
elLoginPasswordInput.addEventListener('keydown', e => {
|
||||
if (e.key === 'Enter') login()
|
||||
if (e.key === 'Enter') onLoginButtonClick()
|
||||
})
|
||||
|
||||
elLoginUsernameInput.focus()
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import '../common'
|
||||
@@ -2,6 +2,7 @@ import { defineConfig, loadEnv } from 'vite'
|
||||
import { resolve } from 'path'
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
// Load environment variables with no prefixes
|
||||
process.env = { ...process.env, ...loadEnv(mode, process.cwd(), '') }
|
||||
|
||||
console.log(`BASE_URL = "${process.env.BASE_URL}"`)
|
||||
@@ -12,7 +13,7 @@ export default defineConfig(({ mode }) => {
|
||||
rollupOptions: {
|
||||
input: {
|
||||
main: resolve(__dirname, 'index.html'),
|
||||
login: resolve(__dirname, 'login.html'),
|
||||
login: resolve(__dirname, 'login/index.html'),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user