mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-01 02:03:22 +00:00
Merge pull request #10833 from Matt-Yorkley/order-angular
Remove Angular from admin orders index page
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 1400 --no-auto-gen-timestamp`
|
||||
# using RuboCop version 1.50.2.
|
||||
# using RuboCop version 1.51.0.
|
||||
# The point is for the user to remove these configuration records
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
@@ -100,7 +100,7 @@ Layout/FirstHashElementIndentation:
|
||||
Exclude:
|
||||
- 'spec/services/products_renderer_spec.rb'
|
||||
|
||||
# Offense count: 11
|
||||
# Offense count: 10
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
|
||||
# SupportedHashRocketStyles: key, separator, table
|
||||
@@ -109,7 +109,6 @@ Layout/FirstHashElementIndentation:
|
||||
Layout/HashAlignment:
|
||||
Exclude:
|
||||
- 'app/controllers/spree/users_controller.rb'
|
||||
- 'app/models/spree/image.rb'
|
||||
- 'spec/migrations/migrate_customer_names_spec.rb'
|
||||
- 'spec/models/enterprise_spec.rb'
|
||||
- 'spec/system/admin/customers_spec.rb'
|
||||
@@ -142,7 +141,7 @@ Layout/LeadingCommentSpace:
|
||||
Exclude:
|
||||
- 'spec/system/admin/enterprises_spec.rb'
|
||||
|
||||
# Offense count: 114
|
||||
# Offense count: 115
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: EnforcedStyle.
|
||||
# SupportedStyles: space, no_space
|
||||
@@ -199,12 +198,13 @@ Layout/LineEndStringConcatenationIndentation:
|
||||
- 'spec/system/consumer/cookies_spec.rb'
|
||||
- 'spec/system/consumer/shopping/cart_spec.rb'
|
||||
|
||||
# Offense count: 615
|
||||
# Offense count: 643
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
|
||||
# URISchemes: http, https
|
||||
Layout/LineLength:
|
||||
Exclude:
|
||||
- 'app/components/confirm_modal_component.rb'
|
||||
- 'app/controllers/admin/bulk_line_items_controller.rb'
|
||||
- 'app/controllers/admin/enterprise_fees_controller.rb'
|
||||
- 'app/controllers/admin/enterprise_relationships_controller.rb'
|
||||
@@ -367,6 +367,8 @@ Layout/LineLength:
|
||||
- 'spec/system/admin/adjustments_spec.rb'
|
||||
- 'spec/system/admin/bulk_order_management_spec.rb'
|
||||
- 'spec/system/admin/bulk_product_update_spec.rb'
|
||||
- 'spec/system/admin/order_spec.rb'
|
||||
- 'spec/system/admin/product_import_spec.rb'
|
||||
|
||||
# Offense count: 1
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
@@ -412,7 +414,7 @@ Layout/TrailingEmptyLines:
|
||||
Exclude:
|
||||
- 'Rakefile'
|
||||
|
||||
# Offense count: 70
|
||||
# Offense count: 73
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: AllowInHeredoc.
|
||||
Layout/TrailingWhitespace:
|
||||
@@ -420,7 +422,6 @@ Layout/TrailingWhitespace:
|
||||
- 'app/controllers/spree/users_controller.rb'
|
||||
- 'app/controllers/user_confirmations_controller.rb'
|
||||
- 'app/models/enterprise.rb'
|
||||
- 'app/models/spree/image.rb'
|
||||
- 'spec/controllers/spree/credit_cards_controller_spec.rb'
|
||||
- 'spec/controllers/user_confirmations_controller_spec.rb'
|
||||
- 'spec/factories/order_factory.rb'
|
||||
@@ -438,7 +439,6 @@ Layout/TrailingWhitespace:
|
||||
- 'spec/system/admin/order_spec.rb'
|
||||
- 'spec/system/admin/product_import_spec.rb'
|
||||
- 'spec/system/admin/shipping_methods_spec.rb'
|
||||
- 'spec/system/consumer/split_checkout_spec.rb'
|
||||
|
||||
# Offense count: 7
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
@@ -468,7 +468,7 @@ Lint/ConstantDefinitionInBlock:
|
||||
- 'spec/validators/date_time_string_validator_spec.rb'
|
||||
- 'spec/validators/integer_array_validator_spec.rb'
|
||||
|
||||
# Offense count: 8
|
||||
# Offense count: 6
|
||||
# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches.
|
||||
Lint/DuplicateBranch:
|
||||
Exclude:
|
||||
@@ -581,6 +581,7 @@ Lint/UselessMethodDefinition:
|
||||
- 'app/models/spree/gateway.rb'
|
||||
|
||||
# Offense count: 3
|
||||
# This cop supports safe autocorrection (--autocorrect).
|
||||
# Configuration parameters: CheckForMethodsWithNoSideEffects.
|
||||
Lint/Void:
|
||||
Exclude:
|
||||
@@ -651,7 +652,7 @@ Metrics/BlockNesting:
|
||||
Exclude:
|
||||
- 'app/models/spree/payment/processing.rb'
|
||||
|
||||
# Offense count: 46
|
||||
# Offense count: 47
|
||||
# Configuration parameters: CountComments, Max, CountAsOne.
|
||||
Metrics/ClassLength:
|
||||
Exclude:
|
||||
@@ -659,7 +660,6 @@ Metrics/ClassLength:
|
||||
- 'app/controllers/admin/enterprise_fees_controller.rb'
|
||||
- 'app/controllers/admin/enterprises_controller.rb'
|
||||
- 'app/controllers/admin/order_cycles_controller.rb'
|
||||
- 'app/controllers/admin/product_import_controller.rb'
|
||||
- 'app/controllers/admin/resource_controller.rb'
|
||||
- 'app/controllers/admin/schedules_controller.rb'
|
||||
- 'app/controllers/admin/subscriptions_controller.rb'
|
||||
@@ -704,7 +704,7 @@ Metrics/ClassLength:
|
||||
- 'lib/reporting/reports/enterprise_fee_summary/scope.rb'
|
||||
- 'lib/reporting/reports/xero_invoices/base.rb'
|
||||
|
||||
# Offense count: 35
|
||||
# Offense count: 36
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
||||
Metrics/CyclomaticComplexity:
|
||||
Exclude:
|
||||
@@ -759,7 +759,7 @@ Metrics/MethodLength:
|
||||
- 'lib/reporting/reports/xero_invoices/base.rb'
|
||||
- 'lib/tasks/sample_data/product_factory.rb'
|
||||
|
||||
# Offense count: 50
|
||||
# Offense count: 49
|
||||
# Configuration parameters: CountComments, Max, CountAsOne.
|
||||
Metrics/ModuleLength:
|
||||
Exclude:
|
||||
@@ -808,16 +808,16 @@ Metrics/ModuleLength:
|
||||
- 'spec/models/spree/product_spec.rb'
|
||||
- 'spec/models/spree/shipping_method_spec.rb'
|
||||
- 'spec/models/spree/tax_rate_spec.rb'
|
||||
- 'spec/models/spree/variant_spec.rb'
|
||||
- 'spec/services/permissions/order_spec.rb'
|
||||
- 'spec/services/variant_units/option_value_namer_spec.rb'
|
||||
- 'spec/support/request/shop_workflow.rb'
|
||||
- 'spec/support/request/stripe_stubs.rb'
|
||||
|
||||
# Offense count: 7
|
||||
# Offense count: 8
|
||||
# Configuration parameters: Max, CountKeywordArgs, MaxOptionalParameters.
|
||||
Metrics/ParameterLists:
|
||||
Exclude:
|
||||
- 'app/components/confirm_modal_component.rb'
|
||||
- 'app/helpers/angular_form_builder.rb'
|
||||
- 'app/models/product_import/entry_processor.rb'
|
||||
- 'lib/reporting/reports/xero_invoices/base.rb'
|
||||
@@ -852,6 +852,7 @@ Naming/HeredocDelimiterNaming:
|
||||
- 'app/models/content_configuration.rb'
|
||||
|
||||
# Offense count: 5
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: EnforcedStyleForLeadingUnderscores.
|
||||
# SupportedStylesForLeadingUnderscores: disallowed, required, optional
|
||||
Naming/MemoizedInstanceVariableName:
|
||||
@@ -930,12 +931,6 @@ Rails/ApplicationController:
|
||||
Exclude:
|
||||
- 'engines/dfc_provider/app/controllers/dfc_provider/base_controller.rb'
|
||||
|
||||
# Offense count: 1
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
Rails/ApplicationJob:
|
||||
Exclude:
|
||||
- 'app/jobs/report_job.rb'
|
||||
|
||||
# Offense count: 5
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: NilOrEmpty, NotPresent, UnlessPresent.
|
||||
@@ -1083,7 +1078,6 @@ Rails/InverseOf:
|
||||
Exclude:
|
||||
- 'app/models/enterprise.rb'
|
||||
- 'app/models/order_cycle.rb'
|
||||
- 'app/models/spree/adjustment.rb'
|
||||
- 'app/models/spree/country.rb'
|
||||
- 'app/models/spree/inventory_unit.rb'
|
||||
- 'app/models/spree/line_item.rb'
|
||||
@@ -1122,7 +1116,7 @@ Rails/LexicallyScopedActionFilter:
|
||||
- 'app/controllers/spree/admin/zones_controller.rb'
|
||||
- 'app/controllers/spree/users_controller.rb'
|
||||
|
||||
# Offense count: 9
|
||||
# Offense count: 8
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
Rails/NegateInclude:
|
||||
Exclude:
|
||||
@@ -1131,11 +1125,10 @@ Rails/NegateInclude:
|
||||
- 'app/models/product_import/spreadsheet_entry.rb'
|
||||
- 'app/models/spree/order/checkout.rb'
|
||||
- 'app/services/order_cart_reset.rb'
|
||||
- 'engines/order_management/app/services/order_management/stock/estimator.rb'
|
||||
- 'lib/spree/localized_number.rb'
|
||||
- 'spec/support/matchers/table_matchers.rb'
|
||||
|
||||
# Offense count: 18
|
||||
# Offense count: 17
|
||||
Rails/OutputSafety:
|
||||
Exclude:
|
||||
- 'app/helpers/angular_form_helper.rb'
|
||||
@@ -1180,7 +1173,7 @@ Rails/PluckInWhere:
|
||||
Exclude:
|
||||
- 'app/models/spree/variant.rb'
|
||||
|
||||
# Offense count: 28
|
||||
# Offense count: 30
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
Rails/RedundantPresenceValidationOnBelongsTo:
|
||||
Exclude:
|
||||
@@ -1690,7 +1683,7 @@ Style/RedundantStringEscape:
|
||||
- 'spec/controllers/spree/admin/shipping_methods_controller_spec.rb'
|
||||
- 'spec/system/admin/enterprise_fees_spec.rb'
|
||||
|
||||
# Offense count: 206
|
||||
# Offense count: 208
|
||||
Style/Send:
|
||||
Exclude:
|
||||
- 'app/controllers/split_checkout_controller.rb'
|
||||
@@ -1740,7 +1733,7 @@ Style/SlicingWithRange:
|
||||
- 'engines/order_management/app/services/order_management/subscriptions/validator.rb'
|
||||
- 'lib/discourse/single_sign_on.rb'
|
||||
|
||||
# Offense count: 29
|
||||
# Offense count: 28
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
# Configuration parameters: Mode.
|
||||
Style/StringConcatenation:
|
||||
@@ -1761,7 +1754,6 @@ Style/StringConcatenation:
|
||||
- 'lib/spree/core/environment_extension.rb'
|
||||
- 'spec/models/spree/line_item_spec.rb'
|
||||
- 'spec/models/spree/product_spec.rb'
|
||||
- 'spec/models/spree/variant_spec.rb'
|
||||
- 'spec/services/embedded_page_service_spec.rb'
|
||||
- 'spec/support/api_helper.rb'
|
||||
- 'spec/support/features/datepicker_helper.rb'
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
// jquery and angular
|
||||
//= require jquery2
|
||||
//= require jquery_ujs
|
||||
//= require jquery.ui.all
|
||||
//= require jquery.powertip
|
||||
//= require jquery.cookie
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
angular.module("admin.orders").controller "bulkInvoiceCtrl", ($scope, $http, $timeout) ->
|
||||
$scope.createBulkInvoice = ->
|
||||
$scope.invoice_id = null
|
||||
$scope.poll = 1
|
||||
$scope.loading = true
|
||||
$scope.message = null
|
||||
$scope.error = null
|
||||
$scope.poll_wait = 5 # 5 Seconds between each check
|
||||
$scope.poll_retries = 80 # Maximum checks before stopping
|
||||
|
||||
$http.post('/admin/orders/invoices', {order_ids: $scope.selected_orders}).then (response) ->
|
||||
$scope.invoice_id = response.data
|
||||
$scope.pollBulkInvoice()
|
||||
|
||||
$scope.pollBulkInvoice = ->
|
||||
$timeout($scope.nextPoll, $scope.poll_wait * 1000)
|
||||
|
||||
$scope.nextPoll = ->
|
||||
$http.get('/admin/orders/invoices/'+$scope.invoice_id+'/poll').then (response) ->
|
||||
$scope.loading = false
|
||||
$scope.message = t('js.admin.orders.index.bulk_invoice_created')
|
||||
|
||||
.catch (response) ->
|
||||
$scope.poll++
|
||||
|
||||
if $scope.poll > $scope.poll_retries
|
||||
$scope.loading = false
|
||||
$scope.error = t('js.admin.orders.index.bulk_invoice_failed')
|
||||
return
|
||||
|
||||
$scope.pollBulkInvoice()
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
angular.module("admin.orders").controller "ordersCtrl", ($scope, $timeout, RequestMonitor, Orders, SortOptions, $window, $filter, $location, KeyValueMapStore) ->
|
||||
$scope.RequestMonitor = RequestMonitor
|
||||
$scope.pagination = Orders.pagination
|
||||
$scope.orders = Orders.all
|
||||
$scope.sortOptions = SortOptions
|
||||
$scope.per_page_options = [
|
||||
{id: 15, name: t('js.admin.orders.index.per_page', results: 15)},
|
||||
{id: 50, name: t('js.admin.orders.index.per_page', results: 50)},
|
||||
{id: 100, name: t('js.admin.orders.index.per_page', results: 100)}
|
||||
]
|
||||
$scope.selected_orders = []
|
||||
$scope.checkboxes = {}
|
||||
$scope.selected = false
|
||||
$scope.select_all = false
|
||||
$scope.poll = 0
|
||||
$scope.rowStatus = {}
|
||||
|
||||
KeyValueMapStore.localStorageKey = 'ordersFilters'
|
||||
KeyValueMapStore.storableKeys = ["q", "sorting", "page", "per_page"]
|
||||
|
||||
$scope.initialise = ->
|
||||
unless KeyValueMapStore.restoreValues($scope)
|
||||
$scope.setDefaults()
|
||||
|
||||
$scope.fetchResults()
|
||||
|
||||
$scope.setDefaults = ->
|
||||
$scope.per_page = 15
|
||||
$scope.q = {
|
||||
completed_at_not_null: true
|
||||
}
|
||||
e = new CustomEvent("flatpickr_clear");
|
||||
window.dispatchEvent(e)
|
||||
|
||||
$scope.clearFilters = () ->
|
||||
KeyValueMapStore.clearKeyValueMap()
|
||||
$scope.setDefaults()
|
||||
$scope.fetchResults()
|
||||
|
||||
$scope.fetchResults = (page=1) ->
|
||||
startDateWithTime = $scope.appendStringIfNotEmpty($scope.q?.completed_at_gteq, ' 00:00:00')
|
||||
endDateWithTime = $scope.appendStringIfNotEmpty($scope.q?.completed_at_lteq, ' 23:59:59')
|
||||
|
||||
$scope.resetSelected()
|
||||
params = {
|
||||
'q[completed_at_gteq]': startDateWithTime,
|
||||
'q[completed_at_lteq]': endDateWithTime,
|
||||
'q[state_eq]': $scope.q?.state_eq,
|
||||
'q[number_cont]': $scope.q?.number_cont,
|
||||
'q[email_cont]': $scope.q?.email_cont,
|
||||
'q[bill_address_firstname_start]': $scope.q?.bill_address_firstname_start,
|
||||
'q[bill_address_lastname_start]': $scope.q?.bill_address_lastname_start,
|
||||
# Set default checkbox values to null. See: https://github.com/openfoodfoundation/openfoodnetwork/pull/3076#issuecomment-440010498
|
||||
'q[completed_at_not_null]': $scope.q?.completed_at_not_null || null,
|
||||
'q[distributor_id_in][]': $scope.q?.distributor_id_in,
|
||||
'q[order_cycle_id_in][]': $scope.q?.order_cycle_id_in,
|
||||
'q[s]': $scope.sorting || 'completed_at desc',
|
||||
shipping_method_id: $scope.q?.shipping_method_id,
|
||||
per_page: $scope.per_page,
|
||||
page: page
|
||||
}
|
||||
KeyValueMapStore.setStoredValues($scope)
|
||||
RequestMonitor.load(Orders.index(params).$promise)
|
||||
|
||||
$scope.appendStringIfNotEmpty = (baseString, stringToAppend) ->
|
||||
return baseString unless baseString
|
||||
return baseString if baseString.endsWith(stringToAppend)
|
||||
|
||||
baseString + stringToAppend
|
||||
|
||||
$scope.resetSelected = ->
|
||||
$scope.selected_orders.length = 0
|
||||
$scope.selected = false
|
||||
$scope.select_all = false
|
||||
$scope.checkboxes = {}
|
||||
|
||||
$scope.toggleSelection = (id) ->
|
||||
index = $scope.selected_orders.indexOf(id)
|
||||
|
||||
if index == -1
|
||||
$scope.selected_orders.push(id)
|
||||
else
|
||||
$scope.selected_orders.splice(index, 1)
|
||||
|
||||
$scope.toggleAll = ->
|
||||
$scope.selected_orders.length = 0
|
||||
$scope.orders.forEach (order) ->
|
||||
$scope.checkboxes[order.id] = $scope.select_all
|
||||
$scope.selected_orders.push order.id if $scope.select_all
|
||||
|
||||
$scope.$watch 'sortOptions', (sort) ->
|
||||
return unless sort && sort.predicate != ""
|
||||
|
||||
$scope.sorting = sort.getSortingExpr()
|
||||
$scope.fetchResults()
|
||||
, true
|
||||
|
||||
$scope.capturePayment = (order) ->
|
||||
$scope.rowAction('capture', order)
|
||||
|
||||
$scope.shipOrder = (order) ->
|
||||
$scope.rowAction('ship', order)
|
||||
|
||||
$scope.rowAction = (action, order) ->
|
||||
$scope.rowStatus[order.id] = "loading"
|
||||
|
||||
Orders[action](order).$promise.then (data) ->
|
||||
$scope.rowStatus[order.id] = "success"
|
||||
$timeout(->
|
||||
$scope.rowStatus[order.id] = null
|
||||
, 1500)
|
||||
, (error) ->
|
||||
$scope.rowStatus[order.id] = "error"
|
||||
|
||||
$scope.changePage = (newPage) ->
|
||||
$scope.page = newPage
|
||||
$scope.fetchResults(newPage)
|
||||
@@ -1,5 +0,0 @@
|
||||
angular.module("admin.orders").directive "invoicesModal", ($modal) ->
|
||||
restrict: 'C'
|
||||
link: (scope, elem, attrs, ctrl) ->
|
||||
elem.on "click", (ev) =>
|
||||
scope.uploadModal = $modal.open(templateUrl: 'admin/modals/bulk_invoice.html', controller: ctrl, scope: scope, windowClass: 'simple-modal')
|
||||
34
app/assets/javascripts/admin_minimal.js
Normal file
34
app/assets/javascripts/admin_minimal.js
Normal file
@@ -0,0 +1,34 @@
|
||||
// This is a manifest file that'll be compiled into including all the files listed below.
|
||||
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
|
||||
// be included in the compiled file accessible from http://example.com/assets/application.js
|
||||
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
||||
// the compiled file.
|
||||
//
|
||||
|
||||
//= require jquery2
|
||||
//= require admin/spree/spree-select2
|
||||
//= require admin/spree/handlebar_extensions
|
||||
|
||||
//= require i18n/translations
|
||||
//= require darkswarm/i18n.translate.js
|
||||
//= require moment/min/moment.min.js
|
||||
//= require moment/locale/ar.js
|
||||
//= require moment/locale/ca.js
|
||||
//= require moment/locale/de.js
|
||||
//= require moment/locale/en-gb.js
|
||||
//= require moment/locale/es.js
|
||||
//= require moment/locale/fil.js
|
||||
//= require moment/locale/fr.js
|
||||
//= require moment/locale/it.js
|
||||
//= require moment/locale/nb.js
|
||||
//= require moment/locale/nl-be.js
|
||||
//= require moment/locale/pt-br.js
|
||||
//= require moment/locale/pt.js
|
||||
//= require moment/locale/ru.js
|
||||
//= require moment/locale/sv.js
|
||||
//= require moment/locale/tr.js
|
||||
//= require moment/locale/pl.js
|
||||
|
||||
//= require js-big-decimal/dist/web/js-big-decimal.min.js
|
||||
|
||||
window.angular = { module: function(noop){ return { value: function(){} } } }
|
||||
@@ -67,11 +67,3 @@ document.addEventListener "turbo:before-render", ->
|
||||
rootscope = null
|
||||
window.injector = null
|
||||
true
|
||||
|
||||
document.addEventListener "ajax:beforeSend", (event) =>
|
||||
window.Turbo.navigator.adapter.progressBar.setValue(0)
|
||||
window.Turbo.navigator.adapter.progressBar.show()
|
||||
|
||||
document.addEventListener "ajax:complete", (event) =>
|
||||
window.Turbo.navigator.adapter.progressBar.setValue(100)
|
||||
window.Turbo.navigator.adapter.progressBar.hide()
|
||||
|
||||
@@ -4,7 +4,16 @@
|
||||
{{ 'admin.actions' | t }}
|
||||
%i{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
%a.menu_item{ 'ng-repeat' => "link in links", href: '{{link.url}}', target: "{{link.target || '_self'}}", data: { method: "{{ link.method || 'get' }}", confirm: "{{link.confirm}}" } }
|
||||
%span
|
||||
%i{ ng: { class: "link.icon" } }
|
||||
%span {{ link.name }}
|
||||
%div{ 'ng-repeat' => "link in links" }
|
||||
%a.menu_item{ 'ng-if': "link.method", href: '{{link.url}}', target: "{{link.target || '_self'}}", data: { method: "{{ link.method }}", "ujs-navigate": "false", confirm: "{{link.confirm}}" } }
|
||||
%span
|
||||
%i{ ng: { class: "link.icon" } }
|
||||
%span {{ link.name }}
|
||||
%a.menu_item{ 'ng-if': "link.confirm && !link.method", href: '{{link.url}}', target: "{{link.target || '_self'}}", "data-confirm": "{{link.confirm}}" }
|
||||
%span
|
||||
%i{ ng: { class: "link.icon" } }
|
||||
%span {{ link.name }}
|
||||
%a.menu_item{ 'ng-if': "!link.confirm && !link.method", href: '{{link.url}}', target: "{{link.target || '_self'}}" }
|
||||
%span
|
||||
%i{ ng: { class: "link.icon" } }
|
||||
%span {{ link.name }}
|
||||
|
||||
13
app/channels/session_channel.rb
Normal file
13
app/channels/session_channel.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SessionChannel < ApplicationCable::Channel
|
||||
def self.for_request(request)
|
||||
"SessionChannel:#{request.session.id}"
|
||||
end
|
||||
|
||||
def subscribed
|
||||
return reject if current_user.nil?
|
||||
|
||||
stream_from "SessionChannel:#{session_id}"
|
||||
end
|
||||
end
|
||||
@@ -1,11 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class ConfirmModalComponent < ModalComponent
|
||||
def initialize(id:, confirm_actions: nil, controllers: nil, message: nil, confirm_reflexes: nil)
|
||||
def initialize(id:, confirm_actions: nil, reflex: nil, controller: nil, message: nil, confirm_reflexes: nil)
|
||||
super(id: id, close_button: true)
|
||||
@confirm_actions = confirm_actions
|
||||
@reflex = reflex
|
||||
@confirm_reflexes = confirm_reflexes
|
||||
@controllers = controllers
|
||||
@controller = controller
|
||||
@message = message
|
||||
end
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
%div{ id: @id, "data-controller": "modal #{@controllers}", "data-action": "keyup@document->modal#closeIfEscapeKey" }
|
||||
%div{ id: @id, "data-controller": "modal #{@controller}", "data-action": "keyup@document->modal#closeIfEscapeKey", "data-#{@controller}-reflex-value": @reflex }
|
||||
.reveal-modal-bg.fade{ "data-modal-target": "background", "data-action": "click->modal#close" }
|
||||
.reveal-modal.fade.tiny.help-modal{ "data-modal-target": "modal" }
|
||||
= content
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
%tr
|
||||
- @columns.each do |column|
|
||||
%td.products_column{class: column[:id]}
|
||||
- if column[:id] == "name" && @image
|
||||
- if column[:id] == "name" && @image&.attachment.present?
|
||||
= image_tag @image.url(:mini)
|
||||
= column[:value]
|
||||
|
||||
@@ -47,17 +47,15 @@ module Api
|
||||
def capture
|
||||
authorize! :admin, order
|
||||
|
||||
pending_payment = order.pending_payments.first
|
||||
payment_capture = OrderCaptureService.new(order)
|
||||
|
||||
return payment_capture_failed unless order.payment_required? && pending_payment
|
||||
|
||||
if pending_payment.capture!
|
||||
if payment_capture.call
|
||||
render json: order.reload, serializer: Api::Admin::OrderSerializer, status: :ok
|
||||
elsif payment_capture.gateway_error.present?
|
||||
error_during_processing(payment_capture.gateway_error)
|
||||
else
|
||||
payment_capture_failed
|
||||
end
|
||||
rescue Spree::Core::GatewayError => e
|
||||
error_during_processing(e)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -9,6 +9,7 @@ require 'spree/core/controller_helpers/common'
|
||||
require 'open_food_network/referer_parser'
|
||||
|
||||
class ApplicationController < ActionController::Base
|
||||
include CablecarResponses
|
||||
include Pagy::Backend
|
||||
include RequestTimeouts
|
||||
|
||||
|
||||
@@ -11,11 +11,18 @@ module Spree
|
||||
before_action :load_order, only: [:edit, :update, :fire, :resend,
|
||||
:invoice, :print, :distribution]
|
||||
before_action :load_distribution_choices, only: [:new, :edit, :update, :distribution]
|
||||
|
||||
before_action :require_distributor_abn, only: :invoice
|
||||
before_action :restore_saved_query!, only: :index
|
||||
|
||||
respond_to :html, :json
|
||||
|
||||
def index
|
||||
orders = SearchOrders.new(search_params, spree_current_user).orders
|
||||
@pagy, @orders = pagy(orders, items: params[:per_page] || 15)
|
||||
|
||||
update_search_results if searching?
|
||||
end
|
||||
|
||||
def new
|
||||
@order = Order.create
|
||||
@order.created_by = spree_current_user
|
||||
@@ -110,6 +117,36 @@ module Spree
|
||||
|
||||
private
|
||||
|
||||
def update_search_results
|
||||
session[:admin_orders_search] = search_params
|
||||
|
||||
render cable_ready: cable_car.inner_html(
|
||||
"#orders-index",
|
||||
partial("spree/admin/orders/table", locals: { pagy: @pagy, orders: @orders })
|
||||
)
|
||||
end
|
||||
|
||||
def searching?
|
||||
params[:q].present? && request.format.symbol == :cable_ready
|
||||
end
|
||||
|
||||
def search_params
|
||||
default_filters.deep_merge(
|
||||
params.permit(:page, :per_page, :shipping_method_id, q: {})
|
||||
).to_h.with_indifferent_access
|
||||
end
|
||||
|
||||
def default_filters
|
||||
{ q: { completed_at_not_null: 1, s: "completed_at desc" } }
|
||||
end
|
||||
|
||||
def restore_saved_query!
|
||||
return unless request.format.html?
|
||||
|
||||
@_params = ActionController::Parameters.new(session[:admin_orders_search] || {})
|
||||
@stored_query = search_params.to_query
|
||||
end
|
||||
|
||||
def on_update
|
||||
@order.recreate_all_fees!
|
||||
|
||||
|
||||
@@ -62,7 +62,6 @@ module Spree
|
||||
{ name: t(:resend_confirmation),
|
||||
url: spree.resend_admin_order_path(@order),
|
||||
icon: 'icon-email',
|
||||
method: 'post',
|
||||
confirm: t(:confirm_resend_order_confirmation) }
|
||||
end
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class BulkInvoiceJob < ApplicationJob
|
||||
def perform(order_ids, filepath)
|
||||
include CableReady::Broadcaster
|
||||
delegate :render, to: ActionController::Base
|
||||
|
||||
def perform(order_ids, filepath, options = {})
|
||||
pdf = CombinePDF.new
|
||||
|
||||
sorted_orders(order_ids).each do |order|
|
||||
@@ -11,6 +14,8 @@ class BulkInvoiceJob < ApplicationJob
|
||||
end
|
||||
|
||||
pdf.save filepath
|
||||
|
||||
broadcast(filepath, options[:channel]) if options[:channel]
|
||||
end
|
||||
|
||||
private
|
||||
@@ -18,10 +23,22 @@ class BulkInvoiceJob < ApplicationJob
|
||||
# Ensures the records are returned in the same order the ids were originally given in
|
||||
def sorted_orders(order_ids)
|
||||
orders_by_id = Spree::Order.where(id: order_ids).to_a.index_by(&:id)
|
||||
order_ids.map { |id| orders_by_id[id] }
|
||||
order_ids.map { |id| orders_by_id[id.to_i] }
|
||||
end
|
||||
|
||||
def renderer
|
||||
@renderer ||= InvoiceRenderer.new
|
||||
end
|
||||
|
||||
def broadcast(filepath, channel)
|
||||
file_id = filepath.split("/").last.split(".").first
|
||||
|
||||
cable_ready[channel].
|
||||
inner_html(
|
||||
selector: "#bulk_invoices_modal .modal-content",
|
||||
html: render(partial: "spree/admin/orders/bulk/invoice_link",
|
||||
locals: { invoice_url: "/admin/orders/invoices/#{file_id}" })
|
||||
).
|
||||
broadcast
|
||||
end
|
||||
end
|
||||
|
||||
101
app/reflexes/admin/orders_reflex.rb
Normal file
101
app/reflexes/admin/orders_reflex.rb
Normal file
@@ -0,0 +1,101 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class OrdersReflex < ApplicationReflex
|
||||
before_reflex :authorize_order, only: [:capture, :ship]
|
||||
|
||||
def capture
|
||||
payment_capture = OrderCaptureService.new(@order)
|
||||
|
||||
if payment_capture.call
|
||||
morph dom_id(@order), render(partial: "spree/admin/orders/table_row",
|
||||
locals: { order: @order.reload, success: true })
|
||||
else
|
||||
flash[:error] = with_locale{
|
||||
payment_capture.gateway_error || I18n.t(:payment_processing_failed)
|
||||
}
|
||||
morph_admin_flashes
|
||||
end
|
||||
end
|
||||
|
||||
def ship
|
||||
if @order.ship
|
||||
morph dom_id(@order), render(partial: "spree/admin/orders/table_row",
|
||||
locals: { order: @order.reload, success: true })
|
||||
else
|
||||
flash[:error] = with_locale{ I18n.t("api.orders.failed_to_update") }
|
||||
morph_admin_flashes
|
||||
end
|
||||
end
|
||||
|
||||
def bulk_invoice(params)
|
||||
cable_ready.append(
|
||||
selector: "#orders-index",
|
||||
html: render(partial: "spree/admin/orders/bulk/invoice_modal")
|
||||
).broadcast
|
||||
|
||||
BulkInvoiceJob.perform_later(
|
||||
params[:bulk_ids],
|
||||
"tmp/invoices/#{Time.zone.now.to_i}-#{SecureRandom.hex(2)}.pdf",
|
||||
channel: SessionChannel.for_request(request)
|
||||
)
|
||||
|
||||
morph :nothing
|
||||
end
|
||||
|
||||
def cancel_orders(params)
|
||||
cancelled_orders = OrdersBulkCancelService.new(params, current_user).call
|
||||
|
||||
cable_ready.dispatch_event(name: "modal:close")
|
||||
|
||||
cancelled_orders.each do |order|
|
||||
cable_ready.replace(
|
||||
selector: dom_id(order),
|
||||
html: render(partial: "spree/admin/orders/table_row", locals: { order: order })
|
||||
)
|
||||
end
|
||||
|
||||
cable_ready.broadcast
|
||||
morph :nothing
|
||||
end
|
||||
|
||||
def resend_confirmation_emails(params)
|
||||
editable_orders.where(id: params[:bulk_ids]).find_each do |order|
|
||||
next unless can? :resend, order
|
||||
|
||||
Spree::OrderMailer.confirm_email_for_customer(order.id, true).deliver_later
|
||||
end
|
||||
|
||||
success("admin.resend_confirmation_emails_feedback", params[:bulk_ids].count)
|
||||
end
|
||||
|
||||
def send_invoices(params)
|
||||
count = 0
|
||||
editable_orders.where(id: params[:bulk_ids]).find_each do |o|
|
||||
next unless o.distributor.can_invoice? && (o.resumed? || o.complete?)
|
||||
|
||||
Spree::OrderMailer.invoice_email(o.id).deliver_later
|
||||
count += 1
|
||||
end
|
||||
|
||||
success("admin.send_invoice_feedback", count)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def authorize_order
|
||||
@order = Spree::Order.find_by(id: element.dataset[:id])
|
||||
authorize! :admin, @order
|
||||
end
|
||||
|
||||
def success(i18n_key, count)
|
||||
flash[:success] = with_locale { I18n.t(i18n_key, count: count) }
|
||||
cable_ready.dispatch_event(name: "modal:close")
|
||||
morph_admin_flashes
|
||||
end
|
||||
|
||||
def editable_orders
|
||||
Permissions::Order.new(current_user).editable_orders
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -20,6 +20,8 @@ class ApplicationReflex < StimulusReflex::Reflex
|
||||
|
||||
delegate :current_user, to: :connection
|
||||
|
||||
private
|
||||
|
||||
def current_ability
|
||||
Spree::Ability.new(current_user)
|
||||
end
|
||||
@@ -27,4 +29,8 @@ class ApplicationReflex < StimulusReflex::Reflex
|
||||
def with_locale(&block)
|
||||
I18n.with_locale(current_user.locale, &block)
|
||||
end
|
||||
|
||||
def morph_admin_flashes
|
||||
morph "#flashes", render(partial: "admin/shared/flashes", locals: { flashes: flash })
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class BulkActionsInOrdersListReflex < ApplicationReflex
|
||||
def resend_confirmation_email(order_ids)
|
||||
editable_orders.where(id: order_ids).find_each do |o|
|
||||
Spree::OrderMailer.confirm_email_for_customer(o.id, true).deliver_later if can? :resend, o
|
||||
end
|
||||
|
||||
success("admin.resend_confirmation_emails_feedback", order_ids.count)
|
||||
end
|
||||
|
||||
def send_invoice(order_ids)
|
||||
count = 0
|
||||
editable_orders.where(id: order_ids).find_each do |o|
|
||||
next unless o.distributor.can_invoice? && (o.resumed? || o.complete?)
|
||||
|
||||
Spree::OrderMailer.invoice_email(o.id).deliver_later
|
||||
count += 1
|
||||
end
|
||||
|
||||
success("admin.send_invoice_feedback", count)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def success(i18n_key, count)
|
||||
flash[:success] = I18n.t(i18n_key, count: count)
|
||||
cable_ready.dispatch_event(name: "modal:close")
|
||||
morph "#flashes", render(partial: "shared/flashes", locals: { flashes: flash })
|
||||
end
|
||||
|
||||
def editable_orders
|
||||
Permissions::Order.new(current_user).editable_orders
|
||||
end
|
||||
end
|
||||
@@ -1,9 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class CancelOrdersReflex < ApplicationReflex
|
||||
def confirm(params)
|
||||
OrdersBulkCancelService.new(params, current_user).call
|
||||
cable_ready.dispatch_event(name: "modal:close")
|
||||
# flash[:success] = Spree.t(:order_updated)
|
||||
end
|
||||
end
|
||||
@@ -21,6 +21,6 @@ class WhiteLabelReflex < ApplicationReflex
|
||||
I18n.t("admin.enterprises.form.white_label.remove_logo_success")
|
||||
}
|
||||
cable_ready.dispatch_event(name: "modal:close")
|
||||
morph "#flashes", render(partial: "shared/flashes", locals: { flashes: flash })
|
||||
morph_admin_flashes
|
||||
end
|
||||
end
|
||||
|
||||
22
app/services/order_capture_service.rb
Normal file
22
app/services/order_capture_service.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Use `authorize! :admin order` before calling this service
|
||||
|
||||
class OrderCaptureService
|
||||
attr_reader :gateway_error
|
||||
|
||||
def initialize(order)
|
||||
@order = order
|
||||
@gateway_error = nil
|
||||
end
|
||||
|
||||
def call
|
||||
return false unless @order.payment_required?
|
||||
return false unless (pending_payment = @order.pending_payments.first)
|
||||
|
||||
pending_payment.capture!
|
||||
rescue Spree::Core::GatewayError => e
|
||||
@gateway_error = e
|
||||
false
|
||||
end
|
||||
end
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
class OrdersBulkCancelService
|
||||
def initialize(params, current_user)
|
||||
@order_ids = params[:order_ids]
|
||||
@order_ids = params[:bulk_ids]
|
||||
@current_user = current_user
|
||||
@send_cancellation_email = params[:send_cancellation_email]
|
||||
@restock_items = params[:restock_items]
|
||||
end
|
||||
|
||||
def call
|
||||
editable_orders.where(id: @order_ids).find_each do |order|
|
||||
editable_orders.where(id: @order_ids).each do |order|
|
||||
order.send_cancellation_email = @send_cancellation_email
|
||||
order.restock_items = @restock_items
|
||||
order.cancel
|
||||
|
||||
@@ -26,7 +26,7 @@ class SearchOrders
|
||||
base_query = ::Permissions::Order.new(current_user).editable_orders.not_empty
|
||||
.or(::Permissions::Order.new(current_user).editable_orders.finalized)
|
||||
|
||||
return base_query unless params[:shipping_method_id]
|
||||
return base_query if params[:shipping_method_id].blank?
|
||||
|
||||
base_query
|
||||
.joins(shipments: :shipping_rates)
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
.row
|
||||
= t('.stripe_account_connected')
|
||||
.row
|
||||
=link_to t('.disconnect'), main_app.admin_stripe_account_path(@stripe_account), method: :delete, class: 'button'
|
||||
|
||||
=link_to t('.disconnect'), main_app.admin_stripe_account_path(@stripe_account), data: { method: :delete, "ujs-navigate": "false"}, class: 'button'
|
||||
- else
|
||||
.row.stripe-info
|
||||
.six.columns.alpha
|
||||
|
||||
@@ -22,4 +22,4 @@
|
||||
%br
|
||||
= button_to t(".link_account_button"),
|
||||
Spree::Core::Engine.routes.url_helpers.spree_user_openid_connect_omniauth_authorize_path(auth_type: "login"),
|
||||
method: :post
|
||||
data: { method: :post, "ujs-navigate": "false" }
|
||||
|
||||
@@ -36,4 +36,4 @@
|
||||
%td.actions{ ng: { if: 'orderCycle.viewing_as_coordinator' } }
|
||||
%a.clone-order-cycle.icon-copy.no-text{ ng: { href: '{{orderCycle.clone_path}}'}, 'ofn-with-tip' => t(:clone) }
|
||||
%td.actions{ ng: { if: 'orderCycle.deletable && orderCycle.viewing_as_coordinator' }}
|
||||
%a.delete-order-cycle.icon-trash.no-text{ ng: { href: '{{orderCycle.delete_path}}'}, data: { method: 'delete', confirm: t(:are_you_sure) }, 'ofn-with-tip' => t(:remove) }
|
||||
%a.delete-order-cycle.icon-trash.no-text{ ng: { href: '{{orderCycle.delete_path}}'}, data: { method: 'delete', confirm: t(:are_you_sure), "ujs-navigate": "false" }, 'ofn-with-tip' => t(:remove) }
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
= hidden_field_tag "order_cycle[selected_distributor_shipping_method_ids][]", ""
|
||||
- @order_cycle.distributors.each do |distributor|
|
||||
- distributor_shipping_methods = @order_cycle.attachable_distributor_shipping_methods.where("distributor_id = ?", distributor.id).includes(:shipping_method)
|
||||
%tr{ class: "distributor-#{distributor.id}-shipping-methods", "data-controller": "select-all" }
|
||||
%tr{ class: "distributor-#{distributor.id}-shipping-methods", "data-controller": "checked" }
|
||||
%td.text-center
|
||||
- if distributor_shipping_methods.many?
|
||||
%label
|
||||
= check_box_tag nil, nil, nil, { "data-action": "change->select-all#toggleAll", "data-select-all-target": "all" }
|
||||
= check_box_tag nil, nil, nil, { "data-action": "change->checked#toggleAll", "data-checked-target": "all" }
|
||||
= t(".select_all")
|
||||
%td
|
||||
%em= distributor.name
|
||||
@@ -36,7 +36,7 @@
|
||||
distributor_shipping_method.id,
|
||||
@order_cycle.distributor_shipping_methods.include?(distributor_shipping_method),
|
||||
id: "order_cycle_selected_distributor_shipping_method_ids_#{distributor_shipping_method.id}",
|
||||
data: ({ "action" => "change->select-all#toggleCheckbox", "select-all-target" => "checkbox" } if distributor_shipping_method.shipping_method.frontend?)
|
||||
data: ({ "action" => "change->checked#toggleCheckbox", "checked-target" => "checkbox" } if distributor_shipping_method.shipping_method.frontend?)
|
||||
= distributor_shipping_method.shipping_method.name
|
||||
- distributor.shipping_methods.backend.each do |shipping_method|
|
||||
%label.disabled
|
||||
@@ -52,11 +52,11 @@
|
||||
= hidden_field_tag "order_cycle[selected_distributor_payment_method_ids][]", ""
|
||||
- @order_cycle.distributors.each do |distributor|
|
||||
- distributor_payment_methods = @order_cycle.attachable_distributor_payment_methods.where("distributor_id = ?", distributor.id).includes(:payment_method)
|
||||
%tr{ class: "distributor-#{distributor.id}-payment-methods", "data-controller": "select-all" }
|
||||
%tr{ class: "distributor-#{distributor.id}-payment-methods", "data-controller": "checked" }
|
||||
%td.text-center
|
||||
- if distributor_payment_methods.many?
|
||||
%label
|
||||
= check_box_tag nil, nil, nil, { "data-action": "change->select-all#toggleAll", "data-select-all-target": "all" }
|
||||
= check_box_tag nil, nil, nil, { "data-action": "change->checked#toggleAll", "data-checked-target": "all" }
|
||||
= t(".select_all")
|
||||
%td
|
||||
%em= distributor.name
|
||||
@@ -67,7 +67,7 @@
|
||||
distributor_payment_method.id,
|
||||
@order_cycle.distributor_payment_methods.include?(distributor_payment_method),
|
||||
id: "order_cycle_selected_distributor_payment_method_ids_#{distributor_payment_method.id}",
|
||||
data: ({ "action" => "change->select-all#toggleCheckbox", "select-all-target" => "checkbox" } if distributor_payment_method.payment_method.frontend?)
|
||||
data: ({ "action" => "change->checked#toggleCheckbox", "checked-target" => "checkbox" } if distributor_payment_method.payment_method.frontend?)
|
||||
= distributor_payment_method.payment_method.name
|
||||
- distributor.payment_methods.inactive_or_backend.each do |payment_method|
|
||||
%label.disabled
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
- mails_sent = @order_cycle.mails_sent?
|
||||
- url = main_app.notify_producers_admin_order_cycle_path
|
||||
- confirm_msg = "#{t('.notify_producers_tip')} #{t(:are_you_sure)}"
|
||||
%a.button.icon-email.with-tip{ href: url, data: { method: 'post', confirm: confirm_msg }, 'data-powertip': t('.notify_producers_tip') }
|
||||
|
||||
%a.button.icon-email.with-tip{ href: url, data: { method: :post, "ujs-navigate": "false", confirm: confirm_msg }, 'data-powertip': t('.notify_producers_tip') }
|
||||
= mails_sent ? t('.re_notify_producers') : t(:notify_producers)
|
||||
- if mails_sent
|
||||
.badge.icon-ok.success
|
||||
|
||||
6
app/views/admin/shared/_flashes.html.haml
Normal file
6
app/views/admin/shared/_flashes.html.haml
Normal file
@@ -0,0 +1,6 @@
|
||||
#flashes
|
||||
- if defined? flashes
|
||||
- flashes.each do |type, msg|
|
||||
.animate-show{"data-controller": "flash"}
|
||||
.flash{type: "#{type}", class: "#{type}"}
|
||||
%span= msg
|
||||
8
app/views/admin/shared/_stimulus_page_controls.html.haml
Normal file
8
app/views/admin/shared/_stimulus_page_controls.html.haml
Normal file
@@ -0,0 +1,8 @@
|
||||
= select_tag :per_page,
|
||||
options_for_select([15, 50, 100].collect{|num| [t('js.admin.orders.index.per_page', results: num), num] }, params[:per_page]),
|
||||
{ class: "no-search primary per-page-dropdown", data: { controller: "tom-select search", action: "change->search#changePerPage" } }
|
||||
|
||||
- if pagy
|
||||
%span.per-page-feedback
|
||||
= t("spree.admin.orders.index.results_found", number: pagy.count)
|
||||
= t("spree.admin.orders.index.viewing", start: pagy.from, end: pagy.to )
|
||||
22
app/views/admin/shared/_stimulus_pagination.html.haml
Normal file
22
app/views/admin/shared/_stimulus_pagination.html.haml
Normal file
@@ -0,0 +1,22 @@
|
||||
- link = pagy_link_proc(pagy)
|
||||
|
||||
.pagination{ "data-controller": "search" }
|
||||
- if pagy.prev
|
||||
%button{ data: { action: 'click->search#changePage', page: pagy.prev } }!= pagy_t('pagy.nav.prev')
|
||||
- else
|
||||
%button.disabled{disabled: "disabled"}!= pagy_t('pagy.nav.prev')
|
||||
|
||||
- pagy.series.each do |item| # series example: [1, :gap, 7, 8, "9", 10, 11, :gap, 36]
|
||||
- if item.is_a?(Integer) # page link
|
||||
%button{ data: { action: 'click->search#changePage', page: item } }= item
|
||||
|
||||
- elsif item.is_a?(String) # current page
|
||||
%button.active= item
|
||||
|
||||
- elsif item == :gap # page gap
|
||||
%span.pagination-ellipsis!= pagy_t('pagy.nav.gap')
|
||||
|
||||
- if pagy.next
|
||||
%button{ data: { action: 'click->search#changePage', page: pagy.next } }!= pagy_t('pagy.nav.next')
|
||||
- else
|
||||
%button.disabled.pagination-next{disabled: "disabled"}!= pagy_t('pagy.nav.next')
|
||||
@@ -1,5 +1,6 @@
|
||||
%div{"data-controller": "tooltip"}
|
||||
%a{"data-tooltip-target": "element", "data-action": "mouseenter->tooltip#showTooltip mouseleave->tooltip#hideTooltip"}= t('admin.whats_this')
|
||||
%a{"data-tooltip-target": "element"}
|
||||
= t('admin.whats_this')
|
||||
.tooltip-container
|
||||
.tooltip{"data-tooltip-target": "tooltip"}
|
||||
= sanitize tooltip_text
|
||||
|
||||
@@ -16,5 +16,5 @@
|
||||
%input#add_quantity.fullwidth{ type: 'number', min: 1, ng: { model: 'newItem.quantity' } }
|
||||
%td
|
||||
.actions
|
||||
%a.icon-plus.button.fullwidth{ href: 'javascript:void(0)', method: :post, ng: { click: 'addSubscriptionLineItem()' } }
|
||||
%a.icon-plus.button.fullwidth{ href: 'javascript:void(0)', ng: { click: 'addSubscriptionLineItem()' } }
|
||||
= t('.add')
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
.admin-current-terms-of-service
|
||||
- if @current_file
|
||||
%p= t(".current_terms_html", tos_link: link_to(t(".terms_of_service"), @current_file.attachment), datetime: @current_file.updated_at)
|
||||
%p= link_to t(".delete"), main_app.admin_terms_of_service_files_path, method: "delete", data: { confirm: t(".confirm_delete") }
|
||||
%p= link_to t(".delete"), main_app.admin_terms_of_service_files_path, data: { method: :delete, "ujs-navigate": "false", confirm: t(".confirm_delete") }
|
||||
- else
|
||||
%p
|
||||
= t(".no_files")
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
- content_for :page_actions do
|
||||
%li
|
||||
= link_to_with_icon 'icon-envelope-alt', t("spree.admin.mail_methods.send_testmail"), testmail_admin_mail_methods_path, method: :post, title: t("spree.admin.mail_methods.send_testmail"), class: 'send_mail button no-text'
|
||||
= link_to_with_icon 'icon-envelope-alt', t("spree.admin.mail_methods.send_testmail"), testmail_admin_mail_methods_path,
|
||||
data: { method: :post, "ujs-navigate": "false" }, title: t("spree.admin.mail_methods.send_testmail"), class: 'send_mail button no-text'
|
||||
|
||||
= render partial: 'spree/shared/error_messages', locals: { target: @mail_method }
|
||||
|
||||
|
||||
25
app/views/spree/admin/orders/_bulk_actions.html.haml
Normal file
25
app/views/spree/admin/orders/_bulk_actions.html.haml
Normal file
@@ -0,0 +1,25 @@
|
||||
.ofn-drop-down-with-prepend
|
||||
.ofn-drop-down-prepend.disabled{ "data-checked-target": "disable" }
|
||||
%span{ "data-controller": "checked-feedback", "data-checked-feedback-translation-value": "spree.admin.orders.index.selected" }
|
||||
= t("spree.admin.orders.index.selected", count: 0)
|
||||
|
||||
%button.plain.ofn-drop-down.disabled{ "data-checked-target": "disable" }
|
||||
%span{ class: 'icon-reorder' }
|
||||
="#{t('admin.actions')}".html_safe
|
||||
%span.toggle-off.icon-caret-up
|
||||
%span.toggle-on.icon-caret-down
|
||||
|
||||
%div.menu.dropdown-content
|
||||
%div.menu_item
|
||||
%span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "resend_confirmation" }
|
||||
= t('spree.admin.orders.index.resend_confirmation')
|
||||
- if Spree::Config[:enable_invoices?]
|
||||
%div.menu_item
|
||||
%span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "send_invoice" }
|
||||
= t('spree.admin.orders.index.send_invoice')
|
||||
%div.menu_item
|
||||
%span.name{ "data-controller": "bulk-actions", "data-action": "click->bulk-actions#perform", "data-bulk-actions-reflex-value": "Admin::Orders#bulk_invoice" }
|
||||
= t('spree.admin.orders.index.print_invoices')
|
||||
%div.menu_item
|
||||
%span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "cancel_orders" }
|
||||
= t('spree.admin.orders.index.cancel_orders')
|
||||
@@ -1,59 +1,61 @@
|
||||
%div.admin-orders-index-search
|
||||
= form_tag spree.admin_orders_url, {name: "orders_form", "ng-submit" => "fetchResults()"} do
|
||||
%div.admin-orders-index-search{ "data-controller": "search", "data-search-restore-value": @stored_query }
|
||||
= form_with url: spree.admin_orders_url, id: "orders_form", method: :get, data: { remote: true, "search-target": "form" } do
|
||||
= hidden_field_tag :page, 1, class: "page"
|
||||
= hidden_field_tag :per_page, 15, class: "per-page"
|
||||
= hidden_field_tag "[q][s]", params.dig(:q, :s) || "completed_at desc", class: "sort", "data-default": "completed_at desc"
|
||||
|
||||
.field-block.alpha.four.columns
|
||||
.date-range-filter.field
|
||||
= label_tag nil, t(:date_range)
|
||||
.date-range-fields{ data: { controller: "flatpickr", "flatpickr-mode-value": "range", "flatpickr-default-date": "{{ [q.completed_at_gteq, q.completed_at_lteq] }}" } }
|
||||
.date-range-fields{ data: { controller: "flatpickr", "flatpickr-mode-value": "range" } }
|
||||
= text_field_tag nil, nil, class: "datepicker", data: { "flatpickr-target": "instance", action: "flatpickr_clear@window->flatpickr#clear" }
|
||||
= text_field_tag "q[completed_at_gteq]", nil, "ng-model": "q.completed_at_gteq", data: { "flatpickr-target": "start" }, style: "display: none"
|
||||
= text_field_tag "q[completed_at_lteq]", nil, "ng-model": "q.completed_at_lteq", data: { "flatpickr-target": "end" }, style: "display: none"
|
||||
.field
|
||||
= label_tag nil, t(:status)
|
||||
%select2-watch-ng-model{'ng-model': 'q.state_eq'}
|
||||
= select_tag("q[state_eq]",
|
||||
options_for_select(Spree::Order.state_machines[:state].states.collect {|s| [t("spree.order_state.#{s.name}"), s.value]}),
|
||||
{include_blank: true, class: 'select2', 'ng-model' => 'q.state_eq'})
|
||||
= select_tag("q[state_eq]",
|
||||
options_for_select(Spree::Order.state_machines[:state].states.collect {|s| [t("spree.order_state.#{s.name}"), s.value]}),
|
||||
{ include_blank: true, class: "primary", "data-controller": "tom-select" })
|
||||
.four.columns
|
||||
.field
|
||||
= label_tag "q_number_cont", t(:order_number)
|
||||
= text_field_tag "q[number_cont]", nil, "ng-model" => "q.number_cont", "ng-keypress" => "$event.keyCode === 13 && fetchResults()"
|
||||
= text_field_tag "q[number_cont]", nil
|
||||
.field
|
||||
= label_tag "q_email_cont", t(:email)
|
||||
= email_field_tag "q[email_cont]", nil, "ng-model" => "q.email_cont", "ng-keypress" => "$event.keyCode === 13 && fetchResults()"
|
||||
= email_field_tag "q[email_cont]", nil
|
||||
.four.columns
|
||||
.field
|
||||
= label_tag "q_bill_address_firstname_start", t(:first_name_begins_with)
|
||||
= text_field_tag "q[bill_address_firstname_start]", nil, size: 25, "ng-model" => "q.bill_address_firstname_start", "ng-keypress" => "$event.keyCode === 13 && fetchResults()"
|
||||
= text_field_tag "q[bill_address_firstname_start]", nil, size: 25
|
||||
.field
|
||||
= label_tag "q_bill_address_lastname_start", t(:last_name_begins_with)
|
||||
= text_field_tag "q[bill_address_lastname_start]", nil, size: 25, "ng-model" => "q.bill_address_lastname_start", "ng-keypress" => "$event.keyCode === 13 && fetchResults()"
|
||||
= text_field_tag "q[bill_address_lastname_start]", nil, size: 25
|
||||
.omega.four.columns
|
||||
.field.checkbox
|
||||
.field.checkbox.inline-checkbox
|
||||
%label
|
||||
= check_box_tag "q[completed_at_not_null]", 1, true, {'ng-model' => 'q.completed_at_not_null'}
|
||||
= check_box_tag "q[completed_at_not_null]", 1, true
|
||||
= t(:show_only_complete_orders)
|
||||
.field
|
||||
= label_tag nil, t(:shipping_method)
|
||||
%select2-watch-ng-model{'ng-model': 'q.shipping_method_id'}
|
||||
= select_tag("q[shipping_method_id]",
|
||||
options_for_select(Spree::ShippingMethod.managed_by(spree_current_user).collect {|s| [t("spree.shipping_method_names.#{s.name}"), s.id]}),
|
||||
{include_blank: true, class: 'select2', 'ng-model': 'q.shipping_method_id'})
|
||||
= select_tag(:shipping_method_id,
|
||||
options_for_select(Spree::ShippingMethod.managed_by(spree_current_user).collect {|s| [t("spree.shipping_method_names.#{s.name}"), s.id]}),
|
||||
{ include_blank: true, class: "primary", "data-controller": "tom-select" })
|
||||
.field-block.alpha.eight.columns
|
||||
= label_tag nil, t(:distributors)
|
||||
%select2-watch-ng-model{'ng-model': 'q.distributor_id_in'}
|
||||
= select_tag("q[distributor_id_in]",
|
||||
options_for_select(Enterprise.is_distributor.managed_by(spree_current_user).map {|e| [e.name, e.id]}, params[:distributor_ids]),
|
||||
{class: "select2 fullwidth", multiple: true, 'ng-model' => 'q.distributor_id_in'})
|
||||
= select_tag("q[distributor_id_in]",
|
||||
options_for_select(Enterprise.is_distributor.managed_by(spree_current_user).map {|e| [e.name, e.id]}, params[:distributor_ids]),
|
||||
{ class: "fullwidth", multiple: true, data: { controller: "tom-select", "tom-select-options-value": { plugins: ['remove_button'], maxItems: nil } }})
|
||||
.field-block.omega.eight.columns
|
||||
= label_tag nil, t(:order_cycles)
|
||||
%select2-watch-ng-model{'ng-model': 'q.order_cycle_id_in'}
|
||||
= select_tag("q[order_cycle_id_in]",
|
||||
options_for_select(OrderCycle.managed_by(spree_current_user).where('order_cycles.orders_close_at is not null').order('order_cycles.orders_close_at DESC').map {|oc| [oc.name, oc.id]}, params[:order_cycle_ids]),
|
||||
{class: "select2 fullwidth", multiple: true, 'ng-model' => 'q.order_cycle_id_in'})
|
||||
= select_tag("q[order_cycle_id_in]",
|
||||
options_for_select(OrderCycle.managed_by(spree_current_user).where('order_cycles.orders_close_at is not null').order('order_cycles.orders_close_at DESC').map {|oc| [oc.name, oc.id]}, params[:order_cycle_ids]),
|
||||
{ class: "fullwidth", multiple: true, data: { controller: "tom-select", "tom-select-options-value": { plugins: ['remove_button'], maxItems: nil } }})
|
||||
.clearfix
|
||||
.actions.filter-actions
|
||||
%a.button.icon-search{'ng-click' => 'fetchResults()'}
|
||||
= t(:filter_results)
|
||||
%a.button{'ng-click' => 'clearFilters()', "id": "clear_filters_button"}
|
||||
= t(:clear_filters)
|
||||
|
||||
.actions.filter-actions{ style: "column-gap: 0" }
|
||||
.eight.columns.alpha
|
||||
%button.float-right.mr-0{type: "submit", class: "button"}
|
||||
%i.icon-search
|
||||
= t(:filter_results)
|
||||
.eight.columns.omega
|
||||
%button.float-left{"id": "clear_filters_button", type: "button", "data-controller": "search", "data-action": "click->search#reset" }
|
||||
= t(:clear_filters)
|
||||
|
||||
30
app/views/spree/admin/orders/_table.html.haml
Normal file
30
app/views/spree/admin/orders/_table.html.haml
Normal file
@@ -0,0 +1,30 @@
|
||||
.row.index-controls
|
||||
%div{ style: "display: flex; justify-content: space-between;" }
|
||||
= render partial: "bulk_actions"
|
||||
.per-page.right
|
||||
= render partial: 'admin/shared/stimulus_page_controls', locals: { pagy: pagy }
|
||||
|
||||
%table#listing_orders.index.responsive{width: "100%" }
|
||||
%colgroup
|
||||
%col{style: "width: 3%"}
|
||||
%thead
|
||||
%tr
|
||||
%th
|
||||
%input#selectAll{ type: 'checkbox', data: { "checked-target": "all", action: "change->checked#toggleAll" } }
|
||||
%th
|
||||
= t(:products_distributor)
|
||||
|
||||
- columns = ['completed_at', 'number', 'state', 'payment_state', 'shipment_state', 'email', 'bill_address_lastname', 'total']
|
||||
|
||||
= render partial: "spree/admin/shared/stimulus_sortable_header", collection: columns, as: :column,
|
||||
locals: { sorted: params.dig(:q, :s), default: "completed_at desc" }
|
||||
|
||||
%th.actions
|
||||
%tbody
|
||||
= render partial: "table_row", collection: orders, as: :order
|
||||
|
||||
|
||||
- if pagy&.count&.positive?
|
||||
= render partial: "admin/shared/stimulus_pagination", locals: { pagy: pagy }
|
||||
- else
|
||||
.no-objects-found= t('spree.admin.orders.index.no_orders_found')
|
||||
53
app/views/spree/admin/orders/_table_row.html.haml
Normal file
53
app/views/spree/admin/orders/_table_row.html.haml
Normal file
@@ -0,0 +1,53 @@
|
||||
%tr{ id: dom_id(order), class: "state-#{order.state}" }
|
||||
%td.align-center
|
||||
%input{type: 'checkbox', value: order.id, name: 'bulk_ids[]', "data-checked-target": "checkbox", "data-action": "change->checked#toggleCheckbox" }
|
||||
%td.align-center
|
||||
= order.distributor.name
|
||||
%td.align-center
|
||||
= I18n.l(order.completed_at, format: '%B %d, %Y') if order.completed_at
|
||||
%td
|
||||
%a{ href: edit_admin_order_path(order) }
|
||||
= order.number
|
||||
- if order.special_instructions.present?
|
||||
%div
|
||||
%br
|
||||
%div{ "data-controller": "tooltip", "data-tooltip-tip-value": order.special_instructions.to_s }
|
||||
%span.icon-warning-sign{ "data-tooltip-target": "element" }
|
||||
= t('spree.admin.orders.index.note')
|
||||
%td.align-center
|
||||
%span.state{ class: order.state.to_s }
|
||||
= t('js.admin.orders.order_state.' + order.state.to_s)
|
||||
%td.align-center
|
||||
- if order.payment_state
|
||||
%span.state{class: 'order.payment_state'}
|
||||
%a{href: spree.admin_order_payments_path(order) }
|
||||
= t('js.admin.orders.payment_states.' + order.payment_state.to_s)
|
||||
- if order.display_outstanding_balance
|
||||
%span
|
||||
= "(#{order.display_outstanding_balance})"
|
||||
%td.align-center
|
||||
- if order.shipment_state
|
||||
%span.state{class: order.shipment_state.to_s}
|
||||
= t('js.admin.orders.shipment_states.' + order.shipment_state.to_s)
|
||||
%td
|
||||
%a{ href: "mailto:#{order.email}", target: "_blank" }
|
||||
= order.email
|
||||
%td
|
||||
= order.bill_address.full_name
|
||||
%td.align-center
|
||||
%span
|
||||
= order.display_total
|
||||
%td.actions
|
||||
%div.row-loading-icons
|
||||
- if local_assigns[:success]
|
||||
%i.success.icon-ok-sign{"data-controller": "ephemeral"}
|
||||
%a.icon_link.with-tip.icon-edit.no-text{href: edit_admin_order_path(order), 'ofn-with-tip' => t('spree.admin.orders.index.edit')}
|
||||
- if order.ready_to_ship?
|
||||
%div{ "data-controller": "tooltip", "data-tooltip-tip-value": t('spree.admin.orders.index.ship') }
|
||||
%button.icon-road.icon_link.with-tip.no-text{"data-reflex": "click->Admin::OrdersReflex#ship", "data-id": order.id.to_s,
|
||||
"data-tooltip-target": "element" }
|
||||
|
||||
- if order.payment_required? && order.pending_payments.reject(&:requires_authorization?).any?
|
||||
%div{ "data-controller": "tooltip", "data-tooltip-tip-value": t('spree.admin.orders.index.capture') }
|
||||
%button.icon-capture.icon_link.no-text{"data-reflex": "click->Admin::OrdersReflex#capture", "data-id": order.id.to_s,
|
||||
"data-tooltip-target": "element" }
|
||||
@@ -0,0 +1,7 @@
|
||||
%p.message
|
||||
= t('js.admin.orders.index.bulk_invoice_created')
|
||||
|
||||
%br
|
||||
|
||||
%a.button.primary{ target: '_blank', href: invoice_url }
|
||||
= t('js.admin.orders.index.view_file')
|
||||
15
app/views/spree/admin/orders/bulk/_invoice_modal.html.haml
Normal file
15
app/views/spree/admin/orders/bulk/_invoice_modal.html.haml
Normal file
@@ -0,0 +1,15 @@
|
||||
%div{ id: "bulk_invoices_modal", "data-controller": "modal", "data-modal-instant-value": true, "data-action": "keyup@document->modal#closeIfEscapeKey" }
|
||||
.reveal-modal-bg.fade{ "data-modal-target": "background", "data-action": "click->modal#remove" }
|
||||
.reveal-modal.fade.tiny.help-modal{ "data-modal-target": "modal" }
|
||||
%div.fullwidth.align-center
|
||||
%h4.modal-title
|
||||
= t('js.admin.orders.index.compiling_invoices')
|
||||
%br
|
||||
%br
|
||||
.modal-content
|
||||
.modal-loading
|
||||
%img.spinner{ src: image_path("/spinning-circles.svg") }
|
||||
%br
|
||||
%br
|
||||
%p= t('js.admin.orders.index.please_wait')
|
||||
%br
|
||||
@@ -8,8 +8,8 @@
|
||||
%li= event_links
|
||||
= render partial: 'spree/admin/shared/order_links'
|
||||
- if can?(:admin, Spree::Order)
|
||||
%li{"ng-controller" => "ordersCtrl"}
|
||||
%a.button.icon-arrow-left{icon: 'icon-arrow-left', ng: { href: admin_orders_path }}
|
||||
%li
|
||||
%a.button.icon-arrow-left{icon: 'icon-arrow-left', href: admin_orders_path }
|
||||
= t(:back_to_orders_list)
|
||||
|
||||
= render partial: "spree/admin/shared/order_page_title"
|
||||
|
||||
@@ -1,137 +1,33 @@
|
||||
- content_for :page_title do
|
||||
= t('.listing_orders')
|
||||
|
||||
- content_for :minimal_js, true
|
||||
|
||||
- content_for :page_actions do
|
||||
%li
|
||||
= button_link_to t('.new_order'), spree.new_admin_order_url, icon: 'icon-plus', id: 'admin_new_order'
|
||||
|
||||
= render partial: 'spree/admin/shared/order_sub_menu'
|
||||
|
||||
- content_for :main_ng_app_name do
|
||||
= "ofn.admin"
|
||||
|
||||
- content_for :main_ng_ctrl_name do
|
||||
= "ordersCtrl"
|
||||
|
||||
- content_for :table_filter_title do
|
||||
= t(:search)
|
||||
|
||||
- content_for :table_filter do
|
||||
= render partial: 'filters'
|
||||
|
||||
.row.index-controls{'ng-show' => '!RequestMonitor.loading && orders.length > 0'}
|
||||
%div{style: "display: flex; justify-content: space-between;"}
|
||||
.ofn-drop-down-with-prepend
|
||||
.ofn-drop-down-prepend{"ng-class": "selected_orders.length == 0 ? 'disabled' : ''"}
|
||||
{{ "spree.admin.orders.index.selected" | t:{count: selected_orders.length} }}
|
||||
.ofn-drop-down{"ng-class": "selected_orders.length == 0 ? 'disabled' : ''"}
|
||||
%span{ :class => 'icon-reorder' }
|
||||
="#{t('admin.actions')}".html_safe
|
||||
%span{ 'ng-class' => "expanded && 'icon-caret-up' || !expanded && 'icon-caret-down'" }
|
||||
%div.menu{ 'ng-show' => "expanded" }
|
||||
%div.menu_item
|
||||
%span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "resend_confirmation" }
|
||||
= t('.resend_confirmation')
|
||||
- if Spree::Config[:enable_invoices?]
|
||||
%div.menu_item
|
||||
%span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "send_invoice" }
|
||||
= t('.send_invoice')
|
||||
%div.menu_item
|
||||
%span.name.invoices-modal{'ng-controller' => 'bulkInvoiceCtrl', 'ng-click' => 'createBulkInvoice()' }
|
||||
= t('.print_invoices')
|
||||
%div.menu_item
|
||||
%span.name{ "data-controller": "modal-link", "data-action": "click->modal-link#open", "data-modal-link-target-value": "cancel_orders" }
|
||||
= t('.cancel_orders')
|
||||
|
||||
= render partial: 'admin/shared/angular_per_page_controls', locals: { position: "right", model: "orders" }
|
||||
|
||||
%table#listing_orders.index.responsive{width: "100%", 'ng-init' => 'initialise()', 'ng-show' => "!RequestMonitor.loading && orders.length > 0" }
|
||||
%colgroup
|
||||
%col{style: "width: 3%"}
|
||||
%thead
|
||||
%tr
|
||||
%th
|
||||
%input#selectAll{type: 'checkbox', 'ng-change' => 'toggleAll()', 'ng-model' => 'select_all'}
|
||||
%th
|
||||
= t(:products_distributor)
|
||||
%th
|
||||
%a{'ng-click' => "sortOptions.toggle('completed_at')"}
|
||||
= t(:completed_at, scope: 'activerecord.attributes.spree/order')
|
||||
%span{'ng-show' => "sorting == 'completed_at asc'"}= "▲".html_safe
|
||||
%span{'ng-show' => "sorting == 'completed_at desc' || sorting === undefined"}= "▼".html_safe
|
||||
|
||||
- ['number', 'state', 'payment_state', 'shipment_state', 'email', 'bill_address_lastname', 'total'].each do |column_name|
|
||||
%th
|
||||
= render partial: 'spree/admin/shared/sortable_header', locals: {column_name: column_name}
|
||||
|
||||
%th.actions
|
||||
%tbody
|
||||
%tr{ng: {repeat: 'order in orders track by order.id', class: {even: "'even'", odd: "'odd'"}}, 'ng-class' => "{'state-{{order.state}}': true, 'row-loading': rowStatus[order.id] == 'loading'}"}
|
||||
%td.align-center
|
||||
%input{type: 'checkbox', 'ng-model' => 'checkboxes[order.id]', 'ng-change' => 'toggleSelection(order.id)', value: '{{order.id}}', name: 'order_ids[]'}
|
||||
%td.align-center
|
||||
{{order.distributor_name}}
|
||||
%td.align-center
|
||||
{{order.completed_at}}
|
||||
%td
|
||||
%a{'ng-href' => '{{order.edit_path}}'}
|
||||
{{order.number}}
|
||||
%div{'ng-if' => 'order.special_instructions'}
|
||||
%br
|
||||
%span.icon-warning-sign{'ofn-with-tip' => "{{order.special_instructions}}"}
|
||||
= t('.note')
|
||||
%td.align-center
|
||||
%span.state{'ng-class' => 'order.state'}
|
||||
{{'js.admin.orders.order_state.' + order.state | t}}
|
||||
%td.align-center
|
||||
%span.state{'ng-class' => 'order.payment_state', 'ng-if' => 'order.payment_state'}
|
||||
%a{'ng-href' => '{{order.payments_path}}' }
|
||||
{{'js.admin.orders.payment_states.' + order.payment_state | t}}
|
||||
%span{'ng-if' => 'order.display_outstanding_balance'}
|
||||
({{order.display_outstanding_balance}})
|
||||
%td.align-center
|
||||
%span.state{'ng-class' => 'order.shipment_state', 'ng-if' => 'order.shipment_state'}
|
||||
{{'js.admin.orders.shipment_states.' + order.shipment_state | t}}
|
||||
%td
|
||||
%a{ ng: { href: "mailto:{{order.email}}" } }
|
||||
{{order.email}}
|
||||
%td
|
||||
{{order.full_name}}
|
||||
%td.align-center
|
||||
%span{'ng-bind-html' => 'order.display_total'}
|
||||
%td.actions
|
||||
%div.row-loading-icons
|
||||
%div{ng: {show: 'rowStatus[order.id] == "loading"', cloak: true}, style: "width: 30px; height: 30px;"}
|
||||
= render partial: "components/spinner"
|
||||
%i.success.icon-ok-sign{ng: {show: 'rowStatus[order.id] == "success"'} }
|
||||
%i.error.icon-remove-sign.with-tip{ng: {show: 'rowStatus[order.id] == "error"'}, 'ofn-with-tip' => t('.order_not_updated')}
|
||||
%a.icon_link.with-tip.icon-edit.no-text{'ng-href' => '{{order.edit_path}}', 'data-action' => 'edit', 'ofn-with-tip' => t('.edit')}
|
||||
%div{'ng-if' => 'order.ready_to_ship'}
|
||||
%button.icon-road.icon_link.with-tip.no-text{'ng-click' => 'shipOrder(order)', rel: 'nofollow', 'ofn-with-tip' => t('.ship')}
|
||||
%div{'ng-if' => 'order.ready_to_capture'}
|
||||
%button.icon-capture.icon_link.no-text{'ng-click' => 'capturePayment(order)', rel: 'nofollow', 'ofn-with-tip' => t('.capture')}
|
||||
|
||||
.sixteen.columns.alpha#loading{ 'ng-show' => 'RequestMonitor.loading' }
|
||||
= render partial: "components/admin_spinner"
|
||||
%h1
|
||||
= t('.loading')
|
||||
|
||||
%div{'ng-show' => "!RequestMonitor.loading && orders.length > 0" }
|
||||
= render partial: 'admin/shared/angular_pagination'
|
||||
|
||||
.no-objects-found{'ng-show' => "!RequestMonitor.loading && orders.length == 0"}
|
||||
= t('.no_orders_found')
|
||||
#orders-index{"data-controller": "search checked"}
|
||||
= render partial: "table", locals: { pagy: @pagy, orders: @orders }
|
||||
|
||||
= render 'spree/admin/shared/custom-confirm'
|
||||
|
||||
= render ConfirmModalComponent.new(id: "resend_confirmation", confirm_actions: "click->resend-confirmation-email#confirm", controllers: "resend-confirmation-email") do
|
||||
= render ConfirmModalComponent.new(id: "resend_confirmation", confirm_actions: "click->bulk-actions#perform", controller: "bulk-actions", reflex: "Admin::Orders#resend_confirmation_emails") do
|
||||
.margin-bottom-30
|
||||
= t('.resend_confirmation_confirm_html')
|
||||
|
||||
= render ConfirmModalComponent.new(id: "send_invoice", confirm_actions: "click->send-invoice#confirm", controllers: "send-invoice") do
|
||||
= render ConfirmModalComponent.new(id: "send_invoice", confirm_actions: "click->bulk-actions#perform", controller: "bulk-actions", reflex: "Admin::Orders#send_invoices") do
|
||||
.margin-bottom-30
|
||||
= t('.send_invoice_confirm_html')
|
||||
|
||||
= render ConfirmModalComponent.new(id: "cancel_orders", confirm_actions: "click->cancel-orders#confirm", controllers: "cancel-orders", message: "spree/admin/orders/messages/cancel_orders") do
|
||||
= render ConfirmModalComponent.new(id: "cancel_orders", confirm_actions: "click->bulk-actions#perform", controller: "bulk-actions", reflex: "Admin::Orders#cancel_orders", message: "spree/admin/orders/messages/cancel_orders") do
|
||||
.margin-bottom-30
|
||||
= t("js.admin.orders.cancel_the_order_html")
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
.modal-message
|
||||
.form
|
||||
%form{ "data-bulk-actions-target": "extraParams" }
|
||||
%input{ type: "checkbox", name: "send_cancellation_email", value: "1", id: "send_cancellation_email", checked: "true" }
|
||||
%label{ for: "send_cancellation_email" }
|
||||
= t("js.admin.orders.cancel_the_order_send_cancelation_email")
|
||||
%br
|
||||
%input{ type: "checkbox", name: "restock_items", id: "restock_items", checked: "true" }
|
||||
%input{ type: "checkbox", name: "restock_items", value: "1", id: "restock_items", checked: "true" }
|
||||
%label{ for: "restock_items" }
|
||||
= t("js.admin.orders.restock_items")
|
||||
.margin-bottom-30
|
||||
|
||||
@@ -16,4 +16,5 @@
|
||||
%span{class: "state #{payment.state}"}= t(payment.state, scope: "spree.payment_states", default: payment.state.capitalize)
|
||||
%td.actions
|
||||
- payment.actions.each do |action|
|
||||
= link_to_with_icon "icon-#{action}", Spree.t(action), fire_admin_order_payment_path(@order, payment, e: action), method: :put, no_text: true, data: {action: action, disable_with: ""}
|
||||
= link_to_with_icon "icon-#{action}", Spree.t(action), fire_admin_order_payment_path(@order, payment, e: action),
|
||||
no_text: true, data: { method: :put, "ujs-navigate": "false", action: action, disable_with: "" }
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
- content_for :page_actions do
|
||||
%li
|
||||
- if @return_authorization.can_receive?
|
||||
= button_link_to t('.receive'), fire_admin_order_return_authorization_url(@order, @return_authorization, e: 'receive'), method: :put, data: { confirm: t('.are_you_sure') }, icon: 'icon-download-alt'
|
||||
= button_link_to t('.receive'), fire_admin_order_return_authorization_url(@order, @return_authorization, e: 'receive'),
|
||||
data: { method: :put, "ujs-navigate": "false", confirm: t('.are_you_sure') }, icon: 'icon-download-alt'
|
||||
%li
|
||||
- if @return_authorization.can_cancel?
|
||||
= button_link_to t('actions.cancel'), fire_admin_order_return_authorization_url(@order, @return_authorization, e: 'cancel'), method: :put, data: { confirm: t('.are_you_sure') }, icon: 'icon-remove'
|
||||
= button_link_to t('actions.cancel'), fire_admin_order_return_authorization_url(@order, @return_authorization, e: 'cancel'),
|
||||
data: { method: :put, "ujs-navigate": "false", confirm: t('.are_you_sure') }, icon: 'icon-remove'
|
||||
|
||||
= render partial: 'spree/admin/shared/order_page_title'
|
||||
= render partial: 'spree/admin/shared/order_tabs', locals: { current: 'Return Authorizations' }
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
= csrf_meta_tags
|
||||
= action_cable_meta_tag
|
||||
|
||||
= action_cable_meta_tag
|
||||
|
||||
%title
|
||||
- if content_for? :html_title
|
||||
= yield :html_title
|
||||
@@ -20,7 +18,11 @@
|
||||
= stylesheet_pack_tag 'admin-styles', media: "screen, print"
|
||||
|
||||
= render "layouts/bugsnag_js"
|
||||
= javascript_include_tag 'admin/all'
|
||||
|
||||
- if content_for? :minimal_js
|
||||
= javascript_include_tag 'admin_minimal'
|
||||
- else
|
||||
= javascript_include_tag 'admin/all'
|
||||
|
||||
= render "spree/admin/shared/translations"
|
||||
= render "spree/admin/shared/routes"
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
%th
|
||||
%a{ "data-action": "click->search#changeSorting", "data-column": "#{column}", "data-current": sorted.to_s }
|
||||
= t("spree.admin.shared.sortable_header.#{column.to_s}")
|
||||
|
||||
- if sorted == "#{column} asc" || sorted.blank? && local_assigns[:default] == "#{column} asc"
|
||||
= "▲".html_safe
|
||||
- if sorted == "#{column} desc" || sorted.blank? && local_assigns[:default] == "#{column} desc"
|
||||
= "▼".html_safe
|
||||
@@ -1,7 +1,7 @@
|
||||
= tab :overview, label: 'dashboard', url: spree.admin_dashboard_path, icon: 'icon-dashboard'
|
||||
= tab :products, :properties, :inventory, :product_import, :images, :variants, :product_properties, :group_buy_options, :seo, url: admin_products_path, icon: 'icon-th-large'
|
||||
= tab :order_cycles, url: main_app.admin_order_cycles_path, icon: 'icon-refresh'
|
||||
= tab :orders, :subscriptions, :customer_details, :adjustments, :payments, :return_authorizations, url: admin_orders_path('q[s]' => 'completed_at desc'), icon: 'icon-shopping-cart'
|
||||
= tab :orders, :subscriptions, :customer_details, :adjustments, :payments, :return_authorizations, url: admin_orders_path, icon: 'icon-shopping-cart'
|
||||
= tab :reports, url: main_app.admin_reports_path, icon: 'icon-file'
|
||||
= tab :general_settings, :mail_methods, :tax_categories, :tax_rates, :tax_settings, :zones, :countries, :states, :payment_methods, :taxonomies, :shipping_methods, :shipping_categories, :enterprise_fees, :contents, :invoice_settings, :matomo_settings, :stripe_connect_settings, label: 'configuration', icon: 'icon-wrench', url: edit_admin_general_settings_path
|
||||
= tab :enterprises, :enterprise_relationships, :vouchers, :oidc_settings, url: main_app.admin_enterprises_path
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<script>
|
||||
if (Spree === undefined) {
|
||||
var Spree = {}
|
||||
}
|
||||
Spree.translations = <%==
|
||||
{:flatpickr_date_format => Spree.t(:flatpickr_date_format,
|
||||
:scope => 'date_picker',
|
||||
|
||||
@@ -4,14 +4,7 @@
|
||||
|
||||
#wrapper
|
||||
.flash-container
|
||||
- if flash[:error]
|
||||
.flash.error= flash[:error]
|
||||
- if notice
|
||||
.flash.notice= notice
|
||||
- if flash[:success]
|
||||
.flash.success= flash[:success]
|
||||
|
||||
= render partial: "shared/flashes"
|
||||
= render partial: "admin/shared/flashes", locals: { flashes: flash }
|
||||
|
||||
= render partial: "spree/layouts/admin/progress_spinner"
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
%head
|
||||
= render :partial => 'spree/admin/shared/head'
|
||||
|
||||
%body.admin
|
||||
%body.admin{ "data-turbo": "false" }
|
||||
- if content_for?(:main_ng_app_name)
|
||||
- if content_for?(:main_ng_ctrl_name)
|
||||
%div{ "ng-app" => yield(:main_ng_app_name).strip.html_safe, "ng-controller" => yield(:main_ng_ctrl_name).strip.html_safe }
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
%html{ lang: "en", "ng-csp": "no-unsafe-eval" }
|
||||
%head= render :partial => 'spree/admin/shared/head'
|
||||
%body.admin{"data-ajax-root-path" => main_app.root_path}
|
||||
%body.admin{"data-turbo": "false", "data-ajax-root-path": main_app.root_path}
|
||||
#wrapper
|
||||
- if flash[:error]
|
||||
.flash.error= flash[:error]
|
||||
- if notice
|
||||
.flash.notice= notice
|
||||
- if flash[:success]
|
||||
.flash.success= flash[:success]
|
||||
= render partial: "admin/shared/flashes", locals: { flashes: flash }
|
||||
|
||||
= render partial: "spree/layouts/admin/progress_spinner"
|
||||
|
||||
%header#header
|
||||
|
||||
8
app/webpacker/channels/session_channel.js
Normal file
8
app/webpacker/channels/session_channel.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import consumer from './consumer'
|
||||
import CableReady from 'cable_ready'
|
||||
|
||||
consumer.subscriptions.create("SessionChannel", {
|
||||
received(data) {
|
||||
if (data.cableReady) CableReady.perform(data.operations)
|
||||
}
|
||||
});
|
||||
@@ -1,20 +1,35 @@
|
||||
import ApplicationController from "./application_controller";
|
||||
|
||||
export default class extends ApplicationController {
|
||||
static targets = ["extraParams"]
|
||||
static values = { reflex: String }
|
||||
|
||||
connect() {
|
||||
super.connect();
|
||||
}
|
||||
|
||||
// abstract
|
||||
confirm(action) {
|
||||
this.stimulate(action, this.getOrdersIds());
|
||||
perform() {
|
||||
let params = { bulk_ids: this.getSelectedIds() };
|
||||
|
||||
if (this.hasExtraParamsTarget) {
|
||||
Object.assign(params, this.extraFormData())
|
||||
}
|
||||
|
||||
this.stimulate(this.reflexValue, params);
|
||||
}
|
||||
|
||||
// private
|
||||
getOrdersIds() {
|
||||
|
||||
getSelectedIds() {
|
||||
const checkboxes = document.querySelectorAll(
|
||||
"#listing_orders input[name='order_ids[]']:checked"
|
||||
"table input[name='bulk_ids[]']:checked"
|
||||
);
|
||||
return Array.from(checkboxes).map((checkbox) => checkbox.value);
|
||||
}
|
||||
|
||||
extraFormData() {
|
||||
if (this.extraParamsTarget.constructor.name !== "HTMLFormElement") { return {} }
|
||||
|
||||
return Object.fromEntries(new FormData(this.extraParamsTarget).entries())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import ApplicationController from "./application_controller";
|
||||
|
||||
export default class extends ApplicationController {
|
||||
connect() {
|
||||
super.connect();
|
||||
}
|
||||
|
||||
confirm() {
|
||||
const send_cancellation_email = document.querySelector(
|
||||
"#send_cancellation_email"
|
||||
).checked;
|
||||
const restock_items = document.querySelector("#restock_items").checked;
|
||||
const order_ids = [];
|
||||
|
||||
document
|
||||
.querySelectorAll("#listing_orders input[name='order_ids[]']:checked")
|
||||
.forEach((checkbox) => {
|
||||
order_ids.push(checkbox.value);
|
||||
});
|
||||
|
||||
const params = {
|
||||
order_ids: order_ids,
|
||||
send_cancellation_email: send_cancellation_email,
|
||||
restock_items: restock_items,
|
||||
};
|
||||
this.stimulate("CancelOrdersReflex#confirm", params).then(() =>
|
||||
window.location.reload()
|
||||
);
|
||||
}
|
||||
}
|
||||
55
app/webpacker/controllers/checked_controller.js
Normal file
55
app/webpacker/controllers/checked_controller.js
Normal file
@@ -0,0 +1,55 @@
|
||||
import { Controller } from "stimulus";
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = ["all", "checkbox", "disable"];
|
||||
static values = { count: Number };
|
||||
|
||||
connect() {
|
||||
this.toggleCheckbox();
|
||||
}
|
||||
|
||||
toggleAll() {
|
||||
this.checkboxTargets.forEach((checkbox) => {
|
||||
checkbox.checked = this.allTarget.checked;
|
||||
});
|
||||
|
||||
this.countValue = this.allTarget.checked ? this.checkboxTargets.length : 0;
|
||||
|
||||
this.#toggleDisabled();
|
||||
}
|
||||
|
||||
toggleCheckbox() {
|
||||
this.countValue = this.#checkedCount();
|
||||
this.allTarget.checked = this.#allChecked();
|
||||
|
||||
this.#toggleDisabled();
|
||||
}
|
||||
|
||||
countValueChanged() {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent("checked:updated", { detail: { count: this.countValue } })
|
||||
);
|
||||
}
|
||||
|
||||
// private
|
||||
|
||||
#checkedCount() {
|
||||
return this.checkboxTargets.filter((checkbox) => checkbox.checked).length;
|
||||
}
|
||||
|
||||
#allChecked() {
|
||||
return this.countValue === this.checkboxTargets.length;
|
||||
}
|
||||
|
||||
#toggleDisabled() {
|
||||
if (!this.hasDisableTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.#checkedCount() === 0) {
|
||||
this.disableTargets.forEach((element) => element.classList.add("disabled"));
|
||||
} else {
|
||||
this.disableTargets.forEach((element) => element.classList.remove("disabled"));
|
||||
}
|
||||
}
|
||||
}
|
||||
19
app/webpacker/controllers/checked_feedback_controller.js
Normal file
19
app/webpacker/controllers/checked_feedback_controller.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Controller } from "stimulus";
|
||||
|
||||
export default class extends Controller {
|
||||
static values = { translation: String };
|
||||
|
||||
connect() {
|
||||
window.addEventListener("checked:updated", this.updateFeedback);
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
window.removeEventListener("checked:updated", this.updateFeedback);
|
||||
}
|
||||
|
||||
updateFeedback = (event) => {
|
||||
this.element.textContent = I18n.t(this.translationValue, {
|
||||
count: event?.detail?.count ? event.detail.count : 0,
|
||||
});
|
||||
};
|
||||
}
|
||||
16
app/webpacker/controllers/ephemeral_controller.js
Normal file
16
app/webpacker/controllers/ephemeral_controller.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Controller } from "stimulus";
|
||||
|
||||
export default class extends Controller {
|
||||
connect() {
|
||||
setTimeout(this.fadeout, 1500);
|
||||
}
|
||||
|
||||
fadeout = () => {
|
||||
this.element.classList.add("animate-hide-500");
|
||||
setTimeout(this.remove, 500);
|
||||
};
|
||||
|
||||
remove = () => {
|
||||
this.element.remove();
|
||||
};
|
||||
}
|
||||
@@ -39,8 +39,8 @@ export default class extends Flatpickr {
|
||||
};
|
||||
|
||||
initialize() {
|
||||
const datetimepicker = this.enableTimeValue == true;
|
||||
const mode = this.modeValue == "range" ? "range" : "single";
|
||||
const datetimepicker = this.enableTimeValue === true;
|
||||
const mode = this.modeValue === "range" ? "range" : "single";
|
||||
// sets your language (you can also set some global setting for all time pickers)
|
||||
this.config = {
|
||||
altInput: true,
|
||||
@@ -54,40 +54,47 @@ export default class extends Flatpickr {
|
||||
plugins: this.plugins(mode, datetimepicker),
|
||||
mode,
|
||||
};
|
||||
window.addEventListener("flatpickr:change", this.onChangeEvent.bind(this));
|
||||
window.addEventListener("flatpickr:clear", this.clear.bind(this));
|
||||
}
|
||||
|
||||
clear(e) {
|
||||
this.fp.setDate(null);
|
||||
connect() {
|
||||
super.connect();
|
||||
window.addEventListener("flatpickr:change", this.onChangeEvent);
|
||||
window.addEventListener("flatpickr:clear", this.clear);
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
super.disconnect();
|
||||
window.removeEventListener("flatpickr:change", this.onChangeEvent);
|
||||
window.removeEventListener("flatpickr:clear", this.clear);
|
||||
}
|
||||
|
||||
clear = (e) => {
|
||||
this.fp.setDate(null);
|
||||
};
|
||||
|
||||
open() {
|
||||
this.fp.element.dispatchEvent(new Event("focus"));
|
||||
if (!this.fp.selectedDates.length) {
|
||||
this.setDefaultDateValue();
|
||||
}
|
||||
}
|
||||
onChangeEvent(e) {
|
||||
if (
|
||||
this.modeValue == "range" &&
|
||||
this.hasStartTarget &&
|
||||
this.hasEndTarget &&
|
||||
e.detail.startDate &&
|
||||
e.detail.endDate
|
||||
) {
|
||||
|
||||
onChangeEvent = (e) => {
|
||||
if (this.modeValue === "range" && this.hasStartTarget && this.hasEndTarget) {
|
||||
// date range mode
|
||||
this.startTarget.value = e.detail.startDate;
|
||||
this.endTarget.value = e.detail.endDate;
|
||||
this.fp.setDate([e.detail.startDate, e.detail.endDate]);
|
||||
if (e.detail) {
|
||||
this.startTarget.value = e.detail.startDate;
|
||||
this.endTarget.value = e.detail.endDate;
|
||||
}
|
||||
this.fp.setDate([this.startTarget.value, this.endTarget.value]);
|
||||
} else if (e.detail.date) {
|
||||
// single date mode
|
||||
this.fp.setDate(e.detail.date);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
change(selectedDates, dateStr, instance) {
|
||||
if (this.hasStartTarget && this.hasEndTarget && this.modeValue == "range") {
|
||||
if (this.hasStartTarget && this.hasEndTarget && this.modeValue === "range") {
|
||||
this.startTarget.value = selectedDates[0]
|
||||
? this.fp.formatDate(selectedDates[0], this.config.dateFormat)
|
||||
: "";
|
||||
@@ -112,11 +119,9 @@ export default class extends Flatpickr {
|
||||
|
||||
plugins = (mode, datetimepicker) => {
|
||||
const buttons = [{ label: Spree.translations.close }];
|
||||
if (mode == "single") {
|
||||
if (mode === "single") {
|
||||
buttons.unshift({
|
||||
label: datetimepicker
|
||||
? Spree.translations.now
|
||||
: Spree.translations.today,
|
||||
label: datetimepicker ? Spree.translations.now : Spree.translations.today,
|
||||
});
|
||||
}
|
||||
return [
|
||||
@@ -132,8 +137,8 @@ export default class extends Flatpickr {
|
||||
// Memorize index used for the 'Close' and 'Today|Now' buttons
|
||||
// it has index of 1 in case of single mode (ie. can set Today or Now date)
|
||||
// it has index of 0 in case of range mode (no Today or Now button)
|
||||
const closeButtonIndex = this.modeValue == "range" ? 0 : 1;
|
||||
const todayButtonIndex = this.modeValue == "range" ? null : 0;
|
||||
const closeButtonIndex = this.modeValue === "range" ? 0 : 1;
|
||||
const todayButtonIndex = this.modeValue === "range" ? null : 0;
|
||||
switch (index) {
|
||||
case todayButtonIndex:
|
||||
fp.setDate(new Date(), true);
|
||||
|
||||
@@ -11,7 +11,7 @@ export const useOpenAndCloseAsAModal = (controller) => {
|
||||
});
|
||||
}.bind(controller),
|
||||
|
||||
close: function () {
|
||||
close: function (_event, remove = false) {
|
||||
this.modalTarget.classList.remove("in");
|
||||
this.backgroundTarget.classList.remove("in");
|
||||
document.querySelector("body").classList.remove("modal-open");
|
||||
@@ -19,6 +19,7 @@ export const useOpenAndCloseAsAModal = (controller) => {
|
||||
setTimeout(() => {
|
||||
this.backgroundTarget.style.display = "none";
|
||||
this.modalTarget.style.display = "none";
|
||||
if (remove) { this.element.remove() }
|
||||
}, 200);
|
||||
}.bind(controller),
|
||||
|
||||
|
||||
@@ -3,13 +3,20 @@ import { useOpenAndCloseAsAModal } from "./mixins/useOpenAndCloseAsAModal";
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = ["background", "modal"];
|
||||
static values = { instant: { type: Boolean, default: false } }
|
||||
|
||||
connect() {
|
||||
useOpenAndCloseAsAModal(this);
|
||||
window.addEventListener("modal:close", this.close.bind(this));
|
||||
|
||||
if (this.instantValue) { this.open() }
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
window.removeEventListener("modal:close", this.close);
|
||||
}
|
||||
|
||||
remove(event) {
|
||||
this.close(event, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import BulkActionsController from "./bulk_actions_controller";
|
||||
|
||||
export default class extends BulkActionsController {
|
||||
connect() {
|
||||
super.connect();
|
||||
}
|
||||
|
||||
confirm() {
|
||||
super.confirm("BulkActionsInOrdersList#resend_confirmation_email");
|
||||
}
|
||||
}
|
||||
89
app/webpacker/controllers/search_controller.js
Normal file
89
app/webpacker/controllers/search_controller.js
Normal file
@@ -0,0 +1,89 @@
|
||||
import { Controller } from "stimulus";
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = ["form"];
|
||||
static values = { restore: String }; // Query string, eg: "?color=red&size=small"
|
||||
|
||||
connect() {
|
||||
this.#setup();
|
||||
}
|
||||
|
||||
changePage(event) {
|
||||
this.page.value = event.target.dataset.page;
|
||||
this.submitSearch();
|
||||
this.page.value = 1;
|
||||
}
|
||||
|
||||
changePerPage(event) {
|
||||
this.per_page.value = parseInt(event.target.value);
|
||||
this.submitSearch();
|
||||
}
|
||||
|
||||
changeSorting(event) {
|
||||
let current = event.target.dataset.current;
|
||||
let column = event.target.dataset.column;
|
||||
|
||||
this.sort.value = current === `${column} asc` ? `${column} desc` : `${column} asc`;
|
||||
|
||||
this.submitSearch();
|
||||
}
|
||||
|
||||
submitSearch() {
|
||||
this.form.requestSubmit();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.clearForm();
|
||||
this.submitSearch();
|
||||
}
|
||||
|
||||
clearForm() {
|
||||
this.form.reset();
|
||||
this.#clearCustomElements();
|
||||
if (this.page) this.page.value = 1;
|
||||
if (this.sort) this.sort.value = this.sort.dataset.default;
|
||||
}
|
||||
|
||||
// private
|
||||
|
||||
#setup() {
|
||||
if (this.hasFormTarget) {
|
||||
this.form = this.formTarget;
|
||||
this.form.controller = this;
|
||||
if (this.restoreValue) this.#restoreFormState(this.form, this.restoreValue);
|
||||
} else {
|
||||
this.form = document.querySelector("form[data-search-target=form]");
|
||||
}
|
||||
|
||||
this.page = this.form.querySelector(".page");
|
||||
this.per_page = this.form.querySelector(".per-page");
|
||||
this.sort = this.form.querySelector(".sort");
|
||||
}
|
||||
|
||||
#clearCustomElements() {
|
||||
window.dispatchEvent(new CustomEvent("flatpickr:clear"));
|
||||
|
||||
this.form.querySelectorAll(".tomselected").forEach((select) => {
|
||||
select.tomselect?.clear();
|
||||
});
|
||||
}
|
||||
|
||||
#restoreFormState(form, queryString) {
|
||||
const params = new URLSearchParams(queryString);
|
||||
|
||||
// Apply non-checkbox values
|
||||
for (const [key, value] of params.entries()) {
|
||||
const input = form.elements[key];
|
||||
if (input && input.type !== "checkbox") input.value = value;
|
||||
}
|
||||
|
||||
// Deal with checkbox values
|
||||
form.querySelectorAll("[type=checkbox]").forEach((checkbox) => {
|
||||
checkbox.checked = !!params.get(checkbox.name);
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
window.dispatchEvent(new CustomEvent("flatpickr:change"));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { Controller } from "stimulus";
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = ["all", "checkbox"];
|
||||
|
||||
connect() {
|
||||
this.toggleCheckbox()
|
||||
}
|
||||
|
||||
toggleAll() {
|
||||
this.checkboxTargets.forEach(checkbox => {
|
||||
checkbox.checked = this.allTarget.checked;
|
||||
});
|
||||
}
|
||||
|
||||
toggleCheckbox() {
|
||||
this.allTarget.checked = this.checkboxTargets.every(checkbox => checkbox.checked);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import BulkActionsController from "./bulk_actions_controller";
|
||||
|
||||
export default class extends BulkActionsController {
|
||||
connect() {
|
||||
super.connect();
|
||||
}
|
||||
|
||||
confirm() {
|
||||
super.confirm("BulkActionsInOrdersList#send_invoice");
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ export default class extends Controller {
|
||||
maxOptions: null,
|
||||
plugins: ["dropdown_input"],
|
||||
allowEmptyOption: true,
|
||||
closeAfterSelect: true,
|
||||
onItemAdd: function () {
|
||||
this.setTextboxValue("");
|
||||
},
|
||||
@@ -34,7 +35,6 @@ export default class extends Controller {
|
||||
|
||||
#placeholder() {
|
||||
const optionsArray = [...this.element.options];
|
||||
return optionsArray.find((option) => [null, ""].includes(option.value))
|
||||
?.text;
|
||||
return optionsArray.find((option) => [null, ""].includes(option.value))?.text;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,12 +4,22 @@ import { computePosition, offset, arrow } from "@floating-ui/dom";
|
||||
export default class extends Controller {
|
||||
static targets = ["element", "tooltip", "arrow"];
|
||||
static values = {
|
||||
placement: {
|
||||
type: String,
|
||||
default: "top",
|
||||
},
|
||||
tip: String,
|
||||
placement: { type: String, default: "top" },
|
||||
};
|
||||
|
||||
connect() {
|
||||
if (this.hasTipValue) { this.insertToolTipMarkup() }
|
||||
|
||||
this.elementTarget.addEventListener("mouseenter", this.showTooltip);
|
||||
this.elementTarget.addEventListener("mouseleave", this.hideTooltip);
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this.elementTarget.removeEventListener("mouseenter", this.showTooltip);
|
||||
this.elementTarget.removeEventListener("mouseleave", this.hideTooltip);
|
||||
}
|
||||
|
||||
update() {
|
||||
computePosition(this.elementTarget, this.tooltipTarget, {
|
||||
placement: this.placementValue,
|
||||
@@ -38,12 +48,31 @@ export default class extends Controller {
|
||||
});
|
||||
}
|
||||
|
||||
showTooltip() {
|
||||
showTooltip = () => {
|
||||
this.tooltipTarget.style.display = "block";
|
||||
this.update();
|
||||
}
|
||||
};
|
||||
|
||||
hideTooltip() {
|
||||
hideTooltip = () => {
|
||||
this.tooltipTarget.style.display = "";
|
||||
};
|
||||
|
||||
insertToolTipMarkup() {
|
||||
let container = document.createElement("div");
|
||||
let tooltip = document.createElement("div");
|
||||
let arrow = document.createElement("div");
|
||||
let text = document.createTextNode(this.tipValue);
|
||||
|
||||
container.classList.add("tooltip-container");
|
||||
tooltip.classList.add("tooltip");
|
||||
tooltip.setAttribute("data-tooltip-target", "tooltip");
|
||||
arrow.classList.add("arrow");
|
||||
arrow.setAttribute("data-tooltip-target", "arrow");
|
||||
|
||||
container.appendChild(tooltip);
|
||||
tooltip.appendChild(text);
|
||||
tooltip.appendChild(arrow);
|
||||
|
||||
this.elementTarget.appendChild(container);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fade-out-hide {
|
||||
0% {opacity: 1; visibility: visible;}
|
||||
99% {opacity: 0; visibility: visible;}
|
||||
100% {opacity: 0; visibility: hidden;}
|
||||
}
|
||||
|
||||
.animate-hide-500 {
|
||||
animation: fade-out-hide 0.5s;
|
||||
}
|
||||
|
||||
// @-webkit-keyframes slideOutDown
|
||||
// 0%
|
||||
// -webkit-transform: translateY(0)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
input[type="submit"],
|
||||
input[type="button"],
|
||||
button,
|
||||
button:not(.plain),
|
||||
.button {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
margin-right: 1em;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.ts-control > * {
|
||||
padding-right: 2.75em;
|
||||
}
|
||||
}
|
||||
|
||||
.per-page-feedback {
|
||||
|
||||
@@ -3,14 +3,9 @@
|
||||
}
|
||||
|
||||
.row-loading-icons {
|
||||
margin-left: 3em;
|
||||
margin-left: 2.5em;
|
||||
position: absolute;
|
||||
|
||||
.spinner {
|
||||
border: 0;
|
||||
width: 2.3em;
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 2.3em;
|
||||
opacity: 0.75;
|
||||
|
||||
@@ -1,5 +1,42 @@
|
||||
.ts-wrapper {
|
||||
min-height: initial;
|
||||
}
|
||||
|
||||
.ts-wrapper.multi {
|
||||
.ts-control {
|
||||
box-shadow: none;
|
||||
border-color: $pale-blue;
|
||||
|
||||
&:focus {
|
||||
border-color: $spree-green;
|
||||
}
|
||||
|
||||
[data-value] {
|
||||
text-shadow: none;
|
||||
background-image: none;
|
||||
background-repeat: initial;
|
||||
box-shadow: none;
|
||||
background-color: $spree-blue;
|
||||
}
|
||||
}
|
||||
|
||||
.ts-control > div {
|
||||
border: none;
|
||||
background-color: $spree-blue;
|
||||
}
|
||||
}
|
||||
|
||||
.ts-wrapper.plugin-remove_button .item .remove {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.ts-dropdown {
|
||||
margin-top: 0;
|
||||
|
||||
.option {
|
||||
min-height: 2.25em;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.ts-wrapper.single .ts-control,
|
||||
@@ -45,6 +82,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.ts-wrapper .select-multiple {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ts-wrapper.dropdown-active.primary .ts-control {
|
||||
background-color: $spree-green;
|
||||
border-color: $spree-green;
|
||||
|
||||
@@ -28,5 +28,4 @@
|
||||
|
||||
.tooltip-container {
|
||||
position: relative;
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
@@ -6,130 +6,156 @@
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.ofn-drop-down:hover, .ofn-drop-down.expanded {
|
||||
border: 1px solid #adadad;
|
||||
color: #575757;
|
||||
.ofn-drop-down {
|
||||
.dropdown-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.toggle-off {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:active:not(.disabled),
|
||||
&:focus:not(.disabled) {
|
||||
.dropdown-content {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.toggle-off {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.toggle-on {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ofn-drop-down:hover,
|
||||
.ofn-drop-down.expanded {
|
||||
border: 1px solid #adadad;
|
||||
color: #575757;
|
||||
}
|
||||
|
||||
@mixin ofn-drop-down-style {
|
||||
padding: 7px 15px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #d4d4d4;
|
||||
background-color: #f5f5f5;
|
||||
display: block;
|
||||
color: #828282;
|
||||
cursor: pointer;
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
text-align: center;
|
||||
padding: 7px 15px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #d4d4d4;
|
||||
background-color: #f5f5f5;
|
||||
display: block;
|
||||
color: #828282;
|
||||
cursor: pointer;
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
text-align: center;
|
||||
margin-right: 10px;
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
|
||||
&:hover {
|
||||
cursor: default;
|
||||
border-color: #d4d4d4;
|
||||
color: #828282;
|
||||
}
|
||||
}
|
||||
&.disabled {
|
||||
opacity: 0.5;
|
||||
|
||||
&:hover {
|
||||
cursor: default;
|
||||
border-color: #d4d4d4;
|
||||
color: #828282;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ofn-drop-down-with-prepend {
|
||||
display: flex;
|
||||
display: flex;
|
||||
|
||||
&.right {
|
||||
float: right;
|
||||
&.right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
}
|
||||
.ofn-drop-down {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.ofn-drop-down {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
.ofn-drop-down-prepend {
|
||||
@include ofn-drop-down-style;
|
||||
|
||||
.ofn-drop-down-prepend {
|
||||
@include ofn-drop-down-style;
|
||||
|
||||
border-right: none;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
cursor: default;
|
||||
}
|
||||
border-right: none;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.ofn-drop-down {
|
||||
@include ofn-drop-down-style;
|
||||
@include ofn-drop-down-style;
|
||||
|
||||
position: relative;
|
||||
float: left;
|
||||
position: relative;
|
||||
float: left;
|
||||
|
||||
&.right {
|
||||
float: right;
|
||||
&.right {
|
||||
float: right;
|
||||
margin-right: 0px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover, &.expanded {
|
||||
&:hover,
|
||||
&.expanded {
|
||||
border: 1px solid #adadad;
|
||||
color: #575757;
|
||||
}
|
||||
|
||||
> span {
|
||||
width: auto;
|
||||
text-transform: uppercase;
|
||||
font-size: 85%;
|
||||
font-weight: 600;
|
||||
}
|
||||
> span {
|
||||
width: auto;
|
||||
text-transform: uppercase;
|
||||
font-size: 85%;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.menu {
|
||||
margin-top: 1px;
|
||||
position: absolute;
|
||||
float: none;
|
||||
top:100%;
|
||||
left: 0px;
|
||||
padding: 5px 0px;
|
||||
border: 1px solid #adadad;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 1px 3px 10px #888888;
|
||||
z-index: 100;
|
||||
.menu {
|
||||
margin-top: 1px;
|
||||
position: absolute;
|
||||
float: none;
|
||||
top: 100%;
|
||||
left: 0px;
|
||||
padding: 5px 0px;
|
||||
border: 1px solid #adadad;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 1px 3px 10px #888888;
|
||||
z-index: 100;
|
||||
white-space: nowrap;
|
||||
|
||||
.filter {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
position: relative;
|
||||
|
||||
> input[type="text"] {
|
||||
border: 1px solid rgba(18, 18, 18, 0.1);
|
||||
width: 100%;
|
||||
padding-left: 30px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
font-size: 13px;
|
||||
color:#454545;
|
||||
}
|
||||
.filter {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
position: relative;
|
||||
|
||||
&:after {
|
||||
content: "\f002";
|
||||
font-family: FontAwesome;
|
||||
position: absolute;
|
||||
left: 15px;
|
||||
top: 13px;
|
||||
color:#454545;
|
||||
}
|
||||
}
|
||||
> input[type="text"] {
|
||||
border: 1px solid rgba(18, 18, 18, 0.1);
|
||||
width: 100%;
|
||||
padding-left: 30px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
font-size: 13px;
|
||||
color: #454545;
|
||||
}
|
||||
|
||||
.menu_item {
|
||||
margin: 0px;
|
||||
padding: 2px 10px;
|
||||
color: #454545;
|
||||
text-align: left;
|
||||
&:after {
|
||||
content: "\f002";
|
||||
font-family: FontAwesome;
|
||||
position: absolute;
|
||||
left: 15px;
|
||||
top: 13px;
|
||||
color: #454545;
|
||||
}
|
||||
}
|
||||
|
||||
.menu_item {
|
||||
margin: 0px;
|
||||
padding: 2px 10px;
|
||||
color: #454545;
|
||||
text-align: left;
|
||||
display: block;
|
||||
|
||||
.check {
|
||||
@@ -146,78 +172,79 @@
|
||||
padding: 0px 15px 0px 0px;
|
||||
}
|
||||
|
||||
&.selected{
|
||||
&.selected {
|
||||
.check:before {
|
||||
content: "\2713";
|
||||
}
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.menu_item:hover {
|
||||
background-color: #ededed;
|
||||
}
|
||||
}
|
||||
.menu_item:hover {
|
||||
background-color: #ededed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ofn-drop-down-v2 {
|
||||
border: 1px solid $pale-blue;
|
||||
background-color: white;
|
||||
padding: 0px;
|
||||
|
||||
&:hover {
|
||||
border-color: $spree-blue;
|
||||
}
|
||||
border: 1px solid $pale-blue;
|
||||
background-color: white;
|
||||
padding: 0px;
|
||||
|
||||
.ofn-drop-down-label {
|
||||
color: $color-3;
|
||||
padding: 10px;
|
||||
width: 235px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
&:hover {
|
||||
border-color: $spree-blue;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $color-3;
|
||||
}
|
||||
.ofn-drop-down-label {
|
||||
color: $color-3;
|
||||
padding: 10px;
|
||||
width: 235px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.label {
|
||||
padding-right: 10px;
|
||||
}
|
||||
&:hover {
|
||||
color: $color-3;
|
||||
}
|
||||
|
||||
.icon-caret-down, .icon-caret-up {
|
||||
padding-right: 0px;
|
||||
}
|
||||
}
|
||||
.label {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.menu {
|
||||
width: 100%;
|
||||
}
|
||||
.icon-caret-down,
|
||||
.icon-caret-up {
|
||||
padding-right: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.menu_items {
|
||||
max-height: 200px;
|
||||
overflow-y: scroll;
|
||||
.menu {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.menu_item {
|
||||
margin-bottom: 5px;
|
||||
color: #454545;
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 5px;
|
||||
text-transform: uppercase;
|
||||
font-size: 85%;
|
||||
}
|
||||
}
|
||||
.menu_items {
|
||||
max-height: 200px;
|
||||
overflow-y: scroll;
|
||||
|
||||
.menu_item {
|
||||
margin-bottom: 5px;
|
||||
color: #454545;
|
||||
font-weight: 400;
|
||||
cursor: pointer;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 5px;
|
||||
text-transform: uppercase;
|
||||
font-size: 85%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ofn-drop-down.ofn-drop-down-v2 {
|
||||
// Add very specific styling here for components that are in transition:
|
||||
// ie. the ones using the two classes above
|
||||
.ofn-drop-down-label {
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
}
|
||||
// Add very specific styling here for components that are in transition:
|
||||
// ie. the ones using the two classes above
|
||||
.ofn-drop-down-label {
|
||||
padding-top: 7px;
|
||||
padding-bottom: 7px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
$text-inputs:
|
||||
"input[type=text], input[type=password], input[type=email], input[type=url], input[type=tel]";
|
||||
$text-inputs: "input[type=text], input[type=password], input[type=email], input[type=url], input[type=tel]";
|
||||
|
||||
#{$text-inputs},
|
||||
input[type="date"],
|
||||
input[type="datetime"],
|
||||
input[type="time"],
|
||||
input[type="number"],
|
||||
textarea, fieldset {
|
||||
textarea,
|
||||
fieldset {
|
||||
@include border-radius($border-radius);
|
||||
padding: 7px 10px;
|
||||
border: 1px solid $color-txt-brd;
|
||||
@@ -48,7 +48,9 @@ label {
|
||||
}
|
||||
}
|
||||
|
||||
.label-block label { display: block }
|
||||
.label-block label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
span.info {
|
||||
font-style: italic;
|
||||
@@ -63,7 +65,7 @@ span.info {
|
||||
padding: 10px 0;
|
||||
|
||||
&.checkbox {
|
||||
min-height: 73px;
|
||||
min-height: 70px;
|
||||
|
||||
input[type="checkbox"] {
|
||||
display: inline-block;
|
||||
@@ -85,7 +87,6 @@ span.info {
|
||||
display: inline-block;
|
||||
padding-right: 10px;
|
||||
|
||||
|
||||
label {
|
||||
font-weight: normal;
|
||||
text-transform: none;
|
||||
@@ -171,14 +172,18 @@ fieldset {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
button, .button, input[type="submit"], input[type="button"], span.or {
|
||||
button,
|
||||
.button,
|
||||
input[type="submit"],
|
||||
input[type="button"],
|
||||
span.or {
|
||||
@include border-radius($border-radius);
|
||||
|
||||
-webkit-box-shadow: 0 0 0 15px $color-1;
|
||||
-moz-box-shadow: 0 0 0 15px $color-1;
|
||||
-ms-box-shadow: 0 0 0 15px $color-1;
|
||||
-o-box-shadow: 0 0 0 15px $color-1;
|
||||
box-shadow: 0 0 0 15px $color-1;
|
||||
-moz-box-shadow: 0 0 0 15px $color-1;
|
||||
-ms-box-shadow: 0 0 0 15px $color-1;
|
||||
-o-box-shadow: 0 0 0 15px $color-1;
|
||||
box-shadow: 0 0 0 15px $color-1;
|
||||
|
||||
&:hover {
|
||||
border-color: $color-1;
|
||||
@@ -197,10 +202,10 @@ fieldset {
|
||||
position: relative;
|
||||
|
||||
-webkit-box-shadow: 0 0 0 5px $color-1;
|
||||
-moz-box-shadow: 0 0 0 5px $color-1;
|
||||
-ms-box-shadow: 0 0 0 5px $color-1;
|
||||
-o-box-shadow: 0 0 0 5px $color-1;
|
||||
box-shadow: 0 0 0 5px $color-1;
|
||||
-moz-box-shadow: 0 0 0 5px $color-1;
|
||||
-ms-box-shadow: 0 0 0 5px $color-1;
|
||||
-o-box-shadow: 0 0 0 5px $color-1;
|
||||
box-shadow: 0 0 0 5px $color-1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +215,8 @@ fieldset {
|
||||
display: table;
|
||||
width: 100%;
|
||||
|
||||
label, input {
|
||||
label,
|
||||
input {
|
||||
display: table-cell !important;
|
||||
}
|
||||
input {
|
||||
@@ -219,7 +225,7 @@ fieldset {
|
||||
|
||||
&.checkbox {
|
||||
input {
|
||||
width: auto !important
|
||||
width: auto !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -247,11 +253,12 @@ select {
|
||||
align-items: center;
|
||||
margin-top: 3px;
|
||||
|
||||
input, label {
|
||||
input,
|
||||
label {
|
||||
cursor: pointer;
|
||||
}
|
||||
label {
|
||||
margin: 0;
|
||||
padding-left: .4rem;
|
||||
padding-left: 0.4rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
//---------------------------------------------------
|
||||
* {
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.admin {
|
||||
@@ -68,6 +68,23 @@
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.float-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.float-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.mr-0 {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.ml-0 {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.print-hidden {
|
||||
display: none !important;
|
||||
@@ -81,7 +98,9 @@
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
#logo { height: 40px }
|
||||
#logo {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
i {
|
||||
@@ -108,7 +127,8 @@
|
||||
}
|
||||
|
||||
@media print {
|
||||
header, nav {
|
||||
display:none;
|
||||
header,
|
||||
nav {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
28
app/webpacker/js/mrujs.js
Normal file
28
app/webpacker/js/mrujs.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import CableReady from "cable_ready";
|
||||
import mrujs from "mrujs";
|
||||
import { CableCar } from "mrujs/plugins";
|
||||
|
||||
mrujs.start({
|
||||
plugins: [new CableCar(CableReady, { mimeType: "text/vnd.cable-ready.json" })],
|
||||
});
|
||||
|
||||
// Handle legacy jquery ujs buttons
|
||||
document.addEventListener("ajax:beforeNavigation", (event) => {
|
||||
if (event.detail.element.dataset.ujsNavigate !== "false") return;
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
if (event.detail.fetchResponse.response.redirected) {
|
||||
document.location.href = event.detail.fetchResponse.response.url;
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("ajax:beforeSend", (event) => {
|
||||
window.Turbo.navigator.adapter.progressBar.setValue(0);
|
||||
window.Turbo.navigator.adapter.progressBar.show();
|
||||
});
|
||||
|
||||
document.addEventListener("ajax:complete", (event) => {
|
||||
window.Turbo.navigator.adapter.progressBar.setValue(100);
|
||||
window.Turbo.navigator.adapter.progressBar.hide();
|
||||
});
|
||||
@@ -1,18 +1,7 @@
|
||||
import { Application } from "stimulus";
|
||||
import { definitionsFromContext } from "stimulus/webpack-helpers";
|
||||
|
||||
const application = Application.start();
|
||||
const context = require.context("controllers", true, /.js$/);
|
||||
application.load(definitionsFromContext(context));
|
||||
|
||||
import StimulusReflex from "stimulus_reflex";
|
||||
import consumer from "../channels/consumer";
|
||||
import controller from "../controllers/application_controller";
|
||||
|
||||
application.consumer = consumer;
|
||||
StimulusReflex.initialize(application, { controller, isolate: true });
|
||||
StimulusReflex.debug = process.env.RAILS_ENV === "development";
|
||||
CableReady.initialize({ consumer });
|
||||
import "controllers";
|
||||
import "channels";
|
||||
import "@hotwired/turbo";
|
||||
import "../js/mrujs";
|
||||
|
||||
import debounced from "debounced";
|
||||
debounced.initialize({ input: { wait: 300 } });
|
||||
|
||||
@@ -1,19 +1,7 @@
|
||||
/* eslint no-console:0 */
|
||||
import CableReady from "cable_ready";
|
||||
import mrujs from "mrujs";
|
||||
import { CableCar } from "mrujs/plugins";
|
||||
import * as Turbo from "@hotwired/turbo";
|
||||
|
||||
window.Turbo = Turbo;
|
||||
window.CableReady = CableReady;
|
||||
mrujs.start({
|
||||
plugins: [
|
||||
new CableCar(CableReady, { mimeType: "text/vnd.cable-ready.json" }),
|
||||
],
|
||||
});
|
||||
import "controllers";
|
||||
import "@hotwired/turbo";
|
||||
import "../js/mrujs";
|
||||
|
||||
require.context("../fonts", true);
|
||||
const images = require.context("../images", true);
|
||||
const imagePath = (name) => images(name, true);
|
||||
|
||||
import "controllers";
|
||||
|
||||
@@ -209,7 +209,7 @@ module Openfoodnetwork
|
||||
# Instead, they must be explicitly included below
|
||||
# http://stackoverflow.com/questions/8012434/what-is-the-purpose-of-config-assets-precompile
|
||||
config.assets.initialize_on_precompile = true
|
||||
config.assets.precompile += ['admin/*.js', 'admin/**/*.js']
|
||||
config.assets.precompile += ['admin/*.js', 'admin/**/*.js', 'admin_minimal.js']
|
||||
config.assets.precompile += ['web/all.js']
|
||||
config.assets.precompile += ['darkswarm/all.js']
|
||||
config.assets.precompile += ['shared/*']
|
||||
|
||||
@@ -4289,6 +4289,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using
|
||||
sortable_header:
|
||||
name: "Name"
|
||||
number: "Number"
|
||||
completed_at: "Completed At"
|
||||
state: "State"
|
||||
payment_state: "Payment State"
|
||||
shipment_state: "Shipment State"
|
||||
|
||||
@@ -86,7 +86,7 @@ Spree::Core::Engine.routes.draw do
|
||||
member do
|
||||
put :fire
|
||||
get :fire
|
||||
post :resend
|
||||
get :resend
|
||||
get :invoice
|
||||
get :print
|
||||
get :distribution
|
||||
|
||||
@@ -3,32 +3,32 @@
|
||||
*/
|
||||
|
||||
import { Application } from "stimulus";
|
||||
import select_all_controller from "../../../app/webpacker/controllers/select_all_controller";
|
||||
import checked_controller from "../../../app/webpacker/controllers/checked_controller";
|
||||
|
||||
describe("SelectAllController", () => {
|
||||
describe("CheckedController", () => {
|
||||
beforeAll(() => {
|
||||
const application = Application.start();
|
||||
application.register("select-all", select_all_controller);
|
||||
application.register("checked", checked_controller);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = `
|
||||
<div data-controller="select-all">
|
||||
<div data-controller="checked">
|
||||
<input
|
||||
id="selectAllCheckbox"
|
||||
type="checkbox"
|
||||
data-action="change->select-all#toggleAll"
|
||||
data-select-all-target="all">
|
||||
data-action="change->checked#toggleAll"
|
||||
data-checked-target="all">
|
||||
<input
|
||||
id="checkboxA"
|
||||
type="checkbox"
|
||||
data-action="change->select-all#toggleCheckbox"
|
||||
data-select-all-target="checkbox">
|
||||
data-action="change->checked#toggleCheckbox"
|
||||
data-checked-target="checkbox">
|
||||
<input
|
||||
id="checkboxB"
|
||||
type="checkbox"
|
||||
data-action="change->select-all#toggleCheckbox"
|
||||
data-select-all-target="checkbox">
|
||||
data-action="change->checked#toggleCheckbox"
|
||||
data-checked-target="checkbox">
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
@@ -83,23 +83,23 @@ describe("SelectAllController", () => {
|
||||
describe("#connect", () => {
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = `
|
||||
<div data-controller="select-all">
|
||||
<div data-controller="checked">
|
||||
<input
|
||||
id="selectAllCheckbox"
|
||||
type="checkbox"
|
||||
data-action="change->select-all#toggleAll"
|
||||
data-select-all-target="all">
|
||||
data-action="change->checked#toggleAll"
|
||||
data-checked-target="all">
|
||||
<input
|
||||
id="checkboxA"
|
||||
type="checkbox"
|
||||
data-action="change->select-all#toggleCheckbox"
|
||||
data-select-all-target="checkbox"
|
||||
data-action="change->checked#toggleCheckbox"
|
||||
data-checked-target="checkbox"
|
||||
checked="checked">
|
||||
<input
|
||||
id="checkboxB"
|
||||
type="checkbox"
|
||||
data-action="change->select-all#toggleCheckbox"
|
||||
data-select-all-target="checkbox"
|
||||
data-action="change->checked#toggleCheckbox"
|
||||
data-checked-target="checkbox"
|
||||
checked="checked">
|
||||
</div>
|
||||
`;
|
||||
@@ -1,76 +0,0 @@
|
||||
describe "ordersCtrl", ->
|
||||
ctrl = null
|
||||
Orders = null
|
||||
$scope = null
|
||||
orders = [
|
||||
{ id: 8, order_cycle: { id: 4 }, distributor: { id: 5 }, number: "R123456" }
|
||||
{ id: 9, order_cycle: { id: 5 }, distributor: { id: 7 }, number: "R213776" }
|
||||
{ id: 10, order_cycle: { id: 6 }, distributor: { id: 8 }, number: "R213777" }
|
||||
]
|
||||
form = {
|
||||
q: {
|
||||
created_at_lt: ''
|
||||
created_at_gt: ''
|
||||
completed_at_not_null: true
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach ->
|
||||
module 'admin.orders'
|
||||
inject ($controller, $rootScope, RequestMonitor, SortOptions) ->
|
||||
$scope = $rootScope.$new()
|
||||
Orders =
|
||||
index: jasmine.createSpy('index').and.returnValue(orders)
|
||||
all: orders
|
||||
ctrl = $controller 'ordersCtrl', { $scope: $scope, RequestMonitor: RequestMonitor, SortOptions: SortOptions, Orders: Orders }
|
||||
$scope.q = form.q
|
||||
|
||||
describe "initialising the controller", ->
|
||||
it "fetches orders", ->
|
||||
$scope.initialise()
|
||||
expect(Orders.index).toHaveBeenCalled()
|
||||
expect($scope.orders).toEqual orders
|
||||
|
||||
it "fetches them sorted by completed_at by default", ->
|
||||
$scope.initialise()
|
||||
expect(Orders.index).toHaveBeenCalledWith(jasmine.objectContaining({
|
||||
'q[s]': 'completed_at desc'
|
||||
}))
|
||||
|
||||
describe "using pagination", ->
|
||||
it "changes the page", ->
|
||||
$scope.changePage(2)
|
||||
expect($scope.page).toEqual 2
|
||||
expect(Orders.index).toHaveBeenCalled()
|
||||
|
||||
describe "sorting orders", ->
|
||||
it "sorts orders", ->
|
||||
spyOn $scope, "fetchResults"
|
||||
|
||||
$scope.sortOptions.toggle('number')
|
||||
$scope.$apply()
|
||||
|
||||
expect($scope.sorting).toEqual 'number asc'
|
||||
expect($scope.fetchResults).toHaveBeenCalled()
|
||||
|
||||
describe "filtering orders", ->
|
||||
it "filters orders by all selected order cycles", ->
|
||||
$scope['q']['order_cycle_id_in'] = ['4', '5']
|
||||
|
||||
$scope.fetchResults()
|
||||
|
||||
# Fetched with correct square brackets in field name for array value
|
||||
expect(Orders.index).toHaveBeenCalledWith(jasmine.objectContaining({
|
||||
'q[order_cycle_id_in][]': ['4', '5']
|
||||
}))
|
||||
|
||||
it "filters orders on inclusive dates", ->
|
||||
$scope['q']['completed_at_gteq'] = '2020-06-08'
|
||||
$scope['q']['completed_at_lteq'] = '2020-06-09'
|
||||
|
||||
$scope.fetchResults()
|
||||
|
||||
expect(Orders.index).toHaveBeenCalledWith(jasmine.objectContaining({
|
||||
'q[completed_at_gteq]': '2020-06-08 00:00:00'
|
||||
'q[completed_at_lteq]': '2020-06-09 23:59:59'
|
||||
}))
|
||||
@@ -84,6 +84,16 @@ module WebHelper
|
||||
find(:css, ".select2-result-label", text: options[:select_text] || value).click
|
||||
end
|
||||
|
||||
def tomselect_open(field_name)
|
||||
page.find("##{field_name}-ts-control").click
|
||||
end
|
||||
|
||||
def tomselect_multiselect(value, options)
|
||||
tomselect_wrapper = page.find("[name='#{options[:from]}']").sibling(".ts-wrapper")
|
||||
tomselect_wrapper.find(".ts-control").click
|
||||
tomselect_wrapper.find(:css, '.ts-dropdown.multi .ts-dropdown-content .option', text: value).click
|
||||
end
|
||||
|
||||
def tomselect_search_and_select(value, options)
|
||||
tomselect_wrapper = page.find("[name='#{options[:from]}']").sibling(".ts-wrapper")
|
||||
tomselect_wrapper.find(".ts-control").click
|
||||
|
||||
@@ -29,9 +29,10 @@ describe '
|
||||
# Verify that the orders have a STATE of COMPLETE
|
||||
expect(page).to have_selector('span', text: 'COMPLETE', count: 2)
|
||||
|
||||
page.check('selectAll')
|
||||
page.find('#selectAll').trigger('click')
|
||||
page.find("span.icon-reorder", text: "ACTIONS").click
|
||||
within ".ofn-drop-down-with-prepend .menu" do
|
||||
within ".ofn-drop-down .menu" do
|
||||
expect(page).to have_selector("span", text: "Cancel Orders")
|
||||
page.find("span", text: "Cancel Orders").click
|
||||
end
|
||||
|
||||
|
||||
@@ -611,6 +611,24 @@ describe '
|
||||
href: spree.fire_admin_order_path(order, e: 'cancel')
|
||||
end
|
||||
end
|
||||
|
||||
context "Resending confirmation email" do
|
||||
before do
|
||||
visit spree.edit_admin_order_path(order)
|
||||
find("#links-dropdown .ofn-drop-down").click
|
||||
end
|
||||
|
||||
it "shows the link" do
|
||||
expect(page).to have_link "Resend Confirmation", href: spree.resend_admin_order_path(order)
|
||||
end
|
||||
|
||||
it "resends the confirmation email" do
|
||||
accept_alert "Are you sure you want to resend the order confirmation email?" do
|
||||
click_link "Resend Confirmation"
|
||||
end
|
||||
expect(page).to have_content "Order email has been resent"
|
||||
end
|
||||
end
|
||||
|
||||
context "Check send/print invoice links" do
|
||||
|
||||
|
||||
@@ -99,17 +99,17 @@ distributors: [distributor4, distributor5]) }
|
||||
end
|
||||
|
||||
it "order cycles appear in descending order by close date on orders page" do
|
||||
open_select2('#s2id_q_order_cycle_id_in')
|
||||
tomselect_open('q_order_cycle_id_in').click
|
||||
|
||||
expect(find('#q_order_cycle_id_in',
|
||||
visible: :all)[:innerHTML]).to have_content(/.*Four.*Three.*Two.*Five/m)
|
||||
end
|
||||
|
||||
it "filter by multiple order cycles" do
|
||||
select2_select 'Two', from: 'q_order_cycle_id_in'
|
||||
select2_select 'Three', from: 'q_order_cycle_id_in'
|
||||
tomselect_multiselect 'Two', from: 'q[order_cycle_id_in][]'
|
||||
tomselect_multiselect 'Three', from: 'q[order_cycle_id_in][]'
|
||||
|
||||
page.find('.filter-actions .button.icon-search').click
|
||||
page.find('.filter-actions .button[type=submit]').click
|
||||
|
||||
# Order 2 and 3 should show, but not 4
|
||||
expect(page).to have_content order2.number
|
||||
@@ -118,10 +118,10 @@ distributors: [distributor4, distributor5]) }
|
||||
end
|
||||
|
||||
it "filter by distributors" do
|
||||
select2_select distributor2.name.to_s, from: 'q_distributor_id_in'
|
||||
select2_select distributor4.name.to_s, from: 'q_distributor_id_in'
|
||||
tomselect_multiselect distributor2.name.to_s, from: 'q[distributor_id_in][]'
|
||||
tomselect_multiselect distributor4.name.to_s, from: 'q[distributor_id_in][]'
|
||||
|
||||
page.find('.filter-actions .button.icon-search').click
|
||||
page.find('.filter-actions .button[type=submit]').click
|
||||
|
||||
# Order 2 and 4 should show, but not 3
|
||||
expect(page).to have_content order2.number
|
||||
@@ -134,7 +134,7 @@ distributors: [distributor4, distributor5]) }
|
||||
select_dates_from_daterangepicker(order3.completed_at.yesterday,
|
||||
order4.completed_at.tomorrow)
|
||||
|
||||
page.find('.filter-actions .button.icon-search').click
|
||||
page.find('.filter-actions .button[type=submit]').click
|
||||
|
||||
# Order 3 and 4 should show, but not 2
|
||||
expect(page).to_not have_content order2.number
|
||||
@@ -145,7 +145,7 @@ distributors: [distributor4, distributor5]) }
|
||||
it "filter by email" do
|
||||
fill_in "Email", with: customer3.email
|
||||
|
||||
page.find('.filter-actions .button.icon-search').click
|
||||
page.find('.filter-actions .button[type=submit]').click
|
||||
|
||||
# Order 3 should show, but not 2 and 4
|
||||
expect(page).to_not have_content order2.number
|
||||
@@ -157,28 +157,28 @@ distributors: [distributor4, distributor5]) }
|
||||
# NOTE: this field refers to the name given in billing addresses and not to customer name
|
||||
# filtering by first name
|
||||
fill_in "First name begins with", with: billing_address2.firstname
|
||||
page.find('.filter-actions .button.icon-search').click
|
||||
page.find('.filter-actions .button[type=submit]').click
|
||||
# Order 2 should show, but not 3 and 4
|
||||
expect(page).to have_content order2.number
|
||||
expect(page).to_not have_content order3.number
|
||||
expect(page).to_not have_content order4.number
|
||||
|
||||
find("a#clear_filters_button").click
|
||||
find("#clear_filters_button").click
|
||||
# filtering by last name
|
||||
|
||||
fill_in "Last name begins with", with: billing_address4.lastname
|
||||
page.find('.filter-actions .button.icon-search').click
|
||||
page.find('.filter-actions .button[type=submit]').click
|
||||
# Order 4 should show, but not 2 and 3
|
||||
expect(page).to_not have_content order2.number
|
||||
expect(page).to_not have_content order3.number
|
||||
expect(page).to have_content order4.number
|
||||
|
||||
find("a#clear_filters_button").click
|
||||
find("#clear_filters_button").click
|
||||
# filtering by first and last name together
|
||||
|
||||
fill_in "First name begins with", with: billing_address3.firstname
|
||||
fill_in "Last name begins with", with: billing_address3.lastname
|
||||
page.find('.filter-actions .button.icon-search').click
|
||||
page.find('.filter-actions .button[type=submit]').click
|
||||
# Order 3 should show, but not 2 and 4
|
||||
expect(page).not_to have_content order2.number
|
||||
expect(page).to have_content order3.number
|
||||
@@ -189,17 +189,17 @@ distributors: [distributor4, distributor5]) }
|
||||
order2.select_shipping_method(shipping_method.id)
|
||||
order4.select_shipping_method(shipping_method2.id)
|
||||
|
||||
select2_select "Pick-up at the farm", from: 'q_shipping_method_id'
|
||||
page.find('.filter-actions .button.icon-search').click
|
||||
tomselect_search_and_select "Pick-up at the farm", from: 'shipping_method_id'
|
||||
page.find('.filter-actions .button[type=submit]').click
|
||||
# Order 2 should show, but not 3 and 5
|
||||
expect(page).to have_content order2.number
|
||||
expect(page).to_not have_content order3.number
|
||||
expect(page).to_not have_content order4.number
|
||||
|
||||
find("a#clear_filters_button").click
|
||||
find("#clear_filters_button").click
|
||||
|
||||
select2_select "Signed, sealed, delivered", from: 'q_shipping_method_id'
|
||||
page.find('.filter-actions .button.icon-search').click
|
||||
tomselect_search_and_select "Signed, sealed, delivered", from: 'shipping_method_id'
|
||||
page.find('.filter-actions .button[type=submit]').click
|
||||
# Order 4 should show, but not 2 and 3
|
||||
expect(page).to_not have_content order2.number
|
||||
expect(page).to_not have_content order3.number
|
||||
@@ -209,7 +209,7 @@ distributors: [distributor4, distributor5]) }
|
||||
it "filter by invoice number" do
|
||||
fill_in "Invoice number:", with: order2.number
|
||||
|
||||
page.find('.filter-actions .button.icon-search').click
|
||||
page.find('.filter-actions .button[type=submit]').click
|
||||
|
||||
# Order 2 should show, but not 3 and 4
|
||||
expect(page).to have_content order2.number
|
||||
@@ -221,7 +221,7 @@ distributors: [distributor4, distributor5]) }
|
||||
order.update(state: "payment")
|
||||
|
||||
uncheck 'Only show complete orders'
|
||||
page.find('.filter-actions .button.icon-search').click
|
||||
page.find('.filter-actions .button[type=submit]').click
|
||||
|
||||
expect(page).to have_content order.number
|
||||
expect(page).to have_content order2.number
|
||||
@@ -229,9 +229,9 @@ distributors: [distributor4, distributor5]) }
|
||||
expect(page).to have_content order4.number
|
||||
expect(page).to have_content order5.number
|
||||
|
||||
select2_select "payment", from: 'q_state_eq'
|
||||
tomselect_search_and_select "payment", from: 'q[state_eq]'
|
||||
|
||||
page.find('.filter-actions .button.icon-search').click
|
||||
page.find('.filter-actions .button[type=submit]').click
|
||||
|
||||
# Order 2 should show, but not 3 and 4
|
||||
expect(page).to have_content order.number
|
||||
@@ -295,7 +295,7 @@ distributors: [distributor4, distributor5]) }
|
||||
login_as_admin
|
||||
visit spree.admin_orders_path
|
||||
uncheck 'Only show complete orders'
|
||||
page.find('.filter-actions .button.icon-search').click
|
||||
page.find('.filter-actions .button[type=submit]').click
|
||||
end
|
||||
|
||||
it "orders by order state" do
|
||||
@@ -389,6 +389,21 @@ distributors: [distributor4, distributor5]) }
|
||||
end
|
||||
end
|
||||
|
||||
context "displaying order special instructions" do
|
||||
before do
|
||||
order3.update(special_instructions: "Leave it next to the porch. Thanks!")
|
||||
login_as_admin
|
||||
visit spree.admin_orders_path
|
||||
end
|
||||
|
||||
it "displays a note with order instructions" do
|
||||
within "tr#order_#{order3.id}" do
|
||||
expect(page).to have_content I18n.t('spree.admin.orders.index.note')
|
||||
expect(page).to have_css "[data-tooltip-tip-value='#{order3.special_instructions}']"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "orders with different order totals" do
|
||||
before do
|
||||
Spree::LineItem.where(order_id: order2.id).first.update!(quantity: 5)
|
||||
@@ -432,7 +447,7 @@ distributors: [distributor4, distributor5]) }
|
||||
page.find("span.icon-reorder", text: "ACTIONS").click
|
||||
expect(page).to have_content "Print Invoices"
|
||||
# unselect all orders
|
||||
page.find("#listing_orders thead th:first-child input[type=checkbox]").click
|
||||
page.find("#listing_orders thead th:first-child input[type=checkbox]").trigger("click")
|
||||
expect(page.find(
|
||||
"#listing_orders tbody tr td:first-child input[type=checkbox]")
|
||||
).to_not be_checked
|
||||
@@ -465,14 +480,14 @@ distributors: [distributor4, distributor5]) }
|
||||
|
||||
it "can bulk print invoices but only for the 'complete' or 'resumed' ones" do
|
||||
within "#listing_orders" do
|
||||
page.find("input[name='order_ids[]'][value='#{order2.id}']").click
|
||||
page.find("input[name='order_ids[]'][value='#{order3.id}']").click
|
||||
page.find("input[name='order_ids[]'][value='#{order4.id}']").click
|
||||
page.find("input[name='order_ids[]'][value='#{order5.id}']").click
|
||||
page.find("input[name='bulk_ids[]'][value='#{order2.id}']").click
|
||||
page.find("input[name='bulk_ids[]'][value='#{order3.id}']").click
|
||||
page.find("input[name='bulk_ids[]'][value='#{order4.id}']").click
|
||||
page.find("input[name='bulk_ids[]'][value='#{order5.id}']").click
|
||||
end
|
||||
|
||||
page.find("span.icon-reorder", text: "ACTIONS").click
|
||||
within ".ofn-drop-down-with-prepend .menu" do
|
||||
within ".ofn-drop-down .menu" do
|
||||
page.find("span", text: "Send Invoices").click
|
||||
end
|
||||
|
||||
@@ -492,11 +507,11 @@ distributors: [distributor4, distributor5]) }
|
||||
end
|
||||
|
||||
it "can bulk send email to 2 orders" do
|
||||
page.find("#listing_orders tbody tr:nth-child(1) input[name='order_ids[]']").click
|
||||
page.find("#listing_orders tbody tr:nth-child(2) input[name='order_ids[]']").click
|
||||
page.find("#listing_orders tbody tr:nth-child(1) input[name='bulk_ids[]']").click
|
||||
page.find("#listing_orders tbody tr:nth-child(2) input[name='bulk_ids[]']").click
|
||||
|
||||
page.find("span.icon-reorder", text: "ACTIONS").click
|
||||
within ".ofn-drop-down-with-prepend .menu" do
|
||||
within ".ofn-drop-down .menu" do
|
||||
page.find("span", text: "Resend Confirmation").click
|
||||
end
|
||||
|
||||
@@ -512,11 +527,11 @@ distributors: [distributor4, distributor5]) }
|
||||
end
|
||||
|
||||
it "can bulk print invoices from 2 orders" do
|
||||
page.find("#listing_orders tbody tr:nth-child(1) input[name='order_ids[]']").click
|
||||
page.find("#listing_orders tbody tr:nth-child(2) input[name='order_ids[]']").click
|
||||
page.find("#listing_orders tbody tr:nth-child(1) input[name='bulk_ids[]']").click
|
||||
page.find("#listing_orders tbody tr:nth-child(2) input[name='bulk_ids[]']").click
|
||||
|
||||
page.find("span.icon-reorder", text: "ACTIONS").click
|
||||
within ".ofn-drop-down-with-prepend .menu" do
|
||||
within ".ofn-drop-down .menu" do
|
||||
page.find("span", text: "Print Invoices").click
|
||||
end
|
||||
|
||||
@@ -527,11 +542,11 @@ distributors: [distributor4, distributor5]) }
|
||||
end
|
||||
|
||||
it "can bulk cancel 2 orders" do
|
||||
page.find("#listing_orders tbody tr:nth-child(1) input[name='order_ids[]']").click
|
||||
page.find("#listing_orders tbody tr:nth-child(2) input[name='order_ids[]']").click
|
||||
page.find("#listing_orders tbody tr:nth-child(1) input[name='bulk_ids[]']").click
|
||||
page.find("#listing_orders tbody tr:nth-child(2) input[name='bulk_ids[]']").click
|
||||
|
||||
page.find("span.icon-reorder", text: "ACTIONS").click
|
||||
within ".ofn-drop-down-with-prepend .menu" do
|
||||
within ".ofn-drop-down .menu" do
|
||||
page.find("span", text: "Cancel Orders").click
|
||||
end
|
||||
|
||||
@@ -545,7 +560,7 @@ distributors: [distributor4, distributor5]) }
|
||||
end
|
||||
|
||||
page.find("span.icon-reorder", text: "ACTIONS").click
|
||||
within ".ofn-drop-down-with-prepend .menu" do
|
||||
within ".ofn-drop-down .menu" do
|
||||
page.find("span", text: "Cancel Orders").click
|
||||
end
|
||||
|
||||
@@ -574,17 +589,17 @@ distributors: [distributor4, distributor5]) }
|
||||
end
|
||||
|
||||
it "cannot send emails to orders if permission have been revoked in the meantime" do
|
||||
page.find("#listing_orders tbody tr:nth-child(1) input[name='order_ids[]']").click
|
||||
page.find("#listing_orders tbody tr:nth-child(1) input[name='bulk_ids[]']").click
|
||||
# Find the clicked order
|
||||
order = Spree::Order.find_by(
|
||||
id: page.find("#listing_orders tbody tr:nth-child(1) input[name='order_ids[]']").value
|
||||
id: page.find("#listing_orders tbody tr:nth-child(1) input[name='bulk_ids[]']").value
|
||||
)
|
||||
# Revoke permission for the current user on that specific order by changing its owners
|
||||
order.update_attribute(:distributor, distributor)
|
||||
order.update_attribute(:order_cycle, order_cycle)
|
||||
|
||||
page.find("span.icon-reorder", text: "ACTIONS").click
|
||||
within ".ofn-drop-down-with-prepend .menu" do
|
||||
within ".ofn-drop-down .menu" do
|
||||
page.find("span", text: "Resend Confirmation").click
|
||||
end
|
||||
|
||||
@@ -608,11 +623,11 @@ distributors: [distributor4, distributor5]) }
|
||||
it "displays pagination options" do
|
||||
# displaying 4 orders (one order per table row)
|
||||
within('tbody') do
|
||||
expect(page).to have_css('tr.ng-scope', count: 4)
|
||||
expect(page).to have_css('tr', count: 4)
|
||||
end
|
||||
# pagination options also refer 4 order
|
||||
expect(page).to have_content "4 Results found. Viewing 1 to 4."
|
||||
page.find(".per-page-select").click # toggling the pagination dropdown
|
||||
page.find(".per-page-dropdown .ts-control .item").click # toggling the pagination dropdown
|
||||
expect(page).to have_content "15 per page"
|
||||
expect(page).to have_content "50 per page"
|
||||
expect(page).to have_content "100 per page"
|
||||
@@ -631,7 +646,7 @@ distributors: [distributor4, distributor5]) }
|
||||
expect(page).to have_current_path spree.admin_orders_path
|
||||
|
||||
# click the 'capture' link for the order
|
||||
page.find("[data-powertip=Capture]").click
|
||||
page.find("button.icon-capture").click
|
||||
|
||||
expect(page).to have_css "i.success"
|
||||
expect(page).to have_css "button.icon-road"
|
||||
@@ -648,7 +663,7 @@ distributors: [distributor4, distributor5]) }
|
||||
login_as_admin
|
||||
visit spree.admin_orders_path
|
||||
|
||||
page.find("[data-powertip=Ship]").click
|
||||
page.find("button.icon-road").click
|
||||
|
||||
expect(page).to have_css "i.success"
|
||||
expect(order.reload.shipments.any?(&:shipped?)).to be true
|
||||
@@ -666,7 +681,7 @@ distributors: [distributor4, distributor5]) }
|
||||
login_as_admin
|
||||
visit spree.admin_orders_path
|
||||
uncheck 'Only show complete orders'
|
||||
page.find('a.icon-search').click
|
||||
page.find('button[type=submit]').click
|
||||
|
||||
find(".icon-edit").click
|
||||
|
||||
@@ -711,7 +726,7 @@ distributors: [distributor4, distributor5]) }
|
||||
expect(page).to have_no_content empty_order.number
|
||||
|
||||
uncheck 'Only show complete orders'
|
||||
page.find('a.icon-search').click
|
||||
page.find('button[type=submit]').click
|
||||
|
||||
expect(page).to have_content complete_order.number
|
||||
expect(page).to have_content incomplete_order.number
|
||||
@@ -741,17 +756,17 @@ distributors: [distributor4, distributor5]) }
|
||||
# Specify each filters
|
||||
uncheck 'Only show complete orders'
|
||||
fill_in "Invoice number", with: "R123456"
|
||||
select2_select order_cycle.name, from: 'q_order_cycle_id_in'
|
||||
select2_select distributor.name, from: 'q_distributor_id_in'
|
||||
select2_select shipping_method.name, from: 'q_shipping_method_id'
|
||||
select2_select "complete", from: 'q_state_eq'
|
||||
tomselect_multiselect order_cycle.name, from: 'q[order_cycle_id_in][]'
|
||||
tomselect_multiselect distributor.name, from: 'q[distributor_id_in][]'
|
||||
tomselect_search_and_select shipping_method.name, from: 'shipping_method_id'
|
||||
tomselect_search_and_select "complete", from: 'q[state_eq]'
|
||||
fill_in "Email", with: user.email
|
||||
fill_in "First name begins with", with: "J"
|
||||
fill_in "Last name begins with", with: "D"
|
||||
find("input.datepicker").click
|
||||
select_dates_from_daterangepicker(Time.zone.at(1.week.ago), Time.zone.now.tomorrow)
|
||||
|
||||
page.find('a.icon-search').click
|
||||
page.find('.button[type=submit]').click
|
||||
end
|
||||
|
||||
it "when reloading the page" do
|
||||
@@ -760,10 +775,10 @@ distributors: [distributor4, distributor5]) }
|
||||
# Check every filters to be equal
|
||||
expect(find_field("Only show complete orders")).not_to be_checked
|
||||
expect(find_field("Invoice number").value).to eq "R123456"
|
||||
expect(find("#s2id_q_shipping_method_id").text).to eq shipping_method.name
|
||||
expect(find("#s2id_q_state_eq").text).to eq "complete"
|
||||
expect(find("#s2id_q_distributor_id_in").text).to eq distributor.name
|
||||
expect(find("#s2id_q_order_cycle_id_in").text).to eq order_cycle.name
|
||||
expect(find("#shipping_method_id-ts-control .item").text).to eq shipping_method.name
|
||||
expect(find("#q_state_eq-ts-control .item").text).to eq "complete"
|
||||
expect(find("#q_distributor_id_in").value).to eq [distributor.id.to_s]
|
||||
expect(find("#q_order_cycle_id_in").value).to eq [order_cycle.id.to_s]
|
||||
expect(find_field("Email").value).to eq user.email
|
||||
expect(find_field("First name begins with").value).to eq "J"
|
||||
expect(find_field("Last name begins with").value).to eq "D"
|
||||
@@ -773,13 +788,13 @@ distributors: [distributor4, distributor5]) }
|
||||
end
|
||||
|
||||
it "and clear filters" do
|
||||
find("a#clear_filters_button").click
|
||||
find("#clear_filters_button").click
|
||||
expect(find_field("Only show complete orders")).to be_checked
|
||||
expect(find_field("Invoice number").value).to eq ""
|
||||
expect(find("#s2id_q_shipping_method_id").text).to be_empty
|
||||
expect(find("#s2id_q_state_eq").text).to be_empty
|
||||
expect(find("#s2id_q_distributor_id_in").text).to be_empty
|
||||
expect(find("#s2id_q_order_cycle_id_in").text).to be_empty
|
||||
expect(find("#shipping_method_id").value).to be_empty
|
||||
expect(find("#q_state_eq").value).to be_empty
|
||||
expect(find("#q_distributor_id_in").value).to be_empty
|
||||
expect(find("#q_order_cycle_id_in").value).to be_empty
|
||||
expect(find_field("Email").value).to be_empty
|
||||
expect(find_field("First name begins with").value).to be_empty
|
||||
expect(find_field("Last name begins with").value).to be_empty
|
||||
|
||||
Reference in New Issue
Block a user