From f28d8f4aac3f6411d43ac81a70bfebe5e61e96c7 Mon Sep 17 00:00:00 2001 From: Julie Lala Date: Sat, 7 Jun 2014 04:50:31 -0400 Subject: arrange edit project/profile modals --- public/assets/javascripts/ui/CreateProjectModal.js | 50 +++++ public/assets/javascripts/ui/EditProfileModal.js | 49 +++++ public/assets/javascripts/ui/EditProjectModal.js | 49 +++++ public/assets/javascripts/ui/MasterView.js | 21 ++ public/assets/javascripts/vendor/view.js | 222 ++++++++++----------- public/assets/stylesheets/app.css | 11 +- server/index.js | 3 +- server/lib/middleware.js | 1 + server/lib/schemas/Project.js | 38 ++++ server/lib/schemas/User.js | 23 +-- server/lib/views.js | 32 ++- views/edit-profile.ejs | 83 -------- views/partials/edit-profile.ejs | 67 +++++++ views/partials/header.ejs | 1 + views/partials/newproject.ejs | 18 -- views/partials/scripts.ejs | 3 + views/partials/signin.ejs | 4 + views/profile.ejs | 113 +++-------- views/projects/create-project.ejs | 18 ++ views/projects/edit-project.ejs | 52 +++++ views/projects/list-projects.ejs | 19 ++ 21 files changed, 551 insertions(+), 326 deletions(-) create mode 100644 public/assets/javascripts/ui/CreateProjectModal.js create mode 100644 public/assets/javascripts/ui/EditProfileModal.js create mode 100644 public/assets/javascripts/ui/EditProjectModal.js create mode 100644 server/lib/schemas/Project.js delete mode 100644 views/edit-profile.ejs create mode 100644 views/partials/edit-profile.ejs delete mode 100644 views/partials/newproject.ejs create mode 100644 views/projects/create-project.ejs create mode 100644 views/projects/edit-project.ejs create mode 100644 views/projects/list-projects.ejs diff --git a/public/assets/javascripts/ui/CreateProjectModal.js b/public/assets/javascripts/ui/CreateProjectModal.js new file mode 100644 index 0000000..44c5a20 --- /dev/null +++ b/public/assets/javascripts/ui/CreateProjectModal.js @@ -0,0 +1,50 @@ + + +var CreateProjectModal = ModalView.extend({ + el: ".mediaDrawer.newProject", + action: "/project/new", + + events: { + "submit form": "submit" + }, + + initialize: function(){ + this.$form = this.$("form") + this.$errors = this.$(".errors") + this.$errorList = this.$(".errorList") + }, + + reset: function(){ + this.$("input").not("[type='submit']").not("[type='hidden']").val("") + }, + + load: function(){ + this.reset() + this.show() + }, + + submit: function(e){ + e.preventDefault() + + this.$errors.hide(); + this.$errorList.empty() + + var fields = this.$form.serializeArray() + + var request = $.post(this.action, $.param(fields)); + request.done($.proxy(function (response) { + if (response.error) { + this.$errors.show(); + for (var key in response.error.errors) { + this.$errorList.append('
' + response.error.errors[key].message + '
'); + } + return + } + else { + window.location.href = "/profile" + } + }, this)); + } + +}) + diff --git a/public/assets/javascripts/ui/EditProfileModal.js b/public/assets/javascripts/ui/EditProfileModal.js new file mode 100644 index 0000000..de8a8be --- /dev/null +++ b/public/assets/javascripts/ui/EditProfileModal.js @@ -0,0 +1,49 @@ + +var EditProfileModal = ModalView.extend({ + el: ".mediaDrawer.editProfile", + action: "/project/edit", + + events: { + "submit form": "submit" + }, + + initialize: function(){ + this.$form = this.$("form") + this.$errors = this.$(".errors") + this.$errorList = this.$(".errorList") + }, + + reset: function(){ + this.$("input").not("[type='submit']").not("[type='hidden']").val("") + }, + + load: function(){ + this.reset() + this.show() + }, + + submit: function(e){ + e.preventDefault() + + this.$errors.hide(); + this.$errorList.empty() + + var fields = this.$form.serializeArray() + + var request = $.post(this.action, $.param(fields)); + request.done($.proxy(function (response) { + if (response.error) { + this.$errors.show(); + for (var key in response.error.errors) { + this.$errorList.append('
' + response.error.errors[key].message + '
'); + } + return + } + else { + window.location.href = "/profile" + } + }, this)); + } + +}) + diff --git a/public/assets/javascripts/ui/EditProjectModal.js b/public/assets/javascripts/ui/EditProjectModal.js new file mode 100644 index 0000000..356d8b7 --- /dev/null +++ b/public/assets/javascripts/ui/EditProjectModal.js @@ -0,0 +1,49 @@ + +var EditProjectModal = ModalView.extend({ + el: ".mediaDrawer.editProject", + action: "/project/edit", + + events: { + "submit form": "submit" + }, + + initialize: function(){ + this.$form = this.$("form") + this.$errors = this.$(".errors") + this.$errorList = this.$(".errorList") + }, + + reset: function(){ + this.$("input").not("[type='submit']").not("[type='hidden']").val("") + }, + + load: function(){ + this.reset() + this.show() + }, + + submit: function(e){ + e.preventDefault() + + this.$errors.hide(); + this.$errorList.empty() + + var fields = this.$form.serializeArray() + + var request = $.post(this.action, $.param(fields)); + request.done($.proxy(function (response) { + if (response.error) { + this.$errors.show(); + for (var key in response.error.errors) { + this.$errorList.append('
' + response.error.errors[key].message + '
'); + } + return + } + else { + window.location.href = "/profile" + } + }, this)); + } + +}) + diff --git a/public/assets/javascripts/ui/MasterView.js b/public/assets/javascripts/ui/MasterView.js index 9657c56..e99777e 100644 --- a/public/assets/javascripts/ui/MasterView.js +++ b/public/assets/javascripts/ui/MasterView.js @@ -5,11 +5,17 @@ var MasterView = View.extend({ events: { "click [data-role='show-signup-modal']": 'showSignUpModal', "click [data-role='show-signin-modal']": 'showSignInModal', + "click [data-role='create-project-modal']": 'showCreateProjectModal', + "click [data-role='edit-project-modal']": 'showEditProjectModal', + "click [data-role='edit-profile-modal']": 'showEditProfileModal', }, initialize: function(){ this.signUpModal = new SignUpModal() this.signInModal = new SignInModal() + this.createProjectModal = new CreateProjectModal() + this.editProjectModal = new EditProjectModal() + this.editProfileModal = new EditProfileModal() $("body").removeClass("loading") // app.launch() @@ -25,6 +31,21 @@ var MasterView = View.extend({ e.preventDefault() this.signInModal.load() }, + + showCreateProjectModal: function(e){ + e.preventDefault() + this.createProjectModal.load() + }, + + showEditProjectModal: function(e){ + e.preventDefault() + this.editProjectModal.load() + }, + + showEditProfileModal: function(e){ + e.preventDefault() + this.editProfileModal.load() + }, }) diff --git a/public/assets/javascripts/vendor/view.js b/public/assets/javascripts/vendor/view.js index ef5d330..823a75b 100644 --- a/public/assets/javascripts/vendor/view.js +++ b/public/assets/javascripts/vendor/view.js @@ -1,128 +1,128 @@ var View = (function($, _){ - var View = function(options) { - this.cid = _.uniqueId('view'); - options || (options = {}); - _.extend(this, _.pick(options, viewOptions)); - this._ensureElement(); - this.initialize.apply(this, arguments); - this.delegateEvents(); - }; - - var delegateEventSplitter = /^(\S+)\s*(.*)$/; - - var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events']; - - _.extend(View.prototype, { - - // The default `tagName` of a View's element is `"div"`. - tagName: 'div', - - $: function(selector) { - return this.$el.find(selector); - }, - - initialize: function(){}, - - setElement: function(element, delegate) { - if (this.$el) this.undelegateEvents(); - this.$el = element instanceof $ ? element : $(element); - this.el = this.$el[0]; - if (delegate !== false) this.delegateEvents(); - return this; - }, - - // Set callbacks, where `this.events` is a hash of - // - // *{"event selector": "callback"}* - // - // { - // 'mousedown .title': 'edit', - // 'click .button': 'save', - // 'click .open': function(e) { ... } - // } - // - // pairs. Callbacks will be bound to the view, with `this` set properly. - // Uses event delegation for efficiency. - // Omitting the selector binds the event to `this.el`. - // This only works for delegate-able events: not `focus`, `blur`, and - // not `change`, `submit`, and `reset` in Internet Explorer. - delegateEvents: function(events) { - if (!(events || (events = _.result(this, 'events')))) return this; - this.undelegateEvents(); - for (var key in events) { - var method = events[key]; - if (!_.isFunction(method)) method = this[events[key]]; - if (!method) continue; - - var match = key.match(delegateEventSplitter); - var eventName = match[1], selector = match[2]; - method = _.bind(method, this); - eventName += '.delegateEvents' + this.cid; - if (selector === '') { - this.$el.on(eventName, method); - } else { - this.$el.on(eventName, selector, method); - } - } - return this; - }, - - // Clears all callbacks previously bound to the view with `delegateEvents`. - undelegateEvents: function() { - this.$el.off('.delegateEvents' + this.cid); - return this; - }, - - // Ensure that the View has a DOM element to render into. - // If `this.el` is a string, pass it through `$()`, take the first - // matching element, and re-assign it to `el`. Otherwise, create - // an element from the `id`, `className` and `tagName` properties. - _ensureElement: function() { + var View = function(options) { + this.cid = _.uniqueId('view'); + options || (options = {}); + _.extend(this, _.pick(options, viewOptions)); + this._ensureElement(); + this.initialize.apply(this, arguments); + this.delegateEvents(); + }; + + var delegateEventSplitter = /^(\S+)\s*(.*)$/; + + var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events']; + + _.extend(View.prototype, { + + // The default `tagName` of a View's element is `"div"`. + tagName: 'div', + + $: function(selector) { + return this.$el.find(selector); + }, + + initialize: function(){}, + + setElement: function(element, delegate) { + if (this.$el) this.undelegateEvents(); + this.$el = element instanceof $ ? element : $(element); + this.el = this.$el[0]; + if (delegate !== false) this.delegateEvents(); + return this; + }, + + // Set callbacks, where `this.events` is a hash of + // + // *{"event selector": "callback"}* + // + // { + // 'mousedown .title': 'edit', + // 'click .button': 'save', + // 'click .open': function(e) { ... } + // } + // + // pairs. Callbacks will be bound to the view, with `this` set properly. + // Uses event delegation for efficiency. + // Omitting the selector binds the event to `this.el`. + // This only works for delegate-able events: not `focus`, `blur`, and + // not `change`, `submit`, and `reset` in Internet Explorer. + delegateEvents: function(events) { + if (!(events || (events = _.result(this, 'events')))) return this; + this.undelegateEvents(); + for (var key in events) { + var method = events[key]; + if (!_.isFunction(method)) method = this[events[key]]; + if (!method) continue; + + var match = key.match(delegateEventSplitter); + var eventName = match[1], selector = match[2]; + method = _.bind(method, this); + eventName += '.delegateEvents' + this.cid; + if (selector === '') { + this.$el.on(eventName, method); + } else { + this.$el.on(eventName, selector, method); + } + } + return this; + }, + + // Clears all callbacks previously bound to the view with `delegateEvents`. + undelegateEvents: function() { + this.$el.off('.delegateEvents' + this.cid); + return this; + }, + + // Ensure that the View has a DOM element to render into. + // If `this.el` is a string, pass it through `$()`, take the first + // matching element, and re-assign it to `el`. Otherwise, create + // an element from the `id`, `className` and `tagName` properties. + _ensureElement: function() { this.setElement(_.result(this, 'el'), false); - } + } - }); + }); - var extend = function(protoProps, staticProps) { - var staticProps = staticProps || {} - var parent = this; - var child; - var childEvents = {}; + var extend = function(protoProps, staticProps) { + var staticProps = staticProps || {} + var parent = this; + var child; + var childEvents = {}; - // The constructor function for the new subclass is either defined by you - // (the "constructor" property in your `extend` definition), or defaulted - // by us to simply call the parent's constructor. - if (protoProps && _.has(protoProps, 'constructor')) { - child = protoProps.constructor; - } else { - child = function(){ return parent.apply(this, arguments); }; - } + // The constructor function for the new subclass is either defined by you + // (the "constructor" property in your `extend` definition), or defaulted + // by us to simply call the parent's constructor. + if (protoProps && _.has(protoProps, 'constructor')) { + child = protoProps.constructor; + } else { + child = function(){ return parent.apply(this, arguments); }; + } - // Extend events so we can subclass views - _.extend(childEvents, parent.prototype.events, protoProps.events) + // Extend events so we can subclass views + _.extend(childEvents, parent.prototype.events, protoProps.events) - // Add static properties to the constructor function, if supplied. - _.extend(child, parent, staticProps); + // Add static properties to the constructor function, if supplied. + _.extend(child, parent, staticProps); - // Set the prototype chain to inherit from `parent`, without calling - // `parent`'s constructor function. - var Surrogate = function(){ this.constructor = child; }; - Surrogate.prototype = parent.prototype; - child.prototype = new Surrogate; + // Set the prototype chain to inherit from `parent`, without calling + // `parent`'s constructor function. + var Surrogate = function(){ this.constructor = child; }; + Surrogate.prototype = parent.prototype; + child.prototype = new Surrogate; - // Add prototype properties (instance properties) to the subclass, - // if supplied. - if (protoProps) _.extend(child.prototype, protoProps); + // Add prototype properties (instance properties) to the subclass, + // if supplied. + if (protoProps) _.extend(child.prototype, protoProps); - // Set a convenience property in case the parent's prototype is needed - // later. - child.prototype.__super__ = parent.prototype; + // Set a convenience property in case the parent's prototype is needed + // later. + child.prototype.__super__ = parent.prototype; child.prototype.events = childEvents - return child; - }; + return child; + }; View.extend = extend; diff --git a/public/assets/stylesheets/app.css b/public/assets/stylesheets/app.css index 48e5e20..85ecdb4 100755 --- a/public/assets/stylesheets/app.css +++ b/public/assets/stylesheets/app.css @@ -333,20 +333,15 @@ h5{ /* PROFILE PAGE */ .profilepage .profilePic { font-size: 148px; - background: url(http://www.clevelandfoundation.org/wp-content/uploads/2012/10/ivan-discussing-his-eye-surgery.jpg); background-size: cover; background-position: center; } .editProfile { - padding: 8px; - float: right; margin-right: 10px; - font-size: 38px; color: black; font-weight: 100; text-decoration: none } - .editProfile span { vertical-align: middle; } @@ -356,6 +351,9 @@ h5{ padding: 8px; margin: 14px; } +.topLinks span { + font-weight: 300; +} .topLinks a { padding: 8px; color: black; @@ -426,6 +424,9 @@ h5{ font-size: 16px; font-weight: 300; } +.profilepage .bio span:nth-of-type:after { content: ' · ' } +.profilepage .bio span:last-of-type:after { display: none; } + .templates { padding-top: 7vh; diff --git a/server/index.js b/server/index.js index a45ff36..01f861b 100644 --- a/server/index.js +++ b/server/index.js @@ -68,7 +68,8 @@ app.get('/auth/twitter', auth.login('twitter')); app.get('/auth/twitter/callback', auth.loggedIn('twitter')); app.get('/auth/facebook', auth.login('facebook')); app.get('/auth/facebook/callback', auth.loggedIn('facebook')); - +app.get('/profile', views.profile) +app.get(/^\/([-_a-zA-Z0-9]+)\/?$/, views.profile) /* diff --git a/server/lib/middleware.js b/server/lib/middleware.js index cace91f..83613e3 100644 --- a/server/lib/middleware.js +++ b/server/lib/middleware.js @@ -39,6 +39,7 @@ var middleware = { res.locals.token = req.csrfToken(); res.locals.logged_in = req.isAuthenticated() res.locals.user = req.user || {} + res.locals.config = config next() }, diff --git a/server/lib/schemas/Project.js b/server/lib/schemas/Project.js new file mode 100644 index 0000000..a0382b3 --- /dev/null +++ b/server/lib/schemas/Project.js @@ -0,0 +1,38 @@ +/* jshint node: true */ + +var NONALPHANUMERICS_REGEX = new RegExp('[^-_a-zA-Z0-9]', 'g') + +var mongoose = require('mongoose'), + _ = require('lodash'), + config = require('../../../config.json'); + +var ProjectSchema = new mongoose.Schema({ + name: { type: String, required: true }, + slug: { + type: String, + required: true, + validate: [function (val){ + val = (val || this.displayName || "").replace(/\s/g,"-").replace(NONALPHANUMERICS_REGEX, '-').replace(/-+/g,"-") + if (! val.length) return false + return true + },"{PATH} name is required"] + }, + privacy: { + type: Boolean, + default: false, + }, + photo: { + type: String, + }, + about: { + type: String, + default: "" + }, + user_id: { type: mongoose.Schema.ObjectId, index: true }, + created_at: { type: Date }, + updated_at: { type: Date }, +}); + + +module.exports = exports = mongoose.model('project', ProjectSchema); +exports.schema = ProjectSchema; diff --git a/server/lib/schemas/User.js b/server/lib/schemas/User.js index ef8fef6..d78bfd2 100644 --- a/server/lib/schemas/User.js +++ b/server/lib/schemas/User.js @@ -10,7 +10,7 @@ var UserSchema = new mongoose.Schema({ twitter_id: String, facebook_id: String, - displayName: String, + displayName: { type: String, default: "" }, username: { type: String, required: true, @@ -36,13 +36,10 @@ var UserSchema = new mongoose.Schema({ return true }, "{PATH} is not an acceptable name"] }, - email: { - type: String, - default: "", - }, + email: { type: String, efault: "" }, emailVerified: { type: Boolean, - default: false, + default: false, }, password: { type: String, @@ -51,15 +48,11 @@ var UserSchema = new mongoose.Schema({ return true }, "{PATH} is not an acceptable password"] }, - photo: { - type: String, - }, - bio: { - type: String, - default: "" - }, - website: String, - twitterName: String, + location: { type: String, default: "" }, + photo: { type: String, default: "" }, + bio: { type: String, default: "" }, + website: { type: String, default: "" }, + twitterName: { type: String, default: "" }, isAdmin: { type: Boolean, default: false } }); diff --git a/server/lib/views.js b/server/lib/views.js index 4ab2a21..a8723eb 100644 --- a/server/lib/views.js +++ b/server/lib/views.js @@ -1,23 +1,45 @@ /* jshint node: true */ var User = require('./schemas/User'), + Project = require('./schemas/Project'), config = require('../../config'), _ = require('lodash'); exports.login = function (req, res) { res.render('login', { - config: config }); }; exports.home = function (req, res) { res.render('home', { - config: config }) } exports.profile = function (req, res) { - res.render('profile', { - config: config - }) + var username = req.params[0] + if (username) { + User.findOne({ username: username }, function (err, user) { + user ? next(user) : done(err, {}, []) + }) + } + else if (req.user) { + next(req.user) + } + else { + done() + } + + function next(user){ + Project.find({ user_id: user._id }, function(err, projects){ + done(err, user, projects) + }) + } + + function done(err, user, projects){ + if (! user) { return res.redirect('/') } + res.render('profile', { + profile: user, + projects: projects || [], + }) + } } diff --git a/views/edit-profile.ejs b/views/edit-profile.ejs deleted file mode 100644 index 0e39ade..0000000 --- a/views/edit-profile.ejs +++ /dev/null @@ -1,83 +0,0 @@ - - - - vvalls - [[ include partials/meta ]] - - -
- - -
- -
- -
    -
  • -

    Edit Profile

    -
  • -
  • - -
    - -
    -
  • -
  • - -
    - -
    -
  • -
  • - -
    - -
    -
  • -
  • - -
    - -
    -
  • -
  • - -
    - -

    please choose a picture at least 500px wide

    -
  • -
  • -

    Edit Password

    -
  • -
  • - -
    - -
    -
  • -
  • - -
    - -
    -
  • -
  • - -
    - -
    -
  • - -
  • - -
  • -
-
-
- -[[ include partials/footer ]] - -
- -[[ include partials/scripts ]] - diff --git a/views/partials/edit-profile.ejs b/views/partials/edit-profile.ejs new file mode 100644 index 0000000..1ac69d7 --- /dev/null +++ b/views/partials/edit-profile.ejs @@ -0,0 +1,67 @@ +
+ X +
+ +
+ +
    +
  • +

    Edit Profile

    +
  • +
  • + +
    + +
    +
  • +
  • + +
    + +
    +
  • +
  • + +
    + +
    +
  • +
  • + +
    + +
    +
  • +
  • + +
    + +

    please choose a picture at least 500px wide

    +
  • +
  • +

    Edit Password

    +
  • +
  • + +
    + +
    +
  • +
  • + +
    + +
    +
  • +
  • + +
    + +
    +
  • + +
  • + +
  • +
+
\ No newline at end of file diff --git a/views/partials/header.ejs b/views/partials/header.ejs index 4ba9773..fce96cc 100644 --- a/views/partials/header.ejs +++ b/views/partials/header.ejs @@ -4,6 +4,7 @@ [[ if (logged_in) { ]] [[- user.displayName ]] View Profile + Edit Profile Sign Out [[ } else { ]] diff --git a/views/partials/newproject.ejs b/views/partials/newproject.ejs deleted file mode 100644 index fc34295..0000000 --- a/views/partials/newproject.ejs +++ /dev/null @@ -1,18 +0,0 @@ -
- X -
- -
-

Choose Room Template

- - - - - - -
-
- -
-
-
\ No newline at end of file diff --git a/views/partials/scripts.ejs b/views/partials/scripts.ejs index 46c5d50..c8f65f6 100644 --- a/views/partials/scripts.ejs +++ b/views/partials/scripts.ejs @@ -47,5 +47,8 @@ + + + diff --git a/views/partials/signin.ejs b/views/partials/signin.ejs index c285811..3341aec 100644 --- a/views/partials/signin.ejs +++ b/views/partials/signin.ejs @@ -1,3 +1,5 @@ +[[ if (! logged_in) { ]] + + +[[ } ]] diff --git a/views/profile.ejs b/views/profile.ejs index 5ac75c5..26f1ee1 100644 --- a/views/profile.ejs +++ b/views/profile.ejs @@ -6,109 +6,46 @@
- - edit profile + [[- include partials/header ]] + [[ if (profile.photo && profile.photo.length) { ]] + + [[ } else { ]] + [[ } ]]
+ - -

Ivan Sidorov

+

[[- profile.displayName ]]

+ [[ if (profile.location) { ]] + + [[- profile.location ]] + + [[ } ]] + [[ if (profile.website && profile.website.length) { ]] + + [[- profile.website ]] + + [[ } ]] + [[ if (profile.twitterName && profile.twitterName.length) { ]] - New York City · http://example.com/ · @twitter + @[[- profile.twitterName ]] + [[ } ]]
-

Ivan has 4 projects

- - - - - - - -
-
edit
-
- X -
-
-
    -
  • -

    Edit Room

    -
  • -
  • - -
    - -
    -
  • -
  • - -
    - -
    -
  • -
  • - -
    - -
    -
  • -
  • - -
    - - - - - -
    -
  • -
  • - + [[ include projects/list-projects ]] - -
  • -
    -
  • - Clone Project - Delete Project -
  • -
-
-
-
- Idea for Show -
-
edit
- Thing I'm Working On -
-
edit
- Pace Gallery -
- - - - - -
-
edit
- Pace Gallery -
- - create project + create project + [[ include partials/edit-profile ]] + [[ include projects/create-project ]] + [[ include projects/edit-project ]] [[ include partials/footer ]]
diff --git a/views/projects/create-project.ejs b/views/projects/create-project.ejs new file mode 100644 index 0000000..e151f3a --- /dev/null +++ b/views/projects/create-project.ejs @@ -0,0 +1,18 @@ +
+ X +
+ +
+

Choose Room Template

+ + + + + + +
+
+ +
+
+
diff --git a/views/projects/edit-project.ejs b/views/projects/edit-project.ejs new file mode 100644 index 0000000..0441b9f --- /dev/null +++ b/views/projects/edit-project.ejs @@ -0,0 +1,52 @@ +
+ X +
+
+
    +
  • +

    Edit Room

    +
  • +
  • + +
    + +
    +
  • +
  • + +
    + +
    +
  • +
  • + +
    + +
    +
  • +
  • + +
    + + + + + +
    +
  • +
  • + +
  • +
    +
  • + Clone Project + Delete Project +
  • +
+
+
+
diff --git a/views/projects/list-projects.ejs b/views/projects/list-projects.ejs new file mode 100644 index 0000000..4e5be75 --- /dev/null +++ b/views/projects/list-projects.ejs @@ -0,0 +1,19 @@ +[[ if (projects.length) { ]] +

[[- profile.username ]] has [[- projects.length ]] project[[- projects.length != 1 && "s" ]]

+ + + + + [[ projects.forEach(function(project) { ]] + + [[ }) ]] + + +
+ [[ if (profile._id == project.user_id) { ]] +
edit
+ [[ } ]] + [[- project.name ]] +
+ +[[ } ]] -- cgit v1.2.3-70-g09d2