User foundation setup

This commit is contained in:
2014-03-26 13:17:34 +01:00
parent 6c2427e082
commit 4e75c72097
87 changed files with 861 additions and 120 deletions
+2 -1
View File
@@ -23,8 +23,9 @@ group :assets do
#gem 'twitter-bootstrap-rails'
gem 'bootstrap-sass', '~>2.3'
#gem 'bourbon'
#gem 'compass-rails'
gem 'compass-rails'
gem 'js-routes'
gem "font-awesome-rails"
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
#gem 'therubyracer', :platforms => :ruby
+12
View File
@@ -110,6 +110,7 @@ GEM
capybara-webkit (1.1.0)
capybara (~> 2.0, >= 2.0.2)
json
chunky_png (1.3.0)
climate_control (0.0.3)
activesupport (>= 3.0)
cocaine (0.5.3)
@@ -122,6 +123,12 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.7.0)
compass (0.12.4)
chunky_png (~> 1.2)
fssm (>= 0.2.7)
sass (~> 3.2.17)
compass-rails (1.1.6)
compass (>= 0.12.2)
connection_pool (1.2.0)
cookiejar (0.3.2)
couchbase (1.3.6)
@@ -194,9 +201,12 @@ GEM
faye-websocket (0.7.2)
eventmachine (>= 0.12.0)
websocket-driver (>= 0.3.1)
font-awesome-rails (4.0.3.1)
railties (>= 3.2, < 5.0)
foundation-rails (5.2.1.0)
railties (>= 3.1.0)
sass (>= 3.2.0)
fssm (0.2.10)
fuubar (1.3.2)
rspec (>= 2.14.0, < 3.1.0)
ruby-progressbar (~> 1.3)
@@ -389,6 +399,7 @@ DEPENDENCIES
capybara-webkit
cmtool!
coffee-rails
compass-rails
couch_potato!
couchbase
couchbase-docstore
@@ -401,6 +412,7 @@ DEPENDENCIES
emblem-rails
factory_girl_rails
faye
font-awesome-rails
foundation-rails
fuubar
jquery-rails
@@ -0,0 +1,2 @@
Ember.Handlebars.registerHelper 'image_tag', (path, params..., options={})->
new Handlebars.SafeString "<img src=\"/assets/#{path}\" alt=\"#{options.alt}\" >"
@@ -1,4 +1,3 @@
Qsupplier.App = Ember.Application.create
LOG_TRANSITIONS: true
rootElement: '#ember-app-container'
@App = Qsupplier.App
@@ -3,14 +3,9 @@ root.Qsupplier=
watch_events: ->
faye = new Faye.Client(event_host)
faye.subscribe "/supplier/"+supplier_id, (e)=>
console.log(e)
if(e.event == 'new_order')
Qsupplier.App.Order.pushByAttriburtes(e.data.order) if Qsupplier.App
# old stuff
body = $('#active-orders-table tbody')
order = new Order(e.data.order)
if body.length
body.append @mustache('#active-order-template', order)
$('.section-table-list-'+order.list_id()).addClass('active_order')
else if(e.event == 'list_needs_help')
if Qsupplier.App and list = Qsupplier.App.List.findCached(e.data.id)
list.markNeedsHelp()
@@ -29,18 +24,6 @@ root.Qsupplier=
list.markIsPaid()
else if e.event == 'list_update'
Qsupplier.App.List.updateOrAdd(e.data.list) if Qsupplier.App
# old stuff
list = new List(e.data.list)
row = $('#active-lists-table .list-row-'+list.id())
content = @mustache('#active-list-template', list)
if row.length then row.replaceWith(content) else $('#active-lists-table tbody').append(content)
table = $('#section-table-'+list.table_id())
if table.length
table.addClass('section-table-list-'+list.id())
table.addClass('occupied')
table.addClass('needs_help') if list.needs_help()
table.addClass('needs_payment') if list.needs_payment()
table.addClass('active_order') if list.has_active_orders()
else if e.event == 'list_closed'
if Qsupplier.App and list = Qsupplier.App.List.findCached(e.data.id)
list.markClosed()
@@ -68,26 +51,24 @@ root.Qsupplier=
Qsupplier.App && Qsupplier.App.List.updateOrAdd(e.data.list)
# old stuff
list = new List(e.data.list)
list_row = $('.list-row-'+list.id())
list_row.find('.table_number').text(list.table_number()).addClass('changed')
list_row.find('.section_title').text(e.data.section_title)
order_rows = $('.of-list-'+list.id())
order_rows.find('.table_number').text(list.table_number()).addClass('changed')
order_rows.find('.section_title').text(e.data.section_title)
# Move properties from table in section tables view
window.last_data = e.data
from_table = $('#section-table-'+e.data.from_table_id)
to_table = $('#section-table-'+list.table_id())
if to_table.length
to_table.addClass('section-table-list-'+list.id())
to_table.addClass('occupied')
to_table.addClass('needs_help') if list.needs_help()
to_table.addClass('needs_payment') if list.needs_payment()
to_table.addClass('active_order') if list.has_active_orders()
from_table.removeClass('occupied needs_help needs_payment active_order section-table-list-'+list.id())
#list = new List(e.data.list)
#list_row = $('.list-row-'+list.id())
#list_row.find('.table_number').text(list.table_number()).addClass('changed')
#list_row.find('.section_title').text(e.data.section_title)
#order_rows = $('.of-list-'+list.id())
#order_rows.find('.table_number').text(list.table_number()).addClass('changed')
#order_rows.find('.section_title').text(e.data.section_title)
## Move properties from table in section tables view
#window.last_data = e.data
#from_table = $('#section-table-'+e.data.from_table_id)
#to_table = $('#section-table-'+list.table_id())
#if to_table.length
#to_table.addClass('section-table-list-'+list.id())
#to_table.addClass('occupied')
#to_table.addClass('needs_help') if list.needs_help()
#to_table.addClass('needs_payment') if list.needs_payment()
#to_table.addClass('active_order') if list.has_active_orders()
#from_table.removeClass('occupied needs_help needs_payment active_order section-table-list-'+list.id())
console.log(e)
false
move_table_to_active_section: (table_id)->
@@ -0,0 +1,20 @@
@App = Ember.Application.create
LOG_TRANSITIONS: true
rootElement: '#ember-app-container'
Ember.$.ajaxPrefilter (options) ->
if options.type.toUpperCase() == 'GET'
if auth_token = Qstorage.getItem('auth_token')
if options.data
options.data += "&auth_token=#{auth_token}"
else
options.data = "auth_token=#{auth_token}"
if options.type.toUpperCase() == 'POST'
if auth_token = Qstorage.getItem('auth_token')
options.data ||= {}
options.data.auth_token = auth_token
true
Ember.$.ajaxSetup
error: (jqXHR, textStatus, errorThrown)->
console.log "Error: #{textStatus}: #{errorThrown}"
if jqXHR.status == 401
App.__container__.lookup('route:application').unauthorized()
@@ -0,0 +1,10 @@
#= require_self
#= require handlebars
#= require ember
#= require ember-data
#= require shared-ember-helpers/all
#= require_directory ./modifications
#= require ./app
#= require user/quser
#= require_tree .
@EmberENV = {FEATURES: {'query-params-new': true}}
@@ -0,0 +1,4 @@
App.ModalDialogComponent = Ember.Component.extend
actions:
close: ->
@sendAction()
@@ -0,0 +1,30 @@
App.ApplicationController = Ember.Controller.extend
notice: ''
actions:
confirmCancel: ->
if cancel = @get('confirm.cancel')
cancel.call(@)
$('#confirm-modal').hide()
clearNotice: ->
@set 'notice', null
openDebugger: ->
debugger
currentPathDidChange: (->
@set 'notice', ''
).observes('currentPath')
events:
list_helped: -> @set 'list.needs_help', false
list_needs_help: -> @set 'list.needs_help', true # incoming from other users
list_is_paid: -> @set 'list.needs_payment', false
list_needs_payment: -> @set 'list.needs_payment', true # incoming from other users
getList: ->
Ember.$.get('/user/list_info.json').then (res)=>
if res.not_present
@set 'list', null
else
@set 'list', App.List.create(res)
@getProducts table_id: @get('list.table_id')
getProducts: (options = {})->
@store.find('product_category', options).then (product_categories)=>
@controllerFor('list_products').set 'model', product_categories
@@ -0,0 +1 @@
App.IndexController = Ember.Controller.extend {}
@@ -0,0 +1,7 @@
App.ListProductsController = Ember.ArrayController.extend
actions:
addProduct: (product)->
if existing = @store.all('product_order').find((po)-> po.get('product') == product)
existing.increment()
else
@store.createRecord 'product_order', product: product
@@ -0,0 +1 @@
App.ListProductsForTableController = Ember.ArrayController.extend {}
@@ -0,0 +1,8 @@
App.ModalConfirmController = Ember.ObjectController.extend
actions:
close: ->
@get('model.cancel').call(@) if @get('model.cancel')
@send 'closeModal'
confirm: ->
@get('model.ok').call(@)
@send 'closeModal'
@@ -0,0 +1,4 @@
App.ModalController = Ember.ObjectController.extend
actions:
close: ->
@send 'closeModal'
@@ -0,0 +1,21 @@
App.ProductOrdersController = Ember.ArrayController.extend
orderTotal: (->
#Math.round(Math.random()*100)
@get('model').getEach('total').reduce(((sum, total) -> sum + total), 0)
).property('model.@each.quantity')
actions:
clearProductOrders: ->
@store.all('product_order').toArray().invoke 'unloadRecord'
orderProducts: ->
orders = @store.all('product_order').toArray()
data = orders.map( (order)->order.serialize() )
dataObject = {order: {}}
for product_order in data
dataObject['order'][product_order.product_id] = product_order.quantity
$.ajax
url: Routes.user_order_selected_products_path()
type: "POST",
data: JSON.stringify(dataObject),
contentType: "application/json",
dataType: 'json'
orders.invoke 'unloadRecord'
@@ -0,0 +1,43 @@
App.SelectQrcodeController = Ember.Controller.extend
actions:
selectQr: (table)->
Qstorage.setItem 'table_id', table._id
@secured ->
$.getJSON(data_host + '/user/table_info.json?'+@authentication_string+'&table_id='+table._id).then (res)=>
if res.current_table_id
if res.other_supplier
@redirect_to 'user_root', message: 'table_is_from_other_supplier'
else if res.current_table_id == table.table_id
#nothing has changed, show product list
@redirect_to 'list_products'
else if res.current_table_id != table.table_id
if res.occupied
@redirect_to 'user_root', message: 'table_is_occupied'
else if res.reserved
@redirect_to 'user_root', message: 'table_is_reserved'
else if table.closed
@redirect_to 'user_root', message: 'table_is_closed'
else if res.supplier_closed
@redirect_to 'user_root', message: 'supplier_is_closed'
else
## Offer to move table
@send 'confirm',
title: t('move_table.confirmation_title')
body: t('move_table.confirmation_body')
ok: =>
$.post(data_host + '/user/move_table.json', $.extend({table_id: table._id}, @authentication_object), (res2)=>
if res2.occupied
@redirect_to 'user_root', {message: 'move_table.cannot_move_to_occupied_tabe'}
else
@redirect_to 'list_products', {message: 'move_table.moved_to_another_table'}
)
cancel: =>
@redirect_to 'list_products'
else
if res.occupied
@redirect_to 'join_occupied_table', {table_id: table.table_id}
else if res.supplier_closed
@redirect_to 'user_root', {message: 'supplier_is_closed'}
else
#$.post(data_host + '/user/create_list.json', {table_id: table.table_id}, (res)-> Quser.handle_response(res))
@redirect_to 'list_products_for_table', {table_id: table.table_id}
@@ -0,0 +1 @@
App.List = Ember.Object.extend {}
@@ -0,0 +1,6 @@
attr = DS.attr
App.Product = DS.Model.extend
name: attr 'string'
price: attr 'number'
product_category: DS.belongsTo('product_category')
product_orders: DS.hasMany('product_order')
@@ -0,0 +1,4 @@
attr = DS.attr
App.ProductCategory = DS.Model.extend
name: attr('string')
products: DS.hasMany('product')
@@ -0,0 +1,7 @@
attr = DS.attr
App.ProductOrder = DS.Model.extend
quantity: attr 'number', defaultValue: 1
product: DS.belongsTo('product')
increment: ->
@set('quantity', @get('quantity') + 1)
total: (-> @get('quantity') * @get('product.price')).property('quantity')
@@ -0,0 +1,50 @@
Ember.Controller.reopen
needs: ['application']
secured: (callback)->
unless Qstorage.getItem('auth_token') && typeof(Qstorage.getItem('auth_token')) == 'string' && Qstorage.getItem('auth_token').length > 0
return @transitionToRoute('obtain_token')
@authentication_string = 'auth_token='+Qstorage.getItem('auth_token')
@authentication_object = {auth_token: Qstorage.getItem('auth_token')}
callback.call(@) if callback
redirect_to: (route, options={})->
route = 'index' if route == 'user_root'
@transitionToRoute(route).then =>
if options.message
tkey = if options.message.indexOf('.') > -1 then options.message else "messages.#{options.message}"
@set 'controllers.application.notice', t(tkey)
#confirm: (options = {})->
##@showModal options
##$(document).foundation('reflow') # needed (stupid!!!)
#r = @container.lookup('route:application')
#r.send('confirm', options)
##window.confirm_cancel = options.cancel
##window.confirm_ok = options.ok
##$('#confirm-modal .confirm-title').html options.title if options.title
##$('#confirm-modal .confirm-content').html options.content if options.content
##@set 'controllers.application.confirm.content', options.content if options.content
##$('#confirm-modal').foundation('reveal', 'open') # this kills the ember actions
##$('#confirm-modal').css('visibility', 'visible').show()
showModal: (options={})->
#this.container.lookup('view:modal', {title:'Test title'})
debugger
$(document).foundation('reflow') # needed (stupid!!!)
@confirm_cancel = options.cancel
@set 'controllers.application.modal.title', options.title if options.title
@set 'controllers.application.modal.content', options.content if options.content
#$('#confirm-modal').foundation('reveal', 'open') #this kills the ember actions
#$('#confirm-modal').css('visibility', 'visible').show()
Ember.ArrayController.reopen
needs: ['application']
secured: (callback)->
unless Qstorage.getItem('auth_token') && typeof(Qstorage.getItem('auth_token')) == 'string' && Qstorage.getItem('auth_token').length > 0
return @transitionToRoute('obtain_token')
@authentication_string = 'auth_token='+Qstorage.getItem('auth_token')
@authentication_object = {auth_token: Qstorage.getItem('auth_token')}
callback.call(@) if callback
redirect_to: (route, options={})->
route = 'index' if route == 'user_root'
@transitionToRoute(route).then =>
if options.message
tkey = if options.message.indexOf('.') > -1 then options.message else "messages.#{options.message}"
@set 'controllers.application.notice', t(tkey)
@@ -0,0 +1,14 @@
# For more information see: http://emberjs.com/guides/routing/
# and for queryParams: https://github.com/alexspeller/website/blob/a96d9afe4506454b155cc64299e86e558ce3c9f1/source/guides/routing/query-params.md
App.Router.reopen
#location: 'history'
rootURL: '/user'
activate: (a,b,c)->
debugger
App.Router.map ->
@route 'select_qrcode'
@route 'obtain_token'
@route 'active_list'
@route 'list_products'
@route 'list_products_for_table'
@@ -0,0 +1,45 @@
App.ApplicationRoute = Ember.Route.extend
setupController: (controller)->
@controllerFor('product_orders').set 'model', @store.all('product_order')
controller.secured ->
faye = new Faye.Client(event_host)
user_id = Qstorage.getItem('user_id')
faye.subscribe "/user/"+user_id, (e)=>
console.log e
@events[e.event].call(@) if @events[e.event]
@getList()
unauthorized: ->
Qstorage.setItem('auth_token', '')
@controllerFor('application').set 'list', null
@transitionTo('obtain_token').then =>
@controllerFor('application').set('notice', t('messages.unauthorized'))
actions:
openModal: (modalName, model)->
@controllerFor(modalName).set('model', model)
@render modalName,
into: 'application'
outlet: 'modal'
closeModal: ->
@disconnectOutlet
outlet: 'modal'
parentView: 'application'
confirm: (options = {})->
@send 'openModal', 'modal_confirm', Ember.Object.create
title: options.title
body: options.body
cancel: options.cancel
ok: options.ok
listNeedsPayment: ->
@get('controller').secured ->
$.post(data_host + '/user/list_needs_payment.json', @authentication_object).then (res) =>
@set('list.needs_payment', true)
listNeedsHelp: ->
@get('controller').secured ->
$.post(data_host + '/user/needs_help.json', @authentication_object).then (res) =>
@set('list.needs_help', true)
scanQr: ->
@transitionTo 'select_qrcode'
events: ->
error: (error)->
debugger
@@ -0,0 +1,18 @@
App.ListProductsForTableRoute = Ember.Route.extend {}
#setupController: (controller)->
#controller.secured ->
#src = '/user/list_products_for_table.json'
#data = {}
#$.getJSON(data_host + src, $.extend(@authentication_object, data)).then (res) =>
#if res.not_present
#@redirect_to 'index', message: 'the_list_has_been_closed'
#return
#for pc in res.categories
#product_category = @store.createRecord 'product_category',
#name: pc.name
#for pp in pc.products
#pp.id = pp._id
#pp.product_category = product_category
#product = @store.createRecord 'product', pp
#controller.set 'model', @store.all('product_category')
#debugger
@@ -0,0 +1,17 @@
App.ListProductsRoute = Ember.Route.extend {}
#setupController: (controller)->
#controller.secured ->
#src = '/user/list_products.json'
#data = {}
#$.getJSON(data_host + src, $.extend(@authentication_object, data)).then (res) =>
#if res.not_present
#@redirect_to 'index', message: 'the_list_has_been_closed'
#return
#for pc in res.categories
#product_category = @store.createRecord 'product_category',
#name: pc.name
#for pp in pc.products
#pp.id = pp._id
#pp.product_category = product_category
#product = @store.createRecord 'product', pp
#controller.set 'model', @store.all('product_category')
@@ -0,0 +1,9 @@
App.SelectQrcodeRoute = Ember.Route.extend
setupController: (controller)->
$.ajax
url: Routes.select_qrcode_path()
type: "GET"
dataType: 'json'
async: false
success: (res)->
controller.set 'tables', res
@@ -0,0 +1,13 @@
# http://emberjs.com/guides/models/defining-a-store/
DS.RESTAdapter.reopen
namespace: 'user'
App.ApplicationSerializer = DS.ActiveModelSerializer
App.CustomAdapter = DS.RESTAdapter.extend
# user underscored paths
pathForType: (type)->
decamelized = Ember.String.decamelize(type)
Ember.String.pluralize(decamelized)
App.Store = DS.Store.extend
adapter: App.CustomAdapter
@@ -0,0 +1 @@
h2 Active list
@@ -0,0 +1,42 @@
.top-menu.off-canvas-wrap
.inner-wrap
nav.tab-bar
section.left-small
a.left-off-canvas-toggle.menu-icon
span
section.right.tab-bar-section
= link-to 'index'
= image_tag 'icons/logo-small.png'
a{action openDebugger} alt=""
span.fa.fa-wrench.fa-lg
.right
if list.id
App.MenuItemView route="active_list"
App.MenuItemView route='list_products'
App.MenuItemListNeedsHelpView
App.MenuItemListNeedsPaymentView
aside.left-off-canvas-menu
ul.off-canvas-list
li
label Menu
li
= link-to 'index'
span Home
li
a{action scanQr bubbles=false}
span Scan QR
li
=link-to 'list_products'
span= t 'list_products.title'
li
=link-to 'active_list'
span= t 'active_list.title'
section.main-section
if notice
#notice.alert-box{action clearNotice} data-alert=true
a.right href="#"
span.fa.fa-times.fa-lg
span= notice
= outlet
a.exit-off-canvas
=outlet modal
@@ -0,0 +1,2 @@
.overlay{action "close"}
.modal{action bubbles=false preventDefault=false}= yield
@@ -0,0 +1,6 @@
.home-panel
.home-header = image_tag 'logo.png'
.home-center
a{action scanQr} href="#"= image_tag 'scan-logo.png'
.home-footer
.home-footer-content
@@ -0,0 +1,9 @@
.row
.large-6.columns
each product_category in controller
hr
h4= product_category.name
hr
each product in product_category.products
a{action addProduct product}= product.name
.large-6.columns= render 'product_orders'
@@ -0,0 +1,8 @@
.row
.large-6.columns
each product_category in controller
hr
h4= product_category.name
hr
each product in product_category.products
a{action addProduct product}= product.name
@@ -0,0 +1 @@
span.fa.fa-list.fa-lg
@@ -0,0 +1 @@
span.needs-help.fa.fa-hand-o-up.fa-lg
@@ -0,0 +1 @@
span.needs-payment.fa.fa-money.fa-lg
@@ -0,0 +1,2 @@
span.fa.fa-cutlery
span.fa.fa-glass
@@ -0,0 +1,4 @@
modal-dialog action="close"
h3.flush--top Alert
p= body
button{action "close"} Done
@@ -0,0 +1,6 @@
modal-dialog action="close"
h3.flush--top= title
p=body
hr
button{action "close"}= t 'confirm.cancel'
button.right{action 'confirm'}= t 'confirm.confirm'
@@ -0,0 +1 @@
h3 Obtain token...
@@ -0,0 +1,19 @@
hr.hide-for-medium-up
if model
a.tiny.button.right{action clearProductOrders} href="#" x
.clearfix
.panel
ul.product-orders
each product_order in controller
li
= product_order.quantity
| x
= product_order.product.name
span.currency=currency product_order.total
else
li= t 'product_orders.no_orders'
li.total
= t 'product_orders.total'
span.currency=currency orderTotal
if model
a.tiny.button.right{action orderProducts} href="#"= t 'product_orders.order_button'
@@ -0,0 +1,2 @@
each table in tables
img{action selectQr table} src="/table_qr_image.svg?table_id=#{unbound table._id}"
@@ -0,0 +1,10 @@
App.MenuItemListNeedsHelpView = Ember.View.extend Ember.ViewTargetActionSupport,
action: 'listNeedsHelp'
templateName: "menu_item_list_needs_help"
classNames: 'menu-list-item'
classNameBindings: ['controller.list.needs_help:active']
click: ->
if @get('controller.list.needs_help')
@set 'controller.notice', t('list_needs_help.help_is_on_its_way')
else
@triggerAction()
@@ -0,0 +1,11 @@
App.MenuItemListNeedsPaymentView = Ember.View.extend Ember.ViewTargetActionSupport,
action: 'listNeedsPayment'
templateName: "menu_item_list_needs_payment"
classNames: 'menu-list-item'
classNameBindings: ['controller.list.needs_payment:active']
click: ->
if @get('controller.list.needs_payment')
@set 'controller.notice', t('list_needs_payment.payment_already_requested')
else
@triggerAction() #action: 'listNeedsPayment'
@@ -0,0 +1,12 @@
App.MenuItemView = Ember.View.extend
classNames: 'menu-list-item'
classNameBindings: ['active']
click: ->
@get('controller').transitionToRoute(@route)
active: (->
if @get('controller.currentPath') == @route then 'active' else ''
).property('controller.currentPath')
init: ->
@templateName = "menu_item_#{@route}"
@_super()
@@ -0,0 +1,10 @@
#App.ModalView = Ember.View.extend
#templateName: "modal"
#title: ""
#classNames: ["reveal-modal"],
#didInsertElement: ->
##this.$().foundation('reveal', 'open')
#actions:
#close: ->
#console.log('close action fired')
#this.destroy()
@@ -48,11 +48,13 @@ var $translations = {
messages: <%= I18n.t('messages', locale: :en).to_json %>,
confirmations: {
},
// Moved to yml
list_needs_help: {
help_is_on_its_way: 'Help is already on its way',
title: 'Request a waiter',
content: 'Request a waiter to your table'
},
// Moved to yml
list_needs_payment: {
payment_already_requested: 'You already asked for the bill',
title: 'Ask for the check',
@@ -75,6 +77,7 @@ var $translations = {
waiting_for_confirmation: 'Waiting for approval of the person on this table...'
}
},
// Moved to yml
move_table: {
cannot_move_to_occupied_table: 'You cannot move to an occupied table',
moved_to_another_table: 'The table is changed.',
@@ -89,11 +92,13 @@ var $translations = {
messages: <%= I18n.t('messages', locale: :nl).to_json %>,
confirmations: {
},
// Moved to yml
list_needs_help: {
help_is_on_its_way: 'Er wordt al iemand naar je tafel gestuurd',
title: 'Ik heb een vraag',
content: 'Wil je een vraag stellen?'
},
// Moved to yml
list_needs_payment: {
payment_already_requested: 'De rekening is reeds gevraagd',
title: 'Vraag om de rekening',
@@ -0,0 +1,21 @@
#= require jquery
#= require jquery_ujs
#= require ../app/application
#= require faye
#= require foundation/foundation
#= require foundation/foundation.offcanvas
#= require moment
#= require jquery.ui.datepicker
#= require translations
#= require js-routes
#= require_directory .
#= require_self
@Qstorage = localStorage
$.extend($translations.en, <%= I18n.t('user', locale: :en).to_json %>);
$.extend($translations.nl, <%= I18n.t('user', locale: :nl).to_json %>);
$(document).foundation()
setLocale('en')
$ ->
$('.main-section').css 'min-height', ($(window).height() - $('.tab-bar:first').outerHeight())
@@ -5,8 +5,8 @@ App.TableController = Ember.ObjectController.extend
actions:
clearProductOrders: ->
@get('product_orders').toArray().forEach (product_order)->
product_order.unloadRecord()
# toArray is needed because without is the iterator is not working properly
@get('product_orders').toArray().invoke 'unloadRecord'
orderProducts: ->
orders = @get('product_orders').toArray()
data = orders.map( (order)->order.serialize() )
@@ -12,11 +12,11 @@ h4
= product_order.quantity
| x
= product_order.product.name
span.currency=currency product_order.product.price
span.currency=currency product_order.total
else
li No products
li= t 'product_orders.no_orders'
li.total
| Total
= t 'product_orders.total'
span.currency=currency orderTotal
if product_orders
a.tiny.button.right{action orderProducts} href="#" = t 'product_orders.order_button'
@@ -8,6 +8,7 @@
#= require js-routes
#= require_directory .
#= require_self
@Qstorage = localStorage
$.extend($translations.en, <%= I18n.t('waiter', locale: :en).to_json %>);
$.extend($translations.nl, <%= I18n.t('waiter', locale: :nl).to_json %>);
@@ -4,7 +4,7 @@
*= require 'jquery-ui-1.8.23.custom.css'
*= require qtip
*= require 'general'
*= require user/active_list
* require user/active_list
*= require_directory ../base1-shared
*= require_directory .
*= require_self
@@ -4,7 +4,7 @@
*= require 'jquery-ui-1.8.23.custom.css'
*= require qtip
*= require 'general'
*= require user/active_list
* require active_list
*= require_directory ../base1-shared
*= require_directory .
*= require_self
@@ -0,0 +1,3 @@
//= require foundation
//= require font-awesome
//= require_directory .
@@ -0,0 +1,12 @@
@import foundation
.home-panel
+panel()
margin: 30px auto 20px auto
width: 350px
text-align: center
padding-bottom: 30px
.home-header
img
display: block
margin: 0 auto
margin-bottom: 24px
@@ -0,0 +1,16 @@
.modal
margin: 10px auto
width: 300px
background-color: #fff
padding: 1em
.overlay
height: 100%
width: 100%
position: fixed
top: 0
left: 0
background-color: rgba(0, 0, 0, 0.2)
.flush--top
margin-top: 0
@@ -0,0 +1,8 @@
ul.product-orders
list-style: none
li
border-bottom: 1px solid #ccc
&.total
border-bottom: none
border-top: 4px solid #333
font-weight: bold
@@ -0,0 +1,21 @@
section.main-section
height: 100%
.top-menu
.menu-list-item
float: right
margin-left: 20px
cursor: pointer
border: 0.08em solid #eee
padding: 3px
border-radius: 0.1em
line-height: 1em
margin-top: 10px
&.active
color: yellow
border-color: yellow
#notice
a
color: white
li
span.currency
float: right
@@ -1,5 +1,5 @@
@import compass
@import mixins
@import ./mixins
// Contents:
// =General
@@ -1,5 +1,5 @@
@import compass
@import user/constants
@import ./constants
#products-table
.order-product-button
+wood-button
@@ -1,5 +1,5 @@
@import compass
@import user/constants
@import ./constants
html
background-image: $wood
background-color: $background-brown
+5 -2
View File
@@ -8,8 +8,11 @@ class DashboardController < ApplicationController
# Testing action
def select_qrcode
#@tables = Table.all.sample(2) | List.active.map(&:table)
@tables = Supplier.first.tables.sample(2) | List.active.map(&:table)
render layout: 'phone'
@tables = Supplier.first.tables.sample(5) | List.active.map(&:table)
respond_to do |format|
format.html { render layout: 'phone' }
format.json { render json: @tables.to_json }
end
end
+11 -54
View File
@@ -1,41 +1,7 @@
class UserController < ApplicationController
class UserController < Users::ApplicationController
before_filter :allow_mobile
before_filter :user_authentication, :unless => ->(c){ %w(obtain_token index).include?(c.action_name) || c.request.format.symbol == :html } # , except: [:obtain_token, :index]
layout 'phone'
def user_authentication
if params[:auth_token].present?
user = User.find_by_authentication_token(params[:auth_token])
sign_in user if user
sign_out current_user if current_user && !user # Other token attempt of logged in user
else
authenticate_user!
end
unless current_user.present?
respond_to do |format|
format.html {redirect_to new_user_session_path}
format.json {render json: json_response(ok: false, status: 401)}
end
end
end
def obtain_token
redirect_to user_omniauth_authorize_path('facebook') and return unless current_user.present?
respond_to do |format|
format.html
format.json do
render json: json_response(ok: false, status: 401) and return unless params[:user].present? && params[:user][:email].present? && params[:user][:password].present?
user = User.find_by_email(params[:user][:email])
render json: json_response(ok: false, status: 401) and return unless user
render json: json_response(ok: false, status: 401) and return unless user.valid_password?(params[:user][:password])
user.ensure_authentication_token
sign_in user
render json: json_response(ok: true, auth_token: user.authentication_token, user_id: user.id)
end
end
end
layout 'user/foundation'
#layout 'phone'
alias :list :active_list
@@ -91,13 +57,17 @@ class UserController < ApplicationController
# GET /suppliers/1/product_list
# GET /suppliers/1/product_list.json
def list_products
redirect_to(user_root_path(message: 'the_list_has_been_closed')) and return unless list
@supplier = list.supplier
respond_to do |format|
format.html do
redirect_to(user_root_path(message: 'the_list_has_been_closed')) and return unless list
@supplier = list.supplier
handle_message_params
end
format.json do
unless list
render json: {not_present: true} and return
end
@supplier = list.supplier
h = ProductCategory.for_user(current_user, table: list.table, list: list, supplier: @supplier) # list is performance parameter
render json: h
#products = list.supplier.products
@@ -291,7 +261,7 @@ class UserController < ApplicationController
end
format.json do
render json: json_alert('messages.cannot_order_on_non_active_list') and return unless @list.active?
@list.place_order params[:products], user: current_user
@list.place_order params[:order] || params[:products], user: current_user
render json: json_notice('messages.order_is_placed', location: :active_list)
end
end
@@ -299,6 +269,7 @@ class UserController < ApplicationController
def move_table
render json: json_alert('messages.no_active_list', list_active: false) and return unless list.present?
render json: json_alert('messages.table_not_found') and return unless params[:table_id].present?
@table = Table.find(params[:table_id])
if @table.occupied?
render json: {occupied: true}
@@ -307,18 +278,4 @@ class UserController < ApplicationController
render json: {occupied: false}
end
end
private
def handle_message_params
flash.now[:notice] = t('messages.the_list_has_been_closed', list: List.model_name.human) if params[:list_closed].present?
flash.now[:notice] = t("messages.#{params[:message]}", list: List.model_name.human, supplier: Supplier.model_name.human) if params[:message].present? && params[:message] =~ /^\w+$/
end
# General handler of json responses. Will be able to set some additional communication data.
# By default a response is ok.
def json_response(obj = {})
obj[:ok] = true unless obj.has_key?(:ok)
obj
end
end
@@ -0,0 +1,52 @@
module Users
class ApplicationController < ::ApplicationController
before_action :user_authentication, :unless => ->(c){ %w(obtain_token).include?(c.action_name) || c.request.format.symbol == :html } # , except: [:obtain_token, :index]
private
def user_authentication
if params[:auth_token].present?
user = User.find_by_authentication_token(params[:auth_token])
sign_in user if user
sign_out current_user if current_user && !user # Other token attempt of logged in user
else
authenticate_user!
end
unless current_user.present?
respond_to do |format|
format.html {redirect_to new_user_session_path}
format.json {render json: json_response(ok: false, status: 401), status: :unauthorized}
end
end
end
def obtain_token
redirect_to user_omniauth_authorize_path('facebook') and return unless current_user.present?
respond_to do |format|
format.html
format.json do
render json: json_response(ok: false, status: 401) and return unless params[:user].present? && params[:user][:email].present? && params[:user][:password].present?
user = User.find_by_email(params[:user][:email])
render json: json_response(ok: false, status: 401) and return unless user
render json: json_response(ok: false, status: 401) and return unless user.valid_password?(params[:user][:password])
user.ensure_authentication_token
sign_in user
render json: json_response(ok: true, auth_token: user.authentication_token, user_id: user.id)
end
end
end
def handle_message_params
flash.now[:notice] = t('messages.the_list_has_been_closed', list: List.model_name.human) if params[:list_closed].present?
flash.now[:notice] = t("messages.#{params[:message]}", list: List.model_name.human, supplier: Supplier.model_name.human) if params[:message].present? && params[:message] =~ /^\w+$/
end
# General handler of json responses. Will be able to set some additional communication data.
# By default a response is ok.
def json_response(obj = {})
obj[:ok] = true unless obj.has_key?(:ok)
obj
end
end
end
@@ -0,0 +1,15 @@
module Users
class ProductCategoriesController < Users::ApplicationController
#EMBER
def index
respond_to do |format|
format.json do
render json: {} and return unless params[:table_id].present?
table = Table.find(params[:table_id])
product_categories = table.supplier.product_categories.include_relation('products') # not yet implemented for many to many
render json: product_categories #, serializer: ProductCategorySerializer
end
end
end
end
end
+1 -1
View File
@@ -108,7 +108,7 @@ class Order
def close!
self.state = 'closed' if placed? || active?
if save
broadcast_user user.id, 'order_closed', id: id
broadcast_user user.id, 'order_closed', id: id if user
broadcast_supplier supplier_id, 'order_closed', id: id
end
end
+1 -1
View File
@@ -10,7 +10,7 @@ html lang="en"
/! Le HTML5 shim, for IE6-8 support of HTML elements
/[if lt IE 9]
= javascript_include_tag "http://html5shim.googlecode.com/svn/trunk/html5.js"
= stylesheet_link_tag "user/application", :media => "all"
= stylesheet_link_tag "user/wood1/application", :media => "all"
link href="/images/apple-touch-icon-144x144.png" rel="apple-touch-icon-precomposed" sizes="144x144"
link href="/images/apple-touch-icon-114x114.png" rel="apple-touch-icon-precomposed" sizes="114x114"
link href="/images/apple-touch-icon-72x72.png" rel="apple-touch-icon-precomposed" sizes="72x72"
+1 -1
View File
@@ -10,7 +10,7 @@ html lang="en"
/! Le HTML5 shim, for IE6-8 support of HTML elements
/[if lt IE 9]
= javascript_include_tag "http://html5shim.googlecode.com/svn/trunk/html5.js"
= stylesheet_link_tag "supplier/basic1/application", media: "all"
= stylesheet_link_tag "supplier/wood1/application", media: "all"
link href="/favicon.ico" rel="shortcut icon"
= render 'suppliers/application/head'
= javascript_include_tag "supplier/application"
@@ -0,0 +1,47 @@
doctype html
html lang="en"
head
meta charset="utf-8"
meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1"
meta name="viewport" content="width=device-width, initial-scale=1.0"
title Qwaiter
= stylesheet_link_tag "user/foundation/application"
= javascript_include_tag "vendor/modernizr"
= javascript_include_tag "user/flat/application"
- if ENV['QWAITER_MOBILE_EXPORT'] == 'yes'
javascript:
var QMobile, Qwaiter, Quser;
var data_host = 'http://data.qwaiter.com';
var event_host = '#{Qwaiter.event_host}';
var $asset_path = '##assets_path##';
var Qstorage = localStorage;
- else
javascript:
var QMobile, Qwaiter, Quser;
var data_host = 'http://data.qwaiter.com';
var event_host = '#{Qwaiter.event_host}';
var $asset_path = '/assets/';
var Qstorage = localStorage;
#{user_dynamic_data_host}
QMobile || (QMobile = {
scanQr: function(){window.location = '/select_qrcode'},
activateRotation: function(){},
mobile: function(){return false},
authentication_string: function(){return this.authentication_string_storage || ''},
authentication_object: function(){return this.authentication_object_storage || '{}'},
setAuthToken: function(token){
this.auth_token = token;
this.authentication_string_storage = 'auth_token='+token;
this.authentication_object_storage = '{"auth_token": "'+token+'"}'
},
root_url: function(){return 'file:///Users/bterkuile/Documents/workspace/Qwaiter/assets/user'},
root_url: function(){return '/user'},
goHome: function(){ redirect_to('user_root')},
connection_problem: function(){alert('There is a problem connecting to the server')},
token: function(){return this.auth_token},
setUserId: function(id){ this.stored_user_id = id},
user_id: function(){return this.stored_user_id },
log: function(str){console.log(str)}
});
body
#ember-app-container
+13
View File
@@ -0,0 +1,13 @@
doctype html
html lang="en"
head
meta charset="utf-8"
meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1"
meta name="viewport" content="width=device-width, initial-scale=1.0"
title Qwaiter
/= stylesheet_link_tag "waiter/application", media: "all"
= stylesheet_link_tag "waiter/application"
= javascript_include_tag "vendor/modernizr"
= javascript_include_tag "waiter/application"
body
#ember-app-container
+1 -1
View File
@@ -2,5 +2,5 @@
span
nav.pagination
ul#list-history-container
script#list-history-template[type="text/html"]= mustache_template 'list-history'
script#list-history-template[type="text/html"]= mustache_template 'list_history'
- onload_javascript "Qstorage.page = 1; Quser.load_list_history()"
+1 -1
View File
@@ -76,7 +76,7 @@ module Qwaiter
config.active_support.escape_html_entities_in_json = true
config.handlebars.templates_root = %w[supplier/app/templates waiter/app/templates]
config.handlebars.templates_root = %w[supplier/app/templates waiter/app/templates user/app/templates]
config.generators do |g|
g.orm :simply_stored
+1
View File
@@ -32,6 +32,7 @@ en:
no_records: There are no items present
actions:
title: Actions
#depricated, moved to user
messages:
cannot_order_on_non_active_list: You cannot place an order on a closed list
no_active_list: There is no active list
+42 -1
View File
@@ -1,9 +1,30 @@
en:
user:
unknown_supplier_name: unknown
messages:
cannot_order_on_non_active_list: You cannot place an order on a closed list
no_active_list: There is no active list
order_is_placed: Your order has been received in good order
new_list_created: A new ${models.list} has been created
the_list_has_been_closed: The list has been closed
illegal_history_list_attempt: The list you want to access is not yours
table_not_found: The requested table cannot be found or is not given
table_is_occupied: The table you want to sit on is already occupied
table_is_reserved: The table you want to sit on is reserved by someone else
table_is_closed: The table you want to sit on is not available for service
supplier_is_closed: The owner of this table is currently not handling orders
join_request_rejected: Your request to join the table has been rejected
join_request_approved: Your request to join the table has been approved
table_is_from_other_supplier: You cannot move to another table when you have an open list
moved_to_another_table: You successfully moved to another table
cannot_identify_table: The application cannot determine the table number
unknown_supplier_name: unknown
unauthorized: Unauthorized action detected
active_list:
title: Active list
needs_payment: Check please!
list_products:
title: Order
history_list:
title: Closed list
list_history:
@@ -28,3 +49,23 @@ en:
title: Authenticate Qwaiter
obtain: Authenticate
invalid_combination: The email password combination is incorrect
move_table:
cannot_move_to_occupied_table: 'You cannot move to an occupied table'
moved_to_another_table: 'The table is changed.'
confirmation_title: 'Move to another table?'
confirmation_body: 'Do you want to move to another table?'
confirm:
cencel: Cancel
confirm: 'Yes'
list_needs_help:
help_is_on_its_way: 'Help is already on its way'
title: 'Request a waiter'
content: 'Request a waiter to your table'
list_needs_payment:
payment_already_requested: 'You already asked for the bill'
title: 'Ask for the check'
content: 'Do you want to pay?'
product_orders:
order_button: Order
total: Total
no_orders: No products
+41 -1
View File
@@ -1,9 +1,29 @@
nl:
user:
unknown_supplier_name: onbekend
messages:
cannot_order_on_non_active_list: Je kan niet bestellen op een gesloten lijst
no_active_list: Er is momenteel geen lijst actief
order_is_placed: Je bestelling is in goede orde aangekomen
new_list_created: Een nieuwe ${models.list} is aangemaakt
the_list_has_been_closed: De lijst is afgesloten
illegal_history_list_attempt: Je probeert een lijst op te vragen die niet van jou is
table_not_found: De gezochte tafel kan niet worden gevonden of is niet opgegeven
table_is_occupied: De tafel waar je aan wil gaan zitten is reeds bezet
table_is_reserved: De tafel waar je aan wil gaan zitten is gereserveerd
table_is_closed: De tafel waar je aan wil gaan zitten is niet beschikbaar voor bediening
supplier_is_closed: De eigenaar van deze tafel is momenteel gesloten
join_request_rejected: Je verzoek om te mogen bestellen op een bestaande lijst is afgewezen
join_request_approved: Je verzoek om te mogen bestellen op een bestaande lijst is goedgekeurd
table_is_from_other_supplier: Je kan geen lijst openen bij een andere zaak zolang je huidige lijst nog niet is afgesloten
moved_to_another_table: De tafel is gewijzigd
cannot_identify_table: De applicatie kan niet bepalen om welke tafel het gaat
unknown_supplier_name: onbekend
unauthorized: Niet toegestane actie
active_list:
title: Actieve lijst
needs_payment: Rekening vragen!
list_products:
title: Bestel
history_list:
title: Afgesloten lijst
list_history:
@@ -28,3 +48,23 @@ nl:
title: Aanmelden bij Qwaiter
obtain: Aanmelden
invalid_combination: De inloggegevens zijn onjuist
move_table:
cannot_move_to_occupied_table: 'Je kan niet verhuizen naar een tafel die reeds gebruikt wordt.'
moved_to_another_table: 'De tafel is gewijzigd.'
confirmation_title: 'Naar een andere tafel verhuizen?'
confirmation_body: 'Wil je aan een andere tafel gaan zitten?'
confirm:
cencel: Annuleer
confirm: 'Ja'
list_needs_help:
help_is_on_its_way: 'Er wordt al iemand naar je tafel gestuurd'
title: 'Ik heb een vraag'
content: 'Wil je een vraag stellen?'
list_needs_payment:
payment_already_requested: 'De rekening is reeds gevraagd'
title: 'Vraag om de rekening'
content: 'Wil je betalen?'
product_orders:
order_button: Bestel
total: Totaal
no_orders: Geen bestellingen
+2
View File
@@ -2,3 +2,5 @@ en:
waiter:
product_orders:
order_button: Order
total: Total
no_orders: No products
+2
View File
@@ -2,3 +2,5 @@ nl:
waiter:
product_orders:
order_button: Bestel
total: Totaal
no_orders: Geen bestellingen
+5 -1
View File
@@ -69,8 +69,12 @@ Qwaiter::Application.routes.draw do
get '/user/obtain_token' => 'user#obtain_token', as: :user_obtain_token
post '/user/obtain_token' => 'user#obtain_token', constraints: {format: :json}
namespace :users, path: '/user' do
resources :product_categories, only: [:index]
end
get '/supplier/suppliers/current' => 'supplier#current' #ember
#match '/show_products' => 'dashboard#show_products', as: :user_products
# GENERAL
@@ -6,6 +6,7 @@ end
step "another user scans the QR code on the table" do
step 'there is another signed in user user'
visit user_root_path
binding.pry
page.execute_script "Quser.actions_for_table({table_id: '#{@table.id}'})"
end
+6 -6
View File
@@ -125,29 +125,29 @@ describe List do
describe '#place_order' do
it 'returns an order object' do
list.place_order(user, product.id => 7).should be_a Order
list.place_order(product.id => 7, user: user).should be_a Order
end
it 'creates an order' do
expect{ list.place_order(user, product.id => 7) }.to change{ Order.count }.by(1)
expect{ list.place_order(product.id => 7, user: user) }.to change{ Order.count }.by(1)
end
describe 'broadcasting' do
it 'broadcasts to the user and the supplier the active order counter' do
list.place_order(user, product.id => 7)
list.place_order(product.id => 7, user: user)
expect{
list.place_order(user, product.id => 3)
list.place_order(product.id => 3, user: user)
}.to broadcast_to_user(user.id).message('orders_placed_count').with(count: 2)
expect{
list.place_order(user, product.id => 5)
list.place_order(product.id => 5, user: user)
}.to broadcast_to_supplier(supplier.id).message('orders_placed_count').with(count: 3)
end
end
it 'sets the list price as kind of caching' do
list.place_order(user, product.id => 7)
list.place_order(product.id => 7, user: user)
list.reload
list.price.should == 15.54
end