table and entries

This commit is contained in:
Janis
2022-04-30 20:57:06 +02:00
parent e950091f2d
commit b2c8e799bd
16 changed files with 807 additions and 11 deletions

View File

@@ -1 +1,25 @@
// ExpressJS server
// Dependencies
const express = require("express");
const configFile = require("./config");
const logger = require("./logger");
const cors = require("cors");
// Global variables
const app = express();
const config = configFile.config;
const port = config.port;
// Routes
const apiEntry = require("./routes/api/entry");
// Set up the express server
app.use(cors());
app.use(express.json()); // Parse JSON data from requests
app.use(express.urlencoded({ extended: true })); // Parse URL encoded data from requests
// routes
app.use("/api/entry", apiEntry);
// Start the express server
app.listen(port, () => logger.info("Server started", `Port: ${port}`));

11
backend/config.js Normal file
View File

@@ -0,0 +1,11 @@
const config = {
db: {
host: "127.0.0.1",
user: "root",
password: "", // In production: "InfraTag!" SORRY FOR CLEAR TEXT PASSWORD
database: "rme-time-tracking",
},
port: 8080, // Port for the server
};
module.exports = { config };

View File

@@ -0,0 +1,34 @@
/*
* Handles the connection to the MySQL database
* - Connects to the database
*/
// Dependencies
const mysql = require("mysql");
const configFile = require("../config");
const logger = require("../logger");
// Global variables
const config = configFile.config;
// Connection to the database
const con = mysql.createConnection({
host: config.db.host,
user: config.db.user,
password: config.db.password,
database: config.db.database,
});
con.connect(function (err) {
if (err) throw err;
logger.info(
`Connected to MySQL`,
`${config.db.user}@${config.db.host}/${config.db.database}`
);
});
con.on("error", () => {
logger.error("MySQL Error");
});
module.exports = { con };

View File

@@ -0,0 +1,25 @@
// Handlers which can be included into the request handler chain.
// Dependencies
const logger = require("../logger");
// better logging of requests
function LoggerHandler(req, res, next) {
const ip = (req.headers["x-forwarded-for"] || req.socket.remoteAddress || "")
.split(",")[0]
.trim()
.split(":")[3]; // get the ip of the client
if (req.method == "GET") {
// If the request is a GET request
logger.get(req.originalUrl, ip); // Log the request
} else if (req.method == "POST") {
// If the request is a POST request
logger.post(req.originalUrl, ip); // Log the request
}
next(); // Continue to the next handler
}
module.exports = {
LoggerHandler,
};

103
backend/logger.js Normal file
View File

@@ -0,0 +1,103 @@
const SHOW_TIME = true;
function getTimestamp() {
let date_ob = new Date();
// current date
// adjust 0 before single digit date
let date = ("0" + date_ob.getDate()).slice(-2);
// current month
let month = ("0" + (date_ob.getMonth() + 1)).slice(-2);
// current year
let year = date_ob.getFullYear();
// current hours
let hours = ("0" + date_ob.getHours()).slice(-2);
// current minutes
let minutes = ("0" + date_ob.getMinutes()).slice(-2);
// current seconds
let seconds = ("0" + date_ob.getSeconds()).slice(-2);
if (SHOW_TIME) {
return hours + ":" + minutes + ":" + seconds;
} else {
return null;
}
}
function cmd(msg, info = null) {
console.log(
"\x1b[36m%s\x1b[0m",
`${
getTimestamp() != null ? "[" + getTimestamp() + "]" : ""
}[COMMAND] ${msg} ${info != null ? "- " + info : ""}`
);
}
function info(msg, info = null) {
console.log(
"\x1b[37m%s\x1b[0m",
`${getTimestamp() != null ? "[" + getTimestamp() + "]" : ""}[INFO] ${msg} ${
info != null ? "- " + info : ""
}`
);
}
function error(msg, info = null) {
console.log(
"\x1b[31m%s\x1b[0m",
`${
getTimestamp() != null ? "[" + getTimestamp() + "]" : ""
}[ERROR] ${msg} ${info != null ? "- " + info : ""}`
);
}
function get(msg, info = null) {
console.log(
"\x1b[37m\x1b[2m%s\x1b[0m",
`${getTimestamp() != null ? "[" + getTimestamp() + "]" : ""}[GET] ${msg} ${
info != null ? "- " + info : ""
}`
);
}
function post(msg, info = null) {
console.log(
"\x1b[37m\x1b[2m%s\x1b[0m",
`${getTimestamp() != null ? "[" + getTimestamp() + "]" : ""}[POST] ${msg} ${
info != null ? "- " + info : ""
}`
);
}
function socket(msg, type) {
console.log(
"\x1b[35m\x1b[1m%s\x1b[0m",
`${
getTimestamp() != null ? "[" + getTimestamp() + "]" : ""
}[SOCKET.IO][${type}] ${msg}`
);
}
function event(msg, type) {
console.log(
"\x1b[35m\x1b[1m%s\x1b[0m",
`${
getTimestamp() != null ? "[" + getTimestamp() + "]" : ""
}[EVENT][${type}] ${msg}`
);
}
module.exports = {
cmd,
info,
error,
get,
post,
socket,
event,
};

