From 0c01985592529f3d99311df1303829e8d251bfd6 Mon Sep 17 00:00:00 2001 From: sciberbee Date: Sat, 6 Apr 2024 18:11:00 +0900 Subject: [PATCH 1/6] Example 1 done --- client/src/pages/css/feed.css | 14 ++++++++++++++ client/src/pages/feed.tsx | 20 ++++++++++++++++++++ seminar/src/routes/feed.js | 21 +++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/client/src/pages/css/feed.css b/client/src/pages/css/feed.css index 53580df..6529dc3 100644 --- a/client/src/pages/css/feed.css +++ b/client/src/pages/css/feed.css @@ -41,4 +41,18 @@ .delete-item:hover { font-weight: bold; cursor: pointer; +} + +.edit-item { + position: absolute; + right: 50px; + top: 12px; + color: blue; + font-size: 18px; +} + +.edit-item:hover { + font-weight: bold; + cursor: pointer; + font-size: 24px; } \ No newline at end of file diff --git a/client/src/pages/feed.tsx b/client/src/pages/feed.tsx index b64c53b..98e35fa 100644 --- a/client/src/pages/feed.tsx +++ b/client/src/pages/feed.tsx @@ -44,6 +44,21 @@ const FeedPage = (props: {}) => { asyncFun().catch(e => window.alert(`AN ERROR OCCURED! ${e}`)); } + const editPost = (id: string, newTitle: string, newContent: string) => { + const asyncFun = async () => { + await axios.post( SAPIBase + '/feed/editFeed', { id: id, title: newTitle, content: newContent } ); + const updatedPosts = LAPIResponse.map( (val) => { + if (val.id === parseInt(id)) { + val.title = newTitle; + val.content = newContent; + } + return val; + }); + setLAPIResponse(updatedPosts); + } + asyncFun().catch(e => window.alert(`AN ERROR OCCURED! ${e}`)); + } + return (
@@ -58,6 +73,11 @@ const FeedPage = (props: {}) => { { LAPIResponse.map( (val, i) =>
deletePost(`${val.id}`)}>ⓧ
+
{ + const newTitle = window.prompt("Enter the new title", val.title); + const newContent = window.prompt("Enter the new content", val.content); + if (newTitle && newContent) editPost(`${val.id}`, newTitle, newContent); + }}>🖍️

{ val.title }

{ val.content }

diff --git a/seminar/src/routes/feed.js b/seminar/src/routes/feed.js index f9c19d9..a36bd37 100644 --- a/seminar/src/routes/feed.js +++ b/seminar/src/routes/feed.js @@ -36,6 +36,17 @@ class FeedDB { if (BItemDeleted) id--; return BItemDeleted; } + + editItem = ( id, item ) => { + const { title, content } = item; + this.#LDataDB = this.#LDataDB.map((value) => { + if (value.id === id) { + value.title = title; + value.content = content; + } + return value; + }); + } } const feedDBInst = FeedDB.getInst(); @@ -73,4 +84,14 @@ router.post('/deleteFeed', (req, res) => { } }) +router.post('/editFeed', (req, res) => { + try { + const { id, title, content } = req.body; + feedDBInst.editItem(parseInt(id), { title, content }); + return res.status(200).json({ isOK: true }); + } catch (e) { + return res.status(500).json({ error: e }); + } +}) + module.exports = router; \ No newline at end of file From bab6db08dddc2559c8fc8a99054ab77924296173 Mon Sep 17 00:00:00 2001 From: sciberbee Date: Sat, 6 Apr 2024 19:02:44 +0900 Subject: [PATCH 2/6] Example 2 done --- client/src/pages/account.tsx | 16 ++++++++++------ seminar/src/middleware/auth.js | 3 ++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/client/src/pages/account.tsx b/client/src/pages/account.tsx index 37341f5..0a15cb1 100644 --- a/client/src/pages/account.tsx +++ b/client/src/pages/account.tsx @@ -5,14 +5,15 @@ import {SAPIBase} from "../tools/api"; import "./css/account.css"; const AccountPage = () => { - const [ SAPIKEY, setSAPIKEY ] = React.useState(""); + const [ SAPIUSERID, setSAPIUSERID ] = React.useState(""); // User ID + const [ SAPIPASSWORD, setSAPIPASSWORD ] = React.useState(""); // Password const [ NBalance, setNBalance ] = React.useState("Not Authorized"); const [ NTransaction, setNTransaction ] = React.useState(0); const getAccountInformation = () => { const asyncFun = async() => { interface IAPIResponse { balance: number }; - const { data } = await axios.post(SAPIBase + '/account/getInfo', { credential: SAPIKEY }); + const { data } = await axios.post(SAPIBase + '/account/getInfo', { userId: SAPIUSERID, password: SAPIPASSWORD }); setNBalance(data.balance); } asyncFun().catch((e) => window.alert(`AN ERROR OCCURED: ${e}`)); @@ -22,7 +23,7 @@ const AccountPage = () => { const asyncFun = async() => { if (amount === '') return; interface IAPIResponse { success: boolean, balance: number, msg: string }; - const { data } = await axios.post(SAPIBase + '/account/transaction', { credential: SAPIKEY, amount: amount }); + const { data } = await axios.post(SAPIBase + '/account/transaction', { userId: SAPIUSERID, password: SAPIPASSWORD, amount: amount }); setNTransaction(0); if (!data.success) { window.alert('Transaction Failed:' + data.msg); @@ -39,10 +40,13 @@ const AccountPage = () => {

Account

-
- Enter API Key: setSAPIKEY(e.target.value)}/> - +
+ Enter User ID: setSAPIUSERID(e.target.value)}/>
+
+ Enter Password: setSAPIPASSWORD(e.target.value)}/> +
+

