From af22151b96f742a6522650367f649eee3406166d Mon Sep 17 00:00:00 2001 From: sidneygoldinger <98048123+sidneygoldinger@users.noreply.github.com> Date: Sat, 1 Oct 2022 16:58:59 -0400 Subject: [PATCH 1/9] Create client.js --- public/client.js | 314 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 public/client.js diff --git a/public/client.js b/public/client.js new file mode 100644 index 000000000..9d51bbfb2 --- /dev/null +++ b/public/client.js @@ -0,0 +1,314 @@ +// client-side js +// run by the browser each time your view template is loaded + +console.log('hello world :o'); + +/* +// INDEX THINGS +//const userFormLogin = document.querySelector("form"); +//const userFormRegister = document.querySelector("form"); +//const userFormLogin = document.getElementById("login"); +//const userFormRegister = document.getElementById("register") +const loginButton = document.getElementById("submit"); +const registerButton = document.getElementById("register"); + +registerButton.addEventListener("click", event => { + // stop our form submission from refreshing the page + event.preventDefault(); + + fetch("/register", { + method: "POST", + body: JSON.stringify({ + usr: registerButton.elements.username.value, // was userForm? problem? + pwd: registerButton.elements.password.value, // was userForm? problem? + entries: [] + }), + headers: { + "Content-Type": "application/json" + } + }) + .then(response => response.json()) + .then(json => { + if (json.login) { + window.location.href = "tracker"; + } else { + window.alert( + "That user already exists! Maybe try another name?" + ); + } + }); + + // reset form + registerButton.reset(); // was userForm? problem? +}); +*/ + +// MAIN THINGS + +// our default array of dreams +const dreams = [] + +// define variables that reference elements on our page +const dreamsList = document.getElementById('dreams'); +let totalDreams = document.getElementById('dreams'); +const dreamsForm = document.forms[0]; +const dreamInput = dreamsForm.elements['dream']; +const logoutButton = document.getElementById("logout"); + +// a helper function that creates a list item for a given dream +const appendNewDream = function(dream) { + const newListItem = document.createElement('li'); + newListItem.innerHTML = dream.className + ", Level " + dream.classNumber; + dreamsList.appendChild(newListItem); +} + +// to print shit +const printThing = function(thingToPrint) { + const newListItem = document.createElement('li'); + newListItem.innerHTML = thingToPrint; + dreamsList.appendChild(newListItem); +} + +// to print list +const printList = function(listToPrint) { + for(var i = 0; i { + event.preventDefault(); + + fetch("/logout", { + method: "POST", + headers: { + "Content-Type": "application/json" + } + }) + .then(response => response.json()) + .then(json => { + window.location.href = "/" + }) +}) + +// end of logout button attempt + +/* +const addToServer = function(className, classNumber) { + fetch("/addToServer", { + method: "POST", + body: JSON.stringify({ + className: className, + classNumber: classNumber + }), + headers: { + "Content-Type": "application/json" + } + }) +} +*/ + + +fetch("/dreams") + .then(response => response.json()) // parse the JSON from the server + .then(dreams => { + console.log(dreams) + // remove the loading text + dreamsList.firstElementChild.remove(); + + // get server shit + /*fetch("/getInitialDreams", { + method: "GET", + headers: { + "Content-Type": "application/json" + } + }) + .then(response => response.json()) + .then(entries => { + console.log("entries: "); + console.log(entries); + dreams = entries; + //fillPage(entries); + }); + */ + // end of get server shit + + // iterate through every dream and add it to our page + dreams.forEach(appendNewDream); + + // second button attempt for all: + dreamsForm.addEventListener("submit", event => { + event.preventDefault(); + //printThing("submit clicked!"); + + //create new Dream + //let newDream = dreamsForm.elements.dream.value; + let newDreamClassName = dreamsForm.elements.dream.value; + let newDreamClassNumber = dreamsForm.elements.classNumber.value; + const newDream = { + className: newDreamClassName, + classNumber: newDreamClassNumber + } + + // loop through to see if it's an edit or deletion + const dreamsLength = dreams.length; + let wasItDone = false; + for (let i = 0; i < dreamsLength && !wasItDone; i++) { + //printThing("Tryna check smth?"); + console.log("name " + dreams[i].className); + console.log("Number " + dreams[i].classNumber); + if (dreams[i].classNumber == newDreamClassNumber) { + if (dreams[i].className == newDreamClassName) { + //delete + //console.log(dreams.length); + console.log("deleting"); + dreams.splice(i,1); + wasItDone = true; + console.log(dreams.length); + + // communicate w database through other server + fetch("/trynaDeleteSmth", { + method: "POST", + body: JSON.stringify({ + className: newDreamClassName, + classNumber: newDreamClassNumber + }), + headers: { + "Content-Type": "application/json" + } + }) + // end database communication + } + else { + // edit + console.log("editing") + dreams[i].className = newDreamClassName; + wasItDone = true; + // communicate w database through other server + fetch("/trynaEditSmth", { + method: "POST", + body: JSON.stringify({ + className: newDreamClassName, + classNumber: newDreamClassNumber + }), + headers: { + "Content-Type": "application/json" + } + }) + // end database communication + } + } + } + if (wasItDone == false) { + // console.log("tryna add smth"); + dreams.push(newDream); + // communicate w the server w the database I think + fetch("/trynaAddSmth", { + method: "POST", + body: JSON.stringify({ + className: newDreamClassName, + classNumber: newDreamClassNumber + }), + headers: { + "Content-Type": "application/json" + } + }) + } + + // remove everything from screen + while (dreamsList.firstChild) { + dreamsList.removeChild(dreamsList.firstChild); + } + + + //reprint everything + //for (let i = 0; i < dreams.length; i++) { + // appendNewDream(dreams[i]); + //} + dreams.forEach(appendNewDream); + + // reset form + dreamsForm.reset(); + dreamsForm.elements.dream.focus(); + }) + + // listen for the form to be submitted and add a new dream when it is + /*dreamsForm.addEventListener("submit", event => { + // stop our form submission from refreshing the page + event.preventDefault(); + + printThing("submit clicked!"); + + // get dream value and add it to the list + //let newDream = dreamsForm.elements.dream.value; + let newDreamClassName = dreamsForm.elements.dream.value; + let newDreamClassNumber = dreamsForm.elements.classNumber.value; + + let newDream = { + className: newDreamClassName, + classNumber: newDreamClassNumber + } + + dreams.push(newDream); + appendNewDream(newDream); + + // reset form + dreamsForm.reset(); + dreamsForm.elements.dream.focus(); + }); + */ + + //edit things + /*dreamsForm.addEventListener("edit", event => { + // stop our form submission from refreshing the page + event.preventDefault(); + + printThing("edit clicked!"); + + // remove all dreams + while (dreamsList.firstChild) { + dreamsList.removeChild(dreamsList.firstChild); + } + + // find class by level and edit it + for (let i = 0; i < totalDreams.length; i++) { + if (totalDreams[i].classNumber === dreamsForm.elements.classNumber.value) { + totalDreams[i].className = dreamsForm.elements.className.value; + } + } + + // reprint all classes + for (let i = 0; i < totalDreams.length; i++) { + appendNewDream(totalDreams[i]); + } + + // reset form + dreamsForm.reset(); + dreamsForm.elements.dream.focus(); + }); + // edit button ending + */ + + // listen for an edit button to be clicked + dreamsForm.addEventListener("edit", event => { + event.preventDefault(); + + }); + }); + From fb7552304769001ca76fc490d0fa1699af23350f Mon Sep 17 00:00:00 2001 From: sidneygoldinger <98048123+sidneygoldinger@users.noreply.github.com> Date: Sat, 1 Oct 2022 16:59:22 -0400 Subject: [PATCH 2/9] Create index.js --- public/index.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 public/index.js diff --git a/public/index.js b/public/index.js new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/public/index.js @@ -0,0 +1 @@ + From f0d1ba1a4d55e4e877f61392b60cf41b3d6fd0bf Mon Sep 17 00:00:00 2001 From: sidneygoldinger <98048123+sidneygoldinger@users.noreply.github.com> Date: Sat, 1 Oct 2022 16:59:47 -0400 Subject: [PATCH 3/9] Create style.css --- public/style.css | 73 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 public/style.css diff --git a/public/style.css b/public/style.css new file mode 100644 index 000000000..1b496438e --- /dev/null +++ b/public/style.css @@ -0,0 +1,73 @@ +/* styles */ +/* called by your view template */ + +* { + box-sizing: border-box; +} + +body { + font-family: "Benton Sans", "Helvetica Neue", helvetica, arial, sans-serif; + margin: 2em; + background-color:#000; + color:#999; +} + +h1 { + font-style: italic; + color: #373fff; +} + +.bold { + font-weight: bold; +} + +p { + max-width: 600px; +} + +form { + margin-bottom: 25px; + padding: 15px; + background-color: cyan; + display: inline-block; + width: 100%; + max-width: 340px; + border-radius: 3px; +} + +input { + display: block; + margin-bottom: 10px; + padding: 5px; + width: 100%; + border: 1px solid lightgrey; + border-radius: 3px; + font-size: 16px; +} + +button { + font-size: 16px; + border-radius: 3px; + background-color: lightgrey; + border: 1px solid grey; + box-shadow: 2px 2px teal; + cursor: pointer; +} + +button:hover { + background-color: yellow; +} + +button:active { + box-shadow: none; +} + +li { + margin-bottom: 5px; +} + +footer { + margin-top: 50px; + padding-top: 25px; + border-top: 1px solid lightgrey; +} From ca1f723b7611b5d2f67a7eef8c7a4680f2e816b0 Mon Sep 17 00:00:00 2001 From: sidneygoldinger <98048123+sidneygoldinger@users.noreply.github.com> Date: Sat, 1 Oct 2022 17:00:26 -0400 Subject: [PATCH 4/9] Create index.handlebars --- views/index.handlebars | 51 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 views/index.handlebars diff --git a/views/index.handlebars b/views/index.handlebars new file mode 100644 index 000000000..1f7d99b37 --- /dev/null +++ b/views/index.handlebars @@ -0,0 +1,51 @@ + + + + + + + + + Class Records Data Website Login + + + +

