From ca4d5e35d14428f16331d565a6e275a270bd834f Mon Sep 17 00:00:00 2001 From: "Glitch (a2-jchengz)" Date: Mon, 9 Sep 2019 15:47:41 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=92=97=F0=9F=8D=A6=20Updated=20with=20Gli?= =?UTF-8?q?tch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 102 ++++------------ public/css/style.css | 178 +++++++++++++++++++++++++++- public/index.html | 100 +++++++++++----- public/js/scripts.js | 275 ++++++++++++++++++++++++++++++++++++++++++- server.improved.js | 91 ++++++++++++-- shrinkwrap.yaml | 15 +++ 6 files changed, 639 insertions(+), 122 deletions(-) create mode 100644 shrinkwrap.yaml diff --git a/README.md b/README.md index 2c6e21e2..43b00c5a 100755 --- a/README.md +++ b/README.md @@ -1,89 +1,29 @@ -Assignment 2 - Short Stack: Basic Two-tier Web Application using HTML/CSS/JS and Node.js -=== - -Due: September 9th, by 11:59 AM. - -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 +Readme --- -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. Only one member needs to submit a pull request. +## Character Creator +By Justin Cheng {http://a2-jchengz.glitch.me} -Sample Readme (delete the above when you're ready to submit, and modify the below so with your links and descriptions) ---- +This project is a demonstration of a preliminary/prototype two-tier web application. The application itself is called Character Creator, where the user creates a generic character +that has the properties of their name, age, occupation, and level of stats for their strength, dexterity, intelligence, luck (how lucky they are), and oddity (how unsual/weird they are). +Together, the stats sum up to the 'Total' stats value, which is calculated on the server end. After the character is created, the user can view that character in a table of characters, +and edit or delete it. -## 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 +The main issue I had was my lack of familiarity with JS and how to properly send over and process data to and from the "server"(working with fetch, json stringify/parsing). A bit of +trial and error, and the support of friends and the omnipotent Google search engine has lead me to produce a functional produce. There was one major issue that I was not able +to address prior to the submission deadline. For the editing functionality, after clicking the butten to send over the new data to the server, the user is immediately +redirected to a blank page. Upon re-entering the page and clicking on the "See All Characters" button, it is evident, however, that the edits made does go through (provided the +refresh from glitch has not happen). ## Technical Achievements -- **Tech Achievement 1**: Using a combination of... -- **Tech Achievement 2**: ... +- **Using JavaScript to modify HTML & CSS**: I used JavaScript to add more html tags to the original index.html file (instead of creating multiple pages) as well as + chaging the style and modifying the display of certain elements. +- **CSS Animation: Typing Effect**: Using the resource listed below, I was able to stylistically incorporate a different kind of CSS animation into my web app. ### 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... +- **Single HTML File by Design**: The application functions using one html file that is the index. +- **CSS Modifications**: Using CSS, the aesthetics are less mundane but the minimal effects provide a clean and low-clutter view + + +# # Resources +- Typing Effect: https://usefulangle.com/post/85/css-typewriter-animation#targetText=CSS%20Properties%20Being%20Animated,a%20solid%20color%20to%20transparent. \ No newline at end of file diff --git a/public/css/style.css b/public/css/style.css index d5f842ab..1800e26f 100755 --- a/public/css/style.css +++ b/public/css/style.css @@ -1 +1,177 @@ -/*Style your own assignment! This is fun! */ \ No newline at end of file +*{ + margin: 0; + padding: 0; +} + +html { + height: 100%; + position: relative; +} + +body { + width: 100% + margin: 0px; + padding: 0px; + background-color: #262626; + color: #e0e0e0; + + +} + +#container { + display: flex; + justify-content: center; + flex-direction: column; +} +button { + font-family: 'Merriweather', serif; + border: none; + border-radius: 2px; +} +td button { + margin: 3px; +} + +.mod-button { + padding: 5px; +} + +#edit-character-button { + background-color: #4CAF50; +} + +#delete-character { + background-color: #f44336; +} +.gen-button { + background-color: #e0e0e0; + color: #262626; + padding: 10px; +} +#gen-buttons { + margin: 3px; + display: +} + +.action-button { + background-color: #e0e0e0; + color: #262626; + padding: 5px; + margin: 10px; +} +form { + margin: 0 auto; + width: 400px; + padding: 1em; +} + +form div { + margin-top: 1em; +} + +#characters td, th { + font-family: 'Merriweather', serif; + padding: 5px; + text-align: center; +} +#character-form { + display: flex; + flex-direction: column; + +} + +#form-inner { + font-family: 'Merriweather', serif; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} + +#form-inner label { + display: flex; + justify-content: left; +} + +.descriptor { + display: flex; + justify-content: center; + padding-top: 5px; + padding-bottom: 5px; +} + +#editing-character { + +} +#editing-character #form-inner2 { + font-family: 'Merriweather', serif; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; +} +#editing-character #form-inner2 label { + display: flex; + justify-content: left; +} +h1 { + font-family: 'Monserrat', sans-serif; + align: center; +} +/* Typing Effect Inspiration: https://usefulangle.com/post/85/css-typewriter-animation#targetText=CSS%20Properties%20Being%20Animated,a%20solid%20color%20to%20transparent. */ +#typing-title { + text-align: center; + display: inline-block; + font-family: 'Monserrat', sans-serif, monospace; +} + +#typing-text { + display: inline-block; + overflow: hidden; + letter-spacing: 2px; + animation: typing 4s steps(19,end), blink .75s step-end infinite; + white-space: nowrap; + + font-weight: 700; + border-right: 4px solid orange; + box-sizing: border-box; +} +#typing-text2 { + display: inline-block; + overflow: hidden; + letter-spacing: 2px; + animation: typing 3s steps(19,end), blink .75s step-end infinite; + white-space: nowrap; + + font-weight: 700; + border-right: 4px solid orange; + box-sizing: border-box; +} +#typing-text3 { + display: inline-block; + overflow: hidden; + letter-spacing: 2px; + animation: typing 4s steps(20,end), blink .75s step-end infinite; + white-space: nowrap; + + font-weight: 700; + border-right: 4px solid orange; + box-sizing: border-box; +} + +@keyframes typing { + from { + width: 0% + } + to { + width: 100% + } +} + +@keyframes blink { + from, to { + border-color: transparent + } + 50% { + border-color: orange; + } \ No newline at end of file diff --git a/public/index.html b/public/index.html index c56d620e..ad0d31d3 100755 --- a/public/index.html +++ b/public/index.html @@ -1,41 +1,83 @@ - CS4241 Assignment 2 + Character Creator + + + + -
- - -
- - - diff --git a/public/js/scripts.js b/public/js/scripts.js index de052eae..f7a95493 100755 --- a/public/js/scripts.js +++ b/public/js/scripts.js @@ -1,3 +1,274 @@ -// Add some Javascript code here, to run on the front end. +const createCharacter = function( e ) { + // prevent default form action from being carried out + e.preventDefault() + + const newCharacter = { + name: document.getElementById( 'name' ).value, + age: document.getElementById( 'age' ).value, + occupation: document.getElementById( 'occupation' ).value, + strength: document.getElementById( 'strength' ).value, + dexterity: document.getElementById( 'dexterity' ).value, + intelligence: document.getElementById( 'intelligence' ).value, + luck: document.getElementById( 'luck' ).value, + odd: document.getElementById( 'odd' ).value + } + + if ( validateCharacter( newCharacter ) ) { + body = JSON.stringify( newCharacter ) + -console.log("Welcome to assignment 2!") \ No newline at end of file + fetch( '/submit', { + method:'POST', + body + }) + .then( function( response ) { + document.getElementById( 'character-created' ).style.display = "flex" + document.getElementById( 'character-created' ).innerHTML += `
+
+

