Added a dashboard
parent
7a1cb87e2a
commit
7cef58dae3
|
@ -116,6 +116,7 @@ const updateTotalCart = (req, res) => {
|
||||||
|
|
||||||
req.session.totalCartAmount = 0;
|
req.session.totalCartAmount = 0;
|
||||||
req.session.totalCartItems = 0;
|
req.session.totalCartItems = 0;
|
||||||
|
req.session.totalCartProducts = 0;
|
||||||
|
|
||||||
// If cart is empty return zero values
|
// If cart is empty return zero values
|
||||||
if(!req.session.cart){
|
if(!req.session.cart){
|
||||||
|
@ -124,6 +125,7 @@ const updateTotalCart = (req, res) => {
|
||||||
|
|
||||||
Object.keys(req.session.cart).forEach((item) => {
|
Object.keys(req.session.cart).forEach((item) => {
|
||||||
req.session.totalCartAmount = req.session.totalCartAmount + req.session.cart[item].totalItemPrice;
|
req.session.totalCartAmount = req.session.totalCartAmount + req.session.cart[item].totalItemPrice;
|
||||||
|
req.session.totalCartProducts = req.session.totalCartProducts + req.session.cart[item].quantity;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update the total items in cart for the badge
|
// Update the total items in cart for the badge
|
||||||
|
|
|
@ -171,5 +171,6 @@
|
||||||
"Shipping options": "Shipping options",
|
"Shipping options": "Shipping options",
|
||||||
"Proceed to payment": "Proceed to payment",
|
"Proceed to payment": "Proceed to payment",
|
||||||
"Return to information": "Return to information",
|
"Return to information": "Return to information",
|
||||||
"Search shop": "Search shop"
|
"Search shop": "Search shop",
|
||||||
|
"Dashboard": "Dashboard"
|
||||||
}
|
}
|
|
@ -255,6 +255,10 @@ input[type=number]::-webkit-outer-spin-button {
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bottom-pad-30{
|
||||||
|
padding-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
.bottom-marg-10{
|
.bottom-marg-10{
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,6 +198,9 @@ input[type=number]::-webkit-outer-spin-button {
|
||||||
.bottom-pad-20 {
|
.bottom-pad-20 {
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
.bottom-pad-30 {
|
||||||
|
padding-bottom: 30px;
|
||||||
|
}
|
||||||
.bottom-marg-10 {
|
.bottom-marg-10 {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -13,7 +13,7 @@ const router = express.Router();
|
||||||
|
|
||||||
// Admin section
|
// Admin section
|
||||||
router.get('/admin', restrict, (req, res, next) => {
|
router.get('/admin', restrict, (req, res, next) => {
|
||||||
res.redirect('/admin/orders');
|
res.redirect('/admin/dashboard');
|
||||||
});
|
});
|
||||||
|
|
||||||
// logout
|
// logout
|
||||||
|
@ -127,6 +127,57 @@ router.post('/admin/setup_action', async (req, res) => {
|
||||||
res.status(200).json({ message: 'Already setup.' });
|
res.status(200).json({ message: 'Already setup.' });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// dashboard
|
||||||
|
router.get('/admin/dashboard', restrict, async (req, res) => {
|
||||||
|
const db = req.app.db;
|
||||||
|
|
||||||
|
// Collate data for dashboard
|
||||||
|
const dashboardData = {
|
||||||
|
productsCount: await db.products.countDocuments({
|
||||||
|
productPublished: true
|
||||||
|
}),
|
||||||
|
ordersCount: await db.orders.countDocuments({}),
|
||||||
|
ordersAmount: await db.orders.aggregate([{ $match: {} },
|
||||||
|
{ $group: { _id: null, sum: { $sum: '$orderTotal' } }
|
||||||
|
}]).toArray(),
|
||||||
|
productsSold: await db.orders.aggregate([{ $match: {} },
|
||||||
|
{ $group: { _id: null, sum: { $sum: '$orderProductCount' } }
|
||||||
|
}]).toArray(),
|
||||||
|
topProducts: await db.orders.aggregate([
|
||||||
|
{ $project: { _id: 0 } },
|
||||||
|
{ $project: { o: { $objectToArray: '$orderProducts' } } },
|
||||||
|
{ $unwind: '$o' },
|
||||||
|
{ $group: {
|
||||||
|
_id: '$o.v.productId',
|
||||||
|
title: { $last: '$o.v.title' },
|
||||||
|
productImage: { $last: '$o.v.productImage' },
|
||||||
|
count:
|
||||||
|
{
|
||||||
|
$sum: '$o.v.quantity'
|
||||||
|
}
|
||||||
|
} },
|
||||||
|
{ $sort: { count: -1 } },
|
||||||
|
{ $limit: 5 }
|
||||||
|
]).toArray()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fix aggregate data
|
||||||
|
dashboardData.ordersAmount = dashboardData.ordersAmount[0].sum;
|
||||||
|
dashboardData.productsSold = dashboardData.productsSold[0].sum;
|
||||||
|
|
||||||
|
res.render('dashboard', {
|
||||||
|
title: 'Cart dashboard',
|
||||||
|
session: req.session,
|
||||||
|
admin: true,
|
||||||
|
dashboardData,
|
||||||
|
themes: common.getThemes(),
|
||||||
|
message: common.clearSessionValue(req.session, 'message'),
|
||||||
|
messageType: common.clearSessionValue(req.session, 'messageType'),
|
||||||
|
helpers: req.handlebars.helpers,
|
||||||
|
config: req.app.config
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
router.get('/admin/settings', restrict, (req, res) => {
|
router.get('/admin/settings', restrict, (req, res) => {
|
||||||
res.render('settings', {
|
res.render('settings', {
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
{{> partials/menu}}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-9">
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<div class="page-header">
|
|
||||||
<h2>Dashboard</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
{{> partials/menu}}
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<h2>Dashboard</h2>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6 bottom-pad-30">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<h5 class="card-title">Orders placed</h5>
|
||||||
|
<h6 class="card-subtitle mb-2 text-muted">Total number of orders placed</h6>
|
||||||
|
<h4 class="card-text text-danger">
|
||||||
|
{{dashboardData.ordersCount}}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 bottom-pad-30">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<h5 class="card-title">Order total value</h5>
|
||||||
|
<h6 class="card-subtitle mb-2 text-muted">Total value of orders placed</h6>
|
||||||
|
<h4 class="card-text text-danger">
|
||||||
|
{{currencySymbol config.currencySymbol}}{{formatAmount dashboardData.ordersAmount}}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 bottom-pad-30">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<h5 class="card-title">Products for sale</h5>
|
||||||
|
<h6 class="card-subtitle mb-2 text-muted">Number of products for sale</h6>
|
||||||
|
<h4 class="card-text text-danger">
|
||||||
|
{{dashboardData.productsCount}}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-6 bottom-pad-30">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<h5 class="card-title">Total products sold</h5>
|
||||||
|
<h6 class="card-subtitle mb-2 text-muted">Number of products sold</h6>
|
||||||
|
<h4 class="card-text text-danger">
|
||||||
|
{{dashboardData.productsSold}}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 bottom-pad-30">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body text-center">
|
||||||
|
<h5 class="card-title">Top products sold</h5>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
{{#each dashboardData.topProducts}}
|
||||||
|
<li class="media my-4 align-middle">
|
||||||
|
<img src="{{this.productImage}}" class="col-2 mr-3 img-fluid" alt="">
|
||||||
|
<div class="media-body">
|
||||||
|
<h5 class="mt-3 mb-1">{{this.title}}</h5>
|
||||||
|
<h4 class="mt-4">Sold: <span class="text-danger">{{this.count}}</span></h4>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -23,8 +23,8 @@
|
||||||
{{#if admin}}
|
{{#if admin}}
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.20.2/codemirror.min.css" />
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.20.2/codemirror.min.css" />
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.11.2/css/all.min.css" integrity="sha256-+N4/V/SbAFiW1MPBCXnfnP9QSN3+Keu+NlB+0ev/YKQ=" crossorigin="anonymous" />
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-tokenfield/0.12.0/css/bootstrap-tokenfield.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-tokenfield/0.12.0/css/bootstrap-tokenfield.min.css" integrity="sha256-4qBzeX420hElp9/FzsuqUNqVobcClz1BjnXoxUDSYQ0=" crossorigin="anonymous" />
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||||
<link rel="stylesheet" href="/stylesheets/codemirror-style.min.css">
|
<link rel="stylesheet" href="/stylesheets/codemirror-style.min.css">
|
||||||
<link rel="stylesheet" href="/stylesheets/style{{config.env}}.css">
|
<link rel="stylesheet" href="/stylesheets/style{{config.env}}.css">
|
||||||
|
@ -32,26 +32,23 @@
|
||||||
<link rel="stylesheet" href="/stylesheets/admin{{config.env}}.css">
|
<link rel="stylesheet" href="/stylesheets/admin{{config.env}}.css">
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/esm/popper.min.js" integrity="sha256-g491Yv8nsEVSfQ6aonhVVFXoX5vF2uJQIU0hVNRg4JQ=" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha256-WqU1JavFxSAMcLP2WIOI+GB2zWmShMI82mTpLDcqFUg=" crossorigin="anonymous"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha256-WqU1JavFxSAMcLP2WIOI+GB2zWmShMI82mTpLDcqFUg=" crossorigin="anonymous"></script>
|
||||||
{{#if admin}}
|
{{#if admin}}
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.20.2/codemirror.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.20.2/codemirror.min.js" integrity="sha256-K1exjHe1X4MP24jRizgBaSbUDUrNhFDRSwGoEYGmtJE=" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.20.2/mode/css/css.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.20.2/mode/css/css.min.js" integrity="sha256-D5oJ11cOmRhXSYWELwG2U/XYH3YveZJr9taRYLZ2DSM=" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.20.2/mode/xml/xml.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.20.2/mode/xml/xml.min.js" integrity="sha256-ERFGS58tayDq5kkyNwd/89iZZ+HglMH7eYXxG1hxTvA=" crossorigin="anonymous"></script>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.7/js/tether.min.js" integrity="sha256-4lietOiwRDBKx1goZZbRiwB06L+/bPYEGDIKZt82bgg=" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/1000hz-bootstrap-validator/0.11.9/validator.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/1000hz-bootstrap-validator/0.11.9/validator.min.js"></script>
|
||||||
<script src="/javascripts/jquery.bootpag.min.js"></script>
|
<script src="/javascripts/jquery.bootpag.min.js"></script>
|
||||||
<script src="/javascripts/cssbeautify.min.js"></script>
|
<script src="/javascripts/cssbeautify.min.js"></script>
|
||||||
{{#unless admin}}
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
|
|
||||||
{{/unless}}
|
|
||||||
<script src="/javascripts/common{{config.env}}.js"></script>
|
<script src="/javascripts/common{{config.env}}.js"></script>
|
||||||
<script src="/javascripts/expressCart{{config.env}}.js"></script>
|
<script src="/javascripts/expressCart{{config.env}}.js"></script>
|
||||||
{{#if admin}}
|
{{#if admin}}
|
||||||
<script src="/javascripts/admin{{config.env}}.js"></script>
|
<script src="/javascripts/admin{{config.env}}.js"></script>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-tokenfield/0.12.0/bootstrap-tokenfield.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-tokenfield/0.12.0/bootstrap-tokenfield.min.js" integrity="sha256-jdwX0QzXB7z7Xc7Vz0ovtIHWO5qIZWg0aLcGv44JDgE=" crossorigin="anonymous"></script>
|
||||||
<!-- SEO data -->
|
<!-- SEO data -->
|
||||||
<link rel="canonical" href="{{config.baseUrl}}" />
|
<link rel="canonical" href="{{config.baseUrl}}" />
|
||||||
<meta name="referrer" content="origin" />
|
<meta name="referrer" content="origin" />
|
||||||
|
|
|
@ -1,24 +1,25 @@
|
||||||
<div class="col-sm-3">
|
<div class="col-sm-3">
|
||||||
<h2> </h2>
|
<h2> </h2>
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
|
<li class="list-group-item"><i class="far fa-chart-bar fa-icon"></i> <a href="/admin/dashboard">{{ @root.__ "Dashboard" }}</a></li>
|
||||||
<li class="list-group-item"><strong>Products</strong></li>
|
<li class="list-group-item"><strong>Products</strong></li>
|
||||||
{{#ifCond session.isAdmin '===' true}}
|
{{#ifCond session.isAdmin '===' true}}
|
||||||
<li class="list-group-item"><i class="fa fa-plus-square-o fa-icon" aria-hidden="true"></i> <a href="/admin/product/new">{{ @root.__ "New" }}</a></li>
|
<li class="list-group-item"><i class="far fa-plus-square"></i> <a href="/admin/product/new">{{ @root.__ "New" }}</a></li>
|
||||||
{{/ifCond}}
|
{{/ifCond}}
|
||||||
<li class="list-group-item"><i class="fa fa-list fa-icon" aria-hidden="true"></i> <a href="/admin/products">{{ @root.__ "List" }}</a></li>
|
<li class="list-group-item"><i class="fas fa-list fa-icon"></i> <a href="/admin/products">{{ @root.__ "List" }}</a></li>
|
||||||
<li class="list-group-item"><strong>Orders</strong></li>
|
<li class="list-group-item"><strong>Orders</strong></li>
|
||||||
<li class="list-group-item"><i class="fa fa-cube fa-icon" aria-hidden="true"></i> <a href="/admin/orders">{{ @root.__ "List" }}</a></li>
|
<li class="list-group-item"><i class="fas fa-cube fa-icon"></i> <a href="/admin/orders">{{ @root.__ "List" }}</a></li>
|
||||||
<li class="list-group-item"><strong>Customers</strong></li>
|
<li class="list-group-item"><strong>Customers</strong></li>
|
||||||
<li class="list-group-item"><i class="fa fa-users fa-icon" aria-hidden="true"></i> <a href="/admin/customers">{{ @root.__ "List" }}</a></li>
|
<li class="list-group-item"><i class="fas fa-users fa-icon"></i> <a href="/admin/customers">{{ @root.__ "List" }}</a></li>
|
||||||
<li class="list-group-item"><strong>Users</strong></li>
|
<li class="list-group-item"><strong>Users</strong></li>
|
||||||
{{#ifCond session.isAdmin '===' true}}
|
{{#ifCond session.isAdmin '===' true}}
|
||||||
<li class="list-group-item"><i class="fa fa-user-plus fa-icon" aria-hidden="true"></i> <a href="/admin/user/new">{{ @root.__ "New" }}</a></li>
|
<li class="list-group-item"><i class="fas fa-user-plus fa-icon"></i> <a href="/admin/user/new">{{ @root.__ "New" }}</a></li>
|
||||||
<li class="list-group-item"><i class="fa fa-user fa-icon" aria-hidden="true"></i> <a href="/admin/users">{{ @root.__ "Edit" }}</a></li>
|
<li class="list-group-item"><i class="fas fa-user fa-icon"></i> <a href="/admin/users">{{ @root.__ "Edit" }}</a></li>
|
||||||
{{/ifCond}}
|
{{/ifCond}}
|
||||||
<li class="list-group-item"><i class="fa fa-user-circle-o fa-icon" aria-hidden="true"></i> <a href="/admin/user/edit/{{session.userId}}">My Account</a></li>
|
<li class="list-group-item"><i class="fas fa-user-circle"></i> <a href="/admin/user/edit/{{session.userId}}">My Account</a></li>
|
||||||
<li class="list-group-item"><strong>{{ @root.__ "Settings" }}</strong></li>
|
<li class="list-group-item"><strong>{{ @root.__ "Settings" }}</strong></li>
|
||||||
<li class="list-group-item"><i class="fa fa-cog fa-icon" aria-hidden="true"></i> <a href="/admin/settings">{{ @root.__ "General settings" }}</a></li>
|
<li class="list-group-item"><i class="fas fa-cog fa-icon"></i> <a href="/admin/settings">{{ @root.__ "General settings" }}</a></li>
|
||||||
<li class="list-group-item"><i class="fa fa-bars fa-icon" aria-hidden="true"></i> <a href="/admin/settings/menu">Menu</a></li>
|
<li class="list-group-item"><i class="fas fa-bars fa-icon"></i> <a href="/admin/settings/menu">Menu</a></li>
|
||||||
<li class="list-group-item"><i class="fa fa-file-o fa-icon" aria-hidden="true"></i> <a href="/admin/settings/pages">{{ @root.__ "Static pages" }}</a></li>
|
<li class="list-group-item"><i class="far fa-file fa-icon"></i> <a href="/admin/settings/pages">{{ @root.__ "Static pages" }}</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
Loading…
Reference in New Issue