Class Data Manager

+
+ +

Welcome to a website to manage and save your classes. Plz sign in.

+ +
+ + + +
+ {{{msg}}} + +


+ +

Or, make a new username and password.

+ + +
+ + + +
+ + + + + From 1c03f7d6a62705f29e2fb1f63dffc45d98659e03 Mon Sep 17 00:00:00 2001 From: sidneygoldinger <98048123+sidneygoldinger@users.noreply.github.com> Date: Sat, 1 Oct 2022 17:00:52 -0400 Subject: [PATCH 5/9] Create main.handlebars --- views/main.handlebars | 75 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 views/main.handlebars diff --git a/views/main.handlebars b/views/main.handlebars new file mode 100644 index 000000000..b5d8c5c62 --- /dev/null +++ b/views/main.handlebars @@ -0,0 +1,75 @@ + + + + + + + + Class Records Data Interaction + + + + {{{msg}}} + +

+

Class Data Manager

+ +

Welcome to a website to manage + and save your classes.

+ + Simple image of an agenda book + +

+ DIRECTIONS: +

+ +

+ Input your class name and number and press submit to see results. If you enter + a new class name with the same class number, we will update that class's name + in the server. If you enter a class name and number that already exist, we will delete it + (if it is the first class name and number listed here. You cannot delete newer + classes without deleting those above them). +

