windhamdavid 4 years ago
parent
commit
1149fe8002

+ 14 - 3
app.js

@@ -100,11 +100,22 @@ var userController = require('./controllers/user');
 
 app.get('/', homeController.index);
 app.get('/login', userController.getLogin);
-//app.post('/login', userController.postLogin);
+app.post('/login', userController.postLogin);
+app.get('/logout', userController.logout);
+app.get('/forgot', userController.getForgot);
+app.post('/forgot', userController.postForgot);
+app.get('/reset/:token', userController.getReset);
+app.post('/reset/:token', userController.postReset);
 app.get('/register', userController.getSignup);
 app.post('/register', userController.postSignup);
-
-
+app.get('/account', passportConf.isAuthenticated, userController.getAccount);
+app.post('/account/profile', passportConf.isAuthenticated, userController.postUpdateProfile);
+app.post('/account/password', passportConf.isAuthenticated, userController.postUpdatePassword);
+app.post('/account/delete', passportConf.isAuthenticated, userController.postDeleteAccount);
+app.get('/account/unlink/:provider', passportConf.isAuthenticated, userController.getOauthUnlink);
+
+//app.get('/contact', contactController.getContact);
+//app.post('/contact', contactController.postContact);
 
 /************* log **************/
 

+ 271 - 0
controllers/user.js

@@ -107,4 +107,275 @@ exports.postSignup = function(req, res, next) {
       });
     });
   });
