51 changed files with 273 additions and 632 deletions
-1
View File
@@ -33,7 +33,6 @@ group :development, :test do
gem 'rspec-rails', '~> 2.8.1'
gem 'factory_girl_rails', '~> 1.2'
gem 'pry', '~> 0.9.8'
gem 'faker'
end
group :test do
+3 -6
View File
@@ -1,6 +1,6 @@
GIT
remote: https://github.com/spacemunkay/acts_as_loggable.git
revision: 8484dc1f7a58699705c5ffb593ebdf700a9f374a
revision: 49e264849ed9018445fb999cacfc55e1e28d7faa
specs:
acts_as_loggable (0.0.7)
activerecord (>= 3.0)
@@ -77,8 +77,6 @@ GEM
factory_girl_rails (1.7.0)
factory_girl (~> 2.6.0)
railties (>= 3.0.0)
faker (1.2.0)
i18n (~> 0.5)
ffi (1.6.0)
formatador (0.2.4)
gherkin (2.11.6)
@@ -124,7 +122,7 @@ GEM
treetop (~> 1.4.8)
method_source (0.8.1)
mime-types (1.22)
multi_json (1.8.2)
multi_json (1.7.9)
netzke-basepack (0.8.4)
netzke-core (~> 0.8.2)
netzke-cancan (0.8.2)
@@ -209,7 +207,7 @@ GEM
less-rails (~> 2.2.2)
railties (>= 3.1)
therubyracer (~> 0.10.1)
tzinfo (0.3.38)
tzinfo (0.3.37)
uglifier (2.1.2)
execjs (>= 0.3.0)
multi_json (~> 1.0, >= 1.0.2)
@@ -233,7 +231,6 @@ DEPENDENCIES
decent_exposure (~> 1.0.1)
devise (~> 2.0.4)
factory_girl_rails (~> 1.2)
faker
guard-rspec
guard-spork
haml-rails (~> 0.3.4)
+2 -24
View File
@@ -1,25 +1,3 @@
# 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
1. use rvm
@@ -27,8 +5,6 @@ At Velocipede, with a mobile friendly UI for users, I'm hoping we can get donati
1. `gem install bundler`
1. `bundle`
1. Install Postgres (Mac OSX instructions below)
1. Copy over DB config: `cp config/database.yml.example config/database.yml`
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.
@@ -43,6 +19,8 @@ At Velocipede, with a mobile friendly UI for users, I'm hoping we can get donati
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`
1. Copy over DB config: `cp config/database.yml.example config/database.yml`
1. Update config with your database (velocipede), user (velocipede), and password.
# Optional
Add icons
@@ -3,232 +3,6 @@ Ext.Ajax.on('requestexception', function(conn, response, options) {
if (response.status === 401) { window.location = '/users/sign_in'; }
}, 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:{
+5 -1
View File
@@ -19,7 +19,11 @@ class AppTabPanel < Netzke::Basepack::TabPanel
#all users
# (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
if not controller.current_user.role?(:admin)
+1 -2
View File
@@ -3,11 +3,10 @@ class BikeBrands < Netzke::Basepack::Grid
super
c.model = "BikeBrand"
c.title = "Brands"
c.force_fit = true
c.prohibit_update = true if cannot? :update, BikeBrand
c.prohibit_create = true if cannot? :create, BikeBrand
c.prohibit_delete = true if cannot? :delete, BikeBrand
c.prohibit_delete = true if cannot? :delete, BikeBrand
end
#override with nil to remove actions
+3 -4
View File
@@ -6,7 +6,6 @@ class BikeLogs < Netzke::Basepack::Grid
c.model = "ActsAsLoggable::Log"
c.title = "Bike History"
c.force_fit = true
c.data_store = {auto_load: false}
c.scope = lambda { |rel| rel.where(:loggable_type => 'Bike',:loggable_id => session[:selected_bike_id]);}
c.strong_default_attrs = {
@@ -19,9 +18,9 @@ class BikeLogs < Netzke::Basepack::Grid
c.columns = [
{ :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) },
:description,
{ :name => :bike_action__action, :text => 'Action', :default_value => ::ActsAsLoggable::BikeAction.first.id},
{ :name => :bike_action__action, :text => 'Action'},
{ :name => :logged_by, :getter => lambda{ |rec|
user = User.find_by_id(rec.logger_id)
user.nil? ? "" : "#{user.first_name} #{user.last_name}"
@@ -48,7 +47,7 @@ class BikeLogs < Netzke::Basepack::Grid
{ :name => :start_date},
{ :name => :description},
#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
@@ -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.title = "Models"
c.force_fit = true
c.data_store = {auto_load: false}
c.scope = lambda { |rel| rel.where(:bike_brand_id => session[:selected_bike_brand_id]);}
c.strong_default_attrs = {
+11 -17
View File
@@ -2,12 +2,11 @@ class Bikes < Netzke::Basepack::Grid
def configure(c)
super
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 = [
{ :name => :shop_id, :text => 'Shop ID', :default_value => Bike.last.id.to_i + 1},
{ :name => :shop_id, :text => 'Shop ID'},
:serial_number,
{ :id => :bike_brand__brand, :name => :bike_brand__brand, :text => 'Brand'},
{ :name => :model, :text => 'Model',
@@ -20,25 +19,20 @@ class Bikes < Netzke::Basepack::Grid
}
},
#needs to have type :action or else won't work in grid, because... netzke
{ :name => "color", :text => "Frame Color", :type => :action, :editor => { :xtype => "xcolorcombo"}, :renderer => :color_block,
:default_value => '000000'},
{ :id => :bike_style__style, :name => :bike_style__style, :text => 'Style', :default_value => BikeStyle.first.id},
{ :name => "color", :text => "Frame Color", :type => :action, :editor => { :xtype => "xcolorcombo"}, :renderer => :color_block},
{ :id => :bike_style__style, :name => :bike_style__style, :text => 'Style' },
{ :name => :seat_tube_height, :text => 'Seat Tube (in)'},
{ :name => :top_tube_length, :text => 'Top Tube (in)'},
{ :name => :bike_wheel_size__display_string, :text => 'Wheel Size'},
{ :name => :wheel_size, :text => 'Wheel Size (in)'},
:value,
{ :id => :bike_condition__condition, :name => :bike_condition__condition, :text => 'Condition', :default_value => BikeCondition.first.id},
{ :id => :bike_purpose__purpose, :name => :bike_purpose__purpose, :text => 'Purpose', :default_value => BikePurpose.first.id},
{ :id => :bike_condition__condition, :name => :bike_condition__condition, :text => 'Condition'},
{ :id => :bike_purpose__purpose, :name => :bike_purpose__purpose, :text => 'Purpose'},
{ :name => :owner, :getter => lambda { |rec|
user = rec.owner
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
@@ -47,18 +41,18 @@ class Bikes < Netzke::Basepack::Grid
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 => :bike_brand__brand, :field_label => 'Brand' },
{ :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 => :bike_style__style, :field_label => 'Style' },
{ :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 => :wheel_size, :field_label => 'Wheel Size (in)'},
{ :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}
{ :name => :bike_condition__condition, :field_label => 'Condition'},
{ :name => :bike_purpose__purpose, :field_label => 'Purpose'}
]
end
#override with nil to remove actions
@@ -3,6 +3,7 @@
// calling superclass's initComponent
this.callParent();
// something...
//due to Netzke bug, :min_chars attribute doesn't work
var min_char_columns = [
"bike_brand__brand",
+2 -2
View File
@@ -2,7 +2,7 @@ class BikesBorder < Netzke::Base
# Remember regions collapse state and size
include Netzke::Basepack::ItemPersistence
component :bikes
component :bike_lower_tabs
component :bike_logs_and_tasks_accordian
def configure(c)
super
@@ -10,7 +10,7 @@ class BikesBorder < Netzke::Base
c.title = "Bikes"
c.items = [
{ 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
@@ -5,30 +5,14 @@
// setting the 'rowclick' event
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){
// 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')});
// query for these components again, can change if not visible because of accordian
var bike_logs_comp = this.queryById('bike_logs');
var bike_tasks_comp = this.queryById('tasks');
if( bike_logs_comp ){
bike_logs_comp.getStore().load();
bike_logs_comp.enable();
if( this.queryById('bike_logs') ){
this.queryById('bike_logs').getStore().load();
}
if( bike_tasks_comp ){
bike_tasks_comp.getStore().load();
bike_tasks_comp.enable();
if( this.queryById('tasks') ){
this.queryById('tasks').getStore().load();
}
}, this);
}
-1
View File
@@ -4,7 +4,6 @@ class CheckIns < Netzke::Basepack::Grid
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")).
-1
View File
@@ -1,7 +1,6 @@
class Customers < Netzke::Basepack::Grid
def configure(c)
c.model = "Customer"
c.force_fit = true
end
#override with nil to remove actions
@@ -4,6 +4,7 @@
this.callParent();
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!
console.log("user: " + record.get('id') );
this.selectCustomer({customer_id: record.get('id'), customer_type: 'Customer'});
}, this);
}
-1
View File
@@ -4,7 +4,6 @@ class Logs < Netzke::Basepack::Grid
super
c.header = false
c.model = "ActsAsLoggable::Log"
c.force_fit = true
c.columns = [
:loggable_id,
:loggable_type,
+4 -7
View File
@@ -2,8 +2,6 @@ class Tasks < Netzke::Basepack::Grid
def configure(c)
super
#disable by default, will be enabled once bike is clicked
c.disabled = true
task_list_id = nil
if session[:selected_bike_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.model = "Task"
c.force_fit = true
c.scope = lambda{ |rel|
if session[:selected_bike_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
}
c.columns = [
:done,
:task,
:notes
:notes,
:done
]
end
@@ -34,9 +31,9 @@ class Tasks < Netzke::Basepack::Grid
fields = []
fields << { :no_binding => true, :xtype => 'displayfield', :fieldLabel => "No Bike Selected", :value => "Select a Bike First!"}
fields.concat( [
:done,
:task,
:notes
:notes,
:done
])
end
+2 -3
View File
@@ -5,7 +5,6 @@ class TransactionLogs < Netzke::Basepack::Grid
c.model = "ActsAsLoggable::Log"
c.title = "Transaction Payments"
c.force_fit = true
c.data_store = {auto_load: false}
c.scope = lambda { |rel| rel.where(:loggable_type => 'Transaction',:loggable_id => session[:selected_transaction_id]);}
c.strong_default_attrs = {
@@ -21,7 +20,7 @@ class TransactionLogs < Netzke::Basepack::Grid
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 => :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|
user = User.find_by_id(rec.logger_id)
user.nil? ? "" : "#{user.first_name} #{user.last_name}"
@@ -50,7 +49,7 @@ class TransactionLogs < Netzke::Basepack::Grid
{ :no_binding => true, :xtype => 'displayfield', :fieldLabel => "Payment for:", :value => "#{item.to_s}"},
{ :name => :description, :xtype => 'numberfield', :field_label => 'Amount'},
#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
-1
View File
@@ -2,7 +2,6 @@ class Transactions < Netzke::Basepack::Grid
def configure(c)
super
c.model = "Transaction"
c.force_fit = true
c.strong_default_attrs = {
:vendor_id => controller.current_user.id,
:customer_id => session[:selected_customer_id],
+2 -2
View File
@@ -4,7 +4,7 @@ class TransactionsBorder < Netzke::Base
component :transactions
component :transaction_logs
#users and customers components are required for the transactions form
component :users_and_customers_lower_tabs
component :users_and_customers_accordian
def configure(c)
super
@@ -13,7 +13,7 @@ class TransactionsBorder < Netzke::Base
c.items = [
{ netzke_component: :transactions, region: :center, height: 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
@@ -5,19 +5,10 @@
// setting the 'rowclick' event
var view = this.getComponent('transactions').getView();
if( this.queryById('transaction_logs')){
this.queryById('transaction_logs').disable();
}
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!
this.selectTransaction({transaction_id: record.get('id')});
this.getComponent('transaction_logs').getStore().load();
if( this.queryById('transaction_logs')){
this.queryById('transaction_logs').enable();
}
}, 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
+9 -20
View File
@@ -1,5 +1,4 @@
class UserLogs < Netzke::Basepack::Grid
def configure(c)
super
@@ -34,7 +33,6 @@ class UserLogs < Netzke::Basepack::Grid
c.model = "ActsAsLoggable::Log"
c.title = "User Timesheet"
c.force_fit = true
c.data_store = user_log_data_store
c.scope = user_log_scope
c.strong_default_attrs = user_log_strong_default_attrs
@@ -53,23 +51,10 @@ class UserLogs < Netzke::Basepack::Grid
]
end
def default_fields_for_forms
#figure out a better way to do this
bike_store = Bike.all.map { |b| [b.id, b.shop_id] }
current_user ||= User.find_by_id(session[:selected_user_id]) || controller.current_user
bike_id = current_user.bike.nil? ? nil : current_user.bike.id
[
{ :no_binding => true, :xtype => 'displayfield', :fieldLabel => "Log for:", :value => "#{current_user.to_s}"},
{ :name => :start_date},
{ :name => :end_date},
{ :name => :description},
#had to hack acts_as_loggable/log.rb to get this to work
{ :name => :user_action__action, :field_label => 'Action', :min_chars => 1},
{ :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}
]
}
]
# Overriding the defautl add in form
def preconfigure_record_window(c)
super
c.form_config.klass = UserLogsAddItem
end
#override with nil to remove actions
@@ -77,4 +62,8 @@ class UserLogs < Netzke::Basepack::Grid
[ :apply, :add_in_form, :search ]
end
end
js_configure do |c|
c.mixin :init_component
end
end
@@ -2,12 +2,48 @@
initComponent: function(){
// calling superclass's initComponent
this.callParent();
// TESTTING
//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;
});
"user_action__purpose"
];
// Ext.each(min_char_columns, function(column, index) {
// Ext.ComponentManager.get(column).editor.minChars = 1;
// });
var view = this.getView();
view.on('itemclick', function(view, record){
console.log('clicking ');
// The beauty of using Ext.Direct: calling 3 endpoints in a row, which results in a single call to the server!
// console.log('Old record', record);
}, this);
view.on('itemupdate', function(record, index, node){
console.log('Updating item');
// if('end_date' in record.data.modified){
// console.log('start', record.data.start_date);
// console.log('end', record.data.end_date);
// }else if('start_date' in record.data.modified){
// }
}, this);
// var hoursInput = Ext.getCmp('user_logs_add_form_hours-inputEl');
// console.log(hoursInput);
// hoursInput.on('change', function(field, newValue, oldValue){
// console.log('Hours changing...', newValue);
// });
},
addNewItem: function(e){
e.fireEvent('click', {});
console.log(e);
console.log('Adding new item');
}
}
+31
View File
@@ -0,0 +1,31 @@
# The User Log Add Item Form
class UserLogsAddItem < Netzke::Basepack::Form
def configure(c)
super
c.model = 'ActsAsLoggable::Log'
#figure out a better way to do this
bike_store = Bike.all.map { |b| [b.id, b.shop_id] }
current_user ||= User.find_by_id(session[:selected_user_id]) || controller.current_user
bike_id = current_user.bike.nil? ? nil : current_user.bike.id
c.items = [
{ :no_binding => true, :xtype => 'displayfield', :fieldLabel => "Log for:", :value => "#{current_user.to_s}"},
{ :id => :user_logs_add_form_start, :name => :start_date},
{ :id => :user_logs_add_form_time, :no_binding => true, :name => :time, :xtype => 'field', :fieldLabel => "Time:", :value => 0 },
{ :id => :user_logs_add_form_units, :xtype => 'combo', :no_binding => true, :name => :units, :fieldLabel => 'Units', :store => ['Mins', 'Hrs'], :value => 'Mins' },
{ :id => :user_logs_add_form_end, :name => :end_date, :hidden => true },
{ :name => :description},
#had to hack acts_as_loggable/log.rb to get this to work
{ :name => :user_action__action, :field_label => 'Action'},
{ :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}
]
}
]
end
js_configure do |c|
c.mixin :init_component
end
end
@@ -0,0 +1,76 @@
{
initComponent: function(){
// calling superclass's initComponent
this.callParent();
var panel = this;
var theForm = this.getForm();
var startInput = Ext.getCmp('user_logs_add_form_start');
var timeInput = Ext.getCmp('user_logs_add_form_time');
var unitsInput = Ext.getCmp('user_logs_add_form_units');
var endInput = Ext.getCmp('user_logs_add_form_end');
var startdate = startInput.items.items[0];
var starttime = startInput.items.items[1];
var enddate = endInput.items.items[0];
var endtime = endInput.items.items[1];
startdate.on('change', function(e){
var hours = timeInput.getValue();
var startTime = starttime.getValue();
if('Mins' === unitsInput.getValue()){
var calculatedEndTime = Ext.Date.add(new Date(startTime), Ext.Date.MINUTE, parseInt(hours));
}else{
var calculatedEndTime = Ext.Date.add(new Date(startTime), Ext.Date.HOUR, parseInt(hours));
}
endtime.setValue(calculatedEndTime);
enddate.setValue(calculatedEndTime);
});
starttime.on('change', function(e){
var hours = timeInput.getValue();
var startTime = starttime.getValue();
if('Mins' === unitsInput.getValue()){
var calculatedEndTime = Ext.Date.add(new Date(startTime), Ext.Date.MINUTE, parseInt(hours));
}else{
var calculatedEndTime = Ext.Date.add(new Date(startTime), Ext.Date.HOUR, parseInt(hours));
}
endtime.setValue(calculatedEndTime);
enddate.setValue(calculatedEndTime);
});
timeInput.on('change', function(e){
var hours = timeInput.getValue();
var startTime = starttime.getValue();
if('Mins' === unitsInput.getValue()){
var calculatedEndTime = Ext.Date.add(new Date(startTime), Ext.Date.MINUTE, parseInt(hours));
}else{
var calculatedEndTime = Ext.Date.add(new Date(startTime), Ext.Date.HOUR, parseInt(hours));
}
endtime.setValue(calculatedEndTime);
enddate.setValue(calculatedEndTime);
});
unitsInput.on('change', function(e){
var hours = timeInput.getValue();
var startTime = starttime.getValue();
if('Mins' === unitsInput.getValue()){
var calculatedEndTime = Ext.Date.add(new Date(startTime), Ext.Date.MINUTE, parseInt(hours));
}else{
var calculatedEndTime = Ext.Date.add(new Date(startTime), Ext.Date.HOUR, parseInt(hours));
}
endtime.setValue(calculatedEndTime);
enddate.setValue(calculatedEndTime);
});
}
}
-1
View File
@@ -19,7 +19,6 @@ class UserProfiles < Netzke::Basepack::Grid
c.model = "UserProfile"
c.title = "Profile"
c.force_fit = true
c.data_store = user_profiles_data_store
c.scope = user_profiles_scope
c.strong_default_attrs = user_profile_strong_default_attrs
+2 -3
View File
@@ -2,7 +2,6 @@ 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}]
@@ -11,9 +10,9 @@ class UserRoleJoins < Netzke::Basepack::Grid
{ :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},
{ :name => :role__role, :text => "Role"},
:created_at,
:updated_at,
:ends ]
+1 -1
View File
@@ -40,7 +40,7 @@ class UserStats < Netzke::Base
private
def user
User.find_by_id(session[:selected_user_id]) || controller.current_user
controller.current_user
end
end
-1
View File
@@ -5,7 +5,6 @@ class UserTransactions < Netzke::Basepack::Grid
c.model = "Transaction"
c.title = "Transactions"
c.force_fit = true
c.scope = lambda { |rel| rel.where(:customer_id => controller.current_user.id, :customer_type => 'User');}
c.data_store = { auto_load: true }
c.columns = [
@@ -5,18 +5,10 @@
// setting the 'rowclick' event
var view = this.getComponent('user_transactions').getView();
if( this.queryById('transaction_logs')){
this.queryById('transaction_logs').disable();
}
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!
this.selectTransaction({transaction_id: record.get('id')});
this.getComponent('transaction_logs').getStore().load();
if( this.queryById('transaction_logs')){
this.queryById('transaction_logs').enable();
}
}, this);
}
}
+1 -2
View File
@@ -12,14 +12,13 @@ class Users < Netzke::Basepack::Grid
super
c.header = false
c.model = "User"
c.force_fit = true
c.columns = [
{ :name => :username, :read_only => true },
:first_name,
:last_name,
:email,
{ :text => "Bike - Shop ID", :id => :bike__shop_id, :name => :bike__shop_id, :default_value => Bike.exists? ? Bike.first.id : nil }
{ :id => :bike__shop_id, :name => :bike__shop_id}
]
c.columns << :reset if can? :manage, User
@@ -1,10 +1,10 @@
class UsersAndCustomersLowerTabs < Netzke::Basepack::TabPanel
class UsersAndCustomersAccordian < Netzke::Basepack::Accordion
component :customers
component :users
def configure(c)
c.prevent_header = true
c.items = [ :users, :customers ]
c.items = [ :customers, :users ]
super
end
end
+4 -2
View File
@@ -2,14 +2,16 @@ class UsersAndProfilesBorder < Netzke::Base
# Remember regions collapse state and size
include Netzke::Basepack::ItemPersistence
component :users
component :user_and_profiles_lower_tabs
component :user_profiles
component :user_logs
def configure(c)
super
c.header = false
c.items = [
{ netzke_component: :users, header: "Users", region: :center, width: 350, 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
@@ -2,48 +2,18 @@
initComponent: function(){
// calling superclass's initComponent
this.callParent();
var stats = this.queryById('user_stats');
var stats = this.getComponent('user_stats');
if (stats != undefined){
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
var view = this.getComponent('users').getView();
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!
this.selectUser({user_id: record.get('id')});
if( this.queryById('user_profiles')){
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.getComponent('user_profiles').getStore().load();
this.getComponent('user_logs').getStore().load();
}, this);
}
}
+7 -8
View File
@@ -1,7 +1,7 @@
class Bike < ActiveRecord::Base
acts_as_loggable
attr_accessible :shop_id, :serial_number, :bike_brand_id, :model, :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_purpose_id
has_many :transactions
@@ -11,16 +11,19 @@ class Bike < ActiveRecord::Base
belongs_to :bike_style
belongs_to :bike_condition
belongs_to :bike_purpose
belongs_to :bike_wheel_size
validates :shop_id, :presence => true, :uniqueness => true, :numericality => { :only_integer => true }
default_scope order('shop_id ASC')
validates :shop_id, :presence => true, :uniqueness => true, :length => { :minimum => 3 }
validates :serial_number, :length => { :minimum => 3 }
validates :model, :length => { :maximum => 50 }
validates :bike_brand_id, :presence => true
validates :color, :presence => true
validates :bike_style_id, :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_purpose_id, :presence => true
@@ -44,10 +47,6 @@ class Bike < ActiveRecord::Base
self.bike_purpose
end
def wheel_size
self.bike_wheel_size
end
def to_s
"#{brand} - #{model} - #{style}"
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
+1 -2
View File
@@ -47,8 +47,7 @@ class User < ActiveRecord::Base
### TODO methods below probably belong somewhere else
def completed_build_bikes
#default BUILDBIKE/CLASS ID is 5
purpose_id = 5
purpose_id = BikePurpose.find_by_purpose("BUILDBIKE").id
Bike.find_by_sql("
SELECT *
FROM bikes
-3
View File
@@ -11,9 +11,6 @@
body {
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
.container
@@ -3,7 +3,8 @@ class AddModelToBike < ActiveRecord::Migration
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 )
bike.model = BikeModel.find_by_id(bike.bike_model_id).model
bike.save
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
+11 -20
View File
@@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20131019170248) do
ActiveRecord::Schema.define(:version => 20130902201853) do
create_table "bike_actions", :force => true do |t|
t.string "action", :limit => 128, :null => false
@@ -46,35 +46,26 @@ ActiveRecord::Schema.define(:version => 20131019170248) do
t.datetime "updated_at", :null => false
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|
t.string "serial_number"
t.integer "bike_brand_id", :null => false
t.integer "bike_brand_id", :null => false
t.integer "bike_model_id"
t.string "color"
t.integer "bike_style_id", :null => false
t.integer "bike_style_id", :null => false
t.float "seat_tube_height"
t.float "top_tube_length"
t.integer "wheel_size"
t.float "value"
t.integer "bike_condition_id", :null => false
t.integer "bike_purpose_id", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "bike_condition_id", :null => false
t.integer "bike_purpose_id", :null => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "shop_id"
t.string "model"
t.integer "shop_id"
t.integer "bike_wheel_size_id"
end
add_index "bikes", ["shop_id"], :name => "index_bikes_on_shop_id", :unique => true
create_table "credit_conversions", :force => true do |t|
t.integer "conversion", :default => 1
t.datetime "created_at", :null => false
-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);
+10 -17
View File
@@ -14,28 +14,21 @@ end
#Load bike brands and models from sql
if BikeBrand.all.empty? and BikeModel.all.empty?
# Need to use DEFAULT instead of explicit IDs that are used in the sql file,
# so that the PG table ID sequence is incremented
#
# 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)
sql_path = File.join(Rails.root, 'db', 'seed', 'sql', 'bike_brands_and_models.sql')
db_config = Rails.application.config.database_configuration[Rails.env]
cmd = "psql #{db_config["database"]} < #{sql_path}"
system cmd
end
if Rails.env.development?
#create default users
#create default admin user
if User.all.empty?
u = FactoryGirl.create(:user)
FactoryGirl.create(:user_profile, user_id: u.id)
u = FactoryGirl.create(:staff)
FactoryGirl.create(:user_profile, user_id: u.id)
u = FactoryGirl.create(:bike_admin)
FactoryGirl.create(:user_profile, user_id: u.id)
u = FactoryGirl.create(:admin)
FactoryGirl.create(:user_profile, user_id: u.id)
FactoryGirl.create(:user)
FactoryGirl.create(:staff)
FactoryGirl.create(:bike_admin)
FactoryGirl.create(:admin)
FactoryGirl.create(:user_profile)
end
#create fake bikes
+15 -13
View File
@@ -2,19 +2,21 @@
FactoryGirl.define do
factory :bike do
sequence(:shop_id) {|n| n}
sequence :serial_number do |n|
"#{Faker::Code.isbn}-#{n}"
sequence :shop_id do |n|
"Shop ID #{n}"
end
bike_brand_id { BikeBrand.all.sample.id }
model { Faker::Commerce.product_name }
color { sprintf("%06X", Random.rand(16777215) ) }
bike_style_id { BikeStyle.all.sample.id }
seat_tube_height { Random.rand(25) }
top_tube_length { Random.rand(25) }
bike_wheel_size_id { BikeWheelSize.all.sample.id }
value { Random.rand(200) }
bike_condition_id { BikeCondition.all.sample.id }
bike_purpose_id { BikePurpose.all.sample.id }
sequence :serial_number do |n|
"S/N# #{n}"
end
bike_brand_id 1
bike_model_id 1
color "FFFFFF"
bike_style_id 1
seat_tube_height 5
top_tube_length 6
wheel_size 27
value 100
bike_condition_id 1
bike_purpose_id 1
end
end
+6 -6
View File
@@ -1,11 +1,11 @@
FactoryGirl.define do
factory :user_profile do
user_id 1
addrStreet1 { Faker::Address.street_address }
addrStreet2 { Faker::Address.secondary_address }
addrCity { Faker::Address.city }
addrState { Faker::Address.state_abbr }
addrZip { Faker::Address.zip_code }
phone { Faker::PhoneNumber.cell_phone }
addrStreet1 "Charles Street"
addrStreet2 "Apt #42"
addrCity "Baltimore"
addrState "MD"
addrZip "21231"
phone "(410)8675309"
end
end
-1
View File
@@ -1,6 +1,5 @@
FactoryGirl.define do
factory :user do
sequence(:username) { |n| "user_#{n}" }
sequence(:email) { |n| "user_#{n}@example.com" }
password 'password'