diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..f0098668 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.DS_Store +node_modules/ +package-lock.json \ No newline at end of file diff --git a/.glitch-assets b/.glitch-assets new file mode 100644 index 00000000..0f95134b --- /dev/null +++ b/.glitch-assets @@ -0,0 +1,3 @@ +{"name":"showpic.png","date":"2019-09-08T19:25:11.108Z","url":"https://www.514blog.ca/wp-content/uploads/2020/03/1_dBJMknulIZSAC36tTmanVA.jpeg","type":"image/png","size":29741,"imageWidth":320,"imageHeight":320,"thumbnail":"https://cdn.glitch.com/4f0fe7ab-196a-4c13-9b89-b60fd2312715%2Fbookpic.png","thumbnailWidth":320,"thumbnailHeight":320,"uuid":"Kjjp8ctJmV6WIBj2"} +{"uuid":"Kjjp8ctJmV6WIBj2","deleted":true} +{"name":"showpic2.jpg","date":"2019-09-08T19:27:31.906Z","url":"https://www.514blog.ca/wp-content/uploads/2020/03/1_dBJMknulIZSAC36tTmanVA.jpeg","thumbnailWidth":330,"thumbnailHeight":207,"uuid":"LsI6dViHEIvjQBNw"} \ No newline at end of file diff --git a/README.md b/README.md index 4471f667..35331b2e 100644 --- a/README.md +++ b/README.md @@ -1,100 +1,31 @@ -Assignment 2 - Short Stack: Basic Two-tier Web Application using HTML/CSS/JS and Node.js +Assignment 2 - Short Stack === + +Ezra Barboza -Due: September 9th, by 11:59 AM. +## Show Tracker -This assignment will 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. +I love watching TV shows but often find myself forgetting the show recommendations that people tell me, or not being able to provide suggestions on the spot. +This project is an organizational tool to keep track of all the shows you have watched and want to watch, showing not only the name of the shows and directors, but also a comments section and ratings. The tracker further sorts the shows into sections based on whether you liked them or not. -Baseline Requirements ---- +While this is a personal organizational tool at this time, in the future I could envision it as a forum for TV show enthusiasts to share their best/worst show picks. -There are a 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. +Note: Almost all the time, clicking the buttons will automatically update the tables (I have my `loadData` function for the tables called in all of the `onclick` functions), but sometimes if the website has been sitting open for a while, you need to refresh the page if a button was pressed but the tables aren't automatically updating. -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 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 `todo` dataset with `task`, `priority`, and `creation_date` may generate a new field `deadline` by looking at `creation_date` and `priority` - -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 -- A results page displaying all data currently available on the server. You will most likely use a `` tag for this, but `
+ + + + + +
NameDirectorCommentsRating
+ + +

Shows You Liked

+
+ + + + + + +
NameDirectorCommentsRating
+
+ +

Shows You Didn't Like