The National Bank of SPARCS API

diff --git a/seminar/src/middleware/auth.js b/seminar/src/middleware/auth.js index 480f41d..f1e8c51 100644 --- a/seminar/src/middleware/auth.js +++ b/seminar/src/middleware/auth.js @@ -1,5 +1,6 @@ const authMiddleware = (req, res, next) => { - if (req.body.credential === process.env.API_KEY) { + const { userId, password } = req.body; // Destructure user ID and password from the request body + if (userId === process.env.API_USERID && password === process.env.API_PASSWORD) { // Check if the user ID and password match the expected values console.log("[AUTH-MIDDLEWARE] Authorized User"); next(); } From b2a9bd7834d0d09340121e39de1c64badc27fb75 Mon Sep 17 00:00:00 2001 From: sciberbee Date: Sat, 6 Apr 2024 19:08:39 +0900 Subject: [PATCH 3/6] .env.example edited --- seminar/.env.example | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/seminar/.env.example b/seminar/.env.example index f84e5b2..b30ea47 100644 --- a/seminar/.env.example +++ b/seminar/.env.example @@ -1,3 +1,5 @@ PORT=8080 NODE_ENV=DEVELOPMENT -API_KEY= \ No newline at end of file +API_KEY= +API_USERID=sciberbee +API_PASSWORD=331 \ No newline at end of file From 77876352e8fd6f46ab4739b5c64f582aaee06714 Mon Sep 17 00:00:00 2001 From: sciberbee Date: Sat, 6 Apr 2024 20:21:58 +0900 Subject: [PATCH 4/6] exercise 3 finished --- client/src/App.tsx | 4 +- client/src/pages/cat-image.tsx | 16 ---- client/src/pages/css/kawaii-stagram.css | 60 ++++++++++++++ client/src/pages/home.tsx | 10 +-- client/src/pages/kawaii-stagram.tsx | 102 ++++++++++++++++++++++++ seminar/src/index.js | 2 + seminar/src/routes/kawaii-stagram.js | 101 +++++++++++++++++++++++ 7 files changed, 270 insertions(+), 25 deletions(-) delete mode 100644 client/src/pages/cat-image.tsx create mode 100644 client/src/pages/css/kawaii-stagram.css create mode 100644 client/src/pages/kawaii-stagram.tsx create mode 100644 seminar/src/routes/kawaii-stagram.js diff --git a/client/src/App.tsx b/client/src/App.tsx index e36b5f1..b527b37 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -7,7 +7,7 @@ import PageNotFound from "./pages/404"; import Footer from "./components/footer"; import './App.css'; import AccountPage from "./pages/account"; -import CatImagePage from "./pages/cat-image"; +import KawaiiStagram from "./pages/kawaii-stagram"; const App = () => { @@ -18,7 +18,7 @@ const App = () => { }/> }/> }/> - }/> + }/> }/> }/> diff --git a/client/src/pages/cat-image.tsx b/client/src/pages/cat-image.tsx deleted file mode 100644 index 64d2caa..0000000 --- a/client/src/pages/cat-image.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from "react"; -import Header from "../components/header"; -import {SAPIBase} from "../tools/api"; - -const CatImagePage = () => { - return ( -
-
-

Static *Cute* Image

- {"cute -

Wow so cute from static

-
- ); -}; - -export default CatImagePage; \ No newline at end of file diff --git a/client/src/pages/css/kawaii-stagram.css b/client/src/pages/css/kawaii-stagram.css new file mode 100644 index 0000000..196a39e --- /dev/null +++ b/client/src/pages/css/kawaii-stagram.css @@ -0,0 +1,60 @@ +.feed-list { + padding: 25px 10px; + width: calc(100% - 20px); + max-width: 800px; + margin: auto; +} + +.feed-item { + border: solid 1px rgba(0,0,0,0.2); + border-radius: 15px; + width: 100%; + margin: 10px 0; + position: relative; + background-color: #FFD700; /* Golden yellow */ + box-shadow: 0 0 10px rgba(0,0,0,0.2); +} + +.feed-item-add { + border: dotted 1px black; + border-radius: 15px; + padding: 20px 10px 0 10px; +} + +.post-add-button { + margin-top: 20px; + padding: 10px; + border-top: solid 1px rgba(0,0,0,0.1); +} + +.post-add-button:hover { + cursor: pointer; + font-weight: 700; +} + +.delete-item { + position: absolute; + right: 15px; + top: 12px; + color: red; + font-size: 18px; +} + +.delete-item:hover { + font-weight: bold; + cursor: pointer; +} + +.edit-item { + position: absolute; + right: 50px; + top: 12px; + color: blue; + font-size: 18px; +} + +.edit-item:hover { + font-weight: bold; + cursor: pointer; + font-size: 24px; +} \ No newline at end of file diff --git a/client/src/pages/home.tsx b/client/src/pages/home.tsx index 3611def..66df8c1 100644 --- a/client/src/pages/home.tsx +++ b/client/src/pages/home.tsx @@ -39,13 +39,9 @@ const HomePage = (props: {}) => {
Example #2
Middleware & Authorization
-
navigate("/cat-image") }> -
Example #3
-
Serve *Cute* Image Files
-
-
navigate("/ssr") }> -
Example #4
-
Server Side Rendering
+
navigate("/kawaii-stagram") }> +
cyber's example
+
Kawaii-stagram
diff --git a/client/src/pages/kawaii-stagram.tsx b/client/src/pages/kawaii-stagram.tsx new file mode 100644 index 0000000..d83aa78 --- /dev/null +++ b/client/src/pages/kawaii-stagram.tsx @@ -0,0 +1,102 @@ +import React from "react"; +import axios from "axios"; +import { SAPIBase } from "../tools/api"; +import Header from "../components/header"; +import "./css/kawaii-stagram.css"; + +interface IAPIResponse { id: number, title: string, description: string, image: string } + +const KawaiiStagram = () => { + const [ LAPIResponse, setLAPIResponse ] = React.useState([]); + const [ NPostCount, setNPostCount ] = React.useState(0); + const [ SNewPostTitle, setSNewPostTitle ] = React.useState(""); + const [ SNewPostdescription, setSNewPostdescription ] = React.useState(""); + const [ SNewPostImage, setSNewPostImage ] = React.useState(""); + + React.useEffect( () => { + let BComponentExited = false; + const asyncFun = async () => { + const { data } = await axios.get( SAPIBase + `/kawaii-stagram/getFeed?count=${ NPostCount }`); + console.log(data); + if (BComponentExited) return; + setLAPIResponse(data); + }; + asyncFun().catch((e) => window.alert(`Error while running API Call: ${e}`)); + return () => { BComponentExited = true; } + }, [ NPostCount ]); + + const createNewPost = () => { + const asyncFun = async () => { + await axios.post( SAPIBase + '/kawaii-stagram/addFeed', { title: SNewPostTitle, description: SNewPostdescription, image: SNewPostImage } ); + setNPostCount(NPostCount + 1); + setSNewPostTitle(""); + setSNewPostdescription(""); + setSNewPostImage(""); + } + asyncFun().catch(e => window.alert(`AN ERROR OCCURED! ${e}`)); + } + + const deletePost = (id: string) => { + const asyncFun = async () => { + // One can set X-HTTP-Method header to DELETE to specify deletion as well + await axios.post( SAPIBase + '/kawaii-stagram/deleteFeed', { id: id } ); + setNPostCount(Math.max(NPostCount - 1, 0)); + } + asyncFun().catch(e => window.alert(`AN ERROR OCCURED! ${e}`)); + } + + const editPost = (id: string, newTitle: string, newDescription: string, newImage: string) => { + const asyncFun = async () => { + await axios.post( SAPIBase + '/kawaii-stagram/editFeed', { id: id, title: newTitle, description: newDescription, image: newImage } ); + const updatedPosts = LAPIResponse.map( (val) => { + if (val.id === parseInt(id)) { + val.title = newTitle; + val.description = newDescription; + val.image = newImage; + } + return val; + }); + setLAPIResponse(updatedPosts); + } + asyncFun().catch(e => window.alert(`AN ERROR OCCURED! ${e}`)); + } + + return ( +
+
+

Kawaii-Stagram

+
+ Number of posts to show:    + setNPostCount( parseInt(e.target.value) ) } + /> +
+
+ { LAPIResponse.map( (val, i) => +
+
deletePost(`${val.id}`)}>ⓧ
+
{ + const newTitle = window.prompt("Enter the new title", val.title); + const newdescription = window.prompt("Enter the new description", val.description); + const newImage = window.prompt("Enter the link of image", val.image); + if (newTitle && newdescription && newImage) editPost(`${val.id}`, newTitle, newdescription, newImage); + }}>🖍️
+

{ val.title }

+

{ val.description }

+ { +
+ ) } +
+ Title: setSNewPostTitle(e.target.value)}/> +      + description: setSNewPostdescription(e.target.value)}/> +      + Image: setSNewPostImage(e.target.value)}/> +
createNewPost()}>Add Post!
+
+
+
+ ); +} + +export default KawaiiStagram; \ No newline at end of file diff --git a/seminar/src/index.js b/seminar/src/index.js index 9f5dc2d..9c3890e 100644 --- a/seminar/src/index.js +++ b/seminar/src/index.js @@ -5,6 +5,7 @@ require('dotenv').config(); const statusRouter = require('./routes/status'); const feedRouter = require('./routes/feed'); const accountRouter = require('./routes/account'); +const kawaiiStagramRouter = require('./routes/kawaii-stagram'); const app = express(); const port = process.env.PORT; @@ -26,6 +27,7 @@ app.use(cors(corsOptions)); app.use('/status', statusRouter); app.use('/feed', feedRouter); app.use('/account', accountRouter); +app.use('/kawaii-stagram', kawaiiStagramRouter); app.listen(port, () => { console.log(`Example App Listening @ http://localhost:${ port }`); diff --git a/seminar/src/routes/kawaii-stagram.js b/seminar/src/routes/kawaii-stagram.js new file mode 100644 index 0000000..4c9d273 --- /dev/null +++ b/seminar/src/routes/kawaii-stagram.js @@ -0,0 +1,101 @@ +const express = require('express'); + +const router = express.Router(); + +class FeedDB { + static _inst_; + static getInst = () => { + if ( !FeedDB._inst_ ) FeedDB._inst_ = new FeedDB(); + return FeedDB._inst_; + } + + #id = 1; #itemCount = 1; #LDataDB = [ + { id: 0, title: "Eru Chitanda", description: "I'm Curious!", image: "https://nekos.best/api/v2/happy/084743a2-4c76-4d3c-9f32-83900742e188.gif"}, + { id: 1, title: "Kaori Miyazono", description: "You gave me a forever within the numbered days, and I'm grateful!", image: "https://nekos.best/api/v2/happy/1c03fe97-7883-4b84-b726-21505e50e587.gif"} +]; + + constructor() { console.log("[Feed-DB] DB Init Completed"); } + + selectItems = ( count ) => { + if (count > this.#itemCount) return { success: false, data: "Too many items queried" }; + if (count < 0) return { success: false, data: "Invalid count provided" }; + else return { success: true, data: this.#LDataDB.slice(0, count) } + } + + insertItem = ( item ) => { + const { title, description, image } = item; + this.#LDataDB.push({ id: this.#id, title, description, image }); + this.#id++; this.#itemCount++; + return true; + } + + deleteItem = ( id ) => { + let BItemDeleted = false; + this.#LDataDB = this.#LDataDB.filter((value) => { + const match = (value.id === id); + if (match) BItemDeleted = true; + return !match; + }); + if (BItemDeleted) id--; + return BItemDeleted; + } + + editItem = ( id, item ) => { + const { title, description, image } = item; + this.#LDataDB = this.#LDataDB.map((value) => { + if (value.id === id) { + value.title = title; + value.description = description; + value.image = image; + } + return value; + }); + } +} + +const feedDBInst = FeedDB.getInst(); + +router.get('/getFeed', (req, res) => { + try { + const requestCount = parseInt(req.query.count); + const dbRes = feedDBInst.selectItems(requestCount); + if (dbRes.success) return res.status(200).json(dbRes.data); + else return res.status(500).json({ error: dbRes.data }) + } catch (e) { + return res.status(500).json({ error: e }); + } +}); + +router.post('/addFeed', (req, res) => { + try { + const { title, description, image } = req.body; + const addResult = feedDBInst.insertItem({ title, description, image }); + if (!addResult) return res.status(500).json({ error: dbRes.data }) + else return res.status(200).json({ isOK: true }); + } catch (e) { + return res.status(500).json({ error: e }); + } +}); + +router.post('/deleteFeed', (req, res) => { + try { + const { id } = req.body; + const deleteResult = feedDBInst.deleteItem(parseInt(id)); + if (!deleteResult) return res.status(500).json({ error: "No item deleted" }) + else return res.status(200).json({ isOK: true }); + } catch (e) { + return res.status(500).json({ error: e }); + } +}) + +router.post('/editFeed', (req, res) => { + try { + const { id, title, description, image } = req.body; + feedDBInst.editItem(parseInt(id), { title, description, image }); + return res.status(200).json({ isOK: true }); + } catch (e) { + return res.status(500).json({ error: e }); + } +}) + +module.exports = router; \ No newline at end of file From b87871b95dfb8e35e3a728edcd54e263bcdb5a1d Mon Sep 17 00:00:00 2001 From: sciberbee Date: Sat, 6 Apr 2024 20:41:58 +0900 Subject: [PATCH 5/6] exercise 3 font changed --- client/public/index.html | 3 +++ client/src/pages/css/kawaii-stagram.css | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/client/public/index.html b/client/public/index.html index 743fab6..e411df2 100644 --- a/client/public/index.html +++ b/client/public/index.html @@ -26,6 +26,9 @@ Learn how to configure a non-root public URL by running `npm run build`. --> SPARCS Backend Seminar Demo Front End + + + diff --git a/client/src/pages/css/kawaii-stagram.css b/client/src/pages/css/kawaii-stagram.css index 196a39e..76e7ab6 100644 --- a/client/src/pages/css/kawaii-stagram.css +++ b/client/src/pages/css/kawaii-stagram.css @@ -13,6 +13,10 @@ position: relative; background-color: #FFD700; /* Golden yellow */ box-shadow: 0 0 10px rgba(0,0,0,0.2); + font-family: "Varela Round", sans-serif; + font-size: larger; + font-weight: 400; + font-style: normal; } .feed-item-add { From c42ad25976cd9b94fe7359a80ca76537be5d5559 Mon Sep 17 00:00:00 2001 From: Seunghyeon Bang Date: Sat, 6 Apr 2024 20:51:59 +0900 Subject: [PATCH 6/6] Update README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 8a8a60a..30e311a 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ Repository for Node / Express Seminar @ SPARCS Code by [Jiho Park](https://github.com/UrWrstNightmare) (Night @ SPARCS) +Finished by [Sé Bang](https://github.com/sciberbee) (cyber @ SPARCS) --- ### Folder Structure @@ -11,3 +12,8 @@ Code by [Jiho Park](https://github.com/UrWrstNightmare) (Night @ SPARCS) ### How to Run +change to example2 branch +and run 2 commands in both 'seminar' and 'client' directories: + +npm install +npm start