mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-08 07:32:40 +00:00
Initial UI functionality for most Lock functions
This commit is contained in:
@@ -8,7 +8,9 @@ const BrewLookup = require('./brewLookup/brewLookup.jsx');
|
|||||||
const BrewCompress = require ('./brewCompress/brewCompress.jsx');
|
const BrewCompress = require ('./brewCompress/brewCompress.jsx');
|
||||||
const Stats = require('./stats/stats.jsx');
|
const Stats = require('./stats/stats.jsx');
|
||||||
|
|
||||||
const tabGroups = ['brews'];
|
const LockTools = require('./lockTools/lockTools.jsx');
|
||||||
|
|
||||||
|
const tabGroups = ['brews', 'locks'];
|
||||||
|
|
||||||
const Admin = createClass({
|
const Admin = createClass({
|
||||||
getDefaultProps : function() {
|
getDefaultProps : function() {
|
||||||
@@ -52,10 +54,11 @@ const Admin = createClass({
|
|||||||
<div className='container'>
|
<div className='container'>
|
||||||
<div className='tabs'>
|
<div className='tabs'>
|
||||||
{tabGroups.map((name, idx)=>{
|
{tabGroups.map((name, idx)=>{
|
||||||
return <button className='tab' key={idx} onClick={()=>{return this.handleClick(name);}}>{name.toUpperCase()}</button>;
|
return <button className={`tab ${this.state.currentTab === name ? 'active' : ''}`} key={idx} onClick={()=>{return this.handleClick(name);}}>{name.toUpperCase()}</button>;
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
{this.state.currentTab == 'brews' && this.renderBrewTools()}
|
{this.state.currentTab == 'brews' && this.renderBrewTools()}
|
||||||
|
{this.state.currentTab == 'locks' && <LockTools />}
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,5 +40,23 @@ body{
|
|||||||
margin : 30px 0px;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
176
client/admin/lockTools/lockTools.jsx
Normal file
176
client/admin/lockTools/lockTools.jsx
Normal file
@@ -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 <div className='lockTools'>
|
||||||
|
<h2>Lock Count</h2>
|
||||||
|
<p>Number of brews currently locked: {this.state.reviewCount}</p>
|
||||||
|
<button onClick={this.updateReviewCount}>REFRESH</button>
|
||||||
|
<hr />
|
||||||
|
<LockTable title='Brews Awaiting Review' resultName='reviewDocuments' fetchURL='/admin/lock/reviews' propertyNames={['shareId', 'title']} ></LockTable>
|
||||||
|
<hr />
|
||||||
|
<h2>Lock Brew</h2>
|
||||||
|
<LockLookup>NYI</LockLookup>
|
||||||
|
<hr />
|
||||||
|
<LockLookup title='Unlock Brew' fetchURL='/admin/unlock' updateFn={this.updateReviewCount}></LockLookup>
|
||||||
|
<hr />
|
||||||
|
<LockLookup title='Clear Review Request' fetchURL='/admin/lock/review/remove'></LockLookup>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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 <>
|
||||||
|
<h2>{this.props.title}</h2>
|
||||||
|
<button onClick={this.clickFn}>
|
||||||
|
<i className={`fas ${!this.state.searching ? 'fa-search' : 'fa-spin fa-spinner'}`} />
|
||||||
|
</button>
|
||||||
|
{this.state.result[this.props.resultName] &&
|
||||||
|
<>
|
||||||
|
<p>Total Reviews Waiting: {this.state.result[this.props.resultName].length}</p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{this.props.propertyNames.map((name, idx)=>{
|
||||||
|
return <th key={idx}>{name}</th>;
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{this.state.result[this.props.resultName].map((result, resultIdx)=>{
|
||||||
|
return <tr key={`${resultIdx}-row`} onClick={()=>{navigator.clipboard.writeText(result.shareId.toString());}}>
|
||||||
|
{this.props.propertyNames.map((name, nameIdx)=>{
|
||||||
|
return <td key={`${resultIdx}-${nameIdx}`}>
|
||||||
|
{result[name].toString()}
|
||||||
|
</td>;
|
||||||
|
})}
|
||||||
|
</tr>;
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
</>;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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 <>
|
||||||
|
<h3>Result:</h3>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
{Object.keys(this.state.result).map((key, idx)=>{
|
||||||
|
return <tr key={`${idx}-row`}>
|
||||||
|
<td key={`${idx}-key`}>{key}</td>
|
||||||
|
<td key={`${idx}-value`}>{this.state.result[key].toString()}
|
||||||
|
</td>
|
||||||
|
</tr>;
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</>;
|
||||||
|
},
|
||||||
|
|
||||||
|
render : function() {
|
||||||
|
return <div className='brewLookup'>
|
||||||
|
<h2>{this.props.title}</h2>
|
||||||
|
<input type='text' value={this.state.query} onChange={this.handleChange} placeholder='edit or share id' />
|
||||||
|
<button onClick={this.clickFn}>
|
||||||
|
<i className={`fas ${!this.state.searching ? 'fa-search' : 'fa-spin fa-spinner'}`} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{this.state.error
|
||||||
|
&& <div className='error'>{this.state.error.toString()}</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
{this.state.result && this.renderResult()}
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = LockTools;
|
||||||
3
client/admin/lockTools/lockTools.less
Normal file
3
client/admin/lockTools/lockTools.less
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.lockTools {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
@@ -162,12 +162,13 @@ router.get('/admin/lock', mw.adminOnly, async (req, res)=>{
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(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)=>{
|
router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{
|
||||||
const lock = req.body;
|
const lock = req.body;
|
||||||
|
lock.applied = new Date;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const filter = {
|
const filter = {
|
||||||
@@ -176,6 +177,8 @@ router.post('/admin/lock/:id', mw.adminOnly, async (req, res)=>{
|
|||||||
|
|
||||||
const brew = await HomebrewModel.findOne(filter);
|
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.lock = lock;
|
||||||
brew.markModified('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}`);
|
console.log(`Lock applied to brew ID ${brew.shareId} - ${brew.title}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(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)=>{
|
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);
|
const reviewDocuments = await HomebrewModel.getAggregate(countReviewsPipeline);
|
||||||
|
console.log(reviewDocuments);
|
||||||
return res.json({
|
return res.json({
|
||||||
reviewDocuments
|
reviewDocuments
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(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);
|
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){
|
if(brew.lock.reviewRequested){
|
||||||
console.log(`Review already requested for brew ${brew.shareId} - ${brew.title}`);
|
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();
|
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}`);
|
console.log(`Review requested on brew ${brew.shareId} - ${brew.title}`);
|
||||||
return res.json(brew);
|
return res.json({ status: 'REVIEW REQUESTED', brew });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(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);
|
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;
|
delete brew.lock.reviewRequested;
|
||||||
brew.markModified('lock');
|
brew.markModified('lock');
|
||||||
@@ -264,7 +292,7 @@ router.get('/admin/lock/review/remove/:id', mw.adminOnly, async (req, res)=>{
|
|||||||
return res.json(brew);
|
return res.json(brew);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(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}` });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user