0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-01-05 12:22:44 +00:00

Merge branch 'master' into Functional-Tag-Editor

This commit is contained in:
Gazook89
2024-09-23 14:53:41 -05:00
5 changed files with 150 additions and 69 deletions

View File

@@ -1,3 +1,5 @@
/*eslint max-lines: ["warn", {"max": 400, "skipBlankLines": true, "skipComments": true}]*/
/*eslint max-params:["warn", { max: 10 }], */
require('./vaultPage.less'); require('./vaultPage.less');
const React = require('react'); const React = require('react');
@@ -18,13 +20,15 @@ const request = require('../../utils/request-middleware.js');
const VaultPage = (props)=>{ const VaultPage = (props)=>{
const [pageState, setPageState] = useState(parseInt(props.query.page) || 1); const [pageState, setPageState] = useState(parseInt(props.query.page) || 1);
const [sortState, setSort] = useState(props.query.sort || 'title');
const [dirState, setdir] = useState(props.query.dir || 'asc');
//Response state //Response state
const [brewCollection, setBrewCollection] = useState(null); const [brewCollection, setBrewCollection] = useState(null);
const [totalBrews, setTotalBrews] = useState(null); const [totalBrews, setTotalBrews] = useState(null);
const [searching, setSearching] = useState(false); const [searching, setSearching] = useState(false);
const [error, setError] = useState(null); const [error, setError] = useState(null);
const titleRef = useRef(null); const titleRef = useRef(null);
const authorRef = useRef(null); const authorRef = useRef(null);
const countRef = useRef(null); const countRef = useRef(null);
@@ -34,7 +38,7 @@ const VaultPage = (props)=>{
useEffect(()=>{ useEffect(()=>{
disableSubmitIfFormInvalid(); disableSubmitIfFormInvalid();
loadPage(pageState, true); loadPage(pageState, true, props.query.sort, props.query.dir);
}, []); }, []);
const updateStateWithBrews = (brews, page)=>{ const updateStateWithBrews = (brews, page)=>{
@@ -43,7 +47,7 @@ const VaultPage = (props)=>{
setSearching(false); setSearching(false);
}; };
const updateUrl = (titleValue, authorValue, countValue, v3Value, legacyValue, page)=>{ const updateUrl = (titleValue, authorValue, countValue, v3Value, legacyValue, page, sort, dir)=>{
const url = new URL(window.location.href); const url = new URL(window.location.href);
const urlParams = new URLSearchParams(url.search); const urlParams = new URLSearchParams(url.search);
@@ -53,21 +57,23 @@ const VaultPage = (props)=>{
urlParams.set('v3', v3Value); urlParams.set('v3', v3Value);
urlParams.set('legacy', legacyValue); urlParams.set('legacy', legacyValue);
urlParams.set('page', page); urlParams.set('page', page);
urlParams.set('sort', sort);
urlParams.set('dir', dir);
url.search = urlParams.toString(); url.search = urlParams.toString();
window.history.replaceState(null, '', url.toString()); window.history.replaceState(null, '', url.toString());
}; };
const performSearch = async (title, author, count, v3, legacy, page)=>{ const performSearch = async (title, author, count, v3, legacy, page, sort, dir)=>{
updateUrl(title, author, count, v3, legacy, page); updateUrl(title, author, count, v3, legacy, page, sort, dir);
const response = await request.get( const response = await request
`/api/vault?title=${title}&author=${author}&v3=${v3}&legacy=${legacy}&count=${count}&page=${page}` .get(`/api/vault?title=${title}&author=${author}&v3=${v3}&legacy=${legacy}&count=${count}&page=${page}&sort=${sort}&dir=${dir}`)
).catch((error)=>{ .catch((error)=>{
console.log('error at loadPage: ', error); console.log('error at loadPage: ', error);
setError(error); setError(error);
updateStateWithBrews([], 1); updateStateWithBrews([], 1);
}); });
if(response.ok) if(response.ok)
updateStateWithBrews(response.body.brews, page); updateStateWithBrews(response.body.brews, page);
@@ -76,9 +82,8 @@ const VaultPage = (props)=>{
const loadTotal = async (title, author, v3, legacy)=>{ const loadTotal = async (title, author, v3, legacy)=>{
setTotalBrews(null); setTotalBrews(null);
const response = await request.get( const response = await request.get(`/api/vault/total?title=${title}&author=${author}&v3=${v3}&legacy=${legacy}`)
`/api/vault/total?title=${title}&author=${author}&v3=${v3}&legacy=${legacy}` .catch((error)=>{
).catch((error)=>{
console.log('error at loadTotal: ', error); console.log('error at loadTotal: ', error);
setError(error); setError(error);
updateStateWithBrews([], 1); updateStateWithBrews([], 1);
@@ -88,9 +93,8 @@ const VaultPage = (props)=>{
setTotalBrews(response.body.totalBrews); setTotalBrews(response.body.totalBrews);
}; };
const loadPage = async (page, updateTotal)=>{ const loadPage = async (page, updateTotal, sort, dir)=>{
if(!validateForm()) if(!validateForm()) return;
return;
setSearching(true); setSearching(true);
setError(null); setError(null);
@@ -100,8 +104,14 @@ const VaultPage = (props)=>{
const count = countRef.current.value || 10; const count = countRef.current.value || 10;
const v3 = v3Ref.current.checked != false; const v3 = v3Ref.current.checked != false;
const legacy = legacyRef.current.checked != false; const legacy = legacyRef.current.checked != false;
const sortOption = sort || 'title';
const dirOption = dir || 'asc';
const pageProp = page || 1;
performSearch(title, author, count, v3, legacy, page); setSort(sortOption);
setdir(dirOption);
performSearch(title, author, count, v3, legacy, pageProp, sortOption, dirOption);
if(updateTotal) if(updateTotal)
loadTotal(title, author, v3, legacy); loadTotal(title, author, v3, legacy);
@@ -248,6 +258,33 @@ const VaultPage = (props)=>{
</div> </div>
); );
const renderSortOption = (optionTitle, optionValue)=>{
const oppositeDir = dirState === 'asc' ? 'desc' : 'asc';
return (
<div className={`sort-option ${sortState === optionValue ? `active` : ''}`}>
<button onClick={()=>loadPage(1, false, optionValue, oppositeDir)}>
{optionTitle}
</button>
{sortState === optionValue && (
<i className={`sortDir fas ${dirState === 'asc' ? 'fa-sort-up' : 'fa-sort-down'}`} />
)}
</div>
);
};
const renderSortBar = ()=>{
return (
<div className='sort-container'>
{renderSortOption('Title', 'title', props.query.dir)}
{renderSortOption('Created Date', 'createdAt', props.query.dir)}
{renderSortOption('Updated Date', 'updatedAt', props.query.dir)}
{renderSortOption('Views', 'views', props.query.dir)}
</div>
);
};
const renderPaginationControls = ()=>{ const renderPaginationControls = ()=>{
if(!totalBrews) return null; if(!totalBrews) return null;
@@ -271,10 +308,8 @@ const VaultPage = (props)=>{
.map((_, index)=>( .map((_, index)=>(
<a <a
key={startPage + index} key={startPage + index}
className={`pageNumber ${ className={`pageNumber ${pageState === startPage + index ? 'currentPage' : ''}`}
pageState === startPage + index ? 'currentPage' : '' onClick={()=>loadPage(startPage + index, false, sortState, dirState)}
}`}
onClick={()=>loadPage(startPage + index, false)}
> >
{startPage + index} {startPage + index}
</a> </a>
@@ -284,7 +319,7 @@ const VaultPage = (props)=>{
<div className='paginationControls'> <div className='paginationControls'>
<button <button
className='previousPage' className='previousPage'
onClick={()=>loadPage(pageState - 1, false)} onClick={()=>loadPage(pageState - 1, false, sortState, dirState)}
disabled={pageState === startPage} disabled={pageState === startPage}
> >
<i className='fa-solid fa-chevron-left'></i> <i className='fa-solid fa-chevron-left'></i>
@@ -293,7 +328,7 @@ const VaultPage = (props)=>{
{startPage > 1 && ( {startPage > 1 && (
<a <a
className='pageNumber firstPage' className='pageNumber firstPage'
onClick={()=>loadPage(1, false)} onClick={()=>loadPage(1, false, sortState, dirState)}
> >
1 ... 1 ...
</a> </a>
@@ -302,7 +337,7 @@ const VaultPage = (props)=>{
{endPage < totalPages && ( {endPage < totalPages && (
<a <a
className='pageNumber lastPage' className='pageNumber lastPage'
onClick={()=>loadPage(totalPages, false)} onClick={()=>loadPage(totalPages, false, sortState, dirState)}
> >
... {totalPages} ... {totalPages}
</a> </a>
@@ -310,7 +345,7 @@ const VaultPage = (props)=>{
</ol> </ol>
<button <button
className='nextPage' className='nextPage'
onClick={()=>loadPage(pageState + 1, false)} onClick={()=>loadPage(pageState + 1, false, sortState, dirState)}
disabled={pageState === totalPages} disabled={pageState === totalPages}
> >
<i className='fa-solid fa-chevron-right'></i> <i className='fa-solid fa-chevron-right'></i>
@@ -385,6 +420,7 @@ const VaultPage = (props)=>{
<div className='form dataGroup'>{renderForm()}</div> <div className='form dataGroup'>{renderForm()}</div>
<div className='resultsContainer dataGroup'> <div className='resultsContainer dataGroup'>
{renderSortBar()}
{renderFoundBrews()} {renderFoundBrews()}
</div> </div>
</SplitPane> </SplitPane>

View File

@@ -6,8 +6,8 @@
*:not(input) { user-select : none; } *:not(input) { user-select : none; }
.content { .content {
height : 100%;
background : #2C3E50; background : #2C3E50;
height: 100%;
.dataGroup { .dataGroup {
width : 100%; width : 100%;
@@ -27,9 +27,9 @@
code { code {
padding-inline : 5px; padding-inline : 5px;
font-family : monospace;
background : lightgrey; background : lightgrey;
border-radius : 5px; border-radius : 5px;
font-family : monospace;
} }
h1, h2, h3, h4 { h1, h2, h3, h4 {
@@ -165,6 +165,48 @@
color : white; color : white;
} }
.sort-container {
display : flex;
flex-wrap : wrap;
column-gap : 15px;
justify-content : center;
height : 30px;
color : white;
background-color : #555555;
border-top : 1px solid #666666;
border-bottom : 1px solid #666666;
.sort-option {
display : flex;
align-items : center;
padding : 0 8px;
&:hover { background-color : #444444; }
&.active {
background-color : #333333;
button {
font-weight : 800;
color : white;
& + .sortDir { padding-left : 5px; }
}
}
button {
padding : 0;
font-size : 11px;
font-weight : normal;
color : #CCCCCC;
text-transform : uppercase;
background-color : transparent;
&:hover { background : none; }
}
}
}
.foundBrews { .foundBrews {
position : relative; position : relative;
width : 100%; width : 100%;
@@ -236,15 +278,15 @@
width : 47%; width : 47%;
margin-right : 40px; margin-right : 40px;
color : black; color : black;
isolation:isolate; isolation : isolate;
&:after { &::after {
position:absolute; position : absolute;
inset:0; inset : 0;
display:block; z-index : -2;
content:''; display : block;
content : '';
background-image : url('/assets/parchmentBackground.jpg'); background-image : url('/assets/parchmentBackground.jpg');
z-index:-1;
} }
&:nth-child(even of .brewItem) { margin-right : 0; } &:nth-child(even of .brewItem) { margin-right : 0; }
@@ -257,28 +299,24 @@
color : var(--HB_Color_HeaderText); color : var(--HB_Color_HeaderText);
} }
.info { .info {
position : relative;
z-index : 2;
font-family : 'ScalySansRemake'; font-family : 'ScalySansRemake';
font-size : 1.2em; font-size : 1.2em;
position:relative;
z-index:2;
>span { >span {
margin-right : 12px; margin-right : 12px;
line-height : 1.5em; line-height : 1.5em;
} }
} }
.links { .links { z-index : 2; }
z-index:2;
}
hr { hr {
margin: 0px; margin : 0px;
visibility: hidden; visibility : hidden;
} }
.thumbnail { .thumbnail { z-index : -1; }
z-index:1;
}
} }
.paginationControls { .paginationControls {

40
package-lock.json generated
View File

@@ -38,7 +38,7 @@
"marked-smartypants-lite": "^1.0.2", "marked-smartypants-lite": "^1.0.2",
"markedLegacy": "npm:marked@^0.3.19", "markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.30.1", "moment": "^2.30.1",
"mongoose": "^8.6.2", "mongoose": "^8.6.3",
"nanoid": "3.3.4", "nanoid": "3.3.4",
"nconf": "^0.12.1", "nconf": "^0.12.1",
"react": "^18.3.1", "react": "^18.3.1",
@@ -51,7 +51,7 @@
}, },
"devDependencies": { "devDependencies": {
"@stylistic/stylelint-plugin": "^3.0.1", "@stylistic/stylelint-plugin": "^3.0.1",
"eslint": "^9.10.0", "eslint": "^9.11.0",
"eslint-plugin-jest": "^28.8.3", "eslint-plugin-jest": "^28.8.3",
"eslint-plugin-react": "^7.36.1", "eslint-plugin-react": "^7.36.1",
"globals": "^15.9.0", "globals": "^15.9.0",
@@ -59,7 +59,7 @@
"jest-expect-message": "^1.1.3", "jest-expect-message": "^1.1.3",
"postcss-less": "^6.0.0", "postcss-less": "^6.0.0",
"stylelint": "^16.9.0", "stylelint": "^16.9.0",
"stylelint-config-recess-order": "^5.1.0", "stylelint-config-recess-order": "^5.1.1",
"stylelint-config-recommended": "^14.0.1", "stylelint-config-recommended": "^14.0.1",
"supertest": "^7.0.0" "supertest": "^7.0.0"
}, },
@@ -2073,9 +2073,9 @@
} }
}, },
"node_modules/@eslint/js": { "node_modules/@eslint/js": {
"version": "9.10.0", "version": "9.11.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.10.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.11.0.tgz",
"integrity": "sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==", "integrity": "sha512-LPkkenkDqyzTFauZLLAPhIb48fj6drrfMvRGSL9tS3AcZBSVTllemLSNyCvHNNL2t797S/6DJNSIwRwXgMO/eQ==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -2091,9 +2091,9 @@
} }
}, },
"node_modules/@eslint/plugin-kit": { "node_modules/@eslint/plugin-kit": {
"version": "0.1.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.1.0.tgz", "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz",
"integrity": "sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==", "integrity": "sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"levn": "^0.4.1" "levn": "^0.4.1"
@@ -5829,17 +5829,17 @@
} }
}, },
"node_modules/eslint": { "node_modules/eslint": {
"version": "9.10.0", "version": "9.11.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.10.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.11.0.tgz",
"integrity": "sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==", "integrity": "sha512-yVS6XODx+tMFMDFcG4+Hlh+qG7RM6cCJXtQhCKLSsr3XkLvWggHjCqjfh0XsPPnt1c56oaT6PMgW9XWQQjdHXA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.11.0", "@eslint-community/regexpp": "^4.11.0",
"@eslint/config-array": "^0.18.0", "@eslint/config-array": "^0.18.0",
"@eslint/eslintrc": "^3.1.0", "@eslint/eslintrc": "^3.1.0",
"@eslint/js": "9.10.0", "@eslint/js": "9.11.0",
"@eslint/plugin-kit": "^0.1.0", "@eslint/plugin-kit": "^0.2.0",
"@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.3.0", "@humanwhocodes/retry": "^0.3.0",
"@nodelib/fs.walk": "^1.2.8", "@nodelib/fs.walk": "^1.2.8",
@@ -10801,9 +10801,9 @@
} }
}, },
"node_modules/mongoose": { "node_modules/mongoose": {
"version": "8.6.2", "version": "8.6.3",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.6.2.tgz", "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.6.3.tgz",
"integrity": "sha512-ErbDVvuUzUfyQpXvJ6sXznmZDICD8r6wIsa0VKjJtB6/LZncqwUn5Um040G1BaNo6L3Jz+xItLSwT0wZmSmUaQ==", "integrity": "sha512-++yRmm7hjMbqVA/8WeiygTnEfrFbiy+OBjQi49GFJIvCQuSYE56myyQWo4j5hbpcHjhHQU8NukMNGTwAWFWjIw==",
"dependencies": { "dependencies": {
"bson": "^6.7.0", "bson": "^6.7.0",
"kareem": "2.6.3", "kareem": "2.6.3",
@@ -13626,9 +13626,9 @@
} }
}, },
"node_modules/stylelint-config-recess-order": { "node_modules/stylelint-config-recess-order": {
"version": "5.1.0", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/stylelint-config-recess-order/-/stylelint-config-recess-order-5.1.0.tgz", "resolved": "https://registry.npmjs.org/stylelint-config-recess-order/-/stylelint-config-recess-order-5.1.1.tgz",
"integrity": "sha512-ddapCF6B/kEtQYIFhQFReQ0dvK1ZdgJDM/SGFtIyeooYDbqaJqcOlGkRRGaVErCQYJY/bPSPsLRS2LdQtLJUVQ==", "integrity": "sha512-eDAHWVBelzDbMbdMj15pSw0Ycykv5eLeriJdbGCp0zd44yvhgZLI+wyVHegzXp5NrstxTPSxl0fuOVKdMm0XLA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"stylelint-order": "^6.0.4" "stylelint-order": "^6.0.4"

View File

@@ -113,7 +113,7 @@
"marked-smartypants-lite": "^1.0.2", "marked-smartypants-lite": "^1.0.2",
"markedLegacy": "npm:marked@^0.3.19", "markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.30.1", "moment": "^2.30.1",
"mongoose": "^8.6.2", "mongoose": "^8.6.3",
"nanoid": "3.3.4", "nanoid": "3.3.4",
"nconf": "^0.12.1", "nconf": "^0.12.1",
"react": "^18.3.1", "react": "^18.3.1",
@@ -126,7 +126,7 @@
}, },
"devDependencies": { "devDependencies": {
"@stylistic/stylelint-plugin": "^3.0.1", "@stylistic/stylelint-plugin": "^3.0.1",
"eslint": "^9.10.0", "eslint": "^9.11.0",
"eslint-plugin-jest": "^28.8.3", "eslint-plugin-jest": "^28.8.3",
"eslint-plugin-react": "^7.36.1", "eslint-plugin-react": "^7.36.1",
"globals": "^15.9.0", "globals": "^15.9.0",
@@ -134,7 +134,7 @@
"jest-expect-message": "^1.1.3", "jest-expect-message": "^1.1.3",
"postcss-less": "^6.0.0", "postcss-less": "^6.0.0",
"stylelint": "^16.9.0", "stylelint": "^16.9.0",
"stylelint-config-recess-order": "^5.1.0", "stylelint-config-recess-order": "^5.1.1",
"stylelint-config-recommended": "^14.0.1", "stylelint-config-recommended": "^14.0.1",
"supertest": "^7.0.0" "supertest": "^7.0.0"
} }

View File

@@ -29,12 +29,18 @@ const rendererConditions = (legacy, v3)=>{
return {}; // If all renderers selected, renderer field not needed in query for speed return {}; // If all renderers selected, renderer field not needed in query for speed
}; };
const sortConditions = (sort, dir) => {
return { [sort]: dir === 'asc' ? 1 : -1 };
};
const findBrews = async (req, res)=>{ const findBrews = async (req, res)=>{
const title = req.query.title || ''; const title = req.query.title || '';
const author = req.query.author || ''; const author = req.query.author || '';
const page = Math.max(parseInt(req.query.page) || 1, 1); const page = Math.max(parseInt(req.query.page) || 1, 1);
const count = Math.max(parseInt(req.query.count) || 20, 10); const count = Math.max(parseInt(req.query.count) || 20, 10);
const skip = (page - 1) * count; const skip = (page - 1) * count;
const sort = req.query.sort || 'title';
const dir = req.query.dir || 'asc';
const combinedQuery = { const combinedQuery = {
$and : [ $and : [
@@ -54,6 +60,7 @@ const findBrews = async (req, res)=>{
}; };
await HomebrewModel.find(combinedQuery, projection) await HomebrewModel.find(combinedQuery, projection)
.sort(sortConditions(sort, dir))
.skip(skip) .skip(skip)
.limit(count) .limit(count)
.maxTimeMS(5000) .maxTimeMS(5000)