diff --git a/client/src/components/MobileNavbar.jsx b/client/src/components/MobileNavbar.jsx index 8e9be9a..01afb2f 100644 --- a/client/src/components/MobileNavbar.jsx +++ b/client/src/components/MobileNavbar.jsx @@ -40,6 +40,15 @@ export default function MobileNavbar({ logout, userData, open, setOpen, mode, se Dashboard + + + {userData?.level} + {userData?.points} + {userData?.badgeCount?.gold} + /{userData?.badgeCount?.silver} + /{userData?.badgeCount?.bronze} + + diff --git a/client/src/controllers/SearchBarController.js b/client/src/controllers/SearchBarController.js index 2202691..b01a111 100644 --- a/client/src/controllers/SearchBarController.js +++ b/client/src/controllers/SearchBarController.js @@ -15,12 +15,12 @@ export default function SearchBarController() { e.preventDefault(); if (location.pathname === '/questions/search') { - setSearchParams({ title: search, text: search, creator: search }, { replace: true }); + setSearchParams({ title: search }, { replace: true }); } else { navigate({ pathname: '/questions/search', search: `?${createSearchParams({ - title: search, text: search, creator: search + title: search, })}`, replace: true, }); diff --git a/client/src/controllers/SearchResultsController.jsx b/client/src/controllers/SearchResultsController.jsx index 196821f..85fe890 100644 --- a/client/src/controllers/SearchResultsController.jsx +++ b/client/src/controllers/SearchResultsController.jsx @@ -9,31 +9,26 @@ export default function SearchResultsController() { const [searchParams] = useSearchParams(); const [loading, setLoading] = useState(true); - const getData = async ({ question_id }) => { - let match = {}; - const regexMatch = {}; - const time = {}; - let newdate = searchParams.get('createdAt'); + const getData = async () => { + const creator = searchParams.get('creator'); + const title = searchParams.get('title'); + const text = searchParams.get('text'); + let tags = searchParams.get('tags'); + let time = searchParams.get('createdAt'); + let createdAt = {}; - if (newdate) { - newdate = new Date(newdate); - time['$gte'] = parseInt(newdate.getTime()); - newdate.setDate(newdate.getDate() + 1); - time['$lte'] = newdate.getTime(); - match = JSON.stringify({ createdAt: time }); + if (time) { + time = new Date(time); + createdAt['$gte'] = parseInt(time.getTime()); + time.setDate(time.getDate() + 1); + createdAt['$lte'] = time.getTime(); + } + if (tags) { + tags = tags.split(' '); } - const creator = searchParams.get('creator'); - if (creator) regexMatch.creator = creator; - - const text = searchParams.get('text'); - if (text) regexMatch.text = text.replaceAll(' ', '|'); - - const title = searchParams.get('title'); - if (title) regexMatch.title = title.replaceAll(' ', '|'); - setLoading(() => true); - const { questions } = await searchQuestions({ after: question_id, regexMatch, match }); + const { questions } = await searchQuestions({ creator, title, text, tags, createdAt }); setLoading(() => false); return questions; diff --git a/client/src/services/fields.js b/client/src/services/fields.js index f5db0c2..e99386f 100644 --- a/client/src/services/fields.js +++ b/client/src/services/fields.js @@ -22,6 +22,7 @@ const askQuestionFields = [ label: 'Text', multiline: true, }, + { id: 'tags', label: 'Tags' }, ]; const commentFields = [ @@ -127,6 +128,7 @@ const searchFields = [ id: 'creator', label: 'Creator', }, + { id: 'tags', label: 'Tags (separated by spaces)' }, ]; const generateEditFields = diff --git a/client/src/services/questionsServices.js b/client/src/services/questionsServices.js index 938ea83..bc3622a 100644 --- a/client/src/services/questionsServices.js +++ b/client/src/services/questionsServices.js @@ -2,7 +2,10 @@ import { createEndpoint } from './api'; const callQuestionsAPI = createEndpoint('/questions'); -const searchQuestions = async (sort) => callQuestionsAPI('get', `/search`, sort); +const searchQuestions = async (sort) => { + console.log(sort); + return callQuestionsAPI('get', `/search`, sort); +}; const postQuestion = async (data) => callQuestionsAPI('post', ``, data); diff --git a/client/src/services/schemas.js b/client/src/services/schemas.js index 0e81f86..d2be36a 100644 --- a/client/src/services/schemas.js +++ b/client/src/services/schemas.js @@ -21,6 +21,10 @@ const searchSchema = Yup.object({ text: Yup.string().max(300, 'Text must be at most 300 characters, try and use keywords only '), // createdAt: Yup.string().max(11, 'Date must be at most 11 characters, make sure body is formatted correctly'), creator: Yup.string().max(16, 'username must be at most 16 characters'), + tags: Yup.string().matches( + /^(?:\b\w+\b[\s\r\n]*){0,5}$/, + 'Tags should be separated by spaces and are limited to 5' + ), }); const loginSchema = Yup.object({ @@ -71,17 +75,12 @@ const questionSchema = Yup.object({ text: Yup.string() .max(3000, 'Body cannot be longer than 3000 characters') .required('A body is required'), + tags: Yup.string().matches( + /^(?:\b\w+\b[\s\r\n]*){0,5}$/, + 'Tags should be separated by spaces and are limited to 5' + ), }); -const editSchema = Yup.object({ - etitle: Yup.string() - .max(150, 'Title cannot be longer then 150 characters.') - .required('A title is required.'), - etext: Yup.string() - .max(3000, 'Body cannot be longer than 3000 characters') - .required('A body is required'), -}) - const patchSchema = Yup.object().shape( { email: Yup.string() diff --git a/server/db/models/User.js b/server/db/models/User.js index a6b75e1..3eb6861 100644 --- a/server/db/models/User.js +++ b/server/db/models/User.js @@ -18,7 +18,6 @@ const User = mongoose.Schema( User.post('findOneAndUpdate', async (doc) => { const badges = calculateUserBadges(doc.points); doc.badges = [...new Set([...doc.badges, ...badges])]; - console.log(doc.badges); await doc.save(); }); diff --git a/server/routes/question/CreateQuestion.js b/server/routes/question/CreateQuestion.js index 0aff3e2..decd845 100644 --- a/server/routes/question/CreateQuestion.js +++ b/server/routes/question/CreateQuestion.js @@ -9,7 +9,7 @@ async function CreateQuestion(req, res) { if (!title || !text) return res.status(400).send(config.errorIncomplete); - if (tags && tags.length > 5) { + if (tags && tags.split(' ').length > 5) { return res.status(400).send(config.errorIncomplete); } @@ -21,7 +21,11 @@ async function CreateQuestion(req, res) { if (!success) return res.status(500).send(config.errorGeneric); - const newQuestion = await Question.create({ ...question, tags, _id: question.question_id }); + const newQuestion = await Question.create({ + ...question, + tags: tags.split(' '), + _id: question.question_id, + }); // Increment user points by 1 await createRequest('patch', `/users/${user.username}/points`, { diff --git a/server/routes/question/Search.js b/server/routes/question/Search.js index eb23464..058f3df 100644 --- a/server/routes/question/Search.js +++ b/server/routes/question/Search.js @@ -1,32 +1,36 @@ -const createRequest = require('server/utils/api'); const config = require('server/config.json'); const Question = require('server/db/models/Question'); async function Search(req, res) { - const { success, questions } = await createRequest( - 'get', - `/questions/search`, - req.query - ); + const { creator, title, text, tags, createdAt } = req.query; - if (!success) return res.status(500).send(config.errorGeneric); + let searchQuery = {}; + if (creator) { + searchQuery['creator'] = { + $regex: creator, + }; + } + if (title) { + searchQuery['title'] = { + $regex: title, + }; + } + if (text) { + searchQuery['text'] = { + $regex: text, + }; + } + if (tags) { + searchQuery['tags'] = { $all: tags }; + } + if (createdAt) { + searchQuery['createdAt'] = createdAt; + } - // Patch search results to database - const questionSet = await Promise.all( - questions - .map((question) => ({ - id: question.question_id, - ...question, - })) - .map(async (question) => { - return Question.findByIdAndUpdate(question.id, question, { - upsert: true, - }); - }) - ); + const questions = await Question.find(searchQuery).sort({ createdAt: 'asc' }); - return res.send({ questions: questionSet.filter(question => question) }); + return res.send({ questions }); } module.exports = Search;