diff --git a/.glitch-assets b/.glitch-assets new file mode 100644 index 0000000..f10dfde --- /dev/null +++ b/.glitch-assets @@ -0,0 +1,6 @@ +{"name":"PlayfairDisplay-VariableFont_wght.ttf","date":"2022-09-03T04:32:41.366Z","url":"https://cdn.glitch.global/be901791-b6e1-44e8-beb5-89b142958435/PlayfairDisplay-VariableFont_wght.ttf","type":"","size":298268,"thumbnail":"https://cdn.glitch.global/be901791-b6e1-44e8-beb5-89b142958435/thumbnails%2FPlayfairDisplay-VariableFont_wght.ttf","thumbnailWidth":210,"thumbnailHeight":210,"uuid":"mWxsKHIQ74T9d9rl"} +{"uuid":"mWxsKHIQ74T9d9rl","deleted":true} +{"name":"PlayfairDisplay.ttf","date":"2022-09-03T04:32:41.366Z","url":"https://cdn.glitch.global/be901791-b6e1-44e8-beb5-89b142958435/PlayfairDisplay.ttf","type":"","size":298268,"thumbnail":"https://cdn.glitch.global/be901791-b6e1-44e8-beb5-89b142958435/thumbnails%2FPlayfairDisplay-VariableFont_wght.ttf","thumbnailWidth":210,"thumbnailHeight":210,"uuid":"mWxsKHIQ74T9d9rl"} +{"name":"clock_in.png","date":"2022-09-03T04:42:30.892Z","url":"https://cdn.glitch.global/be901791-b6e1-44e8-beb5-89b142958435/clock_in.png","type":"image/png","size":289282,"imageWidth":625,"imageHeight":382,"thumbnail":"https://cdn.glitch.global/be901791-b6e1-44e8-beb5-89b142958435/thumbnails%2Fclock_in.png","thumbnailWidth":330,"thumbnailHeight":202,"uuid":"oZkxhVFVeMXRiqCi"} +{"name":"background.jpg","date":"2022-09-04T04:58:52.169Z","url":"https://cdn.glitch.global/be901791-b6e1-44e8-beb5-89b142958435/background.jpg","type":"image/jpeg","size":43861,"imageWidth":1332,"imageHeight":850,"thumbnail":"https://cdn.glitch.global/be901791-b6e1-44e8-beb5-89b142958435/thumbnails%2Fbackground.jpg","thumbnailWidth":330,"thumbnailHeight":211,"uuid":"84tw5BA1YBjGZBjB"} +{"name":"PlayfairDisplay-BoldItalic.ttf","date":"2022-09-08T12:48:32.612Z","url":"https://cdn.glitch.global/7e6532ba-8536-466c-a6ac-f63ef1409fbc/PlayfairDisplay-BoldItalic.ttf","type":"","size":177660,"thumbnail":"https://cdn.glitch.global/7e6532ba-8536-466c-a6ac-f63ef1409fbc/thumbnails%2FPlayfairDisplay-BoldItalic.ttf","thumbnailWidth":210,"thumbnailHeight":210,"uuid":"Dx78QXswdOeDSrj6"} diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..b58b603 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/a2-shortstack.iml b/.idea/a2-shortstack.iml new file mode 100644 index 0000000..0c8867d --- /dev/null +++ b/.idea/a2-shortstack.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..8934f6c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index f229958..fa437e6 100644 --- a/README.md +++ b/README.md @@ -1,98 +1,56 @@ -Assignment 2 - Short Stack: Basic Two-tier Web Application using HTML/CSS/JS and Node.js -=== +# Assignment 2 - Short Stack: Basic Two-tier Web Application using HTML/CSS/JS and Node.js Due: September 8th, by 11:59 AM. -This assignment aims to introduce you to creating a prototype two-tiered web application. -Your application will include the use of HTML, CSS, JavaScript, and Node.js functionality, with active communication between the client and the server over the life of a user session. +## Activity Logger Project -Baseline Requirements ---- +Nicholas Li +https://a2-livingwell088.glitch.me/ -There is a large range of application areas and possibilities that meet these baseline requirements. -Try to make your application do something useful! A todo list, storing / retrieving high scores for a very simple game... have a little fun with it. +
I wanted to create an application that can be used to log in activities done throughout one's everyday. +
It would sort of be like the opposite of a calendar since you should log in your hours after the activity has been done as opposed to a calendar, where you would schedule ahead of time. -Your application is required to implement the following functionalities: +My application has: +
- A "Server" that serves file and keeps track of tabular data in "appdata" with 6 fields ("activity", "date", "time_starteded", "time_ended", "description", and "duration") +
- A "Results" in the form of a table that shows the data on the table from appdata of the server +
- A "Form/Entry" that allows users to add and delete data +
- A "Derived Field" that calculates the duration of the activity that was logged from the starting time and ending time -- 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 (complete at least two) 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 `todo` dataset with `task`, `priority`, and `creation_date` may generate a new field `deadline` by looking at `creation_date` and `priority` +I used one HTML form that includes different type like ("text" for a textbox, "select" for a dropdown select box, "date", and "time") +I did not use a results page since I put both the form and the results on the same page with the results table underneath the form. +I made sure that the pages validate using the link given -Your application is required to demonstrate the use of the following concepts: +I used a grid for the positioning of my form. +I used a custom font from google fonts "PlayfairDisplay" and "PlayfairDisplay-BoldItalics" instead of the normal font +CSS is all in the style.css +I made sure to use CSS selection for element selectors, ID selectors and Class Selectors. -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 -- A results page displaying all data currently available on the server. You will most likely use a `` tag for this, but `
+ + + + + + + + + +
Activity DoneDateTime StartedTime EndedDescriptionDurationDelete?
+ - diff --git a/public/js/scripts.js b/public/js/scripts.js index de052ea..62def92 100644 --- a/public/js/scripts.js +++ b/public/js/scripts.js @@ -1,3 +1,163 @@ -// Add some Javascript code here, to run on the front end. +// Submit button +const submit = function (e) { + + // prevent default form action from being carried out + e.preventDefault(); + + // Get the variables from the form + const activity = document.querySelector("#activity"); + const date = document.querySelector("#date"); + const time_started = document.querySelector("#time_started"); + const time_ended = document.querySelector("#time_ended"); + const description = document.querySelector("#description"); + + // Get the values into a json + const json = { + activity: activity.value, + date: date.value, + time_started: time_started.value, + time_ended: time_ended.value, + description: description.value, + }, + body = JSON.stringify(json); // Stringify + + fetch("/submit", { + method: "POST", + body, + }) + .then((response) => response.json()) + .then((json) => { + console.log(json); + let count = 0; // Count variable for id of each row + + // Reset the table since I am getting back the entirety of appdata array + let table = document.getElementById("table"); + table.innerHTML = + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "
Activity DoneDateTime StartedTime EndedDescriptionDurationDelete?
"; + + // For each of the items in the array + json.forEach((item) => { + // Add the log to the table + table.innerHTML += + "" + + "" + + item.activity + + "" + + "" + + item.date + + "" + + "" + + item.time_started + + "" + + "" + + item.time_ended + + "" + + "" + + item.description + + "" + + "" + + item.duration + + "" + //(time_duration("2:15", "03: 18")) + " " + + ""; + count++; // Increment the count + }); + }); + + return false; +}; + +window.onload = function () { + const button = document.querySelector("button"); + button.onclick = submit; +}; + + +// Function to delete the row +// I wanted to delete a row and then convert the current table to JSON and send back to server and get the new appdata back (without the deleted row) and recreate but it seems to just reset the appdata back to the starting array +function delete_row(id) { + document.getElementById(id).remove(); // Remove from html table based on the id of the row + + // Create a new json array to send back to the server + let table = document.getElementById("table"); + let newTable = []; + + for (let row = 1; row < table.rows[row].length; row++) { + let curRow = []; + for (let col = 0; col < row.cells.length - 1; col++) { + curRow.push(table.rows[row].cells[col].innerHTML); + } + newTable.push(curRow); + } + + const json = JSON.stringify(newTable); // Stringify + fetch("/delete", { + method: "POST", + json, + }) + // Recreate the table + .then((response) => response.json()) + .then((json) => { + console.log(json); + let count = 0; + let table = document.getElementById("table"); + table.innerHTML = + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "
Activity DoneDateTime StartedTime EndedDescriptionDurationDelete?
"; + json.forEach((item) => { + table.innerHTML += + "" + + "" + + item.activity + + "" + + "" + + item.date + + "" + + "" + + item.time_started + + "" + + "" + + item.time_ended + + "" + + "" + + item.description + + "" + + "" + + item.duration + + "" + //(time_duration("2:15", "03: 18")) + " " + + ""; + count++; + }); + }); + + return false; +} -console.log("Welcome to assignment 2!") \ No newline at end of file diff --git a/server.improved.js b/server.improved.js index 26673fc..e645fe1 100644 --- a/server.improved.js +++ b/server.improved.js @@ -1,72 +1,166 @@ -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 appdata = [ - { 'model': 'toyota', 'year': 1999, 'mpg': 23 }, - { 'model': 'honda', 'year': 2004, 'mpg': 30 }, - { 'model': 'ford', 'year': 1987, 'mpg': 14} +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; + +let appdata = [ + // { 'model': 'toyota', 'year': 1999, 'mpg': 23 }, + // { 'model': 'honda', 'year': 2004, 'mpg': 30 }, + // { 'model': 'ford', 'year': 1987, 'mpg': 14} + + + // Starting data + + + { + activity: "Sleep", + date: "2022-09-07", + time_started: "19:15", + time_ended: "19:15", + description: "test", + duration: "0 Hour 0 Minutes", + }, + { + activity: "Food", + date: "2022-09-07", + time_started: "20:15", + time_ended: "21:15", + description: "test", + duration: "1 Hour 0 Minutes", + }, + { + activity: "Work", + date: "2022-09-07", + time_started: "07:00", + time_ended: "19:50", + description: "test", + duration: "12 Hour 50 Minutes", + }, ] -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 server = http.createServer(function (request, response) { + if (request.method === "GET") { + handleGet(request, response); + } else if (request.method === "POST") { + handlePost(request, response); - if( request.url === '/' ) { - sendFile( response, 'public/index.html' ) - }else{ - sendFile( response, filename ) } -} - -const handlePost = function( request, response ) { - let dataString = '' +}); - request.on( 'data', function( data ) { - dataString += data - }) - - request.on( 'end', function() { - console.log( JSON.parse( dataString ) ) - - // ... do something with the data here!!! - - response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }) - response.end() - }) -} +const handleGet = function (request, response) { + const filename = dir + request.url.slice(1); -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 ) + if (request.url === "/") { + sendFile(response, "public/index.html"); + } else { + sendFile(response, filename); + } +}; + +const handlePost = function (request, response) { + // If submit button + if (request.url === "/submit") { + let dataString = ""; + + request.on("data", function (data) { + dataString += data; + }); + + request.on("end", function () { + let data = JSON.parse(dataString); // Parse the new log + data.duration = time_duration(data.time_started, data.time_ended); // Get the derived field from the starting time and ending time + appdata.push(data); // Add to the array + console.log(JSON.stringify(appdata)); + response.writeHead(200, "OK", { "Content-Type": "text/plain" }); + response.end(JSON.stringify(appdata)); // Write back to the client + }); + } + // If the delete button + else if (request.url === "/delete") { + appdata = []; // Reset the appdata + console.log(appdata); + let dataString = ""; + + request.on("data", function (data) { + dataString += data; + }); + + request.on("end", function () { + console.log(JSON.stringify(appdata)); + response.writeHead(200, "OK", { "Content-Type": "text/plain" }); + response.end(JSON.stringify(appdata)); // Write back to the client + }); + } +}; + +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"); + } + }); +}; + +// Function that calculates the duration of the event logged automatically +function time_duration(start, end) { + // Parse the inputs into their hours and minutes + let start_hour = parseInt(start.split(":")[0]); + let start_min = parseInt(start.split(":")[1]); + + let end_hour = parseInt(end.split(":")[0]); + let end_min = parseInt(end.split(":")[1]); + + let dur_hour; + let dur_min; + + // Find the hours and minutes of duration + if (end_hour > start_hour) { + if (end_min >= start_min) { + dur_min = end_min - start_min; + dur_hour = end_hour - start_hour; + } else { + dur_hour = end_hour - start_hour - 1; + dur_min = end_min + 60 - start_min; + } + } else if (end_hour == start_hour) { + if (end_min >= start_min) { + dur_min = end_min - start_min; + dur_hour = 0; + } else { + dur_hour = 23; + dur_min = end_min + 60 - start_min; + } + } else { + if (end_min >= start_min) { + dur_min = end_min - start_min; + dur_hour = end_hour + 24 - start_hour; + } else { + dur_hour = end_hour + 24 - start_hour - 1; + dur_min = end_min + 60 - start_min; + } - }else{ + } + // Return + return ( + dur_hour.toString() + + " Hours " + + dur_min.toString() + + " Minutes" + ).toString(); - // file not found, error code 404 - response.writeHeader( 404 ) - response.end( '404 Error: File Not Found' ) - } - }) } -server.listen( process.env.PORT || port ) +server.listen( process.env.PORT || port ) \ No newline at end of file diff --git a/shrinkwrap.yaml b/shrinkwrap.yaml new file mode 100644 index 0000000..43c1986 --- /dev/null +++ b/shrinkwrap.yaml @@ -0,0 +1,15 @@ +dependencies: + mime: 2.6.0 +packages: + /mime/2.6.0: + dev: false + engines: + node: '>=4.0.0' + hasBin: true + resolution: + integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== +registry: 'https://registry.npmjs.org/' +shrinkwrapMinorVersion: 9 +shrinkwrapVersion: 3 +specifiers: + mime: ^2.4.4 diff --git a/static/PlayfairDisplay-Black.ttf b/static/PlayfairDisplay-Black.ttf new file mode 100644 index 0000000..ce5e24f Binary files /dev/null and b/static/PlayfairDisplay-Black.ttf differ diff --git a/static/PlayfairDisplay-BlackItalic.ttf b/static/PlayfairDisplay-BlackItalic.ttf new file mode 100644 index 0000000..b31c28f Binary files /dev/null and b/static/PlayfairDisplay-BlackItalic.ttf differ diff --git a/static/PlayfairDisplay-Bold.ttf b/static/PlayfairDisplay-Bold.ttf new file mode 100644 index 0000000..178107b Binary files /dev/null and b/static/PlayfairDisplay-Bold.ttf differ diff --git a/static/PlayfairDisplay-BoldItalic.ttf b/static/PlayfairDisplay-BoldItalic.ttf new file mode 100644 index 0000000..83b5fc3 Binary files /dev/null and b/static/PlayfairDisplay-BoldItalic.ttf differ diff --git a/static/PlayfairDisplay-ExtraBold.ttf b/static/PlayfairDisplay-ExtraBold.ttf new file mode 100644 index 0000000..a1ae826 Binary files /dev/null and b/static/PlayfairDisplay-ExtraBold.ttf differ diff --git a/static/PlayfairDisplay-ExtraBoldItalic.ttf b/static/PlayfairDisplay-ExtraBoldItalic.ttf new file mode 100644 index 0000000..4f2cd01 Binary files /dev/null and b/static/PlayfairDisplay-ExtraBoldItalic.ttf differ diff --git a/static/PlayfairDisplay-Italic.ttf b/static/PlayfairDisplay-Italic.ttf new file mode 100644 index 0000000..5739738 Binary files /dev/null and b/static/PlayfairDisplay-Italic.ttf differ diff --git a/static/PlayfairDisplay-Medium.ttf b/static/PlayfairDisplay-Medium.ttf new file mode 100644 index 0000000..ada0c26 Binary files /dev/null and b/static/PlayfairDisplay-Medium.ttf differ diff --git a/static/PlayfairDisplay-MediumItalic.ttf b/static/PlayfairDisplay-MediumItalic.ttf new file mode 100644 index 0000000..e61f6d7 Binary files /dev/null and b/static/PlayfairDisplay-MediumItalic.ttf differ diff --git a/static/PlayfairDisplay-Regular.ttf b/static/PlayfairDisplay-Regular.ttf new file mode 100644 index 0000000..53876bd Binary files /dev/null and b/static/PlayfairDisplay-Regular.ttf differ diff --git a/static/PlayfairDisplay-SemiBold.ttf b/static/PlayfairDisplay-SemiBold.ttf new file mode 100644 index 0000000..04a0fd1 Binary files /dev/null and b/static/PlayfairDisplay-SemiBold.ttf differ diff --git a/static/PlayfairDisplay-SemiBoldItalic.ttf b/static/PlayfairDisplay-SemiBoldItalic.ttf new file mode 100644 index 0000000..4cd40ae Binary files /dev/null and b/static/PlayfairDisplay-SemiBoldItalic.ttf differ