diff --git a/client/admin/admin.jsx b/client/admin/admin.jsx index c20b6abed..088847a66 100644 --- a/client/admin/admin.jsx +++ b/client/admin/admin.jsx @@ -8,7 +8,9 @@ const BrewLookup = require('./brewLookup/brewLookup.jsx'); const BrewCompress = require ('./brewCompress/brewCompress.jsx'); const Stats = require('./stats/stats.jsx'); -const tabGroups = ['brews']; +const LockTools = require('./lockTools/lockTools.jsx'); + +const tabGroups = ['brews', 'locks']; const Admin = createClass({ getDefaultProps : function() { @@ -52,10 +54,11 @@ const Admin = createClass({
{tabGroups.map((name, idx)=>{ - return ; + return ; })}
{this.state.currentTab == 'brews' && this.renderBrewTools()} + {this.state.currentTab == 'locks' && }
; } diff --git a/client/admin/admin.less b/client/admin/admin.less index a61335835..39a66bd54 100644 --- a/client/admin/admin.less +++ b/client/admin/admin.less @@ -40,5 +40,23 @@ body{ margin : 30px 0px; } + .tabs { + border-bottom: 1px solid black; + } + + button.tab { + margin: 2px 2px 0px; + border-width: 1px 1px 0px; + border-style: solid; + border-color: black; + border-radius: 5px 5px 0px 0px; + color: black; + background-color: transparent; + &.active { + background-color: #000000a0; + color: white; + text-decoration: underline; + } + } } diff --git a/client/admin/lockTools/lockTools.jsx b/client/admin/lockTools/lockTools.jsx new file mode 100644 index 000000000..710a29681 --- /dev/null +++ b/client/admin/lockTools/lockTools.jsx @@ -0,0 +1,176 @@ +require('./lockTools.less'); +const React = require('react'); +const createClass = require('create-react-class'); + +const request = require('superagent'); + +const LockTools = createClass({ + getInitialState : function() { + return { + fetching : false, + reviewCount : 0 + }; + }, + + componentDidMount : function() { + this.updateReviewCount(); + }, + + updateReviewCount : async function() { + const newCount = await request.get('/admin/lock') + .then((res)=>{return res.body?.count || 'Unknown';}); + if(newCount != this.state.reviewCount){ + this.setState({ + reviewCount : newCount + }); + } + }, + + render : function() { + return
+

Lock Count

+

Number of brews currently locked: {this.state.reviewCount}

+ +
+ +
+

Lock Brew

+ NYI +
+ +
+ +
; + } +}); + +const LockTable = createClass({ + getDefaultProps : function() { + return { + title : '', + fetchURL : '/admin/locks', + resultName : '', + propertyNames : ['shareId'] + }; + }, + + getInitialState : function() { + return { + result : '', + error : '', + searching : false + }; + }, + + clickFn(){ + this.setState({ searching: true, error: null }); + + request.get(this.props.fetchURL) + .then((res)=>this.setState({ result: res.body })) + .catch((err)=>this.setState({ error: err })) + .finally(()=>{ + this.setState({ searching: false }); + }); + }, + + render : function () { + return <> +

{this.props.title}

+ + {this.state.result[this.props.resultName] && + <> +

Total Reviews Waiting: {this.state.result[this.props.resultName].length}

+ + + + {this.props.propertyNames.map((name, idx)=>{ + return ; + })} + + + + {this.state.result[this.props.resultName].map((result, resultIdx)=>{ + return {navigator.clipboard.writeText(result.shareId.toString());}}> + {this.props.propertyNames.map((name, nameIdx)=>{ + return ; + })} + ; + })} + +
{name}
+ {result[name].toString()} +
+ + } + ; + } +}); + +const LockLookup = createClass({ + getDefaultProps : function() { + return { + fetchURL : '/admin/lookup' + }; + }, + + getInitialState : function() { + return { + query : '', + result : '', + error : '', + searching : false + }; + }, + + handleChange(e){ + this.setState({ query: e.target.value }); + }, + + clickFn(){ + this.setState({ searching: true, error: null }); + + request.get(`${this.props.fetchURL}/${this.state.query}`) + .then((res)=>this.setState({ result: res.body })) + .catch((err)=>this.setState({ error: err })) + .finally(()=>{ + this.setState({ searching: false }); + }); + }, + + renderResult : function(){ + return <> +

Result:

+ + + {Object.keys(this.state.result).map((key, idx)=>{ + return + + + ; + })} + +
{key}{this.state.result[key].toString()} +
+ ; + }, + + render : function() { + return
+

{this.props.title}

+ + + + {this.state.error + &&
{this.state.error.toString()}
+ } + + {this.state.result && this.renderResult()} +
; + } +}); + +module.exports = LockTools; \ No newline at end of file diff --git a/client/admin/lockTools/lockTools.less b/client/admin/lockTools/lockTools.less new file mode 100644 index 000000000..8f0ec04bd --- /dev/null +++ b/client/admin/lockTools/lockTools.less @@ -0,0 +1,3 @@ +.lockTools { + display: block; +} \ No newline at end of file diff --git a/server/admin.api.js b/server/admin.api.js index 455258555..e1d33f029 100644 --- a/server/admin.api.js +++ b/server/admin.api.js @@ -162,12 +162,13 @@ router.get('/admin/lock', mw.adminOnly, async (req, res)=>{ }); } catch (error) { console.error(error); - return res.status(500).json({ error: 'Unable to get lock count' }); + return res.json({ status: 'ERROR', detail: 'Unable to get lock count', error }); } }); router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ const lock = req.body; + lock.applied = new Date; try { const filter = { @@ -176,6 +177,8 @@ router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ const brew = await HomebrewModel.findOne(filter); + if(brew.lock) return res.json({ status: 'ALREADY LOCKED', detail: `Lock already exists on brew ${req.params.id} - ${brew.title}` }); + brew.lock = lock; brew.markModified('lock'); @@ -184,10 +187,34 @@ router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{ console.log(`Lock applied to brew ID ${brew.shareId} - ${brew.title}`); } catch (error) { console.error(error); - return res.status(500).json({ error: `Unable to set lock on brew ${req.params.id}` }); + return res.json({ error, message: `Unable to set lock on brew ${req.params.id}` }); } - return res.status(200).json({ status: 'SUCCESS', lock }); + return res.json({ status: 'LOCKED', detail: `Lock applied to brew ID ${brew.shareId} - ${brew.title}`, lock }); +}); + +router.get('/admin/unlock/:id', mw.adminOnly, async (req, res)=>{ + try { + const filter = { + shareId : req.params.id + }; + + const brew = await HomebrewModel.findOne(filter); + + if(!brew.lock) return res.json({ status: 'NOT LOCKED', detail: `Brew ID ${req.params.id} is not locked!` }); + + brew.lock = undefined; + brew.markModified('lock'); + + await brew.save(); + + console.log(`Lock removed from brew ID ${brew.shareId} - ${brew.title}`); + } catch (error) { + console.error(error); + return res.json({ status: 'ERROR', detail: `Unable to clear lock on brew ${req.params.id}`, error }); + } + + return res.json({ status: 'UNLOCKED', detail: `Lock removed from brew ID ${req.params.id}` }); }); router.get('/admin/lock/reviews', mw.adminOnly, async (req, res)=>{ @@ -202,12 +229,13 @@ router.get('/admin/lock/reviews', mw.adminOnly, async (req, res)=>{ } ]; const reviewDocuments = await HomebrewModel.getAggregate(countReviewsPipeline); + console.log(reviewDocuments); return res.json({ reviewDocuments }); } catch (error) { console.error(error); - return res.status(500).json({ error: 'Unable to get review collection' }); + return res.json({ status: 'ERROR', detail: 'Unable to get review collection', error }); } }); @@ -221,11 +249,11 @@ router.get('/admin/lock/review/request/:id', async (req, res)=>{ }; const brew = await HomebrewModel.findOne(filter); - if(!brew) { return res.status(500).json({ error: `Brew ID ${req.params.id} is not locked!` }); }; + if(!brew) { return res.json({ status: 'NOT LOCKED', detail: `Brew ID ${req.params.id} is not locked!` }); }; if(brew.lock.reviewRequested){ console.log(`Review already requested for brew ${brew.shareId} - ${brew.title}`); - return res.status(500).json({ error: `Review already requested for brew ${brew.shareId} - ${brew.title}` }); + return res.json({ status: 'NOT REQUESTED', detail: `Review already requested for brew ${brew.shareId} - ${brew.title}` }); }; brew.lock.reviewRequested = new Date(); @@ -237,10 +265,10 @@ router.get('/admin/lock/review/request/:id', async (req, res)=>{ }); console.log(`Review requested on brew ${brew.shareId} - ${brew.title}`); - return res.json(brew); + return res.json({ status: 'REVIEW REQUESTED', brew }); } catch (error) { console.error(error); - return res.status(500).json({ error: `Unable to set request for review on brew ID ${req.params.id}` }); + return res.json({ status: 'ERROR', detail: `Unable to set request for review on brew ID ${req.params.id}`, error }); } }); @@ -253,7 +281,7 @@ router.get('/admin/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ }; const brew = await HomebrewModel.findOne(filter); - if(!brew) { return res.status(500).json({ error: `Brew ID ${req.params.id} does not have a review pending!` }); }; + if(!brew) { return res.json({ status: 'NOT REMOVED', detail: `Brew ID ${req.params.id} does not have a review pending!` }); }; delete brew.lock.reviewRequested; brew.markModified('lock'); @@ -264,7 +292,7 @@ router.get('/admin/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{ return res.json(brew); } catch (error) { console.error(error); - return res.status(500).json({ error: `Unable to remove request for review on brew ID ${req.params.id}` }); + return res.json({ error: `Unable to remove request for review on brew ID ${req.params.id}` }); } });