Added global search to the admin UI
parent
a880c0078f
commit
0d44ce7cbb
|
@ -643,6 +643,13 @@ const getCountryList = () => {
|
|||
return countryArray;
|
||||
};
|
||||
|
||||
const cleanAmount = (amount) => {
|
||||
if(amount){
|
||||
return parseInt(amount.toString().replace('.', ''));
|
||||
}
|
||||
return amount;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
allowedMimeType,
|
||||
fileSizeLimit,
|
||||
|
@ -676,5 +683,6 @@ module.exports = {
|
|||
newId,
|
||||
getData,
|
||||
hooker,
|
||||
getCountryList
|
||||
getCountryList,
|
||||
cleanAmount
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-disable prefer-arrow-callback, no-var, no-tabs */
|
||||
/* globals showNotification, slugify */
|
||||
/* globals showNotification, slugify, numeral */
|
||||
$(document).ready(function (){
|
||||
$(document).on('click', '#btnGenerateAPIkey', function(e){
|
||||
e.preventDefault();
|
||||
|
@ -646,4 +646,78 @@ $(document).ready(function (){
|
|||
showNotification(msg.responseJSON.message, 'danger');
|
||||
});
|
||||
});
|
||||
|
||||
$('#global-search-value').on('keyup', (e) => {
|
||||
if($('#global-search-value').val() === ''){
|
||||
$('#global-search-results').empty();
|
||||
$('#global-search-results').addClass('invisible');
|
||||
}
|
||||
// Search when 3 or more characters are entered
|
||||
if($('#global-search-value').val().length > 3){
|
||||
$('#global-search').html('<span class="fa fa-spinner fa-spin"></span>');
|
||||
globalSearch();
|
||||
}
|
||||
});
|
||||
|
||||
$('#globalSearchModal').on('shown.bs.modal', function (){
|
||||
$('#global-search-value').focus();
|
||||
});
|
||||
|
||||
$('body').on('click', '.gr-click', (e) => {
|
||||
$('#global-search-value').val();
|
||||
const url = $(e.currentTarget).closest('.global-result').attr('data-url');
|
||||
if(url){
|
||||
window.location = url;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function globalSearch(){
|
||||
$('#global-search-results').empty();
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/admin/searchall',
|
||||
data: {
|
||||
searchValue: $('#global-search-value').val()
|
||||
}
|
||||
}).done((res) => {
|
||||
$('#global-search').html('<i class="fal fa-search"></i>');
|
||||
let hasResult = false;
|
||||
res.customers.forEach((value) => {
|
||||
hasResult = true;
|
||||
let result = '<li class="list-group-item global-result text-center" data-url="/admin/customer/view/' + value._id + '">';
|
||||
result += '<div class="row">';
|
||||
result += '<div class="col global-result-type gr-click"><i class="fas fa-users"></i> Customer</div>';
|
||||
result += '<div class="col global-result-detail gr-click">' + value.firstName + ' ' + value.lastName + '</div>';
|
||||
result += '<div class="col global-result-detail gr-click">' + value.email + '</div>';
|
||||
result += '</div></li>';
|
||||
$('#global-search-results').append(result);
|
||||
});
|
||||
|
||||
res.orders.forEach((value) => {
|
||||
hasResult = true;
|
||||
let result = '<li class="list-group-item global-result text-center" data-url="/admin/order/view/' + value._id + '">';
|
||||
result += '<div class="row">';
|
||||
result += '<div class="col global-result-type gr-click"><i class="fas fa-cube"></i> Order</div>';
|
||||
result += '<div class="col global-result-detail gr-click">' + value.orderFirstname + ' ' + value.orderLastname + '</div>';
|
||||
result += '<div class="col global-result-detail gr-click">' + value.orderEmail + '</div>';
|
||||
result += '</div></li>';
|
||||
$('#global-search-results').append(result);
|
||||
});
|
||||
|
||||
res.products.forEach((value) => {
|
||||
hasResult = true;
|
||||
let result = '<li class="list-group-item global-result text-center" data-url="/admin/product/edit/' + value._id + '">';
|
||||
result += '<div class="row">';
|
||||
result += '<div class="col global-result-type gr-click"><i class="fas fa-box-open"></i> Product</div>';
|
||||
result += '<div class="col global-result-detail gr-click">' + value.productTitle + '</div>';
|
||||
result += '<div class="col global-result-detail gr-click">' + numeral(value.productPrice).format('0.00') + '</div>';
|
||||
result += '</div></li>';
|
||||
$('#global-search-results').append(result);
|
||||
});
|
||||
|
||||
if(hasResult === true){
|
||||
$('#global-search-results').removeClass('invisible');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -109,6 +109,50 @@ body .popover {
|
|||
color: #838b8f;
|
||||
font-size: 20px;
|
||||
}
|
||||
.global-result-type {
|
||||
color: #8d8d8d;
|
||||
}
|
||||
.global-result:hover {
|
||||
background-color: #007bff;
|
||||
}
|
||||
.global-result:hover .global-result-detail,
|
||||
.global-result:hover .global-result-type,
|
||||
.global-result:hover .global-result-type .fal {
|
||||
color: #fff !important;
|
||||
}
|
||||
.global-result a {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
.global-result:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
.global-result:first-child {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
.global-result {
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
}
|
||||
.global-search-modal-content,
|
||||
.global-search-modal-header {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
#global-search-results {
|
||||
padding-right: 0;
|
||||
border-bottom-left-radius: 0.3rem;
|
||||
border-bottom-right-radius: 0.3rem;
|
||||
}
|
||||
.global-search-form {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
#global-search-value {
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
.search-input-addon {
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
@media only screen and (max-width: 768px) {
|
||||
.navbar-default .navbar-brand {
|
||||
padding-top: 10px;
|
||||
|
|
|
@ -1 +1 @@
|
|||
.btn-outline-primary,.btn-warning{color:#fff!important;background-color:#000;border-color:#000}.btn-outline-danger{color:#fff!important;background-color:#cc4135;border-color:#cc4135}.has-error div,.has-error input,.has-error textarea{border-color:#cc4135}#frm_search,.search-bar-input,.search-bar-input .btn{padding-top:10px;height:45px}.productsWrapper{padding-right:10px;padding-left:10px}.searchBarWrapper{padding-right:0;padding-left:0}.footer{padding-top:20px}.product-price{padding-bottom:0}.navbarMenuWrapper{background-color:#f5f5f5}.navbarMenu>ul>li>a:hover{color:#cc4135!important}.navbarMenu{padding-right:0;padding-left:0}.product-wrapper>a:hover{color:#cc4135!important}#navbar,#navbar>.navbar-nav,#navbar>.navbar-nav>li>a,.navbar-header,.navbar-static-top{margin-bottom:0;height:100px!important}#navbar>.navbar-nav>li>a{padding-top:35px}.pagination>li>a{background-color:#cc4135!important}body .popover{display:none!important}.navbar-brand{color:#cc4135!important;letter-spacing:4px;padding-left:20px!important;padding-top:0!important;height:80px!important;font-size:55px!important}.navbar-brand,.navbar-brand-image{height:80px;padding-left:10px;padding-top:10px}.navbar-default .badge{background-color:#cc4135}#empty-cart:active,#empty-cart:active:hover,#empty-cart:focus,#empty-cart:hover,.pushy-link:active,.pushy-link:active:hover,.pushy-link:focus,.pushy-link:hover{border-color:#cc4135;background-color:#cc4135}.navActive>a{margin-bottom:0;padding-top:15px;border-bottom:5px solid #cc4135}#navbar,#navbar>.navbar-nav,#navbar>.navbar-nav>li>a,.navbar-header,.navbar-static-top{background-color:#fff}.navbar-default .navbar-nav>li>a{color:#838b8f;font-size:20px}@media only screen and (max-width:768px){.navbar-default .navbar-brand{padding-top:10px}.navbar-default .navbar-nav>li>a{font-size:16px}.searchBarWrapper{padding-top:10px}.navbarMenuWrapper{padding-left:0;padding-right:0}.navbarMenuOuter{padding-left:0;padding-right:0}.navActive>a{color:#fff!important}.navbarMenu{padding-right:7.5px;padding-left:7.5px}.navActive>a{color:#fff!important;background-color:#cc4135;border-bottom:none}.footer{padding-top:10px}}
|
||||
.btn-outline-primary,.btn-warning{color:#fff!important;background-color:#000;border-color:#000}.btn-outline-danger{color:#fff!important;background-color:#cc4135;border-color:#cc4135}.has-error div,.has-error input,.has-error textarea{border-color:#cc4135}#frm_search,.search-bar-input,.search-bar-input .btn{padding-top:10px;height:45px}.productsWrapper{padding-right:10px;padding-left:10px}.searchBarWrapper{padding-right:0;padding-left:0}.footer{padding-top:20px}.product-price{padding-bottom:0}.navbarMenuWrapper{background-color:#f5f5f5}.navbarMenu>ul>li>a:hover{color:#cc4135!important}.navbarMenu{padding-right:0;padding-left:0}.product-wrapper>a:hover{color:#cc4135!important}#navbar,#navbar>.navbar-nav,#navbar>.navbar-nav>li>a,.navbar-header,.navbar-static-top{margin-bottom:0;height:100px!important}#navbar>.navbar-nav>li>a{padding-top:35px}.pagination>li>a{background-color:#cc4135!important}body .popover{display:none!important}.navbar-brand{color:#cc4135!important;letter-spacing:4px;padding-left:20px!important;padding-top:0!important;height:80px!important;font-size:55px!important}.navbar-brand,.navbar-brand-image{height:80px;padding-left:10px;padding-top:10px}.navbar-default .badge{background-color:#cc4135}#empty-cart:active,#empty-cart:active:hover,#empty-cart:focus,#empty-cart:hover,.pushy-link:active,.pushy-link:active:hover,.pushy-link:focus,.pushy-link:hover{border-color:#cc4135;background-color:#cc4135}.navActive>a{margin-bottom:0;padding-top:15px;border-bottom:5px solid #cc4135}#navbar,#navbar>.navbar-nav,#navbar>.navbar-nav>li>a,.navbar-header,.navbar-static-top{background-color:#fff}.navbar-default .navbar-nav>li>a{color:#838b8f;font-size:20px}.global-result-type{color:#8d8d8d}.global-result:hover{background-color:#007bff}.global-result:hover .global-result-detail,.global-result:hover .global-result-type,.global-result:hover .global-result-type .fal{color:#fff!important}.global-result a{text-decoration:none!important}.global-result:hover{cursor:pointer}.global-result:first-child{border-top-left-radius:0;border-top-right-radius:0}.global-result{border-left:0;border-right:0}.global-search-modal-content,.global-search-modal-header{background-color:transparent;border:none}#global-search-results{padding-right:0;border-bottom-left-radius:.3rem;border-bottom-right-radius:.3rem}.global-search-form{margin-bottom:0}#global-search-value{border-bottom-right-radius:0}.search-input-addon{border-bottom-left-radius:0}@media only screen and (max-width:768px){.navbar-default .navbar-brand{padding-top:10px}.navbar-default .navbar-nav>li>a{font-size:16px}.searchBarWrapper{padding-top:10px}.navbarMenuWrapper{padding-left:0;padding-right:0}.navbarMenuOuter{padding-left:0;padding-right:0}.navActive>a{color:#fff!important}.navbarMenu{padding-right:7.5px;padding-left:7.5px}.navActive>a{color:#fff!important;background-color:#cc4135;border-bottom:none}.footer{padding-top:10px}}
|
|
@ -116,6 +116,61 @@ body .popover{display:none !important; }
|
|||
font-size: 20px;
|
||||
}
|
||||
|
||||
.global-result-type {
|
||||
color: #8d8d8d;
|
||||
}
|
||||
|
||||
.global-result:hover {
|
||||
background-color: #007bff;
|
||||
}
|
||||
|
||||
.global-result:hover .global-result-detail,
|
||||
.global-result:hover .global-result-type,
|
||||
.global-result:hover .global-result-type .fal {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.global-result a {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.global-result:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.global-result:first-child {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.global-result {
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
.global-search-modal-content, .global-search-modal-header{
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
#global-search-results{
|
||||
padding-right: 0;
|
||||
border-bottom-left-radius: .3rem;
|
||||
border-bottom-right-radius: .3rem;
|
||||
}
|
||||
|
||||
.global-search-form{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#global-search-value {
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.search-input-addon {
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px){
|
||||
.navbar-default .navbar-brand {
|
||||
padding-top: 10px;
|
||||
|
|
|
@ -203,6 +203,14 @@ input[type=number]::-webkit-outer-spin-button {
|
|||
margin-top: -7px;
|
||||
}
|
||||
|
||||
.pad-right-10{
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.pad-right-15{
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.no-pad-left{
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
|
|
@ -159,6 +159,12 @@ input[type=number]::-webkit-outer-spin-button {
|
|||
.list-group-input-pad select {
|
||||
margin-top: -7px;
|
||||
}
|
||||
.pad-right-10 {
|
||||
padding-right: 10px;
|
||||
}
|
||||
.pad-right-15 {
|
||||
padding-right: 15px;
|
||||
}
|
||||
.no-pad-left {
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
107
routes/admin.js
107
routes/admin.js
|
@ -11,6 +11,10 @@ const mime = require('mime-type/with-db');
|
|||
const ObjectId = require('mongodb').ObjectID;
|
||||
const router = express.Router();
|
||||
|
||||
// Regex
|
||||
const emailRegex = /\S+@\S+\.\S+/;
|
||||
const numericRegex = /^\d*\.?\d*$/;
|
||||
|
||||
// Admin section
|
||||
router.get('/admin', restrict, (req, res, next) => {
|
||||
res.redirect('/admin/dashboard');
|
||||
|
@ -497,4 +501,107 @@ router.post('/admin/testEmail', restrict, (req, res) => {
|
|||
res.status(200).json({ message: 'Test email sent' });
|
||||
});
|
||||
|
||||
router.post('/admin/searchall', restrict, async (req, res, next) => {
|
||||
const db = req.app.db;
|
||||
const searchValue = req.body.searchValue;
|
||||
const limitReturned = 5;
|
||||
|
||||
// Empty arrays
|
||||
let customers = [];
|
||||
let orders = [];
|
||||
let products = [];
|
||||
|
||||
// Default queries
|
||||
const customerQuery = {};
|
||||
const orderQuery = {};
|
||||
const productQuery = {};
|
||||
|
||||
// If an ObjectId is detected use that
|
||||
if(ObjectId.isValid(req.body.searchValue)){
|
||||
// Get customers
|
||||
customers = await db.customers.find({
|
||||
_id: ObjectId(searchValue)
|
||||
})
|
||||
.limit(limitReturned)
|
||||
.sort({ created: 1 })
|
||||
.toArray();
|
||||
|
||||
// Get orders
|
||||
orders = await db.orders.find({
|
||||
_id: ObjectId(searchValue)
|
||||
})
|
||||
.limit(limitReturned)
|
||||
.sort({ orderDate: 1 })
|
||||
.toArray();
|
||||
|
||||
// Get products
|
||||
products = await db.products.find({
|
||||
_id: ObjectId(searchValue)
|
||||
})
|
||||
.limit(limitReturned)
|
||||
.sort({ productAddedDate: 1 })
|
||||
.toArray();
|
||||
|
||||
return res.status(200).json({
|
||||
customers,
|
||||
orders,
|
||||
products
|
||||
});
|
||||
}
|
||||
|
||||
// If email address is detected
|
||||
if(emailRegex.test(req.body.searchValue)){
|
||||
customerQuery.email = searchValue;
|
||||
orderQuery.orderEmail = searchValue;
|
||||
}else if(numericRegex.test(req.body.searchValue)){
|
||||
// If a numeric value is detected
|
||||
orderQuery.amount = common.cleanAmount(req.body.searchValue);
|
||||
productQuery.productPrice = common.cleanAmount(req.body.searchValue);
|
||||
}else{
|
||||
// String searches
|
||||
customerQuery.$or = [
|
||||
{ firstName: { $regex: new RegExp(searchValue, 'img') } },
|
||||
{ lastName: { $regex: new RegExp(searchValue, 'img') } }
|
||||
];
|
||||
orderQuery.$or = [
|
||||
{ orderFirstname: { $regex: new RegExp(searchValue, 'img') } },
|
||||
{ orderLastname: { $regex: new RegExp(searchValue, 'img') } }
|
||||
];
|
||||
productQuery.$or = [
|
||||
{ productTitle: { $regex: new RegExp(searchValue, 'img') } },
|
||||
{ productDescription: { $regex: new RegExp(searchValue, 'img') } }
|
||||
];
|
||||
}
|
||||
|
||||
// Get customers
|
||||
if(Object.keys(customerQuery).length > 0){
|
||||
customers = await db.customers.find(customerQuery)
|
||||
.limit(limitReturned)
|
||||
.sort({ created: 1 })
|
||||
.toArray();
|
||||
}
|
||||
|
||||
// Get orders
|
||||
if(Object.keys(orderQuery).length > 0){
|
||||
orders = await db.orders.find(orderQuery)
|
||||
.limit(limitReturned)
|
||||
.sort({ orderDate: 1 })
|
||||
.toArray();
|
||||
}
|
||||
|
||||
// Get products
|
||||
if(Object.keys(productQuery).length > 0){
|
||||
products = await db.products.find(productQuery)
|
||||
.limit(limitReturned)
|
||||
.sort({ productAddedDate: 1 })
|
||||
.toArray();
|
||||
}
|
||||
|
||||
return res.status(200).json({
|
||||
customers,
|
||||
orders,
|
||||
products
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
|
|
@ -119,6 +119,7 @@
|
|||
{{/unless}}
|
||||
{{#if admin}}
|
||||
{{#if @root.session.user}}
|
||||
<li class="nav-item"><a href="#" data-toggle="modal" data-target="#globalSearchModal" class="pad-right-15"><i class="fas fa-search"></i></a></li>
|
||||
<li class="nav-item"><a href="/admin/logout"><i class="fas fa-sign-out-alt"></i> Logout</a></li>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
@ -176,5 +177,28 @@
|
|||
</footer>
|
||||
{{/if}}
|
||||
<script src="/javascripts/pushy.min.js"></script>
|
||||
{{#if admin}}
|
||||
<div class="modal fade" id="globalSearchModal" tabindex="-1" role="dialog" aria-labelledby="globalSearchModal"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content global-search-modal-content">
|
||||
<div class="modal-header global-search-modal-header"></div>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<div class="form-group global-search-form">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<label class="input-group-text search-input-addon" for="global-search-value"><i class="fas fa-search"></i></label>
|
||||
</div>
|
||||
<input type="text" class="form-control form-control-lg" id="global-search-value">
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<ul class="list-group col-12 invisible" id="global-search-results"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Reference in New Issue