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 | {name} | ;
+ })}
+
+
+
+ {this.state.result[this.props.resultName].map((result, resultIdx)=>{
+ return {navigator.clipboard.writeText(result.shareId.toString());}}>
+ {this.props.propertyNames.map((name, nameIdx)=>{
+ return |
+ {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}` });
}
});