Author SHA1 Message Date
Jason Denney 01d1dcdcf1 WIP, got a component enabling/disabling on select. 2013-04-25 21:06:35 -04:00
97 changed files with 422 additions and 1726 deletions
-2
View File
@@ -7,6 +7,4 @@ tmp/
.rbenv-version .rbenv-version
.DS_Store .DS_Store
public/extjs public/extjs
public/assets
public/images/icons public/images/icons
config/database.yml
+2 -2
View File
@@ -14,12 +14,13 @@ gem 'decent_exposure', '~> 1.0.1'
gem 'devise', '~> 2.0.4' gem 'devise', '~> 2.0.4'
gem 'haml-rails', '~> 0.3.4' gem 'haml-rails', '~> 0.3.4'
gem 'jquery-rails', '~> 2.0' gem 'jquery-rails', '~> 2.0'
gem 'pg' gem 'sqlite3', '~> 1.3.5'
gem 'will_paginate', '~> 3.0.3' gem 'will_paginate', '~> 3.0.3'
# Gems used only for assets and not required # Gems used only for assets and not required
# in production environments by default. # in production environments by default.
group :assets do group :assets do
gem 'sass-rails', '~> 3.2.3'
gem 'coffee-rails', '~> 3.2.1' gem 'coffee-rails', '~> 3.2.1'
gem "twitter-bootstrap-rails", "~> 2.0.3" gem "twitter-bootstrap-rails", "~> 2.0.3"
@@ -33,7 +34,6 @@ group :development, :test do
gem 'rspec-rails', '~> 2.8.1' gem 'rspec-rails', '~> 2.8.1'
gem 'factory_girl_rails', '~> 1.2' gem 'factory_girl_rails', '~> 1.2'
gem 'pry', '~> 0.9.8' gem 'pry', '~> 0.9.8'
gem 'faker'
end end
group :test do group :test do
+14 -13
View File
@@ -1,6 +1,6 @@
GIT GIT
remote: https://github.com/spacemunkay/acts_as_loggable.git remote: https://github.com/spacemunkay/acts_as_loggable.git
revision: 8484dc1f7a58699705c5ffb593ebdf700a9f374a revision: 6033fe5a44878bb374f73fe473fed9a3396ddc74
specs: specs:
acts_as_loggable (0.0.7) acts_as_loggable (0.0.7)
activerecord (>= 3.0) activerecord (>= 3.0)
@@ -77,8 +77,6 @@ GEM
factory_girl_rails (1.7.0) factory_girl_rails (1.7.0)
factory_girl (~> 2.6.0) factory_girl (~> 2.6.0)
railties (>= 3.0.0) railties (>= 3.0.0)
faker (1.2.0)
i18n (~> 0.5)
ffi (1.6.0) ffi (1.6.0)
formatador (0.2.4) formatador (0.2.4)
gherkin (2.11.6) gherkin (2.11.6)
@@ -124,18 +122,17 @@ GEM
treetop (~> 1.4.8) treetop (~> 1.4.8)
method_source (0.8.1) method_source (0.8.1)
mime-types (1.22) mime-types (1.22)
multi_json (1.8.2) multi_json (1.7.2)
netzke-basepack (0.8.4) netzke-basepack (0.8.2)
netzke-core (~> 0.8.2) netzke-core (~> 0.8.2)
netzke-cancan (0.8.2) netzke-cancan (0.8.2)
cancan cancan
netzke-core netzke-core
netzke-core (0.8.4) netzke-core (0.8.3)
execjs execjs
uglifier uglifier
nokogiri (1.5.9) nokogiri (1.5.9)
orm_adapter (0.0.7) orm_adapter (0.0.7)
pg (0.15.1)
polyglot (0.3.3) polyglot (0.3.3)
pry (0.9.12) pry (0.9.12)
coderay (~> 1.0.5) coderay (~> 1.0.5)
@@ -164,7 +161,6 @@ GEM
rdoc (~> 3.4) rdoc (~> 3.4)
thor (>= 0.14.6, < 2.0) thor (>= 0.14.6, < 2.0)
rake (10.0.4) rake (10.0.4)
rb-fsevent (0.9.3)
rdoc (3.12.2) rdoc (3.12.2)
json (~> 1.4) json (~> 1.4)
rspec (2.8.0) rspec (2.8.0)
@@ -181,6 +177,11 @@ GEM
railties (>= 3.0) railties (>= 3.0)
rspec (~> 2.8.0) rspec (~> 2.8.0)
rubyzip (0.9.9) rubyzip (0.9.9)
sass (3.2.7)
sass-rails (3.2.6)
railties (~> 3.2.0)
sass (>= 3.1.10)
tilt (~> 1.3)
selenium-webdriver (2.31.0) selenium-webdriver (2.31.0)
childprocess (>= 0.2.5) childprocess (>= 0.2.5)
multi_json (~> 1.0) multi_json (~> 1.0)
@@ -194,6 +195,7 @@ GEM
multi_json (~> 1.0) multi_json (~> 1.0)
rack (~> 1.0) rack (~> 1.0)
tilt (~> 1.1, != 1.3.0) tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.7)
therubyracer (0.10.2) therubyracer (0.10.2)
libv8 (~> 3.3.10) libv8 (~> 3.3.10)
thor (0.18.1) thor (0.18.1)
@@ -209,8 +211,8 @@ GEM
less-rails (~> 2.2.2) less-rails (~> 2.2.2)
railties (>= 3.1) railties (>= 3.1)
therubyracer (~> 0.10.1) therubyracer (~> 0.10.1)
tzinfo (0.3.38) tzinfo (0.3.37)
uglifier (2.1.2) uglifier (1.3.0)
execjs (>= 0.3.0) execjs (>= 0.3.0)
multi_json (~> 1.0, >= 1.0.2) multi_json (~> 1.0, >= 1.0.2)
warden (1.1.1) warden (1.1.1)
@@ -233,7 +235,6 @@ DEPENDENCIES
decent_exposure (~> 1.0.1) decent_exposure (~> 1.0.1)
devise (~> 2.0.4) devise (~> 2.0.4)
factory_girl_rails (~> 1.2) factory_girl_rails (~> 1.2)
faker
guard-rspec guard-rspec
guard-spork guard-spork
haml-rails (~> 0.3.4) haml-rails (~> 0.3.4)
@@ -242,13 +243,13 @@ DEPENDENCIES
netzke-basepack (~> 0.8.0) netzke-basepack (~> 0.8.0)
netzke-cancan netzke-cancan
netzke-core (~> 0.8.0) netzke-core (~> 0.8.0)
pg
pry (~> 0.9.8) pry (~> 0.9.8)
rails (= 3.2.13) rails (= 3.2.13)
rb-fsevent
rspec-rails (~> 2.8.1) rspec-rails (~> 2.8.1)
sass-rails (~> 3.2.3)
shoulda-matchers (~> 1.0.0) shoulda-matchers (~> 1.0.0)
spork spork
sqlite3 (~> 1.3.5)
turnip (~> 0.3.0) turnip (~> 0.3.0)
twitter-bootstrap-rails (~> 2.0.3) twitter-bootstrap-rails (~> 2.0.3)
uglifier (>= 1.0.3) uglifier (>= 1.0.3)
+6 -40
View File
@@ -1,56 +1,22 @@
# About
A web application for bicycle collectives to track bicycles, bicycle work history, volunteer hours, volunteer work history, and volunteers currently in the shop.
See an overview video of what it looks like and how it works here: https://www.youtube.com/watch?v=0-JjM6d9nK4.
## Instructions/Guides
### Adding a User
To add a user, a user must sign themselves up from the initial login screen. An admin can later log in to grant that user admin privileges afterward, if desired.
### Adding a Bike
How to add a bike to the database: https://www.youtube.com/watch?v=1IchWUdMQ90.
## Views
The app has two different views. One for "core" volunteers which I consider admins, and another for "customer" volunteers which I consider users. Because of the Netzke/ExtJS framework I used, the current UI is non-intuitive and I feel like is only adequate for admins who can take the time to learn the system. I'm in the process of designing a simpler mobile friendly UI with the essential functionality that's intended for users.
## Deployment
Currently, at the Velocipede collective we have it running on a computer in the shop on the local network. The reason for this, is so that it's only accessible from within the shop. I'm weighing out different methods for ensuring that users can only log in from within the shop (and not from home), such as a shop password that only admins can see and would make visible from within the physical bike shop. I also don't have to worry about security issues while the app is in development. In the future, I'd like to see the app hosted much like http://freehub.bikekitchen.org/.
## Ideas
At Velocipede, with a mobile friendly UI for users, I'm hoping we can get donations of old, unused smart phones, connected via local wifi, that can be used as stations to log a volunteer's work and hours. They can be secured by gluing cables into the earphone jacks to deter theft if necessary. This might be another way that the collective can promote reuse of materials.
# Developer Setup # Developer Setup
1. use rvm 1. use rvm
1. allow the .rvmrc file 1. allow the .rvmrc file
1. `gem install bundler` 1. `gem install bundler`
1. `bundle` 1. `bundle`
1. Install Postgres (Mac OSX instructions below) 1. `rake db:create db:migrate db:seed`
1. Copy over DB config: `cp config/database.yml.example config/database.yml` 1. Download extJS (http://www.sencha.com/products/extjs), unzip, and place where ever you like.
1. Update config with your database (velocipede), user (velocipede), and password.
1. `rake db:create db:migrate`
1. `rake db:seed`
1. Download extJS 4.1 (A version of 4.1 is hosted here: http://my.jasondenney.com/extjs-4.1.1.zip) Latest versions at http://www.sencha.com/products/extjs. Unzip and place where ever you like.
1. Link to your extJS folder path under `public/extjs`: (From app root) `ln -s /MY/PATH/extjs/ public/extjs` 1. Link to your extJS folder path under `public/extjs`: (From app root) `ln -s /MY/PATH/extjs/ public/extjs`
1. `rails s` 1. `rails s`
# Postgres 9.2 Mac OSX Install
1. Install homebrew `ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"`
1. `brew install postgres`
1. First time db initialization `initdb /usr/local/var/postgres -E utf8`
1. Start Postgres `pg_ctl -D /usr/local/var/postgres -l /usr/local/var/postgres/server.log start`
1. Create your PG user `createuser -d -P velocipede`
1. Create your database `createdb -U velocipede --owner=velocipede velocipede`
# Optional # Optional
Add icons Add icons
1. Download icons from http://www.famfamfam.com/lab/icons/silk/ 1. Download icons from http://www.famfamfam.com/lab/icons/silk/
1. Link to the icons under `public/images/icons`: (From app root) `ln -s /MY/PATH/famfamfam_silk_icons/icons public/images/icons` 1. Link to the icons under `public/images/icons`: (From app root) `ln -s /MY/PATH/famfamfam_silk_icons/icons public/images/icons`
# License
Velocipede is released under the MIT license (http://opensource.org/licenses/MIT)
<a href="http://madewithloveinbaltimore.org">Made with &hearts; in Baltimore</a> ## Mailcatcher
Use mailcatcher to see what emails look like in development.
Follow the instructions from http://mailcatcher.me/ to create an rvm wrapper so you don't install it all over your gemsets
+9 -13
View File
@@ -12,20 +12,16 @@ require File.expand_path('../config/application', __FILE__)
Velocipede::Application.load_tasks Velocipede::Application.load_tasks
unless Rails.env.production? namespace :spec do
desc 'Run all acceptance specs'
namespace :spec do RSpec::Core::RakeTask.new(:acceptance => 'db:test:prepare') do |t|
desc 'Run all acceptance specs' t.pattern = '**/*.feature'
RSpec::Core::RakeTask.new(:acceptance => 'db:test:prepare') do |t|
t.pattern = '**/*.feature'
end
end end
end
Rake.application.remove_task 'spec' Rake.application.remove_task 'spec'
desc 'Run all specs' desc 'Run all specs'
RSpec::Core::RakeTask.new(:spec => 'db:test:prepare') do |t| RSpec::Core::RakeTask.new(:spec => 'db:test:prepare') do |t|
t.pattern = '**/*{_spec.rb,.feature}' t.pattern = '**/*{_spec.rb,.feature}'
end
end end
+2 -1
View File
@@ -12,4 +12,5 @@
// //
//= require jquery //= require jquery
//= require jquery_ujs //= require jquery_ujs
//= require custom_netzke_helpers //= require twitter/bootstrap
//= require_tree .
@@ -3,374 +3,3 @@ Ext.Ajax.on('requestexception', function(conn, response, options) {
if (response.status === 401) { window.location = '/users/sign_in'; } if (response.status === 401) { window.location = '/users/sign_in'; }
}, this); }, this);
//Override default netzke time entry field
Ext.define('Ext.ux.form.field.DateTime', {
extend:'Ext.form.FieldContainer',
mixins:{
field:'Ext.form.field.Field'
},
alias: 'widget.xdatetime',
//configurables
combineErrors: true,
msgTarget: 'under',
layout: 'hbox',
readOnly: false,
dateFormat: 'Y-m-d',
dateSubmitFormat: 'Y-m-d',
timeFormat: 'H:i:s',
timeSubmitFormat: 'H:i:s',
dateConfig:{},
timeConfig:{},
// properties
dateValue: null, // Holds the actual date
dateField: null,
hourField: null,
minuteField: null,
ampmField: null,
initComponent: function(){
var me = this
,i = 0
,key
,tab;
me.items = me.items || [];
me.dateField = Ext.create('Ext.form.field.Date', Ext.apply({
format:me.dateFormat,
flex:1,
isFormField:false, //exclude from field query's
submitValue:false,
submitFormat: me.dateSubmitFormat,
readOnly: me.readOnly
}, me.dateConfig));
me.items.push(me.dateField);
me.hourField = Ext.create('Ext.form.field.Number', {
maxWidth: 25,
allowBlank: false,
allowOnlyWhitespace: false,
blankText: "Hour cannot be blank.",
allowDecimals: false,
maxValue: 12,
minValue: 1,
maxLength: 2,
enforceMaxLength: 2,
hideTrigger: true,
submitValue:false,
flex:1,
fieldStyle: "text-align:right;",
isFormField:false, //exclude from field query's
});
me.items.push(me.hourField);
me.colon = Ext.create('Ext.draw.Text', {
text: ':',
padding: '3 3 0 3'
});
me.items.push(me.colon);
me.minuteField = Ext.create('Ext.form.field.Text', {
maxWidth: 30,
allowBlank: false,
allowOnlyWhitespace: false,
blankText: "Minutes cannot be blank.",
regex: /[012345]\d/,
maxLength: 2,
enforceMaxLength: 2,
submitValue:false,
flex:1,
isFormField:false, //exclude from field query's
});
me.items.push(me.minuteField);
me.ampmField = Ext.create('Ext.form.ComboBox', {
maxWidth: 45,
value: 'PM',
store: ['AM', 'PM'],
forceSelection: true,
flex:1,
editable: false
});
me.items.push(me.ampmField);
for (; i < me.items.length; i++) {
me.items[i].on('focus', Ext.bind(me.onItemFocus, me));
me.items[i].on('blur', Ext.bind(me.onItemBlur, me));
me.items[i].on('specialkey', function(field, event){
key = event.getKey();
tab = key == event.TAB;
if (tab && me.focussedItem == me.dateField) {
event.stopEvent();
me.timeField.focus();
return;
}
me.fireEvent('specialkey', field, event);
});
}
me.callParent();
// this dummy is necessary because Ext.Editor will not check whether an inputEl is present or not
this.inputEl = {
dom: document.createElement('div'),
swallowEvent:function(){}
};
me.initField();
},
focus:function(){
this.callParent(arguments);
this.dateField.focus();
},
onItemFocus:function(item){
if (this.blurTask){
this.blurTask.cancel();
}
this.focussedItem = item;
},
onItemBlur:function(item, e){
var me = this;
if (item != me.focussedItem){ return; }
// 100ms to focus a new item that belongs to us, otherwise we will assume the user left the field
me.blurTask = new Ext.util.DelayedTask(function(){
me.fireEvent('blur', me, e);
});
me.blurTask.delay(100);
},
getValue: function(){
var value = null
,date = this.dateField.getSubmitValue()
,time = null
,format;
hour = this.hourField.getValue();
minute = this.minuteField.getValue();
ampm = this.ampmField.getValue();
time = Ext.Date.parse(hour + " " + minute + " " + ampm, 'g i A');
time = Ext.Date.format(time, this.timeSubmitFormat);
if (date){
if (time){
format = this.getFormat();
value = Ext.Date.parse(date + ' ' + time, format);
} else {
value = this.dateField.getValue();
}
}
return value;
},
getSubmitValue: function(){
// var value = this.getValue();
// return value ? Ext.Date.format(value, this.dateTimeFormat) : null;
var me = this
,format = me.getFormat()
,value = me.getValue();
return value ? Ext.Date.format(value, format) : null;
},
setValue: function(value){
if (Ext.isString(value) || Ext.isDate(value)){
if( Ext.isDate(value)){
dt = value;
}else{
dt = new Date(value);
value = Ext.Date.parse(value, this.getFormat()); //this.dateTimeFormat
}
this.dateField.setValue(value);
hour = Ext.Date.format(dt, 'g');
minute = Ext.Date.format(dt, 'i');
ampm = Ext.Date.format(dt, 'A');
this.hourField.setValue(hour);
this.minuteField.setRawValue(minute);
this.ampmField.setValue(ampm);
}
},
getFormat: function(){
value = (this.dateField.submitFormat || this.dateField.format) + " " + (this.timeSubmitFormat || this.timeFormat);
return value;
},
// Bug? A field-mixin submits the data from getValue, not getSubmitValue
getSubmitData: function(){
var me = this
,data = null;
if (!me.disabled && me.submitValue && !me.isFileUpload()) {
data = {};
data[me.getName()] = '' + me.getSubmitValue();
}
return data;
}
});
Ext.define('Ext.ux.form.field.ColorCombo', {
extend:'Ext.form.FieldContainer',
mixins:{
field:'Ext.form.field.Field'
},
alias: 'widget.xcolorcombo',
//configurables
combineErrors: true,
msgTarget: 'under',
layout: 'hbox',
readOnly: false,
// properties
colorValue: null,
/**
* @property dateField
* @type Ext.form.field.Date
*/
colorField: null,
initComponent: function(){
var me = this
,i = 0
,key
,tab;
me.items = me.items || [];
me.colorField = Ext.create('Ext.form.field.Trigger', {
flex:1,
isFormField:false, //exclude from field query's
submitValue:false,
readOnly: me.readOnly,
onTriggerClick: function() {
//show renders, so put first
me.picker.show();
me.picker.alignTo(me.colorField.inputEl);
}
});
me.items.push(me.colorField);
me.picker = Ext.create('Ext.picker.Color', {
renderTo: document.body,
floating: true,
hidden: true,
style: {
backgroundColor: "#fff"
},
listeners: {
scope:this,
select: function(field, value, opts){
me.setValue(value);
me.picker.hide();
}
}
});
me.items.push(me.picker);
for (; i < me.items.length; i++) {
me.items[i].on('focus', Ext.bind(me.onItemFocus, me));
me.items[i].on('blur', Ext.bind(me.onItemBlur, me));
me.items[i].on('specialkey', function(field, event){
key = event.getKey();
tab = key == event.TAB;
if (tab && me.focussedItem == me.dateField) {
event.stopEvent();
me.timeField.focus();
return;
}
me.fireEvent('specialkey', field, event);
});
}
me.callParent();
// this dummy is necessary because Ext.Editor will not check whether an inputEl is present or not
this.inputEl = {
dom: document.createElement('div'),
swallowEvent:function(){}
};
me.initField();
},
focus:function(){
this.callParent(arguments);
this.colorField.focus();
var me = this;
},
onItemFocus:function(item){
if (this.blurTask){
this.blurTask.cancel();
}
this.focussedItem = item;
},
onItemBlur:function(item, e){
var me = this;
if (item != me.focussedItem){ return; }
// 100ms to focus a new item that belongs to us, otherwise we will assume the user left the field
me.blurTask = new Ext.util.DelayedTask(function(){
me.picker.hide();
me.fireEvent('blur', me, e);
});
me.blurTask.delay(100);
},
getValue: function(){
var value = null
,color = this.colorField.getSubmitValue();
if (color){
value = this.colorField.getValue();
}
return value;
},
getSubmitValue: function(){
// var value = this.getValue();
// return value ? Ext.Date.format(value, this.dateTimeFormat) : null;
var me = this
,value = me.getValue();
return value;
},
setValue: function(value){
this.colorField.setValue(value);
},
// Bug? A field-mixin submits the data from getValue, not getSubmitValue
getSubmitData: function(){
var me = this
,data = null;
if (!me.disabled && me.submitValue && !me.isFileUpload()) {
data = {};
data[me.getName()] = '' + me.getSubmitValue();
}
return data;
}
});
@@ -1,51 +0,0 @@
$(document).ready(function(){
var MIN_LEN = 3;
var MAX_SUBMITS = 3;
var submit_count = 0;
$("input[name=commit]").click( function(e){
console.log("clicked");
submit_count += 1;
//IDs of contact info
var contact_info_ids = [
"user_email",
"user_user_profiles_attributes_0_addrStreet1",
"user_user_profiles_attributes_0_addrCity",
"user_user_profiles_attributes_0_addrState",
"user_user_profiles_attributes_0_addrZip",
"user_user_profiles_attributes_0_phone"
];
var contact_vals = "";
var index = 0;
//see if any contact info exists
for( var index in contact_info_ids){
contact_vals += $("#"+contact_info_ids[index]).val();
}
if( contact_vals.length >= MIN_LEN || submit_count > MAX_SUBMITS){
if( submit_count > MAX_SUBMITS ){
alert("Fine.");
}
return true;
}else{
switch(submit_count){
case 1:
alert("It appears you have not entered any contact information. " +
"Please do.");
break;
case 2:
alert("It is highly recommended that you enter at least one form of" +
" contact information. It is in your best interest.");
break;
case 3:
alert("If something happens to your bicycle, we will not be able to" +
" notify you. Please enter at least one form of contact.");
break;
default:
alert("Please enter at least one form of contact.");
}
return false;
}
});
});
-45
View File
@@ -1,45 +0,0 @@
$(document).ready(function(){
$("#checkin_menu").show();
$("#checkin").click( function(e){
var username = $("#user_email").val();
var password = $("#user_password").val();
$.ajax({
type: 'POST',
url: '/api/v1/checkin',
dataType: 'json',
contentType: 'application/json',
processData: false,
data: JSON.stringify({"username": username, "password": password }),
complete: function() { },
success: function(data) {
alert("Checked IN!");
$("#user_email").val('');
$("#user_password").val('');
},
error: function(data,textStatus) {
alert( "Error: " + JSON.parse(data.responseText)["error"]);
}
})
});
$("#checkout").click( function(e){
var username = $("#user_email").val();
var password = $("#user_password").val();
$.ajax({
type: 'POST',
url: '/api/v1/checkout',
dataType: 'json',
contentType: 'application/json',
processData: false,
data: JSON.stringify({"username": username, "password": password }),
complete: function() { },
success: function(data) {
alert("Checked OUT!");
$("#user_email").val('');
$("#user_password").val('');
},
error: function(data,textStatus) {
alert( "Error: " + JSON.parse(data.responseText)["error"]);
}
})
});
});
View File
+23 -32
View File
@@ -1,56 +1,45 @@
class AppTabPanel < Netzke::Basepack::TabPanel class AppTabPanel < Netzke::Basepack::TabPanel
action :sign_out do |c| action :sign_out do |c|
c.icon = :door_out c.icon = :door_out
c.text = "Exit" c.text = "Sign out #{controller.current_user.email}" if controller.current_user
end
action :check_out do |c|
c.icon = :door_out
c.text = "CHECK OUT" if controller.current_user
end
action :change_account_info do |c|
c.icon = :user_edit
c.text = "Change Email/Password"
end end
def configure(c) def configure(c)
#all users #all users
# (had to use hash for borders to get the title to display properly) # (had to use hash for borders to get the title to display properly)
@@app_tab_panel_items = [ :bikes_border ] @@app_tab_panel_items = [ :bikes_border,
{ layout: :fit,
wrappedComponent: :brands_and_models_border,
title: "Brands/Models"}
]
#for users only #for users
if not controller.current_user.role?(:admin) if controller.current_user.role?(:user)
# (had to use hash for borders to get the title to display properly)
@@app_tab_panel_items.concat [{ layout: :fit, @@app_tab_panel_items.concat [{ layout: :fit,
wrappedComponent: :user_profile_border, wrappedComponent: :user_profile_border,
title: "My Profile"}, title: "Profile"},
{ layout: :fit, { layout: :fit,
wrappedComponent: :user_transactions_border, wrappedComponent: :user_transactions_border,
title: "My Transactions"} title: "Transactions"}
] ]
end end
#for admins #for admins
if can? :manage, User if controller.current_user.role?(:admin)
# (had to use hash for borders to get the title to display properly)
@@app_tab_panel_items.concat [{ layout: :fit, @@app_tab_panel_items.concat [{ layout: :fit,
wrappedComponent: :users_and_profiles_border, wrappedComponent: :users_and_profiles_border,
title: "Users/Profiles"}] title: "Users/Profiles"},
end { layout: :fit,
if can? :manage, Transaction
@@app_tab_panel_items.concat [{ layout: :fit,
wrappedComponent: :transactions_border, wrappedComponent: :transactions_border,
title: "Users/Transactions"}] title: "Transactions"},
end :logs,
if can? :manage, Role :user_roles]
@@app_tab_panel_items.concat [{ layout: :fit,
wrappedComponent: :user_role_joins,
title: "User Roles"},
]
end
if can? :manage, ::ActsAsLoggable::Log.all
@@app_tab_panel_items.concat [:check_ins, :logs]
end end
@@app_tab_panel_items.each do |item| @@app_tab_panel_items.each do |item|
if item.kind_of?(Symbol) if item.kind_of?(Symbol)
self.class.component item self.class.component item
@@ -58,8 +47,9 @@ class AppTabPanel < Netzke::Basepack::TabPanel
self.class.component item[:wrappedComponent] self.class.component item[:wrappedComponent]
end end
end end
c.prevent_header = true c.prevent_header = true
c.tbar = [:sign_out, :check_out, :change_account_info] c.tbar = [:sign_out]
c.items = @@app_tab_panel_items c.items = @@app_tab_panel_items
super super
end end
@@ -68,5 +58,6 @@ class AppTabPanel < Netzke::Basepack::TabPanel
#gets js from app_tab_panel/javascripts/sign_out.js #gets js from app_tab_panel/javascripts/sign_out.js
c.mixin :sign_out c.mixin :sign_out
end end
end end
@@ -5,20 +5,5 @@
url: '/users/sign_out', url: '/users/sign_out',
method: 'DELETE' method: 'DELETE'
}); });
},
onCheckOut: function(){
Ext.Ajax.request({
url: '/api/v1/checkout',
method: 'POST',
success: function(response, opts) {
Ext.Ajax.request({
url: '/users/sign_out',
method: 'DELETE'
});
}
});
},
onChangeAccountInfo: function(){
window.location.href="users/edit";
} }
} }
-1
View File
@@ -3,7 +3,6 @@ class BikeBrands < Netzke::Basepack::Grid
super super
c.model = "BikeBrand" c.model = "BikeBrand"
c.title = "Brands" c.title = "Brands"
c.force_fit = true
c.prohibit_update = true if cannot? :update, BikeBrand c.prohibit_update = true if cannot? :update, BikeBrand
c.prohibit_create = true if cannot? :create, BikeBrand c.prohibit_create = true if cannot? :create, BikeBrand
+33 -14
View File
@@ -6,7 +6,6 @@ class BikeLogs < Netzke::Basepack::Grid
c.model = "ActsAsLoggable::Log" c.model = "ActsAsLoggable::Log"
c.title = "Bike History" c.title = "Bike History"
c.force_fit = true
c.data_store = {auto_load: false} c.data_store = {auto_load: false}
c.scope = lambda { |rel| rel.where(:loggable_type => 'Bike',:loggable_id => session[:selected_bike_id]);} c.scope = lambda { |rel| rel.where(:loggable_type => 'Bike',:loggable_id => session[:selected_bike_id]);}
c.strong_default_attrs = { c.strong_default_attrs = {
@@ -19,36 +18,57 @@ class BikeLogs < Netzke::Basepack::Grid
c.columns = [ c.columns = [
{ :name => :start_date, :format => "g:ia - D, M j - Y", :width => 165, :default_value => Time.now.to_formatted_s(:db) }, { :name => :start_date, :format => "g:ia - D, M j - Y", :width => 165, :default_value => Time.now.to_formatted_s(:db) },
{ :name => :end_date, :format => "g:ia - D, M j - Y", :width => 165, :default_value => Time.now.to_formatted_s(:db) }, { :name => :end_date, :hidden => true, :default_value => Time.now.to_formatted_s(:db) },
{ :name => :hours, :getter => lambda { |rec| (rec.end_date - rec.start_date)/3600 }, :sorting_scope => :sort_by_duration},
:description, :description,
{ :name => :bike_action__action, :text => 'Action', :default_value => ::ActsAsLoggable::BikeAction.first.id}, { :name => :bike_action__action, :text => 'Action'},
{ :name => :created_at, :read_only => true},
{ :name => :updated_at, :read_only => true},
{ :name => :logged_by, :getter => lambda{ |rec| { :name => :logged_by, :getter => lambda{ |rec|
user = User.find_by_id(rec.logger_id) user = User.find_by_id(rec.logger_id)
user.nil? ? "" : "#{user.first_name} #{user.last_name}" user.nil? ? "" : "#{user.first_name} #{user.last_name}"
} }
} }
] ]
@bike_logs = ::ActsAsLoggable::Log.where(:loggable_type => "Bike").all
c.prohibit_update = true if cannot? :update, @bike_logs
c.prohibit_create = true if cannot? :create, @bike_logs
c.prohibit_delete = true if cannot? :delete, @bike_logs
=begin =begin
#TODO: fix GUI so it actually respects this #TODO: fix GUI so it actually respects this
current_bike = Bike.find_by_id(session[:selected_bike_id]) current_bike = Bike.find_by_id(session[:selected_bike_id])
if cannot? :update, current_bike if can? :manage, current_bike
puts "\n\n\nCAN MANAGE!\n\n\n"
c.bbar = default_bbar
else
puts "\n\n\nCANNNOT MANAGE!\n\n\n"
# if you can't update the bike, you can't do anything to the log # if you can't update the bike, you can't do anything to the log
c.prohibit_update = c.prohibit_create = c.prohibit_delete = true #c.prohibit_update = c.prohibit_create = c.prohibit_delete = true
c.bbar = [ :search ]
end end
=end =end
end end
endpoint :set_read_only do |params, this|
current_bike = Bike.find_by_id(session[:selected_bike_id])
puts "\n\n\nREAD!\n\n\n"
if can? :manage, current_bike
puts "\n\n\nCAN MANAGE!\n\n\n"
#calls this.setDisabled()
this.set_disabled(false)
#sets the return value of this function
#this.netzke_set_result(true)
else
puts "\n\n\nCANNNOT MANAGE!\n\n\n"
#sets the return value of this function
#this.netzke_set_result(true)
this.set_disabled(true)
end
end
def default_fields_for_forms def default_fields_for_forms
[ [
{ :name => :start_date}, { :name => :start_date},
{ :name => :end_date},
{ :name => :description}, { :name => :description},
#had to hack acts_as_loggable/log.rb to get this to work #had to hack acts_as_loggable/log.rb to get this to work
{ :name => :bike_action__action, :field_label => 'Action', :min_chars => 1 } { :name => :bike_action__action, :field_label => 'Action'}
] ]
end end
@@ -56,9 +76,8 @@ class BikeLogs < Netzke::Basepack::Grid
#override with nil to remove actions #override with nil to remove actions
def default_bbar def default_bbar
bbar = [ :search ] bbar = [ :search ]
bbar.concat [ :apply ] if can? :update, ::ActsAsLoggable::Log bbar.concat [ :apply ] #if can? :update, ::ActsAsLoggable::Log
bbar.concat [ :add_in_form ] if can? :create, ::ActsAsLoggable::Log bbar.concat [ :add_in_form ] #if can? :create, ::ActsAsLoggable::Log
bbar bbar
end end
end end
@@ -0,0 +1,10 @@
class BikeLogsAndTasksAccordian < Netzke::Basepack::Accordion
component :bike_logs
component :tasks
def configure(c)
c.prevent_header = true
c.items = [ :bike_logs, :tasks ]
super
end
end
-12
View File
@@ -1,12 +0,0 @@
class BikeLowerTabs < Netzke::Basepack::TabPanel
component :bike_logs
component :tasks
component :brands_and_models_border
def configure(c)
c.prevent_header = true
c.items = [ :bike_logs, :tasks,
{netzke_component: :brands_and_models_border, title: "Brands and Models"} ]
super
end
end
-1
View File
@@ -4,7 +4,6 @@ class BikeModels < Netzke::Basepack::Grid
c.model = "BikeModel" c.model = "BikeModel"
c.title = "Models" c.title = "Models"
c.force_fit = true
c.data_store = {auto_load: false} c.data_store = {auto_load: false}
c.scope = lambda { |rel| rel.where(:bike_brand_id => session[:selected_bike_brand_id]);} c.scope = lambda { |rel| rel.where(:bike_brand_id => session[:selected_bike_brand_id]);}
c.strong_default_attrs = { c.strong_default_attrs = {
+11 -40
View File
@@ -2,15 +2,12 @@ class Bikes < Netzke::Basepack::Grid
def configure(c) def configure(c)
super super
c.model = "Bike" c.model = "Bike"
c.force_fit = true
# columns with :id set, have :min_chars set in init_component
# See: http://stackoverflow.com/questions/17738962/netzke-grid-filtering
c.columns = [ c.columns = [
{ :name => :shop_id, :text => 'Shop ID', :default_value => Bike.last.id.to_i + 1}, { :name => :shop_id, :text => 'Shop ID'},
:serial_number, :serial_number,
{ :id => :bike_brand__brand, :name => :bike_brand__brand, :text => 'Brand'}, { :name => :bike_brand__brand, :text => 'Brand' },
{ :name => :model, :text => 'Model', { :name => :bike_model__model, :text => 'Model',
:scope => lambda { |rel| :scope => lambda { |rel|
if session[:selected_bike_brand_id] if session[:selected_bike_brand_id]
rel.where(:bike_brand_id => session[:selected_bike_brand_id]) rel.where(:bike_brand_id => session[:selected_bike_brand_id])
@@ -19,48 +16,22 @@ class Bikes < Netzke::Basepack::Grid
end end
} }
}, },
#needs to have type :action or else won't work in grid, because... netzke :color,
{ :name => "color", :text => "Frame Color", :type => :action, :editor => { :xtype => "xcolorcombo"}, :renderer => :color_block, { :name => :bike_style__style, :text => 'Style' },
:default_value => '000000'}, :seat_tube_height,
{ :id => :bike_style__style, :name => :bike_style__style, :text => 'Style', :default_value => BikeStyle.first.id}, :top_tube_length,
{ :name => :seat_tube_height, :text => 'Seat Tube (in)'}, :wheel_size,
{ :name => :top_tube_length, :text => 'Top Tube (in)'},
{ :name => :bike_wheel_size__display_string, :text => 'Wheel Size'},
:value, :value,
{ :id => :bike_condition__condition, :name => :bike_condition__condition, :text => 'Condition', :default_value => BikeCondition.first.id}, { :name => :bike_condition__condition, :text => 'Condition'},
{ :id => :bike_purpose__purpose, :name => :bike_purpose__purpose, :text => 'Purpose', :default_value => BikePurpose.first.id}, { :name => :bike_status__status, :text => 'Status'},
{ :name => :owner, :getter => lambda { |rec| { :name => :owner, :getter => lambda { |rec|
user = rec.owner user = rec.owner
user.nil? ? "" : "#{user.first_name} #{user.last_name}" user.nil? ? "" : "#{user.first_name} #{user.last_name}"
} }
} }
] ]
# Default the sorting to ASC on shop_id
c.data_store.sorters = [{ property: 'shop_id', direction: 'ASC' }]
@bike = Bike.all
c.prohibit_update = true if cannot? :update, @bike
c.prohibit_create = true if cannot? :create, @bike
c.prohibit_delete = true if cannot? :delete, @bike
end
def default_fields_for_forms
# :field_label MUST be defined in order for search to work
[
{ :name => :bike_brand__brand, :field_label => 'Brand', :min_chars => 1 },
{ :name => :model, :field_label => 'Model'},
{ :name => :shop_id, :field_label => 'Shop ID'},
{ :name => :serial_number, :field_label => 'Serial Number'},
{ :name => "color", :xtype => "xcolorcombo"},
{ :name => :bike_style__style, :field_label => 'Style', :min_chars => 1},
{ :name => :seat_tube_height, :field_label => 'Seat Tube (in)'},
{ :name => :top_tube_length, :field_label => 'Top Tube (in)'},
{ :name => :bike_wheel_size__display_string, :field_label => 'Wheel Size'},
{ :name => :value, :field_label => 'Value'},
{ :name => :bike_condition__condition, :field_label => 'Condition', :min_chars => 1},
{ :name => :bike_purpose__purpose, :field_label => 'Purpose', :min_chars => 1}
]
end end
#override with nil to remove actions #override with nil to remove actions
def default_bbar def default_bbar
[ :apply, :add_in_form, :search ] [ :apply, :add_in_form, :search ]
@@ -3,24 +3,11 @@
// calling superclass's initComponent // calling superclass's initComponent
this.callParent(); this.callParent();
//due to Netzke bug, :min_chars attribute doesn't work
var min_char_columns = [
"bike_brand__brand",
"bike_style__style",
"bike_condition__condition",
"bike_purpose__purpose"]
Ext.each(min_char_columns, function(column, index) {
Ext.ComponentManager.get(column).editor.minChars = 1;
});
// setting the 'rowclick' event // setting the 'rowclick' event
var view = this.getView(); var view = this.getView();
view.on('itemclick', function(view, record){ view.on('itemclick', function(view, record){
// The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server! // The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server!
this.selectBikeBrand({bike_brand_id: record.get('bike_brand__brand')}); this.selectBikeBrand({bike_brand_id: record.get('bike_brand__brand')});
}, this); }, this);
},
colorBlock: function(value){
return Ext.String.format('<div style="display:inline-block">{1}</div><div style="background-color:#{0};width:50px;height:10px;display:inline-block;margin:0 5px 0 5px;border:solid;border-color:gray;border-width:1px;"></div>', value, value);
} }
} }
+2 -2
View File
@@ -2,7 +2,7 @@ class BikesBorder < Netzke::Base
# Remember regions collapse state and size # Remember regions collapse state and size
include Netzke::Basepack::ItemPersistence include Netzke::Basepack::ItemPersistence
component :bikes component :bikes
component :bike_lower_tabs component :bike_logs_and_tasks_accordian
def configure(c) def configure(c)
super super
@@ -10,7 +10,7 @@ class BikesBorder < Netzke::Base
c.title = "Bikes" c.title = "Bikes"
c.items = [ c.items = [
{ netzke_component: :bikes, region: :center, split: true }, { netzke_component: :bikes, region: :center, split: true },
{ netzke_component: :bike_lower_tabs, region: :south, height: 300, split: true} { netzke_component: :bike_logs_and_tasks_accordian, region: :south, height: 300, split: true}
] ]
end end
@@ -5,30 +5,19 @@
// setting the 'rowclick' event // setting the 'rowclick' event
var view = this.getComponent('bikes').getView(); var view = this.getComponent('bikes').getView();
//disable until a bike is clicked
var bike_logs_comp = this.queryById('bike_logs');
if( bike_logs_comp){
bike_logs_comp.disable();
}
var bike_tasks_comp = this.queryById('tasks');
if( bike_tasks_comp ){
bike_tasks_comp.disable();
}
view.on('itemclick', function(view, record){ view.on('itemclick', function(view, record){
// The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server! // The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server!
this.selectBike({bike_id: record.get('id')}); this.selectBike({bike_id: record.get('id')});
// query for these components again, can change if not visible because of accordian if( this.queryById('bike_logs') ){
var bike_logs_comp = this.queryById('bike_logs'); this.queryById('bike_logs').getStore().load();
var bike_tasks_comp = this.queryById('tasks'); this.queryById('bike_logs').setReadOnly({}, function(arg, arg1, arg2){
if( bike_logs_comp ){ console.log(arg);
bike_logs_comp.getStore().load(); console.log(this.getDockedItems());
bike_logs_comp.enable(); });
this.queryById('bike_logs').setDisabled(true);
} }
if( bike_tasks_comp ){ if( this.queryById('tasks') ){
bike_tasks_comp.getStore().load(); this.queryById('tasks').getStore().load();
bike_tasks_comp.enable();
} }
}, this); }, this);
} }
-32
View File
@@ -1,32 +0,0 @@
class CheckIns < Netzke::Basepack::Grid
def configure(c)
super
c.header = false
c.model = "ActsAsLoggable::Log"
c.force_fit = true
c.scope = lambda { |rel| rel.where(:log_action_type => ::ActsAsLoggable::UserAction).
where(:loggable_type => "User").
where(:log_action_id => ::ActsAsLoggable::UserAction.find_by_action("CHECKIN")).
where("start_date >= ?", Time.zone.now.beginning_of_day);
}
c.columns = [
{ :name => :name, :getter => lambda{ |rec|
user = User.find_by_id(rec.loggable_id)
user.nil? ? "" : "#{user.first_name} #{user.last_name}"
}
},
{ :name => "Status", :getter => lambda{ |rec| rec.start_date == rec.end_date ? "Checked In" : "Checked Out" } },
:start_date,
:end_date,
]
c.prohibit_update = true
c.prohibit_create = true
c.prohibit_delete = true
end
def default_bbar
[ :search ]
end
end
-1
View File
@@ -1,7 +1,6 @@
class Customers < Netzke::Basepack::Grid class Customers < Netzke::Basepack::Grid
def configure(c) def configure(c)
c.model = "Customer" c.model = "Customer"
c.force_fit = true
end end
#override with nil to remove actions #override with nil to remove actions
@@ -4,6 +4,7 @@
this.callParent(); this.callParent();
this.getView().on('itemclick', function(view, record){ this.getView().on('itemclick', function(view, record){
// The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server! // The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server!
console.log("user: " + record.get('id') );
this.selectCustomer({customer_id: record.get('id'), customer_type: 'Customer'}); this.selectCustomer({customer_id: record.get('id'), customer_type: 'Customer'});
}, this); }, this);
} }
-22
View File
@@ -4,7 +4,6 @@ class Logs < Netzke::Basepack::Grid
super super
c.header = false c.header = false
c.model = "ActsAsLoggable::Log" c.model = "ActsAsLoggable::Log"
c.force_fit = true
c.columns = [ c.columns = [
:loggable_id, :loggable_id,
:loggable_type, :loggable_type,
@@ -23,25 +22,4 @@ class Logs < Netzke::Basepack::Grid
{ :name => :hours, :getter => lambda { |rec| (rec.end_date - rec.start_date)/3600 }, :sorting_scope => :sort_by_duration} { :name => :hours, :getter => lambda { |rec| (rec.end_date - rec.start_date)/3600 }, :sorting_scope => :sort_by_duration}
] ]
end end
def default_fields_for_forms
[
:loggable_id,
:loggable_type,
:logger_id,
:logger_type,
:context,
:start_date,
:end_date,
:description,
:log_action_id,
:log_action_type,
:created_at,
:updated_at
].map{ |x| { :name => x, :field_label => x.to_s.humanize} }
end
def default_bbar
[ :apply, :add_in_form, :search ]
end
end end
+4 -7
View File
@@ -2,8 +2,6 @@ class Tasks < Netzke::Basepack::Grid
def configure(c) def configure(c)
super super
#disable by default, will be enabled once bike is clicked
c.disabled = true
task_list_id = nil task_list_id = nil
if session[:selected_bike_id] if session[:selected_bike_id]
task_list_id = Bike.find_by_id(session[:selected_bike_id]).task_list.id task_list_id = Bike.find_by_id(session[:selected_bike_id]).task_list.id
@@ -11,7 +9,6 @@ class Tasks < Netzke::Basepack::Grid
c.header = false c.header = false
c.model = "Task" c.model = "Task"
c.force_fit = true
c.scope = lambda{ |rel| c.scope = lambda{ |rel|
if session[:selected_bike_id] if session[:selected_bike_id]
rel.where(:task_list_id => Bike.find_by_id(session[:selected_bike_id]).task_list.id) rel.where(:task_list_id => Bike.find_by_id(session[:selected_bike_id]).task_list.id)
@@ -24,9 +21,9 @@ class Tasks < Netzke::Basepack::Grid
:task_list_id => task_list_id :task_list_id => task_list_id
} }
c.columns = [ c.columns = [
:done,
:task, :task,
:notes :notes,
:done
] ]
end end
@@ -34,9 +31,9 @@ class Tasks < Netzke::Basepack::Grid
fields = [] fields = []
fields << { :no_binding => true, :xtype => 'displayfield', :fieldLabel => "No Bike Selected", :value => "Select a Bike First!"} fields << { :no_binding => true, :xtype => 'displayfield', :fieldLabel => "No Bike Selected", :value => "Select a Bike First!"}
fields.concat( [ fields.concat( [
:done,
:task, :task,
:notes :notes,
:done
]) ])
end end
+13 -13
View File
@@ -5,7 +5,6 @@ class TransactionLogs < Netzke::Basepack::Grid
c.model = "ActsAsLoggable::Log" c.model = "ActsAsLoggable::Log"
c.title = "Transaction Payments" c.title = "Transaction Payments"
c.force_fit = true
c.data_store = {auto_load: false} c.data_store = {auto_load: false}
c.scope = lambda { |rel| rel.where(:loggable_type => 'Transaction',:loggable_id => session[:selected_transaction_id]);} c.scope = lambda { |rel| rel.where(:loggable_type => 'Transaction',:loggable_id => session[:selected_transaction_id]);}
c.strong_default_attrs = { c.strong_default_attrs = {
@@ -21,7 +20,7 @@ class TransactionLogs < Netzke::Basepack::Grid
c.columns = [ c.columns = [
{ :name => :start_date, :format => "g:ia - D, M j - Y", :width => 165, :default_value => Time.now.to_formatted_s(:db), :text => 'Date' }, { :name => :start_date, :format => "g:ia - D, M j - Y", :width => 165, :default_value => Time.now.to_formatted_s(:db), :text => 'Date' },
{ :name => :description, :text => "Amount"} , { :name => :description, :text => "Amount"} ,
{ :name => :transaction_action__action, :text => 'Method', :default_value => ::ActsAsLoggable::TransactionAction.first.id}, { :name => :transaction_action__action, :text => 'Method'},
{ :name => :logged_by, :getter => lambda{ |rec| { :name => :logged_by, :getter => lambda{ |rec|
user = User.find_by_id(rec.logger_id) user = User.find_by_id(rec.logger_id)
user.nil? ? "" : "#{user.first_name} #{user.last_name}" user.nil? ? "" : "#{user.first_name} #{user.last_name}"
@@ -30,27 +29,28 @@ class TransactionLogs < Netzke::Basepack::Grid
} }
] ]
@transaction_logs = ::ActsAsLoggable::Log.where(:loggable_type => "Transaction").all c.prohibit_update = true if cannot? :update, ::ActsAsLoggable::Log
c.prohibit_update = true if cannot? :update, @transaction_logs c.prohibit_create = true if cannot? :create, ::ActsAsLoggable::Log
c.prohibit_create = true if cannot? :create, @transaction_logs c.prohibit_delete = true if cannot? :delete, ::ActsAsLoggable::Log
c.prohibit_delete = true if cannot? :delete, @transaction_logs
end end
def default_fields_for_forms def default_fields_for_forms
customer = "No Customer Selected" customer = nil
item = "No Item Selected" item = nil
trans = Transaction.find_by_id(session[:selected_transaction_id]) if session[:selected_transaction_id]
if trans trans = Transaction.find_by_id(session[:selected_transaction_id])
customer = trans.customer customer = trans.customer
item = trans.item item = trans.item
end end
customer = "No Customer Selected" if customer.nil?
item = "No Item Selected" if item.nil?
[ [
{ :no_binding => true, :xtype => 'displayfield', :fieldLabel => "Payment from:", :value => "#{customer.to_s}"}, { :no_binding => true, :xtype => 'displayfield', :fieldLabel => "Payment from:", :value => "#{customer.to_s}"},
{ :no_binding => true, :xtype => 'displayfield', :fieldLabel => "Payment for:", :value => "#{item.to_s}"}, { :no_binding => true, :xtype => 'displayfield', :fieldLabel => "Payment for:", :value => "#{item.to_s}"},
{ :name => :description, :xtype => 'numberfield', :field_label => 'Amount'}, { :name => :description, :xtype => 'numberfield', :field_label => 'Amount'},
#had to hack acts_as_loggable/log.rb to get this to work #had to hack acts_as_loggable/log.rb to get this to work
{ :name => :transaction_action__action, :field_label => 'Payment Method', :min_chars => 1} { :name => :transaction_action__action, :field_label => 'Payment Method'}
] ]
end end
@@ -58,8 +58,8 @@ class TransactionLogs < Netzke::Basepack::Grid
#override with nil to remove actions #override with nil to remove actions
def default_bbar def default_bbar
bbar = [ :search ] bbar = [ :search ]
bbar.concat [ :apply ] if can? :update, @transaction_logs bbar.concat [ :apply ] if can? :update, ::ActsAsLoggable::Log
bbar.concat [:add_in_form ] if can? :create, @transaction_logs bbar.concat [:add_in_form ] if can? :create, ::ActsAsLoggable::Log
bbar bbar
end end
-1
View File
@@ -2,7 +2,6 @@ class Transactions < Netzke::Basepack::Grid
def configure(c) def configure(c)
super super
c.model = "Transaction" c.model = "Transaction"
c.force_fit = true
c.strong_default_attrs = { c.strong_default_attrs = {
:vendor_id => controller.current_user.id, :vendor_id => controller.current_user.id,
:customer_id => session[:selected_customer_id], :customer_id => session[:selected_customer_id],
+2 -2
View File
@@ -4,7 +4,7 @@ class TransactionsBorder < Netzke::Base
component :transactions component :transactions
component :transaction_logs component :transaction_logs
#users and customers components are required for the transactions form #users and customers components are required for the transactions form
component :users_and_customers_lower_tabs component :users_and_customers_accordian
def configure(c) def configure(c)
super super
@@ -13,7 +13,7 @@ class TransactionsBorder < Netzke::Base
c.items = [ c.items = [
{ netzke_component: :transactions, region: :center, height: 300, split: true }, { netzke_component: :transactions, region: :center, height: 300, split: true },
{ netzke_component: :transaction_logs, region: :east, width: 300, split: true }, { netzke_component: :transaction_logs, region: :east, width: 300, split: true },
{ netzke_component: :users_and_customers_lower_tabs, region: :south, height: 300, split: true } { netzke_component: :users_and_customers_accordian, region: :south, height: 300, split: true }
] ]
end end
@@ -5,19 +5,10 @@
// setting the 'rowclick' event // setting the 'rowclick' event
var view = this.getComponent('transactions').getView(); var view = this.getComponent('transactions').getView();
if( this.queryById('transaction_logs')){
this.queryById('transaction_logs').disable();
}
view.on('itemclick', function(view, record){ view.on('itemclick', function(view, record){
// The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server! // The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server!
this.selectTransaction({transaction_id: record.get('id')}); this.selectTransaction({transaction_id: record.get('id')});
this.getComponent('transaction_logs').getStore().load(); this.getComponent('transaction_logs').getStore().load();
if( this.queryById('transaction_logs')){
this.queryById('transaction_logs').enable();
}
}, this); }, this);
} }
} }
@@ -1,13 +0,0 @@
class UserAndProfilesLowerTabs < Netzke::Basepack::TabPanel
component :user_profiles
component :user_logs
component :user_stats
def configure(c)
c.prevent_header = true
c.items = [ :user_logs,
{ netzke_component: :user_profiles, title: "User Profiles" },
{ netzke_component: :user_stats, title: "User Stats" }]
super
end
end
+10 -15
View File
@@ -14,16 +14,10 @@ class UserLogs < Netzke::Basepack::Grid
:copy_action_id => 4 :copy_action_id => 4
} }
#this seems inefficient if can? :manage, ::ActsAsLoggable::Log
if can? :manage, ::ActsAsLoggable::Log.where(:loggable_type => "User").all
#admins and staff #admins and staff
user_log_scope = lambda { |rel| rel.where(:loggable_type => 'User',:loggable_id => session[:selected_user_id]);}
#if selected user nil, then the admin is viewing their own profile user_log_strong_default_attrs.merge!( { :loggable_id => session[:selected_user_id] } )
selected_user = User.find_by_id(session[:selected_user_id])
selected_user = controller.current_user if selected_user.nil?
user_log_scope = lambda { |rel| rel.where(:loggable_type => 'User',:loggable_id => selected_user.id );}
user_log_strong_default_attrs.merge!( { :loggable_id => selected_user.id } )
user_log_data_store = {auto_load: true } user_log_data_store = {auto_load: true }
else else
#just users #just users
@@ -33,18 +27,19 @@ class UserLogs < Netzke::Basepack::Grid
end end
c.model = "ActsAsLoggable::Log" c.model = "ActsAsLoggable::Log"
c.title = "User Timesheet" c.title = "User History"
c.force_fit = true
c.data_store = user_log_data_store c.data_store = user_log_data_store
c.scope = user_log_scope c.scope = user_log_scope
c.strong_default_attrs = user_log_strong_default_attrs c.strong_default_attrs = user_log_strong_default_attrs
c.columns = [ c.columns = [
{ :name => :start_date, :format => "g:ia - D, M j - Y", :width => 165, :default_value => Time.now.to_formatted_s(:db) }, { :name => :start_date, :format => "g:ia - D, M j - Y", :width => 165, :default_value => Time.now.to_formatted_s(:db) },
{ :name => :end_date, :format => "g:ia - D, M j - Y", :default_value => Time.now.to_formatted_s(:db) }, { :name => :end_date, :hidden => true, :default_value => Time.now.to_formatted_s(:db) },
{ :name => :hours, :getter => lambda { |rec| (rec.end_date - rec.start_date)/3600 }, :sorting_scope => :sort_by_duration}, { :name => :hours, :getter => lambda { |rec| (rec.end_date - rec.start_date)/3600 }, :sorting_scope => :sort_by_duration},
:description, :description,
{ :id => :user_action__action, :name => :user_action__action, :text => 'Action', :default_value => ::ActsAsLoggable::UserAction.all.first.id }, { :name => :user_action__action, :text => 'Action' },
:created_at,
:updated_at,
{ :name => :logged_by, :getter => lambda{ |rec| { :name => :logged_by, :getter => lambda{ |rec|
user = User.find_by_id(rec.logger_id) user = User.find_by_id(rec.logger_id)
user.nil? ? "" : "#{user.first_name} #{user.last_name}" user.nil? ? "" : "#{user.first_name} #{user.last_name}"
@@ -58,13 +53,13 @@ class UserLogs < Netzke::Basepack::Grid
bike_store = Bike.all.map { |b| [b.id, b.shop_id] } bike_store = Bike.all.map { |b| [b.id, b.shop_id] }
current_user ||= User.find_by_id(session[:selected_user_id]) || controller.current_user current_user ||= User.find_by_id(session[:selected_user_id]) || controller.current_user
bike_id = current_user.bike.nil? ? nil : current_user.bike.id bike_id = current_user.bike.nil? ? nil : current_user.bike.id
action_id = current_user.user_role.id
[ [
{ :no_binding => true, :xtype => 'displayfield', :fieldLabel => "Log for:", :value => "#{current_user.to_s}"},
{ :name => :start_date}, { :name => :start_date},
{ :name => :end_date}, { :name => :end_date},
{ :name => :description}, { :name => :description},
#had to hack acts_as_loggable/log.rb to get this to work #had to hack acts_as_loggable/log.rb to get this to work
{ :name => :user_action__action, :field_label => 'Action', :min_chars => 1}, { :name => :user_action__action, :field_label => 'Action', :value => action_id},
{ :name => :for_bike, :checkboxName => :copy_log, :inputValue => true, :title => "Copy description to a Bike's History?", :xtype => 'fieldset', :checkboxToggle => true, :collapsed => true, :items => [ { :name => :for_bike, :checkboxName => :copy_log, :inputValue => true, :title => "Copy description to a Bike's History?", :xtype => 'fieldset', :checkboxToggle => true, :collapsed => true, :items => [
{:xtype => 'combo', :no_binding => true, :name => :copy_id, :title => 'Bike', :fieldLabel => 'Bike', :store => bike_store, :value => bike_id} {:xtype => 'combo', :no_binding => true, :name => :copy_id, :title => 'Bike', :fieldLabel => 'Bike', :store => bike_store, :value => bike_id}
] ]
@@ -1,13 +0,0 @@
{
initComponent: function(){
// calling superclass's initComponent
this.callParent();
//due to Netzke bug, :min_chars attribute doesn't work
var min_char_columns = [
"user_action__purpose"]
Ext.each(min_char_columns, function(column, index) {
Ext.ComponentManager.get(column).editor.minChars = 1;
});
}
}
-1
View File
@@ -19,7 +19,6 @@ class UserProfiles < Netzke::Basepack::Grid
c.model = "UserProfile" c.model = "UserProfile"
c.title = "Profile" c.title = "Profile"
c.force_fit = true
c.data_store = user_profiles_data_store c.data_store = user_profiles_data_store
c.scope = user_profiles_scope c.scope = user_profiles_scope
c.strong_default_attrs = user_profile_strong_default_attrs c.strong_default_attrs = user_profile_strong_default_attrs
-35
View File
@@ -1,35 +0,0 @@
class UserRoleJoins < Netzke::Basepack::Grid
def configure(c)
super
c.model = "UserRoleJoin"
c.force_fit = true
c.header = false
c.title = "User Roles"
c.data_store.sorters = [{ :property => :user__username, :direction => :ASC}]
c.columns = [
{ :name => :user__username, :text => "Username", :read_only => true},
{ :name => :name, :getter => lambda{ |rec|
user = User.find_by_id(rec.user_id)
user.nil? ? "" : "#{user.first_name} #{user.last_name}"
},
},
{ :name => :role__role, :text => "Role", :default_value => Role.exists? ? Role.first.id : nil},
:created_at,
:updated_at,
:ends ]
end
def default_fields_for_forms
[
{ :name => :user__full_name, :field_label => 'Name'},
{ :name => :role__role, :field_label => 'Role' },
{ :name => :ends }
]
end
#override with nil to remove actions
def default_bbar
[ :apply, :add_in_form, :search ]
end
end
+15
View File
@@ -0,0 +1,15 @@
class UserRoles < Netzke::Basepack::Grid
def configure(c)
super
c.model = "UserRole"
c.title = "User Roles"
c.columns = [ :role, :created_at, :updated_at, :ends ]
end
#override with nil to remove actions
def default_bbar
[ :apply, :add_in_form, :search ]
end
end
+4 -19
View File
@@ -2,29 +2,14 @@ class UserStats < Netzke::Base
def body_content(user) def body_content(user)
bike = user.bike bike = user.bike
completed = user.completed_build_bikes %Q(
#TODO I think it's time to switch to a template <div id="user_stats_page">
body = ""
body += %Q(<div id="user_stats_page">)
body += %Q(
<p>Username: #{user.username}</p>
<p>Total Credits Earned: #{user.total_earned_credits}</p>
<p>Credits Available: #{user.total_credits}</p>
<p>Credits Spent: #{user.total_credits_spent}</p>
<p>Total Hours Worked: #{user.total_hours}</p> <p>Total Hours Worked: #{user.total_hours}</p>
<p>Hours worked in #{Time.now.strftime('%B')}: #{user.current_month_hours}</p> <p>Hours worked in #{Time.now.strftime('%B')}: #{user.current_month_hours}</p>
<p>Current bike Shop ID: #{bike.shop_id if bike}</p> <p>Current bike Shop ID: #{bike.shop_id if bike}</p>
<p>Current bike S/N: #{bike.serial_number if bike}</p> <p>Current bike S/N: #{bike.serial_number if bike}</p>
</div>
) )
unless completed.empty?
body += %Q(<p>Previously built bikes (#{completed.count}):</p>)
body += %Q(<ul>)
completed.each do |b|
body += %Q(<li>#{b.shop_id}</li>)
end
body += %Q(</ul>)
end
body += %Q(</div>)
end end
js_configure do |c| js_configure do |c|
@@ -40,7 +25,7 @@ class UserStats < Netzke::Base
private private
def user def user
User.find_by_id(session[:selected_user_id]) || controller.current_user controller.current_user
end end
end end
-1
View File
@@ -5,7 +5,6 @@ class UserTransactions < Netzke::Basepack::Grid
c.model = "Transaction" c.model = "Transaction"
c.title = "Transactions" c.title = "Transactions"
c.force_fit = true
c.scope = lambda { |rel| rel.where(:customer_id => controller.current_user.id, :customer_type => 'User');} c.scope = lambda { |rel| rel.where(:customer_id => controller.current_user.id, :customer_type => 'User');}
c.data_store = { auto_load: true } c.data_store = { auto_load: true }
c.columns = [ c.columns = [
@@ -5,18 +5,10 @@
// setting the 'rowclick' event // setting the 'rowclick' event
var view = this.getComponent('user_transactions').getView(); var view = this.getComponent('user_transactions').getView();
if( this.queryById('transaction_logs')){
this.queryById('transaction_logs').disable();
}
view.on('itemclick', function(view, record){ view.on('itemclick', function(view, record){
// The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server! // The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server!
this.selectTransaction({transaction_id: record.get('id')}); this.selectTransaction({transaction_id: record.get('id')});
this.getComponent('transaction_logs').getStore().load(); this.getComponent('transaction_logs').getStore().load();
if( this.queryById('transaction_logs')){
this.queryById('transaction_logs').enable();
}
}, this); }, this);
} }
} }
+3 -14
View File
@@ -1,28 +1,17 @@
class Users < Netzke::Basepack::Grid class Users < Netzke::Basepack::Grid
include Netzke::Basepack::ActionColumn
column :reset do |c|
c.type = :action
c.actions = [{name: :reset_password, icon: :lock_break}]
c.header = ""
c.width = 20
end
def configure(c) def configure(c)
super super
c.header = false c.header = false
c.model = "User" c.model = "User"
c.force_fit = true
c.columns = [ c.columns = [
{ :name => :username, :read_only => true },
:first_name, :first_name,
:last_name, :last_name,
:nickname,
:email, :email,
{ :text => "Bike - Shop ID", :id => :bike__shop_id, :name => :bike__shop_id, :default_value => Bike.exists? ? Bike.first.id : nil } :user_role__role,
:bike__shop_id
] ]
c.columns << :reset if can? :manage, User
end end
#override with nil to remove actions #override with nil to remove actions
@@ -2,41 +2,10 @@
initComponent: function(){ initComponent: function(){
// calling superclass's initComponent // calling superclass's initComponent
this.callParent(); this.callParent();
//due to Netzke bug, :min_chars attribute doesn't work
var min_char_columns = ["bike__shop_id"]
Ext.each(min_char_columns, function(column, index) {
Ext.ComponentManager.get(column).editor.minChars = 1;
});
this.getView().on('itemclick', function(view, record){ this.getView().on('itemclick', function(view, record){
// The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server! // The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server!
console.log("user: " + record.get('id') );
this.selectCustomer({customer_id: record.get('id'), customer_type: 'User'}); this.selectCustomer({customer_id: record.get('id'), customer_type: 'User'});
}, this); }, this);
},
onResetPassword: function(record){
user = record.data;
Ext.Msg.confirm(
"Reset Password",
"Are you sure you want to reset "+user.first_name+" "+user.last_name+"'s password?",
function(butt_id){
if( butt_id === "yes" ){
$.ajax({
type: 'POST',
url: '/api/v1/reset',
dataType: 'json',
contentType: 'application/json',
processData: false,
data: JSON.stringify({"user_id": user.id}),
complete: function() { },
success: function(data) {
Ext.Msg.alert("Success", "New Password: "+data.password);
},
error: function(data,textStatus) {
Ext.Msg.alert( "Error", JSON.parse(data.responseText)["error"]);
}
});
}
});
} }
} }
@@ -1,10 +1,10 @@
class UsersAndCustomersLowerTabs < Netzke::Basepack::TabPanel class UsersAndCustomersAccordian < Netzke::Basepack::Accordion
component :customers component :customers
component :users component :users
def configure(c) def configure(c)
c.prevent_header = true c.prevent_header = true
c.items = [ :users, :customers ] c.items = [ :customers, :users ]
super super
end end
end end
+5 -3
View File
@@ -2,14 +2,16 @@ class UsersAndProfilesBorder < Netzke::Base
# Remember regions collapse state and size # Remember regions collapse state and size
include Netzke::Basepack::ItemPersistence include Netzke::Basepack::ItemPersistence
component :users component :users
component :user_and_profiles_lower_tabs component :user_profiles
component :user_logs
def configure(c) def configure(c)
super super
c.header = false c.header = false
c.items = [ c.items = [
{ netzke_component: :users, header: "Users", region: :center, width: 350, split: true }, { netzke_component: :users, header: "Users", region: :center, width: 300, split: true },
{ netzke_component: :user_and_profiles_lower_tabs, region: :south, height: 300, split: true} { netzke_component: :user_profiles, region: :south, height: 150, split: true},
{ netzke_component: :user_logs, region: :east, split: true}
] ]
end end
@@ -2,48 +2,18 @@
initComponent: function(){ initComponent: function(){
// calling superclass's initComponent // calling superclass's initComponent
this.callParent(); this.callParent();
var stats = this.queryById('user_stats'); var stats = this.getComponent('user_stats');
if (stats != undefined){ if (stats != undefined){
stats.updateStats(); stats.updateStats();
} }
if( this.queryById('user_profiles')){
this.queryById('user_profiles').disable();
}
if( this.queryById('user_logs')){
this.queryById('user_logs').disable();
//update user stats
var store = this.queryById('user_logs').getStore()
store.on('load', function (store, records, operation, success){
if( this.queryById('user_stats') ){
this.queryById('user_stats').updateStats();
}
}, this);
}
if( this.queryById('user_stats') ){
this.queryById('user_stats').disable();
}
// setting the 'rowclick' event // setting the 'rowclick' event
var view = this.getComponent('users').getView(); var view = this.getComponent('users').getView();
view.on('itemclick', function(view, record){ view.on('itemclick', function(view, record){
// The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server! // The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server!
this.selectUser({user_id: record.get('id')}); this.selectUser({user_id: record.get('id')});
this.getComponent('user_profiles').getStore().load();
if( this.queryById('user_profiles')){ this.getComponent('user_logs').getStore().load();
this.queryById('user_profiles').getStore().load();
this.queryById('user_profiles').enable();
}
if( this.queryById('user_logs')){
this.queryById('user_logs').getStore().load();
this.queryById('user_logs').enable();
}
if( this.queryById('user_stats') ){
this.queryById('user_stats').updateStats();
this.queryById('user_stats').enable();
}
}, this); }, this);
} }
} }
-22
View File
@@ -1,22 +0,0 @@
class Api::V1::BaseController < ActionController::Base
respond_to :json
before_filter :authenticate_user
private
def authenticate_user
if params[:username]
user = User.find_for_database_authentication( :email => params[:username] )
@current_user = user if user && user.valid_password?( params[:password] )
if @current_user.nil?
msg = "Username/Password/Token invalid"
render :json => {:error => msg }, :status => 403 and return
end
else
authenticate_user!
@current_user = current_user
end
end
end
-22
View File
@@ -1,22 +0,0 @@
class Api::V1::LogsController < Api::V1::BaseController
def checkin
#must use @current_user since user may not have signed in
if @current_user.checked_in?
render :json => { "error" => "You are already checked in."}, :status => 404 and return
else
@current_user.checkin
render :nothing => true, :status => 204 and return
end
end
def checkout
#must use @current_user since user may not have signed in
if !@current_user.checked_in?
render :json => { "error" => "You were not even checked in."}, :status => 404 and return
else
@current_user.checkout
render :nothing => true, :status => 204 and return
end
end
end
@@ -1,28 +0,0 @@
require 'securerandom'
class Api::V1::UsersController < Api::V1::BaseController
def password_reset
if can? :manage, User
user = User.find_by_id(params[:user_id])
render :json => { "error" => "User not found"}, :status => 404 and return if user.nil?
render :json => { "error" => "Not allowed to reset your own password in this fashion."}, :status => 403 and return if user.id == current_user.id
new_pass = SecureRandom.hex[0,8]
user.password = new_pass
user.save
render :json => { "password" => new_pass}, :status => 200 and return
else
render :json => { "error" => "You do not have the permission"}, :status => 403 and return
end
end
def checkout
#must use @current_user since user may not have signed in
if !@current_user.checked_in?
render :json => { "error" => "You were not even checked in."}, :status => 404 and return
else
@current_user.checkout
render :nothing => true, :status => 204 and return
end
end
end
-23
View File
@@ -1,23 +0,0 @@
module DeviseHelper
# A simple way to show error messages for the current devise resource. If you need
# to customize this method, you can either overwrite it in your application helpers or
# copy the views to your application.
#
# This method is intended to stay simple and it is unlikely that we are going to change
# it to add more behavior or options.
def devise_error_messages!
return "" if resource.errors.empty?
messages = resource.errors.full_messages.map { |msg| content_tag(:p, msg, :class => "alert") }.join
sentence = I18n.t("errors.messages.not_saved",
:count => resource.errors.count,
:resource => resource.class.model_name.human.downcase)
html = <<-HTML
<p>#{sentence}</p>
#{messages}
HTML
html.html_safe
end
end
+3 -5
View File
@@ -3,9 +3,7 @@ class Ability
def initialize(current_user) def initialize(current_user)
@current_user = current_user @current_user = current_user
current_user.roles.each do |role| self.send(current_user.role.to_sym)
self.send(role.role.to_sym)
end
end end
def admin def admin
@@ -24,7 +22,7 @@ class Ability
def user def user
can :read, :all can :read, :all
can :manage, Bike, :id => @current_user.bike_id unless @current_user.bike.nil? can :manage, Bike, :id => @current_user.bike_id unless @current_user.bike.nil?
can :manage, ::ActsAsLoggable::Log, :loggable_type => "Bike", :loggable_id => @current_user.bike_id can :manage, ::ActsAsLoggable::Log, { :loggable_type => "Bike", :loggable_id => @current_user.bike_id }
can :manage, ::ActsAsLoggable::Log, :loggable_type => "User", :loggable_id => @current_user.id can :manage, ::ActsAsLoggable::Log, { :loggable_type => "User", :loggable_id => @current_user.id }
end end
end end
+18 -16
View File
@@ -1,37 +1,43 @@
class Bike < ActiveRecord::Base class Bike < ActiveRecord::Base
acts_as_loggable acts_as_loggable
attr_accessible :shop_id, :serial_number, :bike_brand_id, :model, :color, :bike_style_id, :seat_tube_height, attr_accessible :shop_id, :serial_number, :bike_brand_id, :bike_model_id, :color, :bike_style_id, :seat_tube_height,
:top_tube_length, :bike_wheel_size_id, :value, :bike_condition_id, :bike_purpose_id :top_tube_length, :wheel_size, :value, :bike_condition_id, :bike_status_id
has_many :transactions has_many :transactions
has_one :owner, :class_name => 'User' has_one :owner, :class_name => 'User'
has_one :task_list, :as => :item, :dependent => :destroy has_one :task_list, :as => :item, :dependent => :destroy
belongs_to :bike_brand belongs_to :bike_brand
belongs_to :bike_model
belongs_to :bike_style belongs_to :bike_style
belongs_to :bike_condition belongs_to :bike_condition
belongs_to :bike_purpose belongs_to :bike_status
belongs_to :bike_wheel_size
validates :shop_id, :presence => true, :uniqueness => true, :numericality => { :only_integer => true } validates :shop_id, :presence => true, :uniqueness => true, :length => { :minimum => 3 }
validates :serial_number, :length => { :minimum => 3 } validates :serial_number, :uniqueness => true, :length => { :minimum => 3 }
validates :model, :length => { :maximum => 50 }
validates :bike_brand_id, :presence => true validates :bike_brand_id, :presence => true
validates :bike_model_id, :presence => true
validates :color, :presence => true validates :color, :presence => true
validates :bike_style_id, :presence => true validates :bike_style_id, :presence => true
validates :seat_tube_height, :presence => true validates :seat_tube_height, :presence => true
validates :bike_wheel_size_id, :presence => true validates :top_tube_length, :presence => true
validates :wheel_size, :presence => true
#validates :value, :presence => true
validates :bike_condition_id, :presence => true validates :bike_condition_id, :presence => true
validates :bike_purpose_id, :presence => true validates :bike_status_id, :presence => true
self.per_page = 15 self.per_page = 15
after_create :create_task_list after_save :create_task_list
def brand def brand
self.bike_brand self.bike_brand
end end
def model
self.bike_model
end
def style def style
self.bike_style self.bike_style
end end
@@ -40,12 +46,8 @@ class Bike < ActiveRecord::Base
self.bike_condition self.bike_condition
end end
def purpose def status
self.bike_purpose self.bike_status
end
def wheel_size
self.bike_wheel_size
end end
def to_s def to_s
+1
View File
@@ -1,6 +1,7 @@
class BikeModel < ActiveRecord::Base class BikeModel < ActiveRecord::Base
attr_accessible :model, :bike_brand_id attr_accessible :model, :bike_brand_id
has_many :bikes
belongs_to :bike_brand belongs_to :bike_brand
default_scope order('model ASC') default_scope order('model ASC')
-9
View File
@@ -1,9 +0,0 @@
class BikePurpose < ActiveRecord::Base
attr_accessible :purpose
belongs_to :bike
def to_s
self.purpose
end
end
+9
View File
@@ -0,0 +1,9 @@
class BikeStatus < ActiveRecord::Base
attr_accessible :status
belongs_to :bike
def to_s
self.status
end
end
-16
View File
@@ -1,16 +0,0 @@
class BikeWheelSize < ActiveRecord::Base
belongs_to :bike
def display_string
result = []
result << "#{twmm}-#{rdmm}" unless twmm.blank? and rdmm.blank?
result << "#{rdin}x#{twin}" unless rdin.blank? and twin.blank?
result << "#{rdfr}x#{twfr}" unless rdfr.blank? and twfr.blank?
result << description unless description.blank?
result.join(" | ")
end
def to_s
"#{twmm}-#{rdmm} #{rdin}x#{twin} #{rdfr}x#{twfr} '#{description}' #{tire_common_score}"
end
end
-3
View File
@@ -1,3 +0,0 @@
class CreditConversion < ActiveRecord::Base
attr_accessible :conversion
end
-16
View File
@@ -1,16 +0,0 @@
class Role < ActiveRecord::Base
attr_accessible :role
has_many :user_role_joins
has_many :users, through: :user_role_joins
validates_uniqueness_of :role
def to_s
self.role
end
def ==(other)
self.role == other.role
end
end
+11 -103
View File
@@ -7,21 +7,16 @@ class User < ActiveRecord::Base
# Setup accessible (or protected) attributes for your model # Setup accessible (or protected) attributes for your model
attr_accessible :email, :password, :password_confirmation, :remember_me, attr_accessible :email, :password, :password_confirmation, :remember_me,
:first_name, :last_name, :bike_id, :first_name, :last_name, :nickname, :user_role_id, :bike_id,
:user_profiles_attributes, :username :user_profiles_attributes
has_many :transactions, as: :customer has_many :transactions
has_many :transaction_logs, through: :transactions, source: :logs
has_many :user_profiles has_many :user_profiles
accepts_nested_attributes_for :user_profiles, allow_destroy: false accepts_nested_attributes_for :user_profiles, allow_destroy: false
has_many :user_role_joins, :conditions => ["ends IS NULL OR ends > ?", Time.now] has_one :user_role
has_many :roles, through: :user_role_joins
belongs_to :bike belongs_to :bike
default_scope order('username ASC')
validates :first_name, :presence => true validates :first_name, :presence => true
validates :last_name, :presence => true validates :last_name, :presence => true
@@ -29,116 +24,29 @@ class User < ActiveRecord::Base
"#{first_name} #{last_name}" "#{first_name} #{last_name}"
end end
def email_required?
false
end
def full_name def full_name
to_s to_s
end end
def role
user_role.role
end
def role?(role) def role?(role)
if role.kind_of?(String) or role.kind_of?(Symbol) user_role.to_s == role.to_s
role = Role.find_by_role(role.to_s)
end
roles.include?(role)
end
### TODO methods below probably belong somewhere else
def completed_build_bikes
#default BUILDBIKE/CLASS ID is 5
purpose_id = 5
Bike.find_by_sql("
SELECT *
FROM bikes
INNER JOIN(
SELECT *
FROM transactions
WHERE customer_id = #{self.id}
) AS transactions ON bikes.id = transactions.bike_id
WHERE bike_purpose_id = #{purpose_id}")
end
def total_credits
total_earned_credits - total_credits_spent
end
def total_credits_spent
log_action = ::ActsAsLoggable::TransactionAction.find_by_action("TIME")
transaction_logs.
where( "log_action_id = ? AND log_action_type = ?",
log_action.id, log_action.class.to_s).
sum{ |r| r.description.to_i }.round(2)
end
def total_earned_credits
log_action = ::ActsAsLoggable::UserAction.find_by_action("CHECKIN")
# Find the first credit conversion which has a created_at date before the
# log's created_at date and join it to the log's row so we can calculate
# the credits earned from that log entry (each log could have a different
# conversion rate)
#
# The DISTINCT ON, and ORDER BY are important to getting the
# single conversion rate that applies to the respective log.
::ActsAsLoggable::Log.find_by_sql("
SELECT DISTINCT ON (logs.created_at) start_date, end_date,
conversion.conversion, conversion.created_at
FROM logs
INNER JOIN(
SELECT conversion, created_at
FROM credit_conversions
) AS conversion ON logs.created_at > conversion.created_at
WHERE logs.loggable_id = #{self.id}
AND logs.loggable_type = 'User'
AND (log_action_id != #{log_action.id} AND log_action_type = '#{log_action.class.to_s}')
ORDER BY logs.created_at, conversion.created_at DESC").
sum{ |l| ((l.end_date - l.start_date)/3600) * l.conversion.to_i}.round(2)
end end
def total_hours def total_hours
log_action = ::ActsAsLoggable::UserAction.find_by_action("CHECKIN") ActsAsLoggable::Log.where( :loggable_type => self.class.to_s, :loggable_id => self.id).sum { |l| (l.end_date - l.start_date)/3600 }.round(2)
logs.where("log_action_id != ? AND log_action_type = ?", log_action.id, log_action.class.to_s).sum { |l| (l.end_date - l.start_date)/3600 }.round(2)
end end
def current_month_hours def current_month_hours
log_action = ::ActsAsLoggable::UserAction.find_by_action("CHECKIN")
#TODO need to prevent users from saving logs across months, force to create a new log if crossing month #TODO need to prevent users from saving logs across months, force to create a new log if crossing month
current_month_range = (Time.now.beginning_of_month..Time.now.end_of_month) current_month_range = (Time.now.beginning_of_month..Time.now.end_of_month)
logs.where("log_action_id != ? AND log_action_type = ?", log_action.id, log_action.class.to_s) ActsAsLoggable::Log.where( :loggable_type => self.class.to_s, :loggable_id => self.id)
.where( :start_date => current_month_range) .where( :start_date => current_month_range)
.where( :end_date => current_month_range) .where( :end_date => current_month_range)
.sum { |l| (l.end_date - l.start_date)/3600 } .sum { |l| (l.end_date - l.start_date)/3600 }
.round(2) .round(2)
end end
def checked_in?
log_action = ::ActsAsLoggable::UserAction.find_by_action("CHECKIN")
checked = logs.where( log_action_id: log_action.id).
where("start_date >= ?", Time.zone.now.beginning_of_day).
where("start_date = end_date")
!checked.empty?
end
def checkin
log_action = ::ActsAsLoggable::UserAction.find_by_action("CHECKIN")
time = Time.now
logs.create( logger_id: self.id,
logger_type: self.class.to_s,
start_date: time,
end_date: time,
log_action_id: log_action.id,
log_action_type: log_action.class.to_s)
save
end
def checkout
log_action = ::ActsAsLoggable::UserAction.find_by_action("CHECKIN")
checked = logs.where( log_action_id: log_action.id).
where("start_date >= ?", Time.zone.now.beginning_of_day).
where("start_date = end_date").first
checked.end_date = Time.now
checked.save
end
end end
+6 -5
View File
@@ -1,15 +1,16 @@
class UserProfile < ActiveRecord::Base class UserProfile < ActiveRecord::Base
# Setup accessible (or protected) attributes for your model
attr_accessible :user_id, :addrStreet1, :addrStreet2, :addrCity, attr_accessible :user_id, :addrStreet1, :addrStreet2, :addrCity,
:addrState, :addrZip, :phone :addrState, :addrZip, :phone
belongs_to :user belongs_to :user
belongs_to :bike belongs_to :bike
#validates :addrStreet1 , :presence => true #validates :addrStreet1 , :presence => true
#validates :addrCity , :presence => true #validates :addrCity , :presence => true
#validates :addrState , :presence => true #validates :addrState , :presence => true
#validates :addrZip , :presence => true #validates :addrZip , :presence => true
#validates :phone, :presence => true validates :phone, :presence => true
def to_s def to_s
[addrStreet1, addrStreet2, addrCity, addrState, addrZip, phone].join(" - ") [addrStreet1, addrStreet2, addrCity, addrState, addrZip, phone].join(" - ")
+11
View File
@@ -0,0 +1,11 @@
class UserRole < ActiveRecord::Base
attr_accessible :role
belongs_to :user
self.per_page = 15
def to_s
self.role
end
end
-13
View File
@@ -1,13 +0,0 @@
class UserRoleJoin < ActiveRecord::Base
self.table_name = :user_role_joins
attr_accessible :role_id, :user_id, :ends
belongs_to :user
belongs_to :role
validate :role_id, presence: true, numericality: true
validate :user_id, presence: true, numericality: true
validates_uniqueness_of :user_id, :scope => :role_id
self.per_page = 15
end
@@ -1,25 +1,24 @@
%fieldset %fieldset
.control-group = f.label :addrStreet1
= f.label :addrStreet1, :class => "control-label" %br/
.controls = f.text_field :addrStreet1
= f.text_field :addrStreet1, :class => "control-label" %br/
.control-group = f.label :addrStreet2
= f.label :addrStreet2, :class => "control-label" %br/
.controls = f.text_field :addrStreet2
= f.text_field :addrStreet2, :class => "control-label" %br/
.control-group = f.label :addrCity
= f.label :addrCity, :class => "control-label" %br/
.controls = f.text_field :addrCity
= f.text_field :addrCity, :class => "control-label" %br/
.control-group = f.label :addrState
= f.label :addrState, :class => "control-label" %br/
.controls = f.text_field :addrState
= f.text_field :addrState, :class => "control-label" %br/
.control-group = f.label :addrZip
= f.label :addrZip, :class => "control-label" %br/
.controls = f.text_field :addrZip
= f.text_field :addrZip, :class => "control-label" %br/
.control-group = f.label :phone
= f.label :phone, :class => "control-label" %br/
.controls = f.text_field :phone
= f.text_field :phone, :class => "control-label"
@@ -1,5 +1,3 @@
<%= stylesheet_link_tag "bootstrap_and_overrides", :media => "all" %>
<h2>Edit <%= resource_name.to_s.humanize %></h2> <h2>Edit <%= resource_name.to_s.humanize %></h2>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %> <%= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f| %>
+30 -34
View File
@@ -1,36 +1,32 @@
= stylesheet_link_tag "bootstrap_and_overrides", :media => "all" %h2 Sign up
= form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :class => "form-horizontal"}) do |f| = form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f|
= devise_error_messages! = devise_error_messages!
.controls %div
%h2 Sign up = f.label :first_name
.control-group %br/
= f.label :username, :class => "control-label" = f.text_field :first_name
.controls %div
= f.text_field :username = f.label :last_name
.control-group %br/
= f.label :first_name, :class => "control-label" = f.text_field :last_name
.controls %div
= f.text_field :first_name = f.label :nickname
.control-group %br/
= f.label :last_name, :class => "control-label" = f.text_field :nickname
.controls %div
= f.text_field :last_name = f.label :email
.control-group %br/
= f.label :email, :class => "control-label" = f.email_field :email
.controls %div
= f.email_field :email = f.fields_for :user_profiles, UserProfile.new do |builder|
- profile_builder = resource.user_profiles.empty? ? resource.user_profiles.build : resource.user_profiles = render 'user_profile_fields', f: builder
= f.fields_for :user_profiles, profile_builder do |builder| %div
= render 'user_profile_fields', f: builder = f.label :password
.control-group %br/
= f.label :password, :class => "control-label" = f.password_field :password
.controls %div
= f.password_field :password = f.label :password_confirmation
.control-group %br/
= f.label :password_confirmation, :class => "control-label" = f.password_field :password_confirmation
.controls %div= f.submit "Sign up"
= f.password_field :password_confirmation
.control-group
.controls
= f.submit "Sign up"
= render "links" = render "links"
+6 -16
View File
@@ -1,12 +1,8 @@
<%= stylesheet_link_tag "bootstrap_and_overrides", :media => "all" %> <h2>Sign in</h2>
<h2>Velocipede</h2>
<a href="http://madewithloveinbaltimore.org">Made with &hearts; in Baltimore</a>
<br>
<br>
<%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %> <%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
<div><%= f.label :username%><br /> <div><%= f.label :email %><br />
<%= f.text_field :username%></div> <%= f.email_field :email %></div>
<div><%= f.label :password %><br /> <div><%= f.label :password %><br />
<%= f.password_field :password %></div> <%= f.password_field :password %></div>
@@ -16,23 +12,17 @@
<% end -%> <% end -%>
<div><%= f.submit "Sign in" %></div> <div><%= f.submit "Sign in" %></div>
<div id="checkin_menu"style="display:none;">
<input id="checkin" name="checkin" type="button" value="CHECK IN">
<input id="checkout" name="checkout" type="button" value="CHECK OUT">
</div>
<% end %> <% end %>
<%= render "links" %> <%= render "links" %>
<% if Rails.env.development? %> <% if Rails.env.development? %>
<% User.all.each do |user| %> <% User.all.each do |user| %>
<%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %> <%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
<%= f.hidden_field :username, :value => user.username%></div> <%= f.hidden_field :email, :value => user.email %></div>
<%= f.hidden_field :password, :value => 'password' %></div> <%= f.hidden_field :password, :value => 'password' %></div>
<div><%= f.submit "Sign in as #{user.username}" %></div> <div><%= f.submit "Sign in as #{user.email}" %></div>
<% end %> <% end %>
<% end %> <% end %>
<% end %> <% end %>
-4
View File
@@ -11,9 +11,6 @@
body { body {
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */ padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
} }
.x-boundlist-item {
white-space: nowrap;
}
%body %body
.container .container
@@ -31,4 +28,3 @@
%p &copy; Velocipede 2013 %p &copy; Velocipede 2013
= javascript_include_tag "application" = javascript_include_tag "application"
= javascript_include_tag params[:controller]
+25
View File
@@ -0,0 +1,25 @@
# SQLite version 3.x
# gem install sqlite3
#
# Ensure the SQLite 3 gem is defined in your Gemfile
# gem 'sqlite3'
development:
adapter: sqlite3
database: db/development.sqlite3
pool: 5
timeout: 5000
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
adapter: sqlite3
database: db/test.sqlite3
pool: 5
timeout: 5000
production:
adapter: sqlite3
database: db/production.sqlite3
pool: 5
timeout: 5000
-23
View File
@@ -1,23 +0,0 @@
development:
adapter: postgresql
database: velocipede
username: velocipede
password:
host: 127.0.0.1
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
adapter: postgresql
database: velocipede
username: velocipede
password:
host: 127.0.0.1
production:
adapter: postgresql
database: velocipede
username: velocipede
password:
host: 127.0.0.1
+12 -4
View File
@@ -5,15 +5,20 @@ Velocipede::Application.configure do
config.cache_classes = true config.cache_classes = true
# Full error reports are disabled and caching is turned on # Full error reports are disabled and caching is turned on
config.consider_all_requests_local = true config.consider_all_requests_local = false
config.action_controller.perform_caching = true config.action_controller.perform_caching = true
# Disable Rails's static asset server (Apache or nginx will already do this) # Disable Rails's static asset server (Apache or nginx will already do this)
config.serve_static_assets = true config.serve_static_assets = false
# Compress JavaScripts and CSS # Compress JavaScripts and CSS
config.assets.compress = false config.assets.compress = true
config.assets.debug = true
# Don't fallback to assets pipeline if a precompiled asset is missed
config.assets.compile = false
# Generate digests for assets URLs
config.assets.digest = true
# Defaults to Rails.root.join("public/assets") # Defaults to Rails.root.join("public/assets")
# config.assets.manifest = YOUR_PATH # config.assets.manifest = YOUR_PATH
@@ -40,6 +45,9 @@ Velocipede::Application.configure do
# Enable serving of images, stylesheets, and JavaScripts from an asset server # Enable serving of images, stylesheets, and JavaScripts from an asset server
# config.action_controller.asset_host = "http://assets.example.com" # config.action_controller.asset_host = "http://assets.example.com"
# Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
# config.assets.precompile += %w( search.js )
# TODO set to host # TODO set to host
# Disable delivery errors, bad email addresses will be ignored # Disable delivery errors, bad email addresses will be ignored
# config.action_mailer.raise_delivery_errors = false # config.action_mailer.raise_delivery_errors = false
+3 -12
View File
@@ -26,7 +26,7 @@ Devise.setup do |config|
# session. If you need permissions, you should implement that in a before filter. # session. If you need permissions, you should implement that in a before filter.
# You can also supply a hash where the value is a boolean determining whether # You can also supply a hash where the value is a boolean determining whether
# or not authentication should be aborted when the value is not present. # or not authentication should be aborted when the value is not present.
config.authentication_keys = [ :username ] # config.authentication_keys = [ :email ]
# Configure parameters from the request object used for authentication. Each entry # Configure parameters from the request object used for authentication. Each entry
# given should be a request method and it will automatically be passed to the # given should be a request method and it will automatically be passed to the
@@ -38,12 +38,12 @@ Devise.setup do |config|
# Configure which authentication keys should be case-insensitive. # Configure which authentication keys should be case-insensitive.
# These keys will be downcased upon creating or modifying a user and when used # These keys will be downcased upon creating or modifying a user and when used
# to authenticate or find a user. Default is :email. # to authenticate or find a user. Default is :email.
config.case_insensitive_keys = [ :username ] config.case_insensitive_keys = [ :email ]
# Configure which authentication keys should have whitespace stripped. # Configure which authentication keys should have whitespace stripped.
# These keys will have whitespace before and after removed upon creating or # These keys will have whitespace before and after removed upon creating or
# modifying a user and when used to authenticate or find a user. Default is :email. # modifying a user and when used to authenticate or find a user. Default is :email.
config.strip_whitespace_keys = [ :username ] config.strip_whitespace_keys = [ :email ]
# Tell if authentication through request.params is enabled. True by default. # Tell if authentication through request.params is enabled. True by default.
# It can be set to an array that will enable params authentication only for the # It can be set to an array that will enable params authentication only for the
@@ -221,12 +221,3 @@ Devise.setup do |config|
# manager.default_strategies(:scope => :user).unshift :some_external_strategy # manager.default_strategies(:scope => :user).unshift :some_external_strategy
# end # end
end end
#Check in the user if they sign in. (Devise uses Warden)
Warden::Manager.after_set_user do |user,auth,opts|
# this essentially gets called after every netzke request, but alas,
# only using the after_authenticaion callback doesn't get fired after
# user creation.
user.checkin unless user.checked_in?
end
+1 -1
View File
@@ -14,7 +14,7 @@ en:
devise: devise:
failure: failure:
already_authenticated: 'You are already signed in.' already_authenticated: 'You are already signed in.'
unauthenticated: 'You need to provide your username and password or sign in/sign up before continuing.' unauthenticated: 'You need to sign in or sign up before continuing.'
unconfirmed: 'You have to confirm your account before continuing.' unconfirmed: 'You have to confirm your account before continuing.'
locked: 'Your account is locked.' locked: 'Your account is locked.'
invalid: 'Invalid email or password.' invalid: 'Invalid email or password.'
-10
View File
@@ -4,14 +4,4 @@ Velocipede::Application.routes.draw do
netzke netzke
root :to => 'site#index' root :to => 'site#index'
###########################
# API Routes
scope 'api', :module => :api do
scope 'v1', :module => :v1 do
post 'checkin' => "logs#checkin", :as => "api_checkin"
post 'checkout' => "logs#checkout", :as => "api_checkout"
post 'reset' => "users#password_reset", :as => "api_password_reset"
end
end
end end
@@ -2,5 +2,6 @@ class AddEndsToUserRoles < ActiveRecord::Migration
def change def change
add_column(:user_roles, :ends, :timestamp) add_column(:user_roles, :ends, :timestamp)
add_column(:user_roles, :user_id, :integer) add_column(:user_roles, :user_id, :integer)
remove_column(:users, :role_id)
end end
end end
@@ -1,23 +0,0 @@
class AlterUserRoles < ActiveRecord::Migration
def up
rename_table :user_roles, :user_role_joins
change_table :user_role_joins do |t|
t.rename :role, :role_id
#t.change :role_id, :integer
end
#for Postgres
connection.execute(%q{
alter table user_role_joins
alter column role_id
type integer using cast(role_id as integer)
})
end
def down
rename_table :user_role_joins, :user_roles
change_table :user_role_joins do |t|
t.rename :role_id, :role
t.change :role_id, :string
end
end
end
-8
View File
@@ -1,8 +0,0 @@
class AddRoles < ActiveRecord::Migration
def change
create_table(:roles) do |t|
t.string :role
t.timestamps
end
end
end
-5
View File
@@ -1,5 +0,0 @@
class AlterUser < ActiveRecord::Migration
def change
remove_column :users, :user_role_id
end
end
@@ -1,6 +0,0 @@
class AddUsernameToUser < ActiveRecord::Migration
def change
add_column :users, :username, :string
add_index :users, :username, :unique => true
end
end
@@ -1,5 +0,0 @@
class RemoveNickname < ActiveRecord::Migration
def change
remove_column :users, :nickname
end
end
@@ -1,8 +0,0 @@
class CreateCreditConversions < ActiveRecord::Migration
def change
create_table(:credit_conversions) do |t|
t.integer :conversion, :default => 1
t.timestamps
end
end
end
@@ -1,15 +0,0 @@
class BikeConditionIdToInt < ActiveRecord::Migration
def up
#for Postgres
connection.execute(%q{
alter table bikes
alter column bike_condition_id
type integer using cast(bike_condition_id as integer)
})
end
def down
#you really don't want to go back
change_column :bikes, :bike_condition_id, :string
end
end
@@ -1,7 +0,0 @@
class BikeStatusToPurpose < ActiveRecord::Migration
def change
rename_column :bikes, :bike_status_id, :bike_purpose_id
rename_column :bike_statuses, :status, :purpose
rename_table :bike_statuses, :bike_purposes
end
end
@@ -1,9 +0,0 @@
class AddModelToBike < ActiveRecord::Migration
def change
add_column :bikes, :model, :string
change_column :bikes, :bike_model_id, :integer, :null => true
Bike.all.each do |bike|
bike.update_attribute(:model, BikeModel.find_by_id(bike['bike_model_id']).model )
end
end
end
@@ -1,22 +0,0 @@
class ChangeBikeShopId < ActiveRecord::Migration
# We don't use change_column because we have arbitrary strings we
# need to strip the characters from to make the integer values
def up
bikes = Bike.all
remove_column :bikes, :shop_id
add_column :bikes, :shop_id, :integer, :unique => true
# deconflict shop ids
bikes.each do |bike|
new_id = bike.shop_id.gsub(/[^\d]/, '').to_i rescue 1
while bikes.map(&:shop_id).include? new_id
new_id += 1
end
bike.update_attribute(:shop_id, new_id)
end
end
def down
change_column :bikes, :shop_id, :string
end
end
@@ -1,22 +0,0 @@
class CreateBikeWheelSizes < ActiveRecord::Migration
def up
create_table(:bike_wheel_sizes) do |t|
t.integer :twmm
t.integer :rdmm
t.string :twin
t.string :rdin
t.string :twfr
t.string :rdfr
t.string :description
t.string :tire_common_score
end
#create the default undetermined wheel size record
BikeWheelSize.create( twmm: 0, rdmm: 0, rdin: 0, twin: 0, rdfr: 0, twfr: 0, description: "UNDETERMINED", tire_common_score: 0)
ActiveRecord::Base.connection.execute(IO.read(File.join(Rails.root, "db", "seed", "sql", "common_wheel_sizes.sql")))
end
def down
drop_table :bike_wheel_sizes
end
end
@@ -1,32 +0,0 @@
class AlterBikeWheelToId < ActiveRecord::Migration
def up
add_column :bikes, :bike_wheel_size_id, :integer
undetermined_id = BikeWheelSize.find_by_rdin("0")
Bike.find_each do |bike|
wheel_size = BikeWheelSize.find_by_rdin(bike['wheel_size'].to_s)
if wheel_size.nil?
wheel_size_id = undetermined_id
else
wheel_size_id = wheel_size.id
end
bike.update_attribute(:bike_wheel_size_id, wheel_size_id)
end
remove_column :bikes, :wheel_size
end
def down
add_column :bikes, :wheel_size, :integer
Bike.find_each do |bike|
wheel_size = BikeWheelSize.find_by_id(bike['bike_wheel_size_id'])
bike.update_attribute(:wheel_size, wheel_size["rdin"].to_i)
end
remove_column :bikes, :bike_wheel_size_id
end
end
+17 -39
View File
@@ -11,7 +11,7 @@
# #
# It's strongly recommended to check this file into your version control system. # It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20131019170248) do ActiveRecord::Schema.define(:version => 20130419010051) do
create_table "bike_actions", :force => true do |t| create_table "bike_actions", :force => true do |t|
t.string "action", :limit => 128, :null => false t.string "action", :limit => 128, :null => false
@@ -34,8 +34,8 @@ ActiveRecord::Schema.define(:version => 20131019170248) do
t.integer "bike_brand_id", :null => false t.integer "bike_brand_id", :null => false
end end
create_table "bike_purposes", :force => true do |t| create_table "bike_statuses", :force => true do |t|
t.string "purpose", :null => false t.string "status", :null => false
t.datetime "created_at", :null => false t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false
end end
@@ -46,40 +46,24 @@ ActiveRecord::Schema.define(:version => 20131019170248) do
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false
end end
create_table "bike_wheel_sizes", :force => true do |t|
t.integer "twmm"
t.integer "rdmm"
t.string "twin"
t.string "rdin"
t.string "twfr"
t.string "rdfr"
t.string "description"
t.string "tire_common_score"
end
create_table "bikes", :force => true do |t| create_table "bikes", :force => true do |t|
t.string "serial_number" t.string "serial_number"
t.integer "bike_brand_id", :null => false t.integer "bike_brand_id", :null => false
t.integer "bike_model_id" t.integer "bike_model_id", :null => false
t.string "color" t.string "color"
t.integer "bike_style_id", :null => false t.integer "bike_style_id", :null => false
t.float "seat_tube_height" t.float "seat_tube_height"
t.float "top_tube_length" t.float "top_tube_length"
t.integer "wheel_size"
t.float "value" t.float "value"
t.integer "bike_condition_id", :null => false t.string "bike_condition_id", :null => false
t.integer "bike_purpose_id", :null => false t.integer "bike_status_id", :null => false
t.datetime "created_at", :null => false t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false
t.string "model" t.string "shop_id"
t.integer "shop_id"
t.integer "bike_wheel_size_id"
end end
create_table "credit_conversions", :force => true do |t| add_index "bikes", ["shop_id"], :name => "index_bikes_on_shop_id", :unique => true
t.integer "conversion", :default => 1
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "customers", :force => true do |t| create_table "customers", :force => true do |t|
t.string "first_name", :null => false t.string "first_name", :null => false
@@ -113,12 +97,6 @@ ActiveRecord::Schema.define(:version => 20131019170248) do
add_index "logs", ["loggable_id", "loggable_type", "context"], :name => "index_logs_on_loggable_id_and_loggable_type_and_context" add_index "logs", ["loggable_id", "loggable_type", "context"], :name => "index_logs_on_loggable_id_and_loggable_type_and_context"
create_table "roles", :force => true do |t|
t.string "role"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "task_lists", :force => true do |t| create_table "task_lists", :force => true do |t|
t.integer "item_id", :null => false t.integer "item_id", :null => false
t.string "item_type", :null => false t.string "item_type", :null => false
@@ -170,8 +148,8 @@ ActiveRecord::Schema.define(:version => 20131019170248) do
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false
end end
create_table "user_role_joins", :force => true do |t| create_table "user_roles", :force => true do |t|
t.integer "role_id" t.string "role"
t.datetime "created_at", :null => false t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false
t.datetime "ends" t.datetime "ends"
@@ -189,6 +167,7 @@ ActiveRecord::Schema.define(:version => 20131019170248) do
t.datetime "last_sign_in_at" t.datetime "last_sign_in_at"
t.string "current_sign_in_ip" t.string "current_sign_in_ip"
t.string "last_sign_in_ip" t.string "last_sign_in_ip"
t.integer "user_role_id", :default => 1, :null => false
t.integer "bike_id" t.integer "bike_id"
t.datetime "created_at", :null => false t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false
@@ -197,12 +176,11 @@ ActiveRecord::Schema.define(:version => 20131019170248) do
t.datetime "locked_at" t.datetime "locked_at"
t.string "first_name", :default => "", :null => false t.string "first_name", :default => "", :null => false
t.string "last_name", :default => "", :null => false t.string "last_name", :default => "", :null => false
t.string "username" t.string "nickname"
end end
add_index "users", ["bike_id"], :name => "index_users_on_bike_id", :unique => true add_index "users", ["bike_id"], :name => "index_users_on_bike_id", :unique => true
add_index "users", ["email"], :name => "index_users_on_email", :unique => true add_index "users", ["email"], :name => "index_users_on_email", :unique => true
add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true add_index "users", ["reset_password_token"], :name => "index_users_on_reset_password_token", :unique => true
add_index "users", ["username"], :name => "index_users_on_username", :unique => true
end end
@@ -13,8 +13,3 @@ staff:
action: STAFF action: STAFF
created_at: <%= Time.now %> created_at: <%= Time.now %>
updated_at: <%= Time.now %> updated_at: <%= Time.now %>
checkin:
id: 4
action: CHECKIN
created_at: <%= Time.now %>
updated_at: <%= Time.now %>
-18
View File
@@ -1,18 +0,0 @@
undetermined:
id: 1
purpose: UNDETERMINED
scrap:
id: 2
purpose: SCRAP
parts:
id: 3
purpose: PARTS
storage:
id: 4
purpose: STORAGE
buildbike:
id: 5
purpose: BUILDBIKE
sale:
id: 6
purpose: SALE
+18
View File
@@ -0,0 +1,18 @@
undetermined:
id: 1
status: UNDETERMINED
scrap:
id: 2
status: SCRAP
parts:
id: 3
status: PARTS
storage:
id: 4
status: STORAGE
buildbike:
id: 5
status: BUILDBIKE
sale:
id: 6
status: SALE
-3
View File
@@ -1,3 +0,0 @@
conversion:
id: 1
conversion: 10
-12
View File
@@ -1,12 +0,0 @@
user:
id: 1
role: user
bike_admin:
id: 2
role: bike_admin
staff:
id: 3
role: staff
admin:
id: 4
role: admin
-37
View File
@@ -1,37 +0,0 @@
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (54,110,'8 1/2','2','','','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (62,203,'12','1/2 x 2 1/4','320 ','57','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (47,305,'16','1,75 [x 2]','','','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (32,369,'16','1 1/4','','','Moulton',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (47,355,'18','1,75 [ x 2]','','','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (28,406,'20','1 1/8','','','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (37,406,'20','1 3/8','','','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (47,406,'20','1,75 [ x 2]','','','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (47,507,'24','1,75 [ x 2]','600 ','50C','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (37,540,'24','1 3/8 A','600 ','35A','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (25,559,'26','1','','','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (32,559,'26','1,3','','','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (37,559,'26','1,5','','','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (40,559,'26','1,6','','','MTB',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (44,559,'26','1,6','','','City/trekking',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (47,559,'26','1,75 [ x 2]','650 ','50','City/Trekking',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (NULL,NULL,'26','1,9','','','MTB',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (50,'559','26','1,9','','','City/Trekking',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (NULL,NULL,'26','2','','','MTB',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (54,559,'26','2,1','','','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (57,559,'26','2,3 [2,125]','','','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (20,571,'','','650 ','20C','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (23,571,'','','650 ','23C','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (28,584,'26','1 1/8 x 1 1/2 B','','','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (37,584,'26','1 3/8 x 1 1/2 B','650 ','38A','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (37,590,'26','1 3/8','650 [','35] A','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (20,622,'','','700 ','20C','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (23,622,'','','700 ','23C','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (25,622,'','','700 ','25C','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (28,622,'28','1 5/8 x 1 3/8','700 ','28C [700C]','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (32,622,'28','1 1/4 x 1 3/4','700 ','32C [700C Course]','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (37,622,'28','1 3/8 x 1 5/8','700 ','37C [35C]','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (42,622,'28','1,6','700 ','42C [40C]','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (47,622,'28','1,75','700 ','47C [45C]','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (28,630,'27','1 1/8 [1 1/4 fifty]','','','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (32,630,'27','1 1/4','','','',1);
INSERT INTO bike_wheel_sizes (TWMM, RDMM,RDIN,TWIN,RDFR,TWFR,DESCRIPTION,tire_common_score) values (40,635,'28','1 1/2 [1 3/8]','700 ','38B [35B]','',1);
+9 -30
View File
@@ -14,28 +14,19 @@ end
#Load bike brands and models from sql #Load bike brands and models from sql
if BikeBrand.all.empty? and BikeModel.all.empty? if BikeBrand.all.empty? and BikeModel.all.empty?
# Need to use DEFAULT instead of explicit IDs that are used in the sql file, sql_path = File.join(Rails.root, 'db', 'seed', 'sql', 'bike_brands_and_models.sql')
# so that the PG table ID sequence is incremented system "sqlite3 db/development.sqlite3 < #{sql_path}" if Rails.env.development?
#
# Note the drop(1) which assumes we have a junk PRAGMA line at the top
load_statements = File.readlines(File.join(Rails.root, 'db', 'seed', 'sql', 'bike_brands_and_models.sql')).drop(1).map do |statement|
statement.sub(/VALUES\(\d+,/, 'VALUES(DEFAULT,')
end
ActiveRecord::Base.connection.execute(load_statements.join)
end end
if Rails.env.development? if Rails.env.development?
#create default users #create default admin user
if User.all.empty? if UserRole.all.empty? and User.all.empty?
u = FactoryGirl.create(:user) FactoryGirl.create(:user)
FactoryGirl.create(:user_profile, user_id: u.id) FactoryGirl.create(:staff)
u = FactoryGirl.create(:staff) FactoryGirl.create(:admin)
FactoryGirl.create(:user_profile, user_id: u.id) FactoryGirl.create(:bike_admin)
u = FactoryGirl.create(:bike_admin) FactoryGirl.create(:user_profile)
FactoryGirl.create(:user_profile, user_id: u.id)
u = FactoryGirl.create(:admin)
FactoryGirl.create(:user_profile, user_id: u.id)
end end
#create fake bikes #create fake bikes
@@ -44,16 +35,4 @@ if Rails.env.development?
FactoryGirl.create(:bike) FactoryGirl.create(:bike)
end end
end end
elsif Rails.env.production?
unless User.find_by_username('admin')
#create an admin
admin = User.create!( :username => 'admin',
:first_name => 'admin',
:last_name => 'admin',
:email=>'admin@example.com',
:password=>'password')
admin.roles << Role.find_by_role('admin')
end
end end
+15 -13
View File
@@ -2,19 +2,21 @@
FactoryGirl.define do FactoryGirl.define do
factory :bike do factory :bike do
sequence(:shop_id) {|n| n} sequence :shop_id do |n|
sequence :serial_number do |n| "Shop ID #{n}"
"#{Faker::Code.isbn}-#{n}"
end end
bike_brand_id { BikeBrand.all.sample.id } sequence :serial_number do |n|
model { Faker::Commerce.product_name } "S/N# #{n}"
color { sprintf("%06X", Random.rand(16777215) ) } end
bike_style_id { BikeStyle.all.sample.id } bike_brand_id 1
seat_tube_height { Random.rand(25) } bike_model_id 1
top_tube_length { Random.rand(25) } color "FFFFFF"
bike_wheel_size_id { BikeWheelSize.all.sample.id } bike_style_id 1
value { Random.rand(200) } seat_tube_height 5
bike_condition_id { BikeCondition.all.sample.id } top_tube_length 6
bike_purpose_id { BikePurpose.all.sample.id } wheel_size 27
value 100
bike_condition_id 1
bike_status_id 1
end end
end end
+6 -6
View File
@@ -1,11 +1,11 @@
FactoryGirl.define do FactoryGirl.define do
factory :user_profile do factory :user_profile do
user_id 1 user_id 1
addrStreet1 { Faker::Address.street_address } addrStreet1 "Charles Street"
addrStreet2 { Faker::Address.secondary_address } addrStreet2 "Apt #42"
addrCity { Faker::Address.city } addrCity "Baltimore"
addrState { Faker::Address.state_abbr } addrState "MD"
addrZip { Faker::Address.zip_code } addrZip "21231"
phone { Faker::PhoneNumber.cell_phone } phone "(410)8675309"
end end
end end
@@ -1,5 +1,5 @@
FactoryGirl.define do FactoryGirl.define do
factory :role do factory :user_role do
factory :role_staff do factory :role_staff do
role 'staff' role 'staff'
end end
+4 -17
View File
@@ -1,39 +1,26 @@
FactoryGirl.define do FactoryGirl.define do
factory :user do factory :user do
sequence(:username) { |n| "user_#{n}" }
sequence(:email) { |n| "user_#{n}@example.com" } sequence(:email) { |n| "user_#{n}@example.com" }
password 'password' password 'password'
password_confirmation { password } password_confirmation { password }
first_name 'Michael' first_name 'Michael'
last_name 'Scott' last_name 'Scott'
sequence(:bike_id) { |n| n } sequence(:bike_id) { |n| n }
after_build do |r| association :user_role, factory: :role_user
r.roles << (Role.find_by_role("user") || FactoryGirl.create(:role_user))
end
factory :staff do factory :staff do
username "staff"
first_name 'Staff' first_name 'Staff'
after_build do |r| association :user_role, factory: :role_staff
r.roles << (Role.find_by_role("staff") || FactoryGirl.create(:role_staff))
end
end end
factory :admin do factory :admin do
username "admin"
first_name 'Admin' first_name 'Admin'
after_build do |r| association :user_role, factory: :role_admin
r.roles << (Role.find_by_role("admin") || FactoryGirl.create(:role_admin))
end
end end
factory :bike_admin do factory :bike_admin do
username "bike_admin"
first_name 'BikeAdmin' first_name 'BikeAdmin'
after_build do |r| association :user_role, factory: :role_bike_admin
r.roles << (Role.find_by_role("bike_admin") || FactoryGirl.create(:role_bike_admin))
end
end end
end end