+};
+
+
+/**
+ * GET /account
+ * Profile page.
+ */
+exports.getAccount = function(req, res) {
+  res.render('account/profile', {
+    title: 'Account Management'
+  });
+};
+
+/**
+ * POST /account/profile
+ * Update profile information.
+ */
+exports.postUpdateProfile = function(req, res, next) {
+  User.findById(req.user.id, function(err, user) {
+    if (err) {
+      return next(err);
+    }
+    user.email = req.body.email || '';
+    user.profile.name = req.body.name || '';
+    user.profile.gender = req.body.gender || '';
+    user.profile.location = req.body.location || '';
+    user.profile.website = req.body.website || '';
+    user.save(function(err) {
+      if (err) {
+        return next(err);
+      }
+      req.flash('success', { msg: 'Profile information updated.' });
+      res.redirect('/account');
+    });
+  });
+};
+
+/**
+ * POST /account/password
+ * Update current password.
+ */
+exports.postUpdatePassword = function(req, res, next) {
+  req.assert('password', 'Password must be at least 4 characters long').len(4);
+  req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password);
+
+  var errors = req.validationErrors();
+
+  if (errors) {
+    req.flash('errors', errors);
+    return res.redirect('/account');
+  }
+
+  User.findById(req.user.id, function(err, user) {
+    if (err) {
+      return next(err);
+    }
+    user.password = req.body.password;
+    user.save(function(err) {
+      if (err) {
+        return next(err);
+      }
+      req.flash('success', { msg: 'Password has been changed.' });
+      res.redirect('/account');
+    });
+  });
+};
+
+/**
+ * POST /account/delete
+ * Delete user account.
+ */
+exports.postDeleteAccount = function(req, res, next) {
+  User.remove({ _id: req.user.id }, function(err) {
+    if (err) {
+      return next(err);
+    }
+    req.logout();
+    req.flash('info', { msg: 'Your account has been deleted.' });
+    res.redirect('/');
+  });
+};
+
+/**
+ * GET /account/unlink/:provider
+ * Unlink OAuth provider.
+ */
+exports.getOauthUnlink = function(req, res, next) {
+  var provider = req.params.provider;
+  User.findById(req.user.id, function(err, user) {
+    if (err) {
+      return next(err);
+    }
+    user[provider] = undefined;
+    user.tokens = _.reject(user.tokens, function(token) { return token.kind === provider; });
+    user.save(function(err) {
+      if (err) return next(err);
+      req.flash('info', { msg: provider + ' account has been unlinked.' });
+      res.redirect('/account');
+    });
+  });
+};
+
+/**
+ * GET /reset/:token
+ * Reset Password page.
+ */
+exports.getReset = function(req, res) {
+  if (req.isAuthenticated()) {
+    return res.redirect('/');
+  }
+  User
+    .findOne({ resetPasswordToken: req.params.token })
+    .where('resetPasswordExpires').gt(Date.now())
+    .exec(function(err, user) {
+      if (err) {
+        return next(err);
+      }
+      if (!user) {
+        req.flash('errors', { msg: 'Password reset token is invalid or has expired.' });
+        return res.redirect('/forgot');
+      }
+      res.render('account/reset', {
+        title: 'Password Reset'
+      });
+    });
+};
+
+/**
+ * POST /reset/:token
+ * Process the reset password request.
+ */
+exports.postReset = function(req, res, next) {
+  req.assert('password', 'Password must be at least 4 characters long.').len(4);
+  req.assert('confirm', 'Passwords must match.').equals(req.body.password);
+
+  var errors = req.validationErrors();
+
+  if (errors) {
+    req.flash('errors', errors);
+    return res.redirect('back');
+  }
+
+  async.waterfall([
+    function(done) {
+      User
+        .findOne({ resetPasswordToken: req.params.token })
+        .where('resetPasswordExpires').gt(Date.now())
+        .exec(function(err, user) {
+          if (err) {
+            return next(err);
+          }
+          if (!user) {
+            req.flash('errors', { msg: 'Password reset token is invalid or has expired.' });
+            return res.redirect('back');
+          }
+          user.password = req.body.password;
+          user.resetPasswordToken = undefined;
+          user.resetPasswordExpires = undefined;
+          user.save(function(err) {
+            if (err) {
+              return next(err);
+            }
+            req.logIn(user, function(err) {
+              done(err, user);
+            });
+          });
+        });
+    },
+    function(user, done) {
+      var transporter = nodemailer.createTransport({
+        service: 'Mandrill',
+        auth: {
+          user: secrets.mandrill.user,
+          pass: secrets.mandrill.password
+        }
+      });
+      var mailOptions = {
+        to: user.email,
+        from: 'hackathon@starter.com',
+        subject: 'Your Hackathon Starter password has been changed',
+        text: 'Hello,\n\n' +
+          'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n'
+      };
+      transporter.sendMail(mailOptions, function(err) {
+        req.flash('success', { msg: 'Success! Your password has been changed.' });
+        done(err);
+      });
+    }
+  ], function(err) {
+    if (err) {
+      return next(err);
+    }
+    res.redirect('/');
+  });
+};
+
+/**
+ * GET /forgot
+ * Forgot Password page.
+ */
+exports.getForgot = function(req, res) {
+  if (req.isAuthenticated()) {
+    return res.redirect('/');
+  }
+  res.render('account/forgot', {
+    title: 'Forgot Password'
+  });
+};
+
+/**
+ * POST /forgot
+ * Create a random token, then the send user an email with a reset link.
+ */
+exports.postForgot = function(req, res, next) {
+  req.assert('email', 'Please enter a valid email address.').isEmail();
+
+  var errors = req.validationErrors();
+
+  if (errors) {
+    req.flash('errors', errors);
+    return res.redirect('/forgot');
+  }
+
+  async.waterfall([
+    function(done) {
+      crypto.randomBytes(16, function(err, buf) {
+        var token = buf.toString('hex');
+        done(err, token);
+      });
+    },
+    function(token, done) {
+      User.findOne({ email: req.body.email.toLowerCase() }, function(err, user) {
+        if (!user) {
+          req.flash('errors', { msg: 'No account with that email address exists.' });
+          return res.redirect('/forgot');
+        }
+        user.resetPasswordToken = token;
+        user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
+        user.save(function(err) {
+          done(err, token, user);
+        });
+      });
+    },
+    function(token, user, done) {
+      var transporter = nodemailer.createTransport({
+        service: 'Mandrill',
+        auth: {
+          user: secrets.mandrill.user,
+          pass: secrets.mandrill.password
+        }
+      });
+      var mailOptions = {
+        to: user.email,
+        from: 'hackathon@starter.com',
+        subject: 'Reset your password on Hackathon Starter',
+        text: 'You are receiving this email because you (or someone else) have requested the reset of the password for your account.\n\n' +
+          'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
+          'http://' + req.headers.host + '/reset/' + token + '\n\n' +
+          'If you did not request this, please ignore this email and your password will remain unchanged.\n'
+      };
+      transporter.sendMail(mailOptions, function(err) {
+        req.flash('info', { msg: 'An e-mail has been sent to ' + user.email + ' with further instructions.' });
+        done(err, 'done');
+      });
+    }
+  ], function(err) {
+    if (err) {
+      return next(err);
+    }
+    res.redirect('/forgot');
+  });
 };