Welcome to the Overworld, ${newCharacter.name}!

+
` + document.getElementById( 'character-form' ).style.display = "none" + document.getElementById( 'typing-title' ).style.display = "none" + console.log( response ) + }) + } + return false + } + + const validateCharacter = function( newCharacter ) { + if ( newCharacter.name =='' || newCharacter.name.length > 20 ) { + alert( "Please Enter a Name (20 Characters Max)" ) + return false + } + else if ( isNaN( newCharacter.age ) || newCharacter.age == ''){ + alert( "Please Enter a proper Age (numerical value)" ) + return false + } + else if ( newCharacter.occupation =='' || newCharacter.occupation.length > 20 ) { + alert( "Please enter an Occupation (20 Characters Max)" ) + return false + } + else if ( isNaN( newCharacter.strength ) || newCharacter.strength == '' || newCharacter.strength < 0 || newCharacter.strength > 100 ){ + alert( "Please enter a Strength Value between 0 and 100 (inclusive)" ) + return false + } + else if ( isNaN( newCharacter.dexterity ) || newCharacter.dexterity == '' || newCharacter.dexterity < 0 || newCharacter.dexterity > 100 ){ + alert( "Please enter a Dexterity Value between 0 and 100 (inclusive)" ) + return false + } + else if ( isNaN( newCharacter.intelligence ) || newCharacter.intelligence == '' || newCharacter.intelligence < 0 || newCharacter.intelligence > 100 ){ + alert( "Please enter an Intelligence Value between 0 and 100 (inclusive)" ) + return false + } + else if ( isNaN( newCharacter.luck ) || newCharacter.luck == '' || newCharacter.luck < 0 || newCharacter.luck > 100 ){ + alert( "Please enter a Luck Value between 0 and 100 (inclusive)" ) + return false + } + else if ( isNaN( newCharacter.odd ) || newCharacter.odd == '' || newCharacter.odd < 0 || newCharacter.odd > 100 ){ + alert( "Please enter an Oddity Value between 0 and 100 (inclusive)" ) + return false + } + else + return true + + } + const deleteCharacter = function( number ){ + const charNum = { charNum : number } + const body = JSON.stringify( charNum ) + + fetch('/delete', { + method: 'POST', + body + }) + + viewCharacters() + + return false + + } + + + const viewCharacters = function() { + + document.getElementById( 'character-table' ).style.display = "block" + document.getElementById( 'character-form' ).style.display = "none" + document.getElementById( 'typing-title' ).style.display = "none" + document.getElementById( 'edit-character' ).style.display = "none" + document.getElementById( 'character-created' ).style.display = "none" + document.getElementById( 'character-table' ).innerHTML = "
" + + "Name" + + "Age" + + "Occupation" + + "Strength" + + "Dexterity" + + "Intelligence" + + "Luck" + + "Oddity" + + "Total Stats" + + "" + + "" + + const character = fetch('/view-characters', { + method: 'GET' + }).then(resp => resp.json()).then( + data => { console.log(data) + //for (let i = 0; i < data.length; i ++){ + let num = 0 + data.forEach(function(char){ + + let row = "" + + "" + char.name + "" + + "" + char.age + "" + + "" + char.occupation + "" + + "" + char.strength + "" + + "" + char.dexterity + "" + + "" + char.intelligence + "" + + "" + char.luck + "" + + "" + char.odd + "" + + "" + char.total + "" + + ` ` + + ` ` + + "
" + + document.getElementById( 'character-table' ).innerHTML += row + num ++ + }) + }) + + + + return false + + } + const dispEdit = function( charNum ) { + + const character = fetch('/view-characters', { + method: 'GET' + }).then(resp => resp.json()).then( + data => { console.log(data) + let char = data[charNum] + document.getElementById( 'character-table' ).style.display = "none" + document.getElementById( 'character-form' ).style.display = "none" + document.getElementById( 'character-created' ).style.display = "none" + document.getElementById( 'edit-character' ).style.display = "block" + document.getElementById( 'edit-character' ).innerHTML = `
+
+