View File

@@ -9,7 +9,9 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"express": "^4.18.1"
"cors": "^2.8.5",
"express": "^4.18.1",
"mysql": "^2.18.1"
}
},
"node_modules/accepts": {
@@ -29,6 +31,14 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"node_modules/bignumber.js": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
"integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==",
"engines": {
"node": "*"
}
},
"node_modules/body-parser": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
@@ -104,6 +114,23 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"node_modules/core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
},
"node_modules/cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
},
"engines": {
"node": ">= 0.10"
}
},
"node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -308,6 +335,11 @@
"node": ">= 0.10"
}
},
"node_modules/isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -364,6 +396,25 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"node_modules/mysql": {
"version": "2.18.1",
"resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
"integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
"dependencies": {
"bignumber.js": "9.0.0",
"readable-stream": "2.3.7",
"safe-buffer": "5.1.2",
"sqlstring": "2.3.1"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mysql/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@@ -372,6 +423,14 @@
"node": ">= 0.6"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-inspect": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
@@ -404,6 +463,11 @@
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -452,6 +516,25 @@
"node": ">= 0.8"
}
},
"node_modules/readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"dependencies": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
}
},
"node_modules/readable-stream/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -536,6 +619,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/sqlstring": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
"integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
@@ -544,6 +635,19 @@
"node": ">= 0.8"
}
},
"node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"dependencies": {
"safe-buffer": "~5.1.0"
}
},
"node_modules/string_decoder/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@@ -572,6 +676,11 @@
"node": ">= 0.8"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@@ -604,6 +713,11 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"bignumber.js": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
"integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A=="
},
"body-parser": {
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
@@ -660,6 +774,20 @@
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
"integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
},
"core-util-is": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
},
"cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"requires": {
"object-assign": "^4",
"vary": "^1"
}
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
@@ -818,6 +946,11 @@
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -856,11 +989,34 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"mysql": {
"version": "2.18.1",
"resolved": "https://registry.npmjs.org/mysql/-/mysql-2.18.1.tgz",
"integrity": "sha512-Bca+gk2YWmqp2Uf6k5NFEurwY/0td0cpebAucFpY/3jhrwrVGuxU2uQFCHjU19SJfje0yQvi+rVWdq78hR5lig==",
"requires": {
"bignumber.js": "9.0.0",
"readable-stream": "2.3.7",
"safe-buffer": "5.1.2",
"sqlstring": "2.3.1"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"object-inspect": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
@@ -884,6 +1040,11 @@
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -917,6 +1078,27 @@
"unpipe": "1.0.0"
}
},
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -980,11 +1162,31 @@
"object-inspect": "^1.9.0"
}
},
"sqlstring": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz",
"integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A="
},
"statuses": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@@ -1004,6 +1206,11 @@
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
"integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",

View File

@@ -9,6 +9,8 @@
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.1"
"cors": "^2.8.5",
"express": "^4.18.1",
"mysql": "^2.18.1"
}
}

118
backend/routes/api/entry.js Normal file
View File