+ 13 - 0
models/user.js

@@ -52,4 +52,17 @@ userSchema.methods.comparePassword = function(candidatePassword, cb) {
 };
 
 
+/********** Gravatar **************/
+
+userSchema.methods.gravatar = function(size) {
+  if (!size) {
+    size = 200;
+  }
+  if (!this.email) {
+    return 'https://gravatar.com/avatar/?s=' + size + '&d=retro';
+  }
+  var md5 = crypto.createHash('md5').update(this.email).digest('hex');
+  return 'https://gravatar.com/avatar/' + md5 + '?s=' + size + '&d=retro';
+};
+
 module.exports = mongoose.model('User', userSchema);

+ 0 - 177
src/index.html

@@ -1,177 +0,0 @@
-<!DOCTYPE html>
-<head>
-   <meta charset="utf-8">
-   <meta http-equiv="x-ua-compatible" content="ie=edge">
-   <meta name="viewport" content="width=device-width, initial-scale=1">
-   <title>Jury'd - You Be the Judge</title>
-   <meta name="description" content="Juryd Judging Everything">
-   <link rel="stylesheet" href="css/style.min.css">
-</head>
-<body>
-
- <nav id="mainNav" class="navbar navbar-default navbar-fixed-top">
-     <div class="container">
-         <div class="navbar-header">
-             <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse">
-                 <span class="sr-only">Toggle navigation</span>
-                 <span class="icon-bar"></span>
-                 <span class="icon-bar"></span>
-                 <span class="icon-bar"></span>
-             </button>
-             <a class="navbar-brand page-scroll" href="#page-top">Juryd</a>
-         </div>
-
-         <div class="collapse navbar-collapse" id="navbar-collapse">
-             <ul class="nav navbar-nav navbar-right">
-                 <li><a class="btn page-scroll" href="login">Sign In</a></li>
-             </ul>
-         </div>
-     </div>
- </nav>
-
-<header>
-   <div class="container-fluid">
-      <div class="header-content">
-         <div class="header-content-inner">
-            <div class="row">
-               <div class="col-sm-8">
-                  <h1>Juryd</h1>
-                  <h2>You be the Judge</h2>
-                  <hr>    
-                  <h4>Juryd is an application that allows the participants to register and vote.</h4>
-               </div>
-               <div class="col-sm-4">
-                  <img src="img/ribbon.svg" class="svg">
-               </div>
-            </div>
-         </div>
-		</div>
-   </div>
-</header>
-
-<section id="about">
-   <div class="container">
-      <div class="row">
-         <div class="col-lg-8">
-            <img src="img/ipad.svg" class="svg">
-         </div>
-         <div class="col-sm-4 text-center">
-            <h2 class="section-heading">How It Works</h2>
-            <hr class="light">
-            <p>Some Copy</p>
-         </div>
-      </div>
-   </div>
-</section>
-
-<section id="details">
-   <div class="container">
-      <div class="row text-center">
-         <div class="col-sm-4">
-            <h3 class="section-heading">Juried Competition</h3>
-            <p>Some Copy</p>
-         </div>
-         <div class="col-sm-4">
-            <h3 class="section-heading">Registrant Jury</h3>
-            <p>Some Copy</p>
-         </div>
-         <div class="col-sm-4">
-            <h3 class="section-heading">Registered Judges</h3>
-            <p>Some Copy</p>
-         </div>
-      </div>
-   </div>
-</section>
-
-<section id="features">
-   <div class="container">
-      <div class="row text-center">
-         <div class="col-sm-6">
-            <h2 class="section-heading">Features</h2>
-            <p>Some Copy</p>
-            <img src="img/ribbon.svg" class="svg icon-small blue-ribbon">
-            <img src="img/ribbon.svg" class="svg icon-small red-ribbon">
-            <img src="img/ribbon.svg" class="svg icon-small yellow-ribbon">
-         </div>
-         <div class="col-sm-6">
-            <img src="img/phone.svg" class="svg">
-         </div>
-      </div>
-   </div>
-</section>
-
-<section id="account">
-   <div class="container">
-      <div class="row text-center">
-         <div class="col-sm-6">
-            <h2 class="section-heading">About</h2>
-            <p>Some Copy</p>
-         </div>
-         <div class="col-sm-6">
-            <h2 class="section-heading">About</h2>
-            <p>Some Copy</p>
-         </div>
-      </div>
-   </div>
-</section>
-
-<section id="call">
-   <div class="container">
-      <div class="row">
-         <div class="col-sm-4 col-lg-offset-2 text-center">
-            <h4 class="section-heading">Connect</h4>
-            <hr class="light">
-            <p>(Social Media Icons)</p>
-         </div>
-         <div class="col-sm-4 text-center">
-            <h4 class="section-heading">Get the Latest</h4>
-            <form class="form-inline">
-               <div class="form-group">
-                  <label class="sr-only" for="exampleInputAmount">Email Addresss</label>
-                  <div class="input-group">
-                     <input type="text" class="form-control" id="email-address" placeholder="Email Address">
-                     <div class="input-group-addon">
-                        <button type="submit" class="btn btn-primary">GO</button>
-                     </div>
-                  </div>
-               </div>
-            </form>
-         </div>
-      </div>
-   </div>
-</section>
-
-
-<footer class="bg-dark">
-   <div class="container bg-dark">
-      <div class="row">
-         <div class="col-sm-3 col-sm-offset-2">
-            <h5>About</h5>
-            <p>Documentation</p>
-            <p>History</p>
-            <p>About Us</p>
-         </div>
-         <div class="col-sm-3">
-            <h5>Support</h5>
-            <p>System Status</p>
-            <p>Support Ticket</p>
-            <p>Account Preferences</p>
-         </div>
-         <div class="col-sm-3">
-            <h5>About</h5>
-            <p>Contact</p>
-            <p>Privacy Policy</p>
-            <p>Terms of Use</p>
-         </div>
-      </div>
-   </div>
-</footer>
-   
-<script src="js/lib.min.js"></script>
-<script src="js/main.min.js"></script>
- <!--[if lt IE 9]>
-   <script src="js/html5shiv.min.js"></script>
-   <script src="js/respond.min.js"></script>
- <![endif]-->
-</body>
-</html>

