spree_admin 5.4.3 → 5.5.0.rc1
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.
- checksums.yaml +4 -4
- data/app/controllers/spree/admin/assets_controller.rb +7 -1
- data/app/controllers/spree/admin/channels_controller.rb +13 -0
- data/app/controllers/spree/admin/products_controller.rb +8 -8
- data/app/helpers/spree/admin/assets_helper.rb +5 -1
- data/app/helpers/spree/admin/base_helper.rb +0 -8
- data/app/helpers/spree/admin/channels_helper.rb +14 -0
- data/app/helpers/spree/admin/json_preview_helper.rb +5 -0
- data/app/helpers/spree/admin/orders_helper.rb +0 -29
- data/app/helpers/spree/admin/publishing_helper.rb +73 -0
- data/app/helpers/spree/admin/turbo_helper.rb +0 -18
- data/app/helpers/spree/admin/webhook_endpoints_helper.rb +1 -0
- data/app/javascript/spree/admin/application.js +2 -0
- data/app/javascript/spree/admin/controllers/product_form_controller.js +0 -26
- data/app/javascript/spree/admin/controllers/product_publishing_controller.js +11 -0
- data/app/javascript/spree/admin/controllers/slug_form_controller.js +18 -1
- data/app/presenters/spree/admin/order_summary_presenter.rb +12 -0
- data/app/views/spree/admin/api_keys/_form.html.erb +18 -0
- data/app/views/spree/admin/assets/edit.html.erb +64 -9
- data/app/views/spree/admin/channels/_form.html.erb +19 -0
- data/app/views/spree/admin/channels/edit.html.erb +1 -0
- data/app/views/spree/admin/channels/index.html.erb +12 -0
- data/app/views/spree/admin/channels/new.html.erb +1 -0
- data/app/views/spree/admin/integrations/_integration.html.erb +7 -9
- data/app/views/spree/admin/integrations/edit.html.erb +1 -1
- data/app/views/spree/admin/products/_form.html.erb +2 -1
- data/app/views/spree/admin/products/form/_publishing.html.erb +125 -0
- data/app/views/spree/admin/products/form/_status.html.erb +3 -47
- data/app/views/spree/admin/shared/_media_asset.html.erb +1 -1
- data/app/views/spree/admin/variants/edit.html.erb +0 -1
- data/config/initializers/spree_admin_navigation.rb +9 -0
- data/config/initializers/spree_admin_tables.rb +60 -0
- data/config/locales/en.yml +32 -3
- data/config/routes.rb +1 -0
- data/lib/spree/admin/engine.rb +2 -0
- metadata +18 -11
- data/app/helpers/spree/admin/modal_helper.rb +0 -31
- data/app/views/spree/admin/shared/_modal.html.erb +0 -27
- /data/app/views/spree/admin/promotion_rules/forms/{_taxon.html.erb → _category.html.erb} +0 -0
- /data/app/views/spree/admin/promotion_rules/forms/{_user.html.erb → _customer.html.erb} +0 -0
- /data/app/views/spree/admin/promotion_rules/forms/{_user_logged_in.html.erb → _customer_logged_in.html.erb} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2f4bbbb1dd33ca70b641c0c4c687f4a8a36731ec816c3324930b44139d911bd6
|
|
4
|
+
data.tar.gz: d402abe10df4c9fe520db4f9b97fb17e33172238ff7e58b14f71b2754a9c09a5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9931e85d7ade12bc71fd556f9014cb410ad03e205eedc6ba4c1bc03d3e5d38a32ddabd7a5b34114fe0cb1b58059d7b6ce36907c3515a866812609c9911989517
|
|
7
|
+
data.tar.gz: 8f9090d2f5d25c761f4f3ce74701fabd179b9947c21ce23ab64362ac67363ce77b63c624136a35bb02b2897d60fe72e716ac4e8539fdc032f5bc7c7cdbcab12c
|
|
@@ -38,8 +38,14 @@ module Spree
|
|
|
38
38
|
|
|
39
39
|
private
|
|
40
40
|
|
|
41
|
+
# Includes :variant_ids so the variant-assignment checkboxes mass-assign
|
|
42
|
+
# straight into Spree::Asset#variant_ids= — the model resolves prefixed
|
|
43
|
+
# ids and rejects cross-product variants.
|
|
41
44
|
def permitted_resource_params
|
|
42
|
-
params.require(:asset).permit(
|
|
45
|
+
params.require(:asset).permit(
|
|
46
|
+
*Spree::PermittedAttributes.asset_attributes,
|
|
47
|
+
variant_ids: []
|
|
48
|
+
)
|
|
43
49
|
end
|
|
44
50
|
|
|
45
51
|
def create_turbo_stream_enabled?
|
|
@@ -21,7 +21,7 @@ module Spree
|
|
|
21
21
|
new_action.before :build_master_stock_items
|
|
22
22
|
edit_action.before :build_master_prices
|
|
23
23
|
edit_action.before :build_master_stock_items
|
|
24
|
-
create.after :
|
|
24
|
+
create.after :assign_session_uploaded_assets
|
|
25
25
|
update.before :skip_updating_status
|
|
26
26
|
update.before :update_status
|
|
27
27
|
update.before :remove_empty_params
|
|
@@ -185,7 +185,7 @@ module Spree
|
|
|
185
185
|
@product_variant_ids[variant.human_name] = variant.id.to_s
|
|
186
186
|
@product_variant_prefix_ids[variant.human_name] = variant.to_param
|
|
187
187
|
|
|
188
|
-
image = variant.primary_media
|
|
188
|
+
image = variant.primary_media || @product.primary_media
|
|
189
189
|
if image.present? && image.attached? && image.variable?
|
|
190
190
|
@product_variant_images[variant.human_name] = helpers.spree_image_url(image, variant: :mini)
|
|
191
191
|
end
|
|
@@ -287,15 +287,15 @@ module Spree
|
|
|
287
287
|
{}
|
|
288
288
|
end
|
|
289
289
|
|
|
290
|
-
def
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
uploaded_assets = session_uploaded_assets('Spree::Variant')
|
|
290
|
+
def assign_session_uploaded_assets
|
|
291
|
+
uploaded_assets = session_uploaded_assets('Spree::Product')
|
|
294
292
|
|
|
295
293
|
return if uploaded_assets.empty?
|
|
296
294
|
|
|
297
|
-
uploaded_assets.update_all(viewable_id: @product.
|
|
298
|
-
|
|
295
|
+
uploaded_assets.update_all(viewable_id: @product.id, viewable_type: 'Spree::Product', updated_at: Time.current)
|
|
296
|
+
@product.update_thumbnail!
|
|
297
|
+
|
|
298
|
+
clear_session_for_uploaded_assets('Spree::Product')
|
|
299
299
|
end
|
|
300
300
|
|
|
301
301
|
def check_slug_availability
|
|
@@ -3,7 +3,11 @@ module Spree
|
|
|
3
3
|
module AssetsHelper
|
|
4
4
|
def media_form_assets(viewable, viewable_type)
|
|
5
5
|
if viewable&.persisted?
|
|
6
|
-
|
|
6
|
+
# Product#images delegates to the master variant (legacy alias), so
|
|
7
|
+
# 5.5+ product-level uploads wouldn't show. gallery_media returns
|
|
8
|
+
# product-level media when present and falls back to legacy
|
|
9
|
+
# variant-pinned images during the transition.
|
|
10
|
+
viewable.respond_to?(:gallery_media) ? viewable.gallery_media : viewable.images
|
|
7
11
|
elsif session_uploaded_assets(viewable_type).any?
|
|
8
12
|
Spree::Asset.accessible_by(current_ability, :manage).where(id: session_uploaded_assets(viewable_type))
|
|
9
13
|
else
|
|
@@ -36,14 +36,6 @@ module Spree
|
|
|
36
36
|
@settings_area.present?
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
def settings_active?
|
|
40
|
-
Spree::Deprecation.warn('settings_active? is deprecated and will be removed in Spree 5.5. Please use settings_area? instead')
|
|
41
|
-
@settings_active || %w[admin_users audits custom_domains exports invitations oauth_applications
|
|
42
|
-
payment_methods refund_reasons reimbursement_types return_authorization_reasons roles
|
|
43
|
-
shipping_categories shipping_methods stock_locations store_credit_categories
|
|
44
|
-
stores tax_categories tax_rates webhooks webhooks_subscribers zones policies metafield_definitions].include?(controller_name) || settings_area?
|
|
45
|
-
end
|
|
46
|
-
|
|
47
39
|
# @return [Array<String>] the available countries for checkout
|
|
48
40
|
def available_countries_iso
|
|
49
41
|
@available_countries_iso ||= current_store.countries_available_for_checkout.pluck(:iso)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Admin
|
|
3
|
+
module ChannelsHelper
|
|
4
|
+
# Registered +Spree::OrderRouting::Strategy::Base+ subclasses presented in
|
|
5
|
+
# the channel edit form, sourced from +Spree.order_routing.strategies+ so the
|
|
6
|
+
# picker can never drift from what the model accepts. A blank value clears the
|
|
7
|
+
# channel-level override and falls back to
|
|
8
|
+
# +Store#preferred_order_routing_strategy+.
|
|
9
|
+
def channel_order_routing_strategy_options
|
|
10
|
+
Spree.order_routing.strategies.map { |strategy| [strategy.display_name, strategy.to_s] }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -69,6 +69,11 @@ module Spree
|
|
|
69
69
|
return Spree.api.public_send(method_name)
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
+
if namespace == 'Spree::Api::V3::Admin'
|
|
73
|
+
method_name = "admin_#{class_name.underscore}_serializer"
|
|
74
|
+
return Spree.api.public_send(method_name) if Spree.api.respond_to?(method_name)
|
|
75
|
+
end
|
|
76
|
+
|
|
72
77
|
# Fall back to direct constant lookup
|
|
73
78
|
serializer_class_name = "#{namespace}::#{class_name}Serializer"
|
|
74
79
|
serializer_class_name.safe_constantize
|
|
@@ -1,24 +1,6 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
module Admin
|
|
3
3
|
module OrdersHelper
|
|
4
|
-
TaxLine = Struct.new(:label, :display_amount, :item, :for_shipment, keyword_init: true) do
|
|
5
|
-
def name
|
|
6
|
-
Spree::Deprecation.warn("TaxLine is deprecated and will be removed in Spree 5.5")
|
|
7
|
-
item_name = item.name
|
|
8
|
-
item_name += " #{Spree.t(:shipment).downcase}" if for_shipment
|
|
9
|
-
|
|
10
|
-
"#{label} (#{item_name})"
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def order_summary_tax_lines_additional(order)
|
|
15
|
-
Spree::Deprecation.warn("order_summary_tax_lines_additional is deprecated and will be removed in Spree 5.5")
|
|
16
|
-
line_item_taxes = order.line_item_adjustments.tax.map { |tax_adjustment| map_to_tax_line(tax_adjustment) }
|
|
17
|
-
shipment_taxes = order.shipment_adjustments.tax.map { |tax_adjustment| map_to_tax_line(tax_adjustment, for_shipment: true) }
|
|
18
|
-
|
|
19
|
-
line_item_taxes + shipment_taxes
|
|
20
|
-
end
|
|
21
|
-
|
|
22
4
|
def order_payment_state(order, options = {})
|
|
23
5
|
return if order.payment_state.blank?
|
|
24
6
|
|
|
@@ -140,17 +122,6 @@ module Spree
|
|
|
140
122
|
'' => 'Transaction failed because wrong CVV2 number was entered or no CVV2 number was entered'
|
|
141
123
|
}
|
|
142
124
|
end
|
|
143
|
-
|
|
144
|
-
private
|
|
145
|
-
|
|
146
|
-
def map_to_tax_line(tax_adjustment, for_shipment: false)
|
|
147
|
-
TaxLine.new(
|
|
148
|
-
label: tax_adjustment.label,
|
|
149
|
-
display_amount: tax_adjustment.display_amount,
|
|
150
|
-
item: tax_adjustment.adjustable,
|
|
151
|
-
for_shipment: for_shipment
|
|
152
|
-
)
|
|
153
|
-
end
|
|
154
125
|
end
|
|
155
126
|
end
|
|
156
127
|
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
module Admin
|
|
5
|
+
# Helpers for the product Publishing card in the Rails admin. Mirrors the
|
|
6
|
+
# SPA's publishing card (see packages/dashboard/src/components/spree/products/publishing-card.tsx):
|
|
7
|
+
# per-channel status is gated by product status — Draft/Archived products
|
|
8
|
+
# render every publication as +not_available+ regardless of window.
|
|
9
|
+
module PublishingHelper
|
|
10
|
+
# @param product_status [String] the product's status (draft/active/archived)
|
|
11
|
+
# @param publication [Spree::ProductPublication]
|
|
12
|
+
# @return [Symbol] one of :live, :scheduled, :hidden, :not_available
|
|
13
|
+
def publication_schedule_status(product_status, publication)
|
|
14
|
+
return :not_available unless product_status == 'active'
|
|
15
|
+
|
|
16
|
+
now = Time.current
|
|
17
|
+
return :hidden if publication.unpublished_at && publication.unpublished_at <= now
|
|
18
|
+
return :scheduled if publication.published_at && publication.published_at > now
|
|
19
|
+
|
|
20
|
+
:live
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Renders a colored dot + status label for a publication row.
|
|
24
|
+
def publication_status_badge(product_status, publication)
|
|
25
|
+
status = publication_schedule_status(product_status, publication)
|
|
26
|
+
dot_class = case status
|
|
27
|
+
when :live then 'bg-success'
|
|
28
|
+
when :scheduled then 'bg-warning'
|
|
29
|
+
else 'bg-secondary'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
label = Spree.t("admin.publishing.status_#{status}")
|
|
33
|
+
|
|
34
|
+
content_tag(:span, class: 'd-inline-flex align-items-center gap-1 text-muted small') do
|
|
35
|
+
content_tag(:span, '', class: "publication-dot rounded-circle #{dot_class}",
|
|
36
|
+
style: 'display:inline-block; width:0.5rem; height:0.5rem;') +
|
|
37
|
+
content_tag(:span, label)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# One-line summary of the publication window in the store's timezone.
|
|
42
|
+
def publication_caption(product_status, publication, store)
|
|
43
|
+
status = publication_schedule_status(product_status, publication)
|
|
44
|
+
tz = ActiveSupport::TimeZone[store.preferred_timezone] || Time.zone
|
|
45
|
+
|
|
46
|
+
case status
|
|
47
|
+
when :not_available
|
|
48
|
+
Spree.t('admin.publishing.caption_not_available',
|
|
49
|
+
product_status: Spree.t("admin.products.status_options.#{product_status}", default: product_status.to_s.humanize))
|
|
50
|
+
when :hidden
|
|
51
|
+
Spree.t('admin.publishing.caption_unpublished',
|
|
52
|
+
date: l(publication.unpublished_at.in_time_zone(tz), format: :short))
|
|
53
|
+
when :scheduled
|
|
54
|
+
if publication.unpublished_at
|
|
55
|
+
Spree.t('admin.publishing.caption_window',
|
|
56
|
+
start: l(publication.published_at.in_time_zone(tz), format: :short),
|
|
57
|
+
end: l(publication.unpublished_at.in_time_zone(tz), format: :short))
|
|
58
|
+
else
|
|
59
|
+
Spree.t('admin.publishing.caption_scheduled',
|
|
60
|
+
date: l(publication.published_at.in_time_zone(tz), format: :short))
|
|
61
|
+
end
|
|
62
|
+
else # :live
|
|
63
|
+
if publication.unpublished_at
|
|
64
|
+
Spree.t('admin.publishing.caption_hidden_after',
|
|
65
|
+
date: l(publication.unpublished_at.in_time_zone(tz), format: :short))
|
|
66
|
+
else
|
|
67
|
+
Spree.t('admin.publishing.caption_live')
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -1,24 +1,6 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
module Admin
|
|
3
3
|
module TurboHelper
|
|
4
|
-
def turbo_close_modal(modal_id = nil)
|
|
5
|
-
Spree::Deprecation.warn('turbo_close_modal is deprecated and will be removed in Spree 5.5. Use turbo_close_dialog instead.')
|
|
6
|
-
|
|
7
|
-
modal_id ||= 'modal'
|
|
8
|
-
|
|
9
|
-
turbo_stream.replace :modal_scripts do
|
|
10
|
-
turbo_frame_tag :modal_scripts do
|
|
11
|
-
javascript_tag do
|
|
12
|
-
raw <<~JS
|
|
13
|
-
if (document.querySelector('##{modal_id}')) {
|
|
14
|
-
window.$("##{modal_id}").modal('hide');
|
|
15
|
-
}
|
|
16
|
-
JS
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
4
|
def turbo_close_dialog
|
|
23
5
|
turbo_stream.replace 'main-dialog' do
|
|
24
6
|
render 'spree/admin/shared/dialog'
|
|
@@ -68,6 +68,7 @@ import OrderBillingAddressController from 'spree/admin/controllers/order_billing
|
|
|
68
68
|
import PageBuilderController from 'spree/admin/controllers/page_builder_controller'
|
|
69
69
|
import PasswordToggle from 'spree/admin/controllers/password_toggle_controller'
|
|
70
70
|
import ProductFormController from 'spree/admin/controllers/product_form_controller'
|
|
71
|
+
import ProductPublishingController from 'spree/admin/controllers/product_publishing_controller'
|
|
71
72
|
import QueryBuilderController from 'spree/admin/controllers/query_builder_controller'
|
|
72
73
|
import RangeInputController from 'spree/admin/controllers/range_input_controller'
|
|
73
74
|
import TableController from 'spree/admin/controllers/table_controller'
|
|
@@ -140,6 +141,7 @@ application.register('page-builder', PageBuilderController)
|
|
|
140
141
|
application.register('password-toggle', PasswordToggle)
|
|
141
142
|
application.register('password-visibility', PasswordVisibility)
|
|
142
143
|
application.register('product-form', ProductFormController)
|
|
144
|
+
application.register('product-publishing', ProductPublishingController)
|
|
143
145
|
application.register('query-builder', QueryBuilderController)
|
|
144
146
|
application.register('range-input', RangeInputController)
|
|
145
147
|
application.register('table', TableController)
|
|
@@ -4,10 +4,6 @@ export default class extends Controller {
|
|
|
4
4
|
static targets = [
|
|
5
5
|
'trackInventoryCheckbox',
|
|
6
6
|
'quantityForm',
|
|
7
|
-
'availableOn',
|
|
8
|
-
'makeActiveAt',
|
|
9
|
-
'discontinueOn',
|
|
10
|
-
'status',
|
|
11
7
|
'pricesForm'
|
|
12
8
|
]
|
|
13
9
|
|
|
@@ -26,28 +22,6 @@ export default class extends Controller {
|
|
|
26
22
|
this.togglePricesFormVisibility()
|
|
27
23
|
}
|
|
28
24
|
|
|
29
|
-
switchAvailabilityDatesFields(event) {
|
|
30
|
-
let status = event.target.value
|
|
31
|
-
if (status === 'draft') {
|
|
32
|
-
this.show(this.availableOnTarget)
|
|
33
|
-
this.show(this.makeActiveAtTarget)
|
|
34
|
-
} else if (status === 'active') {
|
|
35
|
-
this.show(this.availableOnTarget)
|
|
36
|
-
this.hide(this.makeActiveAtTarget)
|
|
37
|
-
} else {
|
|
38
|
-
this.hide(this.availableOnTarget)
|
|
39
|
-
this.hide(this.makeActiveAtTarget)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
show(element) {
|
|
44
|
-
element.classList.remove('hidden', 'd-none')
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
hide(element) {
|
|
48
|
-
element.classList.add('hidden')
|
|
49
|
-
}
|
|
50
|
-
|
|
51
25
|
toggleQuantityFormVisibility() {
|
|
52
26
|
if (this.hasQuantityFormTarget) {
|
|
53
27
|
if (!this.hasVariantsValue && this.trackInventoryCheckboxTarget.checked) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Controller } from '@hotwired/stimulus'
|
|
2
|
+
|
|
3
|
+
// Toggles the "Manage" channel-checkbox panel on the product Publishing card.
|
|
4
|
+
// Pairs with spree/admin/app/views/spree/admin/products/form/_publishing.html.erb.
|
|
5
|
+
export default class extends Controller {
|
|
6
|
+
static targets = ['manage']
|
|
7
|
+
|
|
8
|
+
toggleManage() {
|
|
9
|
+
this.manageTarget.classList.toggle('d-none')
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -6,9 +6,26 @@ export default class extends Controller {
|
|
|
6
6
|
'url',
|
|
7
7
|
]
|
|
8
8
|
|
|
9
|
+
connect() {
|
|
10
|
+
this.urlTouched = this.hasUrlTarget && this.urlTarget.value.length > 0
|
|
11
|
+
if (this.hasUrlTarget) {
|
|
12
|
+
this.urlTarget.addEventListener('input', () => { this.urlTouched = true })
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
9
16
|
updateUrlFromName() {
|
|
17
|
+
if (this.urlTouched) return
|
|
10
18
|
const name = this.nameTarget.value
|
|
11
|
-
|
|
19
|
+
// Mirrors ActiveSupport's +String#parameterize+: NFKD decompose, drop
|
|
20
|
+
// combining marks (accents), lowercase, hyphenate the rest. Keeps the
|
|
21
|
+
// legacy admin's auto-fill aligned with what +normalizes :code+ stores
|
|
22
|
+
// server-side — without NFKD, "Café" rendered "caf-" instead of "cafe".
|
|
23
|
+
const url = name
|
|
24
|
+
.normalize('NFKD')
|
|
25
|
+
.replace(/\p{M}/gu, '')
|
|
26
|
+
.toLowerCase()
|
|
27
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
28
|
+
.replace(/(^-|-$)+/g, '')
|
|
12
29
|
this.urlTarget.value = url
|
|
13
30
|
}
|
|
14
31
|
}
|
|
@@ -13,6 +13,7 @@ module Spree
|
|
|
13
13
|
[
|
|
14
14
|
*metadata_rows,
|
|
15
15
|
:separator,
|
|
16
|
+
channel_row,
|
|
16
17
|
market_row,
|
|
17
18
|
locale_row,
|
|
18
19
|
currency_row,
|
|
@@ -73,6 +74,17 @@ module Spree
|
|
|
73
74
|
rows
|
|
74
75
|
end
|
|
75
76
|
|
|
77
|
+
def channel_row
|
|
78
|
+
return nil if order.channel.blank?
|
|
79
|
+
|
|
80
|
+
{
|
|
81
|
+
label: Spree.t(:channel),
|
|
82
|
+
value: order.channel.name,
|
|
83
|
+
id: 'channel',
|
|
84
|
+
link: Spree::Core::Engine.routes.url_helpers.edit_admin_channel_path(order.channel)
|
|
85
|
+
}
|
|
86
|
+
end
|
|
87
|
+
|
|
76
88
|
def market_row
|
|
77
89
|
return nil if order.market.blank?
|
|
78
90
|
|
|
@@ -24,3 +24,21 @@
|
|
|
24
24
|
<% end %>
|
|
25
25
|
</div>
|
|
26
26
|
</div>
|
|
27
|
+
|
|
28
|
+
<div class="card mb-6" id="api-key-scopes" data-secret-only data-show-when-key-type="secret">
|
|
29
|
+
<div class="card-header">
|
|
30
|
+
<h5 class="card-title"><%= Spree.t('admin.api_keys.scopes', default: 'Scopes') %></h5>
|
|
31
|
+
<p class="text-sm text-gray-500 mt-1">
|
|
32
|
+
<%= Spree.t('admin.api_keys.scopes_description', default: 'Required for secret keys. Pick the narrowest set of scopes your integration needs.') %>
|
|
33
|
+
</p>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="card-body">
|
|
36
|
+
<% Spree::ApiKey::SCOPES.each do |scope| %>
|
|
37
|
+
<div class="custom-control form-checkbox mb-1">
|
|
38
|
+
<%= f.check_box :scopes, { multiple: true, class: 'custom-control-input', id: "api_key_scopes_#{scope}" }, scope, nil %>
|
|
39
|
+
<%= f.label "scopes_#{scope}", scope, class: 'custom-control-label font-mono text-sm' %>
|
|
40
|
+
</div>
|
|
41
|
+
<% end %>
|
|
42
|
+
<%= f.hidden_field :scopes, value: '', multiple: true %>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
@@ -1,17 +1,72 @@
|
|
|
1
|
-
<%= turbo_frame_tag :
|
|
2
|
-
<%= dialog_header(Spree.t(:edit) + ' ' + Spree.t(:image)) %>
|
|
1
|
+
<%= turbo_frame_tag :drawer do %>
|
|
3
2
|
<%= form_with model: @asset, url: spree.admin_asset_path(@asset), method: :put, scope: :asset do |f| %>
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
<%= drawer_header(Spree.t(:edit) + ' ' + Spree.t(:image)) %>
|
|
4
|
+
|
|
5
|
+
<div class="drawer-body">
|
|
6
|
+
<% if @asset.attachment.attached? %>
|
|
7
|
+
<% blob_url_opts = { host: request.host_with_port, protocol: (request.ssl? ? 'https' : 'http') } %>
|
|
8
|
+
<% download_url = Rails.application.routes.url_helpers.rails_blob_url(@asset.attachment.blob, **blob_url_opts, disposition: 'attachment') %>
|
|
9
|
+
<% original_url = Rails.application.routes.url_helpers.rails_blob_url(@asset.attachment.blob, **blob_url_opts) %>
|
|
10
|
+
<div class="mb-4 rounded-lg border bg-light overflow-hidden">
|
|
11
|
+
<a href="<%= original_url %>"
|
|
12
|
+
target="_blank"
|
|
13
|
+
rel="noopener"
|
|
14
|
+
title="<%= Spree.t(:view_full_size) %>"
|
|
15
|
+
style="display: block; position: relative; cursor: zoom-in;">
|
|
16
|
+
<%= spree_image_tag(@asset.attachment, variant: :large, alt: @asset.alt, class: 'w-full max-h-96 object-contain') %>
|
|
17
|
+
<span style="position: absolute; top: 8px; right: 8px; display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 6px; background: rgba(255,255,255,0.95); color: #4b5563; box-shadow: 0 1px 2px rgba(0,0,0,0.1); pointer-events: none;">
|
|
18
|
+
<%= icon('zoom-in', class: 'mr-0') %>
|
|
19
|
+
</span>
|
|
20
|
+
</a>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="flex gap-2 mb-4">
|
|
23
|
+
<%= link_to original_url, target: '_blank', rel: 'noopener', class: 'btn btn-light btn-sm' do %>
|
|
24
|
+
<%= icon('external-link', class: 'mr-1') %><%= Spree.t(:view_full_size) %>
|
|
25
|
+
<% end %>
|
|
26
|
+
<%= link_to download_url, target: '_blank', rel: 'noopener', class: 'btn btn-light btn-sm' do %>
|
|
27
|
+
<%= icon('download', class: 'mr-1') %><%= Spree.t(:download) %>
|
|
28
|
+
<% end %>
|
|
29
|
+
</div>
|
|
30
|
+
<% else %>
|
|
31
|
+
<%= f.spree_file_field :attachment, width: 200, height: 200, can_delete: false, label: Spree.t(:image) %>
|
|
32
|
+
<% end %>
|
|
33
|
+
|
|
34
|
+
<%= f.spree_text_area :alt, label: Spree.t(:alt_text), rows: 3 %>
|
|
35
|
+
|
|
36
|
+
<%# Variant assignment shows only for product-level assets — variant-pinned assets
|
|
37
|
+
already live on a single variant via the polymorphic viewable, so the
|
|
38
|
+
join-table UI doesn't apply. Empty submission (no boxes ticked) clears
|
|
39
|
+
all links thanks to the hidden field below. %>
|
|
40
|
+
<% if @asset.viewable_type == 'Spree::Product' && @asset.viewable.present? && @asset.viewable.variants.any? %>
|
|
41
|
+
<% product = @asset.viewable %>
|
|
42
|
+
<% linked_ids = @asset.variant_media.pluck(:variant_id).to_set %>
|
|
43
|
+
<div class="mt-4 pt-4 border-t">
|
|
44
|
+
<label class="form-label"><%= Spree.t(:assigned_variants) %></label>
|
|
45
|
+
<p class="text-muted small mb-2">
|
|
46
|
+
<%= Spree.t(:assigned_variants_help) %>
|
|
47
|
+
</p>
|
|
48
|
+
<%= hidden_field_tag 'asset[variant_ids][]', '' %>
|
|
49
|
+
<div class="flex flex-col gap-1">
|
|
50
|
+
<% product.variants.each do |variant| %>
|
|
51
|
+
<% chip_id = "asset_variant_#{variant.id}" %>
|
|
52
|
+
<div class="custom-control form-checkbox rounded-lg hover:bg-gray-100 p-2 transition-colors duration-100 ease-linear">
|
|
53
|
+
<%= check_box_tag('asset[variant_ids][]', variant.to_param, linked_ids.include?(variant.id), id: chip_id, class: 'custom-control-input') %>
|
|
54
|
+
<%= label_tag chip_id, class: 'custom-control-label' do %>
|
|
55
|
+
<%= variant.descriptive_name.presence || variant.sku %>
|
|
56
|
+
<% end %>
|
|
57
|
+
</div>
|
|
58
|
+
<% end %>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
<% end %>
|
|
6
62
|
</div>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
</div>
|
|
10
|
-
<div class="dialog-footer">
|
|
63
|
+
|
|
64
|
+
<div class="drawer-footer gap-3">
|
|
11
65
|
<%= turbo_save_button_tag %>
|
|
66
|
+
<%= drawer_discard_button %>
|
|
12
67
|
<%= link_to Spree.t('actions.destroy'), object_url(@asset),
|
|
13
68
|
data: { turbo_method: :delete, turbo_confirm: Spree.t(:are_you_sure_delete), turbo_frame: '_top' },
|
|
14
69
|
class: 'btn btn-danger ml-auto' if can?(:destroy, @asset) %>
|
|
15
70
|
</div>
|
|
16
71
|
<% end %>
|
|
17
|
-
<% end %>
|
|
72
|
+
<% end %>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<div class="card mb-6">
|
|
2
|
+
<div class="card-body" data-controller="slug-form">
|
|
3
|
+
<%= f.spree_text_field :name,
|
|
4
|
+
autofocus: f.object.new_record?,
|
|
5
|
+
required: true,
|
|
6
|
+
data: { slug_form_target: :name, action: 'slug-form#updateUrlFromName' } %>
|
|
7
|
+
<%= f.spree_text_field :code,
|
|
8
|
+
required: true,
|
|
9
|
+
hint: Spree.t('admin.channels.code_hint'),
|
|
10
|
+
data: { slug_form_target: :url } %>
|
|
11
|
+
<%= f.spree_check_box :active, label: Spree.t(:active) %>
|
|
12
|
+
<%= f.spree_check_box :default, label: Spree.t('admin.channels.default'), hint: Spree.t('admin.channels.default_hint') %>
|
|
13
|
+
<%= f.spree_select :preferred_order_routing_strategy,
|
|
14
|
+
options_for_select(channel_order_routing_strategy_options, f.object.preferred_order_routing_strategy),
|
|
15
|
+
{ label: Spree.t('admin.channels.order_routing_strategy'),
|
|
16
|
+
help_bubble: Spree.t('admin.channels.order_routing_strategy_hint'),
|
|
17
|
+
include_blank: Spree.t('admin.channels.order_routing_strategy_inherit') } %>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= render 'spree/admin/shared/edit_resource' %>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<% content_for :page_title do %>
|
|
2
|
+
<%= Spree.t(:channels) %>
|
|
3
|
+
<% end %>
|
|
4
|
+
|
|
5
|
+
<% content_for :page_actions do %>
|
|
6
|
+
<%= render_admin_partials(:channels_actions_partials) %>
|
|
7
|
+
<%= link_to_with_icon 'plus', Spree.t(:new_channel), new_object_url, class: "btn btn-primary" if can? :create, Spree::Channel %>
|
|
8
|
+
<% end %>
|
|
9
|
+
|
|
10
|
+
<%= render_admin_partials(:channels_header_partials) %>
|
|
11
|
+
|
|
12
|
+
<%= render_table @collection, :channels %>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= render 'spree/admin/shared/new_resource' %>
|
|
@@ -5,29 +5,27 @@
|
|
|
5
5
|
|
|
6
6
|
<div id="integration-<%= integration_class.integration_key %>" class="col mb-6 flex items-stretch">
|
|
7
7
|
<div class="card w-full">
|
|
8
|
-
<div class="card-header border-b flex
|
|
8
|
+
<div class="card-header border-b flex items-center justify-center h-auto pr-4 <% if integration.present? %>bg-green-50<% end %>">
|
|
9
9
|
<div class="flex items-center py-1">
|
|
10
10
|
<%= image_tag integration_class.icon_path, alt: integration_name, height: 36 if integration_class.icon_path.present? %>
|
|
11
11
|
</div>
|
|
12
|
-
|
|
13
|
-
<% if integration.present? %>
|
|
14
|
-
<span class="badge badge-success small">
|
|
15
|
-
<%= icon('check', class: 'mr-1') %>
|
|
16
|
-
<%= Spree.t(:active) %>
|
|
17
|
-
</span>
|
|
18
|
-
<% end %>
|
|
19
12
|
</div>
|
|
20
13
|
|
|
21
14
|
<div class="card-body">
|
|
22
15
|
<p><%= integration_description %></p>
|
|
23
16
|
</div>
|
|
24
17
|
|
|
25
|
-
<div class="card-footer flex <% if integration.present? %>items-
|
|
18
|
+
<div class="card-footer flex <% if integration.present? %>items-center gap-3<% else %>flex-col<% end %>">
|
|
26
19
|
<% if integration.present? %>
|
|
27
20
|
<%= link_to spree.edit_admin_integration_path(integration), class: 'btn btn-sm btn-light' do %>
|
|
28
21
|
<%= icon 'settings' %>
|
|
29
22
|
<%= Spree.t(:settings) %>
|
|
30
23
|
<% end if can?(:update, integration) %>
|
|
24
|
+
|
|
25
|
+
<span class="ml-auto inline-flex items-center text-xs font-medium text-green-700">
|
|
26
|
+
<span class="w-1.5 h-1.5 rounded-full bg-green-500 mr-1"></span>
|
|
27
|
+
<%= Spree.t(:active) %>
|
|
28
|
+
</span>
|
|
31
29
|
<% else %>
|
|
32
30
|
<%= link_to_with_icon 'plus', "#{Spree.t('actions.connect')} #{integration_class.integration_name}", spree.new_admin_integration_path(integration: { type: integration_class.name }), class: 'btn btn-primary truncate w-full' if can?(:create, Spree::Integration) %>
|
|
33
31
|
<% end %>
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
<%= render partial: 'spree/admin/shared/error_messages', locals: { target: @integration } %>
|
|
7
7
|
|
|
8
|
-
<%= form_for @integration, as: :integration, url: spree.admin_integration_path(@integration) do |form| %>
|
|
8
|
+
<%= form_for @integration, as: :integration, url: spree.admin_integration_path(@integration), html: { id: "edit_integration_#{@integration.id}" } do |form| %>
|
|
9
9
|
<div class="grid grid-cols-12 gap-6">
|
|
10
10
|
<div class="col-span-12 lg:col-span-6 lg:col-start-4">
|
|
11
11
|
<%= render "spree/admin/integrations/forms/#{@integration.key}", form: form %>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<div class="grid grid-cols-12 gap-6" data-controller="product-form slug-form seo-form" data-seo-form-editor-value="tinymce">
|
|
2
2
|
<div class="col-span-12 md:col-span-8">
|
|
3
3
|
<%= render 'spree/admin/products/form/base', f: f %>
|
|
4
|
-
<%= render 'spree/admin/shared/media_form', viewable: @product
|
|
4
|
+
<%= render 'spree/admin/shared/media_form', viewable: @product, viewable_type: 'Spree::Product' %>
|
|
5
5
|
<%= f.fields_for :master, @product.master do |vf| %>
|
|
6
6
|
<%= render 'spree/admin/variants/form/pricing', f: vf %>
|
|
7
7
|
<% end unless @product.has_variants? %>
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
</div>
|
|
12
12
|
<div class="col-span-12 md:col-span-4">
|
|
13
13
|
<%= render 'spree/admin/products/form/status', f: f %>
|
|
14
|
+
<%= render 'spree/admin/products/form/publishing', f: f %>
|
|
14
15
|
<%= render 'spree/admin/products/form/categorization', f: f %>
|
|
15
16
|
<%= render 'spree/admin/products/form/shipping', f: f %>
|
|
16
17
|
<%= render 'spree/admin/products/form/tax', f: f %>
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
Publishing card — mirrors the SPA's <PublishingCard> in
|
|
3
|
+
packages/dashboard/src/components/spree/products/publishing-card.tsx.
|
|
4
|
+
|
|
5
|
+
All channel attach/detach AND per-channel scheduling submit through the
|
|
6
|
+
same nested-attributes form (+legacy_product_publications_attributes+). Each row
|
|
7
|
+
in the Manage panel toggles the +_destroy+ flag on the corresponding
|
|
8
|
+
publication; unchecked rows for unattached channels send blank attrs that
|
|
9
|
+
the model's +reject_if+ discards.
|
|
10
|
+
|
|
11
|
+
Datetime inputs render values in the store's timezone since datetime-local
|
|
12
|
+
has no TZ awareness (same pattern as the legacy form/_status partial).
|
|
13
|
+
%>
|
|
14
|
+
|
|
15
|
+
<%
|
|
16
|
+
store_timezone = ActiveSupport::TimeZone[current_store.preferred_timezone] || Time.zone
|
|
17
|
+
to_store_local = ->(value) { value&.in_time_zone(store_timezone)&.strftime('%Y-%m-%dT%H:%M') }
|
|
18
|
+
tz_help = Spree.t('admin.datetime_field_timezone_help', zone: store_timezone.name)
|
|
19
|
+
store_channels = current_store.channels.order(:name).to_a
|
|
20
|
+
publications_by_channel = @product.product_publications.includes(:channel).index_by(&:channel_id)
|
|
21
|
+
|
|
22
|
+
# Build one record per store channel: the existing publication when
|
|
23
|
+
# attached, or a fresh ProductPublication when not. Stable channel-keyed
|
|
24
|
+
# indices keep the form params deterministic so checkbox toggles always
|
|
25
|
+
# reference the right slot.
|
|
26
|
+
channel_rows = store_channels.map do |channel|
|
|
27
|
+
publication = publications_by_channel[channel.id] ||
|
|
28
|
+
@product.product_publications.build(channel: channel)
|
|
29
|
+
[channel, publication]
|
|
30
|
+
end
|
|
31
|
+
%>
|
|
32
|
+
|
|
33
|
+
<div class="card mb-6" data-controller="product-publishing">
|
|
34
|
+
<div class="card-header d-flex align-items-center justify-content-between">
|
|
35
|
+
<h6 class="card-title mb-0"><%= Spree.t('admin.publishing.title') %></h6>
|
|
36
|
+
<% if store_channels.any? %>
|
|
37
|
+
<button type="button"
|
|
38
|
+
class="btn btn-sm btn-outline-secondary"
|
|
39
|
+
data-action="product-publishing#toggleManage">
|
|
40
|
+
<%= Spree.t('admin.publishing.manage_cta') %>
|
|
41
|
+
</button>
|
|
42
|
+
<% end %>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div class="card-body">
|
|
46
|
+
<% if store_channels.empty? %>
|
|
47
|
+
<p class="text-xs text-muted mb-0"><%= Spree.t('admin.publishing.no_channels') %></p>
|
|
48
|
+
<% else %>
|
|
49
|
+
<%# ---- Manage panel (hidden by default) ---- %>
|
|
50
|
+
<div class="d-none flex flex-col gap-2 mb-3" data-product-publishing-target="manage">
|
|
51
|
+
<p class="text-xs text-muted mb-1"><%= Spree.t('admin.publishing.manage_description') %></p>
|
|
52
|
+
<% channel_rows.each_with_index do |(channel, publication), index| %>
|
|
53
|
+
<%= f.fields_for :legacy_product_publications, publication, child_index: index do |pf| %>
|
|
54
|
+
<%= pf.hidden_field :id if publication.persisted? %>
|
|
55
|
+
<%= pf.hidden_field :channel_id, value: channel.id %>
|
|
56
|
+
<% checked_by_default = publication.persisted? || (@product.new_record? && channel.default?) %>
|
|
57
|
+
<label class="flex items-center gap-3 rounded border px-3 py-2 cursor-pointer hover:bg-gray-50 text-sm">
|
|
58
|
+
<%# The +_destroy+ hidden field defaults to 1 (= destroy/skip);
|
|
59
|
+
the checkbox flips it to 0 (= keep/upsert) when checked.
|
|
60
|
+
+include_hidden: false+ on the checkbox keeps Rails from
|
|
61
|
+
emitting its own competing hidden field. %>
|
|
62
|
+
<%= pf.hidden_field :_destroy, value: '1', id: nil %>
|
|
63
|
+
<%= pf.check_box :_destroy,
|
|
64
|
+
{ checked: checked_by_default,
|
|
65
|
+
class: 'form-check-input m-0',
|
|
66
|
+
include_hidden: false },
|
|
67
|
+
'0', '1' %>
|
|
68
|
+
<span class="flex-1"><%= channel.name %></span>
|
|
69
|
+
<% unless channel.active? %>
|
|
70
|
+
<span class="text-xs text-muted"><%= Spree.t('admin.publishing.inactive_marker') %></span>
|
|
71
|
+
<% end %>
|
|
72
|
+
</label>
|
|
73
|
+
<% end %>
|
|
74
|
+
<% end %>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<%# ---- Per-publication scheduling rows ---- %>
|
|
78
|
+
<% if publications_by_channel.empty? %>
|
|
79
|
+
<p class="text-xs text-muted mb-0"><%= Spree.t('admin.publishing.empty') %></p>
|
|
80
|
+
<% else %>
|
|
81
|
+
<% channel_rows.each_with_index do |(channel, publication), index| %>
|
|
82
|
+
<% next unless publication.persisted? %>
|
|
83
|
+
<%= f.fields_for :legacy_product_publications, publication, child_index: index do |pf| %>
|
|
84
|
+
<details class="publication-row group rounded -mx-2 px-2 hover:bg-gray-50">
|
|
85
|
+
<summary class="flex items-start justify-between gap-3 py-2 cursor-pointer" style="list-style: none;">
|
|
86
|
+
<div class="flex min-w-0 flex-1 flex-col gap-0.5">
|
|
87
|
+
<div class="flex items-center gap-2">
|
|
88
|
+
<span class="truncate text-sm font-medium"><%= channel.name %></span>
|
|
89
|
+
<%= publication_status_badge(@product.status, publication) %>
|
|
90
|
+
</div>
|
|
91
|
+
<span class="text-xs text-muted">
|
|
92
|
+
<%= publication_caption(@product.status, publication, current_store) %>
|
|
93
|
+
</span>
|
|
94
|
+
</div>
|
|
95
|
+
<span class="shrink-0 mt-1 opacity-0 transition-opacity group-hover:opacity-100 text-muted">
|
|
96
|
+
<%= icon('pencil', class: 'h-3.5 w-3.5') %>
|
|
97
|
+
</span>
|
|
98
|
+
</summary>
|
|
99
|
+
<div class="mt-2 ps-2 pb-2 flex flex-col gap-3">
|
|
100
|
+
<div>
|
|
101
|
+
<%= pf.label :published_at,
|
|
102
|
+
Spree.t('admin.publishing.published_at_label'),
|
|
103
|
+
class: 'form-label text-xs mb-1' %>
|
|
104
|
+
<%= pf.datetime_field :published_at,
|
|
105
|
+
value: to_store_local.call(publication.published_at),
|
|
106
|
+
class: 'form-control form-control-sm' %>
|
|
107
|
+
<small class="text-muted text-xs"><%= tz_help %></small>
|
|
108
|
+
</div>
|
|
109
|
+
<div>
|
|
110
|
+
<%= pf.label :unpublished_at,
|
|
111
|
+
Spree.t('admin.publishing.unpublished_at_label'),
|
|
112
|
+
class: 'form-label text-xs mb-1' %>
|
|
113
|
+
<%= pf.datetime_field :unpublished_at,
|
|
114
|
+
value: to_store_local.call(publication.unpublished_at),
|
|
115
|
+
class: 'form-control form-control-sm' %>
|
|
116
|
+
<small class="text-muted text-xs"><%= tz_help %></small>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</details>
|
|
120
|
+
<% end %>
|
|
121
|
+
<% end %>
|
|
122
|
+
<% end %>
|
|
123
|
+
<% end %>
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
@@ -1,51 +1,7 @@
|
|
|
1
|
-
<%# datetime-local inputs have no timezone; render values in the store's timezone so admins see and submit values in the store's local time. %>
|
|
2
|
-
<%
|
|
3
|
-
store_timezone = ActiveSupport::TimeZone[current_store.preferred_timezone] || Time.zone
|
|
4
|
-
to_store_local = ->(value) { value&.in_time_zone(store_timezone)&.strftime('%Y-%m-%dT%H:%M') }
|
|
5
|
-
make_active_at_local = to_store_local.call(@product.make_active_at)
|
|
6
|
-
available_on_local = to_store_local.call(@product.available_on)
|
|
7
|
-
discontinue_on_local = to_store_local.call(@product.discontinue_on)
|
|
8
|
-
tz_help = Spree.t('admin.datetime_field_timezone_help', zone: store_timezone.name)
|
|
9
|
-
%>
|
|
10
|
-
|
|
11
1
|
<div class="card mb-6">
|
|
12
2
|
<div class="card-body">
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
available_status_options(@product),
|
|
17
|
-
{ selected: @product.status, help_bubble: show_product_status_help_bubble? ? Spree.t('admin.products.status_form.status') : nil },
|
|
18
|
-
{ data: {
|
|
19
|
-
action: 'change->product-form#switchAvailabilityDatesFields',
|
|
20
|
-
'product-form-target': 'status'
|
|
21
|
-
}
|
|
22
|
-
} %>
|
|
23
|
-
</div>
|
|
24
|
-
<div class="col-span-6 <%= 'hidden' if @product.active? %>" data-product-form-target="makeActiveAt">
|
|
25
|
-
<% if can?(:activate, @product) %>
|
|
26
|
-
<%= f.spree_datetime_field :make_active_at,
|
|
27
|
-
value: make_active_at_local,
|
|
28
|
-
help: tz_help,
|
|
29
|
-
help_bubble: Spree.t('admin.products.status_form.make_active_at'),
|
|
30
|
-
max: discontinue_on_local %>
|
|
31
|
-
<% end %>
|
|
32
|
-
</div>
|
|
33
|
-
</div>
|
|
34
|
-
<div class="grid grid-cols-12 gap-6">
|
|
35
|
-
<div data-product-form-target="availableOn" class="col-span-6">
|
|
36
|
-
<%= f.spree_datetime_field :available_on,
|
|
37
|
-
value: available_on_local,
|
|
38
|
-
help: tz_help,
|
|
39
|
-
help_bubble: Spree.t('admin.products.status_form.available_on'),
|
|
40
|
-
max: discontinue_on_local %>
|
|
41
|
-
</div>
|
|
42
|
-
<div data-product-form-target="discontinueOn" class="col-span-6">
|
|
43
|
-
<%= f.spree_datetime_field :discontinue_on,
|
|
44
|
-
value: discontinue_on_local,
|
|
45
|
-
help: tz_help,
|
|
46
|
-
help_bubble: Spree.t('admin.products.status_form.discontinue_on'),
|
|
47
|
-
min: make_active_at_local %>
|
|
48
|
-
</div>
|
|
49
|
-
</div>
|
|
3
|
+
<%= f.spree_select :status,
|
|
4
|
+
available_status_options(@product),
|
|
5
|
+
{ selected: @product.status, help_bubble: show_product_status_help_bubble? ? Spree.t('admin.products.status_form.status') : nil } %>
|
|
50
6
|
</div>
|
|
51
7
|
</div>
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
<button class="btn btn-light btn-sm px-1"><%= icon('grip-vertical', class: 'mr-0') %></button>
|
|
8
8
|
</div>
|
|
9
9
|
|
|
10
|
-
<%= link_to spree.edit_admin_asset_path(asset), data: { turbo_frame: '
|
|
10
|
+
<%= link_to spree.edit_admin_asset_path(asset), data: { turbo_frame: 'drawer', action: 'drawer#open' } do %>
|
|
11
11
|
<%= spree_image_tag(asset.attachment, width: 200, height: 200, alt: asset.alt, class: 'w-full block h-full absolute') %>
|
|
12
12
|
<% end %>
|
|
13
13
|
</div>
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
<%= render 'spree/admin/variants/form/basic', f: f %>
|
|
11
11
|
<%= render 'spree/admin/variants/form/pricing', f: f %>
|
|
12
12
|
<%= render 'spree/admin/variants/form/inventory', f: f %>
|
|
13
|
-
<%= render 'spree/admin/shared/media_form', viewable: @variant, viewable_type: 'Spree::Variant' %>
|
|
14
13
|
<%= render 'spree/admin/shared/edit_resource_links', f: f %>
|
|
15
14
|
</div>
|
|
16
15
|
</div>
|
|
@@ -251,6 +251,15 @@ Rails.application.config.after_initialize do
|
|
|
251
251
|
active: -> { controller_name == 'policies' },
|
|
252
252
|
if: -> { can?(:manage, Spree::Policy) }
|
|
253
253
|
|
|
254
|
+
# Channels
|
|
255
|
+
settings_nav.add :channels,
|
|
256
|
+
label: :channels,
|
|
257
|
+
url: :admin_channels_path,
|
|
258
|
+
icon: 'broadcast',
|
|
259
|
+
position: 65,
|
|
260
|
+
active: -> { controller_name == 'channels' },
|
|
261
|
+
if: -> { can?(:manage, Spree::Channel) }
|
|
262
|
+
|
|
254
263
|
# Payment Methods
|
|
255
264
|
settings_nav.add :payment_methods,
|
|
256
265
|
label: :payments,
|
|
@@ -128,6 +128,19 @@ Rails.application.config.after_initialize do
|
|
|
128
128
|
search_url: ->(view_context) { view_context.spree.admin_tags_select_options_path(format: :json, taggable_type: 'Spree::Product') },
|
|
129
129
|
method: ->(product) { product.tag_list.to_sentence }
|
|
130
130
|
|
|
131
|
+
Spree.admin.tables.products.add :channels,
|
|
132
|
+
label: :channels,
|
|
133
|
+
type: :association,
|
|
134
|
+
filter_type: :select,
|
|
135
|
+
sortable: false,
|
|
136
|
+
filterable: true,
|
|
137
|
+
default: false,
|
|
138
|
+
position: 90,
|
|
139
|
+
ransack_attribute: 'channels_id',
|
|
140
|
+
operators: %i[in not_in],
|
|
141
|
+
value_options: -> { Spree::Current.store&.channels&.order(:name)&.pluck(:name, :id)&.map { |name, id| { value: id, label: name } } || [] },
|
|
142
|
+
method: ->(product) { product.channels.pluck(:name).to_sentence }
|
|
143
|
+
|
|
131
144
|
# Products bulk actions
|
|
132
145
|
Spree.admin.tables.products.add_bulk_action :set_active,
|
|
133
146
|
label: 'admin.bulk_ops.products.title.set_active',
|
|
@@ -350,6 +363,19 @@ Rails.application.config.after_initialize do
|
|
|
350
363
|
operators: %i[in],
|
|
351
364
|
search_url: ->(view_context) { view_context.spree.select_options_admin_promotions_path(format: :json) }
|
|
352
365
|
|
|
366
|
+
Spree.admin.tables.orders.add :channel,
|
|
367
|
+
label: :channel,
|
|
368
|
+
type: :association,
|
|
369
|
+
filter_type: :select,
|
|
370
|
+
sortable: true,
|
|
371
|
+
filterable: true,
|
|
372
|
+
default: true,
|
|
373
|
+
position: 35,
|
|
374
|
+
ransack_attribute: 'channel_id',
|
|
375
|
+
operators: %i[eq not_eq in not_in],
|
|
376
|
+
value_options: -> { Spree::Current.store&.channels&.order(:name)&.pluck(:name, :id)&.map { |name, id| { value: id, label: name } } || [] },
|
|
377
|
+
method: ->(order) { order.channel&.name }
|
|
378
|
+
|
|
353
379
|
# Register Checkouts table (draft orders)
|
|
354
380
|
Spree.admin.tables.register(:checkouts, model_class: Spree::Order, search_param: :search, date_range_param: :created_at, new_resource: false)
|
|
355
381
|
|
|
@@ -1744,6 +1770,40 @@ Rails.application.config.after_initialize do
|
|
|
1744
1770
|
default: true,
|
|
1745
1771
|
position: 10
|
|
1746
1772
|
|
|
1773
|
+
# ==========================================
|
|
1774
|
+
# Register Channels table
|
|
1775
|
+
# ==========================================
|
|
1776
|
+
Spree.admin.tables.register(:channels, model_class: Spree::Channel, search_param: :name_cont)
|
|
1777
|
+
|
|
1778
|
+
Spree.admin.tables.channels.add :name,
|
|
1779
|
+
label: :name,
|
|
1780
|
+
type: :link,
|
|
1781
|
+
sortable: true,
|
|
1782
|
+
filterable: true,
|
|
1783
|
+
default: true,
|
|
1784
|
+
position: 10
|
|
1785
|
+
|
|
1786
|
+
Spree.admin.tables.channels.add :code,
|
|
1787
|
+
label: :code,
|
|
1788
|
+
sortable: true,
|
|
1789
|
+
default: true,
|
|
1790
|
+
position: 20
|
|
1791
|
+
|
|
1792
|
+
Spree.admin.tables.channels.add :active,
|
|
1793
|
+
label: :status,
|
|
1794
|
+
type: :status,
|
|
1795
|
+
sortable: false,
|
|
1796
|
+
default: true,
|
|
1797
|
+
position: 30,
|
|
1798
|
+
method: ->(c) { c.active? ? 'active' : 'inactive' }
|
|
1799
|
+
|
|
1800
|
+
Spree.admin.tables.channels.add :default,
|
|
1801
|
+
label: :default,
|
|
1802
|
+
type: :boolean,
|
|
1803
|
+
sortable: true,
|
|
1804
|
+
default: true,
|
|
1805
|
+
position: 40
|
|
1806
|
+
|
|
1747
1807
|
# ==========================================
|
|
1748
1808
|
# Register Refund Reasons table
|
|
1749
1809
|
# ==========================================
|
data/config/locales/en.yml
CHANGED
|
@@ -26,6 +26,8 @@ en:
|
|
|
26
26
|
revoked: API key has been revoked
|
|
27
27
|
revoked_at: Revoked at
|
|
28
28
|
revoked_by: Revoked by
|
|
29
|
+
scopes: Scopes
|
|
30
|
+
scopes_description: Required for secret keys. Pick the narrowest set of scopes your integration needs.
|
|
29
31
|
secret_key_hidden: This secret key cannot be revealed. If you've lost it, revoke this key and create a new one.
|
|
30
32
|
statuses:
|
|
31
33
|
active: Active
|
|
@@ -73,6 +75,13 @@ en:
|
|
|
73
75
|
title:
|
|
74
76
|
add_tags: Add tags
|
|
75
77
|
remove_tags: Remove tags
|
|
78
|
+
channels:
|
|
79
|
+
code_hint: A short, stable identifier (e.g. "online", "pos", "wholesale"). Used by API clients via the X-Spree-Channel header and for order attribution.
|
|
80
|
+
default: Default channel
|
|
81
|
+
default_hint: Exactly one channel per store is the default. Used as the fallback when no channel is selected and as the auto-publish target for new products.
|
|
82
|
+
order_routing_strategy: Order routing strategy
|
|
83
|
+
order_routing_strategy_hint: Overrides the store-level routing strategy for orders attributed to this channel.
|
|
84
|
+
order_routing_strategy_inherit: Inherit from store
|
|
76
85
|
checkout_settings:
|
|
77
86
|
checkout_links:
|
|
78
87
|
description: Links to these pages will be displayed in the checkout footer.
|
|
@@ -278,14 +287,34 @@ en:
|
|
|
278
287
|
seo:
|
|
279
288
|
placeholder: Add a title and description to see how this product might appear in a search engine listing
|
|
280
289
|
status_form:
|
|
281
|
-
available_on: Marks when the product will be released, put a future date to indicate that this is a pre-order
|
|
282
|
-
discontinue_on: Marks when the product should be automatically taken off from your site
|
|
283
|
-
make_active_at: Marks when the product should be automatically promoted to "active" state
|
|
284
290
|
status: Draft products aren't available for purchase. Active products are live.
|
|
291
|
+
status_options:
|
|
292
|
+
active: Active
|
|
293
|
+
archived: Archived
|
|
294
|
+
draft: Draft
|
|
285
295
|
stores:
|
|
286
296
|
choose_stores: Choose which stores this product should be available in
|
|
287
297
|
variants:
|
|
288
298
|
option_types_link: To add more option types please go to <a href="%{link}">Option Types</a>
|
|
299
|
+
publishing:
|
|
300
|
+
caption_hidden_after: Comes down %{date}
|
|
301
|
+
caption_live: Visible to customers now
|
|
302
|
+
caption_not_available: Hidden — product is %{product_status}
|
|
303
|
+
caption_scheduled: Goes live %{date}
|
|
304
|
+
caption_unpublished: Was hidden on %{date}
|
|
305
|
+
caption_window: Live %{start} → %{end}
|
|
306
|
+
empty: Not listed on any sales channel yet.
|
|
307
|
+
inactive_marker: Inactive
|
|
308
|
+
manage_cta: Manage
|
|
309
|
+
manage_description: Pick which sales channels list this product. Toggle a channel off to unlist it everywhere.
|
|
310
|
+
no_channels: No sales channels available. Create one in Settings → Sales channels.
|
|
311
|
+
published_at_label: Publish from
|
|
312
|
+
status_hidden: Hidden
|
|
313
|
+
status_live: Live
|
|
314
|
+
status_not_available: Not available
|
|
315
|
+
status_scheduled: Scheduled
|
|
316
|
+
title: Publishing
|
|
317
|
+
unpublished_at_label: Unpublish on
|
|
289
318
|
recommended_size: Recommended size
|
|
290
319
|
report_created: Your report is being generated. You will receive an email with a download link when it is ready!
|
|
291
320
|
reset_digital_link_download_limits: Reset download limits
|
data/config/routes.rb
CHANGED
|
@@ -226,6 +226,7 @@ Spree::Core::Engine.add_routes do
|
|
|
226
226
|
resources :payment_methods, except: :show
|
|
227
227
|
resources :shipping_methods, except: :show
|
|
228
228
|
resources :shipping_categories, except: :show
|
|
229
|
+
resources :channels, except: :show
|
|
229
230
|
resources :store_credit_categories
|
|
230
231
|
resources :tax_rates, except: :show
|
|
231
232
|
resources :tax_categories, except: :show
|
data/lib/spree/admin/engine.rb
CHANGED
|
@@ -9,6 +9,8 @@ module Spree
|
|
|
9
9
|
:admin_users_header_partials,
|
|
10
10
|
:body_end_partials,
|
|
11
11
|
:body_start_partials,
|
|
12
|
+
:channels_actions_partials,
|
|
13
|
+
:channels_header_partials,
|
|
12
14
|
:classifications_actions_partials,
|
|
13
15
|
:classifications_header_partials,
|
|
14
16
|
:coupon_codes_actions_partials,
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spree_admin
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.
|
|
4
|
+
version: 5.5.0.rc1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vendo Connect Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-06-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: spree
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 5.
|
|
19
|
+
version: 5.5.0.rc1
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 5.
|
|
26
|
+
version: 5.5.0.rc1
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: active_link_to
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -301,6 +301,7 @@ files:
|
|
|
301
301
|
- app/controllers/spree/admin/assets_controller.rb
|
|
302
302
|
- app/controllers/spree/admin/base_controller.rb
|
|
303
303
|
- app/controllers/spree/admin/bulk_operations_controller.rb
|
|
304
|
+
- app/controllers/spree/admin/channels_controller.rb
|
|
304
305
|
- app/controllers/spree/admin/checkouts_controller.rb
|
|
305
306
|
- app/controllers/spree/admin/classifications_controller.rb
|
|
306
307
|
- app/controllers/spree/admin/countries_controller.rb
|
|
@@ -392,6 +393,7 @@ files:
|
|
|
392
393
|
- app/helpers/spree/admin/base_helper.rb
|
|
393
394
|
- app/helpers/spree/admin/bulk_editor_helper.rb
|
|
394
395
|
- app/helpers/spree/admin/bulk_operations_helper.rb
|
|
396
|
+
- app/helpers/spree/admin/channels_helper.rb
|
|
395
397
|
- app/helpers/spree/admin/code_block_helper.rb
|
|
396
398
|
- app/helpers/spree/admin/customer_returns_helper.rb
|
|
397
399
|
- app/helpers/spree/admin/dialog_helper.rb
|
|
@@ -400,7 +402,6 @@ files:
|
|
|
400
402
|
- app/helpers/spree/admin/flash_helper.rb
|
|
401
403
|
- app/helpers/spree/admin/json_preview_helper.rb
|
|
402
404
|
- app/helpers/spree/admin/metafields_helper.rb
|
|
403
|
-
- app/helpers/spree/admin/modal_helper.rb
|
|
404
405
|
- app/helpers/spree/admin/navigation_helper.rb
|
|
405
406
|
- app/helpers/spree/admin/onboarding_helper.rb
|
|
406
407
|
- app/helpers/spree/admin/orders_filters_helper.rb
|
|
@@ -411,6 +412,7 @@ files:
|
|
|
411
412
|
- app/helpers/spree/admin/promotion_actions_helper.rb
|
|
412
413
|
- app/helpers/spree/admin/promotion_rules_helper.rb
|
|
413
414
|
- app/helpers/spree/admin/promotions_helper.rb
|
|
415
|
+
- app/helpers/spree/admin/publishing_helper.rb
|
|
414
416
|
- app/helpers/spree/admin/reimbursement_type_helper.rb
|
|
415
417
|
- app/helpers/spree/admin/shipment_helper.rb
|
|
416
418
|
- app/helpers/spree/admin/sortable_tree_helper.rb
|
|
@@ -456,6 +458,7 @@ files:
|
|
|
456
458
|
- app/javascript/spree/admin/controllers/page_builder_controller.js
|
|
457
459
|
- app/javascript/spree/admin/controllers/password_toggle_controller.js
|
|
458
460
|
- app/javascript/spree/admin/controllers/product_form_controller.js
|
|
461
|
+
- app/javascript/spree/admin/controllers/product_publishing_controller.js
|
|
459
462
|
- app/javascript/spree/admin/controllers/query_builder_controller.js
|
|
460
463
|
- app/javascript/spree/admin/controllers/range_input_controller.js
|
|
461
464
|
- app/javascript/spree/admin/controllers/replace_controller.js
|
|
@@ -546,6 +549,10 @@ files:
|
|
|
546
549
|
- app/views/spree/admin/bulk_operations/forms/_tag_picker.html.erb
|
|
547
550
|
- app/views/spree/admin/bulk_operations/forms/_taxon_picker.html.erb
|
|
548
551
|
- app/views/spree/admin/bulk_operations/new.html.erb
|
|
552
|
+
- app/views/spree/admin/channels/_form.html.erb
|
|
553
|
+
- app/views/spree/admin/channels/edit.html.erb
|
|
554
|
+
- app/views/spree/admin/channels/index.html.erb
|
|
555
|
+
- app/views/spree/admin/channels/new.html.erb
|
|
549
556
|
- app/views/spree/admin/checkouts/index.html.erb
|
|
550
557
|
- app/views/spree/admin/classifications/_classification.html.erb
|
|
551
558
|
- app/views/spree/admin/classifications/create.turbo_stream.erb
|
|
@@ -776,6 +783,7 @@ files:
|
|
|
776
783
|
- app/views/spree/admin/products/form/_base.html.erb
|
|
777
784
|
- app/views/spree/admin/products/form/_categorization.html.erb
|
|
778
785
|
- app/views/spree/admin/products/form/_inventory.html.erb
|
|
786
|
+
- app/views/spree/admin/products/form/_publishing.html.erb
|
|
779
787
|
- app/views/spree/admin/products/form/_shipping.html.erb
|
|
780
788
|
- app/views/spree/admin/products/form/_status.html.erb
|
|
781
789
|
- app/views/spree/admin/products/form/_tax.html.erb
|
|
@@ -795,17 +803,17 @@ files:
|
|
|
795
803
|
- app/views/spree/admin/promotion_actions/new.html.erb
|
|
796
804
|
- app/views/spree/admin/promotion_rules/_promotion_rule.html.erb
|
|
797
805
|
- app/views/spree/admin/promotion_rules/edit.html.erb
|
|
806
|
+
- app/views/spree/admin/promotion_rules/forms/_category.html.erb
|
|
798
807
|
- app/views/spree/admin/promotion_rules/forms/_country.html.erb
|
|
799
808
|
- app/views/spree/admin/promotion_rules/forms/_currency.html.erb
|
|
809
|
+
- app/views/spree/admin/promotion_rules/forms/_customer.html.erb
|
|
800
810
|
- app/views/spree/admin/promotion_rules/forms/_customer_group.html.erb
|
|
811
|
+
- app/views/spree/admin/promotion_rules/forms/_customer_logged_in.html.erb
|
|
801
812
|
- app/views/spree/admin/promotion_rules/forms/_first_order.html.erb
|
|
802
813
|
- app/views/spree/admin/promotion_rules/forms/_item_total.html.erb
|
|
803
814
|
- app/views/spree/admin/promotion_rules/forms/_one_use_per_user.html.erb
|
|
804
815
|
- app/views/spree/admin/promotion_rules/forms/_option_value.html.erb
|
|
805
816
|
- app/views/spree/admin/promotion_rules/forms/_product.html.erb
|
|
806
|
-
- app/views/spree/admin/promotion_rules/forms/_taxon.html.erb
|
|
807
|
-
- app/views/spree/admin/promotion_rules/forms/_user.html.erb
|
|
808
|
-
- app/views/spree/admin/promotion_rules/forms/_user_logged_in.html.erb
|
|
809
817
|
- app/views/spree/admin/promotion_rules/new.html.erb
|
|
810
818
|
- app/views/spree/admin/promotions/_actions.html.erb
|
|
811
819
|
- app/views/spree/admin/promotions/_form.html.erb
|
|
@@ -881,7 +889,6 @@ files:
|
|
|
881
889
|
- app/views/spree/admin/shared/_map.html.erb
|
|
882
890
|
- app/views/spree/admin/shared/_media_asset.html.erb
|
|
883
891
|
- app/views/spree/admin/shared/_media_form.html.erb
|
|
884
|
-
- app/views/spree/admin/shared/_modal.html.erb
|
|
885
892
|
- app/views/spree/admin/shared/_multi_product_picker.html.erb
|
|
886
893
|
- app/views/spree/admin/shared/_new_item_dropdown.html.erb
|
|
887
894
|
- app/views/spree/admin/shared/_new_resource.html.erb
|
|
@@ -1164,9 +1171,9 @@ licenses:
|
|
|
1164
1171
|
- BSD-3-Clause
|
|
1165
1172
|
metadata:
|
|
1166
1173
|
bug_tracker_uri: https://github.com/spree/spree/issues
|
|
1167
|
-
changelog_uri: https://github.com/spree/spree/releases/tag/v5.
|
|
1174
|
+
changelog_uri: https://github.com/spree/spree/releases/tag/v5.5.0.rc1
|
|
1168
1175
|
documentation_uri: https://docs.spreecommerce.org/
|
|
1169
|
-
source_code_uri: https://github.com/spree/spree/tree/v5.
|
|
1176
|
+
source_code_uri: https://github.com/spree/spree/tree/v5.5.0.rc1
|
|
1170
1177
|
post_install_message:
|
|
1171
1178
|
rdoc_options: []
|
|
1172
1179
|
require_paths:
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
module Spree
|
|
2
|
-
module Admin
|
|
3
|
-
module ModalHelper
|
|
4
|
-
# render a header for the modal
|
|
5
|
-
# @param title [String, Proc] the title of the modal
|
|
6
|
-
# @return [String]
|
|
7
|
-
def modal_header(title)
|
|
8
|
-
Spree::Deprecation.warn('Bootstrap modals are deprecated and will be removed in Spree 5.5. Please use native dialogs with `dialog_header` helper.')
|
|
9
|
-
|
|
10
|
-
title = capture(&title) if block_given?
|
|
11
|
-
content_tag(:div, class: 'modal-header') do
|
|
12
|
-
content_tag(:h5, title, class: 'modal-title') + modal_close_button
|
|
13
|
-
end.html_safe
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# render a close button for the modal
|
|
17
|
-
# @return [String]
|
|
18
|
-
def modal_close_button
|
|
19
|
-
button_tag('', type: 'button', class: 'btn-close', data: { dismiss: 'modal', aria_label: Spree.t(:close) }).html_safe
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
# render a discard button for the modal
|
|
23
|
-
# @return [String]
|
|
24
|
-
def modal_discard_button
|
|
25
|
-
button_tag(type: 'button', class: 'btn btn-light mr-auto', data: { dismiss: 'modal' }) do
|
|
26
|
-
Spree.t('actions.discard')
|
|
27
|
-
end.html_safe
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
<div class="modal fade" id="modal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
|
2
|
-
<div class="modal-dialog modal-dialog-centered">
|
|
3
|
-
<%= turbo_frame_tag :dialog_modal do %>
|
|
4
|
-
<div class="modal-content">
|
|
5
|
-
<%= modal_header(Spree.t(:loading)) %>
|
|
6
|
-
<div class="modal-body text-center min-h-[300px]">
|
|
7
|
-
<%= render 'spree/admin/shared/spinner' %>
|
|
8
|
-
</div>
|
|
9
|
-
</div>
|
|
10
|
-
<% end %>
|
|
11
|
-
</div>
|
|
12
|
-
</div>
|
|
13
|
-
|
|
14
|
-
<div class="modal fade" id="modal-lg" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
|
15
|
-
<div class="modal-dialog modal-dialog-centered modal-lg">
|
|
16
|
-
<%= turbo_frame_tag :dialog_modal_lg do %>
|
|
17
|
-
<div class="modal-content">
|
|
18
|
-
<%= modal_header(Spree.t(:loading)) %>
|
|
19
|
-
<div class="modal-body text-center min-h-[300px]">
|
|
20
|
-
<%= render 'spree/admin/shared/spinner' %>
|
|
21
|
-
</div>
|
|
22
|
-
</div>
|
|
23
|
-
<% end %>
|
|
24
|
-
</div>
|
|
25
|
-
</div>
|
|
26
|
-
|
|
27
|
-
<%= turbo_frame_tag :modal_scripts %>
|
|
File without changes
|
|
File without changes
|
|
File without changes
|