diff --git a/client/src/pages/account.tsx b/client/src/pages/account.tsx index 37341f5..33892d1 100644 --- a/client/src/pages/account.tsx +++ b/client/src/pages/account.tsx @@ -4,15 +4,39 @@ import axios from "axios"; import {SAPIBase} from "../tools/api"; import "./css/account.css"; +interface IAPIResponse { id: number, spend: string, purpose: string } + const AccountPage = () => { - const [ SAPIKEY, setSAPIKEY ] = React.useState(""); + const [ password, setPASSWORD ] = React.useState(""); + const [ username, setUSERNAME ] = React.useState(""); + const [ spend, setSPEND ] = React.useState(""); + const [ purpose, setPURPOSE ] = React.useState(""); const [ NBalance, setNBalance ] = React.useState("Not Authorized"); const [ NTransaction, setNTransaction ] = React.useState(0); + const [ LAPIResponse, setLAPIResponse ] = React.useState([]); + const [ NPostCount, setNPostCount ] = React.useState(0); + const [ NModifyCount, setNModifyCount ] = React.useState(0); + + + React.useEffect( () => { + let BComponentExited = false; + const asyncFun = async () => { + const { data } = await axios.get( SAPIBase + '/account/getBook'); + console.log(data); + // const data = [ { id: 0, title: "test1", content: "Example body" }, { id: 1, title: "test2", content: "Example body" }, { id: 2, title: "test3", content: "Example body" } ].slice(0, NPostCount); + if (BComponentExited) return; + setLAPIResponse(data); + }; + asyncFun().catch((e) => window.alert(`Error while running API Call: ${e}`)); + return () => { BComponentExited = true; } + }, [ NPostCount, NModifyCount ]); + + 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', { credentialID: username, credentialPW: password }); setNBalance(data.balance); } asyncFun().catch((e) => window.alert(`AN ERROR OCCURED: ${e}`)); @@ -22,7 +46,10 @@ 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', { credentialID: username, credentialPW: password, amount: amount }); + + setSPEND("" + amount); + setNTransaction(0); if (!data.success) { window.alert('Transaction Failed:' + data.msg); @@ -35,12 +62,43 @@ const AccountPage = () => { asyncFun().catch((e) => window.alert(`AN ERROR OCCURED: ${e}`)); } + const createNewPost = () => { + const asyncFun = async () => { + await axios.post( SAPIBase + '/account/addBook', { spend: spend, purpose: purpose } ); + setNPostCount(NPostCount + 1); + setSPEND(""); + setPURPOSE(""); + } + 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 + '/account/deleteBook', { id: id } ); + setNPostCount(Math.max(NPostCount - 1, 0)); + } + asyncFun().catch(e => window.alert(`AN ERROR OCCURED! ${e}`)); + } + + const modifyPost = (id: string) => { + const asyncFun = async () => { + await axios.post( SAPIBase + '/account/modifyBook', { id: id, spend: spend, purpose: purpose } ); + setSPEND(""); + setPURPOSE(""); + setNModifyCount(NModifyCount + 1); + } + asyncFun().catch(e => window.alert(`AN ERROR OCCURED! ${e}`)); + } + return (
-

Account

+

Account

- Enter API Key: setSAPIKEY(e.target.value)}/> + Enter Username: setUSERNAME(e.target.value)}/> +
+ Enter Password: setPASSWORD(e.target.value)}/>
@@ -55,6 +113,32 @@ const AccountPage = () => {
+ +
+

가계부

+

수입 / 지출을 기록해 보세요.

+
+
+ 금액 setSPEND(e.target.value)} className={"input-spend"}/> 원 +
+ 내역 setPURPOSE(e.target.value)} className={"input-purpose"}/> + +
+ +
+ { LAPIResponse.map( (val, i) => +
+
modifyPost(`${val.id}`)}>✎
+
deletePost(`${val.id}`)}>ⓧ
+ +

{ val.spend } 원

+

{ val.purpose }