+ 15 - 0
src/views/account/forgot.jade

@@ -0,0 +1,15 @@
+extends ../layout
+
+block content
+  .col-sm-8.col-sm-offset-2
+    form(method='POST')
+      legend Forgot Password
+      input(type='hidden', name='_csrf', value=_csrf)
+      .form-group
+        p Enter your email address below and we will send you password reset instructions.
+        label.control-label(for='email') Email
+        input.form-control(type='email', name='email', id='email', placeholder='Email', autofocus=true)
+      .form-group
+        button.btn.btn-primary(type='submit')
+          i.fa.fa-key
+          | Reset Password

+ 20 - 18
src/views/account/login.jade

@@ -1,21 +1,23 @@
 extends ../layout
 
 block content
-  .page-header
-    h3 Sign in
-  form.form-horizontal(method='POST')
-    input(type='hidden', name='_csrf', value=_csrf)
-    .form-group
-      label.col-sm-3.control-label(for='email') Email
-      .col-sm-7
-        input.form-control(type='email', name='email', id='email', placeholder='Email', autofocus=true)
-    .form-group
-      label.col-sm-3.control-label(for='password') Password
-      .col-sm-7
-        input.form-control(type='password', name='password', id='password', placeholder='Password')
-    .form-group
-      .col-sm-offset-3.col-sm-7
-        button.col-sm-3.btn.btn-primary(type='submit')
-          i.fa.fa-user
-          | Login
-        a.btn.btn-link(href='/forgot') Forgot your password?
+  section#partial
+        .container
+          .row
+            .col-sm-8.col-sm-offset-2
+              form.form-horizontal(method='POST')
+                input(type='hidden', name='_csrf', value=_csrf)
+                .form-group
+                  label.col-sm-3.control-label(for='email') Email
+                  .col-sm-7
+                    input.form-control(type='email', name='email', id='email', placeholder='Email', autofocus=true)
+                .form-group
+                  label.col-sm-3.control-label(for='password') Password
+                  .col-sm-7
+                    input.form-control(type='password', name='password', id='password', placeholder='Password')
+                .form-group
+                  .col-sm-offset-3.col-sm-7
+                    button.col-sm-3.btn.btn-primary(type='submit')
+                      i.fa.fa-user
+                      | Login
+                    a.btn.btn-link(href='/forgot') Forgot your password?

