diff --git a/scoreboard/controllers/db.js b/scoreboard/controllers/db.js new file mode 100644 index 0000000..a135404 --- /dev/null +++ b/scoreboard/controllers/db.js @@ -0,0 +1,46 @@ +"use strict"; + +let db; + +// Import ESM into CJS file using immediately invoked function expression because of missing top-level await in CJS +(async () => { + const { LowSync } = await import('lowdb'); + const { JSONFileSync } = await import('lowdb/node'); + db = new LowSync(new JSONFileSync('db.json'), { teams: []}) +})(); + +function getTeams() { + db.read(); + return db.data.teams +} + +// Adds passed teamName to db if not already existing +function addTeam(teamName) { + console.log(teamName) + db.read(); + if(!db.data.teams.includes(teamName) && teamName != "") { // Teamname not in db + db.data.teams.push(teamName); + db.write(); + } else { // Teamname already in db + } +} + +// Deletes passed teamName from db if existing +function deleteTeam(teamName) { + db.read(); + if(db.data.teams.includes(teamName) && teamName != "") { // Teamname in db + db.data.teams.splice(db.data.teams.indexOf(teamName), 1) // Delete corresponding array entry + db.write(); + } +} + +// Returns important values from db +function getValues() { + db.read(); + console.log(db.data) + return db.data +} + +module.exports = { + getTeams, addTeam, deleteTeam, getValues +} \ No newline at end of file diff --git a/scoreboard/controllers/score.js b/scoreboard/controllers/score.js index 46258f0..cb9533f 100644 --- a/scoreboard/controllers/score.js +++ b/scoreboard/controllers/score.js @@ -1,6 +1,7 @@ const io = require('./socketio'); +const db = require('../controllers/db'); -let enabled = false; +let enabled = true; let teamA = { name: "Team A", name2: "", @@ -112,6 +113,7 @@ function getValues() { teamB: teamB, sideswitch: sideswitch, print: print(), + teams: db.getTeams(), }; } diff --git a/scoreboard/db.json b/scoreboard/db.json new file mode 100644 index 0000000..8d95e21 --- /dev/null +++ b/scoreboard/db.json @@ -0,0 +1,12 @@ +{ + "teams": [ + "Arheilgen", + "Prechtal", + "RVW Merklingen", + "Hofen", + "Leeden", + "Oelde", + "Burgkunstadt", + "Gaustadt" + ] +} \ No newline at end of file diff --git a/scoreboard/package-lock.json b/scoreboard/package-lock.json index 5e9c263..4eea9ad 100644 --- a/scoreboard/package-lock.json +++ b/scoreboard/package-lock.json @@ -14,6 +14,7 @@ "express-ws": "^5.0.2", "hbs": "~4.0.4", "http-errors": "~1.6.3", + "lowdb": "^7.0.1", "moment": "^2.30.1", "morgan": "~1.9.1", "socket.io": "^4.7.5", @@ -469,6 +470,20 @@ "node": ">= 0.10" } }, + "node_modules/lowdb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-7.0.1.tgz", + "integrity": "sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==", + "dependencies": { + "steno": "^4.0.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -843,6 +858,17 @@ "node": ">= 0.6" } }, + "node_modules/steno": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/steno/-/steno-4.0.2.tgz", + "integrity": "sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", diff --git a/scoreboard/package.json b/scoreboard/package.json index 458b6c4..63f7a92 100644 --- a/scoreboard/package.json +++ b/scoreboard/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "private": true, "scripts": { - "start": "nodemon ./bin/www" + "start": "nodemon --ignore '*.json' ./bin/www" }, "dependencies": { "cookie-parser": "~1.4.4", @@ -12,6 +12,7 @@ "express-ws": "^5.0.2", "hbs": "~4.0.4", "http-errors": "~1.6.3", + "lowdb": "^7.0.1", "moment": "^2.30.1", "morgan": "~1.9.1", "socket.io": "^4.7.5", diff --git a/scoreboard/public/javascripts/admin.js b/scoreboard/public/javascripts/admin.js index c6b7492..fe316ce 100644 --- a/scoreboard/public/javascripts/admin.js +++ b/scoreboard/public/javascripts/admin.js @@ -59,10 +59,72 @@ function updateScoreFrontend(values) { document.getElementById("teamBisSpielgemeinschaft").checked = values.teamB.isSpielgemeinschaft; } +// Update DOM for all db contents with the passed values +function updateDbFrontend(values) { + console.log(values); + + let teamAnameDropdown = document.getElementById("teamAnameDropdown"); // Dropdown for TeamA name + let teamAname2Dropdown = document.getElementById("teamAname2Dropdown"); // Dropdown for TeamA name2 + let teamBnameDropdown = document.getElementById("teamBnameDropdown"); // Dropdown for TeamB name + let teamBname2Dropdown = document.getElementById("teamBname2Dropdown"); // Dropdown for TeamB name2 + + // Clear innerHTML of dropdowns + teamAnameDropdown.innerHTML = ""; + teamAname2Dropdown.innerHTML = ""; + teamBnameDropdown.innerHTML = ""; + teamBname2Dropdown.innerHTML = ""; + + let selectDelete = document.getElementById("dbTeamToDelete"); // Select for team deletion from db + selectDelete.innerHTML = "" // Clears all existing options from select + + //
  • Action
  • + + for(team of values.teams) { + // Create custom Dropdowns with different function arguments for team configuration + var li = document.createElement("li"); + li.innerHTML = ` `+ team +``; + teamAnameDropdown.appendChild(li); + var li = document.createElement("li"); + li.innerHTML = ` `+ team +``; + teamAname2Dropdown.appendChild(li); + var li = document.createElement("li"); + li.innerHTML = ` `+ team +``; + teamBnameDropdown.appendChild(li); + var li = document.createElement("li"); + li.innerHTML = ` `+ team +``; + teamBname2Dropdown.appendChild(li); + + // Create select options for deletion in debugwindow + let opt = document.createElement("option"); + opt.value = team; + opt.innerHTML = team; + selectDelete.appendChild(opt); + } +} + +// Inserts the passed name into the passed name text input +async function scoreInsertChosenName(field, name) { + switch(field) { + case 'teamAname': + document.getElementById("teamAname").value = name; + break; + case 'teamAname2': + document.getElementById("teamAname2").value = name; + break; + case 'teamBname': + document.getElementById("teamBname").value = name; + break; + case 'teamBname2': + document.getElementById("teamBname2").value = name; + break; + } +} + // Initial update gets called whenever page has been loaded async function initialUpdate() { timerGetValues(); // Request new values for timer scoreGetValues(); // Request new values for score + dbGetValues(); // Request new values for db contents } // Request monitor refresh for index frontend @@ -194,4 +256,39 @@ async function scoreConfigTeams() { const data = await response.json(); // Wait for asyncronous transfer to complete updateScoreFrontend(data); // Update admin frontend refreshMonitor(); +} + +// ###################################################################################################################################################### +// DB functions +// ###################################################################################################################################################### + +// Request important values for db contents +async function dbGetValues() { + const response = await fetch("/admin/dbGetValues"); // Call API Endpoint /admin/dbGetValues + const data = await response.json(); // Wait for asynchronous transfer to complete + updateDbFrontend(data); // Update admin frontend +} + +// Add team to DB +async function dbAddTeam() { + const response = await fetch("/admin/dbAddTeam", { // Call API Endpoint /admin/scoreAlterScore with the teamname to alter the score in the specified direction + method: 'POST', + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({teamName: document.getElementById("dbTeamToAdd").value}) + }); + document.getElementById("dbTeamToAdd").value = ""; // Empty the text field after sending + const data = await response.json(); // Wait for asyncronous transfer to complete + updateDbFrontend(data); +} + +// Delete team from DB +async function dbDeleteTeam() { + let teamName = document.getElementById("dbTeamToDelete").value; + const response = await fetch("/admin/dbDeleteTeam", { // Call API Endpoint /admin/scoreAlterScore with the teamname to alter the score in the specified direction + method: 'POST', + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({teamName: teamName}) + }); + const data = await response.json(); // Wait for asyncronous transfer to complete + updateDbFrontend(data); } \ No newline at end of file diff --git a/scoreboard/routes/admin.js b/scoreboard/routes/admin.js index 595bb4e..fbdea15 100644 --- a/scoreboard/routes/admin.js +++ b/scoreboard/routes/admin.js @@ -4,6 +4,7 @@ const io = require('../controllers/socketio'); const timer = require('../controllers/timer'); const score = require('../controllers/score'); +const db = require('../controllers/db'); // ###################################################################################################################################################### // General endpoints @@ -123,6 +124,27 @@ router.post('/scoreConfigTeams', function(req, res, next) { res.json(score.getValues()); }); +// ###################################################################################################################################################### +// DB endpoints +// ###################################################################################################################################################### + +// Express router endpoint to get important values for db contents +router.get('/dbGetValues', function(req, res, next) { + res.json(db.getValues()); // Respond with important values for frontend +}); + +// Express router endpoint to add a team to the team database +router.post('/dbAddTeam', function(req, res, next) { + db.addTeam(req.body.teamName); // Add teamname to DB + res.json(db.getValues()); // Respond with all important values to update the frontend +}); + +// Express router endpoint to delete a team from the team database +router.post('/dbDeleteTeam', function(req, res, next) { + db.deleteTeam(req.body.teamName); // Add teamname to DB + res.json(db.getValues()); // Respond with all important values to update the frontend +}); + /* GET home page. */ router.get('/', function(req, res, next) { res.render('admin'); diff --git a/scoreboard/views/admin.hbs b/scoreboard/views/admin.hbs index 781588c..d692dce 100644 --- a/scoreboard/views/admin.hbs +++ b/scoreboard/views/admin.hbs @@ -93,9 +93,9 @@
    - + + -
    @@ -103,13 +103,7 @@
    -
    - - -
    - - - +
    @@ -239,34 +233,49 @@