+ Editing Character... +

+
`+` +
+
+
Basic Info
+
+ + +
+
+ + +
+
+ + +
+
Stats (0 - 100)
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
` + // document.getElementById( 'edit-character' ).style.display = "block" + //document.getElementById( 'edit-character' ).innerHTML = `` + + }) + + + + return false + } + + const editCharacter = function( number ){ + const edits = { + name: document.getElementById( 'edit-name' ).value, + age: document.getElementById( 'edit-age' ).value, + occupation: document.getElementById( 'edit-occupation' ).value, + strength: document.getElementById( 'edit-strength' ).value, + dexterity: document.getElementById( 'edit-dexterity' ).value, + intelligence: document.getElementById( 'edit-intelligence' ).value, + luck: document.getElementById( 'edit-luck' ).value, + odd: document.getElementById( 'edit-odd' ).value + /*name: 'test', + age: 2, + occupation: 'testing', + strength: 2, + dexterity: 2, + intelligence: 2, + luck: 2, + odd: 2*/ + } + + if ( validateCharacter(edits) ) { + debugger + const char = { charNum : number, charEdit: edits} + const body = JSON.stringify( char ) + + fetch('/edit', { + method: 'POST', + body + }) + } + location.reload() + viewCharacters() + return false + + } + const mainScreen = function() { + + document.getElementById( 'character-table' ).style.display = "none" + document.getElementById( 'character-form' ).style.display = "flex" + document.getElementById( 'character-created' ).style.display = "none" + document.getElementById( 'edit-character' ).style.display = "none" + location.reload() + return false + } + + window.onload = function() { + const createButton = document.getElementById( 'create-character' ) + createButton.onclick = createCharacter + + /*const deleteButton = document.getElementById( 'delete-character' ) + deleteButton.onclick = deleteCharacter + + const editButton = document.getElementById( 'edit-character' ) + editButton.onclick = editCharacter*/ + + const characterFormButton = document.getElementById( 'to-character-form' ) + characterFormButton.onclick = mainScreen + + const viewAllCharactersButton = document.getElementById( 'view-characters' ) + viewAllCharactersButton.onclick = viewCharacters + + document.getElementById( 'character-table' ).style.display = "none" + document.getElementById( 'character-form' ).style.display = "flex" + document.getElementById( 'character-created' ).style.display = "none" + document.getElementById( 'edit-character' ).style.display = "none" + + } diff --git a/server.improved.js b/server.improved.js index 26673fc0..5dfa0582 100644 --- a/server.improved.js +++ b/server.improved.js @@ -7,9 +7,8 @@ const http = require( 'http' ), port = 3000 const appdata = [ - { 'model': 'toyota', 'year': 1999, 'mpg': 23 }, - { 'model': 'honda', 'year': 2004, 'mpg': 30 }, - { 'model': 'ford', 'year': 1987, 'mpg': 14} + { 'name': 'John Doe', 'age': 21, 'occupation': 'Student', 'strength': 50, 'dexterity': 45, 'intelligence': 38, 'luck': 25, 'odd': 15, 'total': 173}, + { 'name': 'Jane Doe', 'age': 21, 'occupation': 'Student', 'strength': 55, 'dexterity': 40, 'intelligence': 38, 'luck': 30, 'odd': 15, 'total': 178 } ] const server = http.createServer( function( request,response ) { @@ -25,12 +24,21 @@ const handleGet = function( request, response ) { if( request.url === '/' ) { sendFile( response, 'public/index.html' ) - }else{ + } + else if ( request.url === '/view-characters' ){ + const type = mime.getType( appdata ) + response.writeHeader( 200, { 'Content-Type': type } ) + response.write( JSON.stringify( appdata ) ) + console.log( JSON.stringify( appdata ) ) + response.end() + } + else{ sendFile( response, filename ) } } const handlePost = function( request, response ) { + debugger let dataString = '' request.on( 'data', function( data ) { @@ -38,13 +46,78 @@ const handlePost = function( request, response ) { }) request.on( 'end', function() { - console.log( JSON.parse( dataString ) ) + + if ( request.url === '/submit') { + let inputData = JSON.parse( dataString ) + let totalStats = calculateStats(parseInt(inputData.strength), parseInt(inputData.dexterity), parseInt(inputData.intelligence), parseInt(inputData.luck), parseInt(inputData.odd)) + const newChar = {'name': inputData.name, + 'age': inputData.age, + 'occupation': inputData.occupation, + 'strength': inputData.strength, + 'dexterity': inputData.dexterity, + 'intelligence': inputData.intelligence, + 'luck': inputData.luck, + 'odd': inputData.odd, + 'total': totalStats + } + console.log( newChar ) + + appdata.push( newChar ) + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }) + response.end() + + } + else if ( request.url === '/delete') { + let inputData = JSON.parse( dataString ) + let index = inputData.charNum + + //console.log( "Attempting to delete: " + inputData ) + + if ( index > -1 ) { + appdata.splice( index, 1 ) + } + + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }) + response.end() + + } + else if ( request.url === '/edit') { + debugger + let inputData = JSON.parse( dataString ) + console.log(inputData) + let index = inputData.charNum + console.log(index) + let totalStats = calculateStats(parseInt(inputData.charEdit.strength), parseInt(inputData.charEdit.dexterity), parseInt(inputData.charEdit.intelligence), parseInt(inputData.charEdit.luck), parseInt(inputData.charEdit.odd)) + const edited = {'name': inputData.charEdit.name, + 'age': inputData.charEdit.age, + 'occupation': inputData.charEdit.occupation, + 'strength': inputData.charEdit.strength, + 'dexterity': inputData.charEdit.dexterity, + 'intelligence': inputData.charEdit.intelligence, + 'luck': inputData.charEdit.luck, + 'odd': inputData.charEdit.odd, + 'total': totalStats + } + console.log(edited) + if ( index > -1 ) { + appdata.splice(index, 1, edited) + console.log(appdata) + } + response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }) + response.end() + + } + else + // nothing happens + response.end() + + }) +} - // ... do something with the data here!!! +const calculateStats = function (str, dex, int, luk, odd) { + + return str + dex + int + luk + odd - response.writeHead( 200, "OK", {'Content-Type': 'text/plain' }) - response.end() - }) } const sendFile = function( response, filename ) { diff --git a/shrinkwrap.yaml b/shrinkwrap.yaml new file mode 100644 index 00000000..86c7048d --- /dev/null +++ b/shrinkwrap.yaml @@ -0,0 +1,15 @@ +dependencies: + mime: 2.4.4 +packages: + /mime/2.4.4: + dev: false + engines: + node: '>=4.0.0' + hasBin: true + resolution: + integrity: sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== +registry: 'https://registry.npmjs.org/' +shrinkwrapMinorVersion: 9 +shrinkwrapVersion: 3 +specifiers: + mime: ^2.4.4