+ 108 - 0
src/views/account/profile.jade

@@ -0,0 +1,108 @@
+extends ../layout
+
+block content
+  .page-header
+    h3 Profile Information
+
+  form.form-horizontal(action='/account/profile', method='POST')
+    input(type='hidden', name='_csrf', value=_csrf)
+    .form-group
+      label.col-sm-3.control-label(for='email') Email
+      .col-sm-7
+        input.form-control(type='email', name='email', id='email', value='#{user.email}')
+    .form-group
+      label.col-sm-3.control-label(for='name') Name
+      .col-sm-7
+        input.form-control(type='text', name='name', id='name', value='#{user.profile.name}')
+    .form-group
+      label.col-sm-3.control-label(for='gender') Gender
+      .col-sm-4
+        label.radio.col-sm-4
+          input(type='radio', checked=user.profile.gender=='male', name='gender', value='male', data-toggle='radio')
+          span Male
+        label.radio.col-sm-4
+          input(type='radio', checked=user.profile.gender=='female', name='gender', value='female', data-toggle='radio')
+          span Female
+    .form-group
+      label.col-sm-3.control-label(for='location') Location
+      .col-sm-7
+        input.form-control(type='text', name='location', id='location', value='#{user.profile.location}')
+    .form-group
+      label.col-sm-3.control-label(for='website') Website
+      .col-sm-7
+        input.form-control(type='text', name='website', id='website', value='#{user.profile.website}')
+    .form-group
+      label.col-sm-3.control-label(for='gravatar') Gravatar
+      .col-sm-4
+        img(src="#{user.gravatar()}", class='profile', width='100', height='100')
+    .form-group
+      .col-sm-offset-3.col-sm-4
+        button.btn.btn.btn-primary(type='submit')
+          i.fa.fa-pencilr
+          | Update Profile
+
+  .page-header
+    h3 Change Password
+
+  form.form-horizontal(action='/account/password', method='POST')
+    input(type='hidden', name='_csrf', value=_csrf)
+    .form-group
+      label.col-sm-3.control-label(for='password') New Password
+      .col-sm-7
+        input.form-control(type='password', name='password', id='password')
+    .form-group
+      label.col-sm-3.control-label(for='confirmPassword') Confirm Password
+      .col-sm-7
+        input.form-control(type='password', name='confirmPassword', id='confirmPassword')
+    .form-group
+      .col-sm-offset-3.col-sm-4
+        button.btn.btn-primary(type='submit')
+          i.fa.fa-lock
+          | Change Password
+
+  .page-header
+    h3 Delete Account
+
+  form.form-horizontal(action='/account/delete', method='POST')
+    .form-group
+      p.col-sm-offset-3.col-sm-4 You can delete your account, but keep in mind this action is irreversible.
+      input(type='hidden', name='_csrf', value=_csrf)
+      .col-sm-offset-3.col-sm-4
+        button.btn.btn-danger(type='submit')
+          i.fa.fa-trash
+          | Delete my account
+
+  .page-header
+    h3 Linked Accounts
+  .form-horizontal
+    .form-group
+      .col-sm-offset-3.col-sm-4
+        if user.instagram
+          p: a.text-danger(href='/account/unlink/instagram') Unlink your Instagram account
+        else
+          p: a(href='/auth/instagram') Link your Instagram account
+      .col-sm-offset-3.col-sm-4
+        if user.google
+          p: a.text-danger(href='/account/unlink/google') Unlink your Google account
+        else
+          p: a(href='/auth/google') Link your Google account
+      .col-sm-offset-3.col-sm-4
+        if user.facebook
+          p: a.text-danger(href='/account/unlink/facebook') Unlink your Facebook account
+        else
+          p: a(href='/auth/facebook') Link your Facebook account
+      .col-sm-offset-3.col-sm-4
+        if user.twitter
+          p: a.text-danger(href='/account/unlink/twitter') Unlink your Twitter account
+        else
+          p: a(href='/auth/twitter') Link your Twitter account
+      .col-sm-offset-3.col-sm-4
+        if user.github
+          p: a.text-danger(href='/account/unlink/github') Unlink your GitHub account
+        else
+          p: a(href='/auth/github') Link your GitHub account
+      .col-sm-offset-3.col-sm-4
+        if user.linkedin
+          p: a.text-danger(href='/account/unlink/linkedin') Unlink your LinkedIn account
+        else
+          p: a(href='/auth/linkedin') Link your LinkedIn account

