spree_admin 5.1.0.beta4 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/spree/admin/components/_badges.scss +4 -0
  3. data/app/assets/stylesheets/spree/admin/components/_buttons.scss +13 -2
  4. data/app/assets/stylesheets/spree/admin/components/_filters.scss +8 -1
  5. data/app/assets/stylesheets/spree/admin/components/_main.scss +13 -1
  6. data/app/assets/stylesheets/spree/admin/components/_media_form.scss +1 -1
  7. data/app/assets/stylesheets/spree/admin/components/_modals.scss +3 -1
  8. data/app/assets/stylesheets/spree/admin/components/_navbar.scss +1 -1
  9. data/app/assets/stylesheets/spree/admin/components/_tables.scss +6 -10
  10. data/app/assets/stylesheets/spree/admin/components/_variants_form.scss +1 -1
  11. data/app/assets/stylesheets/spree/admin/global/_variables.scss +12 -7
  12. data/app/assets/stylesheets/spree/admin/shared/_base.scss +7 -6
  13. data/app/controllers/spree/admin/gift_card_batches_controller.rb +23 -0
  14. data/app/controllers/spree/admin/gift_cards_controller.rb +94 -0
  15. data/app/controllers/spree/admin/orders_controller.rb +25 -3
  16. data/app/controllers/spree/admin/payments_controller.rb +3 -0
  17. data/app/controllers/spree/admin/products_controller.rb +3 -3
  18. data/app/controllers/spree/admin/reimbursements_controller.rb +5 -1
  19. data/app/controllers/spree/admin/resource_controller.rb +12 -1
  20. data/app/controllers/spree/admin/search_controller.rb +22 -0
  21. data/app/controllers/spree/admin/store_credits_controller.rb +8 -5
  22. data/app/controllers/spree/admin/translations_controller.rb +31 -25
  23. data/app/helpers/spree/admin/base_helper.rb +55 -0
  24. data/app/helpers/spree/admin/navigation_helper.rb +2 -2
  25. data/app/helpers/spree/admin/payments_helper.rb +0 -5
  26. data/app/helpers/spree/admin/products_helper.rb +1 -1
  27. data/app/helpers/spree/admin/promotion_rules_helper.rb +13 -1
  28. data/app/helpers/spree/admin/shipment_helper.rb +2 -0
  29. data/app/javascript/spree/admin/application.js +2 -0
  30. data/app/javascript/spree/admin/controllers/active_storage_upload_controller.js +2 -11
  31. data/app/javascript/spree/admin/controllers/return_items_controller.js +6 -1
  32. data/app/javascript/spree/admin/controllers/sticky_controller.js +24 -0
  33. data/app/javascript/spree/admin/controllers/variants_form_controller.js +1 -1
  34. data/app/javascript/spree/admin/helpers/uppy_active_storage.js +15 -3
  35. data/app/views/active_storage/_upload_form.html.erb +3 -3
  36. data/app/views/spree/admin/coupon_codes/_coupon_code.html.erb +2 -6
  37. data/app/views/spree/admin/coupon_codes/index.csv.erb +1 -1
  38. data/app/views/spree/admin/custom_domains/edit.html.erb +2 -3
  39. data/app/views/spree/admin/custom_domains/new.html.erb +2 -3
  40. data/app/views/spree/admin/dashboard/_setup_progress.html.erb +2 -3
  41. data/app/views/spree/admin/dashboard/_store_preview.html.erb +3 -9
  42. data/app/views/spree/admin/gift_card_batches/_form.html.erb +30 -0
  43. data/app/views/spree/admin/gift_card_batches/new.html.erb +15 -0
  44. data/app/views/spree/admin/gift_cards/_filters.html.erb +43 -0
  45. data/app/views/spree/admin/gift_cards/_form.html.erb +30 -0
  46. data/app/views/spree/admin/gift_cards/_gift_card.html.erb +28 -0
  47. data/app/views/spree/admin/gift_cards/_list.html.erb +24 -0
  48. data/app/views/spree/admin/gift_cards/_table_filter_dropdown.html.erb +25 -0
  49. data/app/views/spree/admin/gift_cards/edit.html.erb +20 -0
  50. data/app/views/spree/admin/gift_cards/index.csv.erb +22 -0
  51. data/app/views/spree/admin/gift_cards/index.html.erb +41 -0
  52. data/app/views/spree/admin/gift_cards/new.html.erb +30 -0
  53. data/app/views/spree/admin/gift_cards/show.html.erb +199 -0
  54. data/app/views/spree/admin/integrations/new.html.erb +7 -4
  55. data/app/views/spree/admin/oauth_applications/edit.html.erb +2 -3
  56. data/app/views/spree/admin/oauth_applications/new.html.erb +2 -3
  57. data/app/views/spree/admin/option_types/edit.html.erb +4 -0
  58. data/app/views/spree/admin/orders/_customer.html.erb +5 -2
  59. data/app/views/spree/admin/orders/_payments.html.erb +1 -1
  60. data/app/views/spree/admin/orders/_return_authorizations.html.erb +3 -1
  61. data/app/views/spree/admin/orders/_shipment.html.erb +1 -1
  62. data/app/views/spree/admin/orders/_shipments.html.erb +1 -1
  63. data/app/views/spree/admin/orders/return_authorizations/_form.html.erb +13 -11
  64. data/app/views/spree/admin/page_sections/forms/_featured_posts.html.erb +0 -5
  65. data/app/views/spree/admin/pages/_form.html.erb +20 -26
  66. data/app/views/spree/admin/pages/new.html.erb +10 -4
  67. data/app/views/spree/admin/payments/_payment.html.erb +14 -9
  68. data/app/views/spree/admin/payments/new.html.erb +1 -1
  69. data/app/views/spree/admin/payments/source_forms/_gateway.html.erb +1 -1
  70. data/app/views/spree/admin/post_categories/edit.html.erb +2 -3
  71. data/app/views/spree/admin/post_categories/new.html.erb +2 -3
  72. data/app/views/spree/admin/products/edit.html.erb +2 -2
  73. data/app/views/spree/admin/promotion_actions/_promotion_action.html.erb +1 -1
  74. data/app/views/spree/admin/promotion_actions/edit.html.erb +2 -2
  75. data/app/views/spree/admin/promotion_rules/_promotion_rule.html.erb +26 -9
  76. data/app/views/spree/admin/promotion_rules/edit.html.erb +2 -2
  77. data/app/views/spree/admin/promotion_rules/forms/_option_value.html.erb +1 -1
  78. data/app/views/spree/admin/promotions/_actions.html.erb +2 -2
  79. data/app/views/spree/admin/promotions/_rules.html.erb +2 -2
  80. data/app/views/spree/admin/promotions/_sidebar.html.erb +5 -13
  81. data/app/views/spree/admin/properties/_property.html.erb +2 -2
  82. data/app/views/spree/admin/properties/edit.html.erb +12 -5
  83. data/app/views/spree/admin/properties/new.html.erb +9 -5
  84. data/app/views/spree/admin/refunds/new.html.erb +6 -1
  85. data/app/views/spree/admin/reimbursement_types/edit.html.erb +2 -3
  86. data/app/views/spree/admin/reimbursement_types/new.html.erb +2 -4
  87. data/app/views/spree/admin/shared/_content_header.html.erb +9 -4
  88. data/app/views/spree/admin/shared/_edit_resource_links.html.erb +3 -1
  89. data/app/views/spree/admin/shared/_header.html.erb +2 -2
  90. data/app/views/spree/admin/shared/_index_table_options.html.erb +1 -1
  91. data/app/views/spree/admin/shared/_preferences.html.erb +1 -0
  92. data/app/views/spree/admin/shared/_sidebar.html.erb +1 -1
  93. data/app/views/spree/admin/shared/_user.html.erb +2 -7
  94. data/app/views/spree/admin/shared/named_types/_edit.html.erb +2 -3
  95. data/app/views/spree/admin/shared/named_types/_new.html.erb +2 -3
  96. data/app/views/spree/admin/shared/sortable_tree/_taxonomy.html.erb +2 -2
  97. data/app/views/spree/admin/shipping_categories/edit.html.erb +7 -4
  98. data/app/views/spree/admin/shipping_categories/new.html.erb +7 -6
  99. data/app/views/spree/admin/shipping_methods/form/_display.html.erb +23 -1
  100. data/app/views/spree/admin/stock_locations/_form.html.erb +35 -51
  101. data/app/views/spree/admin/stock_locations/edit.html.erb +8 -3
  102. data/app/views/spree/admin/stock_locations/new.html.erb +8 -5
  103. data/app/views/spree/admin/store_credit_events/_store_credit_event.html.erb +29 -0
  104. data/app/views/spree/admin/store_credits/_form.html.erb +13 -21
  105. data/app/views/spree/admin/store_credits/_list.html.erb +4 -24
  106. data/app/views/spree/admin/store_credits/_store_credit.html.erb +11 -0
  107. data/app/views/spree/admin/store_credits/edit.html.erb +16 -31
  108. data/app/views/spree/admin/store_credits/new.html.erb +14 -12
  109. data/app/views/spree/admin/store_credits/show.html.erb +145 -0
  110. data/app/views/spree/admin/storefront/edit.html.erb +1 -1
  111. data/app/views/spree/admin/stores/form/_basic.html.erb +28 -12
  112. data/app/views/spree/admin/tax_categories/_form.html.erb +1 -1
  113. data/app/views/spree/admin/tax_categories/edit.html.erb +7 -4
  114. data/app/views/spree/admin/tax_categories/new.html.erb +7 -4
  115. data/app/views/spree/admin/tax_rates/_form.html.erb +34 -32
  116. data/app/views/spree/admin/tax_rates/edit.html.erb +7 -4
  117. data/app/views/spree/admin/tax_rates/new.html.erb +7 -7
  118. data/app/views/spree/admin/taxonomies/edit.html.erb +2 -4
  119. data/app/views/spree/admin/taxonomies/new.html.erb +2 -2
  120. data/app/views/spree/admin/translations/edit.html.erb +37 -29
  121. data/app/views/spree/admin/translations/option_types/_form.html.erb +17 -0
  122. data/app/views/spree/admin/translations/products/_form.html.erb +11 -20
  123. data/app/views/spree/admin/translations/properties/_form.html.erb +5 -0
  124. data/app/views/spree/admin/translations/stores/_form.html.erb +11 -20
  125. data/app/views/spree/admin/translations/taxonomies/_form.html.erb +3 -18
  126. data/app/views/spree/admin/translations/taxons/_form.html.erb +11 -34
  127. data/app/views/spree/admin/translations/translation_rows/_permalink_field_row.html.erb +41 -0
  128. data/app/views/spree/admin/translations/translation_rows/_rich_textarea_row.html.erb +13 -0
  129. data/app/views/spree/admin/translations/translation_rows/_text_field_row.html.erb +7 -0
  130. data/app/views/spree/admin/translations/translation_rows/_textarea_row.html.erb +11 -0
  131. data/app/views/spree/admin/users/_details.html.erb +1 -0
  132. data/app/views/spree/admin/users/index.html.erb +1 -1
  133. data/app/views/spree/admin/users/new.html.erb +9 -5
  134. data/app/views/spree/admin/users/show.html.erb +3 -3
  135. data/app/views/spree/admin/variants/form/_inventory.html.erb +1 -1
  136. data/app/views/spree/admin/zones/_form.html.erb +36 -40
  137. data/app/views/spree/admin/zones/edit.html.erb +7 -5
  138. data/app/views/spree/admin/zones/new.html.erb +7 -4
  139. data/config/locales/en.yml +12 -1
  140. data/config/routes.rb +8 -2
  141. metadata +33 -8
