From e57365fd0c83b38ff71d85dff82e35218c17fd76 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sat, 4 Jun 2022 14:37:35 +0430 Subject: [PATCH] Added update/edit support. --- api/server.js | 32 +++++++---- src/App.js | 13 ++--- src/componets/AddItem.js | 6 +-- src/componets/EditItem.js | 61 +++++++++++++++++++++ src/componets/List.js | 82 +++++++++++++++++------------ src/componets/TagSelection.js | 6 ++- src/modules/deleteEntity.js | 4 +- src/modules/{addItem.js => send.js} | 19 +++---- src/styles/EditItem.css | 0 src/styles/List.css | 40 +++++++++----- src/styles/Loader.css | 4 +- 11 files changed, 188 insertions(+), 79 deletions(-) create mode 100644 src/componets/EditItem.js rename src/modules/{addItem.js => send.js} (69%) create mode 100644 src/styles/EditItem.css diff --git a/api/server.js b/api/server.js index 912d44e..a31dc96 100644 --- a/api/server.js +++ b/api/server.js @@ -13,6 +13,8 @@ const database = config.API.DB_NAME; const collection = config.API.COLLECTION_NAME; const client = new MongoClient(URI); +const db = client.db(database); +const list = db.collection(collection); app.use(cors()); @@ -42,23 +44,39 @@ app.post('/api', async (req, res) => { console.log(err); insertDoc(req.body); } finally { - res.send('DONE!'); + res.send('Posted!'); } }); +app.put('/api', async (req, res) => { + const id = req.body._id; + + await updateDoc(id, req.body); + + res.send('Updated!'); +}); + app.delete('/api', async (req, res) => { - const id = req.body.id.toString(); + const id = req.body.id; await deleteDoc(id); res.send(`Bookmark with _id:${id} deleted.`); }); +async function updateDoc(id, updatedListing) { + try { + await list.updateOne({ _id: id }, { $set: updatedListing }); + } + + catch(err) { + console.log(err); + } +} + async function insertDoc(doc) { try { - const db = client.db(database); - const list = db.collection(collection); - const result = await list.insertOne(doc); + await list.insertOne(doc); } catch(err) { @@ -68,8 +86,6 @@ async function insertDoc(doc) { async function getDoc() { try { - const db = client.db(database); - const list = db.collection(collection); const result = await list.find({}).toArray(); return result; @@ -82,8 +98,6 @@ async function getDoc() { async function deleteDoc(doc) { try { - const db = client.db(database); - const list = db.collection(collection); const result = await list.deleteOne({"_id": doc}); fs.unlink(config.API.STORAGE_LOCATION + '/LinkWarden/screenshot\'s/' + doc + '.png', (err) => { diff --git a/src/App.js b/src/App.js index fb28b4e..01bafcb 100644 --- a/src/App.js +++ b/src/App.js @@ -41,15 +41,15 @@ function App() { } function exitAdding() { - setNewBox(!newBox); + setNewBox(false); } function exitFilter() { - setFilterBox(!filterBox); + setFilterBox(false); } function exitSorting() { - setSortBox(!sortBox); + setSortBox(false); } function search(e) { @@ -59,8 +59,9 @@ function App() { function sortByFunc(e) { setSortBy(e) } - + const filteredData = filter(data, searchQuery, nameChecked, tagsChecked, descriptionChecked); + const tags = concatTags(data); async function fetchData() { const ADDRESS = config.API.ADDRESS + ":" + config.API.PORT; @@ -91,7 +92,7 @@ function App() { - + {numberOfResults === 0 ? : null} @@ -114,7 +115,7 @@ function App() { SetLoader={SetLoader} onExit={exitAdding} reFetch={fetchData} - tags={() => concatTags(data)} + tags={() => tags} /> : null} {loader ? : null} diff --git a/src/componets/AddItem.js b/src/componets/AddItem.js index 16a85ee..6f878be 100644 --- a/src/componets/AddItem.js +++ b/src/componets/AddItem.js @@ -1,16 +1,16 @@ import { useState } from 'react'; import '../styles/AddItem.css'; import TagSelection from './TagSelection'; -import addItem from '../modules/addItem'; +import addItem from '../modules/send'; const AddItem = ({onExit, reFetch, tags, SetLoader}) => { const [name, setName] = useState(''); const [link, setLink] = useState(''); const [tag, setTag] = useState([]); - + function newItem() { SetLoader(true) - addItem(name, link, tag, reFetch, onExit, SetLoader); + addItem(name, link, tag, reFetch, onExit, SetLoader, "POST"); } function SetName(e) { diff --git a/src/componets/EditItem.js b/src/componets/EditItem.js new file mode 100644 index 0000000..17a1ae7 --- /dev/null +++ b/src/componets/EditItem.js @@ -0,0 +1,61 @@ +import { useState } from 'react'; +import deleteEntity from '../modules/deleteEntity'; +import '../styles/AddItem.css'; +import TagSelection from './TagSelection'; +import editItem from '../modules/send'; + +// deleteEntity(e._id, reFetch) + +const EditItem = ({tags, item, onExit, SetLoader, reFetch}) => { + const [name, setName] = useState(item.name); + const [tag, setTag] = useState(item.tag); + + function EditItem() { + SetLoader(true) + editItem(name, item.link, tag, reFetch, onExit, SetLoader, "PUT", item._id); + } + + function deleteItem() { + SetLoader(true) + deleteEntity(item._id, reFetch, onExit, SetLoader) + } + + function SetName(e) { + setName(e.target.value); + } + + function SetTags(value) { + setTag(value); + setTag(value.map(e => e.value.toLowerCase())); + } + + function abort(e) { + if (e.target.className === "add-overlay") { + onExit(); + } + } + + const url = new URL(item.link); + + return ( + <> +
+
+ Edit bookmark + +
+

Link: {url.hostname}

+

{item.title}

+ +

Name:

+ +

Tags:

+ + +
+
+ + ) +} + +export default EditItem \ No newline at end of file diff --git a/src/componets/List.js b/src/componets/List.js index a832071..28475ba 100644 --- a/src/componets/List.js +++ b/src/componets/List.js @@ -1,44 +1,58 @@ import '../styles/List.css'; import LazyLoad from 'react-lazyload'; import ViewArchived from './ViewArchived'; -import deleteEntity from '../modules/deleteEntity'; +import EditItem from './EditItem'; +import { useState } from 'react' -const List = ({data, reFetch}) => { - return ( -
- {/* eslint-disable-next-line */} - {data.map((e, i) => { - try { - const url = new URL(e.link); - const favicon = 'http://www.google.com/s2/favicons?domain=' + url.hostname; - return ( -
-
- -
-
- {i + 1}. {e.name} ({url.hostname}) -
-
{e.title}
-
- {e.tag.map((e, i) => { - return (
{e}
) - })} +const List = ({data, tags, reFetch, SetLoader}) => { + const [editBox, setEditBox] = useState(false) + const [editIndex, setEditIndex] = useState(0) + + function edit(index) { + setEditBox(true); + setEditIndex(index); + } + + function exitEditing() { + setEditBox(false); + } + + return ( +
+ {/* eslint-disable-next-line */} + {data.map((e, i, array) => { + try { + const url = new URL(e.link); + const favicon = 'http://www.google.com/s2/favicons?domain=' + url.hostname; + return ( +
+
+ +
+
+ {i + 1}. {e.name} ({url.hostname}) +
+
{e.title}
+
+ {e.tag.map((e, i) => { + return (
{e}
) + })} +
+
+ + +
-
- -
deleteEntity(e._id, reFetch)}>
-
-
- ) - } catch (e) { - console.log(e); - } - })} -
- ) + ) + } catch (e) { + console.log(e); + } + })} + {editBox ? tags} onExit={exitEditing} SetLoader={SetLoader} reFetch={reFetch} item={data[editIndex]} /> : null} +
+ ) } export default List \ No newline at end of file diff --git a/src/componets/TagSelection.js b/src/componets/TagSelection.js index c41faf9..510a385 100644 --- a/src/componets/TagSelection.js +++ b/src/componets/TagSelection.js @@ -42,13 +42,17 @@ const customStyles = { }), } -export default function TagSelection({setTags, tags}) { +export default function TagSelection({setTags, tags, tag=[]}) { const data = tags().map((e) => { return { value: e, label: e } }) + const defaultTags = tag.map((e) => { + return { value: e, label: e } +}) return ( { +const deleteEntity = (id, reFetch, onExit, SetLoader) => { const ADDRESS = config.API.ADDRESS + ":" + config.API.PORT; fetch(ADDRESS + "/api", { method: "DELETE", @@ -11,7 +11,9 @@ const deleteEntity = (id, reFetch) => { }) .then(res => res.text()) .then(message => {console.log(message)}) + .then(() => onExit()) .then(() => reFetch()) + .then(() => {SetLoader(false)}); } export default deleteEntity; \ No newline at end of file diff --git a/src/modules/addItem.js b/src/modules/send.js similarity index 69% rename from src/modules/addItem.js rename to src/modules/send.js index 90450b8..fa1b0a2 100644 --- a/src/modules/addItem.js +++ b/src/modules/send.js @@ -1,7 +1,7 @@ import config from '../config'; import { nanoid } from 'nanoid'; -const addItem = async (name, link, tag, reFetch, onExit, SetLoader) => { +const addItem = async (name, link, tag, reFetch, onExit, SetLoader, method, id=nanoid()) => { function isValidHttpUrl(string) { let url; @@ -14,12 +14,12 @@ const addItem = async (name, link, tag, reFetch, onExit, SetLoader) => { return url.protocol === "http:" || url.protocol === "https:"; } - if(name !== '' && isValidHttpUrl(link) && tag !== '') { + if(isValidHttpUrl(link)) { const ADDRESS = config.API.ADDRESS + ":" + config.API.PORT; fetch(ADDRESS + "/api", { - method: "POST", + method: method, body: JSON.stringify({ - _id: nanoid(), + _id: id, name: name, title: '', link: link, @@ -30,17 +30,14 @@ const addItem = async (name, link, tag, reFetch, onExit, SetLoader) => { } }) .then(res => res.text()) - .then(message => {SetLoader(false)}) - .then(() => reFetch()); + .then(() => reFetch()) + .then(() => {SetLoader(false)}); onExit(); - } else if(name !== '' && link !== '' && tag !== '') { + } else { + SetLoader(false) alert('Please make sure the link is valid.\n\n(i.e. starts with "http"/"https")'); } - - else { - alert('Please fill all fields and make sure the link is valid.\n\n(i.e. starts with "http"/"https")'); - } } export default addItem; \ No newline at end of file diff --git a/src/styles/EditItem.css b/src/styles/EditItem.css new file mode 100644 index 0000000..e69de29 diff --git a/src/styles/List.css b/src/styles/List.css index 286bf0d..864c421 100644 --- a/src/styles/List.css +++ b/src/styles/List.css @@ -67,20 +67,28 @@ } -.delete { - color: white; - cursor: pointer; - transition: background-color 0.1s; - font-family: 'Font Awesome 5 Free'; - padding: 10px; - width: fit-content; - height: fit-content; - margin: 10px; +.edit-btn { + position: relative; border-radius: 100%; + margin: 20px 20px 20px 0px; + font-family: 'Font Awesome 5 Free'; + width: 40px; + height: 40px; + padding: 10px; + font-size: 1.1rem; + cursor: pointer; + box-shadow: rgba(0, 0, 0, 0.4) 0px 2px 4px, rgba(0, 0, 0, 0.3) 0px 7px 13px -3px, rgba(0, 0, 0, 0.2) 0px -3px 0px inset; + color: #ffffffb6; + background-color: #1f2c38; + border: none; + transition: background-color 0.1s; } -.delete:hover { - background-color: rgb(255, 123, 123); +.edit-btn:hover { + background-color: rgb(76, 117, 170); +} + +.edit-btn:active { box-shadow: rgba(0, 0, 0, 0.16) 0px 3px 6px, rgba(0, 0, 0, 0.23) 0px 3px 6px; } @@ -111,7 +119,15 @@ font-size: 1rem; } -.options { +.etc { display: flex; align-items: center; } + +.delete { + float: right; +} + +.delete:hover { + background-color: rgba(255, 65, 65, 0.8); +} \ No newline at end of file diff --git a/src/styles/Loader.css b/src/styles/Loader.css index 0451c27..29a84a1 100644 --- a/src/styles/Loader.css +++ b/src/styles/Loader.css @@ -1,6 +1,6 @@ .loader { - position: absolute; - bottom: 100px; + position: fixed; + bottom: 10%; left: 30%; right: 30%; text-align: center;