+ 23 - 21
src/views/account/register.jade

@@ -1,24 +1,26 @@
 extends ../layout
 
 block content
-  .page-header
-    h3 Sign up
-  form.form-horizontal(id='signup-form', method='POST')
-    input(type='hidden', name='_csrf', value=_csrf)
-    .form-group
-      label.col-sm-3.control-label(for='email') Email
-      .col-sm-7
-        input.form-control(type='email', name='email', id='email', placeholder='Email', autofocus)
-    .form-group
-      label.col-sm-3.control-label(for='password') Password
-      .col-sm-7
-        input.form-control(type='password', name='password', id='password', placeholder='Password')
-    .form-group
-      label.col-sm-3.control-label(for='confirmPassword') Confirm Password
-      .col-sm-7
-        input.form-control(type='password', name='confirmPassword', id='confirmPassword', placeholder='Confirm Password')
-    .form-group
-      .col-sm-offset-3.col-sm-7
-        button.btn.btn-success(type='submit')
-          i.fa.fa-user-plus
-          | Signup
+    section#partial
+          .container
+            .row
+              .col-sm-8.col-sm-offset-2
+                form.form-horizontal(id='signup-form', method='POST')
+                  input(type='hidden', name='_csrf', value=_csrf)
+                  .form-group
+                    label.col-sm-3.control-label(for='email') Email
+                    .col-sm-7
+                      input.form-control(type='email', name='email', id='email', placeholder='Email', autofocus)
+                  .form-group
+                    label.col-sm-3.control-label(for='password') Password
+                    .col-sm-7
+                      input.form-control(type='password', name='password', id='password', placeholder='Password')
+                  .form-group
+                    label.col-sm-3.control-label(for='confirmPassword') Confirm Password
+                    .col-sm-7
+                      input.form-control(type='password', name='confirmPassword', id='confirmPassword', placeholder='Confirm Password')
+                  .form-group
+                    .col-sm-offset-3.col-sm-7
+                      button.btn.btn-success(type='submit')
+                        i.fa.fa-user-plus
+                        | Signup

+ 17 - 0
src/views/account/reset.jade

