diff --git a/app/assets/javascripts/shared-ember-helpers/translation.js.coffee b/app/assets/javascripts/shared-ember-helpers/translation.js.coffee index b8f4bd46..14222eb9 100644 --- a/app/assets/javascripts/shared-ember-helpers/translation.js.coffee +++ b/app/assets/javascripts/shared-ember-helpers/translation.js.coffee @@ -1,4 +1,4 @@ Ember.Handlebars.registerHelper 't', (path, params..., options)-> text = t(path) - tag = if options.hash.bare then text else "#{text}" + tag = if options.hash.bare then text else "#{text}" new Handlebars.SafeString tag diff --git a/app/assets/javascripts/supplier/app/app.js.coffee b/app/assets/javascripts/supplier/app/app.js.coffee index 3c6bd8bd..d7d07acb 100644 --- a/app/assets/javascripts/supplier/app/app.js.coffee +++ b/app/assets/javascripts/supplier/app/app.js.coffee @@ -1,5 +1,6 @@ @App = Ember.Application.create LOG_TRANSITIONS: true + LOG_VIEW_LOOKUPS: true rootElement: '#ember-app-container' store: -> @__container__.lookup('controller:application').store @App.modals = Ember.Namespace.create() diff --git a/app/assets/javascripts/supplier/app/components/edit_currency_component.js.coffee b/app/assets/javascripts/supplier/app/components/edit_currency_component.js.coffee new file mode 100644 index 00000000..47af963d --- /dev/null +++ b/app/assets/javascripts/supplier/app/components/edit_currency_component.js.coffee @@ -0,0 +1,49 @@ +App.EditCurrencyComponent = Ember.Component.extend + classNames: ['edit-currency-container', 'row', 'collapse'] + classNameBindings: ['has_error:error'] + currencySymbol: '€' + has_error: false + placeholder: '0.00' + validatePresence: false + + inputValue: Ember.computed (key, value, previousValue)-> + key = "value" + if arguments.length > 1 + if value + # if typeof value is 'number' + # return_value = value.toPrecision() + # @set key, value + if typeof value is 'string' and value.match(/^[+-]?\d+(\.?\d?\d)?$/) + @set 'has_error', false + @set key, parseFloat(value) + else + @set 'has_error', true + + else + @set key, 0.0 # empty + @set 'has_error', false + return_value = value + return_value ||= @get key + @set 'has_error', true if @validatePresence and !return_value + return_value = return_value.toFixed(2) if typeof return_value is 'number' + return_value + + #didInsertElement: -> + #@addObserver "model.#{@attribute}", (attribute)=> + #if value = @get("model.#{@attribute}") + #@set 'value', value.toFixed(2) if parseFloat(@get('value')) isnt value + #else + #@set 'value', 0 + ## @set('value', @get("model.#{@attribute}").toFixed) + ## # dynamically observe the model's attribute + ## # if this changes outside the component's context, it is not + ## # observed by the computed property. model.@attribute is not (yet) working :) + ## current_value = @get 'value' + ## if value = @get("model.#{@attribute}") + ## string_value = value.toPrecision() + ## @set('value', string_value) if parseFloat(current_value) isnt @get("model.#{@attribute}") + #@addObserver "model.errors.#{@attribute}.length", (attribute)=> + #@set 'has_error', !!@get("model.errors.#{@attribute}.length") + #@model.validate().then => + #@set 'has_error', !!@get("model.errors.#{@attribute}.length") + diff --git a/app/assets/javascripts/supplier/app/components/menu_product_component.js.coffee b/app/assets/javascripts/supplier/app/components/menu_product_component.js.coffee new file mode 100644 index 00000000..2b32e3f7 --- /dev/null +++ b/app/assets/javascripts/supplier/app/components/menu_product_component.js.coffee @@ -0,0 +1,19 @@ +App.MenuProductComponent = Ember.Component.extend + editMode: false + actions: + makeEditable: -> @set('editMode', true) + save: -> + @get('product').save() if @get('product.isDirty') + @set 'editMode', false + destroyProduct: (product)-> + if product.get('isNew') + product.deleteRecord() + else + @get('targetObject').modal 'confirm', + model: product + title_path: 'product.destroy_confirmation' + ok: -> product.destroyRecord() + didInsertElement: -> + @set 'editMode', true if @get('product.isNew') + + #templateName: 'menu/product' diff --git a/app/assets/javascripts/supplier/app/components/sections_header_component.js.coffee b/app/assets/javascripts/supplier/app/components/sections_header_component.js.coffee new file mode 100644 index 00000000..126597a1 --- /dev/null +++ b/app/assets/javascripts/supplier/app/components/sections_header_component.js.coffee @@ -0,0 +1,8 @@ +App.SectionsHeaderComponent = Ember.Component.extend + sections: (-> @get('targetObject.store').all('section') ).property() + actions: + setSection: (section)-> + @$('dd').removeClass('active') + @$("[data-section=#{if section then section.id else 'all'}]").addClass('active') + @set('section', section) + didInsertElement: -> @send("setSection", @get('section')) diff --git a/app/assets/javascripts/supplier/app/controllers/menu_controller.js.coffee b/app/assets/javascripts/supplier/app/controllers/menu_controller.js.coffee index b03e1614..e0f03d01 100644 --- a/app/assets/javascripts/supplier/app/controllers/menu_controller.js.coffee +++ b/app/assets/javascripts/supplier/app/controllers/menu_controller.js.coffee @@ -16,3 +16,5 @@ App.MenuController = Ember.ObjectController.extend @modal 'product_category_new', model: @store.createRecord('product_category', position: @get('product_categories.length')) close: -> @get('model').deleteRecord() + addProduct: (product_category)-> + product_category.get('products').addObject @store.createRecord 'product', position: product_category.get('products.length') diff --git a/app/assets/javascripts/supplier/app/controllers/modals/product_category_move_controller.js.coffee b/app/assets/javascripts/supplier/app/controllers/modals/product_category_move_controller.js.coffee index 041cd885..c51fb9f8 100644 --- a/app/assets/javascripts/supplier/app/controllers/modals/product_category_move_controller.js.coffee +++ b/app/assets/javascripts/supplier/app/controllers/modals/product_category_move_controller.js.coffee @@ -13,17 +13,18 @@ App.modals.ProductCategoryMoveController = App.modals.BaseController.extend if below_product_category and product_category.id is below_product_category.id @set 'model.position', position += 1 @get('model').save() - position += 1 + @send 'close' - # moveBelow: (-> - # debugger - # ).observes('move_below_selection') - # change: -> - # debugger product_categories: (-> @store.all('product_category').filter( (pc) => pc.id isnt @get('model.id')).sortBy('position') ).property('model.id') - # setProductCategories: (-> @set 'product_categories', @store.all('product_category')).on('init') + sortableUpdate: (ids)-> + products = @get('model.products') + ids.forEach (id, i)-> + if product = products.findProperty('id', id) + #TODO: raise in frontend if product.get('isDirty'). Cannot modify position of product containing non persisted change + product.set('position', i) + product.save() if product.get('isDirty') diff --git a/app/assets/javascripts/supplier/app/controllers/orders_display_controller.js.coffee b/app/assets/javascripts/supplier/app/controllers/orders_display_controller.js.coffee index 34c1eaf7..5312f5c4 100644 --- a/app/assets/javascripts/supplier/app/controllers/orders_display_controller.js.coffee +++ b/app/assets/javascripts/supplier/app/controllers/orders_display_controller.js.coffee @@ -6,15 +6,3 @@ App.OrdersDisplayController = Ember.ObjectController.extend orders = @get('model').filterBy('needs_supplier_attention') orders = orders.filterBy('section.id', id) if id = @get('active_section.id') orders.sortBy('created_at') # Not reversed, oldest on top, start with oldest order first :-) Customer happyness - activeSectionDidChange: Ember.observer 'active_section.id', -> - container = Ember.$('.orders-display-nav') - container.find('dd').removeClass('active') - if id = @get('active_section.id') - container.find("[data-section='#{id}']").addClass 'active' - else - container.find('[data-section="all"]').addClass 'active' - actions: - clearActiveSection: -> - @set 'active_section', null - setActiveSection: (section)-> - @set 'active_section', section diff --git a/app/assets/javascripts/supplier/app/models/product.js.coffee b/app/assets/javascripts/supplier/app/models/product.js.coffee index 7ec7b997..b859c41b 100644 --- a/app/assets/javascripts/supplier/app/models/product.js.coffee +++ b/app/assets/javascripts/supplier/app/models/product.js.coffee @@ -2,5 +2,12 @@ attr = DS.attr App.Product = DS.Model.extend name: attr 'string' price: attr 'number' + code: attr 'string' + visible: attr('boolean', defaultValue: true) + position: attr('number', defaultValue: 0) product_category: DS.belongsTo('product_category') product_orders: DS.hasMany('product_order') + + code_or_empty: (-> + @get('code') or new Ember.Handlebars.SafeString(' ') + ).property('code') diff --git a/app/assets/javascripts/supplier/app/models/product_category.js.coffee b/app/assets/javascripts/supplier/app/models/product_category.js.coffee index 618fab8c..95e5076b 100644 --- a/app/assets/javascripts/supplier/app/models/product_category.js.coffee +++ b/app/assets/javascripts/supplier/app/models/product_category.js.coffee @@ -15,6 +15,8 @@ App.ProductCategory = DS.Model.extend end_on: attr('number') position: attr('number') + sorted_products: (-> @get('products').sortBy('position') ).property('products.@each.position') + availability_text: Ember.computed 'active_on_sunday', 'active_on_monday', 'active_on_tuesday', 'active_on_wednesday', 'active_on_thursday', 'active_on_friday', 'active_on_saturday', 'full_day', 'start_from', 'end_on', -> days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'] active_days = days.filter (day)=>@get("active_on_#{day}") diff --git a/app/assets/javascripts/supplier/app/templates/components/edit-currency.emblem b/app/assets/javascripts/supplier/app/templates/components/edit-currency.emblem new file mode 100644 index 00000000..c148420f --- /dev/null +++ b/app/assets/javascripts/supplier/app/templates/components/edit-currency.emblem @@ -0,0 +1,3 @@ +.large-2.columns: label.prefix= currencySymbol +.large-4.columns.end= input valueBinding="inputValue" placeholder=placeholder + diff --git a/app/assets/javascripts/supplier/app/templates/components/menu-product.emblem b/app/assets/javascripts/supplier/app/templates/components/menu-product.emblem new file mode 100644 index 00000000..45ddfe85 --- /dev/null +++ b/app/assets/javascripts/supplier/app/templates/components/menu-product.emblem @@ -0,0 +1,13 @@ +.row.menu-product-container + if editMode + .small-3.columns= input value=product.name + .small-3.columns= edit-currency value=product.price validatePresence=true + .small-3.columns= input value=product.code + .small-3.columns + a.destroy-product-action{action "destroyProduct" product}: span + a.save-product-action{action "save"}: span + else + .small-3.columns: span= product.name + .small-3.columns= currency product.price + .small-3.columns: span= product.code_or_empty + .small-3.columns: span.fa.fa-edit{action "makeEditable"} diff --git a/app/assets/javascripts/supplier/app/templates/components/sections-header.emblem b/app/assets/javascripts/supplier/app/templates/components/sections-header.emblem new file mode 100644 index 00000000..244ef4c6 --- /dev/null +++ b/app/assets/javascripts/supplier/app/templates/components/sections-header.emblem @@ -0,0 +1,7 @@ +dl.sections-header-container.sub-nav + dd data-section="all": a.section-header-title{action "setSection"} href="" = t 'sections_header.all_sections' + each s in sections + dd data-section=s.id + a.section-header-title{action "setSection" s} href="#" = s.title + = link-to "section" s.id class="section-jumper" + span.fa.fa-chevron-circle-right diff --git a/app/assets/javascripts/supplier/app/templates/form_elements/boolean_switch.emblem b/app/assets/javascripts/supplier/app/templates/form_elements/boolean_switch.emblem index 2eb5f2f5..fd22b59a 100644 --- a/app/assets/javascripts/supplier/app/templates/form_elements/boolean_switch.emblem +++ b/app/assets/javascripts/supplier/app/templates/form_elements/boolean_switch.emblem @@ -1,2 +1,2 @@ -Ember.Checkbox id=view.switchId checkedBinding="view.value" += input type="checkbox" id=view.switchId checkedBinding="view.value" label for=view.switchId diff --git a/app/assets/javascripts/supplier/app/templates/index.emblem b/app/assets/javascripts/supplier/app/templates/index.emblem index 63e27e9b..d8316676 100644 --- a/app/assets/javascripts/supplier/app/templates/index.emblem +++ b/app/assets/javascripts/supplier/app/templates/index.emblem @@ -1,7 +1,9 @@ += sections-header sectionBinding="controller.controllers.application.active_section" .page-header div.dashboard-section-selection - App.HomeSectionSelectorView selectionBinding="controller.controllers.application.active_section" content=controller.sections prompt=controllers.application.supplier.name - App.HomeSectionJumperView + /App.HomeSectionSelectorView selectionBinding="controller.controllers.application.active_section" content=controller.sections prompt=controllers.application.supplier.name + /= home-section-selector sectionBinding="controller.controllers.application.active_section" + /= view "home-section-jumper" if active_lists.length h3.dashboard-lists-header{action "toggleDashboardLists"} if show_lists diff --git a/app/assets/javascripts/supplier/app/templates/menu.emblem b/app/assets/javascripts/supplier/app/templates/menu.emblem index 53ee5970..165fa8f3 100644 --- a/app/assets/javascripts/supplier/app/templates/menu.emblem +++ b/app/assets/javascripts/supplier/app/templates/menu.emblem @@ -6,10 +6,9 @@ each product_category in sorted_product_categories span.title= product_category.name span.availability= product_category.availability_text a.edit-product-category-button{action "editProductCategory" product_category} href="#" - each product in product_category.products - .row - .small-4.columns= product.name - .small-8.columns= currency product.price + a.add-product-product-category-button{action "addProduct" product_category} href="#": span + each product in product_category.sorted_products + = menu-product product=product .row .small-12.columns a.button{action "newProductCategory"} href="#" = t 'product_category.new_button' diff --git a/app/assets/javascripts/supplier/app/templates/modals/product_category_form.emblem b/app/assets/javascripts/supplier/app/templates/modals/product_category_form.emblem index ac117209..a0c0f528 100644 --- a/app/assets/javascripts/supplier/app/templates/modals/product_category_form.emblem +++ b/app/assets/javascripts/supplier/app/templates/modals/product_category_form.emblem @@ -6,32 +6,32 @@ unless model.supplier.week_starts_on_monday .form-row .form-label.half= t 'date.day_name.sunday' - .form-field.half: App.BooleanSwitchView value=model.active_on_sunday + .form-field.half= view "boolean-switch" value=model.active_on_sunday .form-row .form-label.half= t 'date.day_name.monday' - .form-field.half: App.BooleanSwitchView value=model.active_on_monday + .form-field.half= view "boolean-switch" value=model.active_on_monday .form-row .form-label.half= t 'date.day_name.tuesday' - .form-field.half: App.BooleanSwitchView value=model.active_on_tuesday + .form-field.half= view "boolean-switch" value=model.active_on_tuesday .form-row .form-label.half= t 'date.day_name.wednesday' - .form-field.half: App.BooleanSwitchView value=model.active_on_wednesday + .form-field.half= view "boolean-switch" value=model.active_on_wednesday .form-row .form-label.half= t 'date.day_name.thursday' - .form-field.half: App.BooleanSwitchView value=model.active_on_thursday + .form-field.half= view "boolean-switch" value=model.active_on_thursday .form-row .form-label.half= t 'date.day_name.friday' - .form-field.half: App.BooleanSwitchView value=model.active_on_friday + .form-field.half= view "boolean-switch" value=model.active_on_friday .form-row .form-label.half= t 'date.day_name.saturday' - .form-field.half: App.BooleanSwitchView value=model.active_on_saturday + .form-field.half= view "boolean-switch" value=model.active_on_saturday if model.supplier.week_starts_on_monday .form-row .form-label.half= t 'date.day_name.sunday' - .form-field.half: App.BooleanSwitchView valueBinding=model.active_on_sunday + .form-field.half= view "boolean-switch" valueBinding=model.active_on_sunday .small-6.columns .row - .small-12.columns.text-center: App.BooleanButtonView value=model.full_day reverse=true text_path="product_category.modal.active_between.top" + .small-12.columns.text-center= view 'boolean-button' value=model.full_day reverse=true text_path="product_category.modal.active_between.top" unless model.full_day .row .small-12.columns= view "select-minute-of-day" value=model.start_from diff --git a/app/assets/javascripts/supplier/app/templates/modals/product_category_move.emblem b/app/assets/javascripts/supplier/app/templates/modals/product_category_move.emblem index 0c837c02..ea2645e2 100644 --- a/app/assets/javascripts/supplier/app/templates/modals/product_category_move.emblem +++ b/app/assets/javascripts/supplier/app/templates/modals/product_category_move.emblem @@ -10,4 +10,12 @@ each product_category in product_categories span.title= product_category.name span.availability= product_category.availability_text hr +h4=t 'product_category.modal.move.products.title' +hr +ul.sortable + each product in model.sorted_products + li.sortable-item-container data-sortable-id=product.id + span.handle + span= product.name +hr button.modal-close{action "close"}=t 'section.add_tables.modal.close_button' diff --git a/app/assets/javascripts/supplier/app/templates/orders_display.emblem b/app/assets/javascripts/supplier/app/templates/orders_display.emblem index bc9123cd..2e46c0f5 100644 --- a/app/assets/javascripts/supplier/app/templates/orders_display.emblem +++ b/app/assets/javascripts/supplier/app/templates/orders_display.emblem @@ -1,7 +1,5 @@ -dl.orders-display-nav.sub-nav - dd.active data-section="all": a.active{action "clearActiveSection"} href="" All - each section in sections - dd data-section=section.id: a{action "setActiveSection" section} href="#" = section.title += sections-header sectionBinding="section" +h2= section.title table.table thead tr diff --git a/app/assets/javascripts/supplier/app/views/home_section_jumper_view.js.coffee b/app/assets/javascripts/supplier/app/views/home_section_jumper_view.js.coffee deleted file mode 100644 index 4fba307f..00000000 --- a/app/assets/javascripts/supplier/app/views/home_section_jumper_view.js.coffee +++ /dev/null @@ -1,9 +0,0 @@ -App.HomeSectionJumperView = Ember.View.extend - tagName: 'a' - classNames: ['main-board-section-jumper'] - href: '#' - isVisible: (-> !!@get('controller.active_section.id') ).property('controller.active_section.id') - template: Ember.Handlebars.compile('') - click: (e)-> - e.preventDefault() - @get('controller').transitionToRoute 'section', @get('controller.active_section.id') diff --git a/app/assets/javascripts/supplier/app/views/home_section_selector_view.js.coffee b/app/assets/javascripts/supplier/app/views/home_section_selector_view.js.coffee deleted file mode 100644 index d4599d37..00000000 --- a/app/assets/javascripts/supplier/app/views/home_section_selector_view.js.coffee +++ /dev/null @@ -1,9 +0,0 @@ -HomeSectionOption = Ember.SelectOption.extend - select_label: (-> "- #{@get('content.title')}").property('content.title') - valueBinding: 'content.id' -App.HomeSectionSelectorView = Ember.Select.extend - classNames: 'section_selector' - optionView: HomeSectionOption - #valueBinding: 'controller.active_section_id' - optionValuePath: 'content.id' - optionLabelPath: 'select_label' diff --git a/app/assets/javascripts/supplier/app/views/modal_view.js.coffee b/app/assets/javascripts/supplier/app/views/modal_view.js.coffee index b778c9c8..ad3745f0 100644 --- a/app/assets/javascripts/supplier/app/views/modal_view.js.coffee +++ b/app/assets/javascripts/supplier/app/views/modal_view.js.coffee @@ -1,3 +1,13 @@ App.ModalView = Ember.View.extend layoutName: 'modals/layout' + didInsertElement: -> + sortable = $('.sortable') + controller = @get('controller') + if sortable.length + sortable.sortable + update: (e, ui)-> + ids = sortable.find('.sortable-item-container').map((i, el) -> $(el).data('sortableId')).toArray() + if callback = controller.sortableUpdate + callback.call(controller, ids) + diff --git a/app/assets/javascripts/supplier/foundation1/application.js.erb b/app/assets/javascripts/supplier/foundation1/application.js.erb index 41a6838b..67c9092e 100644 --- a/app/assets/javascripts/supplier/foundation1/application.js.erb +++ b/app/assets/javascripts/supplier/foundation1/application.js.erb @@ -2,6 +2,7 @@ //= require jquery //= require jquery_ujs //= require jquery-ui/sortable +//= require jquery.ui.touch-punch // require foundation FOUNDATION 5 JAVASCRIPT IMPLEMENTATIONS AND EMBER ARE NOT COMPATIBLE, FOUNDATION IS TOO SIMPLISTIC AT THE MOMENT AND DESTROYS DOM EVENTS //= require js-routes //= require moment diff --git a/app/assets/javascripts/translations.js.coffee.erb b/app/assets/javascripts/translations.js.coffee.erb index c9361244..789f9d29 100644 --- a/app/assets/javascripts/translations.js.coffee.erb +++ b/app/assets/javascripts/translations.js.coffee.erb @@ -23,8 +23,7 @@ # return translation in the form # Tafel -@tspan = (path, vars={})-> - "#{t(path)}" +@tspan = (path, vars={}) -> "#{t(path, vars)}" @t = (path, vars={}) -> diff --git a/app/assets/stylesheets/supplier/foundation1/_foundation_and_overrides.css.sass b/app/assets/stylesheets/supplier/foundation1/_foundation_and_overrides.css.sass index 26c09ae8..84582d61 100644 --- a/app/assets/stylesheets/supplier/foundation1/_foundation_and_overrides.css.sass +++ b/app/assets/stylesheets/supplier/foundation1/_foundation_and_overrides.css.sass @@ -1249,6 +1249,11 @@ $secondary-color: #d7d7d7 // CUSTOM VARIABLES @import 'foundation' + +// Prevent empty columns from collapsing +.column, .columns + min-height: 1px + $button-margin: rem-calc(10) $button-qr-code-color: #555 $button-index-color: $secondary-color diff --git a/app/assets/stylesheets/supplier/foundation1/components/_edit_currency.css.sass b/app/assets/stylesheets/supplier/foundation1/components/_edit_currency.css.sass new file mode 100644 index 00000000..2b2180e2 --- /dev/null +++ b/app/assets/stylesheets/supplier/foundation1/components/_edit_currency.css.sass @@ -0,0 +1,12 @@ +.edit-currency-container + input + text-align: right + &.error + input + border-color: $alert-color + color: darken($alert-color, 40%) + label.prefix + background-color: $alert-color + color: white + font-weight: bold + diff --git a/app/assets/stylesheets/supplier/foundation1/components/_modal.css.sass b/app/assets/stylesheets/supplier/foundation1/components/_modal.css.sass index 997a02b3..af3ba991 100644 --- a/app/assets/stylesheets/supplier/foundation1/components/_modal.css.sass +++ b/app/assets/stylesheets/supplier/foundation1/components/_modal.css.sass @@ -12,6 +12,13 @@ .modal-destroy +button($bg: $alert-color) margin-right: 8px + .sortable + list-style: none + .sortable-item-container + cursor: pointer + .handle + @extend .fa, .fa-arrows-v + margin-right: 10px .modal-alert color: $alert-color diff --git a/app/assets/stylesheets/supplier/foundation1/components/_product_categories.css.sass b/app/assets/stylesheets/supplier/foundation1/components/_product_categories.css.sass index fb897b4c..e0ff0bdf 100644 --- a/app/assets/stylesheets/supplier/foundation1/components/_product_categories.css.sass +++ b/app/assets/stylesheets/supplier/foundation1/components/_product_categories.css.sass @@ -1,3 +1,4 @@ +//DEPRICATED #product-category-list list-style: none li diff --git a/app/assets/stylesheets/supplier/foundation1/components/_products_menu.css.sass b/app/assets/stylesheets/supplier/foundation1/components/_products_menu.css.sass index a34e2f13..777ef1a0 100644 --- a/app/assets/stylesheets/supplier/foundation1/components/_products_menu.css.sass +++ b/app/assets/stylesheets/supplier/foundation1/components/_products_menu.css.sass @@ -1,3 +1,4 @@ +//DEPRICATED .row.product_category-container margin-bottom: 15px .product_category-header @@ -7,6 +8,7 @@ @extend .fa // @extend .fa-lg @extend .fa-arrows + margin-right: 8px .title font-weight: bold font-size: 1.3em @@ -22,6 +24,13 @@ @extend .fa @extend .fa-lg @extend .fa-edit + .add-product-product-category-button + float: right + margin-top: 1.2em + span + @extend .fa + @extend .fa-lg + @extend .fa-plus .product_category-move-row .availability font-size: 0.8em @@ -29,3 +38,18 @@ color: #444 .time-range color: rgb(39, 6, 121) + +.menu-product-container + .destroy-product-action + color: $alert-color + margin-right: 12px + span + @extend .fa + @extend .fa-lg + @extend .fa-trash + .save-product-action + color: $success-color + span + @extend .fa + @extend .fa-lg + @extend .fa-save diff --git a/app/assets/stylesheets/supplier/foundation1/components/_sections_header.css.sass b/app/assets/stylesheets/supplier/foundation1/components/_sections_header.css.sass new file mode 100644 index 00000000..39a3077b --- /dev/null +++ b/app/assets/stylesheets/supplier/foundation1/components/_sections_header.css.sass @@ -0,0 +1,13 @@ +.sections-header-container + .section-header-title + padding-right: 0.5em + color: #555 + .section-jumper + display: none + .active + .section-jumper + display: inline-block + +button($bg: #ddd) + margin: 0 + margin-left: 4px + padding: 0 5px diff --git a/app/controllers/suppliers/products_controller.rb b/app/controllers/suppliers/products_controller.rb index 69e8883f..2bb0782a 100644 --- a/app/controllers/suppliers/products_controller.rb +++ b/app/controllers/suppliers/products_controller.rb @@ -49,7 +49,7 @@ module Suppliers respond_to do |format| if @product.save format.html { redirect_to [:suppliers, :products], notice: t('action.create.successfull', model: Product.model_name.human) } - format.json { render json: @product, status: :created, location: @product } + format.json { render json: @product, status: :created } else format.html { render action: "new" } format.json { render json: @product.errors, status: :unprocessable_entity } @@ -65,7 +65,7 @@ module Suppliers respond_to do |format| if @product.update_attributes(product_params) format.html { redirect_to [:suppliers, :products], notice: t('action.update.successfull', model: Product.model_name.human) } - format.json { head :no_content } + format.json { render json: @product } else format.html { render action: "edit" } format.json { render json: @product.errors, status: :unprocessable_entity } @@ -95,7 +95,7 @@ module Suppliers private def product_params - params.require(:product).permit(:name, :code, :price, :description, :image, product_category_ids: []) + params.require(:product).permit(:name, :code, :price, :description, :image, :visible, :position, :product_category_id) end end end diff --git a/app/models/product.rb b/app/models/product.rb index 914d40a8..8579b570 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -7,9 +7,11 @@ class Product property :code property :price, type: Float property :description + property :visible, type: :boolean, default: true + property :position, type: Fixnum - #belongs_to :product_category - has_and_belongs_to_many :product_categories, storing_keys: false + belongs_to :product_category + #has_and_belongs_to_many :product_categories, storing_keys: false belongs_to :supplier # direct! category is an aid has_many :product_orders @@ -20,12 +22,16 @@ class Product validates :price, presence: true, numericality: true view :by_supplier_id_and_id, key: [:supplier_id, :_id] - after_save :persist_product_category_ids + #after_save :persist_product_category_ids - def product_category_ids=(ids) - @product_category_ids = ids.select(&:present?) - is_dirty - end + #def product_category_ids=(ids) + #@product_category_ids = ids.select(&:present?) + #is_dirty + #end + + #def product_category_id=(id) + #self.product_category_ids = [id] + #end property :image_file_name property :image_content_type @@ -38,24 +44,24 @@ class Product validates_attachment :image, content_type: {content_type: ["image/jpg", "image/jpeg", "image/png", "image/gif"]} -private +#private - def persist_product_category_ids - return unless defined?(@product_category_ids) # Do not do anything if nothing happened to this attribute - @product_category_ids ||= [] - existing_product_categories = product_categories + #def persist_product_category_ids + #return unless defined?(@product_category_ids) # Do not do anything if nothing happened to this attribute + #@product_category_ids ||= [] + #existing_product_categories = product_categories - # do nothing if nothing has changed - return if @product_category_ids == existing_product_categories.map(&:id) + ## do nothing if nothing has changed + #return if @product_category_ids == existing_product_categories.map(&:id) - # clear removed product categories - existing_product_categories.reject{|pc| @product_category_ids.include?(pc.id) }.each{|pc| pc.remove_product(self) } + ## clear removed product categories + #existing_product_categories.reject{|pc| @product_category_ids.include?(pc.id) }.each{|pc| pc.remove_product(self) } - # Add product to newly added product categories - database.load(@product_category_ids - existing_product_categories.map(&:id)).each do |product_category| - product_category.add_product(self) - end - end + ## Add product to newly added product categories + #database.load(@product_category_ids - existing_product_categories.map(&:id)).each do |product_category| + #product_category.add_product(self) + #end + #end end diff --git a/app/models/product_category.rb b/app/models/product_category.rb index 92a44fad..e86c3b12 100644 --- a/app/models/product_category.rb +++ b/app/models/product_category.rb @@ -17,7 +17,8 @@ class ProductCategory property :active_on_saturday, type: :boolean, default: true belongs_to :supplier - has_and_belongs_to_many :products, storing_keys: true + #has_and_belongs_to_many :products, storing_keys: true + has_many :products attr_protected :supplier_id diff --git a/app/serializers/product_serializer.rb b/app/serializers/product_serializer.rb index 5c68a99f..a658f134 100644 --- a/app/serializers/product_serializer.rb +++ b/app/serializers/product_serializer.rb @@ -1,6 +1,6 @@ class ProductSerializer < Qwaiter::Serializer embed :ids, include: true - attributes :name, :price, :description, :image + attributes :name, :price, :description, :image, :code, :position, :visible, :product_category_id def image if object.image.present? diff --git a/app/views/suppliers/application/_top_menu.html.slim b/app/views/suppliers/application/_top_menu.html.slim index 81279450..ce165435 100644 --- a/app/views/suppliers/application/_top_menu.html.slim +++ b/app/views/suppliers/application/_top_menu.html.slim @@ -3,10 +3,8 @@ header.top-menu span .menu-content section.main-buttons - //li= link_to t('supplier.menu.active_orders', orders: Order.model_name.human_plural), supplier_active_orders_path - //li= link_to t('supplier.menu.active_lists', lists: List.model_name.human_plural), supplier_active_lists_path = link_to image_tag('icons/logo-small.png'), supplier_root_path, class: 'top-menu-root' - = link_to 'Menu', suppliers_menu_path, class: 'top-menu-menu' + = link_to t('supplier.top_menu.menu'), "/supplier#/menu", class: 'top-menu-menu' = link_to ProductCategory.model_name.human_plural, suppliers_product_categories_path, data: {t: 'models.plural.product_category'}, class: 'top-menu-product_categories' = link_to Product.model_name.human_plural, suppliers_products_path, data: {t: 'models.plural.product'}, class: 'top-menu-products' = link_to Section.model_name.human_plural, "/supplier#/sections", data: {t: 'models.plural.section'}, class: 'top-menu-sections' diff --git a/config/locales/supplier.en.yml b/config/locales/supplier.en.yml index a70317ee..540b40ee 100644 --- a/config/locales/supplier.en.yml +++ b/config/locales/supplier.en.yml @@ -24,8 +24,8 @@ en: could_not_arrange_tables_distributed: 'The tables could not be arranged. Does the ${models.section|downcase} have a width and a height?' could_not_arrange_tables_by_row: 'The tables could not be arranged. Does the ${models.section|downcase} have a width and a height?' could_not_arrange_tables_by_column: 'The tables could not be arranged. Does the ${models.section|downcase} have a width and a height?' - menu: - active_lists: Active lists + top_menu: + menu: Menu active_lists: title: Active lists price: Price @@ -73,6 +73,8 @@ en: order: being_processed: 'In process!' being_served: 'Is delivered!' + sections_header: + all_sections: All section: first_section_title: Room show: @@ -157,9 +159,10 @@ en: title: Move ${models.product_category|downcase} body_header: '' move_to_top: Move to top - move_below_label: "Place below ${product_category|downcase}" + move_below_label: "Place below ${models.product_category|downcase}" product: new: 'New ${model.product|downcase}' + destroy_confirmation: Are you sure you want to delete ${models.product|downcase} %{name} preview: header: 'Select moment to preview products' description: 'Products visible to customers at chosen moment:' diff --git a/config/locales/supplier.nl.yml b/config/locales/supplier.nl.yml index 26510369..48129910 100644 --- a/config/locales/supplier.nl.yml +++ b/config/locales/supplier.nl.yml @@ -23,8 +23,8 @@ nl: could_not_arrange_tables_distributed: 'De ${models.plural.table} konden niet worden gepositioneerd. Heeft de ${models.section|downcase} een hoogte en breedte?' could_not_arrange_tables_by_row: 'De ${models.plural.table} konden niet worden gepositioneerd. Heeft de ${models.section|downcase} een hoogte en breedte?' could_not_arrange_tables_by_column: 'De ${models.plural.table} konden niet worden gepositioneerd. Heeft de ${models.section|downcase} een hoogte en breedte?' - menu: - active_lists: Actieve lijsten + top_menu: + menu: Menu active_lists: title: Actieve lijsten price: Prijs @@ -72,6 +72,8 @@ nl: order: being_processed: 'Ben bezig!' being_served: 'Ik kom het brengen!' + sections_header: + all_sections: Alles section: first_section_title: Ruimte show: @@ -139,7 +141,7 @@ nl: active_between: top: Actief tussen middle: en - destroy_confirm_text: 'Weet je zeker dat je ${models.product_category} %{name} wil verwijderen?' + destroy_confirm_text: 'Weet je zeker dat je ${models.product_category|downcase} %{name} wil verwijderen?' new: title: ${models.product_category} toevoegen body_header: '' @@ -158,9 +160,12 @@ nl: title: Verplaats ${models.product_category|downcase} body_header: '' move_to_top: Plaats bovenaan - move_below_label: "Plaats onder ${product_category|downcase}" + move_below_label: "Plaats onder ${models.product_category|downcase}" + products: + title: Sorteer ${models.plural.product|downcase} product: new: 'Nieuw ${model.product|downcase}' + destroy_confirmation: Weet je zeker dat je ${models.product|downcase} %{name} wilt verwijderen preview: header: 'Selecteer tijdstip voor voorbeeld' description: 'Producten op gekozen tijdstip:' diff --git a/vendor/assets/javascripts/jquery.ui.touch-punch.js b/vendor/assets/javascripts/jquery.ui.touch-punch.js new file mode 100644 index 00000000..16ce41d1 --- /dev/null +++ b/vendor/assets/javascripts/jquery.ui.touch-punch.js @@ -0,0 +1,180 @@ +/*! + * jQuery UI Touch Punch 0.2.3 + * + * Copyright 2011–2014, Dave Furfero + * Dual licensed under the MIT or GPL Version 2 licenses. + * + * Depends: + * jquery.ui.widget.js + * jquery.ui.mouse.js + */ +(function ($) { + + // Detect touch support + $.support.touch = 'ontouchend' in document; + + // Ignore browsers without touch support + if (!$.support.touch) { + return; + } + + var mouseProto = $.ui.mouse.prototype, + _mouseInit = mouseProto._mouseInit, + _mouseDestroy = mouseProto._mouseDestroy, + touchHandled; + + /** + * Simulate a mouse event based on a corresponding touch event + * @param {Object} event A touch event + * @param {String} simulatedType The corresponding mouse event + */ + function simulateMouseEvent (event, simulatedType) { + + // Ignore multi-touch events + if (event.originalEvent.touches.length > 1) { + return; + } + + event.preventDefault(); + + var touch = event.originalEvent.changedTouches[0], + simulatedEvent = document.createEvent('MouseEvents'); + + // Initialize the simulated mouse event using the touch event's coordinates + simulatedEvent.initMouseEvent( + simulatedType, // type + true, // bubbles + true, // cancelable + window, // view + 1, // detail + touch.screenX, // screenX + touch.screenY, // screenY + touch.clientX, // clientX + touch.clientY, // clientY + false, // ctrlKey + false, // altKey + false, // shiftKey + false, // metaKey + 0, // button + null // relatedTarget + ); + + // Dispatch the simulated event to the target element + event.target.dispatchEvent(simulatedEvent); + } + + /** + * Handle the jQuery UI widget's touchstart events + * @param {Object} event The widget element's touchstart event + */ + mouseProto._touchStart = function (event) { + + var self = this; + + // Ignore the event if another widget is already being handled + if (touchHandled || !self._mouseCapture(event.originalEvent.changedTouches[0])) { + return; + } + + // Set the flag to prevent other widgets from inheriting the touch event + touchHandled = true; + + // Track movement to determine if interaction was a click + self._touchMoved = false; + + // Simulate the mouseover event + simulateMouseEvent(event, 'mouseover'); + + // Simulate the mousemove event + simulateMouseEvent(event, 'mousemove'); + + // Simulate the mousedown event + simulateMouseEvent(event, 'mousedown'); + }; + + /** + * Handle the jQuery UI widget's touchmove events + * @param {Object} event The document's touchmove event + */ + mouseProto._touchMove = function (event) { + + // Ignore event if not handled + if (!touchHandled) { + return; + } + + // Interaction was not a click + this._touchMoved = true; + + // Simulate the mousemove event + simulateMouseEvent(event, 'mousemove'); + }; + + /** + * Handle the jQuery UI widget's touchend events + * @param {Object} event The document's touchend event + */ + mouseProto._touchEnd = function (event) { + + // Ignore event if not handled + if (!touchHandled) { + return; + } + + // Simulate the mouseup event + simulateMouseEvent(event, 'mouseup'); + + // Simulate the mouseout event + simulateMouseEvent(event, 'mouseout'); + + // If the touch interaction did not move, it should trigger a click + if (!this._touchMoved) { + + // Simulate the click event + simulateMouseEvent(event, 'click'); + } + + // Unset the flag to allow other widgets to inherit the touch event + touchHandled = false; + }; + + /** + * A duck punch of the $.ui.mouse _mouseInit method to support touch events. + * This method extends the widget with bound touch event handlers that + * translate touch events to mouse events and pass them to the widget's + * original mouse event handling methods. + */ + mouseProto._mouseInit = function () { + + var self = this; + + // Delegate the touch handlers to the widget's element + self.element.bind({ + touchstart: $.proxy(self, '_touchStart'), + touchmove: $.proxy(self, '_touchMove'), + touchend: $.proxy(self, '_touchEnd') + }); + + // Call the original $.ui.mouse init method + _mouseInit.call(self); + }; + + /** + * Remove the touch event handlers + */ + mouseProto._mouseDestroy = function () { + + var self = this; + + // Delegate the touch handlers to the widget's element + self.element.unbind({ + touchstart: $.proxy(self, '_touchStart'), + touchmove: $.proxy(self, '_touchMove'), + touchend: $.proxy(self, '_touchEnd') + }); + + // Call the original $.ui.mouse destroy method + _mouseDestroy.call(self); + }; + +})(jQuery); \ No newline at end of file