diff --git a/README.md b/README.md index 304a16f1..50ed4cd5 100755 --- a/README.md +++ b/README.md @@ -1,89 +1,16 @@ -Assignment 2 - Short Stack: Basic Two-tier Web Application using HTML/CSS/JS and Node.js -=== +## Generic Leaderboard +https://csmbrad-a2-shortstack.glitch.me/ -Due: September 9th, by 11:59 AM. +This project is meant to be a simple time-based leaderboard. Grids are used to position the elements, and a Table is used to create ther results. +The derived field is calculated server side. there are 2 derived fields, one with the position of the time and the other the difference from first place. +at least, that's how the derived fields should be. +I could not get the results to show up in the table, trying to request them at the start makes the site hang and I can't figure out why. +The server does support adding, removing, and editing entries. -This assignment aims to introduce you to the concepts and practice involved in creating a prototype (i.e. not deployment ready) two-tiered web application. - -The baseline aims of this assignment involve creating an application that demonstrates the use of several specific pieces of HTML, CSS, JavaScript, and Node.js functionality. -Another aim of this assignment is to establish creative boundaries in which you and your partner can explore designing, implementing, and evaluating usable, useful, novel, and technically efficient web applications. - -Baseline Requirements ---- - -Note that there is a very large range of application areas and possibilities that meet these baseline requirements. -Games, internet of things, organizational tools, commerce, media - all are possibilities with a two-tiered form-focused web application. - -Do not limit yourselves to any of the examples given below. -Examples like the upcoming `efficiency_ratio` idea for the `cars` dataset are meant to be illustrative and easy to understand. -They are not intended to be sensible or useful ideas. - -Your application is required to implement the following functionalities: - -- a `Server` which not only serves files, but also maintains a tabular dataset with 3 or more fields related to your application -- a `Results` functionality which shows the entire dataset residing in the server's memory -- a `Form/Entry` functionality which allows a user to add, modify, or delete data items residing in the server's memory -- a `Server Logic` which, upon receiving new or modified "incoming" data, includes and uses a function that adds at least one additional derived field to this incoming data before integrating it with the existing dataset - - the `Derived field` for a new row of data must be computed based on fields already existing in the row. For example, a `cars` dataset with `year`, `horsepower`, and `fuel_efficiency` may create a new field `efficiency_ratio` by dividing `fuel_efficiency` by `horsepower` - -Your application is required to demonstrate the use of the following concepts: - -HTML: -- One or more [HTML Forms](https://developer.mozilla.org/en-US/docs/Learn/HTML/Forms), with any combination of form tags appropriate for the user input portion of the application - - Clarification: the results page can be implemented in any way. `
`s, `table`s, and `list`s are common choices - -CSS: -- CSS styling of the primary visual elements in the application -- Various CSS Selector functionality must be demonstrated: - - Element selectors - - ID selectors - - Class selectors -- CSS positioning and sizing of the primary visual elements in the application: - - CSS to cause at least one element to be horizontally centered on the page - - CSS to cause at least one pair of elements to appear side-by-side - - CSS defined in a maintainable, readable form, in external stylesheets - -JavaScript: -- At minimum, a small amount of front-end JavaScript to get / fetch data from the server; a sample is provided in this repository. - -Node.js: -- An HTTP Server that delivers all necessary files and data for the application. A starting point is provided in this repository. - -Deliverables ---- - -Do the following to complete this assignment: - -1. Fork the starting project code. This repo contains some starter code that may be used or discarded as needed. -2. Implement your project with the above requirements. -3. Test your project to make sure that when someone goes to your main page, it displays correctly. -4. Deploy your project to Glitch, and fill in the appropriate fields in your package.json file. -5. Ensure that your project has the proper naming scheme `a2-yourname` so we can find it. -6. Modify the Readme to the specifications below. -7. Create and submit a Pull Request to the original repo. Label the pull request as follows: a2-gitusername-firstname-lastname - -Sample Readme (delete the above when you're ready to submit, and modify the below so with your links and descriptions) ---- - -## Your Web Application Title -Include a very brief summary of your project here. -Images are encouraged, along with concise, high-level text. - -Here is a sample formula for summarizing your activities, talk about: -- the domain area the project pertains to -- the main challenges or problems the application addresses -- the key innovations that make it possible to address the problem -- the main results of the implementation, does it really address the problem? -- any additional implications of the resulting application, or possibly areas for future work that have been discovered as part of the design and implementation activities - -(Note that when I use the above formula, I aim to have only one sentence per thought in order to remain concise.) - -http://a2-charlieroberts.glitch.me +I believe my sortdata function on the server side is borked. That and my table display doesn't work. I would appreciate feedback on these issues, as I can't figure it out. ## Technical Achievements -- **Tech Achievement 1**: Using a combination of... -- **Tech Achievement 2**: ... +- **Tech Achievement 1**: Created a single page app with an HTML form to input fields and buttons. The server has methods for adding, deleting, and editing entries. ### Design/Evaluation Achievements -- **Design Achievement 1**: Shown in `style.css`, the code... -- **Design Achievement 2**: We tested the application with n=X users, finding that... +- **Design Achievement 1**: Not enough time. diff --git a/public/css/style.css b/public/css/style.css index d5f842ab..dff1c070 100755 --- a/public/css/style.css +++ b/public/css/style.css @@ -1 +1,168 @@ -/*Style your own assignment! This is fun! */ \ No newline at end of file +/*Style your own assignment! This is fun! */ +body { + font-family: "PT Sans Caption", sans-serif; + background: #e1f5fe; +} +h1 { + text-shadow: -2px 2px lightgrey; +} + +.mainGrid { + display: inline-grid; + grid-template-areas: + "header header" + "info results" + "form results" + "blank results"; + grid-template-rows: auto auto auto minmax(10px, auto); + grid-template-columns: 25% 75%; + width: 100%; + height: 100%; + margin: 0; + /*border: 1px dashed lightgray; /* debug */ +} + +.headerGrid { + background: #eeeeee; + grid-area: header; + display: grid; + justify-content: center; + algin-content: center; + margin: 0 0 0.5% 0; + border-radius: 10px; + border: 1px solid #bcbcbc; /* debug */ +} + +.infoGrid { + background: #eeeeee; + grid-area: info; + display: grid; + margin: 0 0 2% 0; + border-top-left-radius: 10px; + border-bottom-left-radius: 10px; + border: 1px solid #bcbcbc; /* debug */ + border-right: 0; +} +.infoTitle { + margin-left: 2.5%; + margin-bottom: -6px; +} +.infoText { + margin-left: 2.5%; +} + +.formGrid { + background: #eeeeee; + grid-area: form; + display: grid; + margin: 0%; + border-top-left-radius: 10px; + border-bottom-left-radius: 10px; + border: 1px solid #bcbcbc; /* debug */ + border-right: 0; +} + +.resultGrid { + background: #eeeeee; + grid-area: results; + display: grid; + margin: 0; + margin-right: 0; + border-top-right-radius: 10px; + border-bottom-right-radius: 10px; + border-bottom-left-radius: 10px; + border: 1px solid #bcbcbc; /* debug */ + grid-template-areas: + "lbTitle" + "lbTable"; + grid-template-rows: 55px auto; +} +.leaderboardTitle { + margin-left: 2.5%; + margin-bottom: 0px; +} +.lbTitle { + grid-area: lbTitle; + +} +.lbTable { + grid-area: lbTable; +} + +table { + *border-collapse: collapse; /* IE7 and lower */ + border-spacing: 0; + width: 95%; + margin-left: 2.5%; + background: ghostwhite; + margin-top: ; +} +th { + border: 1px solid #bcbcbc; + background: #lightgrey; +} +th:first-child { + border-radius: 6px 0 0 0; + border: 1px solid #bcbcbc; +} +th:last-child { + border-radius: 0 6px 0 0; + border: 1px solid #bcbcbc; +} +th:only-child { + border-radius: 6px 6px 0 0; + border: 1px solid #bcbcbc; +} +tr:nth-child(even) {background-color: #f2f2f2;} + +.blankGrid { + /*this is just there so the result grid can expand and the form and info will remain the same */ + grid-area: blank; + display: grid; + margin: 0; + margin-right: 0; + /*border: 1px dashed lightgray; /* debug */ +} + +input { + width: 90%; + margin: 0 5% 0 2.5%; + display: block; +} + +label { + margin: 0 5% 0 2.5%; +} + +button { + width: 90%; + margin: 2% 5% 0 4%; + border-radius: 5px; + + transition-duration: 0.4s; +} + +#submitButton { + background-color: #32cd32; + border: 2px solid #2db92d; +} +#submitButton:hover { + background-color: #2db92d; +} + +#editButton { + background-color: darkOrange; + border: 2px solid #e67e00; +} +#editButton:hover { + background-color: #e67e00; +} + +#delButton { + background-color: #cc0000; + border: 2px solid #990000; + margin-bottom: 2.5%; +} +#delButton:hover { + background-color: #990000; +} diff --git a/public/index.html b/public/index.html index c56d620e..c14d90a8 100755 --- a/public/index.html +++ b/public/index.html @@ -1,41 +1,77 @@ - + CS4241 Assignment 2 - + + + -
- - -
- - - diff --git a/public/js/scripts.js b/public/js/scripts.js index de052eae..2ce0b5f7 100755 --- a/public/js/scripts.js +++ b/public/js/scripts.js @@ -1,3 +1,117 @@ // Add some Javascript code here, to run on the front end. -console.log("Welcome to assignment 2!") \ No newline at end of file +console.log("Welcome to assignment 2!"); + + +const submit = function(e) { + // prevent default form action from being carried out + e.preventDefault(); + + //{ pos: 1, name: "Bill", time: 10, diff: 0, done: "Yes" }, + const input = document.querySelector("#yourname"), + time = document.querySelector("#time"), + finished = document.querySelector("#loads"), + json = { + pos: 0, + name: input.value, + time: time.value, + diff: 0, + done: finished.value + }, + body = JSON.stringify(json); + + fetch("/submit", { + method: "POST", + body + }).then(function(response) { + // do something with the reponse + var incomingData = JSON.parse(response); + createTable(incomingData); + }); + + return false; +}; + +const edit = function(e) { + // prevent default form action from being carried out + e.preventDefault(); + + const input = document.querySelector("#yourname"), + time = document.querySelector("#time"), + finished = document.querySelector("#loads"), + json = { + pos: 0, + name: input.value, + time: time.value, + diff: 0, + done: finished.value + }, + body = JSON.stringify(json); + fetch("/edit", { + method: "POST", + body + }).then(function(response) { + // do something with the reponse + var incomingData = JSON.parse(response); + createTable(incomingData); + }); + + return false; +}; + +const delFunc = function(e) { + // prevent default form action from being carried out + e.preventDefault(); + + const input = document.querySelector("#yourname"), + json = { name: input.value }, + body = JSON.stringify(json); + fetch("/delete", { + method: "POST", + body + }).then(function(response) { + // do something with the reponse + var incomingData = JSON.parse(response); + createTable(incomingData); + }); +}; + +window.onload = function() { + firstTime(); + /*this was meant to try to fetch the data for the first time when the page loads. it doesn't work.*/ + const submitButton = document.querySelector("#submitButton"); + const editButton = document.querySelector("#editButton"); + const delButton = document.querySelector("#delButton"); + + submitButton.onclick = submit; + editButton.onclick = edit; + delButton.onclick = delFunc; +}; + +const createTable = function(data) { + document.getElementById("lbBody").innerHTML = ""; + var html = ""; + data.forEach(function(e, i) { + html += "" + + "" + e.pos + "" + + "" + e.name + "" + + "" + e.time + "" + + "" + e.diff + "" + + "" + e.done + "" + + ""; + }); + document.getElementById("lbBody").innerHTML = html; +}; + +const firstTime = function() { + const json = {start:'start'}, + body = JSON.stringify(json); + fetch("/start", { + method: "POST", + body + }).then(function(response) { + console.log(response); + /*var incomingData = JSON.parse(response); + createTable(incomingData);*/ + }); +} \ No newline at end of file diff --git a/server.improved.js b/server.improved.js index 26673fc0..f2f4bc12 100644 --- a/server.improved.js +++ b/server.improved.js @@ -1,72 +1,130 @@ -const http = require( 'http' ), - fs = require( 'fs' ), - // IMPORTANT: you must run `npm install` in the directory for this assignment - // to install the mime library used in the following line of code - mime = require( 'mime' ), - dir = 'public/', - port = 3000 +const http = require("http"), + fs = require("fs"), + mime = require("mime"), + dir = "public/", + port = 3000; const appdata = [ - { 'model': 'toyota', 'year': 1999, 'mpg': 23 }, - { 'model': 'honda', 'year': 2004, 'mpg': 30 }, - { 'model': 'ford', 'year': 1987, 'mpg': 14} -] - -const server = http.createServer( function( request,response ) { - if( request.method === 'GET' ) { - handleGet( request, response ) - }else if( request.method === 'POST' ){ - handlePost( request, response ) + { pos: 1, name: "Bill", time: 10, diff: 0, done: "Yes" }, + { pos: 2, name: "Bob", time: 14, diff: 4, done: "Yes" }, + { pos: 3, name: "Jeb", time: 36, diff: 26, done: "Yes" } +]; + +const server = http.createServer(function(request, response) { + if (request.method === "GET") { + handleGet(request, response); + } else if (request.method === "POST") { + handlePost(request, response); } -}) +}); -const handleGet = function( request, response ) { - const filename = dir + request.url.slice( 1 ) +const handleGet = function(request, response) { + const filename = dir + request.url.slice(1); - if( request.url === '/' ) { - sendFile( response, 'public/index.html' ) - }else{ - sendFile( response, filename ) + if (request.url === "/") { + sendFile(response, "public/index.html"); + } else if (request.url === "css/style.css") { + sendFile(response, "public/css/style.css"); + } else { + sendFile(response, filename); } -} - -const handlePost = function( request, response ) { - let dataString = '' +}; - request.on( 'data', function( data ) { - dataString += data - }) +const handlePost = function(request, response) { + let dataString = ""; - request.on( 'end', function() { - console.log( JSON.parse( dataString ) ) + request.on("data", function(data) { + dataString += data; + console.log(dataString); + }); + request.on("end", function() { + let incoming = JSON.parse(dataString); + let responseStr = ""; // ... do something with the data here!!! - - response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }) - response.end() - }) -} - -const sendFile = function( response, filename ) { - const type = mime.getType( filename ) - - fs.readFile( filename, function( err, content ) { - - // if the error = null, then we've loaded the file successfully - if( err === null ) { - - // status code: https://httpstatuses.com - response.writeHeader( 200, { 'Content-Type': type }) - response.end( content ) - - }else{ - - // file not found, error code 404 - response.writeHeader( 404 ) - response.end( '404 Error: File Not Found' ) - - } - }) -} - -server.listen( process.env.PORT || port ) + if (request.url === "/submit") { + //new entry + addEntry(response, incoming); + } else if (request.url === "/edit") { + //edit existing entry + editEntry(response, incoming); + } else if (request.url === "/delete") { + //removing an entry + deleteEntry(response, incoming); + } else if (request.url === "/start") { + //send the data we have right away + sendAppdata(response); + } + }); +}; + +const sendFile = function(response, filename) { + const type = mime.getType(filename); + + fs.readFile(filename, function(err, content) { + // if the error = null, then we've loaded the file successfully + if (err === null) { + // status code: https://httpstatuses.com + response.writeHeader(200, { "Content-Type": type }); + response.end(content); + } else { + // file not found, error code 404 + response.writeHeader(404); + response.end("404 Error: File Not Found"); + } + }); +}; + +server.listen(process.env.PORT || port); + +const sortData = function() { + //sort the list in terms of which has the lowest time. + appdata.sort((e1, e2) => (e1.time > e2.time ? 1 : -1)); + //assigning positon numbers and calculating time differences + for (let i = 0; i < appdata.length; i++) { + appdata[i].pos = i + 1; + if ((i = 0)) { + appdata[i].diff = 0; + } else { + appdata[i].diff = appdata[i].time - appdata[0].time; + } + } +}; + +const sendAppdata = function(response) { + //sortData(); //commenting out sortdata makes it work. no idea why sortdata is bad. + let data = JSON.stringify(appdata); + response.writeHeader(200, { "Content-Type": "text/plain" }); + response.end(data); +}; + +const addEntry = function(response, input) { + appdata.push(input); + sendAppdata(response); +}; + +const editEntry = function(response, input) { + //first search for the entry to see if it exists, if it does modify it. + let found = false; + for (let i = 0; i < appdata.length; i++) { + if (appdata[i].name === input.name) { + //found it, update + found = true; + appdata[i] = input; + } + } + sendAppdata(response); +}; + +const deleteEntry = function(response, input) { + var index = -1; + for (var i = 0; i < appdata.length; i++) { + if (appdata[i]['name'] === input.name) { + index = i; + } + } + if (index > -1) { + appdata.splice(index, 1); + } + sendAppdata(response); +}; diff --git a/shrinkwrap.yaml b/shrinkwrap.yaml new file mode 100644 index 00000000..f6d7ea4f --- /dev/null +++ b/shrinkwrap.yaml @@ -0,0 +1,15 @@ +dependencies: + mime: 2.4.6 +packages: + /mime/2.4.6: + dev: false + engines: + node: '>=4.0.0' + hasBin: true + resolution: + integrity: sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA== +registry: 'https://registry.npmjs.org/' +shrinkwrapMinorVersion: 9 +shrinkwrapVersion: 3 +specifiers: + mime: ^2.4.4