diff --git a/app/assets/images/.DS_Store b/app/assets/images/.DS_Store index 21735e3d..d82591a5 100644 Binary files a/app/assets/images/.DS_Store and b/app/assets/images/.DS_Store differ diff --git a/app/assets/images/icons/needs-help.png b/app/assets/images/icons/needs-help.png new file mode 100644 index 00000000..d1de25c3 Binary files /dev/null and b/app/assets/images/icons/needs-help.png differ diff --git a/app/assets/images/icons/needs-help.svg b/app/assets/images/icons/needs-help.svg new file mode 100644 index 00000000..e496c95a --- /dev/null +++ b/app/assets/images/icons/needs-help.svg @@ -0,0 +1,72 @@ + + + + + + + + + + image/svg+xml + + + + + + + ? + diff --git a/app/assets/images/icons/needs-payment.png b/app/assets/images/icons/needs-payment.png new file mode 100644 index 00000000..8c595c3d Binary files /dev/null and b/app/assets/images/icons/needs-payment.png differ diff --git a/app/assets/images/icons/needs-payment.svg b/app/assets/images/icons/needs-payment.svg new file mode 100644 index 00000000..80b2469c --- /dev/null +++ b/app/assets/images/icons/needs-payment.svg @@ -0,0 +1,72 @@ + + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/app/assets/javascripts/supplier/jquery.ui.touch-punch.min.js b/app/assets/javascripts/supplier/jquery.ui.touch-punch.min.js new file mode 100644 index 00000000..33d6f97e --- /dev/null +++ b/app/assets/javascripts/supplier/jquery.ui.touch-punch.min.js @@ -0,0 +1,11 @@ +/* + * jQuery UI Touch Punch 0.2.2 + * + * Copyright 2011, Dave Furfero + * Dual licensed under the MIT or GPL Version 2 licenses. + * + * Depends: + * jquery.ui.widget.js + * jquery.ui.mouse.js + */ +(function(b){b.support.touch="ontouchend" in document;if(!b.support.touch){return;}var c=b.ui.mouse.prototype,e=c._mouseInit,a;function d(g,h){if(g.originalEvent.touches.length>1){return;}g.preventDefault();var i=g.originalEvent.changedTouches[0],f=document.createEvent("MouseEvents");f.initMouseEvent(h,true,true,window,1,i.screenX,i.screenY,i.clientX,i.clientY,false,false,false,false,0,null);g.target.dispatchEvent(f);}c._touchStart=function(g){var f=this;if(a||!f._mouseCapture(g.originalEvent.changedTouches[0])){return;}a=true;f._touchMoved=false;d(g,"mouseover");d(g,"mousemove");d(g,"mousedown");};c._touchMove=function(f){if(!a){return;}this._touchMoved=true;d(f,"mousemove");};c._touchEnd=function(f){if(!a){return;}d(f,"mouseup");d(f,"mouseout");if(!this._touchMoved){d(f,"click");}a=false;};c._mouseInit=function(){var f=this;f.element.bind("touchstart",b.proxy(f,"_touchStart")).bind("touchmove",b.proxy(f,"_touchMove")).bind("touchend",b.proxy(f,"_touchEnd"));e.call(f);};})(jQuery); \ No newline at end of file diff --git a/app/assets/javascripts/user/application.js b/app/assets/javascripts/user/application.js index 77b520b3..4278916b 100644 --- a/app/assets/javascripts/user/application.js +++ b/app/assets/javascripts/user/application.js @@ -39,6 +39,20 @@ var translations = { confirmations: { move_to_another_table_title: 'Move to another table?', move_to_another_table: 'Are you sure you want to move to another table?' + }, + 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 check', + title: 'Ask for the check', + content: 'Do you want to pay?' + }, + selected_products: { + order: 'Order', + clear: 'Clear' } } function redirect_to(mapping, variables){ diff --git a/app/assets/javascripts/user/quser.js.coffee b/app/assets/javascripts/user/quser.js.coffee index 258c29b2..31644e8c 100644 --- a/app/assets/javascripts/user/quser.js.coffee +++ b/app/assets/javascripts/user/quser.js.coffee @@ -1,6 +1,7 @@ +data_host = '' window.Quser= handle_active_list: (callback) -> - $.get('/user/list_info.json', (res) -> + $.get(data_host + '/user/list_info.json', (res) -> if !res.list_active window.location = '/user?list_closed=true' return @@ -14,8 +15,8 @@ window.Quser= $('.table-number').text(response.table_number) if response.supplier_name $('.supplier-name').text(response.supplier_name) - Quser.list_needs_payment_default_action() - Quser.list_needs_help_default_action() + Quser.list_needs_payment_default_action(response) + Quser.list_needs_help_default_action(response) # join_request_active is to ensure that there are no more modals loaded when one is still active # console.log('join_request_active=' + window.join_request_active) if !window.join_request_active && response.join_requests && response.join_requests.length @@ -24,11 +25,11 @@ window.Quser= wrapper = $('') join_callback = ( (request)-> -> - $.post('/user/approve_join_request', {user_id: request.user_id}, -> window.join_request_active = false; wrapper.modal('hide') ) + $.post(data_host + '/user/approve_join_request', {user_id: request.user_id}, -> window.join_request_active = false; wrapper.modal('hide') ) )(join_request) reject_callback = ( (request)-> -> - $.post('/user/reject_join_request', {user_id: request.user_id}, -> window.join_request_active = false; wrapper.modal('hide' )) + $.post(data_host + '/user/reject_join_request', {user_id: request.user_id}, -> window.join_request_active = false; wrapper.modal('hide' )) )(join_request) header = $('') .append('') @@ -45,28 +46,58 @@ window.Quser= .appendTo(wrapper) wrapper.modal() - list_needs_help_default_action: -> + list_needs_help_default_action: (response)-> + response ||= window.active_list needs_help_container = $('#list-needs-help-button') if needs_help_container.length - if window.active_list.needs_help - needs_help_container.html('') + if response.needs_help + needs_help_container.data('needs-help', true) + needs_help_container.addClass('active') else - needs_help_container.html($('').click(Quser.list_needs_help)) #TODO TEXT + needs_help_container.data('needs-help', false) + needs_help_container.removeClass('active') + unless needs_help_container.data('click-initialized') + needs_help_container.click( -> + if $(this).data('needs-help') + Qwaiter.alert(t('list_needs_help.help_is_on_its_way')) + else + Qwaiter.confirm( + title: t('list_needs_help.title') + content: t('list_needs_help.content') + ok: Quser.list_needs_help + ) + ) + needs_help_container.data('click-initialized', true) list_needs_help: -> return unless window.active_list && !window.active_list.needs_help - $.post('/user/needs_help.json', (res) -> window.active_list = res; Quser.list_needs_help_default_action()) - list_needs_payment_default_action: -> + $.post(data_host + '/user/needs_help.json', (res) -> window.active_list = res; Quser.list_needs_help_default_action(res)) + list_needs_payment_default_action: (response)-> + response ||= window.active_list needs_payment_container = $('#list-needs-payment-button') if needs_payment_container.length - if window.active_list.needs_payment - needs_payment_container.html('') + if response.needs_payment + needs_payment_container.data('needs-payment', true) + needs_payment_container.addClass('active') else - needs_payment_container.html($('').click(Quser.list_needs_payment)) #TODO TEXT + needs_payment_container.data('needs-payment', false) + needs_payment_container.removeClass('active') + unless needs_payment_container.data('click-initialized') + needs_payment_container.click( -> + if $(this).data('needs-payment') + Qwaiter.alert(t('list_needs_payment.payment_already_requested')) + else + Qwaiter.confirm( + title: t('list_needs_payment.title') + content: t('list_needs_payment.content') + ok: Quser.list_needs_payment + ) + ) + needs_payment_container.data('click-initialized', true) list_needs_payment: -> return unless window.active_list && !window.active_list.needs_payment - $.post('/user/list_needs_payment.json', (res) -> window.active_list = res; Quser.list_needs_payment_default_action()) + $.post(data_host + '/user/list_needs_payment.json', (res) -> window.active_list = res; Quser.list_needs_payment_default_action(res)) load_active_list: () -> - $.get('/user/active_list.json', (res) -> + $.get(data_host + '/user/active_list.json', (res) -> window.active_list = res unless res.list_active window.location = '/user/list_history/'+res._id + '?list_closed=true' @@ -77,7 +108,7 @@ window.Quser= Quser.build_list_table(body, foot, res) ) load_history_list: (list_id) -> - $.get('/user/list_history/'+list_id+'.json', (res) -> + $.get(data_host + '/user/list_history/'+list_id+'.json', (res) -> body = $('#history-list-table tbody') foot = $('#history-list-table tfoot') Quser.build_list_table(body, foot, res) @@ -105,7 +136,7 @@ window.Quser= h ||= {} for product_id, info of window.active_products_list h['products['+product_id+']'] = info.number - $.post('/user/order_selected_products', h, ((res) -> Quser.handle_response(res)), 'json') + $.post(data_host + '/user/order_selected_products', h, ((res) -> Quser.handle_response(res)), 'json') handle_response: (res) -> if(typeof(res) == 'string') return unless res.length @@ -138,7 +169,7 @@ window.Quser= load_table_products: (table_id, occupied)-> Quser.populate_products_table('/user/list_products_for_table.json?table_id='+table_id, !occupied) populate_products_table: (src, include_order_buttons)-> - $.get(src, (res) -> + $.get(data_host + src, (res) -> body = $('#products-table tbody') if res.table_number $('.table-number').text(res.table_number) @@ -182,7 +213,7 @@ window.Quser= ) actions_for_table: (table)-> table = JSON.parse(table) if typeof(table) == 'string' - $.get('/user/table_info.json?table_id='+table.table_id, (res)-> + $.get(data_host + '/user/table_info.json?table_id='+table.table_id, (res)-> if res.current_table_id if res.other_supplier redirect_to 'user_root', {message: 'table_is_from_other_supplier'} @@ -202,7 +233,7 @@ window.Quser= #TODO Offer to move table Qwaiter.confirm( ok: -> - $.post('/user/move_table', {table_id: table.table_id}, (res2)-> + $.post(data_host + '/user/move_table', {table_id: table.table_id}, (res2)-> if res2.occupied alert('Cannot move to occupied table') else @@ -219,7 +250,7 @@ window.Quser= else if res.supplier_closed redirect_to 'user_root', {message: 'supplier_is_closed'} else - #$.post('/user/create_list.json', {table_id: table.table_id}, (res)-> Quser.handle_response(res)) + #$.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} , 'json') @@ -229,10 +260,10 @@ window.Quser= cont.html('') cont.append $('') cont.append $('

Waiting for approval of the person on this table

') - $.post('/user/join_occupied_table', {table_id: table_id}) + $.post(data_host + '/user/join_occupied_table', {table_id: table_id}) setInterval('Quser.check_if_can_join_occupied_table("'+table_id+'")', 7500) check_if_can_join_occupied_table: (table_id)-> - $.post('/user/check_table_join_status', {table_id: table_id}, (res) -> + $.post(data_host + '/user/check_table_join_status', {table_id: table_id}, (res) -> res ||= {} if res.approved redirect_to 'list_products' diff --git a/app/assets/stylesheets/_constants.css.sass b/app/assets/stylesheets/_constants.css.sass index be864f30..919522a7 100644 --- a/app/assets/stylesheets/_constants.css.sass +++ b/app/assets/stylesheets/_constants.css.sass @@ -1,3 +1,4 @@ $qbrown: #634227 +$qbrown-active: lighten($qbrown, 20%) $wood: image-url('textures/wood001-vertical.jpg') $background-brown: #57351f diff --git a/app/assets/stylesheets/supplier/section_tables.css.sass b/app/assets/stylesheets/supplier/section_tables.css.sass index a94e368b..c7c91bd1 100644 --- a/app/assets/stylesheets/supplier/section_tables.css.sass +++ b/app/assets/stylesheets/supplier/section_tables.css.sass @@ -1,9 +1,10 @@ +$table-width: 75px .section-tables-container .section-table background-color: #ccc height: 44px background-repeat: no-repeat - width: 45px + width: $table-width color: black a color: black @@ -33,7 +34,9 @@ &.needs_help background-color: #7f7 &.needs_payment - background-color: #f77 + background-image: image-url('icons/needs-payment.png') + background-position: 40px 2px + //background-color: #f77 a &:hover text-decoration: none @@ -43,3 +46,11 @@ margin-bottom: 30px float: left margin-right: 30px +// INDEX + +table + td + &.table-info + .table-count + &:after + content: " - " diff --git a/app/assets/stylesheets/user/structure.css.sass b/app/assets/stylesheets/user/structure.css.sass index c25c3724..7ae7fe3a 100644 --- a/app/assets/stylesheets/user/structure.css.sass +++ b/app/assets/stylesheets/user/structure.css.sass @@ -1,7 +1,24 @@ @import compass @import constants $side-spacing: 5px - +=user-button + cursor: pointer + border: 2px solid black + +border-radius(4px) + +box-shadow(#000 4px 4px 8px) + width: 34px + height: 34px + display: block + float: left + margin-left: 15px + background-color: $qbrown + text-align: center + line-height: 34px + background-repeat: no-repeat + background-position: center center + text-indent: -5000px + &.active + background-color: $qbrown-active html background-image: $wood @@ -42,20 +59,7 @@ body background-color: green width: 262px a - display: block - border: 2px solid black - +border-radius(4px) - +box-shadow(#000 4px 4px 8px) - width: 34px - height: 34px - float: left - margin-left: 15px - background-color: $qbrown - text-align: center - line-height: 34px - background-repeat: no-repeat - background-position: center center - text-indent: -5000px + +user-button &.home-list-link background-image: image-url('icons/list.png') &.home-menu-link @@ -91,3 +95,13 @@ body margin-left: 5px i padding-left: 5px +.user-top-button + +user-button + &#show-active-list + background-image: image-url('icons/list.png') + &#place-order-on-list + background-image: image-url('icons/menu.png') + &#list-needs-payment-button + background-image: image-url('icons/needs-payment.png') + &#list-needs-help-button + background-image: image-url('icons/needs-help.png') diff --git a/app/controllers/suppliers/sections_controller.rb b/app/controllers/suppliers/sections_controller.rb index 863e5960..77060587 100644 --- a/app/controllers/suppliers/sections_controller.rb +++ b/app/controllers/suppliers/sections_controller.rb @@ -5,6 +5,9 @@ module Suppliers # GET /sections.json def index @sections = current_supplier.sections + @sections.include_relation(:table) + @active_lists = current_supplier.active_lists + @active_table_ids = @active_lists.map(&:table_id).compact respond_to do |format| format.html # index.html.erb diff --git a/app/models/list.rb b/app/models/list.rb index efc143b7..8d48925d 100644 --- a/app/models/list.rb +++ b/app/models/list.rb @@ -20,12 +20,18 @@ class List view :by_supplier_id_and_id, key: [:supplier_id, :_id] - view :active_by_table_id, type: :custom, map_function: %|function(doc){ + view :active_by_table_id_view, type: :custom, map_function: %|function(doc){ if(doc.ruby_class == 'List' && doc.state == 'active'){ emit(doc.table_id, 1); } }|, reduce_function: '_sum' + view :active_by_supplier_id_view, type: :custom, map_function: %|function(doc){ + if(doc.ruby_class == 'List' && doc.state == 'active'){ + emit(doc.supplier_id, 1); + } + }|, reduce_function: '_sum' + view :active_by_section_id_view, type: :custom, map_function: %|function(doc){ if(doc.ruby_class == 'List' && doc.state == 'active' && doc.section_id){ emit(doc.section_id, 1); @@ -40,6 +46,10 @@ class List } }|, reduce_function: '_sum' + def self.active + database.view(active_by_supplier_id_view(reduce: false, include_docs: true)) + end + # Create, a list given a table and a user def self.from_table table, user return if user.has_active_list? @@ -51,10 +61,18 @@ class List list end + def self.active_for_supplier(supplier_id, options = {}) + database.view(active_by_supplier_id_view(key: supplier_id, reduce: false, include_docs: true)) + end + def self.active_for_section(section_id, options = {}) database.view(active_by_section_id_view(key: section_id, reduce: false, include_docs: true)) end + def self.active_for_table(table_id, options = {}) + database.view(active_by_table_id_view(options.reverse_merge(key: table_id, reduce: false, include_docs: true))) + end + def self.for_user(user, options = {}) with_pagination_options(options) do |options| database.view(for_user_view({startkey: ["#{user.id}\u9999"], endkey: [user.id], include_docs: true, reduce: false, descending: true}.merge(options))) diff --git a/app/models/supplier.rb b/app/models/supplier.rb index 2d05db7f..9423a543 100644 --- a/app/models/supplier.rb +++ b/app/models/supplier.rb @@ -33,11 +33,10 @@ class Supplier end def active_lists(options = {}) - return @active_lists if @active_lists - @tables ||= active_tables(options) - @tables.include_relation(:lists) - @active_lists = @tables.map(&:lists).flatten.select(&:active?) + return @active_lists if @active_lists.present? + @active_lists = List.active_for_supplier(id) @active_lists.include_relations(table: :section, orders: {product_orders: :product}) + @active_lists end diff --git a/app/models/table.rb b/app/models/table.rb index 8a7a4fe4..13eba074 100644 --- a/app/models/table.rb +++ b/app/models/table.rb @@ -18,11 +18,11 @@ class Table def occupied? return @is_occupied if instance_variable_defined?(:'@is_occupied') - @is_occupied = !self.class.database.view(List.active_by_table_id(key: id, reduce: true)).zero? + @is_occupied = !self.class.database.view(List.active_by_table_id_view(key: id, reduce: true)).zero? end def active_list - @active_list ||= self.class.database.view(List.active_by_table_id(key: id, include_docs: true, reduce: false, limit: 1)).try(:first) + @active_list ||= self.class.database.view(List.active_by_table_id_view(key: id, include_docs: true, reduce: false, limit: 1)).try(:first) end def reserved? diff --git a/app/views/layouts/phone.html.slim b/app/views/layouts/phone.html.slim index b4a27607..c7336ee4 100644 --- a/app/views/layouts/phone.html.slim +++ b/app/views/layouts/phone.html.slim @@ -58,3 +58,7 @@ html lang="en" /! Placed at the end of the document so the pages load faster = javascript_include_tag "user/application" = yield :footer + javascript: + $(function(){ + $('[data-t]').each(function(){$(this).text(t($(this).attr('data-t')))}) + }); diff --git a/app/views/suppliers/sections/index.html.slim b/app/views/suppliers/sections/index.html.slim index ecf4f15f..d45bdd9e 100644 --- a/app/views/suppliers/sections/index.html.slim +++ b/app/views/suppliers/sections/index.html.slim @@ -6,6 +6,7 @@ thead tr th.link= model_class.human_attribute_name(:title) + th.table-info= Table.model_name.human_plural th.numeric= model_class.human_attribute_name(:width) th.numeric= model_class.human_attribute_name(:height) th.timestamp= model_class.human_attribute_name(:created_at) @@ -14,10 +15,15 @@ - @sections.each do |section| tr td.link= link_to section.title, [:suppliers, section] + td.table-info + span.table-count= section.tables.size + span.active-table-count= (@active_table_ids & section.tables.map(&:id)).size td.numeric= section.width td.numeric= section.height td.timestamp=l section.created_at, format: :short td.actions + = link_to t('supplier.section.tables_view'), [:tables_view, :suppliers, section], class: [:btn, 'btn-mini btn-info'] + ' = link_to t('helpers.links.edit'), [:edit, :suppliers, section], class: 'btn btn-mini' ' = link_to t("helpers.links.destroy"), [:suppliers, section], method: :delete, data: {confirm: are_you_sure? }, class: 'btn btn-mini btn-danger' diff --git a/app/views/user/active_list.html.slim b/app/views/user/active_list.html.slim index 2143c993..d546f347 100644 --- a/app/views/user/active_list.html.slim +++ b/app/views/user/active_list.html.slim @@ -3,9 +3,9 @@ .supplier-name h4= t('user.active_list.title', list: List.model_name.human) .form-actions - = link_to t('helpers.links.place_order'), user_list_products_path, class: ['btn btn-primary'] - span#list-needs-payment-button - span#list-needs-help-button + = link_to t('helpers.links.place_order'), user_list_products_path, class: ['user-top-button'], id: 'place-order-on-list' + span#list-needs-payment-button.user-top-button + span#list-needs-help-button.user-top-button .well table#active-list-table.table thead diff --git a/app/views/user/list_products.html.slim b/app/views/user/list_products.html.slim index 4c5d5f36..22f0c22b 100644 --- a/app/views/user/list_products.html.slim +++ b/app/views/user/list_products.html.slim @@ -3,8 +3,9 @@ .supplier-name h4= t('user.show_products.title', products: Product.model_name.human_plural) .form-actions - = link_to t('helpers.links.show_active_list', list: List.model_name.human), user_active_list_path, class: ['btn btn'] - span#list-needs-help-button + = link_to t('helpers.links.show_active_list', list: List.model_name.human), user_active_list_path, class: ['user-top-button'], id: 'show-active-list' + span#list-needs-payment-button.user-top-button + span#list-needs-help-button.user-top-button .well table#products-table.table tbody @@ -21,9 +22,9 @@ tfoot tr td colspan=2 - button class="btn btn-primary" onClick="Quser.handle_active_list(function(){Quser.order_selected_products()})" Bestellen + button class="btn btn-primary" onClick="Quser.handle_active_list(function(){Quser.order_selected_products()})" data-t="selected_products.order"= t('selected_products.order') |  - button class="btn btn btn-warning" onClick="Quser.clear_selected_products()" Clear + button class="btn btn btn-warning" onClick="Quser.clear_selected_products()" data-t="selected_products.clear"= t('selected_products.clear') td.currency strong#active-order-total td diff --git a/config/locales/en.yml b/config/locales/en.yml index 4a88d09d..0effb3cf 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -110,3 +110,7 @@ en: boolean: boolean_yes: "Yes" boolean_no: "No" +# FOLLOWING ARE BACKED BY DATA ATTRIBUTES + selected_products: + clear: Clear + order: Order