From cf36e56755f19986313e77a97a60bc94b4ebf85b Mon Sep 17 00:00:00 2001 From: Yannick Lescure Date: Sat, 16 Apr 2022 18:02:59 -0400 Subject: [PATCH] improve UI/UX + add views counter to articles --- client/src/components/Article/Actions.js | 33 +++++++++++++ client/src/components/Article/Article.js | 2 + client/src/constants.js | 2 +- client/src/pages/Article/Article.js | 46 ++++++++++++------ client/src/pages/UserPage.js | 30 +++++++++--- client/src/settings.js | 1 + server/handlers.js | 59 ++++++++++++++++++++++-- server/index.js | 2 + 8 files changed, 149 insertions(+), 26 deletions(-) create mode 100644 client/src/components/Article/Actions.js diff --git a/client/src/components/Article/Actions.js b/client/src/components/Article/Actions.js new file mode 100644 index 0000000..0cef92f --- /dev/null +++ b/client/src/components/Article/Actions.js @@ -0,0 +1,33 @@ +import styled from "styled-components"; +import { FaBookmark, FaRegBookmark, FaRegHeart, FaHeart, FaShare } from "react-icons/fa"; +import { COLORS } from "../../constants"; + +const Actions = ({ article }) => { + return ( + + + + {article.views} views + + + + + + + ) +} + +const Wrapper = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + margin: 48px 0; +`; +const Container = styled.div` + display: flex; + align-items: center; + color: ${COLORS.secondary}; + gap: 16px; +`; + +export default Actions; \ No newline at end of file diff --git a/client/src/components/Article/Article.js b/client/src/components/Article/Article.js index b0e5eda..8d66fb0 100644 --- a/client/src/components/Article/Article.js +++ b/client/src/components/Article/Article.js @@ -1,6 +1,7 @@ import styled from "styled-components"; import Content from './Content'; import Head from './Head'; +import Actions from './Actions'; const Article = ({ user, article }) => { return ( @@ -9,6 +10,7 @@ const Article = ({ user, article }) => { {article.title} + ) } diff --git a/client/src/constants.js b/client/src/constants.js index deb5c54..38174f2 100644 --- a/client/src/constants.js +++ b/client/src/constants.js @@ -33,5 +33,5 @@ export const COLORS = { // For styled-components // ${COLORS.danger} -export const MIN_CHAR = 3; +export const MIN_CHAR = 2; export const TEXTAREA_HEIGHT = 300; diff --git a/client/src/pages/Article/Article.js b/client/src/pages/Article/Article.js index ba373fa..41ab8df 100644 --- a/client/src/pages/Article/Article.js +++ b/client/src/pages/Article/Article.js @@ -13,12 +13,32 @@ const ArticlePage = () => { const [visibility, setVisibility] = useState(undefined); const [article, setArticle] = useState({}); const [loading, setLoading] = useState(true); - + const { state: { user }, } = useContext(UserContext); // console.log(user); + useEffect(() => { + if (username && slug) { + fetch(`/api/views`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ username, slug }), + }) + .then((res) => res.json()) + .then((json) => { + // console.log(json); + }) + .catch((err) => { + console.error(err); + // errorFromServerUser({ message: "An unknown error has occurred" }); + }); + } + }, []); + useEffect(() => { let unmounted = false; let fetchUrl = `/api/stories/${username}/${slug}`; @@ -33,30 +53,28 @@ const ArticlePage = () => { // console.log(response); const status = { 404: () => { - setVisibility('not-found'); + setVisibility("not-found"); }, 200: () => { setArticle(response.data); - } + }, }; status[response.status](); // setUser(response.data); setLoading(false); } }); - - return () => { - unmounted = true; - }; - // eslint-disable-next-line + + return () => { + unmounted = true; + }; + // eslint-disable-next-line }, [user, username, slug]); if (loading) return ; - if (visibility === 'not-found') return ; + if (visibility === "not-found") return ; - return ( -
- ) -} + return
; +}; -export default ArticlePage; \ No newline at end of file +export default ArticlePage; diff --git a/client/src/pages/UserPage.js b/client/src/pages/UserPage.js index 97e291f..cd97f3e 100644 --- a/client/src/pages/UserPage.js +++ b/client/src/pages/UserPage.js @@ -34,6 +34,7 @@ const UserPage = () => { if (loading) return ; + return ( <> @@ -43,7 +44,13 @@ const UserPage = () => { { - userPage.stories.map(story => ( + userPage.stories.length === 0 + ? +
Empty
+
Definition: Containing nothing.
+
Example: This page is empty.
+
+ : userPage.stories.map(story => (
)) } @@ -68,15 +75,24 @@ const Title = styled.h1` font-size: 24px; padding-bottom: 4px; `; -const UserImage = styled.img` - width: 32px; - height: 32px; - object-fit: cover; - border-radius: 50%; -`; const Spacer = styled.div` border-top: 1px solid ${COLORS.grey}; margin-bottom: 16px; `; +const EmptyText = styled.div` + display: flex; + flex-direction: column; + line-height: 1.6; + font-size: 16px; + + & div:first-child { + font-weight: bold; + } + + & em { + font-weight: bold; + color: ${COLORS.secondary}; + } +`; export default UserPage; \ No newline at end of file diff --git a/client/src/settings.js b/client/src/settings.js index 1694ff0..cfcc58c 100644 --- a/client/src/settings.js +++ b/client/src/settings.js @@ -19,6 +19,7 @@ export const initialStates = { visibility: "public", createdAt: "0", updatedAt: "0", + views: 0, }, user: { _id: null, diff --git a/server/handlers.js b/server/handlers.js index cdcdf34..135c0b0 100644 --- a/server/handlers.js +++ b/server/handlers.js @@ -189,6 +189,7 @@ const createUser = async (req, res) => { .toLowerCase() .replace(/[^a-zA-Z 0-9]+/g, ""); const result = await db.collection("users").insertOne(userArray); + const { _id, cart, bookmarks, ordersHistory, username } = userArray result ? res.status(200).json({ status: 200, @@ -196,11 +197,12 @@ const createUser = async (req, res) => { firstName, lastName, email, - _id: userArray._id, - cart: userArray.cart, - bookmarks: userArray.bookmarks, - ordersHistory: userArray.ordersHistory, + _id, + cart, + bookmarks, + ordersHistory, imageSrc: "undefined", + username }, message: "User Created", }) @@ -307,6 +309,52 @@ const updateStory = async (req, res) => { } }; +const updateStoryViews = async (req, res) => { + const client = new MongoClient(MONGO_URI, option); + const { username, slug } = req.body; + try { + await client.connect(); + const db = client.db(DB_NAME); + + const story = await db.collection("stories").findOne({ + $and : [ + { username }, { slug } + ] + }); + console.log(story); + + if (story) { + const updatedStory = {}; + updatedStory.views = story.views ? story.views + 1 : 1; + console.log(updatedStory); + + const result = await db.collection("stories").updateOne( + { _id: story._id }, + { + $set: updatedStory, + } + ); + console.log(result); + result + ? res.status(200).json({ + status: 200, + data: updatedStory, + message: "Story updated", + }) + : res.status(409).json({ status: 409, message: "ERROR" }); + } + else { + res.status(404).json({ status: 404, message: "Item not found" }); + } + + } catch (err) { + console.log("Error", err); + res.status(500).json({ status: 500, message: err }); + } finally { + client.close(); + } +}; + const getStory = async (req, res) => { console.log(req.params); console.log(req.query); @@ -324,6 +372,7 @@ const getStory = async (req, res) => { let data = {}; if (result) { const { title, content, imageSrc, createdAt, updatedAt, _id, userId, slug, visibility, username } = result; + const views = result.views ? result.views : 1; const user = await db.collection("users").findOne({ _id: userId }); data = { _id, @@ -341,6 +390,7 @@ const getStory = async (req, res) => { }, slug, visibility, + views }; const switchVisibility = { unlisted: () => { @@ -416,6 +466,7 @@ module.exports = { getStory, getStories, updateStory, + updateStoryViews, // updateCart, // updateBookmarks, // updateOrdersHistory, diff --git a/server/index.js b/server/index.js index 6fbe208..01a7f18 100644 --- a/server/index.js +++ b/server/index.js @@ -15,6 +15,7 @@ const { getStory, getStories, updateStory, + updateStoryViews, // updateCart, // updateBookmarks, // updateOrdersHistory, @@ -56,6 +57,7 @@ app.get("/api/users/:username", getUser) app.get("/api/stories/:username", getStories) app.get("/api/stories/:username/:slug", getStory) app.put("/api/stories/:username/:slug", updateStory) +app.put("/api/views", updateStoryViews) app.put("/api/users", updateUser) app.post("/api/stories", createStory) app.post("/api/login", loginUser)