@@ -8,13 +8,7 @@ module Spree
8
8
  def edit; end
9
9
 
10
10
  def update
11
- locale_translation_params = permitted_translation_params.to_h.transform_values do |translations|
12
- translations[@selected_translation_locale]
13
- end
14
-
15
- Mobility.with_locale(@selected_translation_locale) do
16
- @resource.update!(locale_translation_params)
17
- end
11
+ @resource.update!(permitted_translation_params)
18
12
 
19
13
  flash[:success] = Spree.t('notice_messages.translations_saved')
20
14
 
@@ -28,11 +22,20 @@ module Spree
28
22
  private
29
23
 
30
24
  def permitted_translation_params
31
- params.require(:translation).permit(
32
- resource_class.translatable_fields.each_with_object({}) do |field, acc|
33
- acc[field] = current_store.supported_locales_list.map(&:to_sym)
34
- end
35
- )
25
+ params.require(@resource.model_name.param_key).permit(translation_fields(resource_class), **nested_params)
26
+ end
27
+
28
+ def nested_params
29
+ case resource_class.to_s
30
+ when 'Spree::OptionType'
31
+ { option_values_attributes: [ :id, *translation_fields(Spree::OptionValue)] }
32
+ else
33
+ {}
34
+ end
35
+ end
36
+
37
+ def translation_fields(klass)
38
+ klass.translatable_fields.map { |field| "#{field}_#{@selected_translation_locale}" }
36
39
  end