+
+ ) + } +
+ + ); } diff --git a/client/src/pages/css/account.css b/client/src/pages/css/account.css index a0f123d..a8b96aa 100644 --- a/client/src/pages/css/account.css +++ b/client/src/pages/css/account.css @@ -1,3 +1,5 @@ + + .account-bank { margin: 20px auto; border: solid 1px rgba(0,0,0,0.3); @@ -30,4 +32,101 @@ .transaction { margin: 30px 0 10px 0; -} \ No newline at end of file +} + +.account-book-title p { + color: gray; +} + +.account-book-input { + + border: 1px solid black; + border-radius: 8px; + width: 40%; + justify-content: space-between; + align-items: center; + margin-left: 30%; + margin-right: 30%; + margin-bottom: 40px; + + +} + +.input-spend { + border: 1pt solid #ccc; + border-radius: 5px; + height: 25px; + width: 150px; + margin: 10px; + + +} + +.input-purpose { + border: 1pt solid #ccc; + border-radius: 5px; + height: 25px; + width: 300px; + margin: 10px; +} + +.book-add-button { + border: 1px solid #ccc; + background-color: #ccc; + border-radius: 5px; + height: 30px; + width: 40px; +} + + +.book-item { + position: relative; + border: 1px solid black; + border-radius: 5px; + width: 40%; + margin-left: 30%; + margin-right: 30%; + height: 100px; + margin-top: 10px; + +} + +.book-title { + + position: absolute; + left: 40px; + top: 12px; + margin: 10px; +} + +.book-body { + position: absolute; + left: 40px; + top: 50px; + margin: 10px; +} + +.delete-book-item { + position: absolute; + right: 15px; + top: 12px; + color: red; + font-size: 18px; + font-weight: 300; +} + +.modify-book-item { + color: blue; + position: absolute; + right: 45px; + top: 12px; + font-size: 18px; +} + +.delete-book-item:hover, .modify-book-item:hover { + cursor: pointer; + font-weight: bolder; + font-size:larger +} + + diff --git a/client/src/pages/css/feed.css b/client/src/pages/css/feed.css index 53580df..af6ac53 100644 --- a/client/src/pages/css/feed.css +++ b/client/src/pages/css/feed.css @@ -38,7 +38,15 @@ font-size: 18px; } -.delete-item:hover { +.modify-item { + position: absolute; + right: 45px; + top: 12px; + color: blue; + font-size: 18px; +} + +.delete-item, .modify-item:hover { font-weight: bold; cursor: pointer; } \ No newline at end of file diff --git a/client/src/pages/feed.tsx b/client/src/pages/feed.tsx index b64c53b..48d48a7 100644 --- a/client/src/pages/feed.tsx +++ b/client/src/pages/feed.tsx @@ -9,6 +9,7 @@ interface IAPIResponse { id: number, title: string, content: string } const FeedPage = (props: {}) => { const [ LAPIResponse, setLAPIResponse ] = React.useState([]); const [ NPostCount, setNPostCount ] = React.useState(0); + const [ NModifyCount, setNModifyCount ] = React.useState(0); const [ SNewPostTitle, setSNewPostTitle ] = React.useState(""); const [ SNewPostContent, setSNewPostContent ] = React.useState(""); @@ -23,7 +24,7 @@ const FeedPage = (props: {}) => { }; asyncFun().catch((e) => window.alert(`Error while running API Call: ${e}`)); return () => { BComponentExited = true; } - }, [ NPostCount ]); + }, [ NPostCount, NModifyCount ]); const createNewPost = () => { const asyncFun = async () => { @@ -44,6 +45,16 @@ const FeedPage = (props: {}) => { asyncFun().catch(e => window.alert(`AN ERROR OCCURED! ${e}`)); } + const modifyPost = (id: string) => { + const asyncFun = async () => { + await axios.post( SAPIBase + '/feed/modifyFeed', { id: id, title: SNewPostTitle, content: SNewPostContent } ); + setSNewPostTitle(""); + setSNewPostContent(""); + setNModifyCount(NModifyCount + 1); + } + asyncFun().catch(e => window.alert(`AN ERROR OCCURED! ${e}`)); + } + return (
@@ -58,6 +69,7 @@ const FeedPage = (props: {}) => { { LAPIResponse.map( (val, i) =>
deletePost(`${val.id}`)}>ⓧ
+
modifyPost(`${val.id}`)}>✎

{ val.title }

{ val.content }

diff --git a/seminar/.env.example b/seminar/.env.example index f84e5b2..64a2dfe 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 +USERNAME="chacha" +PASSWORD="chacha" +MONGO_URI="mongodb://localhost:27017/todos" \ No newline at end of file diff --git a/seminar/src/middleware/auth.js b/seminar/src/middleware/auth.js index 480f41d..490c385 100644 --- a/seminar/src/middleware/auth.js +++ b/seminar/src/middleware/auth.js @@ -1,5 +1,5 @@ const authMiddleware = (req, res, next) => { - if (req.body.credential === process.env.API_KEY) { + if ((req.body.credentialID == process.env.USERNAME) && (req.body.credentialPW == process.env.PASSWORD)) { console.log("[AUTH-MIDDLEWARE] Authorized User"); next(); } diff --git a/seminar/src/routes/account.js b/seminar/src/routes/account.js index 51572f8..1b48cb4 100644 --- a/seminar/src/routes/account.js +++ b/seminar/src/routes/account.js @@ -12,6 +12,7 @@ class BankDB { #total = 10000; + constructor() { console.log("[Bank-DB] DB Init Completed"); } getBalance = () => { @@ -24,7 +25,62 @@ class BankDB { } } +class BookDB { + static _inst_; + static getInst = () => { + if ( !BookDB._inst_ ) BookDB._inst_ = new BookDB(); + return BookDB._inst_; + } + + #id = 0; #itemCount = 0; #LDataDB = []; + + selectItems = () => { + return { success: true, data: this.#LDataDB } + } + + insertItem = ( item ) => { + const { spend, purpose } = item; + this.#LDataDB.push({ id: this.#id, spend, purpose }); + 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--; + this.#id--; + } + return BItemDeleted; + } + + modifyItem = ( item ) => { + let BItemModified = false; + + const { id, spend, purpose } = item; + const num_id = parseInt(id); + + this.#LDataDB.forEach((value) => { + const match = (value.id == num_id); + if (match) { + this.#LDataDB[value.id].spend = spend; + this.#LDataDB[value.id].purpose = purpose; + BItemModified = true; + } + }); + + return BItemModified; + } +} + + const bankDBInst = BankDB.getInst(); +const bookDBInst = BookDB.getInst(); router.post('/getInfo', authMiddleware, (req, res) => { try { @@ -47,4 +103,46 @@ router.post('/transaction', authMiddleware, (req, res) => { } }) +router.get('/getBook', (req, res) => { + try { + const dbRes = bookDBInst.selectItems(); + 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('/addBook', (req, res) => { + try { + const { spend, purpose } = req.body; + const addResult = bookDBInst.insertItem({ spend, purpose }); + if (!addResult) return res.status(500).json({ error: addResult.data }) + else return res.status(200).json({ isOK: true }); + } catch (e) { + return res.status(500).json({ error: e }); + } + }); + +router.post('/deleteBook', (req, res) => { + try { + const { id } = req.body; + const deleteResult = bookDBInst.deleteItem(parseInt(id)); + if (!deleteResult) return res.status(500).json({ error: "No item deleted" }) + else return res.status(200).json({ isDeleteOK: true }); + } catch (e) { + return res.status(500).json({ error: e }); + } +}); + +router.post('/modifyBook', (req, res) => { + try { + const { id, spend, purpose } = req.body; + const modifyResult = bookDBInst.modifyItem({ id, spend, purpose }); + if (!modifyResult) return res.status(500).json({ error: "No item modified" }) + else return res.status(200).json({ isModifyOK: true }); + } catch (e) { + return res.status(500).json({ error: e }); + } +}); module.exports = router; \ No newline at end of file diff --git a/seminar/src/routes/feed.js b/seminar/src/routes/feed.js index f9c19d9..d686e01 100644 --- a/seminar/src/routes/feed.js +++ b/seminar/src/routes/feed.js @@ -36,6 +36,24 @@ class FeedDB { if (BItemDeleted) id--; return BItemDeleted; } + + modifyItem = ( item ) => { + let BItemModified = false; + + const { id, title, content } = item; + const num_id = parseInt(id); + + this.#LDataDB.forEach((value) => { + const match = (value.id == num_id); + if (match) { + this.#LDataDB[value.id].title = title; + this.#LDataDB[value.id].content = content; + BItemModified = true; + } + }); + + return BItemModified; + } } const feedDBInst = FeedDB.getInst(); @@ -55,7 +73,7 @@ router.post('/addFeed', (req, res) => { try { const { title, content } = req.body; const addResult = feedDBInst.insertItem({ title, content }); - if (!addResult) return res.status(500).json({ error: dbRes.data }) + if (!addResult) return res.status(500).json({ error: addResult.data }) else return res.status(200).json({ isOK: true }); } catch (e) { return res.status(500).json({ error: e }); @@ -67,10 +85,21 @@ router.post('/deleteFeed', (req, res) => { 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 }); + else return res.status(200).json({ isDeleteOK: true }); } catch (e) { return res.status(500).json({ error: e }); } -}) +}); + +router.post('/modifyFeed', (req, res) => { + try { + const { id, title, content } = req.body; + const modifyResult = feedDBInst.modifyItem({ id, title, content }); + if (!modifyResult) return res.status(500).json({ error: "No item modified" }) + else return res.status(200).json({ isModifyOK: true }); + } catch (e) { + return res.status(500).json({ error: e }); + } +}); module.exports = router; \ No newline at end of file