commit
bc413a39d6
31158
WebUI/data.json
31158
WebUI/data.json
File diff suppressed because it is too large
Load Diff
@ -10,8 +10,11 @@
|
|||||||
<script src="main.js" defer></script>
|
<script src="main.js" defer></script>
|
||||||
<script src="https://cdn.socket.io/socket.io-3.0.1.min.js"></script>
|
<script src="https://cdn.socket.io/socket.io-3.0.1.min.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body onload="checkUser(), checkMode(), getStartValues(), updateChart()">
|
<body onload="checkUser(), getStartValues(), updateChart()">
|
||||||
<header>
|
<header>
|
||||||
|
<form id="pre-reg" method="get" action="/register" style="margin-top: 10px; margin-right: 10px;">
|
||||||
|
<input id="reg" type="submit" value="Register new user">
|
||||||
|
</form>
|
||||||
<h1>ABB Ventilation Controller</h1>
|
<h1>ABB Ventilation Controller</h1>
|
||||||
</header>
|
</header>
|
||||||
<div class="logout">
|
<div class="logout">
|
||||||
@ -132,17 +135,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="user-table">
|
<div id="user-table">
|
||||||
<h2>User Log History</h2>
|
<h2>User Log History</h2>
|
||||||
<table>
|
<div id="log">
|
||||||
<thead>
|
<table>
|
||||||
<tr>
|
<thead>
|
||||||
<th>User</th>
|
<tr>
|
||||||
<th>Login time</th>
|
<th>User</th>
|
||||||
<th>Logout time</th>
|
<th>Login time</th>
|
||||||
</tr>
|
<th>Logout time</th>
|
||||||
</thead>
|
</tr>
|
||||||
<tbody id="table-output">
|
</thead>
|
||||||
</tbody>
|
<tbody id="table-output">
|
||||||
</table>
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
114
WebUI/index.js
114
WebUI/index.js
@ -60,6 +60,13 @@ function getTime(){
|
|||||||
return today;
|
return today;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isLogged = (req, res, next) =>{
|
||||||
|
if(!req.session.userId){
|
||||||
|
res.send(`<h1 style="text-align:center; margin-top:50px;">This page is only for registered users.</h1>`);
|
||||||
|
}
|
||||||
|
else next()
|
||||||
|
}
|
||||||
|
|
||||||
client.on('connect', ()=>{
|
client.on('connect', ()=>{
|
||||||
console.log('MQTT client connected: '+ client.connected);
|
console.log('MQTT client connected: '+ client.connected);
|
||||||
});
|
});
|
||||||
@ -94,22 +101,10 @@ client.on('message', async (topic, message) =>{
|
|||||||
io.emit('data', newData);
|
io.emit('data', newData);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
app.get('/', (req, res)=>{
|
app.get('/', (req, res)=>{
|
||||||
res.sendFile(path.join(__dirname + '/index.html'));
|
res.sendFile(path.join(__dirname + '/index.html'));
|
||||||
})
|
})
|
||||||
|
|
||||||
app.get('/register', (req,res)=>{
|
|
||||||
res.send(`
|
|
||||||
<h1>Register</h1>
|
|
||||||
<form method='post' action='/register' />
|
|
||||||
<input type='text' name='name' placeholder='Username' required />
|
|
||||||
<input type='password' name='password' placeholder='Password' required />
|
|
||||||
<input type='submit' />
|
|
||||||
</form>
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post('/', async (req,res)=>{
|
app.post('/', async (req,res)=>{
|
||||||
let pwd;
|
let pwd;
|
||||||
const { username, password } = req.body;
|
const { username, password } = req.body;
|
||||||
@ -129,7 +124,6 @@ app.post('/', async (req,res)=>{
|
|||||||
let y = now.slice(0,4);
|
let y = now.slice(0,4);
|
||||||
let t = now.slice(11, 19);
|
let t = now.slice(11, 19);
|
||||||
let stamp = d + '.' + m + '.' + y + ' ' + t;
|
let stamp = d + '.' + m + '.' + y + ' ' + t;
|
||||||
//console.log(stamp);
|
|
||||||
req.session.userId = username;
|
req.session.userId = username;
|
||||||
req.session.startTime = stamp;
|
req.session.startTime = stamp;
|
||||||
console.log('Password is correct');
|
console.log('Password is correct');
|
||||||
@ -145,7 +139,52 @@ app.post('/', async (req,res)=>{
|
|||||||
console.log('Wrong password');
|
console.log('Wrong password');
|
||||||
res.status(205);
|
res.status(205);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
|
app.post('/logout', async (req, res) =>{
|
||||||
|
let now = getTime();
|
||||||
|
let d = now.slice(8,10);
|
||||||
|
let m = now.slice(5,7);
|
||||||
|
let y = now.slice(0,4);
|
||||||
|
let t = now.slice(11, 19);
|
||||||
|
let stamp = d + '.' + m + '.' + y + ' ' + t;
|
||||||
|
req.session.endTime = stamp;
|
||||||
|
let sesEnd = req.session.endTime;
|
||||||
|
let log = [];
|
||||||
|
try {
|
||||||
|
log = await read('user_log.json');
|
||||||
|
} catch (e) { console.log(e); }
|
||||||
|
|
||||||
|
let newLog = {
|
||||||
|
"id": log.length +1,
|
||||||
|
"UserId": sesUser,
|
||||||
|
"Login": sesStart,
|
||||||
|
"Logout": sesEnd
|
||||||
|
}
|
||||||
|
log.unshift(newLog);
|
||||||
|
write(log, 'user_log.json');
|
||||||
|
|
||||||
|
req.session.destroy(err => {
|
||||||
|
if(err){
|
||||||
|
return res.redirect('/');
|
||||||
|
}
|
||||||
|
res.clearCookie('sid');
|
||||||
|
res.redirect('/');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(isLogged);
|
||||||
|
|
||||||
|
app.get('/register', (req,res)=>{
|
||||||
|
res.send(`
|
||||||
|
<h1 style="text-align:center; margin-top:50px;">Register new user</h1>
|
||||||
|
<form method='post' action='/register' style="text-align: center;"/>
|
||||||
|
<input type='text' name='name' placeholder='Username' required /><br>
|
||||||
|
<input type='password' name='password' placeholder='Password' required /><br>
|
||||||
|
<input type='submit' id='post-reg' value="Send"/>
|
||||||
|
</form>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
app.post('/register', async (req,res) =>{
|
app.post('/register', async (req,res) =>{
|
||||||
let users = [];
|
let users = [];
|
||||||
@ -176,50 +215,7 @@ app.post('/register', async (req,res) =>{
|
|||||||
console.log('User exists already. No registeration done.');
|
console.log('User exists already. No registeration done.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.redirect('/register')
|
res.redirect('/')
|
||||||
})
|
|
||||||
|
|
||||||
app.post('/logout',async (req, res) =>{
|
|
||||||
let now = getTime();
|
|
||||||
let d = now.slice(8,10);
|
|
||||||
let m = now.slice(5,7);
|
|
||||||
let y = now.slice(0,4);
|
|
||||||
let t = now.slice(11, 19);
|
|
||||||
let stamp = d + '.' + m + '.' + y + ' ' + t;
|
|
||||||
req.session.endTime = stamp;
|
|
||||||
let sesEnd = req.session.endTime;
|
|
||||||
let log = [];
|
|
||||||
try {
|
|
||||||
log = await read('user_log.json');
|
|
||||||
} catch (e) { console.log(e); }
|
|
||||||
|
|
||||||
let newLog = {
|
|
||||||
"id": log.length +1,
|
|
||||||
"UserId": sesUser,
|
|
||||||
"Login": sesStart,
|
|
||||||
"Logout": sesEnd
|
|
||||||
}
|
|
||||||
console.log(newLog);
|
|
||||||
log.unshift(newLog);
|
|
||||||
write(log, 'user_log.json');
|
|
||||||
|
|
||||||
req.session.destroy(err => {
|
|
||||||
if(err){
|
|
||||||
return res.redirect('/');
|
|
||||||
}
|
|
||||||
res.clearCookie('sid');
|
|
||||||
res.redirect('/');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
/*
|
|
||||||
app.get('/data', async (req, res) => {
|
|
||||||
try {
|
|
||||||
const data = await read('data.json');
|
|
||||||
res.json(data);
|
|
||||||
} catch (e) {
|
|
||||||
res.status(404).send(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
server.listen(3000, () => console.log('Server listening on port 3000'));
|
server.listen(3000, () => console.log('Server listening on port 3000'));
|
||||||
@ -18,8 +18,8 @@ const end = document.getElementById('end-time');
|
|||||||
const reset = document.getElementById('btn_reset');
|
const reset = document.getElementById('btn_reset');
|
||||||
|
|
||||||
let user;
|
let user;
|
||||||
let pointerX = -1;
|
let pointX = -1;
|
||||||
let pointerY = -1;
|
let pointY = -1;
|
||||||
let lastX = 0;
|
let lastX = 0;
|
||||||
let lastY = 0;
|
let lastY = 0;
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
@ -56,13 +56,12 @@ socket.on('pwd', (data) =>{
|
|||||||
if(data){
|
if(data){
|
||||||
sessionStorage.setItem('reload', true);
|
sessionStorage.setItem('reload', true);
|
||||||
document.location.reload();
|
document.location.reload();
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('user', (data) =>{
|
socket.on('user', (data) =>{
|
||||||
user = data;
|
user = data;
|
||||||
localStorage.setItem('user', data);
|
sessionStorage.setItem('user', data);
|
||||||
sessionStorage.setItem('loggedIn', 'true');
|
sessionStorage.setItem('loggedIn', 'true');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -72,9 +71,6 @@ reset.addEventListener('click', e =>{
|
|||||||
end.value = today.replace('T', ' ') + ':00';
|
end.value = today.replace('T', ' ') + ':00';
|
||||||
start_time = new Date(start.value).getTime();
|
start_time = new Date(start.value).getTime();
|
||||||
end_time = new Date(end.value).getTime();
|
end_time = new Date(end.value).getTime();
|
||||||
|
|
||||||
//start.value = today.replace('T', ' ') + ':00';
|
|
||||||
//end.value = today.replace('T', ' ') + ':00';
|
|
||||||
updateChart();
|
updateChart();
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -122,12 +118,8 @@ automode.addEventListener('click', () =>{
|
|||||||
manmode.addEventListener('click', () =>{
|
manmode.addEventListener('click', () =>{
|
||||||
if(!sessionStorage.getItem('loggedIn')){
|
if(!sessionStorage.getItem('loggedIn')){
|
||||||
document.getElementById('login-form').style.display = "block";
|
document.getElementById('login-form').style.display = "block";
|
||||||
document.getElementById('sp_div').style.display = "none";
|
document.getElementsByClassName('modes').style.display = "none";
|
||||||
document.getElementById('pr_div').style.display = "none";
|
document.getElementsByClassName('set_values').style.display = "none";
|
||||||
document.getElementById('m_auto').style.display = "none";
|
|
||||||
document.getElementById('m_man').style.display = "none";
|
|
||||||
document.getElementById('auto_label').style.display = "none";
|
|
||||||
document.getElementById('man_label').style.display = "none";
|
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
document.getElementById('login-form').style.display = "none";
|
document.getElementById('login-form').style.display = "none";
|
||||||
@ -154,17 +146,20 @@ document.getElementById('password').addEventListener('click', ()=>{
|
|||||||
});
|
});
|
||||||
|
|
||||||
log_out.addEventListener('click', () =>{
|
log_out.addEventListener('click', () =>{
|
||||||
console.log('log out clicked');
|
|
||||||
localStorage.clear();
|
|
||||||
sessionStorage.clear();
|
sessionStorage.clear();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
document.getElementById('reg').addEventListener('click', ()=>{
|
||||||
|
sessionStorage.setItem('reload', true);
|
||||||
|
})
|
||||||
|
|
||||||
window.addEventListener('beforeunload', (e)=>{
|
window.addEventListener('beforeunload', (e)=>{
|
||||||
if(document.cookie){
|
if(!sessionStorage.getItem('reload')){
|
||||||
log_out.click();
|
logOutUser();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Changes the border-color of output elements in monitor fieldset if values are outside the ranges
|
||||||
function circleColor(){
|
function circleColor(){
|
||||||
if(g_pressure.value > 115){
|
if(g_pressure.value > 115){
|
||||||
g_pressure.style.borderColor = "red";
|
g_pressure.style.borderColor = "red";
|
||||||
@ -198,85 +193,64 @@ function circleColor(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function logOutUser(){
|
|
||||||
if(document.cookie){
|
|
||||||
log_out.click();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.onmousemove = function(event) {
|
document.onmousemove = function(event) {
|
||||||
pointerX = event.pageX;
|
pointX = event.pageX;
|
||||||
pointerY = event.pageY;
|
pointY = event.pageY;
|
||||||
}
|
}
|
||||||
setInterval(activityCheck, 1000);
|
setInterval(activityCheck, 1000);
|
||||||
|
|
||||||
|
// Keeps the counter if no mouse moves are detected. If the set counter limit is exceeded, the user is logged out
|
||||||
function activityCheck() {
|
function activityCheck() {
|
||||||
if(sessionStorage.getItem('loggedIn')){
|
if(sessionStorage.getItem('loggedIn')){
|
||||||
if(pointerX - lastX === 0 && pointerY - lastY === 0){
|
if(pointX - lastX === 0 && pointY - lastY === 0){
|
||||||
counter = counter + 1;
|
counter = counter + 1;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
lastX = pointerX;
|
lastX = pointX;
|
||||||
lastY = pointerY;
|
lastY = pointY;
|
||||||
counter = 0;
|
counter = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(counter > 60){
|
if(counter > 600){
|
||||||
|
logOutUser();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logs out the user, click of the log out button cleares session storage with the event listener of the button
|
||||||
|
function logOutUser(){
|
||||||
|
if(sessionStorage.getItem('loggedIn')){
|
||||||
log_out.click();
|
log_out.click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Session storage is used to check which elements are visible when the body of the page is loaded
|
||||||
function checkUser(){
|
function checkUser(){
|
||||||
if(sessionStorage.getItem('reload')){
|
if(sessionStorage.getItem('reload')){
|
||||||
document.getElementById('login-form').style.display = "block";
|
document.getElementById('login-form').style.display = "block";
|
||||||
document.getElementById('pwd-warn').style.display = "block";
|
document.getElementById('pwd-warn').style.display = "block";
|
||||||
sessionStorage.removeItem('reload');
|
sessionStorage.removeItem('reload');
|
||||||
}
|
}
|
||||||
if(document.cookie && sessionStorage.getItem('loggedIn')){
|
if(sessionStorage.getItem('loggedIn')){
|
||||||
document.getElementById('login-form').style.display = "none";
|
document.getElementById('login-form').style.display = "none";
|
||||||
document.getElementById('user').style.display = "block";
|
document.getElementById('user').style.display = "block";
|
||||||
document.getElementById('user').innerHTML = 'Signed in user: ' + localStorage.getItem('user');
|
document.getElementById('user').innerHTML = 'Signed in user: ' + sessionStorage.getItem('user');
|
||||||
document.getElementById('btn_log_out').style.display = "block";
|
document.getElementById('btn_log_out').style.display = "block";
|
||||||
document.getElementById('user').style.display = "block";
|
|
||||||
document.getElementById('user').innerHTML = 'Signed in user: ' + localStorage.getItem('user');
|
|
||||||
document.getElementById('btn_log_out').style.display = "block";
|
|
||||||
document.getElementById('user-table').style.width = "45%";
|
|
||||||
document.getElementById('chart-cont').style.width = "50%";
|
|
||||||
|
|
||||||
if(manmode.checked = true){
|
if(manmode.checked = true){
|
||||||
s_pressure.disabled = true;
|
s_pressure.disabled = true;
|
||||||
document.getElementById('pr_div').style.opacity = 0.4;
|
document.getElementById('pr_div').style.opacity = 0.4;
|
||||||
|
document.getElementById('sp_div').style.opacity = 1;
|
||||||
s_speed.disabled = false;
|
s_speed.disabled = false;
|
||||||
}
|
}
|
||||||
if(automode.checked = true){
|
if(automode.checked = true){
|
||||||
s_speed.disabled = true;
|
s_speed.disabled = true;
|
||||||
document.getElementById('sp_div').style.opacity = 0.4;
|
document.getElementById('sp_div').style.opacity = 0.4;
|
||||||
|
document.getElementById('pr_div').style.opacity = 1;
|
||||||
s_pressure.disabled = false;
|
s_pressure.disabled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
document.getElementById('login-form').style.display = "block";
|
document.getElementById('login-form').style.display = "block";
|
||||||
document.getElementById('sp_div').style.display = "none";
|
document.getElementById('pre-reg').style.display = "none";
|
||||||
document.getElementById('pr_div').style.display = "none";
|
|
||||||
document.getElementById('m_auto').style.display = "none";
|
|
||||||
document.getElementById('m_man').style.display = "none";
|
|
||||||
document.getElementById('auto_label').style.display = "none";
|
|
||||||
document.getElementById('man_label').style.display = "none";
|
|
||||||
document.getElementById('user-table').style.display = "none";
|
|
||||||
document.getElementById('chart-cont').style.width = "95%";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkMode(){
|
|
||||||
if(document.cookie && sessionStorage.getItem('loggedIn')){
|
|
||||||
automode.checked = true;
|
|
||||||
s_speed.disabled = true;
|
|
||||||
document.getElementById('sp_div').style.opacity = 0.4;
|
|
||||||
document.getElementById('pr_div').style.opacity = 1;
|
|
||||||
s_pressure.disabled = false;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
document.getElementById('login-form').style.display = "block";
|
|
||||||
document.getElementById('sp_div').style.display = "none";
|
document.getElementById('sp_div').style.display = "none";
|
||||||
document.getElementById('pr_div').style.display = "none";
|
document.getElementById('pr_div').style.display = "none";
|
||||||
document.getElementById('m_auto').style.display = "none";
|
document.getElementById('m_auto').style.display = "none";
|
||||||
@ -287,16 +261,19 @@ function checkMode(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sends the selected pressure value with socket to the server
|
||||||
function sendPressure(){
|
function sendPressure(){
|
||||||
let press = { auto: true, pressure: parseInt(s_pressure.value) }
|
let press = { auto: true, pressure: parseInt(s_pressure.value) }
|
||||||
socket.emit('setting', press);
|
socket.emit('setting', press);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sends the selected fan speed value with socket to the server
|
||||||
function sendSpeed(){
|
function sendSpeed(){
|
||||||
let speed = { auto: false, speed: parseInt(s_speed.value) }
|
let speed = { auto: false, speed: parseInt(s_speed.value) }
|
||||||
socket.emit('setting', speed);
|
socket.emit('setting', speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Updates the data chart on the page, fetches values from json file (db) and shows the data with selected time interval
|
||||||
function updateChart(){
|
function updateChart(){
|
||||||
async function fetchData(){
|
async function fetchData(){
|
||||||
const response = await fetch('data.json');
|
const response = await fetch('data.json');
|
||||||
@ -345,6 +322,7 @@ function updateChart(){
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// data and config are base settings for the data chart
|
||||||
const data = {
|
const data = {
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: 'CO2',
|
label: 'CO2',
|
||||||
@ -433,6 +411,7 @@ const config = {
|
|||||||
|
|
||||||
const myChart = new Chart(canvas, config);
|
const myChart = new Chart(canvas, config);
|
||||||
|
|
||||||
|
// Fetches the data from json file (db) and updates the the user log history table
|
||||||
fetch('user_log.json')
|
fetch('user_log.json')
|
||||||
.then((res)=>{
|
.then((res)=>{
|
||||||
return res.json();
|
return res.json();
|
||||||
@ -452,6 +431,7 @@ fetch('user_log.json')
|
|||||||
placeholder.innerHTML = out;
|
placeholder.innerHTML = out;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fetches the data from json file (db) and sets the latest values to the monitor fieldset output elements when the body of the page is loaded
|
||||||
async function getStartValues(){
|
async function getStartValues(){
|
||||||
await fetch('data.json')
|
await fetch('data.json')
|
||||||
.then((res) =>{
|
.then((res) =>{
|
||||||
|
|||||||
@ -71,6 +71,19 @@ h4{
|
|||||||
font-weight: bolder;
|
font-weight: bolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pre-reg{
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
float:right;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#reg{
|
||||||
|
border-radius: 5px;
|
||||||
|
color: black;
|
||||||
|
background-color: #CCE3DE;
|
||||||
|
}
|
||||||
|
|
||||||
.logout{
|
.logout{
|
||||||
margin-left: 20px;
|
margin-left: 20px;
|
||||||
margin-top: 35px;
|
margin-top: 35px;
|
||||||
@ -91,6 +104,7 @@ h4{
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background-color: #CCE3DE;
|
background-color: #CCE3DE;
|
||||||
border: 0.5px solid;
|
border: 0.5px solid;
|
||||||
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
#user{
|
#user{
|
||||||
@ -289,11 +303,19 @@ canvas{
|
|||||||
margin-bottom: 50px;
|
margin-bottom: 50px;
|
||||||
width: 50%;
|
width: 50%;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#log{
|
||||||
|
height: 300px;
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-y: scroll;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
table{
|
table{
|
||||||
background-color: #CCE3DE;
|
background-color: #CCE3DE;
|
||||||
width: 90%;
|
width: 100%;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
@ -302,6 +324,12 @@ table, th, td {
|
|||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.th.fixed{
|
||||||
|
top: 0;
|
||||||
|
z-index: 2;
|
||||||
|
position: sticky;
|
||||||
|
}
|
||||||
|
|
||||||
td{
|
td{
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,34 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"id": 20,
|
||||||
|
"UserId": "Miisa",
|
||||||
|
"Login": "28.10.2022 11:48:28",
|
||||||
|
"Logout": "28.10.2022 11:55:17"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 19,
|
||||||
|
"UserId": "Miisa",
|
||||||
|
"Login": "28.10.2022 11:38:52",
|
||||||
|
"Logout": "28.10.2022 11:45:02"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 18,
|
||||||
|
"UserId": "Miisa",
|
||||||
|
"Login": "28.10.2022 08:28:47",
|
||||||
|
"Logout": "28.10.2022 08:29:58"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 17,
|
||||||
|
"UserId": "Miisa",
|
||||||
|
"Login": "28.10.2022 08:25:47",
|
||||||
|
"Logout": "28.10.2022 08:27:40"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"UserId": "Jaakko",
|
||||||
|
"Login": "28.10.2022 08:15:12",
|
||||||
|
"Logout": "28.10.2022 08:19:01"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": 15,
|
"id": 15,
|
||||||
"UserId": "Miisa",
|
"UserId": "Miisa",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user