@@ -0,0 +1,118 @@
// Dependencies
const express = require("express");
const mysql = require("../../handlers/mysql_handler");
const logger = require("../../logger");
const request_handler = require("../../handlers/request_handler");
// Global variables
const con = mysql.con;
const router = express.Router();
function formatDate(date) {
let result = date.split("T")[0].split("-");
result = result[2] + "." + result[1] + "." + result[0];
return result;
}
// Calculate time difference
function calcInd(date, checkedIn, checkedOut) {
date = date.split("T")[0];
let ind =
(new Date(date + "T" + checkedOut) - new Date(date + "T" + checkedIn)) /
1000 /
60 /
60;
// round ind to 2 decimal places
ind = Math.round(ind * 100) / 100;
if (ind > 6) {
ind -= 0.5;
}
return ind;
}
//! Bei dieser Funktion habe 112312 Gehirnzellen verloren. Bitte nicht drauf ansprechen.
function calcNorm(date, checkedIn, checkedOut) {
let ind = calcInd(date, checkedIn, checkedOut).toString().split(".");
let hours = parseInt(ind[0]);
let minutes = 0;
if (ind.length > 1) {
minutes = parseInt(ind[1] * 6) / 10;
minutes = Math.round(minutes);
}
if (hours < 10) {
hours = "0" + hours;
}
if (minutes < 10) {
minutes = "0" + minutes;
}
return hours + ":" + minutes;
}
function formatTime(time) {
let result = time.split(":");
result = result[0] + ":" + result[1];
return result;
}
function isInMonth(date, monthYear) {
if (monthYear == undefined) {
return true;
}
date = date.split("-");
let month = parseInt(date[1]);
let year = parseInt(date[0]);
monthYear = monthYear.split("-");
if (month == monthYear[0] && year == monthYear[1]) {
return true;
} else {
return false;
}
}
// monthYear = month number-year number (04-2019)
router.get("/all/:monthYear?", request_handler.LoggerHandler, (req, res) => {
const monthYear = req.params.monthYear;
con.query(`SELECT * FROM entries ORDER BY date ASC`, (err, result) => {
if (err) {
logger.error(err, "routes/api/entries.js");
res.send(JSON.parse(JSON.stringify({ error: err }))); // Send error message
return;
} else {
let entries = [];
result = JSON.parse(JSON.stringify(result));
for (let i = 0; i < result.length; i++) {
if (isInMonth(result[i].date, monthYear)) {
entries.push({
date: formatDate(result[i].date),
checkedIn: formatTime(result[i].checked_in),
checkedOut: formatTime(result[i].checked_out),
ind: calcInd(
result[i].date,
result[i].checked_in,
result[i].checked_out
),
norm: calcNorm(
result[i].date,
result[i].checked_in,
result[i].checked_out
),
});
}
}
res.send(entries); // Send result
}
});
});
module.exports = router;

View File

@@ -13,6 +13,7 @@
"@testing-library/user-event": "^13.5.0",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"sass": "^1.51.0",
"web-vitals": "^2.1.4"
@@ -7991,6 +7992,14 @@
"he": "bin/he"
}
},
"node_modules/history": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz",
"integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==",
"dependencies": {
"@babel/runtime": "^7.7.6"
}
},
"node_modules/hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@@ -13425,6 +13434,30 @@
"node": ">=0.10.0"
}
},
"node_modules/react-router": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.3.0.tgz",
"integrity": "sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==",
"dependencies": {
"history": "^5.2.0"
},
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/react-router-dom": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.3.0.tgz",
"integrity": "sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==",
"dependencies": {
"history": "^5.2.0",
"react-router": "6.3.0"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/react-scripts": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
@@ -21933,6 +21966,14 @@
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
},
"history": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz",
"integrity": "sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==",
"requires": {
"@babel/runtime": "^7.7.6"
}
},
"hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@@ -25729,6 +25770,23 @@
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
"integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A=="
},
"react-router": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.3.0.tgz",
"integrity": "sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==",
"requires": {
"history": "^5.2.0"
}
},
"react-router-dom": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.3.0.tgz",
"integrity": "sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==",
"requires": {
"history": "^5.2.0",
"react-router": "6.3.0"
}
},
"react-scripts": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",

View File

@@ -8,6 +8,7 @@
"@testing-library/user-event": "^13.5.0",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"sass": "^1.51.0",
"web-vitals": "^2.1.4"

View File

@@ -1,15 +1,24 @@
import "./css/App.scss";
import "./css/app.scss";
import ServerProvider from "./contexts/ServerContext";
import Table from "./components/table/Table";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
function App() {
return (
<div id="app">
<div id="content" className="app-container">
<h1>Hello World</h1>
<ServerProvider>
<div id="app">
<div id="content" className="app-container">
<Router>
<Routes>
<Route exact path="/table/:monthYear" element={<Table />} />
</Routes>
</Router>
</div>
<div id="sidebar" className="app-container">
<h1>Sidebar</h1>
</div>
</div>
<div id="sidebar" className="app-container">
<h1>Sidebar</h1>
</div>
</div>
</ServerProvider>
);
}

