Showing posts with label cacoo. Show all posts
Showing posts with label cacoo. Show all posts

2.16.2012

Nomination part 8 - ui


Version en español


This post is part of the series about creating an app with node.js, express for Facebook




Lets see how the ui will end up, lets see the mocks i did on cacoo, this is the index


Dashboard


Good lets try to do them in jade


We are going to use the structure from html5boilerplate with the css, we are going to add some small things at the end i will show all the css added


Lets look at the "index.jade"

#header-container
    header.wrapper.clearfix
        h1.title= t('app.name')
#main-container
    #main.wrapper.clearfix
        table.att_table
            thead
                tr
                    th= t('index.features')
            tbody
                tr
                    td= t('index.feature1')
                tr
                    td= t('index.feature2')
                tr
                    td= t('index.feature3')
                tr
                    td= t('index.feature4')
                tr
                    td= t('index.feature5')
                    td
                        a(href="/login")
                            img(src="/images/cwf.png")
        - if (error)
            .error #{error}
#footer-container
    footer.wrapper
        .ficon
            img(src="/images/ipn.png")


We are adding our title a table with the features, an image to load to Facebook, a placeholder for show any error and a footer with our logo, will look like this:





"Dashboard.jade"

#header-container
    header.wrapper.clearfix
        .new
            a#nn(href="#")= t('dashboard.new_nomination')
        .loading
            img(src="/images/loading.png")
        .exit
            a(href="/logout")= t('dashboard.exit')
