From 441570960f7ba4c0a75f228bc0b59cc8c3b197f3 Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Tue, 21 Oct 2025 21:23:25 -0700 Subject: [PATCH] final submission --- app.db | Bin 0 -> 12288 bytes backend/db.ts | 30 +++++++++++++++++ backend/index.ts | 44 +++++++++++++++++++++++++ frontend/index.tsx | 80 +++++++++++++++++++++++++++++++++++++++++++-- notesApp.txt | 35 ++++++++++++++++++++ package.json | 4 +++ 6 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 app.db create mode 100644 backend/db.ts create mode 100644 notesApp.txt diff --git a/app.db b/app.db new file mode 100644 index 0000000000000000000000000000000000000000..1fc57076d87b8b7acefdc12e773bc814897e021d GIT binary patch literal 12288 zcmeI$!EVz)5C-7gqzP>#aBmieia{Kjv?+>hXr+h)E-Wa*wTszERuA(c`pfjCzG$m{HSXV;tWHh%LuNDD)!v-vbB$P?>AO7WCP2+`tJ z=e7vRot4Faf6MXTq9q>AzuxAo+MRF0YVZvK0SG_<0uX=z1Rwwb2tWV=5coHN!&=kc z-j<^xIi8sOe8TnXQEtxPne621u>LGozE-6D{Xo%`+;)~sO_x`8K7Ek4Y}=NfqiWA= zR+zlH)t1&*kEq?ED$+3xLakn^m=5E?fgg|Pl^T&h)X^a1bq6Zc-QT%IT1*US^;T0D zaT^A~V(NG{{&@Y&`!rAaAJTpl1Va=VSTs0SZCG~?jj%n0SG_<0uX=z1Rwwb2tWV=H(p?? z(G+(dta@CgD{{+=Q?}cdQ;rL zx9Vw=7gT)?sCs|y+}rJYo>yy%4Tn!*E-2(P=(98@z7op#N%^Mb> | null = null; + +export async function getDb(): Promise> { + if (!dbInstance) { + dbInstance = open({ + filename: 'app.db', + driver: sqlite3.Database, + }); + } + return dbInstance; +} + +export async function initSchema(): Promise { + const db = await getDb(); + await db.exec(` + CREATE TABLE IF NOT EXISTS notes ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT NOT NULL, + body TEXT NOT NULL, + visible BOOLEAN NOT NULL DEFAULT TRUE, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + `); +} diff --git a/backend/index.ts b/backend/index.ts index f86611f..2aed1a4 100644 --- a/backend/index.ts +++ b/backend/index.ts @@ -1,4 +1,5 @@ import express from 'express'; +import { initSchema, getDb } from './db'; const args = process.argv.slice(2); const PORT = args[0] == "--port" ? args[1] : 80; @@ -6,11 +7,54 @@ main(); async function main() { const app = express(); + app.use(express.json()); + + await initSchema(); app.get('/tryme', (_, res) => { res.send('Hello from the backend!') }) + app.get('/notes', async (_, res) => { + const db = await getDb(); + const notes = await db.all( + 'SELECT id, title, body, created_at FROM notes WHERE visible = true ORDER BY id DESC' + ); + res.json(notes); + }) + + app.post('/notes', async (req, res) => { + const { title, body } = req.body ?? {}; + console.log('posting note'); + console.log(req); + console.log(JSON.stringify(req.body)); + if (typeof title !== 'string' || typeof body !== 'string') { + res.status(400).json({ error: 'title and body are required strings' }); + return; + } + const db = await getDb(); + const result = await db.run( + 'INSERT INTO notes (title, body) VALUES (?, ?)', + title, + body + ); + res.status(201).json({ id: result.lastID, title, body }); + }) + + app.put('/delete-note', async (req, res) => { + const { id } = req.body ?? {}; + if (typeof id !== 'number') { + res.status(400).json({ error: 'id is required number' }); + return; + } + const db = await getDb(); + await db.run( + 'UPDATE notes SET visible = false WHERE id = ?', + id + ); + res.status(200).send(); + }) + app.listen(PORT, () => { console.log(`Example app listening on port ${PORT}`) }); diff --git a/frontend/index.tsx b/frontend/index.tsx index 1c79a42..b1a4ffd 100644 --- a/frontend/index.tsx +++ b/frontend/index.tsx @@ -1,13 +1,87 @@ -import React from 'react'; +import { Button, Card, CardContent, ListItemText, ListItem, List, TextField, Typography } from '@mui/material'; +import { useEffect, useState } from 'react'; import ReactDOM from 'react-dom/client'; + +const Notes = () => { + const [title, setTitle] = useState(''); + const [body, setBody] = useState(''); + const [notes, setNotes] = useState<{ id: number, title: string, body: string, visible: boolean }[]>([]); + + const getNotes = async () => { + const response = await fetch("/api/notes"); + const data = await response.json(); + setNotes(data); + } + + const addNote = async (title: string, body: string) => { + const response = await fetch("/api/notes", { + method: "POST", + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ title, body }), + }); + const data = await response.json(); + setNotes([...notes, data]); + } + + const deleteNote = async (id: number) => { + await fetch("/api/delete-note", { + method: "PUT", + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ id }), + }); + setNotes(notes.filter(note => note.id !== id)); + } + + useEffect(() => { + getNotes(); + }, []); + + return ( +
+ + + Add a Note + setTitle(e.target.value)} /> + setBody(e.target.value)} /> + + + + + {notes?.length > 0 ? notes?.map(note => ( + + + + + )) : } + + +
+ ) +} const heading =
-

Hello World!

-

This is an example page.

+
const rootNode = document.getElementById('app'); ReactDOM.createRoot(rootNode!).render(heading); +// fetch("/api/notes").then(v => v.json()).then(notes => console.log(`notes: ${JSON.stringify(notes)}`)); fetch("/api/tryme").then(v => v.text()).then(txt => console.log(`Message from the backend: "${txt}"`)); + + + + + +// {notes?.length > 0 ? notes?.map(note => ( +//
+//

{note.title}

+//

{note.body}

+// +//
+// )) :

No notes found

} \ No newline at end of file diff --git a/notesApp.txt b/notesApp.txt new file mode 100644 index 0000000..0b42413 --- /dev/null +++ b/notesApp.txt @@ -0,0 +1,35 @@ +Notes app. You can +* Add notes +* Remove notes +* View your notes from other computers in the same network +Notes have a title and body + + + + +1. persistent data storage - id, title, body, visible +2. add notes => plus button where they can add a note - title, body and press save +3. view notes => get request to retrieve all notes where visible is true -> pagination may be required +4. delete notes => trash icon inline with note that user can click, see confirmation, once confirmed, +make put request to update visible to false + +sql lite + +api endpoints +- notes + - get => retrieve notes + - put => update note + - post => create notes + + + +on page load make a get call to notes display results +add button to create notes +- open a modal where user can add title, body, and save +- on click of save make post request then invalidate (refetch the notes) + + +each notes will be a card that has a delete button +- open confirmatin modal on delete make put to change visibility then refetch notes + + diff --git a/package.json b/package.json index 2ec91ab..9c66fa3 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,14 @@ "author": "", "license": "ISC", "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/material": "^7.3.4", "@types/express": "^5.0.3", "@types/react": "^19.2.2", "@types/react-dom": "^19.2.2", "@vitejs/plugin-react-swc": "^4.1.0", + "better-sqlite3": "^12.4.1", "express": "^5.1.0", "sqlite": "^5.1.1", "sqlite3": "^5.1.7",