diff --git a/css/main.css b/css/main.css index f4c93df..b9a9641 100644 --- a/css/main.css +++ b/css/main.css @@ -156,16 +156,39 @@ input, select, textarea { box-shadow: #000000 3px 3px 5px; } +#deleteButton { + border-radius: 5px; + border:0; + min-width: 150px; + min-height: 50px; + background-image: linear-gradient(#c51210, #5c1210); + color: #ffffff; +} + +#deleteButton:hover { + background-image: linear-gradient(#e53333, #d51210); + box-shadow: #000000 3px 3px 5px; +} + #successMessage { background-color: #4c6b22; color: #b3d4fc; - min-height: 50px; + min-height: 30px; } #errorMessage { background-color: darkred; color: #b3d4fc; - min-height: 50px; + min-height: 30px; +} + +.warning { + background-color: darkred; + color: #b3d4fc; + min-height: 30px; + font-weight: bolder; + font-size: 24px; + text-align: center; } .tableCoverArt { diff --git a/delete.html b/delete.html new file mode 100644 index 0000000..72f63d8 --- /dev/null +++ b/delete.html @@ -0,0 +1,310 @@ +<!doctype html> +<html> + +<head> + <meta charset="utf-8"> + <title>Delete Game</title> + <meta name="description" content=""> + <meta name="viewport" content="width=device-width, initial-scale=1"> + + <link rel="manifest" href="site.webmanifest"> + <link rel="apple-touch-icon" href="icon.png"> + + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" + integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous"> + <link rel="stylesheet" href="css/normalize.css"> + <link rel="stylesheet" href="css/main.css"> + + <meta name="theme-color" content="#488ea7"> +</head> + +<body> +<div id="header" class="container"> + <h1>Edit Game</h1> + <hr/> +</div> + +<div class="row mb-3"> + <div class="col"></div> + <div class="col card" id="successMessage"></div> + <div class="col"></div> +</div> + +<div class="row mb-3"> + <div class="col"></div> + <div class="col card" id="errorMessage"></div> + <div class="col"></div> +</div> + +<div class="row mb-3"> + <div class="col"></div> + <div class="col card warning"> + <p>Are you SURE you want to delete this game?</p> + </div> + <div class="col"></div> +</div> + +<div id="form"> + <div class="container"> + + <div class="row mb-3"> + <div class="col"> + <label>Title</label> + </div> + <div class="col"> + <input id="title" type="text"/> + </div> + <div class="col"></div> + </div> + + <div class="row mb-3"> + <div class="col"> + <label> + Developer + </label> + </div> + <div class="col"> + <select id="developer"> + + </select> + </div> + <div class="col"></div> + </div> + + <div class="row mb-3"> + <div class="col"> + <label> + Publisher + </label> + </div> + <div class="col"> + <select id="publisher"> + + </select> + </div> + <div class="col"></div> + </div> + + <div class="row mb-3"> + <div class="col"> + <label>Release Date</label> + </div> + <div class="col"> + <input id="releaseDate" type="date"/> + </div> + <div class="col"></div> + </div> + + <div class="row mb-3"> + <div class="col"> + <label>Supported OSes</label> + </div> + <div class="col"> + <div class="row"> + <div class="col"> + <span>Windows</span> + <input id="win" type="checkbox"/> + </div> + <div class="col"> + <span>MacOS</span> + <input id="mac" type="checkbox"/> + </div> + <div class="col"> + <span>Linux</span> + <input id="linux" type="checkbox"/> + </div> + </div> + </div> + <div class="col"></div> + </div> + + <div class="row mb-3"> + <div class="col"> + <label>Cover Art</label> + </div> + <div class="col"> + <div id="currentArt"></div> + </div> + <div class="col"></div> + </div> + + <div class="row mb-3"> + <div class="col"> + <label>Short Description</label> + </div> + <div class="col"> + <textarea id="shortDescription" rows="5" cols="40" maxlength="300"></textarea> + </div> + <div class="col"></div> + </div> + + <div class="row mb-3"> + <div class="col"> + <label>Long Description</label> + </div> + <div class="col"> + <textarea id="longDescription" rows="15" cols="40"></textarea> + </div> + <div class="col"></div> + </div> + + <div class="row mb-3"> + <div class="col-2 align-content-center"> + <button id="deleteButton" type="submit">Delete</button> + </div> + <div class="col-2 align-content-center"> + <button id="backButton">Back</button> + </div> + <div class="col-8"></div> + </div> + + <hr/> + </div> + + +</div> + +<footer id="footer"> + <p>Page created by <a href="https://lunarpenguin.net/">Kaj Forney</a>, as work for class (GIMM 285).</p> + <p>All example data used is originally from <a href="https://store.steampowered.com/">Steam</a>.</p> +</footer> + +<script src="js/vendor/modernizr-3.11.2.min.js"></script> +<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" + integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" + crossorigin="anonymous" defer></script> + +<script> + const isEmpty = (obj) => Object.keys(obj).length === 0; + + document.getElementById("successMessage").hidden = true; + document.getElementById("errorMessage").hidden = true; + + fetch("http://localhost:8787/developers", { method: 'GET' }) + .then((response) => { + return new Promise((resolve) => response.json() + .then((json) => resolve({ + status: response.status, + json, + }) + )); + }) + .then(({ status, json }) => { + if (200 === status) { + const developerSelect = document.getElementById('developer'); + for (dev of json.data) { + let option = document.createElement("option"); + option.value = dev.id; + option.text = dev.name; + developerSelect.add(option); + } + } + }) + .catch(error => { + console.error('Error:', error); + }); + + fetch("http://localhost:8787/publishers", { method: 'GET' }) + .then((response) => { + return new Promise((resolve) => response.json() + .then((json) => resolve({ + status: response.status, + json, + }) + )); + }) + .then(({ status, json }) => { + if (200 === status) { + const publisherSelect = document.getElementById('publisher'); + for (publisher of json.data) { + let option = document.createElement("option"); + option.value = publisher.id; + option.text = publisher.name; + publisherSelect.add(option); + } + } + }) + .catch(error => { + console.error('Error:', error); + }); + + let urlParams = new URLSearchParams(window.location.search) + fetch("http://localhost:8787/games/" + urlParams.get("id"), {method: 'GET'}) + .then((response) => { + return new Promise((resolve) => response.json() + .then((json) => resolve({ + status: response.status, + json, + }) + )); + }) + .then(({ status, json }) => { + if (200 === status) { + let gameData = json.data[0]; + + if(gameData !== undefined) { + document.getElementById("title").value = gameData.title; + + let releaseDate = new Date(gameData.releaseDate); + document.getElementById("releaseDate").value = releaseDate.toISOString().split("T", 1); + + document.getElementById("win").checked = Boolean(gameData.win); + document.getElementById("mac").checked = Boolean(gameData.mac); + document.getElementById("linux").checked = Boolean(gameData.linux); + + let coverImg = document.createElement('img'); + coverImg.src = "http://localhost:8787/images/" + gameData.coverArt; + document.getElementById("currentArt").appendChild(coverImg); + + document.getElementById("shortDescription").value = gameData.shortDescription; + document.getElementById("longDescription").value = gameData.longDescription; + } else { + document.getElementById("errorMessage").innerHTML = "Invalid App ID!"; + document.getElementById("errorMessage").hidden = false; + } + } + }) + .catch(error => { + console.error('Error:', error); + }); + + document.getElementById('deleteButton').addEventListener('click', (event) => { + const parameters = {}; + + if(urlParams.get("id") !== undefined) { + parameters.id = urlParams.get("id"); + } + + + //Settings for FETCH API request + let fetchSettings = { + method: 'DELETE' + }; + + //Send FETCH API request + fetch("http://localhost:8787/games/" + urlParams.get("id") + (!isEmpty(parameters) ? '?' + new URLSearchParams(parameters) : ''), fetchSettings) + .then((response) => { + return new Promise((resolve) => response.json() + .then((json) => resolve({ + status: response.status, + json, + }) + )); + }) + .then(({status, json}) => { + if (status === 200) { + let message = "Game deletion successful!" + document.getElementById('successMessage').hidden = false; + document.getElementById('successMessage').innerHTML = message; + } + }) + .catch(error => { + console.error('Error:', error); + }); + return; + }); +</script> + +</body> + +</html> diff --git a/insert.html b/insert.html index 638f17f..84edebb 100644 --- a/insert.html +++ b/insert.html @@ -293,8 +293,8 @@ .then(({status, json}) => { if (status === 200) { let message = "Game submission successful!" - document.getElementById('outputMessage').hidden = false; - document.getElementById('outputMessage').innerHTML = message; + document.getElementById('successMessage').hidden = false; + document.getElementById('successMessage').innerHTML = message; } }) .catch(error => { diff --git a/server/index.js b/server/index.js index 81e93ac..265ac25 100644 --- a/server/index.js +++ b/server/index.js @@ -156,7 +156,7 @@ app.post('/games', upload.single('coverArt'), async (request, response) => { console.log(request.file); const errors = validationResult(request); - if(!errors.isEmpty()) { + if (!errors.isEmpty()) { return response .status(400) .setHeader('Access-Control-Allow-Origin', '*') //Prevent CORS error @@ -184,13 +184,13 @@ app.post('/games', upload.single('coverArt'), app.post('/developers', upload.none(), check('name', 'You must enter a name.').isLength({min: 3}), - check('description', 'You must enter a description').isLength({min:5}), + check('description', 'You must enter a description').isLength({min: 5}), check('homepage', 'You must enter a homepage URL.').isURL(), async (request, response) => { const errors = validationResult(request); - if(!errors.isEmpty()) { + if (!errors.isEmpty()) { return response .status(400) .setHeader('Access-Control-Allow-Origin', '*') //Prevent CORS error @@ -218,13 +218,13 @@ app.post('/developers', upload.none(), app.post('/publishers', upload.none(), check('name', 'You must enter a name.').isLength({min: 3}), - check('description', 'You must enter a description').isLength({min:5}), + check('description', 'You must enter a description').isLength({min: 5}), check('homepage', 'You must enter a homepage URL.').isURL(), async (request, response) => { const errors = validationResult(request); - if(!errors.isEmpty()) { + if (!errors.isEmpty()) { return response .status(400) .setHeader('Access-Control-Allow-Origin', '*') //Prevent CORS error @@ -270,7 +270,7 @@ app.put('/games/:id', upload.single('coverArt'), async (request, response) => { const errors = validationResult(request); - if(!errors.isEmpty()) { + if (!errors.isEmpty()) { return response .status(400) .setHeader('Access-Control-Allow-Origin', '*') //Prevent CORS error @@ -296,6 +296,25 @@ app.put('/games/:id', upload.single('coverArt'), } }); +app.delete('/games/:id', upload.none(), + + async (request, response) => { + let result = {}; + try { + result = await games.deleteGame(request.query.id); + } catch (error) { + console.log(error); + return response + .status(500) //Error code when something goes wrong with the server + .setHeader('Access-Control-Allow-Origin', '*') //Prevent CORS error + .json({message: 'Something went wrong with the server.'}); + } + response + .setHeader('Access-Control-Allow-Origin', '*') //Prevent CORS error + .json({message: 'Game deleted successfully!'}); + + }); + app.listen(port, () => { console.log(`Application listening at http://localhost:${port}`); }) diff --git a/server/model/games.js b/server/model/games.js index c93dfb5..8388150 100644 --- a/server/model/games.js +++ b/server/model/games.js @@ -1,5 +1,6 @@ const connection = require('./connection'); const parseText = require('./parseText'); +const {del} = require("express/lib/application"); async function getAllGames(parameters = {}) { let selectSql = `SELECT @@ -186,9 +187,20 @@ async function editGame(id, formInput, imagePath) { return await connection.query(updateSql, queryParameters); } +async function deleteGame(id) { + + let deleteSql = `DELETE FROM games_master_table + WHERE id =` + id; + + console.log(deleteSql); + + return await connection.query(deleteSql); +} + module.exports = { getAllGames, getGame, addNewGame, - editGame + editGame, + deleteGame }