#main-container
    #main.wrapper.clearfix
        .nominations
            div.nheader.title #{t('dashboard.mine')}
            ul
                li
                    input(type="checkbox", checked="true")
                    label  Nomination 1
                li
                    input(type="checkbox")
                    label  Nomination 2
                li
                    input(type="checkbox")
                    label  Nomination 3
            div.title #{t('dashboard.appear')}
            ul
                li
                    input(type="checkbox")
                    label  Nomination a
                li
                    input(type="checkbox")
                    label  Nomination b
                li
                    input(type="checkbox")
                    label  Nomination d
                li
                    input(type="checkbox")
                    label  Nomination e
                li
                    input(type="checkbox")
                    label  Nomination f
                li
                    input(type="checkbox")
                    label  Nomination g
                li
                    input(type="checkbox")
                    label  Nomination h

            div.title #{t('dashboard.voted')}
            ul
                li
                    input(type="checkbox")
                    label  Nomination i
                li
                    input(type="checkbox")
                    label  Nomination ii
                li
                    input(type="checkbox")
                    label  Nomination iii
        .details
            fieldset.fs
                legend Nomination 1
                .header
                    .users #{t('dashboard.users')}
                    .add
                        a#am(href="#")#{t('dashboard.add_more')}
                    .date Date
                br
                .list
                    table.userst
                        thead
                            tr
                                th #{t('dashboard.name')}
                                th #{t('dashboard.votes')}
                                th
                        tbody
                            tr
                                td User 1
                                td 2
                                td
                                    a(href="#") #{t('dashboard.vote')}
                                    | /
                                    a(href="#") #{t('dashboard.erase')}
                            tr
                                td User 2
                                td 3
                                td
                                    a(href="#") #{t('dashboard.vote')}
                                    | /
                                    a(href="#") #{t('dashboard.erase')}
                            - for (var i =4; i                                tr
                                    td User #{i}
                                    td #{Math.floor(Math.random()*11)}
                                    td
                                        a(href="#") #{t('dashboard.vote')}
                                        | /
                                        a(href="#") #{t('dashboard.erase')}
                .btns
                    button.myButton#end #{t('dashboard.end_nomination')}
                    button.myButton#remove #{t('dashboard.remove_me')}
                    button.myButton#cancel #{t('dashboard.cancel')}
        .images
        .logo logo
        .ficon
            img(src="/images/ipn.png")
#footer-container
    footer.wrapper

#dialog-new(title="Create new nomination")
    p.validateTips All form fields are required
    form
        fieldset
            label.forml(for="name") Name:
            input#name.text.ui-widget-content.ui-corner-all(type="text", name="name")
            br
            label.forml(for="email") End date:
            input#datep.text.ui-widget-content.ui-corner-all(type="text", name="datep")

#dialog-add(title="Add friends")
    form
        fieldset
            ol#selectable
                li.ui-state-default 1
                li.ui-state-default 2
                li.ui-state-default 3
                li.ui-state-default 4
                li.ui-state-default 5
                li.ui-state-default 6


We are adding a lot of things that we are going to erase later, but its to see if this is looking ok, the only interesting thing here will be the "for" to add some users around lines 89-96, we will fill out this from the app later.


We have two lost forms at the end, but we will hide them because we will use them as dialogs with jquery-ui.


Lets update our layout to add our css and the js needed

link(rel="stylesheet", href="/stylesheets/jquery-ui.css", type="text/css")

script(defer, src="/javascripts/lib/jquery-ui.min.js")


Lets start our dialogs and a date picker, we add al this to "script.js"

    $( "#datep" ).datepicker();
    $( "#selectable" ).selectable();
    $("#nn").click(function(){
        $( "#dialog-new" ).dialog( "open" );
    });
    $("#am").click(function(){
        $( "#dialog-add" ).dialog( "open" );
    });
    $( "#dialog-new" ).dialog({
  autoOpen: false,
  height: 300,
  width: 450,
  modal: true,
  buttons: {
   "Create a nomination": function() {
    $( this ).dialog( "close" );
   },
   Cancel: function() {
    $( this ).dialog( "close" );
   }
  },
  close: function() {
   //TODO:
  }
 });
    $( "#dialog-add" ).dialog({
        autoOpen: false,
  height: 300,
  width: 450,
  modal: true,
  buttons: {
   "Add friend(s)": function() {
    $( this ).dialog( "close" );
   },
   Cancel: function() {
    $( this ).dialog( "close" );
   }
  },
  close: function() {
   //TODO:
  }
 });


Then we also one that only one checkbox can be selected at the time and also when they select a label the checkbox get selected ask, we use some query magic and add it also to "script.js"

    function checkOne(){
        var currentEl = $(this);
        $("input:checked").each(function(){
            $(this).attr('checked', false);
        });
        if (currentEl.is('input')){
            currentEl.attr('checked', true);
            return;
        }
        var checkbox = currentEl.siblings('input');
        checkbox.attr('checked', !checkbox.attr('checked'));
    }
    $(".nominations ul label").click(checkOne);
    $(".nominations ul input").click(checkOne);


Here we just listen to the click event in the checkbox and the label, we deselect the already selected and we select the current one

We end up with something like this:



and the dialogs







And the css that we are going to add is:

/* =============================================================================
   Primary styles
   Author: mrpix
   ========================================================================== */
.logo{
    text-align: center;
}
.list{
    margin-top: 20px;
}
.btns{
    margin-top: 5%;
}
#main-container{
    height: 500px;
}
#main{
    height: 100%;
}
.userst th, .userst td{
    border: 1px solid #ededed;
}
.userst th{
    background-color: #ededed
}
.userst{
    width: 95%;
}
.btns button{
    margin-left: 5%;
}
.users, .add{
    float: left;
    margin-right: 30%;
}
.date{
    float: right;
}
.nominations .title{
    background-color: #ededed;
    width: 60%;
    padding: 0 0 0 40px;
    border: 1px solid black;
    text-align: left;
}
.nominations ul{
    border: 1px solid black;
    width: 60%;
    height: 120px;
    overflow: auto;
    margin-bottom: 0;
}
.nheader{
    -webkit-border-top-left-radius: 13px;
    -webkit-border-top-right-radius: 13px;
    -moz-border-radius-topleft: 13px;
    -moz-border-radius-topright: 13px;
    border-top-left-radius: 13px;
    border-top-right-radius: 13px;
}
.nominations li{
    list-style:none;
    background-color: white;
    margin-left: 0;
    border-bottom: 1px solid #ededed;
    margin-left: -25px;
}
label{
    padding-left: 5px;
    padding-right: 5px;
}
.nominations{
    width: 30%;
    height: 90%;
    overflow: auto;
    float: left;
}
.details{
    width: 69%;
    height: 90%;
    overflow: auto;
    float: left;
}
.new{
    float: left;
}
.loading{
    text-align: center;
}
.exit{
    float: right;
    margin-top: -1.7em;
}

.copy {
    text-align: center;
}
.ficon{
 float: right;
}

