This commit is contained in:
tuffahni 2024-12-25 03:14:41 +00:00
parent f82ea59048
commit 39574a6599
9 changed files with 389 additions and 274 deletions

View File

@ -3,44 +3,48 @@
/* font: sans-serif for headers, serif for paragraphs, mono for whatever */ /* 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{ body {
background-color: rgb(232, 163, 217); background-color: rgb(232, 163, 217);
color: rgb(146, 0, 24) color: rgb(146, 0, 24);
} }
h1, h2, h3, h4, h5 { h1,
font-family: 'Cutive Mono', monospace; h2,
h3,
h4,
h5 {
font-family: "Cutive Mono", monospace;
} }
p { p {
font-family: 'EB Garamond', serif; font-family: "EB Garamond", serif;
} }
.title-text { .title-text {
font-size: 30px; font-size: 30px;
margin-bottom: 0px; margin-bottom: 0px;
} }
.form-submit { .form-submit {
background-color: rgb(151, 227, 29); background-color: rgb(151, 227, 29);
} }
.form-submit:hover { .form-submit:hover {
background:rgb(62, 100, 2); background: rgb(62, 100, 2);
cursor: pointer; cursor: pointer;
} }
.todo-input { .todo-input {
background: rgb(16, 232, 236) background: rgb(16, 232, 236);
} }
.container { .container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-content: space-around; align-content: space-around;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
flex-wrap: nowrap; flex-wrap: nowrap;
} }

View File

@ -3,158 +3,178 @@
/* font: sans-serif for headers, serif for paragraphs, mono for whatever */ /* 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{ body {
background-color: rgb(238, 213, 232); background-color: rgb(238, 213, 232);
color: rgb(92, 89, 92) color: rgb(92, 89, 92);
} }
h1, h2, h3, h4, h5 { h1,
font-family: 'EB Garamond', serif; h2,
h3,
h4,
h5 {
font-family: "EB Garamond", serif;
} }
p { p {
font-family: 'Curtive Mono', monospace; font-family: "Curtive Mono", monospace;
} }
.title-text { .title-text {
font-size: 30px; font-size: 30px;
margin-bottom: 0px; margin-bottom: 0px;
} }
img { img {
width: 10%; width: 10%;
height: auto; height: auto;
margin-top: 5px; margin-top: 5px;
} }
.form-submit { .form-submit {
background-color: rgb(255, 124, 192); background-color: rgb(255, 124, 192);
} }
.form-submit:hover { .form-submit:hover {
background:rgb(252, 27, 121); background: rgb(252, 27, 121);
cursor: pointer; cursor: pointer;
} }
form input, form textarea { form input,
font-family: 'Cutive Mono', monospace; form textarea {
} font-family: "Cutive Mono", monospace;
}
.todo-input { .todo-input {
text-align: left; text-align: left;
margin-top: 30px; 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;
}
button { label,
color: white; input,
font-family: 'Curtive Mono', monospace; button {
font-size: 16px; display: block;
margin: 20px auto; width: 100%;
height: 40px; padding: 0;
width: 100px; border: none;
border-radius: 20px; outline: none;
cursor: pointer; box-sizing: border-box;
display: block; }
}
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 { .container {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-content: space-around; align-content: space-around;
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
flex-wrap: nowrap; flex-wrap: nowrap;
} }
.todo-container { .todo-container {
max-width: 300px; max-width: 300px;
font-family: 'Curtive Mono', monospace; font-family: "Curtive Mono", monospace;
background-color: #fff5f7; /* Soft cream background for the list */ background-color: #fff5f7; /* Soft cream background for the list */
border: 4px solid #ffd0dc; /* Pink border */ border: 4px solid #ffd0dc; /* Pink border */
border-radius: 30px; border-radius: 30px;
box-shadow: 6px 4px 10px rgba(0, 0, 0, 0.1); /* Soft shadow */ box-shadow: 6px 4px 10px rgba(0, 0, 0, 0.1); /* Soft shadow */
position: relative; position: relative;
padding: 20px 20px 20px 20px;/* up left down right */ padding: 20px 20px 20px 20px; /* up left down right */
background-image: url('/images/polka.jpg'); /* Add your image path */ background-image: url("/images/polka.jpg"); /* Add your image path */
background-size: cover; /* Scale the image to cover the entire list item */ background-size: cover; /* Scale the image to cover the entire list item */
background-repeat: no-repeat; /* Prevent the image from repeating */ background-repeat: no-repeat; /* Prevent the image from repeating */
background-position: center; /* Center thse image within the list item */ background-position: center; /* Center thse image within the list item */
} }
.inner-todo { .inner-todo {
background-color: #fee6eb; background-color: #fee6eb;
padding: 20px; padding: 20px;
border-radius: 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;
}

View File

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tiffy's to-do list website</title>
<meta name="description" content="Tiffy's todo list website" />
<meta name="keywords" content="list, todo" />
<meta name="author" content="Tiffany" />
<link rel="icon" href="images/favicon.ico" />
<link rel="stylesheet" href="css/main.css" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"
integrity="sha512-Evv84Mr4kqVGRNSgIGL/F/aIDqQb7xQ2vcrdIwxfjThSH8CSR7PBEakCr51Ck+w+/U6swU2Im1vVX0SVk9ABhg=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
</head>
<body>
<section>
<div class="container">
<h1 class="title-text">Tiffy's to-do list website</h1>
<img src="images/image.jpg" alt="Image not found" />
<p><i>Add something cute to my to-do list!</i></p>
<form action="/form-action" method="post">
<label for="to-do"></label>
<input
name="todo"
type="text"
placeholder="Enter a to-do"
required
class="todo-input"
/>
<button type="submit" class="form-submit">Add</button>
</form>
<!-- List -->
<!-- ordered list -->
<div class="todo-container">
<div class="inner-todo">
<h1>To do list</h1>
<ol>
<!-- <li>Learn from Ronald</li>
<li>Watch movies</li>
<li>Sleep</li> -->
</ol>
<div class="decor">
<img src="images/cryinggirl.gif" />
</div>
</div>
</div>
<!-- unordered list -->
<!-- <ul></ul> -->
</div>
</section>
</body>
<script src="js/todo.js"></script>
</html>

View File

@ -1,15 +1,51 @@
//fetch: send a url request, client side rendering //fetch: send a url request, client side rendering
//update the list, by default: fetch uses get //update the list, by default: fetch uses "get"
fetch('/get-todos', {method: 'GET'}). then((response) => { fetch("/get-todos", { method: "GET" }).then((response) => {
response.json(). then((data) => { response.json().then((data) => {
const ol = document.querySelector('ol'); const ol = document.querySelector("ol");
data.forEach((item) => { data.forEach((item) => {
//Create li //Create li
const li = document.createElement('li'); const li = document.createElement("li");
//add text to li //add text to li
li.textContent = item; li.textContent = item;
//add li underneath (child) ol
ol.appendChild(li); // 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);
});
});
});

View File

@ -1,62 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Tiffy's to-do list website</title>
<meta name="description" content="Tiffy's todo list website" />
<meta name="keywords" content="list, todo" />
<meta name="author" content="Tiffany" />
<link rel="icon" href="images/favicon.ico" />
<link rel="stylesheet" href="css/main.css"/>
</head>
<body>
<section>
<div class="container">
<h1 class="title-text">Tiffy's to-do list website</h1>
<p><i>Add something cute to my to-do list!</i></p>
<img src="images/image.jpg" alt="Image not found">
<form action="/form-action" method="post">
<label for="to-do"></label>
<input name="todo" type="text" placeholder="Enter a to-do" required class="todo-input"/>
<!-- button, if it's a submit button then it will end the form and submit it, other than that the button doesnt do anything -->
<button type="submit" class="form-submit">Add</button>
</form>
<!-- List -->
<!-- ordered list -->
<div class="todo-container">
<div class="inner-todo">
<h1>To do list</h1>
<ol>
<!-- <li>Learn from Ronald</li>
<li>Watch movies</li>
<li>Sleep</li> -->
</ol>
<div class="decor">
<img src="images/cryinggirl.gif"/>
</div>
</div>
</div>
<!-- unordered list -->
<!-- <ul></ul> -->
</div>
</section>
</body>
<script src="js/todo.js"> </script>
</html>

View File

@ -1,23 +1,24 @@
//importing the dotenv package (secret file), configuring dotenv for our project //importing the dotenv package (secret file), configuring dotenv for our project
//dotenv, secret file, people cant read //dotenv, secret file, people cant read
import dotenv from 'dotenv'; import dotenv from "dotenv";
dotenv.config({ path: `${__dirname}/../.env` }); dotenv.config({ path: `${__dirname}/../.env` });
//Variable declaration //Variable declaration
//we named express as a variable name, usually name same as imported package //we named express as a variable name, usually name same as imported package
//express: designed to easily make a web server //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 //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 bodyParser from "body-parser";
import fs from 'fs'; 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 //initializing the express web server, everything in express, we want to put in app
const app = express(); const app: Application = express();
const port = process.env.PORT || 3000; const port: Number = parseInt(process.env.PORT as string) || 3000;
// Middleware // Middleware
//unwrap the words //unwrap the words
@ -31,45 +32,93 @@ app.use(bodyParser.json());
//to make icon files, because icon files are always downloaded //to make icon files, because icon files are always downloaded
app.use(express.static(`${__dirname}/../public`)); app.use(express.static(`${__dirname}/../public`));
app.get(`/`, (req, res) => { app.get(`/`, (_req: Request, res: Response): void => {
res.sendFile(`${__dirname}/index.html`); res.sendFile(path.join(__dirname, "../public/html/index.html"));
}); });
app.post('/form-action', (req, res) => { app.post("/form-action", (req: Request, res: Response): void => {
const todo = req.body.todo; const todo = req.body.todo;
console.log(todo);
// use fs to add the item to the file
fs.appendFile(filePath, todo + '\n', (err) => { // use fs to add the item to the file
if (err) {
console.log(err) fs.appendFile(filePath, "\n" + todo, (err) => {
res.send("Hey there is an error.") if (err) {
} else { console.log(err);
res.redirect("/"); res.send("Hey there is an error.");
} } else {
}); res.redirect("/");
}
});
}); });
app.get('/get-todos', (req: Request, res: Response): void => { app.get("/get-todos", (_req: Request, res: Response): void => {
//Using the fs module to read the file //Using the fs module to read the file
fs.readFile(filePath, 'utf-8', (err:NodeJS.ErrnoException | null, data: string) => { fs.readFile(
if (err) { filePath,
console.log(err) "utf-8",
res.send("An error occured.") (err: NodeJS.ErrnoException | null, data: string) => {
} else { if (err) {
//Return the file contents as a response of each list item data.split console.log(err);
//Send the response back to the client as a json object res.json res.send("An error occured.");
res.json(data.split('\n')) } 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 // slash'/' is the home page, /about is the about page
//get, post, put, delete //get, post, put, delete
app.listen(port, () => { app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`); console.log(`Server is running on http://localhost:${port}`);
}); });

View File

@ -1,6 +1,3 @@
Learn coding ABC
Cook dinner CDE
Make a pretty website FADDGD
Delete list items
Edit list items
Move items to database!!!

View File

@ -30,7 +30,9 @@
"moduleResolution": "node10" /* Specify how TypeScript looks up a file from a given module specifier. */, "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. */ // "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. */ // "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'. */ // "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. */ // "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
@ -85,18 +87,18 @@
/* Type Checking */ /* Type Checking */
"strict": true /* Enable all strict type-checking options. */, "strict": true /* Enable all strict type-checking options. */,
"noImplicitAny": true /* Enable error reporting for expressions and declarations with an implied 'any' type. */, "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'. */ "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. */ "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. */ // "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. */ // "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'. */ // "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'. */ // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
"alwaysStrict": true /* Ensure 'use strict' is always emitted. */, "alwaysStrict": true /* Ensure 'use strict' is always emitted. */,
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ // "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'. */ // "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. */ // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ // "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. */ // "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. */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */
} }
} }