+
+ +
+ + + +
+ +
+ +
+ +



+
+ +
+ + + +

Learn about the author and her farm here

+ + From e9bcc5a334be5b5fa93e8f3cbf972208c9fddddc Mon Sep 17 00:00:00 2001 From: sidneygoldinger <98048123+sidneygoldinger@users.noreply.github.com> Date: Sat, 1 Oct 2022 17:01:19 -0400 Subject: [PATCH 6/9] Create package.json --- package.json | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 package.json diff --git a/package.json b/package.json new file mode 100644 index 000000000..ac161d892 --- /dev/null +++ b/package.json @@ -0,0 +1,34 @@ +{ + "//1": "describes your app and its dependencies", + "//2": "https://docs.npmjs.com/files/package.json", + "//3": "updating this file will download and update your packages", + "name": "hello-express", + "version": "0.0.1", + "description": "A simple Node app built on Express, instantly up and running.", + "main": "server.js", + "scripts": { + "start": "node server.js" + }, + "dependencies": { + "body-parser": "^1.19.0", + "connect-timeout": "^1.9.0", + "cookie-session": "^2.0.0", + "ejs": "^3.1.8", + "express": "^4.16.4", + "express-handlebars": "^6.0.6", + "lowdb": "^1.0.0", + "mongodb": "^4.10.0" + }, + "engines": { + "node": "16.x" + }, + "repository": { + "url": "https://glitch.com/edit/#!/hello-express" + }, + "license": "MIT", + "keywords": [ + "node", + "glitch", + "express" + ] +} From 92045071285988894e05433c6f3b0a2cde704ced Mon Sep 17 00:00:00 2001 From: sidneygoldinger <98048123+sidneygoldinger@users.noreply.github.com> Date: Sat, 1 Oct 2022 17:01:33 -0400 Subject: [PATCH 7/9] Create robots.txt --- robots.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 robots.txt diff --git a/robots.txt b/robots.txt new file mode 100644 index 000000000..1f53798bb --- /dev/null +++ b/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: / From 55c0a2ef167431e5b7c69a42b8063a02ba6d0106 Mon Sep 17 00:00:00 2001 From: sidneygoldinger <98048123+sidneygoldinger@users.noreply.github.com> Date: Sat, 1 Oct 2022 17:02:02 -0400 Subject: [PATCH 8/9] Create server.js --- server.js | 358 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 server.js diff --git a/server.js b/server.js new file mode 100644 index 000000000..281132523 --- /dev/null +++ b/server.js @@ -0,0 +1,358 @@ +const express = require( 'express' ), + errorhandler = require('errorhandler'), + helmet = require( 'helmet' ), + cookie = require( 'cookie-session' ), + hbs = require( 'express-handlebars' ).engine, + bodyParser = require( 'body-parser' ), + mongodb = require( 'mongodb' ), + timeout = require( 'connect-timeout' ), + app = express() + +const userlogin = { + username: 'user', + password: 'test' +} + +// use express.urlencoded to get data sent by defaut form actions +// or GET requests +app.use( express.urlencoded({ extended:true }) ) +app.use( express.static('public') ) +app.use( express.json() ) +app.use( timeout('30s') ) // this is part of express too? + +// we're going to use handlebars, but really all the template +// engines are equally painful. choose your own poison at: +// http://expressjs.com/en/guide/using-template-engines.html +app.engine( 'handlebars', hbs() ) +app.set( 'view engine', 'handlebars' ) +app.set( 'views', './views' ) + + +// cookie middleware! The keys are used for encryption and should be changed +app.use( cookie({ + name: 'session', + keys: ['firstkey', 'secondkey'] +})) + +if (process.env.NODE_ENV === 'development') { + // only use in development + app.use(errorhandler()) +} + +let dreams = [ + { + className: "calc", + classNumber: "4444" + }, + { + className: "beansClass", + classNumber: "3334" + }, +] + +/* +app.post( '/login', (req,res)=> { + if( req.body.password === 'test' ) { + req.session.login = true + res.redirect( 'main.html' ) + }else{ + req.session.login = false + res.render('index', { msg:'login failed, please try again', layout:false }) + } +}) +*/ + +//logout code + +app.post("/logout", bodyParser.json(), (request, response) => { + if (request.session.login == true) { + request.session.usr = ""; + request.session.login = false; + + response.json({ logout: true }); + }else{ + response.json({ logout: false }) + } +}); + +//logout code ending +app.get("/getInitialDreams", bodyParser.json(), (request, response) => { + console.log("getInitialDreams called!") + collection + .find({ usr: request.session.usr}) + .toArray() + .then(result => { + let entries = result[0].entries; + console.log(result[0]); + console.log(entries); + console.log("entries^"); + response.json(entries); + }); +}); +// get initial dreams + + + +// get initial dreams ending + + +// register code +app.post("/register", bodyParser.json(), (req, res) => { + console.log("registering works!"); + + collection + .find({ usr: req.body.username }) + .toArray() + .then(result => { + if (result.length >= 1) { + console.log(result) + // response.json({ login: false }); + } else { + //user does not exist, create + let newUser = { + usr: req.body.username, + pwd: req.body.password, + entries: [] + }; + + console.log(newUser); + collection.insertOne(newUser); + collection.insertOne(newUser); + console.log("registering"); + console.log(req.body.usr); + req.session.usr = req.body.usr; + console.log("name:"); + console.log(req.session.usr); + console.log(req.session.usr); + req.session.login = true; + //req.json({ login: true }); + + res.redirect( 'index.html' ) + } + }); +}); + +app.post("/login", bodyParser.json(), (req, res) => { + console.log("login works!"); + collection + .find({ usr: req.body.username, pwd: req.body.password }) + .toArray() + .then(result => { + if (result.length >= 1) { + req.session.usr = req.body.username; + req.session.login = true; + console.log("valid login"); + console.log(req.session.usr); + //res.json({ login: true }); + res.redirect( 'main.html' ) + } else { + console.log("invalid login") + res.render('index', { msg:'login failed, please try again', layout:false }) + //res.json({ login: false }); + } + }); +}); +// end of register code + +// getname? + +app.get("/getname", bodyParser.json(), (request, response) => { + console.log("getname called! "); + console.log(request.session.login); + console.log(request.session.usr); + response.json({ usr: request.session.usr }); +}); + +// getname ending + +app.get( '/', (req,res) => { + res.render( 'index', { msg:'', layout:false }) +}) + +// add some middleware that always sends unauthenicaetd users to the login page +app.use( function( req,res,next) { + if( req.session.login === true ) + next() + else + res.render('index', { msg:'login failed, please try again', layout:false }) +}) + +app.get( '/main.html', ( req, res) => { + res.render( 'main', { msg:'success you have logged in', layout:false }) +}) + +// send the default array of dreams to the webpage +app.get("/dreams", (request, response) => { + //console.log("(getinitial)dreams called!") + collection + .find({ usr: request.session.usr }) + .toArray() + .then(result => { + //console.log("usr: " + request.session.usr); + let entries = result[0].entries; + //console.log(result[0]); + //console.log(entries); + //console.log("entries^"); + response.json(entries); + //response.json(dreams); // with just this and not responding w entries it works + }); + // express helps us take JS objects and send them as JSON + //response.json(dreams); +}); + +const uri = 'mongodb+srv://a3db:a3DB@cluster0.qxmb84d.mongodb.net/?retryWrites=true&w=majority' + +const client = new mongodb.MongoClient( uri, { useNewUrlParser: true, useUnifiedTopology:true }) +let collection = null + +client.connect() + .then( () => { + // will only create collection if it doesn't exist + return client.db( 'classData' ).collection( 'classDataCollectionOne' ) + //console.log("connected to mongodb") + }) + .then( __collection => { + // store reference to collection + collection = __collection + // blank query returns all documents + return collection.find({ }).toArray() + }) + //.then( console.log ) + + +// ADDING TO DATABASE CAREFULLLL +/* +const newUser1 = { + username: "testingUsername", + password: "testingPassword", + entries: [] +} + +collection.insertOne(newUser1) // cannot read properties of null? +*/ +// DONE W THAT SUS SHIT + + + +// route to get all docs +app.get( '/', (req,res) => { + console.log('things are printing in the server') + if( collection !== null ) { + // get array and pass to res.json + collection.find({ }).toArray().then( result => res.json( result ) ) + } +}) + +// ATTENTION: Express code attempt here + + + +// ATTENTION: express code attempt ending + +app.listen( 3000 ) + +app.use( (req,res,next) => { + if( collection !== null ) { + next() + }else{ + res.status( 503 ).send() + } +}) + +app.post( '/add', (req,res) => { + // assumes only one object to insert + collection.insertOne( req.body ).then( result => res.json( result ) ) +}) + +// adding but "/trynaAddSmth" +app.post("/trynaAddSmth", bodyParser.json(), (request, response) => { + collection + .find({ usr: request.session.usr }) + .toArray() + .then(result => { + let entries = result[0].entries; + + const newEntry = { + className: request.body.className, + classNumber: request.body.classNumber + } + entries.push(newEntry); + + collection.updateOne( + { _id: mongodb.ObjectId(result[0]._id) }, + { $set: { entries: entries } } + ); + response.json(entries); + }); +}); + +// editing but "/trynaEditSmth"; doesn't work +app.post("/trynaEditSmth", bodyParser.json(), (request, response) => { + //console.log("in tryna edit smth"); + collection + .find({ usr: request.session.usr }) + .toArray() + .then(result => { + let entries = result[0].entries; + + let testClassname = 'ii' + /* + console.log("entries: "); + console.log(entries); + console.log("request.body.classNumber:"); + console.log(request.body.classNumber); + console.log("entries[request.body.testClassname]:"); + console.log(entries[request.body.testClassname]); + */ + + let updatedEntry = { + className: request.body.className, + classNumber: request.body.classNumber + }; + //console.log("updatedEntry:"); + //console.log(updatedEntry); + + if (entries[request.body.classNumber]) { + entries[request.body.classNumber] = updatedEntry; + } + + collection.updateOne( + { _id: mongodb.ObjectId(result[0]._id) }, + { $set: { entries: entries } } + ); + + response.json(entries); + }); +}); + +// trynaDeleteSmth + +app.post("/trynaDeleteSmth", bodyParser.json(), (request, response) => { + console.log("in tryna delete smth"); + collection + .find({ usr: request.session.usr }) + .toArray() + .then(result => { + let entries = result[0].entries; + + /* + console.log("entries: "); + console.log(entries); + console.log("request.body.className:"); + console.log(request.body.className); + console.log("entries[request.body.className]:"); + console.log(entries[request.body.index]); + */ + + //if (entries[request.body.className]) { + entries.splice(request.body.className, 1); // only deletes first entry + //} + + collection.updateOne( + { _id: mongodb.ObjectId(result[0]._id) }, + { $set: { entries: entries } } + ); + response.json(entries); + }); +}); + From 19b1d8a459966d2e9592f58ed5e84c7fc08b2556 Mon Sep 17 00:00:00 2001 From: sidneygoldinger <98048123+sidneygoldinger@users.noreply.github.com> Date: Sat, 1 Oct 2022 17:02:45 -0400 Subject: [PATCH 9/9] Update README.md --- README.md | 232 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 120 insertions(+), 112 deletions(-) diff --git a/README.md b/README.md index 1ba2c7a4c..b7f80e673 100644 --- a/README.md +++ b/README.md @@ -1,116 +1,124 @@ -Assignment 3 - Persistence: Two-tier Web Application with Database, Express server, and CSS template -=== - -Due: September 22nd, by 11:59 AM. - -This assignnment continues where we left off, extending it to use the most popular Node.js server framework (express), -a database (mongodb), and a CSS application framework / template of your choice (Boostrap, Material Design, Semantic UI, Pure etc.) - -Baseline Requirements ---- - -Your application is required to implement the following functionalities: - -- a `Server`, created using Express (no alternatives will be accepted for this assignment) -- a `Results` functionality which shows all data associated with a logged in user (except passwords) -- a `Form/Entry` functionality which allows users to add, modify, and delete data items (must be all three!) associated with their user name / account. -- Use of at least five [Express middleware packages](https://expressjs.com/en/resources/middleware.html). Explore! One of these five middleware -can be a custom function that you write yourself; if you choose to do this, make sure to describe what this function is in your README. -- Persistent data storage in between server sessions using [mongodb](https://www.mongodb.com/cloud/atlas) -- Use of a [CSS framework or template](https://github.com/troxler/awesome-css-frameworks). -This should do the bulk of your styling/CSS for you and be appropriate to your application. -For example, don't use [NES.css](https://nostalgic-css.github.io/NES.css/) (which is awesome!) unless you're creating a game or some type of retro 80s site. - -Your application is required to demonstrate the use of the following concepts: - -HTML: -- HTML input tags and form fields of various flavors (`