├── .bowerrc ├── .gitignore ├── LICENSE ├── README.md ├── app.js ├── bootcards-functions.js ├── bower.json ├── data ├── data.json └── index.js ├── package.json ├── public ├── bootcards-demo-app │ ├── css │ │ └── bootcards-demo-app.css │ ├── images │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── bootcards-splash-1024x748.png │ │ ├── bootcards-splash-1024x748@2x.png │ │ ├── bootcards-splash-320x460.png │ │ ├── bootcards-splash-320x460@2x.png │ │ ├── bootcards-splash-320x548.png │ │ ├── bootcards-splash-320x548@2x.png │ │ ├── bootcards-splash-768x1004.png │ │ └── bootcards-splash-768x1004@2x.png │ └── js │ │ └── bootcards-demo-app.js ├── images │ ├── Annabelle Malcomb.jpg │ ├── Arthur Dube.jpg │ ├── Arthur Edge.jpg │ ├── Brandon Aponte.jpg │ ├── Carlos Stuart.jpg │ ├── Chiam See Tong.jpg │ ├── Chris Grocott.jpg │ ├── Chris Massie.jpg │ ├── Christian Szot.jpg │ ├── Clinton Quincy.jpg │ ├── Cody Swinford.jpg │ ├── Curtis Rowland.jpg │ ├── Danny Shin.jpg │ ├── Darren Sizelove.jpg │ ├── Darryl Thornell.jpg │ ├── David Peters.jpg │ ├── David Tay.jpg │ ├── Donna Mazzola.jpg │ ├── Earl Saub.jpg │ ├── Elsie Garner.jpg │ ├── Evelyn Dwyer.jpg │ ├── Fred Hafer.jpg │ ├── Gideon Henry.jpg │ ├── Gregory Gant.jpg │ ├── Gurmit Sim.jpg │ ├── Harry Harkins.jpg │ ├── Jack Floyd.jpg │ ├── Jack Levy.jpg │ ├── Jack Ortiz.jpg │ ├── Jacob Monte.jpg │ ├── James Smith.jpg │ ├── Jamie Biddy.jpg │ ├── Janet Helman.jpg │ ├── Janet McKnight.jpg │ ├── Jay Muir.jpg │ ├── Jeffery Kahle.jpg │ ├── Jerry Bess.jpg │ ├── Jerry Greeley.jpg │ ├── Jerry Williamson.jpg │ ├── John Randle.jpg │ ├── Joseph Barish.jpg │ ├── Josephine Driscoll.jpg │ ├── Kelly Palin.jpg │ ├── Kelly Podesta.jpg │ ├── Kristin Rayner.jpg │ ├── Lance McHaney.jpg │ ├── Larry Drury.jpg │ ├── Laura Johnson.jpg │ ├── Laura Tejada.jpg │ ├── Lee Seng Choh.jpg │ ├── Lim Yew Jin.jpg │ ├── Lois Brush.jpg │ ├── Louisa Coale.jpg │ ├── Manuel Mills.jpg │ ├── Margaret Fielding.jpeg │ ├── Mark Booth.jpg │ ├── Mathew Salone.jpg │ ├── Matthew Shipster.jpg │ ├── Max Cuthbert.jpeg │ ├── Nelson Raia.jpg │ ├── Noreen Mends.jpg │ ├── Norman Weimer.jpg │ ├── Poh Seng Miang.jpg │ ├── Richard Bailey.jpg │ ├── Ricky Lee.jpg │ ├── Robert Caban.jpg │ ├── Robert Jarrell.jpg │ ├── Ronald Tee.jpg │ ├── Roy Moultrie.jpeg │ ├── Roy Rock.jpeg │ ├── Ryan Smith.jpg │ ├── Ryan Sydnor.jpg │ ├── Sharon Burns.jpg │ ├── Simon Primm.jpg │ ├── Sofia Acey.jpg │ ├── Stacy Korner.jpg │ ├── Stanley Marchese.jpg │ ├── Tara Wasserman.jpg │ ├── Teddy Skolnik.jpg │ ├── Terri Donner.jpg │ ├── Tony Hensley.jpg │ ├── Tyrone Studdard.jpg │ ├── Wayne Sherman.jpg │ └── Wee Kim Yaw.jpg └── snippets │ ├── base-card-form.html │ ├── chart.html │ ├── file-card.html │ ├── form-card.html │ ├── list-card.html │ ├── list-detailed.html │ ├── media-card.html │ ├── summary.html │ └── table.html ├── routes ├── company.js ├── contact.js ├── dashboard.js ├── docs.js ├── media.js ├── note.js └── settings.js └── views ├── activities_for_company.html ├── activities_for_contact.html ├── activity_edit.html ├── activity_form.html ├── charts.html ├── charts ├── closed_sales.html ├── current_month_forecast.html ├── database_size.html └── sales_product_type.html ├── companies.html ├── company.html ├── company_activity_edit.html ├── company_edit.html ├── contact.html ├── contact_activity_edit.html ├── contact_edit.html ├── contacts.html ├── contacts_for_parent.html ├── dashboard.html ├── dialog_buttons.html ├── docs.html ├── layout.hbs ├── notes.html ├── notes ├── file.html ├── media.html ├── text.html └── todo.html ├── settings.html └── settings_edit.html /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory" : "public/bower_components", 3 | "storage": { 4 | "packages": ".bower-cache", 5 | "registry": ".bower-registry" 6 | }, 7 | "tmp": ".bower-tmp" 8 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules/ 16 | bower_components/ 17 | .bower-cache/ 18 | .bower-registry/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014, 2015 Bootcards 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | bootcards-demo-app 2 | ================== 3 | 4 | Sample application to showcase the Bootcards UI framework. It can be viewed live at demo.bootcards.org. 5 | 6 | View the app with different devices (iOS, Android, PC) to see the near-native look. 7 | 8 | Important: Not all functions have been implemented: you cannot save, add or delete items for instance. 9 | 10 | This demo application uses the following Node plugins: 11 | 12 | Express (for the web app) 13 | express-hbs (using handlebar templates server side) 14 | express-pjax (pjax partials with express) 15 | moment (date/time handling) 16 | bower (clientside libraries package manager) 17 | 18 | The following client libraries are used: 19 | 20 | - jQuery 21 | - Bootstrap v3.3 22 | - jQuery UI (slide animations) 23 | - Font Awesome (icons) 24 | - morris.js (chart demos) 25 | - Raphael 26 | - Sizzle -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Bootcards Customers demo application 3 | */ 4 | 5 | var express = require('express'); 6 | 7 | //routes 8 | var company = require('./routes/company'); 9 | var contact = require('./routes/contact'); 10 | var note = require('./routes/note'); 11 | var media = require('./routes/media'); 12 | var settings = require('./routes/settings'); 13 | var dashboard = require('./routes/dashboard'); 14 | var snippets = require('./routes/docs'); 15 | var sampleData = require('./data'); 16 | 17 | var pjson = require('./package.json'); //read the package.json file to get the current version 18 | 19 | var bc = require('./bootcards-functions'); //bootcards functions 20 | 21 | var http = require('http'); 22 | var path = require('path'); //work with paths 23 | var pjax = require('express-pjax'); //express pjax (partial reloads) 24 | var hbs = require('express-hbs'); //express handlebars 25 | var moment = require('moment'); //moment date formatting lib 26 | var app = express(); 27 | 28 | //app.use(express.compress()); 29 | //enable Express session support 30 | app.use( express.cookieParser() ); 31 | app.use( express.session({secret : 'QWERTY'})); 32 | 33 | app.set('port', 3000); 34 | app.engine( 'html', hbs.express3({ 35 | partialsDir : __dirname + '/views' 36 | })); 37 | app.set('view engine', 'html'); 38 | app.set('views', path.join(__dirname, 'views')); 39 | 40 | //pjax middleware for partials 41 | app.use(pjax()); 42 | 43 | //send session info to handlebars, check OS used to send correct stylesheet 44 | app.use(function(req, res, next){ 45 | 46 | var ua = req.headers['user-agent']; 47 | req.session.isAndroid = (ua.match(/Android/i) != null); 48 | req.session.isIos = (ua.match(/iPhone|iPad|iPod/i) != null); 49 | req.session.isDev = (process.env.NODE_ENV !='production'); 50 | req.session.test = (process.env.NODE_ENV); 51 | 52 | res.locals.session = req.session; 53 | 54 | next(); 55 | }); 56 | 57 | app.use(express.favicon()); 58 | app.use(express.urlencoded()); 59 | app.use(express.methodOverride()); 60 | app.use(app.router); 61 | 62 | var fiveDays = 5*86400000; 63 | 64 | app.use(express.static(path.join(__dirname, 'public'), { maxAge: fiveDays } ) ); 65 | 66 | //register a helper for date formatting using handlebars 67 | hbs.registerHelper("formatDate", function(datetime, format) { 68 | if (moment) { 69 | f = "ddd DD MMM YYYY HH:mm" 70 | return moment(datetime).format(f); 71 | } 72 | else { 73 | return datetime; 74 | } 75 | }); 76 | 77 | //helper to get the icon for a item type 78 | hbs.registerHelper("getIconForType", function(type) { 79 | return bc.getIconForType(type); 80 | }); 81 | 82 | //helper to get the number of data elements 83 | hbs.registerHelper('count', function(type) { 84 | switch (type) { 85 | case 'companies': 86 | return companies.length; 87 | case 'contacts': 88 | return contacts.length; 89 | case 'notes': 90 | return notes.length; 91 | case 'charts': 92 | return 4; 93 | } 94 | 95 | return 0; 96 | }); 97 | 98 | //helper to get the stylesheet for the current user agent 99 | hbs.registerHelper("getCSSforOS", function(session) { 100 | var bootCardsBase = "//cdnjs.cloudflare.com/ajax/libs/bootcards/1.1.2/css/"; 101 | if (session.isAndroid) { 102 | return ''; 103 | } else if (session.isIos) { 104 | return ''; 105 | } else { 106 | return ''; 107 | } 108 | }); 109 | 110 | hbs.registerHelper("isMobile", function(session) { 111 | return ''; 112 | }); 113 | 114 | //helper to get the app version 115 | hbs.registerHelper("getAppVersion", function() { 116 | return pjson.version; 117 | }); 118 | 119 | //read sample data 120 | companies = []; 121 | notes = []; 122 | contacts = []; 123 | 124 | sampleData.read(); 125 | 126 | //setup menu 127 | menu = [ 128 | { id : 'dashboard', name : 'Dashboard', title : 'Customers', icon : "fa-dashboard", active : false, url : '/dashboard'}, 129 | { id : 'companies', name : "Companies", title : 'Companies', icon : "fa-building-o", active : false, url : '/companies'}, 130 | { id : 'contacts', name : "Contacts", title : 'Contacts', icon : "fa-users", active : true, url : '/contacts'}, 131 | { id : 'notes', name : "Notes", title : 'Notes', icon : "fa-clipboard", active : false, url : '/notes'}, 132 | { id : 'charts', name : "Charts", title : 'Charts', icon : "fa-bar-chart-o", active : false, url : '/charts'}, 133 | { id : 'settings', name : "Settings", title : 'Settings', icon : "fa-gears", active : false, url : '/settings'} 134 | ]; 135 | 136 | // development only 137 | if ('development' == app.get('env')) { 138 | app.use(express.errorHandler()); 139 | } 140 | 141 | //routes 142 | app.get('/', dashboard.list); 143 | app.get('/dashboard', dashboard.list); 144 | 145 | app.get('/companies', company.list); 146 | app.get('/companies/add', company.add); 147 | app.get('/companies/:id', company.read); 148 | app.put('/companies/:id', company.save); 149 | app.get('/companies/:id/edit', company.edit); 150 | 151 | app.get('/companies/:id/notes', company.listNotes); 152 | app.get('/companies/:id/notes/add', company.addNote); 153 | app.get('/companies/:id/notes/:noteId', company.readNote); 154 | app.get('/companies/:id/notes/:noteId/edit', company.editNote); 155 | app.put('/companies/:id/notes', company.saveNote); //save new note 156 | app.put('/companies/:id/notes/:noteId', company.saveNote); 157 | 158 | app.get('/companies/:id/contacts/add', company.addContact); 159 | 160 | app.get('/contacts', contact.list); //list 161 | app.put('/contacts', contact.save); //save new contact 162 | app.get('/contacts/add', contact.add); 163 | app.get('/contacts/:id', contact.read); //read a contact 164 | app.put('/contacts/:id', contact.save); //save a specific contact 165 | app.get('/contacts/:id/edit', contact.edit); 166 | 167 | app.get('/contacts/:id/notes', contact.listNotes); 168 | app.get('/contacts/:id/notes/add', contact.addNote); 169 | app.get('/contacts/:id/notes/:noteId', contact.readNote); 170 | app.get('/contacts/:id/notes/:noteId/edit', contact.editNote); 171 | app.put('/contacts/:id/notes', contact.saveNote); //save new note in contact 172 | app.put('/contacts/:id/notes/:noteId', contact.saveNote); 173 | 174 | app.get('/notes', note.list); 175 | app.get('/notes/add/:contactId', note.add); 176 | app.get('/notes/add', note.add); 177 | app.get('/notes/:id', note.read); 178 | app.get('/notes/:id/edit', note.edit); 179 | app.put('/notes', note.save); 180 | 181 | app.get('/charts', media.list); 182 | 183 | app.get('/settings', settings.read); 184 | app.get('/settings/edit', settings.edit); 185 | 186 | app.get('/snippets/:id', snippets.show); 187 | 188 | http 189 | .createServer(app) 190 | .listen(app.get('port'), function(){ 191 | console.log('Bootcards demo app listening on port ' + app.get('port')); 192 | } 193 | ); 194 | -------------------------------------------------------------------------------- /bootcards-functions.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | //sort an object by a property 4 | exports.sortByField = function(data, field) { 5 | 6 | return data.sort( 7 | 8 | function(a,b) { 9 | return ( (a[field] > b[field]) ? 1 : ((a[field] < b[field]) ? -1 : 0)); 10 | } 11 | 12 | ); 13 | 14 | } 15 | 16 | //set the active menu 17 | exports.getActiveMenu = function(menu, id) { 18 | 19 | for (var i=0; i
Date
23 | 24 | 25 | 26 | 27 |Date
29 | 30 | 31 | ... 32 | 33 |City, Country
10 |Type
13 |Revenue
14 |City, Country
26 |Type
29 |Revenue
30 |Name | Sales value |
---|---|
Guy Bardsley | 550 |
Adam Callahan | 1500 |
Arlo Geist | 3750 |
Sheila Hutchins | 3500 |
Jeanette Quijano | 1250 |
Simon Sweet | 5250 |
Total | 15800 |
' + data + '
'
20 | });
21 |
22 |
23 | });
24 |
25 | };
--------------------------------------------------------------------------------
/routes/media.js:
--------------------------------------------------------------------------------
1 | var bc = require('../bootcards-functions.js');
2 |
3 | exports.list = function(req, res){
4 |
5 | res.renderPjax('charts', {
6 | menu: bc.getActiveMenu(menu, 'charts')
7 | });
8 |
9 | };
--------------------------------------------------------------------------------
/routes/note.js:
--------------------------------------------------------------------------------
1 | var bc = require('../bootcards-functions.js');
2 | var moment = require('moment');
3 |
4 | exports.list = function(req, res) {
5 | res.renderPjax('notes', {
6 | activities: notes,
7 | note : notes[0],
8 | menu: bc.getActiveMenu(menu, 'notes')
9 | });
10 | };
11 |
12 | exports.read = function(req, res) {
13 |
14 | var note = bc.getNoteById(req.params.id);
15 |
16 | if (note != null) {
17 |
18 |
19 |
20 | res.renderPjax( exports.getNotePartialRenderer(note.type) , {
21 | activities : notes,
22 | note: note,
23 | menu: bc.getActiveMenu(menu, 'notes')
24 | });
25 |
26 | }
27 |
28 | }
29 |
30 | exports.edit = function(req, res) {
31 |
32 | res.renderPjax('activity_edit', {
33 | activities : notes,
34 | activity: bc.getNoteById(req.params.id),
35 | menu: bc.getActiveMenu(menu, 'notes')
36 | });
37 |
38 | }
39 |
40 | /*a note can be added to a contact or company*/
41 | exports.add = function(req, res) {
42 |
43 | if (req.params.contactId) {
44 |
45 | var contact = bc.getContactById(req.params.contactId);
46 |
47 | res.renderPjax('activity_edit', {
48 | activities: notes,
49 | activity : {
50 | date : new Date(),
51 | isNew : true
52 | },
53 | contact: contact,
54 | menu: bc.getActiveMenu(menu, 'notes')
55 | });
56 | } else {
57 |
58 | res.renderPjax('activity_edit', {
59 | activities: notes,
60 | activity : {
61 | date : new Date(),
62 | isNew : true
63 | },
64 | menu: bc.getActiveMenu(menu, 'notes')
65 | });
66 |
67 | }
68 | };
69 |
70 | exports.save = function(req, res) {
71 |
72 | //retrieve the parent contact
73 | var contact = bc.getContactById(req.body.contactId);
74 |
75 | if (contact != null) {
76 | //found the contact: add new note
77 |
78 | var note = {
79 | type: req.body.type,
80 | subject: req.body.subject,
81 | date: moment(req.body.date),
82 | parentIds : [contact.id],
83 | details: req.body.details
84 | }
85 |
86 | notes.push(note);
87 |
88 | res.renderPjax('contact', {
89 | contacts:contacts,
90 | menu: bc.getActiveMenu(menu, 'notes'),
91 | contact: contact,
92 | activities : bc.getNotesForParent(contact.id),
93 | });
94 | }
95 |
96 | }
97 |
98 | //returns the name of a partial used to render a specific note type
99 | exports.getNotePartialRenderer = function(type) {
100 | var renderWith = 'notes/text'; //default template
101 |
102 | if (type == 'file') {
103 | renderWith = 'notes/file';
104 | } else if (type == 'todo') {
105 | renderWith = 'notes/todo';
106 | } else if (type == 'text') {
107 | renderWith = 'notes/text';
108 | } else if (type == 'media') {
109 | renderWith = 'notes/media';
110 | }
111 |
112 | return renderWith;
113 |
114 | }
115 |
116 |
--------------------------------------------------------------------------------
/routes/settings.js:
--------------------------------------------------------------------------------
1 | var bc = require('../bootcards-functions.js');
2 |
3 | exports.read = function(req, res){
4 |
5 | res.renderPjax('settings', {
6 | menu: bc.getActiveMenu(menu, 'settings')
7 | });
8 |
9 | };
10 |
11 | exports.edit = function(req, res){
12 |
13 | res.renderPjax('settings_edit', {
14 | menu: bc.getActiveMenu(menu, 'settings')
15 | });
16 |
17 | };
--------------------------------------------------------------------------------
/views/activities_for_company.html:
--------------------------------------------------------------------------------
1 | {{!< layout}}
2 |
3 | {{formatDate this.date "long"}}
24 | 25 | {{else}} 26 |{{formatDate this.date "long"}}
24 | 25 | {{else}} 26 |{{this.city}}, {{this.country}}
37 |Customer Type
40 |$35,000 Revenue YTD
41 |{{this.company}}
36 | 37 | {{/each}} 38 | 39 |{{this.company}}
22 | 23 | 24 | {{else}} 25 | 26 |{{formatDate this.date "long"}} 34 |
35 | 36 | {{else}} 37 |Added by Jack Herbert on 5 Mar 2014
23 |{{note.details}}
26 |Username
18 |Password
22 |Type
26 |