View File

@@ -0,0 +1,25 @@
import "../../css/components/table.scss";
function Header({ date, checkedIn, checkedOut, ind, norm }) {
return (
<div className="table-header">
<div className="table-cell">
<span>{date}</span>
</div>
<div className="table-cell">
<span>{checkedIn}</span>
</div>
<div className="table-cell">
<span>{checkedOut}</span>
</div>
<div className="table-cell">
<span>{ind}</span>
</div>
<div className="table-cell">
<span>{norm}</span>
</div>
</div>
);
}
export default Header;

View File

@@ -0,0 +1,25 @@
import "../../css/components/table.scss";
function Row({ date, checkedIn, checkedOut, ind, norm }) {
return (
<div className="table-row">
<div className="table-cell">
<span>{date ? date : "-"}</span>
</div>
<div className="table-cell">
<span>{checkedIn ? checkedIn : "-"}</span>
</div>
<div className="table-cell">
<span>{checkedOut ? checkedOut : "-"}</span>
</div>
<div className="table-cell">
<span>{ind ? ind : "-"}</span>
</div>
<div className="table-cell">
<span>{norm ? norm : "-"}</span>
</div>
</div>
);
}
export default Row;

View File

@@ -0,0 +1,81 @@
import React, { useState, useContext, useEffect } from "react";
import { ServerContext } from "../../contexts/ServerContext";
import { useParams } from "react-router-dom";
import "../../css/components/table.scss";
import Row from "./Row";
import Header from "./Header";
function Table() {
const params = useParams();
const { URL } = useContext(ServerContext);
const [entries, setEntries] = useState([]);
const [monthYear, setMonthYear] = useState(params.monthYear);
async function fetchEntries() {
let response = await fetch(URL + `/api/entry/all/` + monthYear);
let data = await response.json();
setEntries(data);
}
useEffect(() => {
fetchEntries();
}, []);
function nextMonth() {
let month = parseInt(monthYear.split("-")[0]);
if (month === 12) {
window.location.href = `/table/${1}-${
parseInt(monthYear.split("-")[1]) + 1
}`;
} else {
window.location.href = `/table/${month + 1}-${monthYear.split("-")[1]}`;
}
}
function previousMonth() {
let month = parseInt(monthYear.split("-")[0]);
if (month === 1) {
window.location.href = `/table/${12}-${
parseInt(monthYear.split("-")[1]) - 1
}`;
} else {
window.location.href = `/table/${month - 1}-${monthYear.split("-")[1]}`;
}
}
return (
<div id="table">
<div className="table-nav">
<div onClick={previousMonth}> </div>
<h2>{monthYear}</h2>
<div onClick={nextMonth}> </div>
</div>
<div className="table-container">
<Header
date="Date"
checkedIn="Checked In"
checkedOut="Checked Out"
ind="Ind."
norm="Norm."
/>
{/* ROWS */}
{entries.map((entry, index) => (
<Row
date={entry.date}
checkedIn={entry.checkedIn}
checkedOut={entry.checkedOut}
ind={entry.ind}
norm={entry.norm}
/>
))}
</div>
</div>
);
}
export default Table;

View File

@@ -0,0 +1,14 @@
import React, { createContext } from "react";
export const ServerContext = createContext();
const ServerProvider = (props) => {
const URL = "http://127.0.0.1:8080";
return (
<ServerContext.Provider value={{ URL }}>
{props.children}
</ServerContext.Provider>
);
};
export default ServerProvider;

View File

@@ -0,0 +1,59 @@
#table {
height: 100%;
width: 100%;
.table-nav {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
padding-top: 10px;
padding-bottom: 10px;
div {
background-color: red;
width: 50px;
height: 50px;
display: block;
margin-left: auto;
margin-right: auto;
cursor: pointer;
}
h2 {
text-align: center;
padding-top: 10px;
}
}
.table-container {
height: 100%;
width: 100%;
text-align: center;
font-size: 1rem;
.table-header {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
width: 100%;
background-color: rgb(28, 28, 28);
font-size: 1.2rem;
font-weight: bold;
border-bottom: 2px solid rgb(157, 157, 157);
}
.table-row {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
width: 100%;
background-color: rgb(61, 61, 61);
border-top: 1px solid rgb(85, 85, 85);
}
.table-cell {
grid-area: auto;
padding: 10px;
border-right: 1px solid rgb(85, 85, 85);
border-left: 1px solid rgb(85, 85, 85);
}
}
}