From 8a2a0f024b010e2705510ba06513adff759a2060 Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Wed, 10 Nov 2021 13:07:10 +0000 Subject: [PATCH] Fix for Classification Reposition using Update Action (#23) * Change PUT to PATCH on classifications. * Fix using update. * Use fetch utility for ajax indicators on fetch requests. * Set up drag-and-drop Caprybara. * Fix spelling. --- .eslintrc.cjs | 2 +- .../spree/backend/global/_index.js | 2 + .../spree/backend/global/animate_css.es6 | 19 +++ .../backend/global/fetch_request_utility.es6 | 120 ++++++++++++++++++ .../javascripts/spree/backend/taxons.js | 29 +++-- app/views/spree/admin/taxons/index.html.erb | 2 +- app/views/spree/layouts/admin.html.erb | 6 +- spec/features/admin/taxons_spec.rb | 26 ++++ 8 files changed, 189 insertions(+), 17 deletions(-) create mode 100644 app/assets/javascripts/spree/backend/global/animate_css.es6 create mode 100644 app/assets/javascripts/spree/backend/global/fetch_request_utility.es6 diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 11e6d8873b..f4d038e60f 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -5,7 +5,7 @@ module.exports = { }, "extends": "eslint:recommended", "parserOptions": { - "ecmaVersion": 13, + "ecmaVersion": 2021, "sourceType": "module" }, "rules": { diff --git a/app/assets/javascripts/spree/backend/global/_index.js b/app/assets/javascripts/spree/backend/global/_index.js index a3eb44d8e2..79b838b95b 100644 --- a/app/assets/javascripts/spree/backend/global/_index.js +++ b/app/assets/javascripts/spree/backend/global/_index.js @@ -1,5 +1,7 @@ //= require spree/backend/global/alerts +//= require spree/backend/global/animate_css //= require spree/backend/global/bootstrap +//= require spree/backend/global/fetch_request_utility //= require spree/backend/global/flatpickr //= require spree/backend/global/ransack //= require spree/backend/global/info_alert diff --git a/app/assets/javascripts/spree/backend/global/animate_css.es6 b/app/assets/javascripts/spree/backend/global/animate_css.es6 new file mode 100644 index 0000000000..330d4d415e --- /dev/null +++ b/app/assets/javascripts/spree/backend/global/animate_css.es6 @@ -0,0 +1,19 @@ +/* eslint-disable no-unused-vars */ + +// +// Handle clearing out of animation styles from complete animation. +const animateCSS = (element, animation, speed, prefix = 'animate__') => + new Promise((resolve) => { + const animationName = `${prefix}${animation}` + const node = document.querySelector(element) + + node.classList.add(`${prefix}animated`, animationName, prefix + speed) + + function handleAnimationEnd(event) { + event.stopPropagation() + node.classList.remove(`${prefix}animated`, animationName) + resolve('Animation ended') + } + + node.addEventListener('animationend', handleAnimationEnd, { once: true }) + }) diff --git a/app/assets/javascripts/spree/backend/global/fetch_request_utility.es6 b/app/assets/javascripts/spree/backend/global/fetch_request_utility.es6 new file mode 100644 index 0000000000..be7c53a33d --- /dev/null +++ b/app/assets/javascripts/spree/backend/global/fetch_request_utility.es6 @@ -0,0 +1,120 @@ +/* eslint-disable no-undef */ +/* eslint-disable no-unused-vars */ + +// +// Shows the progress bar on fech requests +const showProgressIndicator = () => { + const progressBar = document.querySelector('#progress') + progressBar.classList.add('d-block') + animateCSS('#progress', 'fadeIn', 'faster') +} + +// +// Hides the progress bar on fech requests +const hideProgressIndicator = () => { + const progressBar = document.querySelector('#progress') + + animateCSS('#progress', 'fadeOut', 'fast') + + progressBar.addEventListener('animationend', () => { + progressBar.classList.remove('d-block') + }) +} + +// +// Handles the fetch request response. +// If the response has content it is returned, else if the response is a 204 no-content, +// the resolved text is returned. +const spreeHandleFetchRequestResponse = function(response) { + hideProgressIndicator() + + if (response.status === 204) { + return response.text() + } else { + return response.json() + } +} + +// +// Handles fech request errors by triggering the appropriate flash alert type and displaying +// the response message. +const spreeHandleFetchRequestError = function(data) { + if (data.error != null) { + show_flash('error', data.error) + } else if (data.message != null) { + show_flash('success', data.message) + } else if (data.exception != null) { + show_flash('info', data.exception) + } else if (data.detail != null) { + show_flash('info', data.detail) + } +} + +// +// Reloads the window. +const spreeWindowReload = function() { + window.location.reload() +} + +// +// SPREE FECTCH REQUEST +// Pass a json object containing your fetch request details and settings, you can also send a second optional +// argument, this second argument is your success response handler, and lastly a third argument that carries +// through a target element to the response method if needed, the second and third arguments are optional. +// When using spreeFetchRequest() the loading... progress bar, flash notice and errors are all handled for you. +// +// EXAMPLE SENDING A POST REQUEST TO CREATE A NEW SHIPMENT: +// const data = { +// order_id: 'H8213728798', +// variant_id: 2, +// quantity: 4, +// stock_location_id: 1 +// } +// +// const requestData = { +// // request details +// uri: Spree.routes.shipments_api_v2, +// method: 'POST', +// dataBody: data, +// +// // Optional Settings +// // disableProgressIndicator: true, Allows you to disable the progress loader bar if needed. +// // formatDataBody: false If you have pre-formatted data, pass this option to stop the function performing the default stringify. +// } +// spreeFetchRequest(requestData, someCallbackFunction, this) +// +const spreeFetchRequest = function(requstData, success = null, target = null) { + if (!requstData.disableProgressIndicator === true) showProgressIndicator() + + let requestDataBody + + const requestUri = requstData.uri || null + const requestMethod = requstData.method || 'GET' + const requestContentType = requstData.ContentType || 'application/json' + + if (requstData.formatDataBody === false) { + requestDataBody = requstData.dataBody + } else { + requestDataBody = JSON.stringify(requstData.dataBody) || null + } + + if (requestUri == null) return + + fetch(requestUri, { + method: requestMethod, + headers: { + 'Authorization': 'Bearer ' + OAUTH_TOKEN, + 'Content-Type': requestContentType + }, + body: requestDataBody + }) + .then((response) => spreeHandleFetchRequestResponse(response) + .then((data) => { + if (response.ok) { + if (success != null) success(data, target) + } else { + spreeHandleFetchRequestError(data) + } + })) + .catch(err => console.log(err)) +} diff --git a/app/assets/javascripts/spree/backend/taxons.js b/app/assets/javascripts/spree/backend/taxons.js index c3d99051aa..971a951f6f 100644 --- a/app/assets/javascripts/spree/backend/taxons.js +++ b/app/assets/javascripts/spree/backend/taxons.js @@ -14,23 +14,26 @@ $(function () { swapThreshold: 0.9, forceFallback: true, onEnd: function (evt) { - var classificationId = evt.item.getAttribute('data-classification-id') - var newIndex = evt.newIndex - return $.ajax({ - url: Spree.routes.classifications_api_v2 + '/' + classificationId.toString() + '/reposition', - headers: Spree.apiV2Authentication(), - method: 'PUT', - dataType: 'json', - data: { - classification: { - position: newIndex - } - } - }) + handleClassificationReposition(evt) } }) } + function handleClassificationReposition(evt) { + var classificationId = evt.item.getAttribute('data-classification-id') + var data = { + classification: { + position: parseInt(evt.newIndex, 10) + 1 + } + } + var requestData = { + uri: Spree.routes.classifications_api_v2 + '/' + classificationId, + method: 'PATCH', + dataBody: data, + } + spreeFetchRequest(requestData) + } + if (taxonId.length > 0) { taxonId.select2({ placeholder: Spree.translations.find_a_taxon, diff --git a/app/views/spree/admin/taxons/index.html.erb b/app/views/spree/admin/taxons/index.html.erb index 024179cfdf..d805e1ff81 100644 --- a/app/views/spree/admin/taxons/index.html.erb +++ b/app/views/spree/admin/taxons/index.html.erb @@ -3,7 +3,7 @@ <% end %>
-
+
<%= select_tag :taxon_id, nil, class: 'd-block w-100' %>
diff --git a/app/views/spree/layouts/admin.html.erb b/app/views/spree/layouts/admin.html.erb index 02a63d5be3..08aacdf689 100644 --- a/app/views/spree/layouts/admin.html.erb +++ b/app/views/spree/layouts/admin.html.erb @@ -83,8 +83,10 @@ <%#-------------------------------------------------%>
-
<%= Spree.t(:loading) %>...
-
<%= Spree.t(:loading) %>...
+
+ + <%= Spree.t(:loading) %>... +
diff --git a/spec/features/admin/taxons_spec.rb b/spec/features/admin/taxons_spec.rb index 99772a0ba7..1eb0e81b22 100644 --- a/spec/features/admin/taxons_spec.rb +++ b/spec/features/admin/taxons_spec.rb @@ -82,6 +82,32 @@ expect(page).to have_css('#taxon_icon_field img') end + # it 'admin should be able to drag and save' do + # taxon_2 = create(:taxon, name: 'Drag Test') + + # product_drag_a = create(:product, stores: Spree::Store.all) + # product_drag_b = create(:product, stores: Spree::Store.all) + # product_drag_c = create(:product, stores: Spree::Store.all) + # product_drag_d = create(:product, stores: Spree::Store.all) + # product_drag_e = create(:product, stores: Spree::Store.all) + + # product_drag_a.taxons << taxon_2 + # product_drag_b.taxons << taxon_2 + # product_drag_c.taxons << taxon_2 + # product_drag_d.taxons << taxon_2 + # product_drag_e.taxons << taxon_2 + + # visit spree.admin_taxons_path + + # select2(taxon_2.pretty_name, css: '#taxonSearch', search: 'Drag') + + # first_item = page.find("li#product_#{product_drag_a.id} > div > nav > a") + # last_item = page.find("li#product_#{product_drag_e.id} > div > nav > a") + + # last_item.drag_to(first_item) + # expect(page).to have_content('Fail') + # end + it 'admin should be able to remove taxon icon' do add_icon_to_root_taxon