diff --git a/nodeJS/Learning/05/public/css/lesson.css b/nodeJS/Learning/05/public/css/lesson.css index ae99285..faae602 100644 --- a/nodeJS/Learning/05/public/css/lesson.css +++ b/nodeJS/Learning/05/public/css/lesson.css @@ -3,44 +3,48 @@ /* font: sans-serif for headers, serif for paragraphs, mono for whatever */ -@import url('https://fonts.googleapis.com/css2?family=Cutive+Mono&family=EB+Garamond:ital,wght@0,400..800;1,400..800&display=swap'); +@import url("https://fonts.googleapis.com/css2?family=Cutive+Mono&family=EB+Garamond:ital,wght@0,400..800;1,400..800&display=swap"); -body{ - background-color: rgb(232, 163, 217); - color: rgb(146, 0, 24) +body { + background-color: rgb(232, 163, 217); + color: rgb(146, 0, 24); } -h1, h2, h3, h4, h5 { - font-family: 'Cutive Mono', monospace; +h1, +h2, +h3, +h4, +h5 { + font-family: "Cutive Mono", monospace; } p { - font-family: 'EB Garamond', serif; + font-family: "EB Garamond", serif; } .title-text { - font-size: 30px; - margin-bottom: 0px; + font-size: 30px; + margin-bottom: 0px; } .form-submit { - background-color: rgb(151, 227, 29); + background-color: rgb(151, 227, 29); } .form-submit:hover { - background:rgb(62, 100, 2); - cursor: pointer; + background: rgb(62, 100, 2); + cursor: pointer; } .todo-input { - background: rgb(16, 232, 236) + background: rgb(16, 232, 236); } .container { - display: flex; - flex-direction: column; - align-content: space-around; - align-items: center; - justify-content: flex-end; - flex-wrap: nowrap; -} \ No newline at end of file + display: flex; + flex-direction: column; + align-content: space-around; + align-items: center; + justify-content: flex-end; + flex-wrap: nowrap; +} diff --git a/nodeJS/Learning/05/public/css/main.css b/nodeJS/Learning/05/public/css/main.css index 818e357..c4b1b41 100644 --- a/nodeJS/Learning/05/public/css/main.css +++ b/nodeJS/Learning/05/public/css/main.css @@ -3,158 +3,178 @@ /* font: sans-serif for headers, serif for paragraphs, mono for whatever */ -@import url('https://fonts.googleapis.com/css2?family=Cutive+Mono&family=EB+Garamond:ital,wght@0,400..800;1,400..800&display=swap'); +@import url("https://fonts.googleapis.com/css2?family=Cutive+Mono&family=EB+Garamond:ital,wght@0,400..800;1,400..800&display=swap"); -body{ - background-color: rgb(238, 213, 232); - color: rgb(92, 89, 92) +body { + background-color: rgb(238, 213, 232); + color: rgb(92, 89, 92); } -h1, h2, h3, h4, h5 { - font-family: 'EB Garamond', serif; +h1, +h2, +h3, +h4, +h5 { + font-family: "EB Garamond", serif; } p { - font-family: 'Curtive Mono', monospace; + font-family: "Curtive Mono", monospace; } .title-text { - font-size: 30px; - margin-bottom: 0px; + font-size: 30px; + margin-bottom: 0px; } img { - width: 10%; - height: auto; - margin-top: 5px; - } + width: 10%; + height: auto; + margin-top: 5px; +} .form-submit { - background-color: rgb(255, 124, 192); + background-color: rgb(255, 124, 192); } .form-submit:hover { - background:rgb(252, 27, 121); - cursor: pointer; + background: rgb(252, 27, 121); + cursor: pointer; } -form input, form textarea { - font-family: 'Cutive Mono', monospace; - } +form input, +form textarea { + font-family: "Cutive Mono", monospace; +} .todo-input { - text-align: left; - margin-top: 30px; - } - - label, input, button { - display: block; - width: 100%; - padding: 0; - border: none; - outline: none; - box-sizing: border-box; - } - - label { - margin-bottom: 4px; - } - - label:nth-of-type(2) { - margin-top: 12px; - } - - input::placeholder { - color: gray; - } - - input { - background: #ecf0f3; - padding: 10px; - padding-left: 20px; - height: 50px; - font-size: 14px; - border-radius: 50px; - box-shadow: inset 6px 6px 6px #cbced1, inset -6px -6px 6px white; - } + text-align: left; + margin-top: 30px; +} - button { - color: white; - font-family: 'Curtive Mono', monospace; - font-size: 16px; - margin: 20px auto; - height: 40px; - width: 100px; - border-radius: 20px; - cursor: pointer; - display: block; - } +label, +input, +button { + display: block; + width: 100%; + padding: 0; + border: none; + outline: none; + box-sizing: border-box; +} + +label { + margin-bottom: 4px; +} + +label:nth-of-type(2) { + margin-top: 12px; +} + +input::placeholder { + color: gray; +} + +input { + background: #ecf0f3; + padding: 10px; + padding-left: 20px; + height: 50px; + font-size: 14px; + border-radius: 50px; + box-shadow: inset 6px 6px 6px #cbced1, inset -6px -6px 6px white; +} + +.form-submit { + color: white; + font-family: "Curtive Mono", monospace; + font-size: 16px; + margin: 20px auto; + height: 40px; + width: 100px; + border-radius: 20px; + cursor: pointer; + display: block; +} .container { - display: flex; - flex-direction: column; - align-content: space-around; - align-items: center; - justify-content: flex-end; - flex-wrap: nowrap; + display: flex; + flex-direction: column; + align-content: space-around; + align-items: center; + justify-content: flex-end; + flex-wrap: nowrap; } .todo-container { - max-width: 300px; - font-family: 'Curtive Mono', monospace; - background-color: #fff5f7; /* Soft cream background for the list */ - border: 4px solid #ffd0dc; /* Pink border */ - border-radius: 30px; - box-shadow: 6px 4px 10px rgba(0, 0, 0, 0.1); /* Soft shadow */ - position: relative; - padding: 20px 20px 20px 20px;/* up left down right */ - background-image: url('/images/polka.jpg'); /* Add your image path */ - background-size: cover; /* Scale the image to cover the entire list item */ - background-repeat: no-repeat; /* Prevent the image from repeating */ - background-position: center; /* Center thse image within the list item */ - } - - .inner-todo { - background-color: #fee6eb; - padding: 20px; - border-radius: 20px; + max-width: 300px; + font-family: "Curtive Mono", monospace; + background-color: #fff5f7; /* Soft cream background for the list */ + border: 4px solid #ffd0dc; /* Pink border */ + border-radius: 30px; + box-shadow: 6px 4px 10px rgba(0, 0, 0, 0.1); /* Soft shadow */ + position: relative; + padding: 20px 20px 20px 20px; /* up left down right */ + background-image: url("/images/polka.jpg"); /* Add your image path */ + background-size: cover; /* Scale the image to cover the entire list item */ + background-repeat: no-repeat; /* Prevent the image from repeating */ + background-position: center; /* Center thse image within the list item */ +} + +.inner-todo { + background-color: #fee6eb; + padding: 20px; + border-radius: 20px; +} + +/* Heading */ +.todo-container h1 { + font-size: 25px; + font-family: "EB Garamond", serif; + color: #545353; /* Pink color for title */ + margin-top: 10px; + font-weight: bold; +} + +/* List Styling */ +ol { + padding: 0; + margin: 0; +} + +ol li { + color: #545353; /* Adjust text color for visibility */ + margin: 10px; + background-color: #ffffff; /* Soft cream background for the list */ + padding: 20px; /* Increase padding for better layout */ + border-radius: 20px; + text-align: left; + font-family: "Curtive Mono", monospace; /* Consistent font */ + min-width: 250px; + display: flex; + flex-direction: row; + justify-content: space-between; +} + +ol li i { + font-size: 20px; +} + +li button { + background-color: white; + color: rgb(61, 61, 61); /* Button text color */ + cursor: pointer; /* Pointer cursor for interactivity */ + width: 15px; +} + +/* Decoration Image */ +.decor { + position: absolute; + bottom: -20px; + right: 250px; +} + +.decor img { + width: 150px; + height: auto; } - - /* Heading */ - .todo-container h1 { - font-size: 25px; - font-family: 'EB Garamond', serif; - color: #545353; /* Pink color for title */ - margin-top: 10px; - font-weight: bold; - } - - /* List Styling */ - ol { - list-style: none; - padding: 0; - margin: 0; - } - - ol li { - color: #545353; /* Adjust text color for visibility */ - margin: 10px; - background-color: #ffffff; /* Soft cream background for the list */ - padding: 20px; /* Increase padding for better layout */ - border-radius: 20px; - text-align: left; - font-family: 'Curtive Mono', monospace; /* Consistent font */ - min-width: 250px; - } - - /* Decoration Image */ - .decor { - position: absolute; - bottom: -20px; - right: -20px; - } - - .decor img { - width: 150px; - height: auto; - } \ No newline at end of file diff --git a/nodeJS/Learning/05/public/html/index.html b/nodeJS/Learning/05/public/html/index.html new file mode 100644 index 0000000..83d0cd5 --- /dev/null +++ b/nodeJS/Learning/05/public/html/index.html @@ -0,0 +1,69 @@ + + + + + + Tiffy's to-do list website + + + + + + + + + + + + + +
+
+

Tiffy's to-do list website

+ + Image not found + +

Add something cute to my to-do list!

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

To do list

+
    + +
+
+ +
+
+
+ + + +
+
+ + + + diff --git a/nodeJS/Learning/05/src/lesson.html b/nodeJS/Learning/05/public/html/lesson.html similarity index 100% rename from nodeJS/Learning/05/src/lesson.html rename to nodeJS/Learning/05/public/html/lesson.html diff --git a/nodeJS/Learning/05/public/js/todo.js b/nodeJS/Learning/05/public/js/todo.js index fe78ce6..8ffa0b5 100644 --- a/nodeJS/Learning/05/public/js/todo.js +++ b/nodeJS/Learning/05/public/js/todo.js @@ -1,15 +1,51 @@ //fetch: send a url request, client side rendering -//update the list, by default: fetch uses get -fetch('/get-todos', {method: 'GET'}). then((response) => { - response.json(). then((data) => { - const ol = document.querySelector('ol'); - data.forEach((item) => { - //Create li - const li = document.createElement('li'); - //add text to li - li.textContent = item; - //add li underneath (child) ol - ol.appendChild(li); - }) - }); -}); \ No newline at end of file +//update the list, by default: fetch uses "get" +fetch("/get-todos", { method: "GET" }).then((response) => { + response.json().then((data) => { + const ol = document.querySelector("ol"); + data.forEach((item) => { + //Create li + const li = document.createElement("li"); + //add text to li + li.textContent = item; + + // Create button + const button = document.createElement("button"); + button.type = "submit"; + + // Add icon to button + const icon = document.createElement("i"); + icon.className = "fa-solid fa-delete-left"; + + // Append icon to button + button.appendChild(icon); + + // Append button to li + li.appendChild(button); + + // Add delete functionality + button.addEventListener("click", () => { + // Send DELETE request + fetch(`/delete-todo`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ todo: item }), + }) + .then((response) => { + if (response.ok) { + // Remove the item from the DOM + li.remove(); + } else { + console.error("Failed to delete to-do item"); + } + }) + .catch((error) => console.error("Error:", error)); + }); + + // Append li underneath (child) ol + ol.appendChild(li); + }); + }); +}); diff --git a/nodeJS/Learning/05/src/index.html b/nodeJS/Learning/05/src/index.html deleted file mode 100644 index 37b53dd..0000000 --- a/nodeJS/Learning/05/src/index.html +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - Tiffy's to-do list website - - - - - - - - - - - - -
-
-

Tiffy's to-do list website

-

Add something cute to my to-do list!

- - Image not found - -
- - - - - -
- - - - -
-
-

To do list

-
    - -
-
- -
-
-
- - - - -
-
- - - - - - - \ No newline at end of file diff --git a/nodeJS/Learning/05/src/index.ts b/nodeJS/Learning/05/src/index.ts index 2911cad..8f562df 100644 --- a/nodeJS/Learning/05/src/index.ts +++ b/nodeJS/Learning/05/src/index.ts @@ -1,23 +1,24 @@ //importing the dotenv package (secret file), configuring dotenv for our project //dotenv, secret file, people cant read -import dotenv from 'dotenv'; +import dotenv from "dotenv"; dotenv.config({ path: `${__dirname}/../.env` }); //Variable declaration //we named express as a variable name, usually name same as imported package //express: designed to easily make a web server -import express, { Request, Response } from 'express'; +import express, { Application, Request, Response } from "express"; //bodyParser: sending form action to my server, wrap the user's data in "body" to my server, and my server is unwrapping it -import bodyParser from 'body-parser'; -import fs from 'fs'; +import bodyParser from "body-parser"; +import fs from "fs"; +import path from "path"; -const filePath = 'todolist.txt'; +const filePath: string = "todolist.txt"; //initializing the express web server, everything in express, we want to put in app -const app = express(); -const port = process.env.PORT || 3000; +const app: Application = express(); +const port: Number = parseInt(process.env.PORT as string) || 3000; // Middleware //unwrap the words @@ -31,45 +32,93 @@ app.use(bodyParser.json()); //to make icon files, because icon files are always downloaded app.use(express.static(`${__dirname}/../public`)); -app.get(`/`, (req, res) => { - res.sendFile(`${__dirname}/index.html`); +app.get(`/`, (_req: Request, res: Response): void => { + res.sendFile(path.join(__dirname, "../public/html/index.html")); }); -app.post('/form-action', (req, res) => { - const todo = req.body.todo; - console.log(todo); - - // use fs to add the item to the file +app.post("/form-action", (req: Request, res: Response): void => { + const todo = req.body.todo; - fs.appendFile(filePath, todo + '\n', (err) => { - if (err) { - console.log(err) - res.send("Hey there is an error.") - } else { - res.redirect("/"); - } - }); - + // use fs to add the item to the file + + fs.appendFile(filePath, "\n" + todo, (err) => { + if (err) { + console.log(err); + res.send("Hey there is an error."); + } else { + res.redirect("/"); + } + }); }); -app.get('/get-todos', (req: Request, res: Response): void => { - //Using the fs module to read the file - fs.readFile(filePath, 'utf-8', (err:NodeJS.ErrnoException | null, data: string) => { - if (err) { - console.log(err) - res.send("An error occured.") - } else { - //Return the file contents as a response of each list item data.split - //Send the response back to the client as a json object res.json - res.json(data.split('\n')) - } - }) -}) +app.get("/get-todos", (_req: Request, res: Response): void => { + //Using the fs module to read the file + fs.readFile( + filePath, + "utf-8", + (err: NodeJS.ErrnoException | null, data: string) => { + if (err) { + console.log(err); + res.send("An error occured."); + } else { + //Return the file contents as a response of each list item data.split + //Send the response back to the client as a json object res.json + res.json(data.split("\n").filter((data) => data.trim() !== "")); + } + } + ); +}); +app.delete("/delete-todo", (req: Request, res: Response): void => { + const todoToDelete = (req.body.todo || "").trim(); // Ensure input is trimmed + + if (!todoToDelete) { + res.status(400).json({ error: "No to-do item provided to delete" }); + return; + } + + // Read the file to get the current list of todos + fs.readFile(filePath, "utf8", (err, data) => { + if (err) { + console.error("Error reading file:", err); + res.status(500).json({ error: "Internal Server Error" }); + return; + } + + // Split the content into lines and trim whitespace from each line + const todos = data + .split("\n") + .map((line) => line.trim()) + .filter((line) => line !== ""); // Remove empty lines + + // Filter out the todo item to delete + const updatedTodos = todos.filter((todo) => todo !== todoToDelete); + + if (todos.length === updatedTodos.length) { + res.status(404).json({ error: "Todo not found" }); + return; + } + + // Join the updated list back into a string + const updatedContent = updatedTodos.join("\n"); + + // Write the updated content back to the file + fs.writeFile(filePath, updatedContent, "utf8", (writeErr) => { + if (writeErr) { + console.error("Error writing file:", writeErr); + res.status(500).json({ error: "Internal Server Error" }); + return; + } + + console.log(`Deleted todo: ${todoToDelete}`); + res.status(200).json({ message: "Todo deleted successfully" }); + }); + }); +}); // slash'/' is the home page, /about is the about page //get, post, put, delete app.listen(port, () => { - console.log(`Server is running on http://localhost:${port}`); -}); \ No newline at end of file + console.log(`Server is running on http://localhost:${port}`); +}); diff --git a/nodeJS/Learning/05/todolist.txt b/nodeJS/Learning/05/todolist.txt index 55b1898..48d732e 100644 --- a/nodeJS/Learning/05/todolist.txt +++ b/nodeJS/Learning/05/todolist.txt @@ -1,6 +1,3 @@ -Learn coding -Cook dinner -Make a pretty website -Delete list items -Edit list items -Move items to database!!! \ No newline at end of file +ABC +CDE +FADDGD \ No newline at end of file diff --git a/nodeJS/Learning/05/tsconfig.json b/nodeJS/Learning/05/tsconfig.json index 774a683..a0270e6 100644 --- a/nodeJS/Learning/05/tsconfig.json +++ b/nodeJS/Learning/05/tsconfig.json @@ -30,7 +30,9 @@ "moduleResolution": "node10" /* Specify how TypeScript looks up a file from a given module specifier. */, // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ - "rootDirs": ["./"], /* Allow multiple folders to be treated as one when resolving modules. */ + "rootDirs": [ + "./" + ] /* Allow multiple folders to be treated as one when resolving modules. */, // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ // "types": [], /* Specify type package names to be included without being referenced in a source file. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ @@ -85,18 +87,18 @@ /* Type Checking */ "strict": true /* Enable all strict type-checking options. */, "noImplicitAny": true /* Enable error reporting for expressions and declarations with an implied 'any' type. */, - // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ - // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + "strictNullChecks": true /* When type checking, take into account 'null' and 'undefined'. */, + "strictFunctionTypes": true /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */, // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ // "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */ - // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + "noImplicitThis": true /* Enable error reporting when 'this' is given the type 'any'. */, // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ "alwaysStrict": true /* Ensure 'use strict' is always emitted. */, // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ - // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + "noUnusedParameters": true /* Raise an error when a function parameter isn't read. */, // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ - // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + "noImplicitReturns": true /* Enable error reporting for codepaths that do not explicitly return in a function. */, // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ @@ -108,4 +110,4 @@ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ } -} \ No newline at end of file +}