37
40
 
38
41
  def resource_class
@@ -61,20 +64,23 @@ module Spree
61
64
 
62
65
  def load_data
63
66
  @locales = (current_store.supported_locales_list - [@default_locale]).sort
67
+ @resource_name = @resource.try(:name)
64
68
 
65
- case @resource
66
- when Spree::Product
67
- @resource_name = @resource.name
68
- @back_path = spree.edit_admin_product_path(@resource)
69
- when Spree::Taxon
70
- @resource_name = @resource.name
71
- @back_path = spree.edit_admin_taxonomy_taxon_path(@resource.taxonomy, @resource.id)
72
- when Spree::Taxonomy
73
- @resource_name = @resource.name
74
- @back_path = spree.edit_admin_taxonomy_path(@resource)
75
- when Spree::Store
76
- @resource_name = @resource.name
77
- @back_path = spree.edit_admin_store_path(section: "general-settings")
69
+ @back_path = case @resource.class.name
70
+ when 'Spree::OptionType'
71
+ spree.edit_admin_option_type_path(@resource)
72
+ when 'Spree::Product'
73
+ spree.edit_admin_product_path(@resource)
74
+ when 'Spree::Property'
75
+ spree.edit_admin_property_path(@resource)
76
+ when 'Spree::Store'
77
+ spree.edit_admin_store_path(section: "general-settings")
78
+ when 'Spree::Taxon'
79
+ spree.edit_admin_taxonomy_taxon_path(@resource.taxonomy, @resource.id)
80
+ when 'Spree::Taxonomy'
81
+ spree.admin_taxonomy_path(@resource)
82
+ else
83
+ [:edit, :admin, @resource]
78
84
  end
79
85
  end
80
86
  end
@@ -243,6 +243,61 @@ module Spree
243
243
  content_tag(:span, ' *', class: 'required font-weight-bold text-danger')
244
244
  end
245
245
 
