CMU Coding Bootcamp

feat: dec 09

thecoded.prof 533221b2 abd8d9c0

verified
Changed files
+176 -13
react
src
server
+38 -3
react/src/App.tsx
··· 1 1 import { posts } from "./lib/post"; 2 2 import { BlogPostList } from "./components/BlogPostList"; 3 3 import { Link } from "react-router"; 4 + import { useState } from "react"; 4 5 5 6 export function App() { 7 + const [searchBarDisplay, displaySearchBar] = useState(false); 6 8 return ( 7 9 <> 8 10 <title>Posts</title> 9 11 <div className="w-screen p-5 flex flex-col items-center gap-10"> 12 + <nav className="flex justify-between items-center w-full sticky top-0"> 13 + <h1 className="text-3xl font-bold text-left">Blog App</h1> 14 + {searchBarDisplay ? ( 15 + <> 16 + <input 17 + type="text" 18 + placeholder="Search..." 19 + className="border border-gray-300 rounded px-2 py-1" 20 + onChange={(e) => { 21 + // Implement search functionality here 22 + }} 23 + /> 24 + <button 25 + className="bg-blue-500 hover:bg-blue-700 text-white w-full font-bold py-2 px-4 rounded cursor-pointer text-center" 26 + onClick={() => displaySearchBar(false)} 27 + > 28 + Close 29 + </button> 30 + </> 31 + ) : ( 32 + <button 33 + className="bg-blue-500 hover:bg-blue-700 text-white w-full font-bold py-2 px-4 rounded cursor-pointer text-center" 34 + onClick={() => displaySearchBar(true)} 35 + > 36 + Search 37 + </button> 38 + )} 39 + <div className="flex w-full justify-end items-center"> 40 + <Link to="/post" className="w-1/3"> 41 + <div className="bg-blue-500 hover:bg-blue-700 text-white w-full font-bold py-2 px-4 rounded cursor-pointer text-center"> 42 + New Post 43 + </div> 44 + </Link> 45 + </div> 46 + </nav> 10 47 <div className="flex flex-col gap-4 md:grid md:grid-cols-3 items-center justify-between w-full"> 11 - <h1 className="text-5xl font-bold md:col-start-2 text-center w-full"> 12 - Posts 13 - </h1> 14 48 <div className="flex w-full justify-end items-center"> 49 + <h1 className="text-5xl font-bold text-center">Posts</h1> 15 50 <Link to="/post" className="w-1/3"> 16 51 <div className="bg-blue-500 hover:bg-blue-700 text-white w-full font-bold py-2 px-4 rounded cursor-pointer text-center"> 17 52 New Post
+137 -10
server/index.ts
··· 1 1 import express from "express"; 2 + import { readFile, writeFile, exists } from "fs/promises"; 2 3 3 4 const app = express(); 4 5 const port = process.env["NODE_PORT"] ?? 5173; 5 6 6 - app.get("/fast", (req, res) => { 7 - res.send("Fast response!"); 7 + interface Task { 8 + id: string; 9 + title: string; 10 + completed: boolean; 11 + } 12 + 13 + const getTasks: () => Promise<Task[]> = async () => { 14 + if (!(await exists("tasks.json"))) { 15 + await writeFile("tasks.json", JSON.stringify([])); 16 + } 17 + const file = await readFile("tasks.json", "utf-8"); 18 + if (file.length === 0) { 19 + return []; 20 + } 21 + return JSON.parse(file); 22 + }; 23 + 24 + const updateTask = async (task: Task): Promise<void> => { 25 + const tasks = await getTasks(); 26 + const index = tasks.findIndex((t) => t.id === task.id); 27 + if (index !== -1) { 28 + tasks[index] = task; 29 + } else { 30 + tasks.push(task); 31 + } 32 + await writeFile("tasks.json", JSON.stringify(tasks)); 33 + }; 34 + 35 + const removeTask = async (id: string): Promise<void> => { 36 + const tasks = await getTasks(); 37 + const index = tasks.findIndex((t) => t.id === id); 38 + if (index !== -1) { 39 + tasks.splice(index, 1); 40 + await writeFile("tasks.json", JSON.stringify(tasks)); 41 + } 42 + }; 43 + 44 + app.use(express.json()); 45 + 46 + app.get("/tasks", async (_req, res) => { 47 + res.json(await getTasks()); 48 + }); 49 + 50 + app.post("/tasks", async (req, res) => { 51 + if (!(typeof req.body.title === "string")) { 52 + res.status(400).send("Invalid title"); 53 + return; 54 + } 55 + const newTask = { 56 + id: Math.random().toString(16).substring(2, 8), 57 + title: req.body.title, 58 + completed: false, 59 + }; 60 + await updateTask(newTask); 61 + res.json(newTask).status(201); 62 + }); 63 + 64 + app.get("/tasks/:id", async (req, res) => { 65 + const task = (await getTasks()).find((t) => t.id === req.params.id); 66 + if (!task) { 67 + res.status(404).send("Task not found"); 68 + } else { 69 + res.json(task); 70 + } 71 + }); 72 + 73 + app.put("/tasks/:id", async (req, res) => { 74 + const task = (await getTasks()).find((t) => t.id === req.params.id); 75 + if (!task) { 76 + res.status(404).send("Task not found"); 77 + } else { 78 + const missing = []; 79 + if (req.body.title === undefined) missing.push("title"); 80 + if (req.body.completed === undefined) missing.push("completed"); 81 + if (missing.length > 0) { 82 + res 83 + .status(400) 84 + .send( 85 + `Missing field${missing.length > 1 ? "s" : ""}: ${missing.join(", ")}`, 86 + ); 87 + } 88 + const badTypes = []; 89 + if (!(typeof req.body.title === "string")) badTypes.push("title"); 90 + if (!(typeof req.body.completed === "boolean")) badTypes.push("completed"); 91 + if (badTypes.length > 0) { 92 + res 93 + .status(400) 94 + .send( 95 + `Invalid type${badTypes.length > 1 ? "s" : ""}: ${badTypes.join(", ")}`, 96 + ); 97 + return; 98 + } 99 + task.title = req.body.title ?? task.title; 100 + task.completed = req.body.completed ?? task.completed; 101 + await updateTask(task); 102 + res.json(task); 103 + } 104 + }); 105 + 106 + app.patch("/tasks/:id", async (req, res) => { 107 + const task = (await getTasks()).find((t) => t.id === req.params.id); 108 + if (!task) { 109 + res.status(404).send("Task not found"); 110 + } else { 111 + const badTypes = []; 112 + if ( 113 + Object.keys(req.body).includes("title") && 114 + !(typeof req.body.title === "string") 115 + ) 116 + badTypes.push("title"); 117 + if ( 118 + Object.keys(req.body).includes("completed") && 119 + !(typeof req.body.completed === "boolean") 120 + ) 121 + badTypes.push("completed"); 122 + if (badTypes.length > 0) { 123 + res 124 + .status(400) 125 + .send( 126 + `Invalid type${badTypes.length > 1 ? "s" : ""}: ${badTypes.join(", ")}`, 127 + ); 128 + return; 129 + } 130 + task.title = req.body.title ?? task.title; 131 + task.completed = req.body.completed ?? task.completed; 132 + await updateTask(task); 133 + res.json(task); 134 + } 8 135 }); 9 136 10 - app.get("/slow", (req, res) => { 11 - console.log("Slow task start"); 12 - const t = Date.now(); 13 - setTimeout(() => { 14 - let f = Date.now(); 15 - console.log(`Slow task finished in ${f - t}`); 16 - res.send(`Slow response after ${f - t} ms`); 17 - }, 5000); 137 + app.delete("/tasks/:id", async (req, res) => { 138 + const task = (await getTasks()).find((t) => t.id === req.params.id); 139 + if (!task) { 140 + res.status(404).send("Task not found"); 141 + } else { 142 + await removeTask(task.id); 143 + res.status(204).send(); 144 + } 18 145 }); 19 146 20 147 app.listen(port, () => {
+1
server/tasks.json
··· 1 + [{"id":"2d516f","title":"","completed":4},{"id":"a4ec84","title":"","completed":4},{"id":"a667ea","title":"","completed":4},{"id":"b4d420","title":"4e4tta475aj","completed":false}]