Final changes

Changed login to both modes and layout, modified period selection in chart
This commit is contained in:
Miisa Ekholm 2022-10-27 22:49:37 +03:00
parent 2cb3347da8
commit a4cd746fed
5 changed files with 449 additions and 299 deletions

View File

@ -9,135 +9,128 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.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://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.min.js" type="text/javascript" defer></script>
</head>
<body onload="updateChart(), checkMode(), getStartValues()">
<body onload="checkUser(), checkMode(), getStartValues(), updateChart()">
<header>
<h1>ESP-Ventilation</h1>
<div class="logout">
<p id="user"></p>
<form id="log_out" method="post" action="/logout">
<input id="btn_log_out" type="submit" value="Log out">
</form>
</div>
<h1>ABB Ventilation Controller</h1>
</header>
<main>
<div class="modes">
<div class="logout">
<p id="user"></p>
<form id="log_out" method="post" action="/logout">
<input id="btn_log_out" type="submit" value="Log out">
</form>
</div>
<div class="upper">
<main>
<div class="login">
<form id="login-form" method="post" action="/">
<br>
<p id="pwd-warn">Wrong username or password</p>
<p id="form-warn">All fields must be filled!</p>
<p>You must log in to change settings<br>Please give your credentials:</p>
<input type="text" name="username" id="username" placeholder="Username" required>
<br><br>
<input type="password" name="password" id="password" placeholder="Password" required>
<br><br>
<input type="submit" name="btn_login" id="btn_login" value="Login">
<br><br>
<p id="acc" style="font-weight:normal">If you don't have an account, please contact your administrator</p>
<br>
</form>
</div>
<div class="modes">
<div id="buttons">
<input type="radio" class="custom-radio" id="m_auto" name="mode" value="Automatic" checked="true">
<label for="m_auto" id="auto_label">Automatic</label><br>
</div>
<div id="buttons">
<input type="radio" class="custom-radio" id="m_man" name="mode" value="Manual">
<label for="m_man" id="man_label">Manual</label><br><br>
</div>
</div>
<br>
<div class="buttons">
<input type="radio" class="custom-radio" id="m_auto" name="mode" value="Automatic" checked="true">
<label for="m_auto">Automatic</label><br>
</div>
<div class="buttons">
<input type="radio" class="custom-radio" id="m_man" name="mode" value="Manual">
<label for="m_man">Manual</label><br><br>
</div>
</div>
<div class="login">
<form id="login-form" method="post" action="/">
<br>
<p id="pwd-warn">Wrong username or password</p>
<p id="form-warn">All fields must be filled!</p>
<p>You must log in to access manual mode<br>Please give your credentials:</p>
<input type="text" name="username" id="username" placeholder="Username" required>
<br><br>
<input type="password" name="password" id="password" placeholder="Password" required>
<br><br>
<input type="submit" name="btn_login" id="btn_login" value="Login">
<br>
<p id="acc" style="font-weight:normal">If you don't have account, please contact the site administrator</p>
<br>
</form>
</div>
<br>
<p id="mon-warn">Fan is not settled in required time!</p>
<div class="set_values">
<div class="field">
<div class="ofield">
<div id="pr_div">
<h3>Set pressure value</h3>
<output class="set_output" id="set_press">10 Pa</output>
<input class="slider" id="pressure" type="range" value="10" max="120" oninput="set_press.value = this.value + ' Pa'">
<div class="set_values">
<div class="field">
<div class="ofield">
<div id="pr_div">
<h3>Set pressure value</h3>
<output class="set_output" id="set_press">0 Pa</output>
<input class="slider" id="pressure" type="range" value="0" max="120" oninput="set_press.value = this.value + ' Pa'">
</div>
</div>
</div>
<div class="field">
<div class="ofield">
<div id="sp_div">
<h3>Set fan speed</h3>
<output class="set_output" id="set_speed">0 %</output>
<input class="slider" id="speed" type="range" value="0" max="100" oninput="set_speed.value = this.value + ' %'" disabled="true">
</div>
</div>
</div>
</div>
<div class="field">
<div class="ofield">
<div id="sp_div">
<h3>Set fan speed</h3>
<output class="set_output" id="set_speed">0 %</output>
<input class="slider" id="speed" type="range" value="0" max="100" oninput="set_speed.value = this.value + ' %'" disabled="true">
</main>
<aside>
<p id="mon-warn">Fan is not settled in required time!</p>
<div class="get_values">
<fieldset id="monitor">
<legend style="font-size: 20px;"><b>Monitor</b></legend>
<div class="field">
<div class="ifield">
<h3>Pressure</h3>
<output class="get_output" id="get_press">0</output>
<h4>Pa</h4>
</div>
</div>
</div>
</div>
<br><br>
<div class="get_values">
<fieldset id="monitor">
<legend style="font-size: 20px;"><b>Monitor</b></legend>
<div class="field">
<div class="ifield">
<h3>Pressure</h3>
<output class="get_output" id="get_press">0</output>
<h4>Pa</h4>
<div class="field">
<div class="ifield">
<h3>Fan speed</h3>
<output class="get_output" id="get_speed">0</output>
<h4>%</h4>
</div>
</div>
</div>
<div class="field">
<div class="ifield">
<h3>Fan speed</h3>
<output class="get_output" id="get_speed">0</output>
<h4>%</h4>
<div class="field">
<div class="ifield">
<h3>CO2</h3>
<output class="get_output" id="get_co">0</output>
<h4>ppm</h4>
</div>
</div>
</div>
<div class="field">
<div class="ifield">
<h3>CO2</h3>
<output class="get_output" id="get_co">0</output>
<h4>ppm</h4>
<div class="field">
<div class="ifield">
<h3>Temperature</h3>
<output class="get_output" id="get_temp">0</output>
<h4>&#8451</h4>
</div>
</div>
</div>
<div class="field">
<div class="ifield">
<h3>Temperature</h3>
<output class="get_output" id="get_temp">0</output>
<h4>&#8451</h4>
<div class="field">
<div class="ifield">
<h3>Relative Humidity</h3>
<output class="get_output" id="get_rh">0</output>
<h4>%</h4>
</div>
</div>
</fieldset>
</div>
<div class="field">
<div class="ifield">
<h3>Relative Humidity</h3>
<output class="get_output" id="get_rh">0</output>
<h4>%</h4>
</div>
</div>
</fieldset>
</div>
</main>
<aside>
<br>
<h2>Data History</h2>
<br>
<div class="chart-container">
<span id="chart_warning" style="display:none; color: red;">Not enough data to show the chosen period. Showing all values.</span>
<div class="dropdown">
<label for="data">Select period:</label>
<select name="data" id="data">
<option value="0">All</option>
<option value="1">10 min</option>
<option value="2">30 min</option>
<option value="3">60 min</option>
</select>
</aside>
</div>
<br>
<div class="lower">
<div class="chart-container" id="chart-cont">
<h2>Data History</h2>
<span id="chart_warn">No data found for selected period.</span>
<div class="filter">
<label>Select period:</label><br>
<label for="start-time">Start time:</label>
<input type="datetime-local" id="start-time" name="start-time" min="" max="" value="">
<label for="end-time">End time:</label>
<input type="datetime-local" id="end-time" name="end-time" min="" max="" value="">
<button id="btn_reset">Reset</button>
</div>
<canvas id="dataChart" width="600px" height="300px"></canvas>
</div>
<br><br>
<div class="user-table">
<div id="user-table">
<h2>User Log History</h2>
<table>
<thead>
@ -151,7 +144,6 @@
</tbody>
</table>
</div>
<br><br>
</aside>
</div>
</body>
</html>

