diff options
40 files changed, 856 insertions, 115 deletions
diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index aecd6be..a149166 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -604,7 +604,9 @@ iframe.embed { .page h1:nth-child(2) { margin-top: 40px; } - +.page h1.leader { + margin-top: 60px; +} .page p { margin: 20px; } @@ -673,7 +675,21 @@ iframe.embed { font-weight: 300; } +.aboutintro { + text-align: center; + line-height: 43px; + font-size: 24px; + padding: 5% 0; + font-weight: 300; +} +.aboutintro .inner { + max-width: 800px; + margin: 0 auto; + text-align: center; +} + /* PROFILE PAGE */ + .profilePic { background-size: cover; background-position: center; @@ -2464,7 +2480,7 @@ form li { form label { float:left; } -form input[type="text"],form input[type="password"] { +form input[type="text"],form input[type="password"],form input[type="number"] { border: 1px solid; font-size: 20px; padding: 5px; @@ -3034,8 +3050,8 @@ a[data-role="forgot-password"] { form li { font-size: 16px; } - form input[type="text"], form input[type="password"] { - font-size: 15px; + form input[type="text"],form input[type="password"],form input[type="number"] { + font-size: 15px; } .page h1 { font-size: 26px; diff --git a/public/assets/stylesheets/staff.css b/public/assets/stylesheets/staff.css index de31571..fffadbf 100644 --- a/public/assets/stylesheets/staff.css +++ b/public/assets/stylesheets/staff.css @@ -67,10 +67,26 @@ hr { .staff .body a { border-bottom: 1px dotted; } -.staff .editLinks a { +.staff .editLinks a, .staff a.bluelink { color: #00f; border-bottom: 1px solid; } +.staff form { + max-width: none; + width: 600px; + padding: 20px; +} +.staff form label { + float: none; +} +.staff form p { + width: 350px; + margin: 5px 0; + color: #444; +} +.staff form div li { + width: 180px; +} #iframe-embed, #iframe-embed tr, #iframe-embed td { width: 79vw; } diff --git a/server/index.js b/server/index.js index 9a9323c..a14eaab 100644 --- a/server/index.js +++ b/server/index.js @@ -70,6 +70,8 @@ site.setup = function(){ app.all('*', middleware.ensureLocals); app.all('*', middleware.ensureIP); + app.get('/subscribe/webhook', views.subscription.webhook); + server = http.createServer(app) server.listen(app.get('port'), function () { console.log('Express server listening on port ' + app.get('port')); diff --git a/server/lib/middleware.js b/server/lib/middleware.js index 4848ab0..797d677 100644 --- a/server/lib/middleware.js +++ b/server/lib/middleware.js @@ -75,7 +75,7 @@ var middleware = { } next() }) - } + } else { req.project = null next() diff --git a/server/lib/schemas/Plan.js b/server/lib/schemas/Plan.js new file mode 100644 index 0000000..1208672 --- /dev/null +++ b/server/lib/schemas/Plan.js @@ -0,0 +1,42 @@ +/* jshint node: true */ + +var mongoose = require('mongoose'), + _ = require('lodash'), + crypto = require('crypto'), + config = require('../../../config.json'), + util = require('../util'); + +var PlanSchema = new mongoose.Schema({ + name: { type: String }, + slug: { type: String }, + + monthly_price: { type: Number }, + yearly_price: { type: Number }, + + basic_layout_monthly_price: { type: Number }, + basic_layout_yearly_price: { type: Number }, + + pro_layout_monthly_price: { type: Number }, + pro_layout_yearly_price: { type: Number }, + + basic_layout_limit: { type: Number }, + pro_layout_limit: { type: Number }, + + stock_project_limit: { type: Number }, + basic_project_limit: { type: Number }, + pro_project_limit: { type: Number }, + + permissions: { + basic_editor: { type: Boolean, default: false }, + pro_editor: { type: Boolean, default: false }, + solids: { type: Boolean, default: false }, + collaborators: { type: Boolean, default: false }, + no_logo: { type: Boolean, default: false }, + }, + + created_at: { type: Date, default: Date.now }, + updated_at: { type: Date, default: Date.now }, +}) + +module.exports = exports = mongoose.model('plan', PlanSchema); +exports.schema = PlanSchema; diff --git a/server/lib/schemas/Subscription.js b/server/lib/schemas/Subscription.js new file mode 100644 index 0000000..8315009 --- /dev/null +++ b/server/lib/schemas/Subscription.js @@ -0,0 +1,25 @@ +/* jshint node: true */ + +var mongoose = require('mongoose'), + _ = require('lodash'), + crypto = require('crypto'), + config = require('../../../config.json'), + util = require('../util'); + +var SubscriptionSchema = new mongoose.Schema({ + user_id: { type: mongoose.Schema.ObjectId, index: true }, + + monthly_total: { type: Number }, + yearly_total: { type: Number }, + + plans: [{ + tier: { type: String }, + monthly: { type: Boolean }, + }], + + created_at: { type: Date, default: Date.now }, + updated_at: { type: Date, default: Date.now }, +}) + +module.exports = exports = mongoose.model('subscription', SubscriptionSchema); +exports.schema = SubscriptionSchema; diff --git a/server/lib/views/index.js b/server/lib/views/index.js index 8c3e63d..2a8f921 100644 --- a/server/lib/views/index.js +++ b/server/lib/views/index.js @@ -4,6 +4,7 @@ var User = require('../schemas/User'), Project = require('../schemas/Project'), Documentation = require('../schemas/Documentation'), Collaborator = require('../schemas/Collaborator'), + Plan = require('../schemas/Plan'), config = require('../../../config'), marked = require('marked'), util = require('../util'), @@ -21,6 +22,7 @@ marked.setOptions({ var views = module.exports = { staff: require('./staff'), + subscription: require('./subscription'), editor_new: function (req, res) { if (! req.user) { @@ -88,13 +90,6 @@ var views = module.exports = { }, home: function (req, res) { - // while in development, blank homepage if not logged in -/* - if (! req.user) { - res.send("<html></html>") - return - } -*/ views_middleware.fetchProjects({ featured: true }, null, null, function(err, projects){ res.render('home', { projects: projects || [] @@ -117,6 +112,13 @@ var views = module.exports = { res.render('about/' + name) return } + if (name == "brochure" || name == "plans") { + // TODO: fetch plans + views_middleware.ensurePlans(req, res, function(){ + res.render('about/' + name) + }) + return + } if (name == "about" || name == "index") { res.render('about/' + name) return @@ -225,6 +227,19 @@ var views = module.exports = { } var views_middleware = { + ensurePlans: function(req, res, next){ + Plan.find(function (err, plans) { + res.locals.plans = {} + plans.forEach(function(plan){ + res.locals.plans[ plan.slug ] = plan + "monthly_price yearly_price basic_layout_monthly_price basic_layout_yearly_price pro_layout_monthly_price pro_layout_yearly_price".split(" ").forEach(function(key){ + plan[key] = (plan[key]/100).toFixed(2)+"" + }) + }) + next() + }) + }, + fetchProjects: function (criteria, limit, offset, next) { limit = limit || 7 offset = offset || 0 diff --git a/server/lib/views/staff.js b/server/lib/views/staff.js index 49f492b..74dd7cd 100644 --- a/server/lib/views/staff.js +++ b/server/lib/views/staff.js @@ -4,6 +4,8 @@ var User = require('../schemas/User'), Project = require('../schemas/Project'), Media = require('../schemas/Media'), Collaborator = require('../schemas/Collaborator'), + Plan = require('../schemas/Plan'), + Subscription = require('../schemas/Subscription'), config = require('../../../config'), middleware = require('../middleware'), util = require('../util'), @@ -16,6 +18,10 @@ var staff = module.exports = { fields: { user: "_id username displayName photo created_at updated_at last_seen created_ip last_ip", project: "_id name slug user_id privacy created_at updated_at", + plans: "monthly_price yearly_price basic_layout_monthly_price basic_layout_yearly_price " + + "pro_layout_monthly_price pro_layout_yearly_price " + + "basic_layout_limit pro_layout_limit stock_project_limit basic_project_limit pro_project_limit", + plans_permissions: "basic_editor pro_editor solids collaborators no_logo", }, defaults: { @@ -119,6 +125,77 @@ var staff = module.exports = { }) }, + ensureSubscriptions: function(req, res, next){ + var paginationInfo = res.locals.pagination = {} + var criteria = req.criteria || {} + var limit = paginationInfo.limit = Math.min( Number(req.query.limit) || 50, 200 ) + var offset = paginationInfo.offset = Number(req.query.offset) || 0 + var sort + paginationInfo.sort = req.query.sort + paginationInfo.sortOptions = ["date", "name"] + switch (req.query.sort) { + case 'created': + sort = {'created_at': -1} + break + default: + case 'date': + sort = {'updated_at': -1} + break + } + Subscription.find(criteria) + .select(staff.fields.project) + .sort(sort) + .skip(offset) + .limit(limit) + .exec(function (err, subscriptions) { + res.locals.subscriptions = subscriptions.map(staff.helpers.subscription) + next() + }) + }, + + ensurePlans: function(req, res, next){ + Plan.find(function (err, plans) { + res.locals.plans = (plans || []).map(staff.helpers.plan) + res.locals.plans.sort(function(a,b){ return a.monthly_price }) + next() + }) + }, + ensurePlan: function (req, res, next) { + if (req.params.slug) { + Plan.findOne({ slug: req.params.slug }, function(err, plan){ + if (err || ! plan) { + console.error(err) + res.redirect("/staff/plans/") + } + else { + req.plan = plan + next() + } + }) + } + else { + res.redirect("/staff/plans/") + } + }, + + ensureSubscription: function (req, res, next) { + if (req.params.id) { + Subscription.findOne({ _id: req.params.id }, function(err, subscription){ + if (err || ! subscription) { + console.error(err) + res.redirect("/staff/subscriptions/") + } + else { + req.subscription = subscription + next() + } + }) + } + else { + res.redirect("/staff/subscriptions/") + } + }, + ensureRecentProjects: function(req, res, next){ var dreq = { params: { sort: 'created_at', limit: 20, offset: 0 } } staff.middleware.ensureProjects(dreq, res, next) @@ -129,11 +206,23 @@ var staff = module.exports = { staff.middleware.ensureObjectsUsers(res.locals.projects, next) }, + ensureSubscriptionsUsers: function(req, res, next){ + if (! res.locals.subscriptions || ! res.locals.subscriptions.length) { return next() } + staff.middleware.ensureObjectsUsers(res.locals.subscriptions, next) + }, + ensureMediaUsers: function(req, res, next){ if (! res.locals.media || ! res.locals.media.length) { return next() } staff.middleware.ensureObjectsUsers(res.locals.media, next) }, + ensureSubscriptionUser: function(req, res, next){ + if (! res.locals.subscription) { return next() } + staff.middleware.ensureObjectsUsers([ res.locals.subscription ], function(){ + next() + }) + }, + ensureMediaUser: function(req, res, next){ if (! res.locals.media) { return next() } staff.middleware.ensureObjectsUsers([ res.locals.media ], function(){ @@ -299,7 +388,21 @@ var staff = module.exports = { media.user = {} media.shortUrl = media.url.replace(/^http.:\/\//,"") return media - } + }, + + plan: function(plan){ + plan = plan.toObject() + plan.date = moment( plan.updated_at || plan.created_at ).format("M/DD/YYYY hh:mm a") + plan.user = {} + return plan + }, + + subscription: function(subscription){ + subscription = subscription.toObject() + subscription.date = moment( subscription.updated_at || subscription.created_at ).format("M/DD/YYYY hh:mm a") + subscription.user = {} + return subscription + }, }, route: function(app){ @@ -422,7 +525,67 @@ var staff = module.exports = { staff.media.show ); + + // + // plans + + app.get('/staff/plans', + middleware.ensureAuthenticated, + middleware.ensureIsStaff, + + staff.middleware.ensurePlans, + + staff.plans.index + ); + app.get('/staff/plans/new', + middleware.ensureAuthenticated, + middleware.ensureIsStaff, + + staff.plans.new + ); + app.post('/staff/plans/new', + middleware.ensureAuthenticated, + middleware.ensureIsStaff, + + staff.plans.create + ); + app.get('/staff/plans/:slug', + middleware.ensureAuthenticated, + middleware.ensureIsStaff, + + staff.middleware.ensurePlan, + staff.plans.edit + ); + app.post('/staff/plans/:slug', + middleware.ensureAuthenticated, + middleware.ensureIsStaff, + + staff.middleware.ensurePlan, + + staff.plans.update + ); + + // + // subscriptions + app.get('/staff/subscriptions', + middleware.ensureAuthenticated, + middleware.ensureIsStaff, + + staff.middleware.ensureSubscriptions, + staff.middleware.ensureSubscriptionsUsers, + + staff.subscriptions.index + ); + app.get('/staff/subscriptions/:id', + middleware.ensureAuthenticated, + middleware.ensureIsStaff, + + staff.middleware.ensureSubscription, + staff.middleware.ensureSubscriptionUser, + + staff.subscriptions.show + ); }, paginate: function(req, res){ @@ -540,6 +703,70 @@ var staff = module.exports = { res.render('staff/media/show_404') } }, - } + }, + + plans: { + index: function(req, res){ + res.locals.fields = staff.fields.plans.split(" ") + res.locals.permissions = staff.fields.plans_permissions.split(" ") + res.render('staff/plans/index') + }, + new: function(req, res){ + res.locals.plan = new Plan () + res.render('staff/plans/new') + }, + edit: function(req, res){ + res.locals.plan = req.plan + res.render('staff/plans/edit') + }, + create: function(req, res){ + var plan = new Plan () + var fields = staff.fields.plans.split(" ") + var permissions = staff.fields.plans_permissions.split(" ") + + var data = util.cleanQuery(req.body) + data.name = util.sanitize(data.name) + data.slug = util.sanitize(data.slug.toLowerCase()) + + permissions.forEach(function(field){ + data[field] = data["permissions_" + field] + }) + + new Plan (data).save(function(err, doc){ + if (err || ! doc) { return res.json({ error: err }) } + res.redirect("/staff/plans/") + }) + }, + update: function(req, res){ + var fields = staff.fields.plans.split(" ") + var permissions = staff.fields.plans_permissions.split(" ") + + var data = util.cleanQuery(req.body) + data.name = util.sanitize(data.name) + data.slug = util.sanitize(data.slug.toLowerCase()) + + _.extend(req.plan, data) + permissions.forEach(function(field){ + req.plan.permissions[field] = data["permissions_" + field].length == 2 + }) + + req.plan.save(function(err, doc){ + if (err || ! doc) { return res.json({ error: err }) } + res.redirect("/staff/plans/") + }) + }, + }, + + subscriptions: { + index: function(req, res){ + res.locals.pagination.count = res.locals.subscriptions.length + res.locals.pagination.max = res.locals.subscriptionCount + staff.paginate(req, res) + res.render('staff/subscriptions/index') + }, + show: function(req, res){ + res.render('staff/subscriptions/show') + }, + }, } diff --git a/server/lib/views/subscription.js b/server/lib/views/subscription.js new file mode 100644 index 0000000..e29e40d --- /dev/null +++ b/server/lib/views/subscription.js @@ -0,0 +1,41 @@ +/* jshint node: true */ + +var User = require('../schemas/User'), + Subscription = require('../schemas/Subscription'), + config = require('../../../config'), + middleware = require('../middleware'), + util = require('../util'), + _ = require('lodash'), + moment = require('moment'); + +var subscription = module.exports = { + + fields: { + user: "_id username displayName photo created_at updated_at last_seen created_ip last_ip", + }, + + defaults: { + user: { + _id: "", username: "", displayName: "", + created_at: "", updated_at: "", created_ip: "", last_ip: "", + }, + }, + + middleware: { + }, + + helpers: { + project: function(project){ + project = project.toObject() + project.date = moment( project.updated_at || project.created_at ).format("M/DD/YYYY hh:mm a") + project.user = {} + return project + }, + }, + + // need a route for the webhook, + // then calls to get appropriate info from the recurly api + webhook: function(req, res){ + res.status(200).end() + }, +} diff --git a/server/repl.js b/server/repl.js index ba94d45..353d8c5 100644 --- a/server/repl.js +++ b/server/repl.js @@ -9,6 +9,8 @@ mongoose.connect('mongodb://' + DB_HOST + '/vvalls', {}, function(){ "./lib/schemas/Layout", "./lib/schemas/Media", "./lib/schemas/Project", + "./lib/schemas/Plan", + "./lib/schemas/Subscription", ].forEach(function(modName){ // console.log(name, modName) var namez = modName.split("/"), name = namez[namez.length-1]; diff --git a/views/about/_blank.ejs b/views/about/_blank.ejs index 0e9ea7e..732ea3f 100644 --- a/views/about/_blank.ejs +++ b/views/about/_blank.ejs @@ -1,14 +1,14 @@ <!doctype html> <html> <head> - <title>vvalls</title> + <title>VValls</title> [[ include ../partials/meta ]] </head> <body class="loading"> <div class="rapper page home"> [[ include ../partials/header ]] - + <h1 class="leader">Hallo</h1> <!-- put stuff here --> diff --git a/views/about/about.ejs b/views/about/about.ejs index dd536be..6d5f011 100644 --- a/views/about/about.ejs +++ b/views/about/about.ejs @@ -1,14 +1,14 @@ <!doctype html> <html> <head> - <title>vvalls</title> + <title>About VValls</title> [[ include ../partials/meta ]] </head> <body class="loading"> <div class="rapper page"> [[ include ../partials/header ]] - <h1>About VValls</h1> + <h1 class="leader">About VValls</h1> <div class="projectList about aboutintro"> <div class="inner"> @@ -50,17 +50,3 @@ </body> [[ include ../partials/scripts ]] </html> -<style> -.aboutintro { - text-align: center; - line-height: 43px; - font-size: 24px; - padding: 5% 0; - font-weight: 300; -} -.aboutintro .inner { - max-width: 800px; - margin: 0 auto; - text-align: center; -} -</style>
\ No newline at end of file diff --git a/views/about/brochure.ejs b/views/about/brochure.ejs new file mode 100644 index 0000000..e75f8c6 --- /dev/null +++ b/views/about/brochure.ejs @@ -0,0 +1,126 @@ +<!doctype html> +<html> +<head> + <title>VValls Subscriptions</title> + [[ include ../partials/meta ]] +</head> +<body class="loading"> + <div class="rapper page"> + [[ include ../partials/header ]] + + <h1 class="leader">Subscriptions</h1> + + <div class="projectList about aboutintro"> + <div class="inner"> + Want to get more out of VValls? Consider becoming a subscription user. + <br><br> + </div> + + <div class="about_plan planbox"> + <h3>[[- plans.free.name ]]</h3> + <ul> + <li> [[- plans.free.stock_project_limit ]] exhibition with pre-designed template floor plan + </ul> + </div> + + <div class="about_plan planbox"> + <h3>[[- plans.basic.name ]]</h3> + <ul> + <li> $[[- plans.basic.monthly_price ]]/mo or $[[- plans.basic.yearly_price ]]/year + <li> Comes with [[- plans.basic.basic_layout_limit ]] basic floor plan and [[- plans.basic.basic_project_limit ]] exhibitions + <li> Each new basic floor plan costs $[[- plans.basic.basic_layout_monthly_price ]]/mo + or $[[- plans.basic.basic_layout_yearly_price ]]/year, minimum 3 months + <li> Each new floor plan can have up to [[- plans.basic.basic_project_limit ]] exhibitions + <li> VValls logo appears when embedding an exhibition on a web page + <li> + <!-- check current subscription plan --> + <button>Buy Now</button> + </ul> + </div> + + <div class="about_plan planbox"> + <h3>[[- plans.pro.name ]]</h3> + <ul> + <li> $[[- plans.pro.monthly_price ]]/mo or $[[- plans.pro.yearly_price ]]/year + <li> Comes with [[- plans.pro.pro_layout_limit ]] pro floor plan and [[- plans.pro.pro_project_limit ]] exhibitions + <li> Each new pro floor plan costs $[[- plans.pro.pro_layout_monthly_price ]]/mo + or $[[- plans.pro.pro_layout_yearly_price ]]/year, minimum 3 months + <li> Each new pro floor plan can have up to [[- plans.pro.pro_project_limit ]] exhibitions + <li> Includes planning for 3D objects in the room + <li> No VValls logo on embed + <li> + <!-- check current subscription plan --> + <button>Buy Now</button> + </ul> + </div> + + <div class="about_custom planbox miscbox"> + <ul> + <li> Buying any extra floor plan unlocks collaboration. Invite an artist or curator to work on the exhibition with you. + <li> Basic Floor plan: Rectangle-based design of any dimension. + <li> Pro Floor plan: Trace an arbitrary floor plan from image. + </ul> + </div> + + <div class="about_custom planbox"> + <h3>Want Something Custom?</h3> + <li> We offer customized white-label options for business and educational uses. + <li> <a href="mailto:hello@vvalls.com">Contact us</a> for more information. + </div> + </div> + + + [[ include ../partials/confirm-modal ]] + [[ include ../projects/layouts-modal ]] + [[ include ../partials/sign-in ]] + [[ include ../partials/footer ]] + + </div> +</body> +[[ include ../partials/scripts ]] +</html> +<style> +/* nb these styles should be fixed for narrower screens/mobile layout */ +.about_plan { + width: 28vw; + margin: 2vw; + float: left; + min-height: 28vw; + position: relative; +} +.about_plan button { + position: absolute; + bottom: 5%; + left: 5%; + width: 90%; +} +.about_custom { + clear: both; + width: 76vw; + margin: 2vw 10vw; +} +.planbox { + padding: 2vw; + border: 1px solid #ddd; + background: white; + border-radius: 25px; + font-size: 15px; + line-height: 23px; + text-align: center; +} +.planbox h3 { + text-align: center; + margin-bottom: 10px; +} +.planbox li { + list-style-type: none; + margin-bottom: 5px; +} +.planbox.miscbox { + border: 0; + background: #f8f8f8; +} +.about_custom a { + border-bottom: 1px solid; +} +</style>
\ No newline at end of file diff --git a/views/about/howto.ejs b/views/about/howto.ejs index 5278a40..914c3b3 100644 --- a/views/about/howto.ejs +++ b/views/about/howto.ejs @@ -1,7 +1,7 @@ <!doctype html> <html> <head> - <title>vvalls</title> + <title>How to Use VValls</title> [[ include ../partials/meta ]] </head> <body class="loading"> diff --git a/views/builder.ejs b/views/builder.ejs index afb8c66..0ba4238 100644 --- a/views/builder.ejs +++ b/views/builder.ejs @@ -1,7 +1,7 @@ <!doctype html> <html> <head> - <title>vvalls</title> + <title>VValls</title> [[ include partials/meta ]] </head> <body class="editing loading"> diff --git a/views/docs.ejs b/views/docs.ejs index b3ead82..a1f081f 100644 --- a/views/docs.ejs +++ b/views/docs.ejs @@ -1,7 +1,7 @@ <!doctype html> <html> <head> - <title>vvalls</title> + <title>VValls</title> [[ include partials/meta ]] </head> <body class="loading"> diff --git a/views/editor.ejs b/views/editor.ejs index 656615c..74e4d6d 100755 --- a/views/editor.ejs +++ b/views/editor.ejs @@ -1,7 +1,7 @@ <!doctype html> <html> <head> - <title>vvalls</title> + <title>VValls</title> [[ include partials/meta ]] </head> <body class="editing loading"> diff --git a/views/home.ejs b/views/home.ejs index 36fc2fc..ffb0976 100755 --- a/views/home.ejs +++ b/views/home.ejs @@ -1,7 +1,7 @@ <!doctype html> <html> <head> - <title>vvalls</title> + <title>VValls</title> [[ include partials/meta ]] </head> <body class="loading"> diff --git a/views/modal.ejs b/views/modal.ejs index 7ca869c..732953d 100644 --- a/views/modal.ejs +++ b/views/modal.ejs @@ -1,7 +1,7 @@ <!doctype html> <html> <head> - <title>vvalls</title> + <title>VValls</title> [[ include partials/meta ]] </head> <body class="loading"> diff --git a/views/profile.ejs b/views/profile.ejs index a62652c..88af6b0 100644 --- a/views/profile.ejs +++ b/views/profile.ejs @@ -1,7 +1,7 @@ <!doctype html> <html> <head> - <title>vvalls</title> + <title>VValls | [[- profile.displayName ]]</title> [[ include partials/meta ]] </head> <body class="loading"> diff --git a/views/reader.ejs b/views/reader.ejs index 6c9856a..7035356 100644 --- a/views/reader.ejs +++ b/views/reader.ejs @@ -1,7 +1,7 @@ <!doctype html> <html> <head> - <title>vvalls</title> + <title>VValls</title> [[ include partials/meta ]] </head> <body class="loading reader"> diff --git a/views/staff/_header.ejs b/views/staff/_header.ejs index 3bbf4f1..a73c12e 100644 --- a/views/staff/_header.ejs +++ b/views/staff/_header.ejs @@ -1,7 +1,7 @@ <!doctype html> <html> <head> - <title>vvalls | staff</title> + <title>VValls | staff</title> [[ include ../partials/meta ]] <link rel="stylesheet" href="/assets/javascripts/vendor/bower_components/jquery-jsonview/dist/jquery.jsonview.css"></script> <link rel="stylesheet" href="/assets/stylesheets/staff.css"></script> diff --git a/views/staff/_nav.ejs b/views/staff/_nav.ejs new file mode 100644 index 0000000..e79ff69 --- /dev/null +++ b/views/staff/_nav.ejs @@ -0,0 +1,8 @@ +<nav> + <a href="/staff">home</a> + <a href="/staff/users">users</a> + <a href="/staff/projects">projects</a> + <a href="/staff/media">media</a> + <a href="/staff/plans">plans</a> + <a href="/staff/subscriptions">subscriptions</a> +</nav>
\ No newline at end of file diff --git a/views/staff/index.ejs b/views/staff/index.ejs index 5ca7269..1b73641 100644 --- a/views/staff/index.ejs +++ b/views/staff/index.ejs @@ -2,11 +2,7 @@ <h1>Staff Area</h1> - <nav> - <a href="/staff/users">users</a> - <a href="/staff/projects">projects</a> - <a href="/staff/media">media</a> - </nav> + [[ include _nav ]] <hr> diff --git a/views/staff/media/index.ejs b/views/staff/media/index.ejs index 516af2d..3805c8e 100644 --- a/views/staff/media/index.ejs +++ b/views/staff/media/index.ejs @@ -2,12 +2,7 @@ <h1>Media</h1> - <nav> - <a href="/staff">home</a> - <a href="/staff/users">users</a> - <a href="/staff/projects">projects</a> - <a href="/staff/media">media</a> - </nav> +[[ include ../_nav ]] <hr> diff --git a/views/staff/media/show.ejs b/views/staff/media/show.ejs index 76dcd32..9d05cb9 100644 --- a/views/staff/media/show.ejs +++ b/views/staff/media/show.ejs @@ -2,12 +2,7 @@ <h1>Media: [[- media.type ]]</h1> - <nav> - <a href="/staff">home</a> - <a href="/staff/users">users</a> - <a href="/staff/projects">projects</a> - <a href="/staff/media">media</a> - </nav> +[[ include ../_nav ]] <hr> diff --git a/views/staff/media/show_404.ejs b/views/staff/media/show_404.ejs index f07cef2..c6bac6f 100644 --- a/views/staff/media/show_404.ejs +++ b/views/staff/media/show_404.ejs @@ -2,13 +2,8 @@ <h1>Media not found</h1> - <nav> - <a href="/staff">home</a> - <a href="/staff/users">users</a> - <a href="/staff/projects">projects</a> - <a href="/staff/media">media</a> - </nav> - +[[ include ../_nav ]] + <hr> [[ include ../_footer ]] diff --git a/views/staff/plans/_form.ejs b/views/staff/plans/_form.ejs new file mode 100644 index 0000000..ae5ca5a --- /dev/null +++ b/views/staff/plans/_form.ejs @@ -0,0 +1,149 @@ +<input type="hidden" id="_csrf" name="_csrf" value="[[- token ]]"> + +<ul> + +<li class="section_break"> + <h3>New Plan</h3> +</li> + +<li> + <label for="plan_name">Name</label> + <div><input id="plan_name" name="name" type="text" value="[[- plan.name ]]"></div> +</li> + +<li> + <label for="plan_slug">Slug</label> + <div><input id="plan_slug" name="slug" type="text" value="[[- plan.slug ]]"></div> +</li> + +<!-- - - - - --> + +<li class="section_break"> + <h3>Plan Pricing</h3> +</li> + +<li> + <label for="plan_monthly_price">Monthly Price</label> + <div><input id="plan_monthly_price" name="monthly_price" type="number" value="[[- plan.monthly_price ]]"></div> +</li> + +<li> + <label for="plan_yearly_price">Yearly Price</label> + <div><input id="plan_yearly_price" name="yearly_price" type="number" value="[[- plan.yearly_price ]]"></div> +</li> + +<p> + <i>Note:</i> Pricing should be in cents, i.e. a price of $10.00 should be entered as 1000. +</p> + +<!-- - - - - --> + +<li class="section_break"> + <h3>Additional Template Pricing</h3> +</li> + +<li> + <label for="plan_basic_layout_monthly_price">Basic Template Price (Monthly)</label> + <div><input id="plan_basic_layout_monthly_price" name="basic_layout_monthly_price" type="number" value="[[- plan.basic_layout_monthly_price ]]"></div> +</li> + +<li> + <label for="plan_basic_layout_yearly_price">Basic Template Price (Yearly)</label> + <div><input id="plan_basic_layout_yearly_price" name="basic_layout_yearly_price" type="number" value="[[- plan.basic_layout_yearly_price ]]"></div> +</li> + +<li> + <label for="plan_pro_layout_monthly_price">Pro Template Price (Monthly)</label> + <div><input id="plan_pro_layout_monthly_price" name="pro_layout_monthly_price" type="number" value="[[- plan.pro_layout_monthly_price ]]"></div> +</li> + +<li> + <label for="plan_pro_layout_yearly_price">Pro Template Price (Yearly)</label> + <div><input id="plan_pro_layout_yearly_price" name="pro_layout_yearly_price" type="number" value="[[- plan.pro_layout_yearly_price ]]"></div> +</li> + +<!-- - - - - --> + +<li class="section_break"> + <h3>Per-Plan Template Limits</h3> +</li> + +<li> + <label for="plan_basic_layout_limit">Basic Template Limit</label> + <div><input id="plan_basic_layout_limit" name="basic_layout_limit" type="number" value="[[- plan.basic_layout_limit ]]"></div> +</li> + +<li> + <label for="plan_pro_layout_limit">Pro Template Limit</label> + <div><input id="plan_pro_layout_limit" name="pro_layout_limit" type="number" value="[[- plan.pro_layout_limit ]]"></div> +</li> + +<!-- - - - - --> + +<li class="section_break"> + <h3>Per-Plan Project Limits</h3> +</li> + +<li> + <label for="plan_stock_project_limit">Stock Project Limit</label> + <div><input id="plan_stock_project_limit" name="stock_project_limit" type="number" value="[[- plan.stock_project_limit ]]"></div> +</li> + +<li> + <label for="plan_basic_project_limit">Basic Project Limit</label> + <div><input id="plan_basic_project_limit" name="basic_project_limit" type="number" value="[[- plan.basic_project_limit ]]"></div> +</li> + +<li> + <label for="plan_pro_project_limit">Pro Layout Limit</label> + <div><input id="plan_pro_project_limit" name="pro_project_limit" type="number" value="[[- plan.pro_project_limit ]]"></div> +</li> + +<!-- - - - - --> + +<li class="section_break"> + <h3>Permissions</h3> +</li> + +<div> +<li> + <input name="permissions_basic_editor" type="hidden" value="0"> + <input id="plan_permissions_basic_editor" name="permissions_basic_editor" type="checkbox" value="1" [[ if (plan.permissions.basic_editor) { ]]checked[[ } ]]> + <label for="plan_permissions_basic_editor">Basic Editor</label> +</li> + +<li> + <input name="permissions_pro_editor" type="hidden" value="0"> + <input id="plan_permissions_pro_editor" name="permissions_pro_editor" type="checkbox" value="1" [[ if (plan.permissions.pro_editor) { ]]checked[[ } ]]> + <label for="plan_permissions_pro_editor">Pro Editor</label> +</li> + +<li> + <input name="permissions_solids" type="hidden" value="0"> + <input id="plan_permissions_solids" name="permissions_solids" type="checkbox" value="1" [[ if (plan.permissions.solids) { ]]checked[[ } ]]> + <label for="plan_permissions_solids">3D Objects</label> +</li> + +<li> + <input name="permissions_collaborators" type="hidden" value="0"> + <input id="plan_permissions_collaborators" name="permissions_collaborators" type="checkbox" value="1" [[ if (plan.permissions.collaborators) { ]]checked[[ } ]]> + <label for="plan_permissions_collaborators">Collaborators</label> +</li> + +<li> + <input name="permissions_no_logo" type="hidden" value="0"> + <input id="plan_permissions_no_logo" name="permissions_no_logo" type="checkbox" value="1" [[ if (plan.permissions.no_logo) { ]]checked[[ } ]]> + <label for="plan_permissions_no_logo">No Logo</label> +</li> + +<p> + These permissions should harmonize with the restrictions on layouts set above. +</p> + +</div> + +<li> + <input type="submit" value="Save Changes"> +</li> + +</ul> diff --git a/views/staff/plans/edit.ejs b/views/staff/plans/edit.ejs new file mode 100644 index 0000000..9848873 --- /dev/null +++ b/views/staff/plans/edit.ejs @@ -0,0 +1,13 @@ +[[ include ../_header ]] + + <h1>Edit Plan</h1> + +[[ include ../_nav ]] + + <hr> + +<form action="" method="post"> +[[- include _form ]] +</form> + +[[ include ../_footer ]] diff --git a/views/staff/plans/index.ejs b/views/staff/plans/index.ejs new file mode 100644 index 0000000..16fcf14 --- /dev/null +++ b/views/staff/plans/index.ejs @@ -0,0 +1,65 @@ +[[ include ../_header ]] + + <h1>Plans</h1> + +[[ include ../_nav ]] + + <hr> + +<table> + <tr> + <td> + </td> + [[ plans.forEach(function(plan){ ]] + <td style="text-align:right; min-width: 100px;"> + <b style="text-transform:uppercase">[[- plan.name ]]</b> + </td> + [[ }) ]] + </tr> + + <tr class="editLinks"> + <td> + <a href="/staff/plans/new">New Plan</a> + </td> + [[ plans.forEach(function(plan){ ]] + <td style="text-align:right"> + [<a href="/staff/plans/[[- plan.slug ]]">edit</a>] + </td> + [[ }) ]] + </tr> + + [[ fields.forEach(function(field){ ]] + <tr> + <td style="text-transform:capitalize">[[- field.replace(/_/g," ") ]]</td> + [[ plans.forEach(function(plan){ ]] + <td style="text-align:right"> + [[ if (field.indexOf("_price") != -1) { ]] + [[- plan[field] == 0 ? "" : "$" + (plan[field]/100).toFixed(2) ]] + [[ } else { ]] + [[- plan[field] ]] + [[ } ]] + </td> + [[ }) ]] + </tr> + [[ }) ]] + + <tr> + <td style="border-bottom: 1px dotted #888;" colspan='10'></td> + </tr> + + [[ permissions.forEach(function(permission){ ]] + <tr> + <td style="text-transform:capitalize">[[- permission.replace(/_/g," ") ]]</td> + [[ plans.forEach(function(plan){ ]] + <td style="text-align:right"> + [[- plan.permissions[permission] ? "<b>x</b>" : " " ]] + </td> + [[ }) ]] + </tr> + [[ }) ]] +</table> +<br clear="all"> + + <hr> + +[[ include ../_footer ]] diff --git a/views/staff/plans/new.ejs b/views/staff/plans/new.ejs new file mode 100644 index 0000000..297d3d6 --- /dev/null +++ b/views/staff/plans/new.ejs @@ -0,0 +1,13 @@ +[[ include ../_header ]] + + <h1>New Plan</h1> + +[[ include ../_nav ]] + + <hr> + +<form action="" method="post"> +[[- include _form ]] +</form> + +[[ include ../_footer ]] diff --git a/views/staff/projects/index.ejs b/views/staff/projects/index.ejs index 482ea25..e4ba469 100644 --- a/views/staff/projects/index.ejs +++ b/views/staff/projects/index.ejs @@ -2,12 +2,7 @@ <h1>Projects</h1> - <nav> - <a href="/staff">home</a> - <a href="/staff/users">users</a> - <a href="/staff/projects">projects</a> - <a href="/staff/media">media</a> - </nav> +[[ include ../_nav ]] <hr> diff --git a/views/staff/projects/show.ejs b/views/staff/projects/show.ejs index 1034b31..b090a41 100644 --- a/views/staff/projects/show.ejs +++ b/views/staff/projects/show.ejs @@ -2,13 +2,8 @@ <h1>[[- project.name ]]</h1> - <nav> - <a href="/staff">home</a> - <a href="/staff/users">users</a> - <a href="/staff/projects">projects</a> - <a href="/staff/media">media</a> - </nav> - +[[ include ../_nav ]] + <hr> <table> diff --git a/views/staff/projects/show_404.ejs b/views/staff/projects/show_404.ejs index 70320c0..f5e1658 100644 --- a/views/staff/projects/show_404.ejs +++ b/views/staff/projects/show_404.ejs @@ -2,12 +2,7 @@ <h1>Project not found</h1> - <nav> - <a href="/staff">home</a> - <a href="/staff/users">users</a> - <a href="/staff/projects">projects</a> - <a href="/staff/media">media</a> - </nav> +[[ include ../_nav ]] <hr> diff --git a/views/staff/subscriptions/index.ejs b/views/staff/subscriptions/index.ejs new file mode 100644 index 0000000..d1c0588 --- /dev/null +++ b/views/staff/subscriptions/index.ejs @@ -0,0 +1,36 @@ +[[ include ../_header ]] + + <h1>Users</h1> + +[[ include ../_nav ]] + + <hr> + +[[ include ../_pagination ]] + +<table id="users"> +[[ subscriptions.forEach(function(subscription){ ]] + <tr> + <td> + <a href="/staff/subscriptions/[[- subscription._id ]]"><div style="background-image:url([[- subscription.user.photo ]])" class="avatar"></div></a> + </td> + <td> + <a href="/staff/subscriptions/[[- subscription._id ]]">[[- subscription.user.username ]]</a> + </td> + <td> + [[- subscription.user.displayName ]] + </td> + <td class="editLinks"> + <a href="/profile/[[- user.username ]]">[view profile]</a> + </td> + <td> + [[- subscription.user.last_seen ]] + </td> + </tr> +[[ }) ]] +</table> + + +[[ include ../_pagination ]] + +[[ include ../_footer ]] diff --git a/views/staff/subscriptions/show.ejs b/views/staff/subscriptions/show.ejs new file mode 100644 index 0000000..e2839a6 --- /dev/null +++ b/views/staff/subscriptions/show.ejs @@ -0,0 +1,12 @@ +[[ include ../_header ]] + <h1>User: [[- subscription.user.username ]]</h1> + +[[ include ../_nav ]] + + <hr> + +<pre> +info to show.. +- link to recurly profile +- link to vvalls profile +- subscription tier + add-ons diff --git a/views/staff/users/index.ejs b/views/staff/users/index.ejs index f14d666..1795dde 100644 --- a/views/staff/users/index.ejs +++ b/views/staff/users/index.ejs @@ -2,12 +2,7 @@ <h1>Users</h1> - <nav> - <a href="/staff">home</a> - <a href="/staff/users">users</a> - <a href="/staff/projects">projects</a> - <a href="/staff/media">media</a> - </nav> +[[ include ../_nav ]] <hr> diff --git a/views/staff/users/media.ejs b/views/staff/users/media.ejs index c1097dd..8927c00 100644 --- a/views/staff/users/media.ejs +++ b/views/staff/users/media.ejs @@ -1,15 +1,10 @@ [[ include ../_header ]] - <h1>User Media: [[- profile.username ]]</h1> +<h1>User Media: [[- profile.username ]]</h1> - <nav> - <a href="/staff">home</a> - <a href="/staff/users">users</a> - <a href="/staff/projects">projects</a> - <a href="/staff/media">media</a> - </nav> - - <hr> +[[ include ../_nav ]] + +<hr> [[ include ../_pagination ]] [[ include ../_gallery ]] diff --git a/views/staff/users/show.ejs b/views/staff/users/show.ejs index 8e9b447..4ce1d9a 100644 --- a/views/staff/users/show.ejs +++ b/views/staff/users/show.ejs @@ -1,12 +1,7 @@ [[ include ../_header ]] <h1>User: [[- profile.username ]]</h1> - <nav> - <a href="/staff">home</a> - <a href="/staff/users">users</a> - <a href="/staff/projects">projects</a> - <a href="/staff/media">media</a> - </nav> +[[ include ../_nav ]] <hr> diff --git a/views/staff/users/show_404.ejs b/views/staff/users/show_404.ejs index bcd0271..11663fa 100644 --- a/views/staff/users/show_404.ejs +++ b/views/staff/users/show_404.ejs @@ -1,13 +1,9 @@ [[ include ../_header ]] - <h1>User not found</h1> - <nav> - <a href="/staff">home</a> - <a href="/staff/users">users</a> - <a href="/staff/projects">projects</a> - <a href="/staff/media">media</a> - </nav> - - <hr> +<h1>User not found</h1> + +[[ include ../_nav ]] + +<hr> [[ include ../_footer ]] |
