From 993258608555f39353b5174cdbb46e20c42ab3b6 Mon Sep 17 00:00:00 2001 From: Kyle Slugg Date: Wed, 17 May 2023 17:33:32 -0400 Subject: [PATCH 1/2] Minor stylistic changes --- .../containers/MainContainer/MainContainer.jsx | 15 +++++++++------ client/src/containers/Sidebar/Sidebar.module.scss | 1 + 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/client/src/containers/MainContainer/MainContainer.jsx b/client/src/containers/MainContainer/MainContainer.jsx index f450085..1dfa4e2 100644 --- a/client/src/containers/MainContainer/MainContainer.jsx +++ b/client/src/containers/MainContainer/MainContainer.jsx @@ -18,12 +18,12 @@ const MainContainer = () => { fetch('http://localhost:3000/authentication/login', { method: 'POST', headers: { - 'Content-Type': 'application/json', + 'Content-Type': 'application/json' }, body: JSON.stringify({ username: usernameInputValue, - password: passwordInputValue, - }), + password: passwordInputValue + }) }) .then((result) => result.json()) .then((result) => { @@ -33,6 +33,9 @@ const MainContainer = () => { .catch((err) => { console.log(err); }); + + //Bypass login requirement: + setLogin(true); }; //functino to handle showing the signup page const handleHaveAccount = () => setHaveAccount(false); @@ -48,12 +51,12 @@ const MainContainer = () => { fetch('http://localhost:3000/authentication/signup', { method: 'POST', headers: { - 'Content-Type': 'application/json', + 'Content-Type': 'application/json' }, body: JSON.stringify({ username: nameValue, - password: passwordValue, - }), + password: passwordValue + }) }) .then((result) => result.json()) .then((result) => { diff --git a/client/src/containers/Sidebar/Sidebar.module.scss b/client/src/containers/Sidebar/Sidebar.module.scss index cbca283..a795c8f 100644 --- a/client/src/containers/Sidebar/Sidebar.module.scss +++ b/client/src/containers/Sidebar/Sidebar.module.scss @@ -16,6 +16,7 @@ width: 100%; input { display: none; + appearance: none; } label { } From 74d1a9d20a4e075836193ca0bdb74f3bd65aeb58 Mon Sep 17 00:00:00 2001 From: Kyle Slugg Date: Wed, 17 May 2023 20:38:06 -0400 Subject: [PATCH 2/2] Added preliminary tag support, though not yet filtering --- .../src/components/AddSnippet/AddSnippet.jsx | 15 +- client/src/containers/Sidebar/Sidebar.jsx | 138 +++++++++++++----- .../containers/Sidebar/Sidebar.module.scss | 35 +++-- .../containers/Sidebar/TagsList/TagsList.jsx | 41 ++++++ .../Sidebar/TagsList/TagsList.module.scss | 16 ++ server/controllers/snippetsController.js | 3 + server/routes/snippetsRouter.js | 10 +- 7 files changed, 196 insertions(+), 62 deletions(-) create mode 100644 client/src/containers/Sidebar/TagsList/TagsList.jsx create mode 100644 client/src/containers/Sidebar/TagsList/TagsList.module.scss diff --git a/client/src/components/AddSnippet/AddSnippet.jsx b/client/src/components/AddSnippet/AddSnippet.jsx index b65d255..c92bfa8 100644 --- a/client/src/components/AddSnippet/AddSnippet.jsx +++ b/client/src/components/AddSnippet/AddSnippet.jsx @@ -7,8 +7,6 @@ import TagInput from '../../components/ui/TagInput/TagInput'; // importing external functionality import CodeMirror from '@uiw/react-codemirror'; import PropTypes from 'prop-types'; -import { markdown, markdownLanguage } from '@codemirror/lang-markdown'; -import { languages } from '@codemirror/language-data'; import { langs } from '@uiw/codemirror-extensions-langs'; // importing utils @@ -21,12 +19,12 @@ import styles from './AddSnippet.module.scss'; // importing data import { LANGUAGES } from '../../data/data.js'; -const AddSnippet = ({ closeModal }) => { +const AddSnippet = ({ closeModal, getUserData }) => { const [title, setTitle] = useState(''); const [language, setLanguage] = useState(''); const [comments, setComments] = useState(''); const [storedCode, setStoredCode] = useState(''); - const [tagList, setTags] = useState(''); + const [tagList, setTags] = useState([]); const [error, setError] = useState(false); const [openModal, setOpenModal] = useState(false); @@ -57,7 +55,11 @@ const AddSnippet = ({ closeModal }) => { storedCode: storedCode }) }) - .then((data) => data.json()) + .then((data) => { + getUserData(); + closeModal(false); + data.json(); + }) .catch((err) => { console.log(err); console.log('failed saving snippet'); @@ -178,7 +180,8 @@ const AddSnippet = ({ closeModal }) => { }; AddSnippet.propTypes = { - closeModal: PropTypes.func + closeModal: PropTypes.func, + getUserData: PropTypes.func }; export default AddSnippet; diff --git a/client/src/containers/Sidebar/Sidebar.jsx b/client/src/containers/Sidebar/Sidebar.jsx index ca6a0b5..8abb0ee 100644 --- a/client/src/containers/Sidebar/Sidebar.jsx +++ b/client/src/containers/Sidebar/Sidebar.jsx @@ -4,6 +4,7 @@ import React, { useState, useEffect } from 'react'; import SnippetDisplay from '../../components/SnippetDisplay/SnippetDisplay.jsx'; import AddSnippet from '../../components/AddSnippet/AddSnippet.jsx'; import SnippetsRadioList from './SnippetsRadioList/SnippetsRadioList.jsx'; +import TagsList from './TagsList/TagsList.jsx'; // importing utils import { Card, Spinner } from 'react-bootstrap'; @@ -16,27 +17,35 @@ import arrow from '../../assets/arrow.png'; import img from '../../assets/star nose mole.jpeg'; const Sidebar = ({ handleLogin }) => { + //Snippets and selected snippet const [snippets, setSnippets] = useState([]); const [selectedSnippet, setSelectedSnippet] = useState({}); + + //Tags and selected tags + const [userTags, setUserTags] = useState([]); + const [selectedTags, setSelectedTags] = useState([]); + const [openModal, setOpenModal] = useState(false); const [collapse, setCollapse] = useState(false); const [loading, setLoading] = useState(true); + const [displayType, setDisplayType] = useState('snippets'); useEffect(() => { - getSnippet(); + getUserData(); }, []); //Get all snippets stored under user's account //TODO: Get user ID from global state to include in request //FIXME: HARD CODING ID FOR NOW const userId = '6463eb52ab99bf89a84a3ebd'; - const getSnippet = () => { + const getUserData = () => { setLoading(true); fetch('/snippets?' + new URLSearchParams({ userId: userId })) .then((res) => res.json()) - .then((newSnippetArray) => { + .then((data) => { //As structured in snippets route, should receive an array of snippet objects - setSnippets(newSnippetArray); + setSnippets(data.snippets); + setUserTags([...data.tagsLangs.tags, ...data.tagsLangs.languages]); setLoading(false); }) .catch((error) => @@ -60,10 +69,69 @@ const Sidebar = ({ handleLogin }) => { setSelectedSnippet(e); }; + const selectDeselectTag = (tagValue) => { + const newTagList = new Set(selectedTags); + if (!newTagList.has(tagValue)) { + newTagList.add(tagValue); + } else { + newTagList.delete(tagValue); + } + + setSelectedTags(Array.from(newTagList)); + }; + const toggleSidebar = () => { setCollapse(() => !collapse); }; + const toggleDisplayType = (event) => { + console.log(event.target.value); + setDisplayType(event.target.value); + }; + + const snippetsDisplay = ( + + {/* Animation while app is fetching data from DB */} +
+ {loading && ( +
+ +
+ )} + +
+
+ ); + + const tagsDisplay = ( + + {/* Animation while app is fetching data from DB */} +
+ {loading && ( +
+ +
+ )} + +
+
+ ); + return ( {/*----- SIDE BAR -----*/} @@ -74,21 +142,28 @@ const Sidebar = ({ handleLogin }) => { {/* Changes the collapse state, which will render/unrender the sidebar*/}
- - - - + className={ + displayType === 'snippets' + ? styles.displayTypeButtonActive + : styles.displayTypeButtonInactive + } + onClick={toggleDisplayType} + > + Snippets + +
{/*----- ADD SNIPPET MODAL -----*/} - {openModal && } + {openModal && ( + + )} {/*----- SNIPPET DISPLAY -----*/} @@ -144,7 +204,7 @@ const Sidebar = ({ handleLogin }) => { {snippets && ( )} diff --git a/client/src/containers/Sidebar/Sidebar.module.scss b/client/src/containers/Sidebar/Sidebar.module.scss index a795c8f..f29bab6 100644 --- a/client/src/containers/Sidebar/Sidebar.module.scss +++ b/client/src/containers/Sidebar/Sidebar.module.scss @@ -9,25 +9,29 @@ // border-color: black; // } -.displayTypeSelectosr { +.displayTypeSelector { display: flex; justify-content: space-around; + text-align: center; gap: 10px; width: 100%; - input { - display: none; - appearance: none; - } - label { - } - .displayTypeButton { - .active { - color: whitesmoke; - border-bottom: 1pt double whitesmoke; - } - .inactive { - } - } +} + +.displayTypeButtonActive { + background-color: transparent; + box-shadow: none; + width: 100%; + color: whitesmoke !important; + border: none; + border-bottom: 1.5pt solid whitesmoke !important; +} + +.displayTypeButtonInactive { + background-color: transparent; + box-shadow: none; + width: 100%; + color: rgb(124, 124, 124) !important; + border: none !important; } .sidebar { @@ -119,7 +123,6 @@ .addButton { border: transparent; height: 50px; - width: 270px; // padding-left: 7px; // padding-right: 7px; border-radius: 10px; diff --git a/client/src/containers/Sidebar/TagsList/TagsList.jsx b/client/src/containers/Sidebar/TagsList/TagsList.jsx new file mode 100644 index 0000000..5522660 --- /dev/null +++ b/client/src/containers/Sidebar/TagsList/TagsList.jsx @@ -0,0 +1,41 @@ +import React, { useEffect } from 'react'; + +// importing utils +import PropTypes from 'prop-types'; + +// importing styles +import styles from './TagsList.module.scss'; + +function TagsList({ allTags, selectedTags, selectDeselectTag }) { + const buttonList = []; + + const onTagClick = (e) => { + selectDeselectTag(e.target.value); + }; + + if (allTags) { + allTags.forEach((el) => { + const currButton = ( + + ); + buttonList.push(currButton); + }); + } + + return
{buttonList}
; +} +TagsList.propTypes = { + allTags: PropTypes.array, + selectedTags: PropTypes.array, + selectDeselectTag: PropTypes.func +}; +export default TagsList; diff --git a/client/src/containers/Sidebar/TagsList/TagsList.module.scss b/client/src/containers/Sidebar/TagsList/TagsList.module.scss new file mode 100644 index 0000000..17a0433 --- /dev/null +++ b/client/src/containers/Sidebar/TagsList/TagsList.module.scss @@ -0,0 +1,16 @@ +.activeTag { + border: none; + background-color: whitesmoke; + color: black; + border-radius: 10px; +} + +.inactiveTag { + border: 1pt solid whitesmoke; + background-color: black; + color: whitesmoke; + border-radius: 10px; +} + +.tagsListDisplay { +} diff --git a/server/controllers/snippetsController.js b/server/controllers/snippetsController.js index 96bf898..8295cc4 100644 --- a/server/controllers/snippetsController.js +++ b/server/controllers/snippetsController.js @@ -12,6 +12,7 @@ const createError = (method, log, status, message = log) => { }; //Retrieves all snippets associated with a user by looking up user (by ID) and referencing all snippets in the associated list +//NOTE: WE SHOULD REALLY SEPARATE OUT STUFF LIKE THIS INTO A SEPARATE USER ROUTE AND USER CONTROLLER snippetsController.getSnippetsByUser = (req, res, next) => { const { userId } = req.query; //const userId = '645fee9104d1f0acef95a002'; @@ -21,6 +22,7 @@ snippetsController.getSnippetsByUser = (req, res, next) => { .exec() .then((user) => { res.locals.allSnippets = user.snippets; + res.locals.userTagsLangs = { tags: user.tags, languages: user.languages }; return next(); }) .catch((err) => { @@ -55,6 +57,7 @@ snippetsController.saveSnippetToUser = (req, res, next) => { .save() .then((r) => { res.locals.updatedUserRecord = r; + res.locals.changeFlag = true; return next(); }) .catch((err) => { diff --git a/server/routes/snippetsRouter.js b/server/routes/snippetsRouter.js index 02ff000..c6428f5 100644 --- a/server/routes/snippetsRouter.js +++ b/server/routes/snippetsRouter.js @@ -4,14 +4,22 @@ const snippetsController = require('../controllers/snippetsController'); const router = express.Router(); +//NOTE: I'm muddying things here by returning tags and languages alongside snippets +//In the future, this should be refactored as a route to explicitly load all user data +//in the context of a separate user route and user controller + router.get('/', snippetsController.getSnippetsByUser, (req, res) => - res.status(200).json(res.locals.allSnippets) + res.status(200).json({ + snippets: res.locals.allSnippets, + tagsLangs: res.locals.userTagsLangs + }) ); router.post( '/', snippetsController.createSnippet, snippetsController.saveSnippetToUser, + snippetsController.recalcTagsAndLang, (req, res) => res.status(200).json(res.locals.newSnippet) );