diff --git a/client/homebrew/navbar/vault.navitem.jsx b/client/homebrew/navbar/vault.navitem.jsx index 8752b8e0f..51c5c341d 100644 --- a/client/homebrew/navbar/vault.navitem.jsx +++ b/client/homebrew/navbar/vault.navitem.jsx @@ -2,11 +2,16 @@ const React = require('react'); const Nav = require('naturalcrit/nav/nav.jsx'); -module.exports = function(props){ - return + rel="noopener noreferrer" + > Vault + ); }; diff --git a/client/homebrew/pages/vaultPage/vaultPage.jsx b/client/homebrew/pages/vaultPage/vaultPage.jsx index 59e831aa4..377260d0f 100644 --- a/client/homebrew/pages/vaultPage/vaultPage.jsx +++ b/client/homebrew/pages/vaultPage/vaultPage.jsx @@ -16,458 +16,458 @@ const SplitPane = require('../../../../shared/naturalcrit/splitPane/splitPane.js const request = require('../../utils/request-middleware.js'); const VaultPage = (props) => { - const [title, setTitle] = useState(props.query.title || ''); - //state author - const [author, setAuthor] = useState(props.query.author || ''); - const [legacy, setLegacy] = useState(props.query.legacy !== 'false'); - const [v3, setV3] = useState(props.query.v3 !== 'false'); - const [count, setCount] = useState(props.query.count || 20); - const [page, setPage] = useState(parseInt(props.query.page) || 1); - const [brewCollection, setBrewCollection] = useState(null); - const [totalBrews, setTotalBrews] = useState(null); - const [searching, setSearching] = useState(false); - const [error, setError] = useState(null); + const [title, setTitle] = useState(props.query.title || ''); + //state author + const [author, setAuthor] = useState(props.query.author || ''); + const [legacy, setLegacy] = useState(props.query.legacy !== 'false'); + const [v3, setV3] = useState(props.query.v3 !== 'false'); + const [count, setCount] = useState(props.query.count || 20); + const [page, setPage] = useState(parseInt(props.query.page) || 1); + const [brewCollection, setBrewCollection] = useState(null); + const [totalBrews, setTotalBrews] = useState(null); + const [searching, setSearching] = useState(false); + const [error, setError] = useState(null); - const titleRef = useRef(null); - const authorRef = useRef(null); - const countRef = useRef(null); - const v3Ref = useRef(null); - const legacyRef = useRef(null); - const searchButtonRef = useRef(null); + const titleRef = useRef(null); + const authorRef = useRef(null); + const countRef = useRef(null); + const v3Ref = useRef(null); + const legacyRef = useRef(null); + const searchButtonRef = useRef(null); - useEffect(() => { - disableSubmitIfFormInvalid(); - loadPage(page, false, true); - }, []); + useEffect(() => { + disableSubmitIfFormInvalid(); + loadPage(page, false, true); + }, []); - const updateStateWithBrews = (brews, page) => { - setBrewCollection(brews || null); - setPage(parseInt(page) || 1); - setSearching(false); - }; + const updateStateWithBrews = (brews, page) => { + setBrewCollection(brews || null); + setPage(parseInt(page) || 1); + setSearching(false); + }; - const updateUrl = (title, author, count, v3, legacy, page) => { - const url = new URL(window.location.href); - const urlParams = new URLSearchParams(); + const updateUrl = (title, author, count, v3, legacy, page) => { + const url = new URL(window.location.href); + const urlParams = new URLSearchParams(); - Object.entries({ - title, - author, - count, - v3, - legacy, - page, - }).forEach(([key, value]) => urlParams.set(key, value)); + Object.entries({ + title, + author, + count, + v3, + legacy, + page, + }).forEach(([key, value]) => urlParams.set(key, value)); - url.search = urlParams.toString(); - window.history.replaceState(null, null, url); - }; + url.search = urlParams.toString(); + window.history.replaceState(null, null, url); + }; - const loadPage = async (page, update, total) => { - //Different searches use the update or total props to make only the necessary queries and functions + const loadPage = async (page, update, total) => { + //Different searches use the update or total props to make only the necessary queries and functions - if (!validateForm()) { - return; - } + if (!validateForm()) { + return; + } - setSearching(true); - setError(null); + setSearching(true); + setError(null); - const performSearch = async ({ title, author, count, v3, legacy }) => { - updateUrl(title, author, count, v3, legacy, page); - console.log(title, author, count, v3, legacy); - if ((title || author) && (v3 || legacy)) { - try { - const response = await request.get( - `/api/vault?title=${title}&author=${author}&v3=${v3}&legacy=${legacy}&count=${count}&page=${page}` - ); - if (response.ok) { - updateStateWithBrews(response.body.brews, page); - } else { - throw new Error(`Error: ${response.status}`); - } - } catch (error) { - console.log('error at loadPage: ', error); - setError( - `${ - error.response - ? error.response.status - : error.message - }` - ); - updateStateWithBrews([], 1); - } - } else { - setError('404'); - } - }; + const performSearch = async ({ title, author, count, v3, legacy }) => { + updateUrl(title, author, count, v3, legacy, page); + console.log(title, author, count, v3, legacy); + if ((title || author) && (v3 || legacy)) { + try { + const response = await request.get( + `/api/vault?title=${title}&author=${author}&v3=${v3}&legacy=${legacy}&count=${count}&page=${page}` + ); + if (response.ok) { + updateStateWithBrews(response.body.brews, page); + } else { + throw new Error(`Error: ${response.status}`); + } + } catch (error) { + console.log('error at loadPage: ', error); + setError( + `${ + error.response + ? error.response.status + : error.message + }` + ); + updateStateWithBrews([], 1); + } + } else { + setError('404'); + } + }; - const loadTotal = async ({ title, v3, legacy }) => { - setTotalBrews(null); - setError(null); - if ((title || author) && (v3 || legacy)) { - try { - const response = await request.get( - `/api/vault/total?title=${title}&author=${author}&v3=${v3}&legacy=${legacy}` - ); + const loadTotal = async ({ title, v3, legacy }) => { + setTotalBrews(null); + setError(null); + if ((title || author) && (v3 || legacy)) { + try { + const response = await request.get( + `/api/vault/total?title=${title}&author=${author}&v3=${v3}&legacy=${legacy}` + ); - if (response.ok) { - setTotalBrews(response.body.totalBrews); - } else { - throw new Error( - `Failed to load total brews: ${response.statusText}` - ); - } - } catch (error) { - console.log('error at loadTotal: ', error); - setError(`${error.response.status}`); - updateStateWithBrews([], 1); - } - } - }; + if (response.ok) { + setTotalBrews(response.body.totalBrews); + } else { + throw new Error( + `Failed to load total brews: ${response.statusText}` + ); + } + } catch (error) { + console.log('error at loadTotal: ', error); + setError(`${error.response.status}`); + updateStateWithBrews([], 1); + } + } + }; - const title = titleRef.current.value || ''; - const author = authorRef.current.value || ''; - const count = countRef.current.value || 10; - const v3 = v3Ref.current.checked != false; - const legacy = legacyRef.current.checked != false; + const title = titleRef.current.value || ''; + const author = authorRef.current.value || ''; + const count = countRef.current.value || 10; + const v3 = v3Ref.current.checked != false; + const legacy = legacyRef.current.checked != false; - console.log(title); - if (update) { - setTitle(title); - setAuthor(author); - setCount(count); - setV3(v3); - setLegacy(legacy); - } + console.log(title); + if (update) { + setTitle(title); + setAuthor(author); + setCount(count); + setV3(v3); + setLegacy(legacy); + } - // Perform search with the latest input values, because state is not fast enough - performSearch({ title, author, count, v3, legacy }); + // Perform search with the latest input values, because state is not fast enough + performSearch({ title, author, count, v3, legacy }); - if (total) { - loadTotal({ title, author, v3, legacy }); - } - }; + if (total) { + loadTotal({ title, author, v3, legacy }); + } + }; - const renderNavItems = () => ( - - - - Vault: Search for brews - - - - - - - - - - ); + const renderNavItems = () => ( + + + + Vault: Search for brews + + + + + + + + + + ); - const validateForm = () => { - //form validity: title or author must be written, and at least one renderer set - const { current: titleInput } = titleRef; - const { current: legacyCheckbox } = legacyRef; - const { current: v3Checkbox } = v3Ref; - const { current: authorInput } = authorRef; + const validateForm = () => { + //form validity: title or author must be written, and at least one renderer set + const { current: titleInput } = titleRef; + const { current: legacyCheckbox } = legacyRef; + const { current: v3Checkbox } = v3Ref; + const { current: authorInput } = authorRef; - const isTitleValid = titleInput.validity.valid && titleInput.value; - //because a pattern attr is set in the input, title must be over 2 chars long - const isAuthorValid = authorInput.validity.valid && authorInput.value; - const isCheckboxChecked = legacyCheckbox.checked || v3Checkbox.checked; + const isTitleValid = titleInput.validity.valid && titleInput.value; + //because a pattern attr is set in the input, title must be over 2 chars long + const isAuthorValid = authorInput.validity.valid && authorInput.value; + const isCheckboxChecked = legacyCheckbox.checked || v3Checkbox.checked; - const isFormValid = - (isTitleValid || isAuthorValid) && isCheckboxChecked; + const isFormValid = + (isTitleValid || isAuthorValid) && isCheckboxChecked; - return isFormValid; - }; + return isFormValid; + }; - const disableSubmitIfFormInvalid = () => { - const { current: submitButton } = searchButtonRef; - submitButton.disabled = !validateForm(); - }; + const disableSubmitIfFormInvalid = () => { + const { current: submitButton } = searchButtonRef; + submitButton.disabled = !validateForm(); + }; - const renderForm = () => ( -
-

Brew Lookup

-
- + const renderForm = () => ( +
+

Brew Lookup

+
+ - + - + - + - + - -
- -

Tips and tricks

-
    -
  • - You can only search brews with this tool if they are - published -
  • -
  • - Usernames are case sensitive, make sure you are writing - it correctly -
  • -
  • - You can use - to negate words, assuming - there is any word not negated, and "word" - to specify an exact string. -
  • + +
+ +

Tips and tricks

+
    +
  • + You can only search brews with this tool if they are + published +
  • +
  • + Usernames are case sensitive, make sure you are writing + it correctly +
  • +
  • + You can use - to negate words, assuming + there is any word not negated, and "word" + to specify an exact string. +
  • -
  • - Some words like a, after, through, itself, or here, are - ignored in searches, make sure your search has relevant - words. The full list can be found   - - here - -
  • -
-
- -
- ); +
  • + Some words like a, after, through, itself, or here, are + ignored in searches, make sure your search has relevant + words. The full list can be found   + + here + +
  • + + + +
    + ); - const renderPaginationControls = () => { - if (!totalBrews) return null; + const renderPaginationControls = () => { + if (!totalBrews) return null; - const countInt = parseInt(count); - const totalPages = Math.ceil(totalBrews / countInt); + const countInt = parseInt(count); + const totalPages = Math.ceil(totalBrews / countInt); - let startPage, endPage; - if (page <= 6) { - startPage = 1; - endPage = Math.min(totalPages, 10); - } else if (page + 4 >= totalPages) { - startPage = Math.max(1, totalPages - 9); - endPage = totalPages; - } else { - startPage = page - 5; - endPage = page + 4; - } + let startPage, endPage; + if (page <= 6) { + startPage = 1; + endPage = Math.min(totalPages, 10); + } else if (page + 4 >= totalPages) { + startPage = Math.max(1, totalPages - 9); + endPage = totalPages; + } else { + startPage = page - 5; + endPage = page + 4; + } - const pagesAroundCurrent = new Array(endPage - startPage + 1) - .fill() - .map((_, index) => ( - loadPage(startPage + index, false, false)} - > - {startPage + index} - - )); + const pagesAroundCurrent = new Array(endPage - startPage + 1) + .fill() + .map((_, index) => ( + loadPage(startPage + index, false, false)} + > + {startPage + index} + + )); - return ( -
    - -
      - {startPage > 1 && ( - loadPage(1, false, false)} - > - 1 ... - - )} - {pagesAroundCurrent} - {endPage < totalPages && ( - loadPage(totalPages, false, false)} - > - ... {totalPages} - - )} -
    - -
    - ); - }; + return ( +
    + +
      + {startPage > 1 && ( + loadPage(1, false, false)} + > + 1 ... + + )} + {pagesAroundCurrent} + {endPage < totalPages && ( + loadPage(totalPages, false, false)} + > + ... {totalPages} + + )} +
    + +
    + ); + }; - const renderFoundBrews = () => { - if (searching) { - return ( -
    -

    Searching

    -
    - ); - } + const renderFoundBrews = () => { + if (searching) { + return ( +
    +

    Searching

    +
    + ); + } - if (error) { - console.log('render Error: ', error); - let errorMessage; - switch (error.errorCode) { - case '404': - errorMessage = "404 - We didn't find any brew"; - break; - case '503': - errorMessage = - '503 - Service Unavailable, try again later, sorry.'; - break; - case '500': - errorMessage = - "500 - We don't know what happened, go ahead and contact the mods or report as a mistake."; - break; - default: - errorMessage = 'An unexpected error occurred'; - } + if (error) { + console.log('render Error: ', error); + let errorMessage; + switch (error.errorCode) { + case '404': + errorMessage = "404 - We didn't find any brew"; + break; + case '503': + errorMessage = + '503 - Service Unavailable, try again later, sorry.'; + break; + case '500': + errorMessage = + "500 - We don't know what happened, go ahead and contact the mods or report as a mistake."; + break; + default: + errorMessage = 'An unexpected error occurred'; + } - return ( -
    -

    Error: {errorMessage}

    -
    - ); - } + return ( +
    +

    Error: {errorMessage}

    +
    + ); + } - if (!brewCollection) { - return ( -
    -

    No search yet

    -
    - ); - } + if (!brewCollection) { + return ( +
    +

    No search yet

    +
    + ); + } - if (brewCollection.length === 0) { - return ( -
    -

    No brews found

    -
    - ); - } + if (brewCollection.length === 0) { + return ( +
    +

    No brews found

    +
    + ); + } - return ( -
    - - {`Brews found: `} - {totalBrews} - - {brewCollection.map((brew, index) => ( - - ))} - {renderPaginationControls()} -
    - ); - }; + return ( +
    + + {`Brews found: `} + {totalBrews} + + {brewCollection.map((brew, index) => ( + + ))} + {renderPaginationControls()} +
    + ); + }; - return ( -
    - - - {renderNavItems()} -
    - -
    {renderForm()}
    + return ( +
    + + + {renderNavItems()} +
    + +
    {renderForm()}
    -
    - {renderFoundBrews()} -
    -
    -
    -
    - ); +
    + {renderFoundBrews()} +
    +
    +
    +
    + ); }; module.exports = VaultPage; diff --git a/server/vault.api.js b/server/vault.api.js index 996ccf536..6a5b58140 100644 --- a/server/vault.api.js +++ b/server/vault.api.js @@ -5,121 +5,121 @@ const HomebrewModel = require('./homebrew.model.js').model; const router = express.Router(); const buildTitleConditions = (title) => { - if (!title) return {}; - return { - $text: { - $search: title, - $caseSensitive: false, - }, - }; + if (!title) return {}; + return { + $text: { + $search: title, + $caseSensitive: false, + }, + }; }; const buildAuthorConditions = (author) => { - if (!author) return {}; - return { authors: author }; + if (!author) return {}; + return { authors: author }; }; //"$and": [ {"published": true}, {"$text": { "$search": "titleString", "$caseSensitive": false } }, { "authors" : "authorString"}] //is a good example of a query constructed with this function const handleErrorResponse = (res, error, functionName) => { - const status = error.response?.status || 500; - const message = - status === 503 ? 'Service Unavailable' : 'Internal Server Error'; + const status = error.response?.status || 500; + const message = + status === 503 ? 'Service Unavailable' : 'Internal Server Error'; - console.error(`Error in ${functionName}:`, error); + console.error(`Error in ${functionName}:`, error); - return res.status(status).json({ - errorCode: status.toString(), - message: `Error in function ${functionName}: ${message}`, - }); + return res.status(status).json({ + errorCode: status.toString(), + message: `Error in function ${functionName}: ${message}`, + }); }; const buildBrewsQuery = (legacy, v3) => { - const brewsQuery = { published: true }; - if (legacy === 'true' && v3 === 'true') return { published: true }; + const brewsQuery = { published: true }; + if (legacy === 'true' && v3 === 'true') return { published: true }; - if (legacy === 'true' && v3 !== 'true') { - brewsQuery.renderer = 'legacy'; - } else if (v3 === 'true' && legacy !== 'true') { - brewsQuery.renderer = 'V3'; - } + if (legacy === 'true' && v3 !== 'true') { + brewsQuery.renderer = 'legacy'; + } else if (v3 === 'true' && legacy !== 'true') { + brewsQuery.renderer = 'V3'; + } - return brewsQuery; + return brewsQuery; }; const vault = { - findBrews: async (req, res) => { - try { - const title = req.query.title || ''; - const author = req.query.author || ''; - 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; + findBrews: async (req, res) => { + try { + const title = req.query.title || ''; + const author = req.query.author || ''; + 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 titleConditions = buildTitleConditions(title); - const authorConditions = buildAuthorConditions(author); + const brewsQuery = buildBrewsQuery(req.query.legacy, req.query.v3); + const titleConditions = buildTitleConditions(title); + const authorConditions = buildAuthorConditions(author); - const combinedQuery = { - $and: [brewsQuery, titleConditions, authorConditions], - }; + const combinedQuery = { + $and: [brewsQuery, titleConditions, authorConditions], + }; - const projection = { - editId: 0, - googleId: 0, - text: 0, - textBin: 0, - version: 0, - thumbnail: 0, - }; + const projection = { + editId: 0, + googleId: 0, + text: 0, + textBin: 0, + version: 0, + thumbnail: 0, + }; - const brews = await HomebrewModel.find(combinedQuery, projection) - .skip(skip) - .limit(count) - .maxTimeMS(5000) - .exec(); + const brews = await HomebrewModel.find(combinedQuery, projection) + .skip(skip) + .limit(count) + .maxTimeMS(5000) + .exec(); - console.log( - 'Query in findBrews: ', - JSON.stringify(combinedQuery, null, 2) - ); - return res.json({ brews, page }); - } catch (error) { - console.error(error); - return handleErrorResponse(res, error, 'findBrews'); - } - }, + console.log( + 'Query in findBrews: ', + JSON.stringify(combinedQuery, null, 2) + ); + return res.json({ brews, page }); + } catch (error) { + console.error(error); + return handleErrorResponse(res, error, 'findBrews'); + } + }, - findTotal: async (req, res) => { - try { - const title = req.query.title || ''; - const author = req.query.author || ''; + findTotal: async (req, res) => { + try { + const title = req.query.title || ''; + const author = req.query.author || ''; - const brewsQuery = buildBrewsQuery(req.query.legacy, req.query.v3); - const titleConditions = buildTitleConditions(title); - const authorConditions = buildAuthorConditions(author); + const brewsQuery = buildBrewsQuery(req.query.legacy, req.query.v3); + const titleConditions = buildTitleConditions(title); + const authorConditions = buildAuthorConditions(author); - const combinedQuery = { - $and: [brewsQuery, titleConditions, authorConditions], - }; + const combinedQuery = { + $and: [brewsQuery, titleConditions, authorConditions], + }; - const totalBrews = await HomebrewModel.countDocuments( - combinedQuery - ); - console.log( - 'when returning, the total of brews is ', - totalBrews, - 'for the query', - JSON.stringify(combinedQuery) - ); - return res.json({ totalBrews }); - } catch (error) { - console.error(error); - return handleErrorResponse(res, error, 'findTotal'); - } - }, + const totalBrews = await HomebrewModel.countDocuments( + combinedQuery + ); + console.log( + 'when returning, the total of brews is ', + totalBrews, + 'for the query', + JSON.stringify(combinedQuery) + ); + return res.json({ totalBrews }); + } catch (error) { + console.error(error); + return handleErrorResponse(res, error, 'findTotal'); + } + }, }; router.get('/api/vault/total', asyncHandler(vault.findTotal));