246
+ # renders a clipboard button
247
+ # @param options [Hash] the options for the button
248
+ # @option options [String] :class the CSS class(es) of the button
249
+ # @option options [Hash] :data the data attributes for the button
250
+ # @option options [String] :title the title of the button
251
+ # @return [String] the button
252
+ def clipboard_button(options = {})
253
+ options[:class] ||= 'btn btn-clipboard with-tip'
254
+ options[:type] ||= 'button'
255
+ options[:data] ||= {}
256
+ options[:data][:action] = 'clipboard#copy'
257
+ options[:data][:clipboard_target] = 'button'
258
+ options[:data][:title] = Spree.t('admin.copy_to_clipboard')
259
+ options[:aria_label] ||= Spree.t('admin.copy_to_clipboard') # screen-reader label
260
+
261
+ content_tag(:button, options) do
262
+ icon('copy', class: 'mr-0 font-size-sm')
263
+ end
264
+ end
265
+
266
+ # renders a clipboard component
267
+ # @param text [String] the text to copy
268
+ # @param options [Hash] the options for the component
269
+ # @option options [String] :class the CSS class(es) of the component
270
+ # @option options [Hash] :data the data attributes for the component
271
+ # @option options [String] :title the title of the component
272
+ # @return [String] the component
273
+ def clipboard_component(text, options = {})
274
+ options[:data] ||= {}
275
+ options[:data][:controller] = 'clipboard'
276
+ options[:data][:clipboard_success_content_value] ||= raw(icon('check', class: 'mr-0 font-size-sm'))
277
+
278
+ content_tag(:span, data: options[:data]) do
279
+ hidden_field_tag(:clipboard_source, text, data: { clipboard_target: 'source' }) +
280
+ clipboard_button
281
+ end
282
+ end
283
+
284
+ # renders a progress bar component
285
+ # @param options [Hash] the options for the component
286
+ # @param value [Integer] the value of the progress bar
287
+ # @option options [Integer] :min the minimum value of the progress bar
288
+ # @option options [Integer] :max the maximum value of the progress bar
289
+ # @return [String] the component
290
+ def progress_bar_component(value, options = {})
291
+ min = options[:min] || 0
292
+ max = options[:max] || 100
293
+ percentage = (value.to_f / max * 100).round
294
+
295
+ content_tag(:div, class: 'progress') do
296
+ content_tag(:div, { class: 'progress-bar', role: 'progressbar', style: "width: #{percentage}%", aria: { valuenow: value, valuemin: min, valuemax: max } }) do
297
+ end
298
+ end
299
+ end
300
+
246
301
  # returns the allowed file types for upload, according to the active storage configuration
247
302
  # @return [Array<String>] the allowed file types for upload, eg. ['image/png', 'image/jpeg', 'image/gif', 'image/webp']
248
303
  def allowed_file_types_for_upload
@@ -231,7 +231,7 @@ module Spree
231
231
  link_to url, opts, &block
232
232
  else
233
233
  link_to url, opts do
234
- (label + icon('external-link', class: 'ml-1 mr-0 small')).html_safe
234
+ (label + icon('external-link', class: 'ml-1 mr-0 small opacity-50')).html_safe
235
235
  end
236
236
  end
237
237
  end
@@ -263,7 +263,7 @@ module Spree
263
263
  # @param css [String] the css class of the help bubble
264
264
  # @return [String] the help bubble with the icon
265
265
  def help_bubble(text = '', placement = 'bottom', css: nil)
266
- css ||= 'text-muted opacity-75 cursor-help'
266
+ css ||= 'text-muted opacity-75 cursor-default'
267
267
  content_tag :small, icon('info-square-rounded', class: css), data: { placement: placement }, class: "with-tip #{css}", title: text
268
268
  end
269
269
 
@@ -11,11 +11,6 @@ module Spree
11
11
  end
12
12
  end
13
13
 
14
- def payment_method_icon_tag(payment_method, opts = {})
15
- image_tag "payment_icons/#{payment_method}.svg", opts
16
- rescue Sprockets::Rails::Helper::AssetNotFound
17
- end
18
-
19
14
  def available_payment_methods
20
15
  @available_payment_methods ||= Spree::PaymentMethod.providers.map { |provider| provider.name.constantize.new }.delete_if { |payment_method| !payment_method.show_in_admin? || current_store.payment_methods.pluck(:type).include?(payment_method.type) }.sort_by(&:name)
21
16
  end
@@ -124,7 +124,7 @@ module Spree
124
124
  end
125
125
 
126
126
  def sorted_product_properties(product)
127
- product.product_properties.sort_by { |product_property| product_property.property.position }
127
+ product.product_properties.includes(:property).sort_by { |product_property| product_property.property.position }
128
128
  end
129
129
  end
130
130
  end
@@ -10,7 +10,19 @@ module Spree
10
10
  eligible_values = promotion_rule.preferred_eligible_values || []
11
11
  return [] if eligible_values.empty?
12
12
 
13
- Spree::OptionValueVariant.where(id: eligible_values).to_tom_select_json
13
+ Spree::OptionValue.includes(:option_type).where(id: eligible_values).map do |ov|
14
+ {
15
+ id: ov.id,
16
+ name: ov.display_presentation
17
+ }
18
+ end
19
+ end
20
+
21
+ # Returns the promotion rule option values
22
+ # @param value_ids [Array<Integer>]
23
+ # @return [Array<String>]
24
+ def promotion_rule_option_values(value_ids)
25
+ Spree::OptionValue.includes(:option_type).where(id: value_ids).map(&:display_presentation)
14
26
  end
15
27
  end
16
28
  end
@@ -1,5 +1,7 @@
1
1
  module Spree::Admin
2
2
  module ShipmentHelper
3
+ include Spree::ShipmentHelper
4
+
3
5
  def can_ship?(shipment)
4
6
  can?(:update, shipment) && shipment.shippable?
5
7
  end
