Added support for returning customers

react_convert
Mark Moffat 2018-01-21 22:20:33 +01:00
parent 3730f84796
commit 6076455d06
13 changed files with 627 additions and 68 deletions

1
app.js
View File

@ -288,6 +288,7 @@ MongoClient.connect(config.databaseConnectionString, {}, (err, client) => {
db.orders = db.collection('orders'); db.orders = db.collection('orders');
db.pages = db.collection('pages'); db.pages = db.collection('pages');
db.menu = db.collection('menu'); db.menu = db.collection('menu');
db.customers = db.collection('customers');
// add db to app for routes // add db to app for routes
app.db = db; app.db = db;

View File

@ -32,6 +32,7 @@
"nodemailer": "^4.4.1", "nodemailer": "^4.4.1",
"numeral": "^2.0.6", "numeral": "^2.0.6",
"paypal-rest-sdk": "^1.6.9", "paypal-rest-sdk": "^1.6.9",
"rand-token": "^0.4.0",
"rimraf": "^2.6.2", "rimraf": "^2.6.2",
"sitemap": "^1.6.0", "sitemap": "^1.6.0",
"stripe": "^5.4.0", "stripe": "^5.4.0",

View File

@ -294,6 +294,79 @@ $(document).ready(function (){
} }
}); });
$('#customerLogout').on('click', function(e){
$.ajax({
method: 'POST',
url: '/customer/logout',
data: {}
})
.done(function(msg){
location.reload();
});
});
$('#createCustomerAccount').validator().on('click', function(e){
e.preventDefault();
if($('#shipping-form').validator('validate').has('.has-error').length === 0){
$.ajax({
method: 'POST',
url: '/customer/create',
data: {
email: $('#shipEmail').val(),
firstName: $('#shipFirstname').val(),
lastName: $('#shipLastname').val(),
address1: $('#shipAddr1').val(),
address2: $('#shipAddr2').val(),
country: $('#shipCountry').val(),
state: $('#shipState').val(),
postcode: $('#shipPostcode').val(),
phone: $('#shipPhoneNumber').val(),
password: $('#newCustomerPassword').val()
}
})
.done(function(msg){
// Just reload to fill in the form from session
location.reload();
})
.fail(function(msg){
showNotification(msg.responseJSON.err, 'danger');
});
}
});
// call update settings API
$('#customerLogin').on('click', function(e){
if(!e.isDefaultPrevented()){
e.preventDefault();
$.ajax({
method: 'POST',
url: '/customer/login_action',
data: {
loginEmail: $('#customerLoginEmail').val(),
loginPassword: $('#customerLoginPassword').val()
}
})
.done(function(msg){
var customer = msg.customer;
// Fill in customer form
$('#shipEmail').val(customer.email);
$('#shipFirstname').val(customer.firstName);
$('#shipLastname').val(customer.lastName);
$('#shipAddr1').val(customer.address1);
$('#shipAddr2').val(customer.address2);
$('#shipCountry').val(customer.country);
$('#shipState').val(customer.state);
$('#shipPostcode').val(customer.postcode);
$('#shipPhoneNumber').val(customer.phone);
location.reload();
})
.fail(function(msg){
showNotification(msg.responseJSON.err, 'danger');
});
}
e.preventDefault();
});
$(document).on('click', '.image-next', function(e){ $(document).on('click', '.image-next', function(e){
var thumbnails = $('.thumbnail-image'); var thumbnails = $('.thumbnail-image');
var index = 0; var index = 0;
@ -473,7 +546,7 @@ $(document).ready(function (){
} }
}); });
// applies an product filter // applies an product filter
$(document).on('click', '#btn_product_filter', function(e){ $(document).on('click', '#btn_product_filter', function(e){
if($('#product_filter').val() !== ''){ if($('#product_filter').val() !== ''){
window.location.href = '/admin/products/filter/' + $('#product_filter').val(); window.location.href = '/admin/products/filter/' + $('#product_filter').val();
@ -491,22 +564,21 @@ $(document).ready(function (){
} }
}); });
// applies an product filter
$(document).on('click', '#btn_customer_filter', function(e){
if($('#customer_filter').val() !== ''){
window.location.href = '/admin/customers/filter/' + $('#customer_filter').val();
}else{
showNotification('Please enter a keyword to filter', 'danger');
}
});
// resets the order filter // resets the order filter
$(document).on('click', '#btn_search_reset', function(e){ $(document).on('click', '#btn_search_reset', function(e){
window.location.replace('/'); window.location.replace('/');
}); });
// resets the product filter // search button click event
$(document).on('click', '#btn_product_reset', function(e){
window.location.href = '/admin/products';
});
// resets the order filter
$(document).on('click', '#btn_order_reset', function(e){
window.location.href = '/admin/orders';
});
// search button click event
$(document).on('click', '#btn_search', function(e){ $(document).on('click', '#btn_search', function(e){
e.preventDefault(); e.preventDefault();
if($('#frm_search').val().trim() === ''){ if($('#frm_search').val().trim() === ''){

File diff suppressed because one or more lines are too long

View File

@ -6,59 +6,93 @@
<div class="col-md-5"> <div class="col-md-5">
<div class="panel panel-default" style="margin-top: 30px;"> <div class="panel panel-default" style="margin-top: 30px;">
<div class="panel-heading">Customer details</div> <div class="panel-heading">Customer details</div>
{{#unless session.customer}}
<div class="panel-body customer-details-login">
<p>Existing customer</p>
<div class="form-group">
<input type="email" class="form-control" id="customerLoginEmail" name="loginEmail" minlength="5" placeholder="Email address" required>
</div>
<div class="form-group">
<input type="password" class="form-control" id="customerLoginPassword" name="loginPassword" minlength="5" placeholder="Password" required>
</div>
<div class="form-group">
<a href="/customer/forgotten" class="btn btn-default pull-left">Forgotten</a>
</div>
<div class="form-group">
<button id="customerLogin" class="btn btn-success pull-right" type="submit">Login</button>
</div>
</div>
{{/unless}}
<div class="panel-body customer-details"> <div class="panel-body customer-details">
{{#if session.customer}}
<div class="col-xs-12 col-md-12">
<button id="customerLogout" class="btn btn-sm btn-success pull-right">Change customer</button>
</div>
{{/if}}
<form id="shipping-form" class="shipping-form" action="/{{config.paymentGateway}}/checkout_action" method="post" role="form" data-toggle="validator" novalidate="false"> <form id="shipping-form" class="shipping-form" action="/{{config.paymentGateway}}/checkout_action" method="post" role="form" data-toggle="validator" novalidate="false">
<div class="col-xs-12 col-md-12"> <div class="col-xs-12 col-md-12">
<div class="form-group"> <div class="form-group">
<input type="email" class="form-control customerDetails" id="shipEmail" name="shipEmail" minlength="5" placeholder="Email address" required> <input type="email" class="form-control customerDetails" id="shipEmail" name="shipEmail" minlength="5" placeholder="Email address" value="{{session.customer.email}}" required>
</div> </div>
</div> </div>
<div class="col-xs-12 col-md-6"> <div class="col-xs-12 col-md-6">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control customerDetails" id="shipFirstname" name="shipFirstname" placeholder="First name" required> <input type="text" class="form-control customerDetails" id="shipFirstname" name="shipFirstname" placeholder="First name" value="{{session.customer.firstName}}" required>
</div> </div>
</div> </div>
<div class="col-xs-12 col-md-6"> <div class="col-xs-12 col-md-6">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control customerDetails" id="shipLastname" name="shipLastname" placeholder="Last name" required> <input type="text" class="form-control customerDetails" id="shipLastname" name="shipLastname" placeholder="Last name" value="{{session.customer.lastName}}" required>
</div> </div>
</div> </div>
<div class="col-xs-12 col-md-12"> <div class="col-xs-12 col-md-12">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control customerDetails" id="shipAddr1" name="shipAddr1" placeholder="Address 1" required> <input type="text" class="form-control customerDetails" id="shipAddr1" name="shipAddr1" placeholder="Address 1" value="{{session.customer.address1}}" required>
</div> </div>
</div> </div>
<div class="col-xs-12 col-md-12"> <div class="col-xs-12 col-md-12">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control customerDetails" id="shipAddr2" name="shipAddr2" placeholder="Address 2 (optional)"> <input type="text" class="form-control customerDetails" id="shipAddr2" name="shipAddr2" placeholder="Address 2 (optional)" value="{{session.customer.address1}}">
</div> </div>
</div> </div>
<div class="col-xs-12 col-md-4"> <div class="col-xs-12 col-md-4">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control customerDetails" id="shipCountry" name="shipCountry" placeholder="Country" required> <input type="text" class="form-control customerDetails" id="shipCountry" name="shipCountry" placeholder="Country" value="{{session.customer.country}}" required>
</div> </div>
</div> </div>
<div class="col-xs-12 col-md-4"> <div class="col-xs-12 col-md-4">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control customerDetails" id="shipState" name="shipState" placeholder="State" required> <input type="text" class="form-control customerDetails" id="shipState" name="shipState" placeholder="State" value="{{session.customer.state}}" required>
</div> </div>
</div> </div>
<div class="col-xs-12 col-md-4"> <div class="col-xs-12 col-md-4">
<div class="form-group"> <div class="form-group">
<input type="number" class="form-control customerDetails" id="shipPostcode" name="shipPostcode" placeholder="Post code" required> <input type="text" class="form-control customerDetails" id="shipPostcode" name="shipPostcode" placeholder="Post code" value="{{session.customer.postcode}}" required>
</div> </div>
</div> </div>
<div class="col-xs-12 col-md-12"> <div class="col-xs-12 col-md-12">
<div class="form-group"> <div class="form-group">
<input type="number" class="form-control customerDetails" id="shipPhoneNumber" name="shipPhoneNumber" placeholder="Phone number (optional)"> <input type="number" class="form-control customerDetails" id="shipPhoneNumber" name="shipPhoneNumber" placeholder="Phone number" value="{{session.customer.phone}}" required>
</div> </div>
</div> </div>
{{#if session.customer}}
{{#ifCond config.paymentGateway '==' 'paypal'}} {{#ifCond config.paymentGateway '==' 'paypal'}}
<div class="paypal_button col-xs-12 col-md-12 text-center"> <div class="paypal_button col-xs-12 col-md-12 text-center">
<button id="checkout_paypal" class="btn btn-success" type="submit"><i class="fa fa-cc-paypal fa-lg" aria-hidden="true"></i> Pay with PayPal <i class="fa fa-cc-paypal fa-lg" aria-hidden="true"></i></button> <button id="checkout_paypal" class="btn btn-success" type="submit"><i class="fa fa-cc-paypal fa-lg" aria-hidden="true"></i> Pay with PayPal <i class="fa fa-cc-paypal fa-lg" aria-hidden="true"></i></button>
</div> </div>
{{/ifCond}} {{/ifCond}}
{{/if}}
{{#unless session.customer}}
<div class="col-xs-12 col-md-12">
<p class="text-muted">Enter a password to create an account for next time</p>
<div class="form-group">
<input type="password" class="form-control customerDetails" id="newCustomerPassword" name="newCustomerPassword" placeholder="Password" required>
</div>
<a id="createCustomerAccount" class="btn btn-success pull-right">Create account</a>
</div>
{{/unless}}
</form> </form>
{{#if session.customer}}
{{#ifCond config.paymentGateway '==' 'stripe'}} {{#ifCond config.paymentGateway '==' 'stripe'}}
<div class="col-xs-12 col-md-12 text-center"> <div class="col-xs-12 col-md-12 text-center">
<form method="POST" id="payment-form"> <form method="POST" id="payment-form">
@ -82,6 +116,8 @@
</form> </form>
</div> </div>
{{/ifCond}} {{/ifCond}}
{{/if}}
</div> </div>
</div> </div>
</div> </div>

View File

@ -13,7 +13,7 @@ router.get('/', common.restrict, (req, res, next) => {
// Admin section // Admin section
router.get('/orders', common.restrict, (req, res, next) => { router.get('/orders', common.restrict, (req, res, next) => {
let db = req.app.db; const db = req.app.db;
// Top 10 products // Top 10 products
common.dbQuery(db.orders, {}, {'orderDate': -1}, 10, (err, orders) => { common.dbQuery(db.orders, {}, {'orderDate': -1}, 10, (err, orders) => {
@ -35,7 +35,7 @@ router.get('/orders', common.restrict, (req, res, next) => {
// Admin section // Admin section
router.get('/orders/bystatus/:orderstatus', common.restrict, (req, res, next) => { router.get('/orders/bystatus/:orderstatus', common.restrict, (req, res, next) => {
let db = req.app.db; const db = req.app.db;
if(typeof req.params.orderstatus === 'undefined'){ if(typeof req.params.orderstatus === 'undefined'){
res.redirect('/admin/orders'); res.redirect('/admin/orders');
@ -65,7 +65,7 @@ router.get('/orders/bystatus/:orderstatus', common.restrict, (req, res, next) =>
// render the editor // render the editor
router.get('/order/view/:id', common.restrict, (req, res) => { router.get('/order/view/:id', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
db.orders.findOne({_id: common.getId(req.params.id)}, (err, result) => { db.orders.findOne({_id: common.getId(req.params.id)}, (err, result) => {
if(err){ if(err){
console.info(err.stack); console.info(err.stack);
@ -91,7 +91,7 @@ router.get('/order/view/:id', common.restrict, (req, res) => {
// Admin section // Admin section
router.get('/orders/filter/:search', common.restrict, (req, res, next) => { router.get('/orders/filter/:search', common.restrict, (req, res, next) => {
let db = req.app.db; const db = req.app.db;
let searchTerm = req.params.search; let searchTerm = req.params.search;
let ordersIndex = req.app.ordersIndex; let ordersIndex = req.app.ordersIndex;
@ -121,7 +121,7 @@ router.get('/orders/filter/:search', common.restrict, (req, res, next) => {
// order product // order product
router.get('/order/delete/:id', common.restrict, (req, res) => { router.get('/order/delete/:id', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
// remove the article // remove the article
db.orders.remove({_id: common.getId(req.params.id)}, {}, (err, numRemoved) => { db.orders.remove({_id: common.getId(req.params.id)}, {}, (err, numRemoved) => {
@ -141,7 +141,7 @@ router.get('/order/delete/:id', common.restrict, (req, res) => {
// update order status // update order status
router.post('/order/statusupdate', common.restrict, (req, res) => { router.post('/order/statusupdate', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
db.orders.update({_id: common.getId(req.body.order_id)}, {$set: {orderStatus: req.body.status}}, {multi: false}, (err, numReplaced) => { db.orders.update({_id: common.getId(req.body.order_id)}, {$set: {orderStatus: req.body.status}}, {multi: false}, (err, numReplaced) => {
if(err){ if(err){
console.info(err.stack); console.info(err.stack);
@ -152,7 +152,7 @@ router.post('/order/statusupdate', common.restrict, (req, res) => {
// Admin section // Admin section
router.get('/products', common.restrict, (req, res, next) => { router.get('/products', common.restrict, (req, res, next) => {
let db = req.app.db; const db = req.app.db;
// get the top results // get the top results
common.dbQuery(db.products, {}, {'productAddedDate': -1}, 10, (err, topResults) => { common.dbQuery(db.products, {}, {'productAddedDate': -1}, 10, (err, topResults) => {
if(err){ if(err){
@ -173,7 +173,7 @@ router.get('/products', common.restrict, (req, res, next) => {
// Admin section // Admin section
router.post('/product/addtocart', (req, res, next) => { router.post('/product/addtocart', (req, res, next) => {
let db = req.app.db; const db = req.app.db;
let productQuantity = req.body.productQuantity ? parseInt(req.body.productQuantity) : 1; let productQuantity = req.body.productQuantity ? parseInt(req.body.productQuantity) : 1;
// setup cart object if it doesn't exist // setup cart object if it doesn't exist
@ -240,7 +240,7 @@ router.post('/product/addtocart', (req, res, next) => {
// Updates a single product quantity // Updates a single product quantity
router.post('/product/updatecart', (req, res, next) => { router.post('/product/updatecart', (req, res, next) => {
let db = req.app.db; const db = req.app.db;
let cartItems = JSON.parse(req.body.items); let cartItems = JSON.parse(req.body.items);
let hasError = false; let hasError = false;
@ -310,7 +310,7 @@ router.post('/product/emptycart', (req, res, next) => {
// Admin section // Admin section
router.get('/products/filter/:search', common.restrict, (req, res, next) => { router.get('/products/filter/:search', common.restrict, (req, res, next) => {
let db = req.app.db; const db = req.app.db;
let searchTerm = req.params.search; let searchTerm = req.params.search;
let productsIndex = req.app.productsIndex; let productsIndex = req.app.productsIndex;
@ -358,7 +358,7 @@ router.get('/product/new', common.restrict, (req, res) => {
// insert new product form action // insert new product form action
router.post('/product/insert', common.restrict, (req, res) => { router.post('/product/insert', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
let doc = { let doc = {
productPermalink: req.body.frmProductPermalink, productPermalink: req.body.frmProductPermalink,
@ -429,7 +429,7 @@ router.post('/product/insert', common.restrict, (req, res) => {
// render the editor // render the editor
router.get('/product/edit/:id', common.restrict, (req, res) => { router.get('/product/edit/:id', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
common.getImages(req.params.id, req, res, (images) => { common.getImages(req.params.id, req, res, (images) => {
db.products.findOne({_id: common.getId(req.params.id)}, (err, result) => { db.products.findOne({_id: common.getId(req.params.id)}, (err, result) => {
@ -460,7 +460,7 @@ router.get('/product/edit/:id', common.restrict, (req, res) => {
// Update an existing product form action // Update an existing product form action
router.post('/product/update', common.restrict, (req, res) => { router.post('/product/update', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
db.products.findOne({_id: common.getId(req.body.frmProductId)}, (err, product) => { db.products.findOne({_id: common.getId(req.body.frmProductId)}, (err, product) => {
if(err){ if(err){
@ -540,7 +540,7 @@ router.post('/product/update', common.restrict, (req, res) => {
// delete product // delete product
router.get('/product/delete/:id', common.restrict, (req, res) => { router.get('/product/delete/:id', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
let rimraf = require('rimraf'); let rimraf = require('rimraf');
// remove the article // remove the article
@ -568,7 +568,7 @@ router.get('/product/delete/:id', common.restrict, (req, res) => {
// users // users
router.get('/users', common.restrict, (req, res) => { router.get('/users', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
db.users.find({}).toArray((err, users) => { db.users.find({}).toArray((err, users) => {
if(err){ if(err){
console.info(err.stack); console.info(err.stack);
@ -589,7 +589,7 @@ router.get('/users', common.restrict, (req, res) => {
// edit user // edit user
router.get('/user/edit/:id', common.restrict, (req, res) => { router.get('/user/edit/:id', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
db.users.findOne({_id: common.getId(req.params.id)}, (err, user) => { db.users.findOne({_id: common.getId(req.params.id)}, (err, user) => {
if(err){ if(err){
console.info(err.stack); console.info(err.stack);
@ -618,7 +618,7 @@ router.get('/user/edit/:id', common.restrict, (req, res) => {
// update a user // update a user
router.post('/user/update', common.restrict, (req, res) => { router.post('/user/update', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
let bcrypt = req.bcrypt; let bcrypt = req.bcrypt;
let isAdmin = req.body.user_admin === 'on' ? 'true' : 'false'; let isAdmin = req.body.user_admin === 'on' ? 'true' : 'false';
@ -666,7 +666,7 @@ router.post('/user/update', common.restrict, (req, res) => {
// insert a user // insert a user
router.post('/setup_action', (req, res) => { router.post('/setup_action', (req, res) => {
let db = req.app.db; const db = req.app.db;
let bcrypt = req.bcrypt; let bcrypt = req.bcrypt;
let doc = { let doc = {
@ -704,7 +704,7 @@ router.post('/setup_action', (req, res) => {
// insert a user // insert a user
router.post('/user/insert', common.restrict, (req, res) => { router.post('/user/insert', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
let bcrypt = req.bcrypt; let bcrypt = req.bcrypt;
let url = require('url'); let url = require('url');
@ -765,6 +765,79 @@ router.post('/user/insert', common.restrict, (req, res) => {
}); });
}); });
// render the customer view
router.get('/customer/view/:id?', common.restrict, (req, res) => {
const db = req.app.db;
console.log('here');
db.customers.findOne({_id: common.getId(req.params.id)}, (err, result) => {
if(err){
console.info(err.stack);
}
res.render('customer', {
title: 'View customer',
result: result,
admin: true,
session: req.session,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
config: common.getConfig(),
editor: true,
helpers: req.handlebars.helpers
});
});
});
// customers list
router.get('/customers', common.restrict, (req, res) => {
const db = req.app.db;
db.customers.find({}).limit(20).sort({created: -1}).toArray((err, customers) => {
res.render('customers', {
title: 'Customers - List',
admin: true,
customers: customers,
session: req.session,
helpers: req.handlebars.helpers,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
config: common.getConfig()
});
});
});
// Filtered customers list
router.get('/customers/filter/:search', common.restrict, (req, res, next) => {
const db = req.app.db;
let searchTerm = req.params.search;
let customersIndex = req.app.customersIndex;
let lunrIdArray = [];
customersIndex.search(searchTerm).forEach((id) => {
lunrIdArray.push(common.getId(id.ref));
});
// we search on the lunr indexes
db.customers.find({_id: {$in: lunrIdArray}}).sort({created: -1}).toArray((err, customers) => {
if(err){
console.error(colors.red('Error searching', err));
}
res.render('customers', {
title: 'Customer results',
customers: customers,
admin: true,
config: common.getConfig(),
session: req.session,
searchTerm: searchTerm,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
helpers: req.handlebars.helpers
});
});
});
// users new // users new
router.get('/user/new', common.restrict, (req, res) => { router.get('/user/new', common.restrict, (req, res) => {
res.render('user_new', { res.render('user_new', {
@ -780,7 +853,7 @@ router.get('/user/new', common.restrict, (req, res) => {
// delete user // delete user
router.get('/user/delete/:id', common.restrict, (req, res) => { router.get('/user/delete/:id', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
if(req.session.isAdmin === 'true'){ if(req.session.isAdmin === 'true'){
db.users.remove({_id: common.getId(req.params.id)}, {}, (err, numRemoved) => { db.users.remove({_id: common.getId(req.params.id)}, {}, (err, numRemoved) => {
if(err){ if(err){
@ -825,7 +898,7 @@ router.post('/settings/update', common.restrict, (req, res) => {
// settings update // settings update
router.post('/settings/option/remove', common.restrict, (req, res) => { router.post('/settings/option/remove', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
db.products.findOne({_id: common.getId(req.body.productId)}, (err, product) => { db.products.findOne({_id: common.getId(req.body.productId)}, (err, product) => {
if(err){ if(err){
console.info(err.stack); console.info(err.stack);
@ -852,7 +925,7 @@ router.post('/settings/option/remove', common.restrict, (req, res) => {
// settings update // settings update
router.get('/settings/menu', common.restrict, async (req, res) => { router.get('/settings/menu', common.restrict, async (req, res) => {
let db = req.app.db; const db = req.app.db;
res.render('settings_menu', { res.render('settings_menu', {
title: 'Cart menu', title: 'Cart menu',
session: req.session, session: req.session,
@ -867,7 +940,7 @@ router.get('/settings/menu', common.restrict, async (req, res) => {
// settings page list // settings page list
router.get('/settings/pages', common.restrict, (req, res) => { router.get('/settings/pages', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
common.dbQuery(db.pages, {}, null, null, async (err, pages) => { common.dbQuery(db.pages, {}, null, null, async (err, pages) => {
if(err){ if(err){
console.info(err.stack); console.info(err.stack);
@ -889,7 +962,7 @@ router.get('/settings/pages', common.restrict, (req, res) => {
// settings pages new // settings pages new
router.get('/settings/pages/new', common.restrict, async (req, res) => { router.get('/settings/pages/new', common.restrict, async (req, res) => {
let db = req.app.db; const db = req.app.db;
res.render('settings_page_edit', { res.render('settings_page_edit', {
title: 'Static pages', title: 'Static pages',
@ -906,7 +979,7 @@ router.get('/settings/pages/new', common.restrict, async (req, res) => {
// settings pages editor // settings pages editor
router.get('/settings/pages/edit/:page', common.restrict, (req, res) => { router.get('/settings/pages/edit/:page', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
db.pages.findOne({_id: common.getId(req.params.page)}, async (err, page) => { db.pages.findOne({_id: common.getId(req.params.page)}, async (err, page) => {
if(err){ if(err){
console.info(err.stack); console.info(err.stack);
@ -942,7 +1015,7 @@ router.get('/settings/pages/edit/:page', common.restrict, (req, res) => {
// settings update page // settings update page
router.post('/settings/pages/update', common.restrict, (req, res) => { router.post('/settings/pages/update', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
let doc = { let doc = {
pageName: req.body.pageName, pageName: req.body.pageName,
@ -982,7 +1055,7 @@ router.post('/settings/pages/update', common.restrict, (req, res) => {
// settings delete page // settings delete page
router.get('/settings/pages/delete/:page', common.restrict, (req, res) => { router.get('/settings/pages/delete/:page', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
db.pages.remove({_id: common.getId(req.params.page)}, {}, (err, numRemoved) => { db.pages.remove({_id: common.getId(req.params.page)}, {}, (err, numRemoved) => {
if(err){ if(err){
req.session.message = 'Error deleting page. Please try again.'; req.session.message = 'Error deleting page. Please try again.';
@ -1040,7 +1113,7 @@ router.post('/settings/menu/save_order', common.restrict, (req, res) => {
router.post('/api/validate_permalink', (req, res) => { router.post('/api/validate_permalink', (req, res) => {
// if doc id is provided it checks for permalink in any products other that one provided, // if doc id is provided it checks for permalink in any products other that one provided,
// else it just checks for any products with that permalink // else it just checks for any products with that permalink
let db = req.app.db; const db = req.app.db;
let query = {}; let query = {};
if(typeof req.body.docId === 'undefined' || req.body.docId === ''){ if(typeof req.body.docId === 'undefined' || req.body.docId === ''){
@ -1065,7 +1138,7 @@ router.post('/api/validate_permalink', (req, res) => {
// update the published state based on an ajax call from the frontend // update the published state based on an ajax call from the frontend
router.post('/product/published_state', common.restrict, (req, res) => { router.post('/product/published_state', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
db.products.update({_id: common.getId(req.body.id)}, {$set: {productPublished: req.body.state}}, {multi: false}, (err, numReplaced) => { db.products.update({_id: common.getId(req.body.id)}, {$set: {productPublished: req.body.state}}, {multi: false}, (err, numReplaced) => {
if(err){ if(err){
@ -1081,7 +1154,7 @@ router.post('/product/published_state', common.restrict, (req, res) => {
// set as main product image // set as main product image
router.post('/product/setasmainimage', common.restrict, (req, res) => { router.post('/product/setasmainimage', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
// update the productImage to the db // update the productImage to the db
db.products.update({_id: common.getId(req.body.product_id)}, {$set: {productImage: req.body.productImage}}, {multi: false}, (err, numReplaced) => { db.products.update({_id: common.getId(req.body.product_id)}, {$set: {productImage: req.body.productImage}}, {multi: false}, (err, numReplaced) => {
@ -1095,7 +1168,7 @@ router.post('/product/setasmainimage', common.restrict, (req, res) => {
// deletes a product image // deletes a product image
router.post('/product/deleteimage', common.restrict, (req, res) => { router.post('/product/deleteimage', common.restrict, (req, res) => {
let db = req.app.db; const db = req.app.db;
let fs = require('fs'); let fs = require('fs');
let path = require('path'); let path = require('path');
@ -1136,7 +1209,7 @@ router.post('/product/deleteimage', common.restrict, (req, res) => {
let multer = require('multer'); let multer = require('multer');
let upload = multer({dest: 'public/uploads/'}); let upload = multer({dest: 'public/uploads/'});
router.post('/file/upload', common.restrict, upload.single('upload_file'), (req, res, next) => { router.post('/file/upload', common.restrict, upload.single('upload_file'), (req, res, next) => {
let db = req.app.db; const db = req.app.db;
let fs = require('fs'); let fs = require('fs');
let path = require('path'); let path = require('path');
@ -1195,6 +1268,7 @@ router.post('/file/upload', common.restrict, upload.single('upload_file'), (req,
// delete a file via ajax request // delete a file via ajax request
router.post('/testEmail', common.restrict, (req, res) => { router.post('/testEmail', common.restrict, (req, res) => {
let config = common.getConfig(); let config = common.getConfig();
// TODO: Should fix this to properly handle result
common.sendEmail(config.emailAddress, 'expressCart test email', 'Your email settings are working'); common.sendEmail(config.emailAddress, 'expressCart test email', 'Your email settings are working');
res.status(200).json('Test email sent'); res.status(200).json('Test email sent');
}); });

View File

@ -2,6 +2,7 @@ const express = require('express');
const router = express.Router(); const router = express.Router();
const colors = require('colors'); const colors = require('colors');
const _ = require('lodash'); const _ = require('lodash');
const randtoken = require('rand-token');
const common = require('./common'); const common = require('./common');
router.get('/payment/:orderId', async (req, res, next) => { router.get('/payment/:orderId', async (req, res, next) => {
@ -236,6 +237,211 @@ router.post('/login_action', (req, res) => {
}); });
}); });
// insert a customer
router.post('/customer/create', (req, res) => {
const db = req.app.db;
const bcrypt = req.bcrypt;
let doc = {
email: req.body.email,
firstName: req.body.firstName,
lastName: req.body.lastName,
address1: req.body.address1,
address2: req.body.address2,
country: req.body.country,
state: req.body.state,
postcode: req.body.postcode,
phone: req.body.phone,
password: bcrypt.hashSync(req.body.password),
created: new Date()
};
// check for existing customer
db.customers.findOne({email: req.body.email}, (err, customer) => {
if(customer){
res.status(404).json({
err: 'A customer already exists with that email address'
});
return;
}
// email is ok to be used.
db.customers.insertOne(doc, (err, newCustomer) => {
if(err){
if(newCustomer){
console.error(colors.red('Failed to insert customer: ' + err));
res.status(400).json({
err: 'A customer already exists with that email address'
});
return;
}
console.error(colors.red('Failed to insert customer: ' + err));
res.status(400).json({
err: 'Customer creation failed.'
});
return;
}
// Customer creation successful
req.session.customer = newCustomer.ops[0];
res.status(200).json({
message: 'Successfully logged in',
customer: newCustomer
});
});
});
});
// login the customer and check the password
router.post('/customer/login_action', (req, res) => {
let db = req.app.db;
let bcrypt = req.bcrypt;
db.customers.findOne({email: req.body.loginEmail}, (err, customer) => {
if(err){
// An error accurred
return res.status(400).json({
err: 'Access denied. Check password and try again.'
});
}
// check if customer exists with that email
if(customer === undefined || customer === null){
return res.status(400).json({
err: 'A customer with that email does not exist.'
});
}
// we have a customer under that email so we compare the password
if(bcrypt.compareSync(req.body.loginPassword, customer.password) === false){
// password is not correct
return res.status(400).json({
err: 'Access denied. Check password and try again.'
});
}
// Customer login successful
req.session.customer = customer;
return res.status(200).json({
message: 'Successfully logged in',
customer: customer
});
});
});
// customer forgotten password
router.get('/customer/forgotten', (req, res) => {
res.render('forgotten', {
title: 'Forgotten',
route: 'customer',
forgotType: 'customer',
config: common.getConfig(),
helpers: req.handlebars.helpers,
message: common.clearSessionValue(req.session, 'message'),
messageType: common.clearSessionValue(req.session, 'messageType'),
showFooter: 'showFooter'
});
});
// forgotten password
router.post('/customer/forgotten_action', (req, res) => {
const db = req.app.db;
const config = common.getConfig();
let passwordToken = randtoken.generate(30);
// find the user
db.customers.findOne({email: req.body.email}, (err, customer) => {
// if we have a customer, set a token, expiry and email it
if(customer){
let tokenExpiry = Date.now() + 3600000;
db.customers.update({email: req.body.email}, {$set: {resetToken: passwordToken, resetTokenExpiry: tokenExpiry}}, {multi: false}, (err, numReplaced) => {
// send forgotten password email
let mailOpts = {
to: req.body.email,
subject: 'Forgotten password request',
body: `You are receiving this because you (or someone else) have requested the reset of the password for your user account.\n\n
Please click on the following link, or paste this into your browser to complete the process:\n\n
${config.baseUrl}/customer/reset/${passwordToken}\n\n
If you did not request this, please ignore this email and your password will remain unchanged.\n`
};
// send the email with token to the user
// TODO: Should fix this to properly handle result
common.sendEmail(mailOpts.to, mailOpts.subject, mailOpts.body);
req.session.message = 'An email has been sent to ' + req.body.email + ' with further instructions';
req.session.message_type = 'success';
return res.redirect('/customer/forgotten');
});
}else{
req.session.message = 'Account does not exist';
res.redirect('/customer/forgotten');
}
});
});
// reset password form
router.get('/customer/reset/:token', (req, res) => {
const db = req.app.db;
// Find the customer using the token
db.customers.findOne({resetToken: req.params.token, resetTokenExpiry: {$gt: Date.now()}}, (err, customer) => {
if(!customer){
req.session.message = 'Password reset token is invalid or has expired';
req.session.message_type = 'danger';
res.redirect('/forgot');
return;
}
// show the password reset form
res.render('reset', {
title: 'Reset password',
token: req.params.token,
route: 'customer',
config: common.getConfig(),
message: common.clearSessionValue(req.session, 'message'),
message_type: common.clearSessionValue(req.session, 'message_type'),
show_footer: 'show_footer',
helpers: req.handlebars.helpers
});
});
});
// reset password action
router.post('/customer/reset/:token', (req, res) => {
const db = req.app.db;
let bcrypt = req.bcrypt;
// get the customer
db.customers.findOne({resetToken: req.params.token, resetTokenExpiry: {$gt: Date.now()}}, (err, customer) => {
if(!customer){
req.session.message = 'Password reset token is invalid or has expired';
req.session.message_type = 'danger';
return res.redirect('/forgot');
}
// update the password and remove the token
let newPassword = bcrypt.hashSync(req.body.password);
db.customers.update({email: customer.email}, {$set: {password: newPassword, resetToken: undefined, resetTokenExpiry: undefined}}, {multi: false}, (err, numReplaced) => {
let mailOpts = {
to: customer.email,
subject: 'Password successfully reset',
body: 'This is a confirmation that the password for your account ' + customer.email + ' has just been changed successfully.\n'
};
// TODO: Should fix this to properly handle result
common.sendEmail(mailOpts.to, mailOpts.subject, mailOpts.body);
req.session.message = 'Password successfully updated';
req.session.message_type = 'success';
return res.redirect('/pay');
});
return'';
});
});
// logout the customer
router.post('/customer/logout', (req, res) => {
req.session.customer = null;
res.status(200).json({});
});
// search products // search products
router.get('/search/:searchTerm/:pageNum?', (req, res) => { router.get('/search/:searchTerm/:pageNum?', (req, res) => {
let db = req.app.db; let db = req.app.db;

49
views/customer.hbs Normal file
View File

@ -0,0 +1,49 @@
{{> menu}}
<div class="col-lg-9">
<div class="row">
<div class="col-lg-12">
<h2>Customer</h2>
</div>
<div class="col-xs-12 col-md-12">
<table class="table-bordered">
<tr>
<td><span class="text-info">Email:</span></td>
<td>{{result.email}}</td>
</tr>
<tr>
<td><span class="text-info">Name:</span></td>
<td>{{result.firstName}} {{result.lastName}}</td>
</tr>
<tr>
<td><span class="text-info">Address 1:</span></td>
<td>{{result.address1}}</td>
</tr>
<tr>
<td><span class="text-info">Address 2:</span></td>
<td>{{result.address2}}</td>
</tr>
<tr>
<td><span class="text-info">Country:</span></td>
<td>{{result.country}}</td>
</tr>
<tr>
<td><span class="text-info">State:</span></td>
<td>{{result.state}}</td>
</tr>
<tr>
<td><span class="text-info">Postcode:</span></td>
<td>{{result.postcode}}</td>
</tr>
<tr>
<td><span class="text-info">Phone number:</span></td>
<td>{{result.phone}}</td>
</tr>
<tr>
<td><span class="text-info">Creation date:</span></td>
<td>{{formatDate result.created "DD/MM/YYYY hh:mmA"}}</td>
</tr>
</table>
</div>
</div>
<input type="hidden" id="productId" value="{{result._id}}">
</div>

64
views/customers.hbs Normal file
View File

@ -0,0 +1,64 @@
{{> menu}}
<div class="col-lg-9">
<div class="col-lg-12">
<h2>Customers</h2>
</div>
<div class="col-lg-12 search_bar">
<div class="input-group">
<input type="text" name="customer_filter" id="customer_filter" class="form-control input-lg" placeholder="Filter customers">
<span class="input-group-btn">
<button class="btn btn-success btn-lg" id="btn_customer_filter">Filter</button>
<a href="/admin/customers" class="hidden-xs btn btn-warning btn-lg"><i class="fa fa-times" aria-hidden="true"></i></a>
</span>
</div>
<p class="text-warning top-pad-10">Customers can be filtered by: email, name or phone numnber</p>
</div>
{{#if customers}}
<div class="col-lg-12">
<ul class="list-group">
<li class="list-group-item">
<strong>
Customers
{{#if searchTerm}}
- <span class="text-danger">Filtered term: {{searchTerm}} </span>
{{/if}}
</strong>
</li>
<li class="list-group-item">
<div class="row">
<div class="col-md-4">
<h5 class="text-info"><strong>Email address</strong></h5>
</div>
<div class="col-md-4">
<h5 class="text-info"><strong>Name</strong></h5>
</div>
<div class="col-md-4">
<h5 class="text-info"><strong>Phone number</strong></h5>
</div>
</div>
</li>
{{#each customers}}
<li class="list-group-item">
<div class="row">
<a href="/admin/customer/view/{{this._id}}">
<div class="col-md-4">
<h5 class="">{{this.email}}</h5>
</div>
<div class="col-md-4">
<h5 class="">{{this.firstName}} {{this.lastName}}</h5>
</div>
<div class="col-md-4">
<h5 class="">{{this.phone}}</h5>
</div>
</a>
</div>
</li>
{{/each}}
</ul>
</div>
{{else}}
<h4 class="text-center">
No orders found
</h4>
{{/if}}
</div>

9
views/forgotten.hbs Normal file
View File

@ -0,0 +1,9 @@
<div class="col-md-offset-4 col-md-4 col-lg-offset-4 col-lg-4" style="padding-top: 100px" >
<form class="form-signin" action="/{{route}}/forgotten_action" method="post" role="form" data-toggle="validator">
<h2 class="form-signin-heading">Please enter your email address</h2>
<div class="form-group">
<input type="email" name="email" class="form-control" placeholder="email address" required autofocus>
</div>
<button class="btn btn-lg btn-primary btn-block" type="submit">Reset</button>
</form>
</div>

View File

@ -6,6 +6,8 @@
<li class="list-group-item"><i class="fa fa-list fa-icon" aria-hidden="true"></i> &nbsp; <a href="/admin/products">List</a></li> <li class="list-group-item"><i class="fa fa-list fa-icon" aria-hidden="true"></i> &nbsp; <a href="/admin/products">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> &nbsp; <a href="/admin/orders">List</a></li> <li class="list-group-item"><i class="fa fa-cube fa-icon" aria-hidden="true"></i> &nbsp; <a href="/admin/orders">List</a></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> &nbsp; <a href="/admin/customers">List</a></li>
<li class="list-group-item"><strong>Users</strong></li> <li class="list-group-item"><strong>Users</strong></li>
<li class="list-group-item"><i class="fa fa-user-plus fa-icon" aria-hidden="true"></i> &nbsp; <a href="/admin/user/new">New</a></li> <li class="list-group-item"><i class="fa fa-user-plus fa-icon" aria-hidden="true"></i> &nbsp; <a href="/admin/user/new">New</a></li>
<li class="list-group-item"><i class="fa fa-user fa-icon" aria-hidden="true"></i> &nbsp; <a href="/admin/users">Edit</a></li> <li class="list-group-item"><i class="fa fa-user fa-icon" aria-hidden="true"></i> &nbsp; <a href="/admin/users">Edit</a></li>

View File

@ -6,64 +6,93 @@
<div class="col-md-5"> <div class="col-md-5">
<div class="panel panel-default" style="margin-top: 30px;"> <div class="panel panel-default" style="margin-top: 30px;">
<div class="panel-heading">Customer details</div> <div class="panel-heading">Customer details</div>
{{#unless session.customer}}
<div class="panel-body customer-details-login">
<p>Existing customer</p>
<div class="form-group">
<input type="email" class="form-control" id="customerLoginEmail" name="loginEmail" minlength="5" placeholder="Email address" required>
</div>
<div class="form-group">
<input type="password" class="form-control" id="customerLoginPassword" name="loginPassword" minlength="5" placeholder="Password" required>
</div>
<div class="form-group">
<a href="/customer/forgotten" class="btn btn-default pull-left">Forgotten</a>
</div>
<div class="form-group">
<button id="customerLogin" class="btn btn-success pull-right" type="submit">Login</button>
</div>
</div>
{{/unless}}
<div class="panel-body customer-details"> <div class="panel-body customer-details">
{{#if session.customer}}
<div class="col-xs-12 col-md-12">
<button id="customerLogout" class="btn btn-sm btn-success pull-right">Change customer</button>
</div>
{{/if}}
<form id="shipping-form" class="shipping-form" action="/{{config.paymentGateway}}/checkout_action" method="post" role="form" data-toggle="validator" novalidate="false"> <form id="shipping-form" class="shipping-form" action="/{{config.paymentGateway}}/checkout_action" method="post" role="form" data-toggle="validator" novalidate="false">
<div class="col-xs-12 col-md-12"> <div class="col-xs-12 col-md-12">
<div class="form-group"> <div class="form-group">
<input type="email" class="form-control customerDetails" id="shipEmail" name="shipEmail" minlength="5" placeholder="Email address" required> <input type="email" class="form-control customerDetails" id="shipEmail" name="shipEmail" minlength="5" placeholder="Email address" value="{{session.customer.email}}" required>
</div> </div>
</div> </div>
<div class="col-xs-12 col-md-6"> <div class="col-xs-12 col-md-6">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control customerDetails" id="shipFirstname" name="shipFirstname" placeholder="First name" required> <input type="text" class="form-control customerDetails" id="shipFirstname" name="shipFirstname" placeholder="First name" value="{{session.customer.firstName}}" required>
</div> </div>
</div> </div>
<div class="col-xs-12 col-md-6"> <div class="col-xs-12 col-md-6">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control customerDetails" id="shipLastname" name="shipLastname" placeholder="Last name" required> <input type="text" class="form-control customerDetails" id="shipLastname" name="shipLastname" placeholder="Last name" value="{{session.customer.lastName}}" required>
</div> </div>
</div> </div>
<div class="col-xs-12 col-md-12"> <div class="col-xs-12 col-md-12">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control customerDetails" id="shipAddr1" name="shipAddr1" placeholder="Address 1" required> <input type="text" class="form-control customerDetails" id="shipAddr1" name="shipAddr1" placeholder="Address 1" value="{{session.customer.address1}}" required>
</div> </div>
</div> </div>
<div class="col-xs-12 col-md-12"> <div class="col-xs-12 col-md-12">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control customerDetails" id="shipAddr2" name="shipAddr2" placeholder="Address 2 (optional)"> <input type="text" class="form-control customerDetails" id="shipAddr2" name="shipAddr2" placeholder="Address 2 (optional)" value="{{session.customer.address1}}">
</div> </div>
</div> </div>
<div class="col-xs-12 col-md-4"> <div class="col-xs-12 col-md-4">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control customerDetails" id="shipCountry" name="shipCountry" placeholder="Country" required> <input type="text" class="form-control customerDetails" id="shipCountry" name="shipCountry" placeholder="Country" value="{{session.customer.country}}" required>
</div> </div>
</div> </div>
<div class="col-xs-12 col-md-4"> <div class="col-xs-12 col-md-4">
<div class="form-group"> <div class="form-group">
<input type="text" class="form-control customerDetails" id="shipState" name="shipState" placeholder="State" required> <input type="text" class="form-control customerDetails" id="shipState" name="shipState" placeholder="State" value="{{session.customer.state}}" required>
</div> </div>
</div> </div>
<div class="col-xs-12 col-md-4"> <div class="col-xs-12 col-md-4">
<div class="form-group"> <div class="form-group">
<input type="number" class="form-control customerDetails" id="shipPostcode" name="shipPostcode" placeholder="Post code" required> <input type="text" class="form-control customerDetails" id="shipPostcode" name="shipPostcode" placeholder="Post code" value="{{session.customer.postcode}}" required>
</div> </div>
</div> </div>
<div class="col-xs-12 col-md-12"> <div class="col-xs-12 col-md-12">
<div class="form-group"> <div class="form-group">
<input type="number" class="form-control customerDetails" id="shipPostcode" name="shipPostcode" placeholder="Post code" required> <input type="number" class="form-control customerDetails" id="shipPhoneNumber" name="shipPhoneNumber" placeholder="Phone number" value="{{session.customer.phone}}" required>
</div>
</div>
<div class="col-xs-12 col-md-12">
<div class="form-group">
<input type="number" class="form-control customerDetails" id="shipPhoneNumber" name="shipPhoneNumber" placeholder="Phone number (optional)">
</div> </div>
</div> </div>
{{#if session.customer}}
{{#ifCond config.paymentGateway '==' 'paypal'}} {{#ifCond config.paymentGateway '==' 'paypal'}}
<div class="paypal_button col-xs-12 col-md-12 text-center"> <div class="paypal_button col-xs-12 col-md-12 text-center">
<button id="checkout_paypal" class="btn btn-success" type="submit"><i class="fa fa-cc-paypal fa-lg" aria-hidden="true"></i> Pay with PayPal <i class="fa fa-cc-paypal fa-lg" aria-hidden="true"></i></button> <button id="checkout_paypal" class="btn btn-success" type="submit"><i class="fa fa-cc-paypal fa-lg" aria-hidden="true"></i> Pay with PayPal <i class="fa fa-cc-paypal fa-lg" aria-hidden="true"></i></button>
</div> </div>
{{/ifCond}} {{/ifCond}}
{{/if}}
{{#unless session.customer}}
<div class="col-xs-12 col-md-12">
<p class="text-muted">Enter a password to create an account for next time</p>
<div class="form-group">
<input type="password" class="form-control customerDetails" id="newCustomerPassword" name="newCustomerPassword" placeholder="Password" required>
</div>
<a id="createCustomerAccount" class="btn btn-success pull-right">Create account</a>
</div>
{{/unless}}
</form> </form>
{{#if session.customer}}
{{#ifCond config.paymentGateway '==' 'stripe'}} {{#ifCond config.paymentGateway '==' 'stripe'}}
<div class="col-xs-12 col-md-12 text-center"> <div class="col-xs-12 col-md-12 text-center">
<form method="POST" id="payment-form"> <form method="POST" id="payment-form">
@ -87,6 +116,7 @@
</form> </form>
</div> </div>
{{/ifCond}} {{/ifCond}}
{{/if}}
</div> </div>
</div> </div>
</div> </div>

15
views/reset.hbs Normal file
View File

@ -0,0 +1,15 @@
<div class="col-md-offset-4 col-md-4 col-lg-offset-4 col-lg-4">
<form class="form-signin" action="/{{route}}/reset/{{token}}" method="post" role="form" data-toggle="validator">
<h2 class="form-signin-heading">Password reset</h2>
<div class="form-group">
<label for="user_password">Password *</label>
<input type="password" class="form-control" id="user_password" name="password" placeholder="Password" required>
</div>
<div class="form-group">
<label for="frm_user_password_confirm">Confirm password *</label>
<input type="password" data-match="#user_password" placeholder="Confirm password" class="form-control" name="frm_user_password_confirm" required>
</div>
<button class="btn btn-lg btn-success btn-block" type="submit">Update password</button>
</br>
</form>
</div>