.title{
 text-align: center;
}
.att_table
{

 background: #fff;
 margin: 0px 40px 3px 50px;
 width: 90%;
 border-collapse: collapse;
 text-align: left;
}
.att_table th
{

 color: #039;
 padding: 10px 8px;
 border-bottom: 2px solid #6678b1;
}
.att_table td
{
 border-bottom: 1px solid #ccc;
 color: #669;
 padding: 6px 8px;
}
.att_table tbody tr:hover td
{
 color: #00c;
}
.myButton {
    -moz-box-shadow:inset 0px 1px 0px 0px #ffffff;
 -webkit-box-shadow:inset 0px 1px 0px 0px #ffffff;
 box-shadow:inset 0px 1px 0px 0px #ffffff;
 background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #ededed), color-stop(1, #dfdfdf) );
 background:-moz-linear-gradient( center top, #ededed 5%, #dfdfdf 100% );
 filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ededed', endColorstr='#dfdfdf');
 background-color:#ededed;
 -moz-border-radius:6px;
 -webkit-border-radius:6px;
 border-radius:6px;
 border:1px solid #dcdcdc;
 display:inline-block;
 color:#777777;
 font-family:arial;
 font-size:15px;
 font-weight:bold;
 padding:6px 24px;
 text-decoration:none;
 text-shadow:1px 1px 0px #ffffff;
}.myButton:hover {
 background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #dfdfdf), color-stop(1, #ededed) );
 background:-moz-linear-gradient( center top, #dfdfdf 5%, #ededed 100% );
 filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#dfdfdf', endColorstr='#ededed');
 background-color:#dfdfdf;
}.myButton:active {
 position:relative;
 top:1px;
}
#dialog-add, #dialog-new{
    display: none;
}
#selectable .ui-selecting { background: #FECA40; }
#selectable .ui-selected { background: #F39814; color: white; }
#selectable { list-style-type: none; margin: 0; padding: 0; }
#selectable li { margin: 3px; padding: 1px; float: left; width: 50px; height: 40px; font-size: 2em; text-align: center; }


If you need help understanding the css let me know in the comments

The design is not the best but we are making progress, any suggestions is welcome


Fork the code in github:


Greetings 

2.10.2012

Nomination part 4 - models


This post is part of the series about creating an app with node.js, express for Facebook


For this part we will use the mongoose module that we already have installed, so no updates to the package.json.

Our model looks more or less to something like this:


Nomination is our main doc that have all the attributes to create a nomination, this one have a list of users, users its a doc that is embedded in nomination, i put it like this so you can see the separation but its not another doc in mongodb but we need to create the schema definition anyway in mongoose.

To add our modules lets create a folder called "models" or whatever in the root path of our app, inside that lets create a file called "nomination.js".

Lets see our code in nomination.js:

function defineModels(mongoose, fn) {
 var Schema = mongoose.Schema,
 Nomination, User;

/**
 *
 * Model: User
 *
 */
 User = new Schema({
 '_id' : String, //fb id
 'name' : String, //user name
 'votes' : Number //number of votes for this user
 });

 /**
 *
 * Model: Nomination
 *
 */
 Nomination = new Schema({
 'name' : String,
 'owner' : String, //who is the owner of the nomination
 'endDate' : Date, //when this nomination is going to end
 'users' : [User], //users added to this nomination
 'voters' : [String], //all the dudes that vote this nomination
 'category' : String,
 'sub_cat' : String,
 'active' : Boolean, //nomination finished
 'erased' : [String] //people erased by facebook id, we wont be able to re-add them
 });

 Nomination.virtual('id')
 .get(function() {
 return this._id.toHexString();
 });

 Nomination.pre('save', function(next) {
 //we could use this later
 next();
 });

 mongoose.model('Nomination', Nomination);

fn();
}

exports.defineModels = defineModels;

Line 1: Creating our main function to export later

2-3: our internal variables and a reference to schema

5-14: user model by "new Schema", with id gathered from fb, name and number of votes this user will have, all of type of String excepto votes

16-31: Our nomination model, this will have name, owner, date to deactivate it, list of nominated users, people that already voted here, category and a subcategory, activation flag and the list of deleted users, in this case we will let mongo to create an id, even if we dont put it, doesnt mean it isn't present in the document, we are just letting mongoose do the work

33-36: A virtual function to get the id, just in case

38-41: a small save middleware, before saving a doc of this type, mongoose will first call this function, for this example im not putting nothing yet, "next()" its to call the next function that in this case will be the actual save.

43: declaring the model

45: we call the callback passed, usually its to make an instance of the model to start using it

48: Exporting the function

Nice, we have our models defined, lets create something to use it, i called it "controller", from there we will handle the mongoose functionality so we are going to expose only whats is necesary.

We need to create a "controllers" folder or whatever name and a file called "nominator.js" that is going to be our controller, this file will look like this:

var mongoose = require('mongoose'),
    models = require('../models/nomination.js'),
    Nomination, db;

models.defineModels(mongoose, function() {
  Nomination = mongoose.model('Nomination');
  db = mongoose.connect('mongodb://nominator:nominat0r@ds029257.mongolab.com:29257/nomi-nation');
});

var NOMINATOR = {};

function findIndexByKeyValue(obj, key, value)
{
    var l = obj.length;
    for (var i = 0; i < l; i++) {
  if (obj[i][key] == value) {
   return i;
  }
 }
 return null;
}

/**
 * Add vote
 * @nomination object
 * @voterId string
 * @userId string
 * @callback function
*/
NOMINATOR.vote = function(nomination, voterId, userId, callback) {
    //TODO: check we dont want more than 10 users per nomination
    var isPresent = -1;
    try {
        isPresent = nomination.voters.indexOf(voterId);
    }
    catch (e){
        isPresent = -1;
    }
    if (isPresent < 0){
        nomination.voters.push(voterId);
    }
    //TODO: change to an actual mongo function
    var index = findIndexByKeyValue(nomination.users, "_id", userId);
    nomination.users[index].votes += 1;
    nomination.save(callback);
    //Nomination.update({_id :nomination.id}, {users: nomination.users}, callback);
};

/**
 * Add user
 * @nomination object
 * @user object
 * @callback function
*/
NOMINATOR.addUser = function(nomination, user, callback) {
    //TODO: check we dont want more than 10 users per nomination
    var isPresent = nomination.erased.indexOf(user._id);
    if (isPresent >= 0){ callback(new Error('User can\'t be added'), null); return; }
    if (user instanceof Array){
        nomination.users = user;
    }else{
        nomination.users.push(user);
    }
    nomination.save(callback);
    //Nomination.update({_id :nomination.id}, {users: nomination.users}, callback);
};

/**
 * Erase user
 * @nomination object
 * @user object
 * @callback function
*/
NOMINATOR.eraseUser = function(nomination, user, callback) {
    nomination.users.remove(user); //erased from the users list
    nomination.erased.push(user._id); //add to the erased so we dont add them again
    nomination.save(callback);
    //Nomination.update({_id :nomination.id}, {users: nomination.users, erased: nomination.erased}, callback);
};

/**
 * Create nomination
 * @nomination object
 * @callback function
*/
NOMINATOR.createNomination = function(nomination, callback) {
    var myNomination = new Nomination(nomination);
    myNomination.save(callback);
};

/**
 * Erase nomination
 * @nominationid string
 * @callback function
*/
NOMINATOR.eraseNomination = function(nominationid, callback) {
    Nomination.erase({'_id' : nominationid }, callback);
};

/**
 * find nominations the user own
 * @userid fb userid
 * @callback function
 *
*/
NOMINATOR.findMyNominations = function(userId, callback) {
    Nomination.where('owner', userId)
        .where('active', true)
        .run(callback);
};

/**
 * find nominations the user voted
 * @userId fb userid
 * @callback function
 *
*/
NOMINATOR.findVoted = function(userId, callback) {
    Nomination.where('voters').in([userId])
        .where('active', true)
        .run(callback);
};

/**
 * find nominates where the user was nominated
 * @userId fb userid
 * @callback function
 *
*/
NOMINATOR.findNominated = function(userId, callback) {
    Nomination.where('users._id').in([userId])
        .where('active', true)
        .run(callback);
};

// Exporting functions
module.exports = NOMINATOR;

Line 1: loading the mongoose modules

2: loading the models, we only export one function

3: our local variables

5-8: we start using the function we exported in "models.js" and we make an instance of the model "nomination" and we connect to the db

10: Lets create our object that will hold all the functionality to export it at the end

11: A small helper function to look for indexes inside the arrays

23-47: Adding votes, lets first check if the user voted its already in the list, if not to added if yes, to add one vote to this victim nominated, we save the model that function will make this change to go directly to the mongodb, lets call the callback to show the results for whom call us, this callback will have as parameters "error" and "doc"

49-66: Adding users, first lets check the user its not in our list of people deleted so we don't put it again, if so lets throw an error, then lets check if we are getting an array or a single user and add them to the list of users and then we save and call the callback


68-79: Erasing users, first lets erase him for the list of users and then add them to the list of erased, save and call the callback


81-89: Creating nominations, we get as a parameter an object with the attributes needed to create a nomination, we make an instance of it and save it, calling the callback


91-125: Searching, search is easy using query, we use the base model and we keep adding whatever we are looking for, e.g,:  "where('owner', userId)", here we are looking where the nomination owner is the userId that we get as a parameter, then we check that this nomination is active and at the end we run the query with "run(callback)" and send the results to the callback, this will be the same for all the searching just with different parameters


128: Lets export our object with all the functionality 


We are done!!!, we have our models and our controller to handle them in our app, next time we will see how make some testing for our models and routes


The model picture was created using cacoo :) online


Fork the code in github:

Greetings