View File

@ -12,7 +12,15 @@ const mqtt = require('mqtt');
const session = require('express-session');
const bcrypt = require('bcrypt')
const client = mqtt.connect('mqtt://127.0.0.1:1883', {clientId: 'node_server', clean: true})
const client = mqtt.connect('mqtt://192.168.1.254:1883',
{username: 'SmartIotMQTT',
password: 'SmartIot',
clientId: 'node_server',
clean: true,
connectTimeout: 8000,
reconnectPeriod: 1000,
})
//const client = mqtt.connect('mqtt://127.0.0.1:1883', {clientId: 'node_server', clean: true})
let newData;
let user;
let msg;
@ -46,14 +54,10 @@ function write(data, filePath) {
}
function getTime(){
let today = new Date();
let day = ("0" + today.getDate()).slice(-2);
let month = ("0" + (today.getMonth() + 1)).slice(-2);
let hours = ("0" + today.getHours()).slice(-2);
let mins = ("0" + today.getMinutes()).slice(-2);
let secs = ("0" + today.getSeconds()).slice(-2);
let timestamp = (day + '.' + month + '.' + today.getFullYear() + ' ' + hours + '.' + mins + '.' + secs).toString();
return timestamp;
let day = new Date();
day = new Date(day.getTime() - day.getTimezoneOffset()*60000);
let today = day.toISOString().replace('T', ' ').slice(0,19);
return today;
}
client.on('connect', ()=>{
@ -62,14 +66,13 @@ client.on('connect', ()=>{
client.subscribe('controller/status', (err)=>{
if(err){
console.log('Subscription failed')
console.log('Subscription failed ' + err);
}
});
io.on('connection', (socket)=>{
console.log("User " + socket.id + " connected");
socket.on('setting', (arg)=> {
let data = JSON.stringify(arg);
client.publish("controller/settings", JSON.stringify(arg), { qos: 2, retain: false }, (error)=>{
if(error){
console.log(error);
@ -80,9 +83,6 @@ io.on('connection', (socket)=>{
client.on('message', async (topic, message) =>{
newData = JSON.parse(message);
console.log(newData);
io.emit('data', newData);
let info = [];
try {
info = await read('data.json');
@ -91,6 +91,7 @@ client.on('message', async (topic, message) =>{
newData['ts'] = now;
info.push(newData);
write(info, 'data.json');
io.emit('data', newData);
});
@ -98,15 +99,6 @@ app.get('/', (req, res)=>{
res.sendFile(path.join(__dirname + '/index.html'));
})
app.get('/data', async (req, res) => {
try {
const data = await read('data.json');
res.json(data);
} catch (e) {
res.status(404).send(e);
}
});
app.get('/register', (req,res)=>{
res.send(`
<h1>Register</h1>
@ -132,14 +124,20 @@ app.post('/', async (req,res)=>{
if(bcrypt.compareSync(password, pwd)){
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;
//console.log(stamp);
req.session.userId = username;
req.session.startTime = now;
req.session.startTime = stamp;
console.log('Password is correct');
user = req.session.userId;
io.emit('user', user);
sesUser = user;
sesStart = req.session.startTime;
res.status(205);
res.redirect('/');
}
else{
msg = 'wrong';
@ -183,7 +181,12 @@ app.post('/register', async (req,res) =>{
app.post('/logout',async (req, res) =>{
let now = getTime();
req.session.endTime = now;
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 {
@ -196,6 +199,7 @@ app.post('/logout',async (req, res) =>{
"Login": sesStart,
"Logout": sesEnd
}
console.log(newLog);
log.unshift(newLog);
write(log, 'user_log.json');
@ -208,7 +212,7 @@ app.post('/logout',async (req, res) =>{
});
});
/*
app.get('/data', async (req, res) => {
try {
const data = await read('data.json');
@ -217,6 +221,5 @@ app.get('/data', async (req, res) => {
res.status(404).send(e);
}
});
*/
server.listen(3000, () => console.log('Server listening on port 3000'));

View File

@ -1,3 +1,4 @@
const automode = document.getElementById('m_auto');
const manmode = document.getElementById('m_man');
const s_pressure = document.getElementById('pressure'); //slider
@ -12,7 +13,10 @@ const canvas = document.getElementById('dataChart');
const filter = document.getElementById('data');
const submit = document.getElementById('btn_login');
const log_out = document.getElementById('btn_log_out');
let data_miss = false;
const start = document.getElementById('start-time');
const end = document.getElementById('end-time');
const reset = document.getElementById('btn_reset');
let user;
let pointerX = -1;
let pointerY = -1;
@ -21,18 +25,23 @@ let lastY = 0;
let counter = 0;
let lastSpeed = 0;
let notSettled = 0;
let day = new Date();
day = new Date(day.getTime() - day.getTimezoneOffset()*60000);
let today = day.toISOString().slice(0,16);
start.value = today.replace('T', ' ') + ':00';
end.value = today.replace('T', ' ') + ':00';
let start_time = new Date(start.value).getTime();
let end_time = new Date(end.value).getTime();
const socket = io();
socket.on('connection');
socket.on('data', (data) =>{
if(data.auto === true){
s_pressure.value = data.setpoint;
document.getElementById('set_press').value = s_pressure.value + ' Pa';
if(data.error === true){
document.getElementById('mon-warn').style.display = 'block';
}
if(data.auto === false){
s_speed.value = data.setpoint;
document.getElementById('set_speed').value = s_speed.value + ' %';
else{
document.getElementById('mon-warn').style.display = 'none';
}
g_pressure.value = data.pressure;
g_co.value = data.co2;
@ -40,16 +49,14 @@ socket.on('data', (data) =>{
g_speed.value = data.speed;
g_temp.value = data.temp;
updateChart();
checkData();
checkFan();
circleColor();
});
socket.on('pwd', (data) =>{
if(data){
automode.checked = true;
document.getElementById('login-form').style.display = "block";
document.getElementById('pwd-warn').style.display = "block";
sessionStorage.setItem('reload', true);
document.location.reload();
}
});
@ -59,6 +66,32 @@ socket.on('user', (data) =>{
sessionStorage.setItem('loggedIn', 'true');
});
reset.addEventListener('click', e =>{
e.preventDefault();
start.value = today.replace('T', ' ') + ':00';
end.value = today.replace('T', ' ') + ':00';
start_time = new Date(start.value).getTime();
end_time = new Date(end.value).getTime();
//start.value = today.replace('T', ' ') + ':00';
//end.value = today.replace('T', ' ') + ':00';
updateChart();
})
start.addEventListener('change', e =>{
e.preventDefault();
start_time = start.value + ':00';
start_time = new Date(start_time).getTime();
updateChart();
});
end.addEventListener('change', e =>{
e.preventDefault();
end_time = end.value + ':00';
end_time = new Date(end_time).getTime();
updateChart();
});
s_pressure.addEventListener('input', e =>{
e.preventDefault();
sendPressure();
@ -70,20 +103,34 @@ s_speed.addEventListener('input', e =>{
});
automode.addEventListener('click', () =>{
document.getElementById('login-form').style.display = "none";
if(!sessionStorage.getItem('loggedIn')){
document.getElementById('login-form').style.display = "block";
document.getElementsByClassName('modes').style.display = "none";
document.getElementsByClassName('set_values').style.display = "none";
}
else{
document.getElementById('login-form').style.display = "none";
s_pressure.disabled = false;
document.getElementById('pr_div').style.opacity = 1;
s_pressure.disabled = false;
document.getElementById('pr_div').style.opacity = 1;
s_speed.disabled = true;
document.getElementById('sp_div').style.opacity = 0.4;
}
s_speed.disabled = true;
document.getElementById('sp_div').style.opacity = 0.4;
})
manmode.addEventListener('click', () =>{
if(!sessionStorage.getItem('loggedIn')){
document.getElementById('login-form').style.display = "block";
document.getElementById('sp_div').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";
}
else{
document.getElementById('login-form').style.display = "none";
document.getElementById('sp_div').style.opacity = 1;
s_speed.disabled = false;
@ -92,11 +139,6 @@ manmode.addEventListener('click', () =>{
}
})
filter.addEventListener('change', e =>{
e.preventDefault();
updateChart();
})
submit.addEventListener('click', e =>{
if(document.getElementById('username').value && document.getElementById('password').value){
document.getElementById('login-form').style.display = "none";
@ -111,7 +153,8 @@ document.getElementById('password').addEventListener('click', ()=>{
document.getElementById('form-warn').style.display = "none";
});
log_out.addEventListener('click', ()=>{
log_out.addEventListener('click', () =>{
console.log('log out clicked');
localStorage.clear();
sessionStorage.clear();
})
@ -155,20 +198,6 @@ function circleColor(){
}
}
function checkFan(){
if(lastSpeed !== g_speed.value){
notSettled += 1;
lastSpeed = g_speed.value;
}
else{
notSettled = 0;
document.getElementById('mon-warn').style.display = 'none';
}
if(notSettled > 12){
document.getElementById('mon-warn').style.display = 'block';
}
}
function logOutUser(){
if(document.cookie){
log_out.click();
@ -182,7 +211,7 @@ document.onmousemove = function(event) {
setInterval(activityCheck, 1000);
function activityCheck() {
if(document.cookie){
if(sessionStorage.getItem('loggedIn')){
if(pointerX - lastX === 0 && pointerY - lastY === 0){
counter = counter + 1;
}
@ -197,33 +226,64 @@ function activityCheck() {
}
}
function checkMode(){
function checkUser(){
if(sessionStorage.getItem('reload')){
document.getElementById('login-form').style.display = "block";
document.getElementById('pwd-warn').style.display = "block";
sessionStorage.removeItem('reload');
}
if(document.cookie && sessionStorage.getItem('loggedIn')){
document.getElementById('login-form').style.display = "none";
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').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%";
manmode.checked = true;
s_pressure.disabled = true;
document.getElementById('pr_div').style.opacity = 0.4;
s_speed.disabled = false;
if(manmode.checked = true){
s_pressure.disabled = true;
document.getElementById('pr_div').style.opacity = 0.4;
s_speed.disabled = false;
}
if(automode.checked = true){
s_speed.disabled = true;
document.getElementById('sp_div').style.opacity = 0.4;
s_pressure.disabled = false;
}
}
else{
automode.checked = true;
s_speed.disabled = true;
document.getElementById('sp_div').style.opacity = 0.4;
s_pressure.disabled = false;
document.getElementById('user').style.display = "none";
document.getElementById('btn_log_out').style.display = "none";
document.getElementById('login-form').style.display = "block";
document.getElementById('sp_div').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 checkData(){
if(data_miss){
document.getElementById('chart_warning').style.display = "block";
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;
}
if(!data_miss){
document.getElementById('chart_warning').style.display = "none";
else{
document.getElementById('login-form').style.display = "block";
document.getElementById('sp_div').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";
}
}
@ -237,6 +297,54 @@ function sendSpeed(){
socket.emit('setting', speed);
}
function updateChart(){
async function fetchData(){
const response = await fetch('data.json');
const data = await response.json();
if(data.length !== 0){
let min = data[0].ts;
let max = data[data.length-1].ts;
start.min = min.slice(0, 16);
start.max = max.slice(0, 16);
end.min = min.slice(0, 16);
end.max = max.slice(0, 16);
}
const datapoints = data.filter(d => {
if((end_time-start_time) !== 0){
return (new Date(d.ts).getTime() >= start_time && new Date(d.ts).getTime() <= end_time)
}
else{
return data;
}
})
return datapoints;
};
fetchData().then(datapoints => {
if(datapoints.length === 0)
{
document.getElementById('chart_warn').style.display = "block";
}
const time = datapoints.map((index) => {
return index.ts;
});
const co = datapoints.map((index) => {
return index.co2;
});
const pressure = datapoints.map((index) => {
return index.pressure;
});
const rh = datapoints.map((index) => {
return index.rh;
});
myChart.config.data.labels = time;
myChart.config.data.datasets[0].data = co;
myChart.config.data.datasets[1].data = pressure;
myChart.config.data.datasets[2].data = rh;
myChart.update();
});
};
const data = {
datasets: [{
label: 'CO2',
@ -318,73 +426,13 @@ const config = {
}
}//,
//aspectRatio: 1
},
aspectRatio: 2
}
};
const myChart = new Chart(canvas, config);
function updateChart(){
async function fetchData(){
let datasize = 0;
let data_per_min = 12;
let datapoints;
await fetch('/data')
.then(res => res.json())
.then(data =>{
console.log(data.length);
datapoints = data.filter((elem, index)=>{
switch(parseInt(filter.value))
{
case 0: //all
datasize = data.length;
break;
case 1: //10min
datasize = 10*data_per_min;
break;
case 2: //30min
datasize = 30*data_per_min;
break;
case 3: //1 hour
datasize = 60*data_per_min;
break;
}
if(datasize > data.length){
data_miss = true;
}
else{
data_miss = false;
}
return index >= data.length -datasize;
});
});
return datapoints;
};
fetchData().then(datapoints => {
const time = datapoints.map((time) => {
return time.ts;
});
const co = datapoints.map((time) => {
return time.co2;
});
const pressure = datapoints.map((time) => {
return time.pressure;
});
const rh = datapoints.map((time) => {
return time.rh;
});
myChart.config.data.labels = time;
myChart.config.data.datasets[0].data = co;
myChart.config.data.datasets[1].data = pressure;
myChart.config.data.datasets[2].data = rh;
myChart.update();
checkData();
});
};
fetch('user_log.json')
.then((res)=>{
return res.json();
@ -404,8 +452,8 @@ fetch('user_log.json')
placeholder.innerHTML = out;
});
function getStartValues(){
fetch('data.json')
async function getStartValues(){
await fetch('data.json')
.then((res) =>{
return res.json();
})

View File

@ -11,7 +11,7 @@ body {
padding-bottom: 5px;
}
main {
margin-top: 100px;
margin-top: 50px;
padding-bottom: 20px;
display: block;
position: relative;
@ -21,11 +21,27 @@ main {
aside {
width: 100%;
background-color: #F6FFF8;
margin-bottom: 50px;
}
.upper{
display: flex;
flex-wrap: wrap;
width: 100%;
}
.lower{
display: flex;
flex-wrap: wrap;
flex-direction: column;
width: 100%;
margin-bottom: 50px;
}
header {
width: 100%;
height: 150px;
height: 100px;
color: black;
}
h1 {
padding-top: 40px;
@ -51,12 +67,13 @@ h4{
text-align: center;
}
.dropdown{
position: relative;
float: right;
.filter{
font-weight: bolder;
}
.logout{
margin-left: 20px;
margin-top: 35px;
}
#btn_log_out{
margin-bottom: 10px;
@ -68,6 +85,14 @@ h4{
display: none;
}
#btn_reset{
position: relative;
float: right;
border-radius: 5px;
background-color: #CCE3DE;
border: 0.5px solid;
}
#user{
display: none;
font-weight: bolder;
@ -77,6 +102,11 @@ h4{
background-color: red;
}
.login{
margin-top: 50px;
padding-top: 50px;
}
#login-form:valid #btn_login{
background-color: mediumseagreen;
}
@ -89,6 +119,7 @@ h4{
font-weight: bolder;
border-radius: 10px;
display: none;
}
#acc{
@ -96,6 +127,12 @@ h4{
margin: auto;
}
#chart_warn{
display: none;
text-align: center;
color: red;
}
#mon-warn{
height: 20px;
text-align: center;
@ -120,9 +157,8 @@ h4{
flex-direction: row;
flex-wrap: wrap;
justify-content: space-evenly;
margin: 20px;
padding: 20px;
width: 80%;
margin: auto;
width: 85%;
}
fieldset {
@ -176,7 +212,8 @@ fieldset {
background-color: gray;
}
.buttons{
#buttons{
display: block;
font-size: 25px;
font-weight: bolder;
width: 12ch;
@ -186,6 +223,7 @@ fieldset {
display: flex;
flex-direction: column;
align-items: center;
padding-top: 30px;
}
.set_values{
@ -195,6 +233,10 @@ fieldset {
justify-content: space-evenly;
}
.sp_div{
display: block;
}
.get_values{
display: flex;
flex-direction: row;
@ -226,30 +268,32 @@ fieldset {
display: flex;
flex-direction: column;
align-items: center;
width: 150px;
width: 200px;
}
.chart-container{
position: relative;
width: 700px;
height: 350px;
#chart-cont{
display: block;
width: 90%;
margin: auto;
margin-bottom: 30px;
}
canvas{
border-radius: 10px;
border: 1px solid black;
background-color: white;
}
.user-table{
#user-table{
display: block;
margin-bottom: 50px;
width: 50%;
margin: auto;
}
table{
background-color: #CCE3DE;
width: 700px;
width: 90%;
margin: auto;
border-collapse: collapse;
}
@ -274,10 +318,13 @@ output {
width: 50%;
}
aside {
margin-top: 100px;
margin-top: 50px;
background: #F6FFF8;
width: 50%;
}
.lower{
flex-direction: row;
}
}
input[type="radio"] {

View File

@ -1,15 +1,75 @@
[
{
"id": 15,
"UserId": "Miisa",
"Login": "27.10.2022 18:51:43",
"Logout": "27.10.2022 18:54:01"
},
{
"id": 14,
"UserId": "Jaakko",
"Login": "27.10.2022 16:59:26",
"Logout": "27.10.2022 17:00:54"
},
{
"id": 13,
"UserId": "Miisa",
"Login": "27.10.2022 16:56:19",
"Logout": "27.10.2022 16:59:03"
},
{
"id": 12,
"UserId": "Miisa",
"Login": "27.10.2022 16:54:23",
"Logout": "27.10.2022 16:54:33"
},
{
"id": 11,
"UserId": "Miisa",
"Login": "27.10.2022 14:18:53",
"Logout": "27.10.2022 14:19:30"
},
{
"id": 10,
"UserId": "Miisa",
"Login": "27.10.2022 14:17:45",
"Logout": "27.10.2022 14:18:42"
},
{
"id": 9,
"UserId": "Miisa",
"Login": "27.10.2022 14:15:43",
"Logout": "27.10.2022 14:16:01"
},
{
"id": 8,
"UserId": "Miisa",
"Login": "27.10.2022 14:14:49",
"Logout": "27.10.2022 14:15:06"
},
{
"id": 7,
"UserId": "Miisa",
"Login": "27.10.2022 14:12:33",
"Logout": "27.10.2022 14:14:10"
},
{
"id": 6,
"UserId": "Miisa",
"Login": "27.10.2022 13:29:52",
"Logout": "27.10.2022 13:30:54"
},
{
"id": 5,
"UserId": "Miisa",
"Login": "25.10.2022 20.19.50",
"Logout": "25.10.2022 20.19.55"
"Login": "27.10.2022 08:10:40",
"Logout": "27.10.2022 08:11:42"
},
{
"id": 4,
"UserId": "Miisa",
"Login": "25.10.2022 20.18.27",
"Logout": "25.10.2022 20.18.43"
"Login": "27.10.2022 07:55:06",
"Logout": "27.10.2022 07:56:01"
},
{
"id": 3,