@@ -73,6 +73,7 @@ import SectionFormController from 'spree/admin/controllers/section_form_controll
73
73
  import SelectController from 'spree/admin/controllers/select_controller'
74
74
  import SeoFormController from 'spree/admin/controllers/seo_form_controller'
75
75
  import SlugFormController from 'spree/admin/controllers/slug_form_controller'
76
+ import StickyController from 'spree/admin/controllers/sticky_controller'
76
77
  import SortableTree from 'spree/admin/controllers/sortable_tree_controller'
77
78
  import StockTransferController from 'spree/admin/controllers/stock_transfer_controller'
78
79
  import StoreFormController from 'spree/admin/controllers/store_form_controller'
@@ -124,6 +125,7 @@ application.register('section-form', SectionFormController)
124
125
  application.register('select', SelectController)
125
126
  application.register('seo-form', SeoFormController)
126
127
  application.register('slug-form', SlugFormController)
128
+ application.register('sticky', StickyController)
127
129
  application.register('sortable', Sortable)
128
130
  application.register('sortable-tree', SortableTree)
129
131
  application.register('stock-transfer', StockTransferController)
@@ -29,7 +29,8 @@ export default class extends Controller {
29
29
  })
30
30
 
31
31
  this.uppy.use(ActiveStorageUpload, {
32
- directUploadUrl: document.querySelector("meta[name='direct-upload-url']").getAttribute('content')
32
+ directUploadUrl: document.querySelector("meta[name='direct-upload-url']").getAttribute('content'),
33
+ crop: this.cropValue
33
34
  })
34
35
 
35
36
  let dashboardOptions = {}
