diff --git a/app/assets/javascripts/admin/spree/orders/shipments.js.erb b/app/assets/javascripts/admin/spree/orders/shipments.js.erb index 75fcdcb93a..d122365e3d 100644 --- a/app/assets/javascripts/admin/spree/orders/shipments.js.erb +++ b/app/assets/javascripts/admin/spree/orders/shipments.js.erb @@ -43,6 +43,9 @@ $(document).ready(function() { $('a.edit-tracking').click(toggleTrackingEdit); $('a.cancel-tracking').click(toggleTrackingEdit); + $('a.edit-note.icon-edit').click(toggleNoteEdit); + $('a.cancel-note').click(toggleNoteEdit); + handle_tracking_save = function(){ var link = $(this); var shipment_number = link.data('shipment-number'); @@ -59,5 +62,23 @@ $(document).ready(function() { console.log(msg); }); } + + handle_note_save = function(){ + var link = $(this); + var note = link.parents('tbody').find('#note').val(); + var url = Spree.url( Spree.routes.orders_api + "/" + order_number); + + $.ajax({ + type: "PUT", + url: url, + data: { note: note } + }).done(function( msg ) { + window.location.reload(); + }).error(function( msg ) { + console.log(msg); + }); + } + $('[data-hook=admin_order_edit_form] a.save-tracking').click(handle_tracking_save); + $('[data-hook=admin_order_edit_form] a.save-note').click(handle_note_save); }); diff --git a/app/assets/javascripts/admin/spree/orders/variant_autocomplete.js.erb b/app/assets/javascripts/admin/spree/orders/variant_autocomplete.js.erb index ea46231ece..094889d20a 100644 --- a/app/assets/javascripts/admin/spree/orders/variant_autocomplete.js.erb +++ b/app/assets/javascripts/admin/spree/orders/variant_autocomplete.js.erb @@ -145,6 +145,12 @@ toggleTrackingEdit = function(){ link.parents('tbody').find('tr.show-tracking').toggle(); } +toggleNoteEdit = function(){ + var link = $(this); + link.parents('tbody').find('tr.edit-note').toggle(); + link.parents('tbody').find('tr.show-note').toggle(); +} + toggleMethodEdit = function(){ var link = $(this); link.parents('tbody').find('tr.edit-method').toggle(); diff --git a/app/controllers/api/v0/orders_controller.rb b/app/controllers/api/v0/orders_controller.rb index 81e425d571..3f23a32e40 100644 --- a/app/controllers/api/v0/orders_controller.rb +++ b/app/controllers/api/v0/orders_controller.rb @@ -26,6 +26,13 @@ module Api } end + def update + authorize! :admin, order + + order.update!(order_params) + render json: order, serializer: Api::OrderDetailedSerializer, current_order: order + end + def ship authorize! :admin, order @@ -72,6 +79,10 @@ module Api includes(line_items: { variant: [:product, :stock_items, :default_price] }). first! end + + def order_params + params.permit(:note) + end end end end diff --git a/app/views/spree/admin/orders/_form.html.haml b/app/views/spree/admin/orders/_form.html.haml index 0e801912fd..4ec49a8d95 100644 --- a/app/views/spree/admin/orders/_form.html.haml +++ b/app/views/spree/admin/orders/_form.html.haml @@ -6,6 +6,7 @@ = render 'spree/admin/orders/insufficient_stock_lines', insufficient_stock_lines: @order.insufficient_stock_lines = render :partial => "spree/admin/orders/shipment", :collection => @order.shipments, :locals => { :order => order } + = render partial: "spree/admin/orders/note", locals: { order: @order } = render :partial => "spree/admin/orders/_form/adjustments", :locals => { :adjustments => @order.line_item_adjustments, :title => t(".line_item_adjustments")} = render :partial => "spree/admin/orders/_form/adjustments", :locals => { :adjustments => order_adjustments_for_display(@order), :title => t(".order_adjustments")} diff --git a/app/views/spree/admin/orders/_note.html.haml b/app/views/spree/admin/orders/_note.html.haml new file mode 100644 index 0000000000..d5b2df2d38 --- /dev/null +++ b/app/views/spree/admin/orders/_note.html.haml @@ -0,0 +1,23 @@ +%table.index.edit-note-table + %tr.edit-note.hidden.total + %td{ colspan: "5", data: { controller: "input-char-count" }, style: "position: relative;" } + %label + = t(".note_label") + = text_field_tag :note, @order.note, { maxLength: 280, data: { "input-char-count-target": "input" } } + %span.edit-note-count{ data: { "input-char-count-target": "count" }, style: "position: absolute; right: 7px; top: 7px; font-size: 11px;" } + + %td.actions + = link_to '', '', class: 'save-note icon_link icon-ok no-text with-tip', data: { action: 'save' }, title: I18n.t('actions.save') + = link_to '', '', class: 'cancel-note icon_link icon-cancel no-text with-tip', data: { action: 'cancel' }, title: I18n.t('actions.cancel') + + %tr.show-note.total + %td{ :colspan => "5" } + - if order.note.present? + %strong + = t(".note_label") + = order.note + - else + = t(".no_note_present") + + %td.actions + = link_to '', '', class: 'edit-note icon_link icon-edit no-text with-tip', data: { action: 'edit' }, title: Spree.t('edit') diff --git a/app/webpacker/controllers/input_char_count_controller.js b/app/webpacker/controllers/input_char_count_controller.js new file mode 100644 index 0000000000..cafd93a673 --- /dev/null +++ b/app/webpacker/controllers/input_char_count_controller.js @@ -0,0 +1,21 @@ +import { Controller } from "stimulus"; + +export default class extends Controller { + static targets = ["count", "input"]; + + connect() { + this.inputTarget.addEventListener("keyup", this.countCharacters.bind(this)); + this.countCharacters(); + } + + countCharacters() { + this.displayCount( + this.inputTarget.value.length, + this.inputTarget.maxLength + ); + } + + displayCount(count, max) { + this.countTarget.textContent = `${count}/${max}`; + } +} diff --git a/app/webpacker/css/admin/orders.scss b/app/webpacker/css/admin/orders.scss index f5f9e4f811..7cd7c559a7 100644 --- a/app/webpacker/css/admin/orders.scss +++ b/app/webpacker/css/admin/orders.scss @@ -96,6 +96,18 @@ table.index td.actions { text-align: left; } +table.edit-note-table { + margin-top: -15px; + + tr:first-child th, tr:first-child td { + border-top: none; + } + + td.actions { + width: 15%; + } +} + .index-controls { button { diff --git a/config/locales/en.yml b/config/locales/en.yml index 59c0faed04..d54cf9ecdb 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -839,6 +839,9 @@ en: variants_without_unit_value: "WARNING: Some variants do not have a unit value" all: "All" select_variant: "Select a variant" + note: + note_label: "Note:" + no_note_present: "No note provided." enterprise: select_outgoing_oc_products_from: Select outgoing OC products from @@ -3796,6 +3799,9 @@ See the %{link} to find out more about %{sitename}'s features and to start using from: "From" to: "Bill to" shipping: "Shipping" + note: + note_label: "Note:" + no_note_present: "No note provided." form: distribution_fields: title: "Distribution" diff --git a/config/routes/api.rb b/config/routes/api.rb index 748c4ac563..f7bc47085e 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -18,7 +18,7 @@ Openfoodnetwork::Application.routes.draw do resources :variants, :only => [:index] - resources :orders, only: [:index, :show] do + resources :orders, only: [:index, :show, :update] do member do put :capture put :ship diff --git a/db/migrate/20220629080906_add_note_to_orders.rb b/db/migrate/20220629080906_add_note_to_orders.rb new file mode 100644 index 0000000000..3cb5415cf4 --- /dev/null +++ b/db/migrate/20220629080906_add_note_to_orders.rb @@ -0,0 +1,5 @@ +class AddNoteToOrders < ActiveRecord::Migration[6.1] + def change + add_column :spree_orders, :note, :string, null: false, default: "" + end +end diff --git a/db/schema.rb b/db/schema.rb index 9494745876..401e3906ff 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2022_06_21_230907) do +ActiveRecord::Schema.define(version: 2022_06_29_080906) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -597,6 +597,7 @@ ActiveRecord::Schema.define(version: 2022_06_21_230907) do t.integer "created_by_id" t.decimal "included_tax_total", precision: 10, scale: 2, default: "0.0", null: false t.decimal "additional_tax_total", precision: 10, scale: 2, default: "0.0", null: false + t.string "note", default: "", null: false t.index ["completed_at", "user_id", "created_by_id", "created_at"], name: "spree_orders_completed_at_user_id_created_by_id_created_at_idx" t.index ["customer_id"], name: "index_spree_orders_on_customer_id" t.index ["distributor_id"], name: "index_spree_orders_on_distributor_id" diff --git a/spec/javascripts/stimulus/input_char_count_controller_test.js b/spec/javascripts/stimulus/input_char_count_controller_test.js new file mode 100644 index 0000000000..668531bfd1 --- /dev/null +++ b/spec/javascripts/stimulus/input_char_count_controller_test.js @@ -0,0 +1,34 @@ +/** + * @jest-environment jsdom + */ + +import { Application } from "stimulus"; +import input_char_count_controller from "../../../app/webpacker/controllers/input_char_count_controller"; + +describe("InputCharCountController", () => { + beforeAll(() => { + const application = Application.start(); + application.register("input-char-count", input_char_count_controller); + }); + + describe("default behavior", () => { + beforeEach(() => { + document.body.innerHTML = `