@@ -0,0 +1,17 @@
+extends ../layout
+
+block content
+  .col-sm-8.col-sm-offset-2
+    form(method='POST')
+      legend Reset Password
+      input(type='hidden', name='_csrf', value=_csrf)
+      .form-group
+        label(for='password') New Password
+        input.form-control(type='password', name='password', value='', placeholder='New password', autofocus=true)
+      .form-group
+        label(for='confirm') Confirm Password
+        input.form-control(type='password', name='confirm', value='', placeholder='Confirm password')
+      .form-group
+        button.btn.btn-primary.btn-reset(type='submit')
+          i.fa.fa-keyboard-o
+          | Change Password

+ 25 - 0
src/views/contact.jade

@@ -0,0 +1,25 @@
+extends layout
+
+block content
+  .page-header
+    h3 Contact Form
+
+  form.form-horizontal(role='form', method='POST')
+    input(type='hidden', name='_csrf', value=_csrf)
+    .form-group
+      label(class='col-sm-2 control-label', for='name') Name
+      .col-sm-8
+        input.form-control(type='text', name='name', id='name', autofocus=true)
+    .form-group
+      label(class='col-sm-2 control-label', for='email') Email
+      .col-sm-8
+        input.form-control(type='text', name='email', id='email')
+    .form-group
+      label(class='col-sm-2 control-label', for='message') Body
+      .col-sm-8
+        textarea.form-control(type='text', name='message', id='message', rows='7')
+    .form-group
+      .col-sm-offset-2.col-sm-8
+        button.btn.btn-primary(type='submit')
+          i.fa.fa-envelope
+          | Send

+ 72 - 0
src/views/home.jade

@@ -0,0 +1,72 @@
+extends layout
+
+block content
+header
+.container-fluid
+  .header-content
+    .header-content-inner
+      .row
+        .col-sm-8
+          h1 Juryd
+          h2 You be the Judge
+          hr
+          h4 Juryd is an application that allows the participants to register and vote.
+        .col-sm-4
+          img.svg(src='img/ribbon.svg')
+    section#about
+      .container
+        .row
+          .col-lg-8
+            img.svg(src='img/ipad.svg')
+          .col-sm-4.text-center
+            h2.section-heading How It Works
+            hr.light
+            p Some Copy
+    section#details
+      .container
+        .row.text-center
+          .col-sm-4
+            h3.section-heading Juried Competition
+            p Some Copy
+          .col-sm-4
+            h3.section-heading Registrant Jury
+            p Some Copy
+          .col-sm-4
+            h3.section-heading Registered Judges
+            p Some Copy
+    section#features
+      .container
+        .row.text-center
+          .col-sm-6
+            h2.section-heading Features
+            p Some Copy
+            img.svg.icon-small.blue-ribbon(src='img/ribbon.svg')
+            img.svg.icon-small.red-ribbon(src='img/ribbon.svg')
+            img.svg.icon-small.yellow-ribbon(src='img/ribbon.svg')
+          .col-sm-6
+            img.svg(src='img/phone.svg')
+    section#account
+      .container
+        .row.text-center
+          .col-sm-6
+            h2.section-heading About
+            p Some Copy
+          .col-sm-6
+            h2.section-heading About
+            p Some Copy
+    section#call
+      .container
+        .row
+          .col-sm-4.col-lg-offset-2.text-center
+            h4.section-heading Connect
+            hr.light
+            p (Social Media Icons)
+          .col-sm-4.text-center
+            h4.section-heading Get the Latest
+            form.form-inline
+              .form-group
+                label.sr-only(for='exampleInputAmount') Email Addresss
+                .input-group
+                  input#email-address.form-control(type='text', placeholder='Email Address')
+                  .input-group-addon
+                    button.btn.btn-primary(type='submit') GO

+ 3 - 2
src/views/layout.jade

@@ -15,8 +15,9 @@ html
           .container
             .row
               .col-sm-8.col-sm-offset-2
-              include partials/flash
-              block content
+                include partials/flash
+                
+    block content
 
     include partials/footer