diff --git a/client/homebrew/pages/vaultPage/vaultPage.jsx b/client/homebrew/pages/vaultPage/vaultPage.jsx index a5c9a5937..0ccd1144b 100644 --- a/client/homebrew/pages/vaultPage/vaultPage.jsx +++ b/client/homebrew/pages/vaultPage/vaultPage.jsx @@ -18,6 +18,9 @@ const request = require('../../utils/request-middleware.js'); const VaultPage = (props) => { const [title, setTitle] = useState(props.query.title || ''); + //state author and owner + const [author, setAuthor] = useState(props.query.author || ''); + const [owner, setOwner] = useState(props.query.owner !== 'false'); const [legacy, setLegacy] = useState(props.query.legacy !== 'false'); const [v3, setV3] = useState(props.query.v3 !== 'false'); const [count, setCount] = useState(props.query.count || 20); @@ -28,6 +31,8 @@ const VaultPage = (props) => { const [error, setError] = useState(null); const titleRef = useRef(null); + const authorRef = useRef(null); + const ownerRef = useRef(null); const countRef = useRef(null); const v3Ref = useRef(null); const legacyRef = useRef(null); @@ -46,13 +51,19 @@ const VaultPage = (props) => { setSearching(false); }; - const updateUrl = (title, page, count, v3, legacy) => { + const updateUrl = (title, author, owner, count, v3, legacy, page) => { const url = new URL(window.location.href); const urlParams = new URLSearchParams(); - Object.entries({ title, v3, legacy, count, page }).forEach( - ([key, value]) => urlParams.set(key, value) - ); + Object.entries({ + title, + author, + owner, + count, + v3, + legacy, + page, + }).forEach(([key, value]) => urlParams.set(key, value)); url.search = urlParams.toString(); window.history.replaceState(null, null, url); @@ -62,14 +73,22 @@ const VaultPage = (props) => { setSearching(true); setError(null); - const performSearch = async ({ title, count, v3, legacy }) => { - updateUrl(title, page, count, v3, legacy); - if (title && (v3 || legacy)) { + const performSearch = async ({ + title, + author, + owner, + count, + v3, + legacy, + }) => { + updateUrl(title, author, owner, count, v3, legacy, page); + if ((title || author) && (v3 || legacy)) { try { const response = await request.get( - `/api/vault?title=${title}&v3=${v3}&legacy=${legacy}&count=${count}&page=${page}` + `/api/vault?title=${title}&author=${author}&owner=${owner}&v3=${v3}&legacy=${legacy}&count=${count}&page=${page}` ); if (response.ok) { + console.log(response.body.brews); updateStateWithBrews(response.body.brews, page); } else { throw new Error(`Error: ${response.status}`); @@ -93,10 +112,10 @@ const VaultPage = (props) => { const loadTotal = async ({ title, v3, legacy }) => { setTotalBrews(null); setError(null); - if (title) { + if ((title || author) && (v3 || legacy)) { try { const response = await request.get( - `/api/vault/total?title=${title}&v3=${v3}&legacy=${legacy}` + `/api/vault/total?title=${title}&author=${author}&owner=${owner}&v3=${v3}&legacy=${legacy}` ); if (response.ok) { @@ -115,23 +134,26 @@ const VaultPage = (props) => { }; const title = titleRef.current.value || ''; + const author = authorRef.current.value || ''; + const owner = ownerRef.current.checked != false; const count = countRef.current.value || 10; const v3 = v3Ref.current.checked != false; const legacy = legacyRef.current.checked != false; + console.log(author); if (update) { setTitle(title); setCount(count); setV3(v3); setLegacy(legacy); - performSearch({ title, count, v3, legacy }); + performSearch({ title, author, owner, count, v3, legacy }); } else { - performSearch({ title, count, v3, legacy }); + performSearch({ title, author, owner, count, v3, legacy }); } if (total) { - loadTotal({ title, v3, legacy }); + loadTotal({ title, author, owner, v3, legacy }); } }; @@ -152,15 +174,19 @@ const VaultPage = (props) => { ); const validateForm = () => { - const submitButton = searchButtonRef.current; - const textInput = titleRef.current; - const legacyCheckbox = legacyRef.current; - const v3Checkbox = v3Ref.current; + //form validity: title or author must be written, and at least one renderer set + const { current: submitButton } = searchButtonRef; + const { current: titleInput } = titleRef; + const { current: legacyCheckbox } = legacyRef; + const { current: v3Checkbox } = v3Ref; + const { current: authorInput } = authorRef; - const isTextValid = textInput.validity.valid && textInput.value; + const isTitleValid = titleInput.validity.valid && titleInput.value; + const isAuthorValid = authorInput.validity.valid && authorInput.value; const isCheckboxChecked = legacyCheckbox.checked || v3Checkbox.checked; - submitButton.disabled = !(isTextValid && isCheckboxChecked); + submitButton.disabled = + !(isTitleValid || isAuthorValid) || !isCheckboxChecked; }; const renderForm = () => ( @@ -176,6 +202,7 @@ const VaultPage = (props) => { defaultValue={title} onKeyUp={validateForm} pattern=".{3,}" + title="At least 3 characters" onKeyDown={(e) => { if (e.key === 'Enter') { if (!searchButtonRef.current.disabled) { @@ -190,6 +217,35 @@ const VaultPage = (props) => { Tip! you can use - to negate words, and{' '} "word" to specify an exact string. + + Author of the brew + { + if (e.key === 'Enter') { + if (!searchButtonRef.current.disabled) { + loadPage(1, true, true); + } + } + }} + placeholder="Gazook89" + /> + + + + Author is the owner + Results per page @@ -202,7 +258,6 @@ const VaultPage = (props) => { { { ); } - if (title === '') { + if (title === '' && author === '') { return ( No search yet diff --git a/client/homebrew/pages/vaultPage/vaultPage.less b/client/homebrew/pages/vaultPage/vaultPage.less index b9179c7d5..66f63bded 100644 --- a/client/homebrew/pages/vaultPage/vaultPage.less +++ b/client/homebrew/pages/vaultPage/vaultPage.less @@ -64,10 +64,14 @@ body { select { margin : 0 10px; } input { - margin : 0 10px; + margin : 0 10px; + + &:invalid { + background : rgb(255, 188, 181); + } - &.renderer { - position : relative; + &[type='checkbox'] { + position : relative; display : inline-block; width : 50px; height : 30px; @@ -84,9 +88,9 @@ body { &::before,&::after { position : absolute; inset : 0; + z-index : 5; padding-top : 2px; text-align : center; - z-index : 5; } &::before { diff --git a/server/vault.api.js b/server/vault.api.js index 0783446fd..369e73ff1 100644 --- a/server/vault.api.js +++ b/server/vault.api.js @@ -1,33 +1,30 @@ -const HomebrewModel = require('./homebrew.model.js').model; -const router = require('express').Router(); +const express = require('express'); const asyncHandler = require('express-async-handler'); +const HomebrewModel = require('./homebrew.model.js').model; -const buildTitleConditions = (inputString) => { - return [ - { - $text: { - $search: inputString, - $caseSensitive: false, - }, +const router = express.Router(); + +const buildTitleConditions = (title) => { + if (!title) return {}; + return { + $text: { + $search: title, + $caseSensitive: false, }, - ]; + }; +}; + +const buildAuthorConditions = (author, owner) => { + if (!author) return {}; + return owner ? { 'authors.0': author } : { authors: author }; }; const handleErrorResponse = (res, error, functionName) => { - let status; - let message; + const status = error.response?.status || 500; + const message = + status === 503 ? 'Service Unavailable' : 'Internal Server Error'; - if (error.response && error.response.status) { - status = error.response.status; - } else { - status = 500; - } - - if (status === 503) { - message = 'Service Unavailable'; - } else { - message = 'Internal Server Error'; - } + console.error(`Error in ${functionName}:`, error); return res.status(status).json({ errorCode: status.toString(), @@ -37,43 +34,35 @@ const handleErrorResponse = (res, error, functionName) => { const buildBrewsQuery = (legacy, v3) => { const renderers = []; + if (legacy === 'true') renderers.push('legacy'); + if (v3 === 'true') renderers.push('V3'); - if (legacy === 'true') { - renderers.push('legacy'); - } - - if (v3 === 'true') { - renderers.push('V3'); - } - - const brewsQuery = { - published: true, - }; - - if (renderers.length > 0) { - brewsQuery.renderer = { $in: renderers }; - } + const brewsQuery = { published: true }; + if (renderers.length > 0) brewsQuery.renderer = { $in: renderers }; return brewsQuery; }; const vault = { - findBrews: async (req, res, next) => { + findBrews: async (req, res) => { try { console.log(`Query as received in vault api for findBrews:`); console.table(req.query); const title = req.query.title || ''; + const author = req.query.author || ''; + const owner = req.query.owner === 'true'; const page = Math.max(parseInt(req.query.page) || 1, 1); const mincount = 10; const count = Math.max(parseInt(req.query.count) || 20, mincount); const skip = (page - 1) * count; const brewsQuery = buildBrewsQuery(req.query.legacy, req.query.v3); - const titleConditionsArray = buildTitleConditions(title); + const titleConditions = buildTitleConditions(title); + const authorConditions = buildAuthorConditions(author, owner); - const titleQuery = { - $and: [brewsQuery, ...titleConditionsArray], + const combinedQuery = { + $and: [brewsQuery, titleConditions, authorConditions] }; const projection = { @@ -82,35 +71,51 @@ const vault = { text: 0, textBin: 0, }; - const brews = await HomebrewModel.find(titleQuery, projection) + const brews = await HomebrewModel.find(combinedQuery, projection) .skip(skip) .limit(count) .maxTimeMS(5000) .exec(); + console.log('query', JSON.stringify(combinedQuery, null, 2)); return res.json({ brews, page }); - } catch (error) { console.error(error); return handleErrorResponse(res, error, 'findBrews'); } }, + findTotal: async (req, res) => { console.log(`Query as received in vault api for totalBrews:`); console.table(req.query); try { const title = req.query.title || ''; + const author = req.query.author || ''; + const owner = req.query.owner === 'true'; const brewsQuery = buildBrewsQuery(req.query.legacy, req.query.v3); - const titleConditionsArray = buildTitleConditions(title); + const titleConditions = buildTitleConditions(title); + const authorConditions = buildAuthorConditions(author, owner); - const titleQuery = { - $and: [brewsQuery, ...titleConditionsArray], + const combinedQuery = { + $and: [brewsQuery, titleConditions, authorConditions] }; - const totalBrews = await HomebrewModel.countDocuments(titleQuery); - console.log('when returning, totalbrews is ', totalBrews, 'for the query ',JSON.stringify(titleQuery)); - return res.json({ totalBrews }); + console.log( + 'Combined Query:', + JSON.stringify(combinedQuery, null, 2) + ); + + const totalBrews = await HomebrewModel.countDocuments( + combinedQuery + ); + console.log( + 'when returning, totalbrews is ', + totalBrews, + 'for the query', + JSON.stringify(combinedQuery) + ); + return res.json({ totalBrews }); } catch (error) { console.error(error); return handleErrorResponse(res, error, 'findTotal');
-
"word"