@@ -55,16 +56,6 @@ export default class extends Controller {
55
56
 
56
57
  this.uppy.on('file-editor:complete', (updatedFile) => {
57
58
  console.log('File editing complete:', updatedFile)
58
- // Remove the old file from Uppy's file list
59
- this.uppy.removeFile(updatedFile.id)
60
- // Add the updated file to Uppy
61
- this.uppy.addFile({
62
- name: updatedFile.name,
63
- type: updatedFile.type,
64
- data: updatedFile.data,
65
- source: 'local',
66
- isRemote: false
67
- })
68
59
 
69
60
  this.handleUI(updatedFile)
70
61
 
@@ -21,7 +21,12 @@ export default class extends CheckboxSelectAll {
21
21
  var totalPretaxRefund = 0
22
22
  this.checked.forEach((checkbox) => {
23
23
  const returnItemRow = checkbox.closest('tr')
24
- const amount = parseFloat(returnItemRow.querySelector('.refund-amount-input').value)
24
+ const refundAmountInput = returnItemRow.querySelector('.refund-amount-input')
25
+ let amount = parseFloat(refundAmountInput.value)
26
+ if (!Number.isFinite(amount)) {
27
+ amount = 0
28
+ refundAmountInput.value = 0
29
+ }
25
30
  totalPretaxRefund += amount
26
31
  })
27
32
 
@@ -0,0 +1,24 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ static values = {
5
+ threshold: { type: Number, default: 60 }
6
+ }
7
+
8
+ connect() {
9
+ this.handleScroll = this.handleScroll.bind(this)
10
+ window.addEventListener('scroll', this.handleScroll)
11
+ }
12
+
13
+ disconnect() {
14
+ window.removeEventListener('scroll', this.handleScroll)
15
+ }
16
+
17
+ handleScroll() {
18
+ if (window.scrollY > this.thresholdValue) {
19
+ this.element.classList.add('sticky-top')
20
+ } else {
21
+ this.element.classList.remove('sticky-top')
22
+ }
23
+ }
24
+ }
@@ -495,7 +495,7 @@ export default class extends CheckboxSelectAll {
495
495
  const variantEditButton = variantTarget.querySelector('[data-slot="variantEditButton"]')
496
496
 
497
497
  if (variantEditButton) {
498
- variantEditButton.href = `/admin/products/${this.productIdValue}/variants/${variantId}/edit`
498
+ variantEditButton.href = `${Spree.adminPath}/products/${this.productIdValue}/variants/${variantId}/edit`
499
499
  variantEditButton.classList.remove('invisible')
500
500
  }
501
501
  }
@@ -20,7 +20,8 @@ export default class ActiveStorageUpload extends BasePlugin {
20
20
  limit: 0,
21
21
  timeout: 30 * 1000,
22
22
  directUploadUrl: null,
23
- headers: {}
23
+ headers: {},
24
+ crop: false
24
25
  }
25
26
 
26
27
  this.opts = Object.assign({}, defaultOptions, opts)
@@ -37,10 +38,19 @@ export default class ActiveStorageUpload extends BasePlugin {
37
38
 
38
39
  install() {
39
40
  this.uppy.addUploader(this.handleUpload)
41
+ this.uppy.on('file-editor:complete', this.onEditorComplete)
40
42
  }
41
43
 
42
44
  uninstall() {
43
45
  this.uppy.removeUploader(this.handleUpload)
46
+ this.uppy.off('file-editor:complete', this.onEditorComplete)
47
+ }
48
+
49
+ onEditorComplete = (updatedFile) => {
50
+ this.handleUpload([updatedFile.id])
51
+
52
+ // call directly upload method after editing image
53
+ return this.uploadFiles([updatedFile])
44
54
  }
45
55
 
46
56
  handleUpload(fileIDs) {
@@ -48,6 +58,10 @@ export default class ActiveStorageUpload extends BasePlugin {
48
58
  this.uppy.log("[ActiveStorage] No files to upload!")
49
59
  return Promise.resolve()
50
60
  }
61
+ // do not upload before editing is done
62
+ if (this.opts.crop) {
63
+ return Promise.resolve()
64
+ }
51
65
 
52
66
  this.uppy.log("[ActiveStorage] Uploading...")
53
67
  const files = fileIDs.map(fileID => this.uppy.getFile(fileID))
@@ -115,8 +129,6 @@ export default class ActiveStorageUpload extends BasePlugin {
115
129
  directUploadSignedId: blob.signed_id,
116
130
  }
117
131
 
118
- this.uppy.setFileState(file.id, { response })
119
-
120
132
  this.uppy.emit("upload-success", file, blob)
121
133
 
122
134
  return resolve(file)
@@ -2,7 +2,7 @@
2
2
  <% height ||= 300 %>
3
3
  <% crop ||= false unless defined?(crop) %>
4
4
  <% auto_submit ||= false %>
5
- <% can_delete ||= true unless defined?(can_delete) %>
5
+ <% show_delete_button = defined?(can_delete) ? can_delete : true %>
6
6
  <% css ||= '' %>
7
7
 
8
8
  <% if field_name && form %>
@@ -36,7 +36,7 @@
36
36
 
37
37
  <% if crop %>
38
38
  <span class="text-muted ml-2 text-center small mt-2">
39
- Recommended size: <%= width %>x<%= height %>px
39
+ <%= Spree.t('admin.recommended_size') %>: <%= width %>x<%= height %>px
40
40
  </span>
41
41
  <% end %>
42
42
  </div>
@@ -48,7 +48,7 @@
48
48
  <%= Spree.t('actions.select_file') %>
49
49
  </button>
50
50
 
51
- <% if can_delete %>
51
+ <% if show_delete_button %>
52
52
  <button type="button" class="btn btn-danger ml-auto" data-action="active-storage-upload#remove" data-turbo-confirm="<%= Spree.t(:are_you_sure) %>">
53
53
  <%= icon('trash') %>
54
54
 
@@ -1,11 +1,7 @@
1
1
  <tr id="spree_coupon_code_<%= coupon_code[0] %>">
2
2
  <td class="w-50">
3
- <div data-controller="clipboard">
4
- <code data-clipboard-target="source"><%= coupon_code[1].upcase %></code>
5
- <button type="button" class="btn btn-light p-1 ml-2 with-tip" data-action="clipboard#copy" title="<%= Spree.t('admin.copy_to_clipboard') %>">
6
- <%= icon('copy-check', class: 'mr-0 font-size-sm') %>
7
- </button>
8
- </div>
3
+ <code><%= coupon_code[1].upcase %></code>
4
+ <%= clipboard_component(coupon_code[1].upcase) %>
9
5
  </td>
10
6
  <td class="w-25 font-size-sm"><%= active_badge(coupon_code[2] == 'used') %></td>
11
7
  <td class="w-25 font-size-sm"><%= local_time(coupon_code[3]) %></td>
@@ -10,5 +10,5 @@
10
10
  coupon_code[0].upcase,
11
11
  coupon_code[1],
12
12
  coupon_code[2]
13
- ]).strip.html_safe %>
13
+ ]).strip %>
14
14
  <%- end -%>
@@ -3,11 +3,10 @@
3
3
  <%= @custom_domain.url %>
4
4
  <% end %>
5
5
 
6
- <%= render partial: 'spree/admin/shared/error_messages', locals: { target: @object } %>
7
-
8
6
  <%= form_for @object, url: spree.admin_custom_domain_path(@object) do |f| %>
9
7
  <div class="row">
10
- <div class="col-lg-6">
8
+ <div class="col-lg-6 offset-lg-3">
9
+ <%= render partial: 'spree/admin/shared/error_messages', locals: { target: @object } %>
11
10
  <%= render partial: 'form', locals: { f: f } %>
12
11
  <%= render partial: 'spree/admin/shared/edit_resource_links', locals: { f: f } %>
13
12
  </div>
@@ -3,11 +3,10 @@
3
3
  <%= Spree.t(:new_custom_domain) %>
4
4
  <% end %>
5
5
 
6
- <%= render partial: 'spree/admin/shared/error_messages', locals: { target: @object } %>
7
-
8
6
  <%= form_for @object, url: spree.admin_custom_domains_path do |f| %>
9
7
  <div class="row">
10
- <div class="col-lg-6">
8
+ <div class="col-lg-6 offset-lg-3">
9
+ <%= render partial: 'spree/admin/shared/error_messages', locals: { target: @object } %>
11
10
  <%= render partial: 'form', locals: { f: f } %>
12
11
  <%= render partial: 'spree/admin/shared/new_resource_links' %>
13
12
  </div>
@@ -12,8 +12,7 @@
12
12
  </span>
13
13
  </span>
14
14
  </div>
15
- <div class="progress">
16
- <div class="progress-bar bg-success" style="width: <%= current_store.setup_percentage %>%" role="progressbar" aria-valuenow="<%= current_store.setup_percentage %>" aria-valuemin="0" aria-valuemax="100"></div>
17
- </div>
15
+
16
+ <%= progress_bar_component(current_store.setup_percentage) %>
18
17
  </div>
19
18
  </div>
@@ -12,15 +12,9 @@
12
12
  <% end %>
13
13
 
14
14
  <div class="card-body px-2 pt-0 pb-2">
15
- <div class="flex my-2 p-1 w-100 rounded border" data-controller="clipboard" data-clipboard-success-content-value="Copied!">
16
- <% if current_store.default_custom_domain&.active? %>
17
- <%= text_field_tag :custom_domain, current_store.default_custom_domain.url, readonly: true, class: 'form-control-plaintext py-0', data: { clipboard_target: 'source' } %>
18
- <% else %>
19
- <%= text_field_tag :domain, current_store.url, readonly: true, class: 'form-control-plaintext py-0', data: { clipboard_target: 'source' } %>
20
- <% end %>
21
- <button data-action="clipboard#copy" data-clipboard-target="button" class="btn btn-sm py-1 hover-gray">
22
- Copy
23
- </button>
15
+ <div class="flex my-2 p-1 w-100 rounded border">
16
+ <%= text_field_tag :domain, current_store.url_or_custom_domain, readonly: true, class: 'form-control-plaintext py-0' %>
17
+ <%= clipboard_component(current_store.url_or_custom_domain) %>
24
18
  </div>
25
19
 
26
20
  <div class="d-flex">
@@ -0,0 +1,30 @@
1
+ <div class="card mb-4">
2
+ <div class="card-header">
3
+ <h5 class="card-title">
4
+ <%= Spree.t(:gift_card_batch) %>
5
+ </h5>
6
+ </div>
7
+
8
+ <div class="card-body">
9
+ <div class="form-group">
10
+ <%= f.label :prefix, raw(Spree.t('admin.gift_card_batches.prefix') + required_span_tag)%>
11
+ <%= f.text_field :prefix, class: 'form-control', disabled: f.object.persisted? %>
12
+ <%= f.error_message_on :prefix %>
13
+ </div>
14
+ <div class="form-group">
15
+ <%= f.label :amount, raw(Spree.t(:amount) + required_span_tag) %>
16
+ <%= f.number_field :amount, class: 'form-control', disabled: f.object.persisted?, required: true %>
17
+ <%= f.error_message_on :amount %>
18
+ </div>
19
+ <div class="form-group">
20
+ <%= f.label :codes_count, raw(Spree.t('admin.gift_card_batches.codes_count') + required_span_tag) %>
21
+ <%= f.number_field :codes_count, class: 'form-control', disabled: f.object.persisted?, required: true, min: 1, max: Spree::Config[:gift_card_batch_limit] %>
22
+ <%= f.error_message_on :codes_count %>
23
+ </div>
24
+ <div class="form-group">
25
+ <%= f.label :expires_at, Spree.t(:expires_at) %>
26
+ <%= f.date_field :expires_at, class: 'form-control', disabled: f.object.persisted? %>
27
+ <%= f.error_message_on :expires_at %>
28
+ </div>
29
+ </div>
30
+ </div>
@@ -0,0 +1,15 @@
1
+ <% content_for :page_title do %>
2
+ <%= page_header_back_button spree.admin_gift_cards_path %>
3
+ <%= Spree.t('admin.gift_card_batches.new_gift_card_batch') %>
4
+ <% end %>
5
+
6
+ <%= form_for [:admin, @object] do |f| %>
7
+ <div class="row">
8
+ <div class="col-lg-6 offset-lg-3">
9
+ <%= render partial: 'spree/admin/shared/error_messages', locals: { target: @object } %>
10
+
11
+ <%= render 'form', f: f %>
12
+ <%= render 'spree/admin/shared/new_resource_links' %>
13
+ </div>
14
+ </div>
15
+ <% end %>
@@ -0,0 +1,43 @@
1
+ <% frame_name ||= nil %>
2
+ <% filter_form_url = params[:user_id].present? ? spree.admin_user_gift_cards_path(params[:user_id]) : spree.admin_gift_cards_path %>
3
+
4
+ <%= search_form_for @search,
5
+ url: filter_form_url,
6
+ class: "filter-wrap",
7
+ data: {
8
+ controller: "filters reveal",
9
+ reveal_hidden_class: "d-none",
10
+ filters_url_value: request.url
11
+ } do |f| %>
12
+ <%= hidden_field_tag :frame_name, frame_name if frame_name.present? %>
13
+
14
+ <div class="d-flex flex-column flex-lg-row gap-2">
15
+ <%= render 'spree/admin/shared/filters_search_bar', param: :code_eq, label: Spree.t(:code) %>
16
+ <%= render "table_filter_dropdown" %>
17
+ <%= render "spree/admin/shared/filters_button" %>
18
+ </div>
19
+ <div data-reveal-target="item" class="d-none" id="table-filter">
20
+ <% if params[:user_id].blank? %>
21
+ <div class="form-group">
22
+ <%= f.label :users_email_eq, Spree.t(:email) %>
23
+ <%= f.text_field :users_email_eq, class: "form-control", data: {filters_target: :input} %>
24
+ </div>
25
+ <% end %>
26
+
27
+ <div class="form-group">
28
+ <%= f.label :batch_prefix_eq, Spree.t('admin.gift_card_batches.prefix') %>
29
+ <%= f.text_field :batch_prefix_eq, class: "form-control", data: {filters_target: :input} %>
30
+ </div>
31
+
32
+ <div class="form-actions">
33
+ <%= turbo_save_button_tag Spree.t(:filter_results) do %>
34
+ <%= icon("search") %>
35
+ <%= Spree.t(:filter_results) %>
36
+ <% end %>
37
+ </div>
38
+ </div>
39
+
40
+ <%= render "spree/admin/shared/filter_badge_template" %>
41
+
42
+ <div data-filters-target="badgesContainer" class="filter-badges-container"></div>
43
+ <% end %>
@@ -0,0 +1,30 @@
1
+ <% unless f.object.persisted? %>
2
+ <div class="form-group">
3
+ <%= f.label :code do %>
4
+ <%= Spree.t(:code) %>
5
+ <%= help_bubble(Spree.t('admin.gift_cards.code_help_text')) %>
6
+ <% end %>
7
+ <%= f.text_field :code, class: 'form-control' %>
8
+ <%= f.error_message_on :code %>
9
+ </div>
10
+ <% end %>
11
+
12
+ <div class="form-group">
13
+ <%= f.label :amount, raw(Spree.t(:amount) + required_span_tag) %>
14
+ <%= f.text_field :amount, class: 'form-control', required: true %>
15
+ <%= f.error_message_on :amount %>
16
+ </div>
17
+
18
+ <div class="form-group">
19
+ <%= f.label :user_id, Spree.t(:customer) %>
20
+ <%= tom_select_tag 'gift_card[user_id]',
21
+ active_option: @user&.id || @object.user_id,
22
+ include_blank: true,
23
+ options: users_for_select_options %>
24
+ </div>
25
+ <div class="form-group">
26
+ <%= f.label :expires_at, Spree.t(:expires_at) %>
27
+ <%= f.date_field :expires_at, class: 'form-control' %>
28
+ <%= f.error_message_on :expires_at %>
29
+ </div>
30
+
@@ -0,0 +1,28 @@
1
+ <% link = @user.present? ? spree.admin_user_gift_card_path(gift_card, user_id: @user.id) : spree.admin_gift_card_path(gift_card) %>
2
+
3
+ <tr id="<%= spree_dom_id gift_card %>" data-controller="row-link">
4
+ <td class="w-25 py-0">
5
+ <%= link_to gift_card.code.upcase, link, class: 'font-weight-bold d-block w-100 h-100 py-2', data: { 'turbo-frame': '_top', row_link_target: :link } %>
6
+ </td>
7
+ <td class="w-10 cursor-pointer" data-action="click->row-link#openLink"><%= gift_card.display_amount %></td>
8
+ <td class="w-10 cursor-pointer" data-action="click->row-link#openLink"><%= gift_card.display_amount_used %></td>
9
+ <td class="w-10 cursor-pointer" data-action="click->row-link#openLink"><%= gift_card.currency.upcase %></td>
10
+ <td class="w-10 cursor-pointer" data-action="click->row-link#openLink"><%= active_badge(gift_card.active?, label: gift_card.display_state) %></td>
11
+ <td class="w-10 cursor-pointer" data-action="click->row-link#openLink">
12
+ <% if gift_card.expires_at.present? %>
13
+ <%= local_date(gift_card.expires_at) %>
14
+ <% else %>
15
+ <span class="text-muted">
16
+ <%= Spree.t(:not_available) %>
17
+ </span>
18
+ <% end %></td>
19
+ <td class="w-15 cursor-pointer" data-action="click->row-link#openLink">
20
+ <% if gift_card.user.present? %>
21
+ <%= gift_card.user.email %>
22
+ <% else %>
23
+ <span class="text-muted">
24
+ <%= Spree.t(:not_available) %>
25
+ </span>
26
+ <% end %>
27
+ </td>
28
+ </tr>
@@ -0,0 +1,24 @@
1
+ <% if @collection.any? %>
2
+ <div class="table-responsive" id="listing_gift_cards">
3
+ <table class="table">
4
+ <thead class="text-muted">
5
+ <tr>
6
+ <th><%= Spree.t(:code) %></th>
7
+ <th><%= Spree.t(:amount) %></th>
8
+ <th><%= Spree.t(:used) %></th>
9
+ <th><%= Spree.t(:currency) %></th>
10
+ <th><%= Spree.t(:status) %></th>
11
+ <th><%= Spree.t(:expires_at) %></th>
12
+ <th><%= Spree.t(:customer) %></th>
13
+ </tr>
14
+ </thead>
15
+ <tbody>
16
+ <%= render collection: @collection, partial: 'spree/admin/gift_cards/gift_card' %>
17
+ </tbody>
18
+ </table>
19
+ </div>
20
+ <%= render partial: 'spree/admin/shared/index_table_options', locals: { collection: @collection } %>
21
+ <% else %>
22
+ <% new_object_url = @user.present? ? spree.new_admin_user_gift_card_path(@user) : spree.new_admin_gift_card_path %>
23
+ <%= render 'spree/admin/shared/no_resource_found', model_class: Spree::GiftCard, new_object_url: new_object_url %>
24
+ <% end %>