+
+ + + + + + +
NameDirectorCommentsRating
+
+ + + +
+
+ + Oops, Made A Mistake? + + Update Show:
+
+
+ Name of Show: + +
+
+ New Rating: + +
+
+
+
+ +
+ +
Show Name to Delete:
+
+
+ +
+
+
- + + \ No newline at end of file diff --git a/public/js/main.js b/public/js/main.js deleted file mode 100644 index a569258f..00000000 --- a/public/js/main.js +++ /dev/null @@ -1,27 +0,0 @@ -// FRONT-END (CLIENT) JAVASCRIPT HERE - -const submit = async function( event ) { - // stop form submission from trying to load - // a new .html page for displaying results... - // this was the original browser behavior and still - // remains to this day - event.preventDefault() - - const input = document.querySelector( '#yourname' ), - json = { yourname: input.value }, - body = JSON.stringify( json ) - - const response = await fetch( '/submit', { - method:'POST', - body - }) - - const text = await response.text() - - console.log( 'text:', text ) -} - -window.onload = function() { - const button = document.querySelector("button"); - button.onclick = submit; -} \ No newline at end of file diff --git a/public/js/scripts.js b/public/js/scripts.js new file mode 100644 index 00000000..9a720ae1 --- /dev/null +++ b/public/js/scripts.js @@ -0,0 +1,160 @@ +// Display functions for each table +const displayShows = function(shdata) { + const template = '{name}{director}{comments}{rating}'; // Fixed closing tag + const row = template.replace("{name}", shdata.showName) + .replace("{director}", shdata.directorName) + .replace("{comments}", shdata.comments) + .replace("{rating}", shdata.rating); + const tbody = document.querySelector("#all-shows"); + tbody.innerHTML += row; +}; + +const displayGoodShows = function(shdata) { + const template = '{name}{director}{comments}{rating}'; // Fixed closing tag + const row = template.replace("{name}", shdata.showName) + .replace("{director}", shdata.directorName) + .replace("{comments}", shdata.comments) + .replace("{rating}", shdata.rating); + const tbody = document.querySelector("#good-shows"); + tbody.innerHTML += row; +}; + +const displayBadShows = function(shdata) { + const template = '{name}{director}{comments}{rating}'; // Fixed closing tag + const row = template.replace("{name}", shdata.showName) + .replace("{director}", shdata.directorName) + .replace("{comments}", shdata.comments) + .replace("{rating}", shdata.rating); + const tbody = document.querySelector("#bad-shows"); + tbody.innerHTML += row; +}; + +// Display data for the three different tables +const displayData = function(data) { + document.querySelector("#all-shows").innerHTML = ""; + document.querySelector("#good-shows").innerHTML = ""; + document.querySelector("#bad-shows").innerHTML = ""; + + for (let i = 0; i < data.length; i++) { + const shdata = data[i]; + displayShows(shdata); + if (shdata.rating === "1" || shdata.rating === "2") { + displayBadShows(shdata); + } + if (shdata.rating === "3" || shdata.rating === "4" || shdata.rating === "5") { + displayGoodShows(shdata); + } + } +}; + +// Fetch the appdata and then call the display functions +const loadData = function() { + fetch('/shows') + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + return response.json(); + }) + .then(displayData) + .catch(error => console.error('Fetch error:', error)); // Error handling +}; + +// Add Show function +const addShow = function(e) { + e.preventDefault(); // Prevent default form submission + + const newShow = { + showName: document.getElementById('showName').value, + directorName: document.getElementById('directorName').value, + comments: document.getElementById('comments').value, + rating: document.getElementById('rating').value, + status: 'none' + }; + + newShow.status = (newShow.rating === "1" || newShow.rating === "2") ? 'bad' : 'good'; + + fetch('/addShow', { + method: 'POST', + body: JSON.stringify(newShow), + headers: { + 'Content-Type': 'application/json' // Specify the content type + } + }) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + resetOrderForm(); + loadData(); + }) + .catch(error => console.error('Fetch error:', error)); // Error handling +}; + +// Delete show based on name +const delShow = function(e) { + e.preventDefault(); // Prevent default form submission + + const body = JSON.stringify({ showName: document.getElementById('delShowName').value }); // Send showName in JSON + fetch('/delShow', { + method: 'POST', + body: body, + headers: { + 'Content-Type': 'application/json' // Specify the content type + } + }) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + resetOrderForm(); + loadData(); + }) + .catch(error => console.error('Fetch error:', error)); // Error handling +}; + +// Edit show based on name and new rating +const editShow = function(e) { + e.preventDefault(); // Prevent default form submission + + const newShow2 = { + showName: document.getElementById('editShowName').value, + rating: document.getElementById('editShowRating').value, + }; + + fetch('/editShow', { + method: 'POST', + body: JSON.stringify(newShow2), + headers: { + 'Content-Type': 'application/json' // Specify the content type + } + }) + .then(response => { + if (!response.ok) { + throw new Error('Network response was not ok'); + } + resetOrderForm(); + loadData(); + }) + .catch(error => console.error('Fetch error:', error)); // Error handling +}; + +// Reset order form after adding show +const resetOrderForm = () => { + document.getElementById('showName').value = ''; + document.getElementById('directorName').value = ''; + document.getElementById('comments').value = ''; + document.getElementById('rating').value = ''; + document.getElementById('delShowName').value = ''; + document.getElementById('editShowName').value = ''; + document.getElementById('editShowRating').value = ''; +}; + +// Window onload function +window.onload = function() { + document.getElementById('submit-btn').addEventListener('click', addShow); // Use addEventListener + document.getElementById('del-btn').addEventListener('click', delShow); // Use addEventListener + document.getElementById('edit-btn').addEventListener('click', editShow); // Use addEventListener + + loadData(); +}; diff --git a/server.js b/server.js index 9ac27fb8..4a6e7a92 100644 --- a/server.js +++ b/server.js @@ -1,74 +1,61 @@ -const http = require( 'http' ), - fs = require( 'fs' ), - // IMPORTANT: you must run `npm install` in the directory for this assignment - // to install the mime library if you're testing this on your local machine. - // However, Glitch will install it automatically by looking in your package.json - // file. - mime = require( 'mime' ), - dir = 'public/', - port = 3000 +const express = require('express'); +const path = require('path'); +const app = express(); +const 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 ) + { 'showName': 'Breaking Bad', 'directorName': 'Vince Gilligan', 'comments': 'An incredible journey of transformation and moral dilemmas.', 'rating': '5', 'status': 'excellent' }, + { 'showName': 'Law and Order: SVU', 'directorName': 'Dick Wolf', 'comments': 'A compelling procedural that tackles tough issues.', 'rating': '4', 'status': 'good' }, + { 'showName': 'Avatar: The Last Airbender', 'directorName': 'Michael Dante DiMartino', 'comments': 'A excellent story but its ending was anticlimactic', 'rating': '2', 'status': 'bad' } +]; + +// Serve static files +app.use(express.static(path.join(__dirname, 'public'))); + +app.use(express.json()); + +app.get('/', (req, res) => { + res.sendFile(path.join(__dirname, 'public', 'index.html')); +}); + +// API route to get shows data +app.get('/shows', (req, res) => { + res.json(appdata); +}); + +// POST route to add a new show +app.post('/addShow', (req, res) => { + const newShow = req.body; + appdata.push(newShow); + res.status(200).json({ message: 'Show added successfully', newShow }); +}); + +// POST route to delete a show +app.post('/delShow', (req, res) => { + const showName = req.body.showName; + const index = appdata.findIndex(show => show.showName === showName); + if (index !== -1) { + appdata.splice(index, 1); + res.status(200).json({ message: 'Show deleted successfully' }); + } else { + res.status(404).json({ message: 'Show not found' }); } -}) - -const handleGet = function( request, response ) { - const filename = dir + request.url.slice( 1 ) - - if( request.url === '/' ) { - sendFile( response, 'public/index.html' ) - }else{ - sendFile( response, filename ) +}); + +// POST route to edit a show's rating and status +app.post('/editShow', (req, res) => { + const { showName, rating } = req.body; + const show = appdata.find(b => b.showName === showName); + if (show) { + show.rating = rating; + show.status = (rating === '1' || rating === '2') ? 'bad' : 'good'; + res.status(200).json({ message: 'Show updated successfully', show }); + } else { + res.status(404).json({ message: 'Show not found' }); } -} - -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('test') - }) -} - -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 ) +// Start the server +app.listen(port, () => { + console.log(`Server is running on http://localhost:${port}`); +});