spree_admin 5.2.0.rc3 → 5.2.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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/spree/admin/application.scss +1 -1
  3. data/app/assets/stylesheets/spree/admin/components/_dropdowns.scss +2 -0
  4. data/app/assets/stylesheets/spree/admin/components/_main.scss +2 -233
  5. data/app/assets/stylesheets/spree/admin/components/_sidebar.scss +693 -0
  6. data/app/assets/stylesheets/spree/admin/global/_variables.scss +1 -0
  7. data/app/assets/stylesheets/spree/admin/shared/_base.scss +7 -1
  8. data/app/assets/stylesheets/spree/admin/views/_dashboard.scss +14 -0
  9. data/app/controllers/spree/admin/integrations_controller.rb +1 -1
  10. data/app/controllers/spree/admin/metafields_controller.rb +1 -1
  11. data/app/controllers/spree/admin/page_blocks_controller.rb +1 -1
  12. data/app/controllers/spree/admin/payment_methods_controller.rb +1 -1
  13. data/app/controllers/spree/admin/promotion_actions_controller.rb +1 -1
  14. data/app/controllers/spree/admin/promotion_rules_controller.rb +1 -1
  15. data/app/controllers/spree/admin/promotions_controller.rb +1 -1
  16. data/app/controllers/spree/admin/reports_controller.rb +1 -1
  17. data/app/controllers/spree/admin/taxons_controller.rb +1 -1
  18. data/app/controllers/spree/admin/translations_controller.rb +1 -1
  19. data/app/helpers/spree/admin/base_helper.rb +1 -1
  20. data/app/helpers/spree/admin/drawer_helper.rb +6 -6
  21. data/app/helpers/spree/admin/dropdown_helper.rb +10 -2
  22. data/app/helpers/spree/admin/modal_helper.rb +2 -0
  23. data/app/helpers/spree/admin/navigation_helper.rb +46 -3
  24. data/app/helpers/spree/admin/orders_filters_helper.rb +1 -0
  25. data/app/helpers/spree/admin/promotion_actions_helper.rb +1 -1
  26. data/app/helpers/spree/admin/promotion_rules_helper.rb +1 -1
  27. data/app/helpers/spree/admin/translations_helper.rb +1 -1
  28. data/app/javascript/spree/admin/application.js +2 -1
  29. data/app/javascript/spree/admin/controllers/dropdown_controller.js +85 -14
  30. data/app/javascript/spree/admin/controllers/sidebar_controller.js +231 -0
  31. data/app/javascript/spree/admin/controllers/tooltip_controller.js +84 -31
  32. data/app/models/spree/admin/form_builder.rb +74 -16
  33. data/app/models/spree/admin/navigation/builder.rb +82 -0
  34. data/app/models/spree/admin/navigation/item.rb +177 -0
  35. data/app/models/spree/admin/navigation.rb +193 -0
  36. data/app/views/layouts/spree/admin.html.erb +1 -1
  37. data/app/views/spree/admin/assets/edit.html.erb +1 -1
  38. data/app/views/spree/admin/custom_domains/_form.html.erb +2 -14
  39. data/app/views/spree/admin/gift_cards/_filters.html.erb +26 -18
  40. data/app/views/spree/admin/gift_cards/index.html.erb +1 -1
  41. data/app/views/spree/admin/orders/_customer.html.erb +2 -2
  42. data/app/views/spree/admin/orders/_filters.html.erb +34 -25
  43. data/app/views/spree/admin/orders/_table_filter_dropdown.html.erb +1 -1
  44. data/app/views/spree/admin/page_blocks/forms/_image.html.erb +2 -5
  45. data/app/views/spree/admin/page_links/_form.html.erb +4 -13
  46. data/app/views/spree/admin/page_sections/forms/_header.html.erb +0 -2
  47. data/app/views/spree/admin/payments/_payment.html.erb +7 -0
  48. data/app/views/spree/admin/posts/_form.html.erb +1 -4
  49. data/app/views/spree/admin/posts/filters.html.erb +18 -8
  50. data/app/views/spree/admin/products/_bulk_operations.html.erb +1 -1
  51. data/app/views/spree/admin/products/_filters.html.erb +17 -6
  52. data/app/views/spree/admin/products/_table_filter_dropdown.html.erb +1 -1
  53. data/app/views/spree/admin/profile/edit.html.erb +9 -61
  54. data/app/views/spree/admin/promotions/_filters.html.erb +23 -13
  55. data/app/views/spree/admin/promotions/_table_filter_dropdown.html.erb +1 -1
  56. data/app/views/spree/admin/promotions/form/_settings.html.erb +2 -13
  57. data/app/views/spree/admin/refunds/_form.html.erb +1 -9
  58. data/app/views/spree/admin/return_authorizations/filters.html.erb +1 -1
  59. data/app/views/spree/admin/shared/_audit_nav.html.erb +2 -0
  60. data/app/views/spree/admin/shared/_calendar_range_picker.html.erb +2 -2
  61. data/app/views/spree/admin/shared/_content_header.html.erb +1 -1
  62. data/app/views/spree/admin/shared/_developers_nav.html.erb +2 -4
  63. data/app/views/spree/admin/shared/_header.html.erb +5 -7
  64. data/app/views/spree/admin/shared/_navigation.html.erb +5 -0
  65. data/app/views/spree/admin/shared/_navigation_item.html.erb +64 -0
  66. data/app/views/spree/admin/shared/_new_item_dropdown.html.erb +1 -1
  67. data/app/views/spree/admin/shared/_page_section_image.html.erb +2 -5
  68. data/app/views/spree/admin/shared/_page_section_logo.html.erb +1 -1
  69. data/app/views/spree/admin/shared/_returns_and_refunds_nav.html.erb +2 -3
  70. data/app/views/spree/admin/shared/_shipping_nav.html.erb +3 -2
  71. data/app/views/spree/admin/shared/_sidebar.html.erb +33 -7
  72. data/app/views/spree/admin/shared/_stock_nav.html.erb +6 -3
  73. data/app/views/spree/admin/shared/_tax_nav.html.erb +1 -2
  74. data/app/views/spree/admin/shared/_team_nav.html.erb +2 -3
  75. data/app/views/spree/admin/shared/_user_dropdown.html.erb +26 -17
  76. data/app/views/spree/admin/shared/sidebar/_customers_nav.html.erb +7 -0
  77. data/app/views/spree/admin/shared/sidebar/_orders_nav.html.erb +22 -2
  78. data/app/views/spree/admin/shared/sidebar/_products_nav.html.erb +21 -0
  79. data/app/views/spree/admin/shared/sidebar/_promotions_nav.html.erb +8 -0
  80. data/app/views/spree/admin/shared/sidebar/_returns_nav.html.erb +12 -0
  81. data/app/views/spree/admin/shared/sidebar/_store_dropdown.html.erb +3 -1
  82. data/app/views/spree/admin/shared/sidebar/_store_nav.html.erb +15 -1
  83. data/app/views/spree/admin/shared/sidebar/_storefront_nav.html.erb +25 -3
  84. data/app/views/spree/admin/shared/sortable_tree/_taxonomy.html.erb +1 -1
  85. data/app/views/spree/admin/stock_items/_filters.html.erb +18 -8
  86. data/app/views/spree/admin/stock_locations/_table_row.html.erb +1 -1
  87. data/app/views/spree/admin/stock_transfers/_filters.html.erb +19 -9
  88. data/app/views/spree/admin/storefront/edit.html.erb +2 -14
  89. data/app/views/spree/admin/stores/form/_basic.html.erb +2 -8
  90. data/app/views/spree/admin/stores/form/_emails.html.erb +1 -1
  91. data/app/views/spree/admin/tax_rates/_form.html.erb +1 -10
  92. data/app/views/spree/admin/taxons/_form.html.erb +2 -9
  93. data/app/views/spree/admin/themes/_theme.html.erb +1 -1
  94. data/app/views/spree/admin/translations/translation_rows/_permalink_field_row.html.erb +1 -12
  95. data/app/views/spree/admin/users/_filters.html.erb +23 -13
  96. data/config/initializers/spree_admin_navigation.rb +510 -0
  97. data/config/locales/en.yml +4 -0
  98. data/lib/generators/spree/admin/scaffold/templates/controller.rb.tt +3 -1
  99. data/lib/generators/spree/admin/scaffold/templates/views/_table_row.html.erb.tt +8 -6
  100. data/lib/spree/admin/engine.rb +64 -2
  101. data/lib/spree/admin/runtime_configuration.rb +1 -0
  102. data/lib/spree/admin.rb +20 -0
  103. metadata +17 -15
  104. data/app/assets/stylesheets/spree/admin/components/_offcanvas.scss +0 -26
  105. data/app/javascript/spree/admin/helpers/canvas.js +0 -29
  106. data/app/views/spree/admin/shared/_offcanvas_nav.html.erb +0 -12
@@ -0,0 +1,82 @@
1
+ module Spree
2
+ module Admin
3
+ class Navigation
4
+ class Builder
5
+ attr_reader :registry, :parent_item
6
+
7
+ def initialize(registry, parent_item = nil)
8
+ @registry = registry
9
+ @parent_item = parent_item
10
+ end
11
+
12
+ # Add a navigation item
13
+ # If parent_item is set, the item becomes a child
14
+ def add(key, **options, &block)
15
+ # If we have a parent item, set it in the options
16
+ if parent_item
17
+ options[:parent] = parent_item.key
18
+ # Adjust position to be relative to parent
19
+ options[:position] ||= parent_item.children.size * 10
20
+ end
21
+
22
+ item = registry.add(key, **options, &block)
23
+ item
24
+ end
25
+
26
+ # Add a section (group of items)
27
+ def section(key, label: nil, &block)
28
+ section_item = add(key, section_label: label || key.to_s.humanize)
29
+
30
+ if block_given?
31
+ builder = self.class.new(registry, section_item)
32
+ # Support both block styles: |nav| nav.add or just add
33
+ if block.arity > 0
34
+ # Block expects parameter: do |nav| nav.add ... end
35
+ block.call(builder)
36
+ else
37
+ # Block uses implicit self: do add ... end
38
+ builder.instance_eval(&block)
39
+ end
40
+ end
41
+
42
+ section_item
43
+ end
44
+
45
+ # Remove an item
46
+ def remove(key)
47
+ registry.remove(key)
48
+ end
49
+
50
+ # Update an item
51
+ def update(key, **options)
52
+ registry.update(key, **options)
53
+ end
54
+
55
+ # Insert before another item
56
+ def insert_before(target_key, new_key, **options)
57
+ registry.insert_before(target_key, new_key, **options)
58
+ end
59
+
60
+ # Insert after another item
61
+ def insert_after(target_key, new_key, **options)
62
+ registry.insert_after(target_key, new_key, **options)
63
+ end
64
+
65
+ # Move an item
66
+ def move(key, **position_options)
67
+ registry.move(key, **position_options)
68
+ end
69
+
70
+ # Replace an item
71
+ def replace(key, **options, &block)
72
+ registry.replace(key, **options, &block)
73
+ end
74
+
75
+ # Reorder items with a block
76
+ def reorder(&block)
77
+ instance_eval(&block) if block_given?
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,177 @@
1
+ module Spree
2
+ module Admin
3
+ class Navigation
4
+ class Item
5
+ attr_accessor :key, :label, :url, :icon, :position, :parent_key,
6
+ :condition, :badge, :badge_class, :tooltip, :target, :data_attributes, :children, :section_label, :active_condition
7
+
8
+ def initialize(key, **options)
9
+ @key = key.to_sym
10
+ @label = options[:label]
11
+ @url = options[:url]
12
+ @icon = options[:icon]
13
+ @position = options[:position] || 999
14
+ @parent_key = options[:parent]
15
+ @active_condition = options[:active]
16
+ @condition = options.key?(:if) ? options[:if] : options[:condition]
17
+ @badge = options[:badge]
18
+ @badge_class = options[:badge_class]
19
+ @tooltip = options[:tooltip]
20
+ @target = options[:target]
21
+ @data_attributes = options[:data_attributes] || {}
22
+ @section_label = options[:section_label]
23
+ @children = []
24
+ end
25
+
26
+ # Check if this item should be visible for the given user/context
27
+ # @param user_or_context [Object] Either a user object or a view context
28
+ def visible?(user_or_context = nil)
29
+ return true if condition.nil?
30
+
31
+ if condition.respond_to?(:call)
32
+ # If we have a view context with instance_exec, use it to evaluate the condition
33
+ # This allows access to can? and other helper methods
34
+ if user_or_context.respond_to?(:instance_exec)
35
+ user_or_context.instance_exec(&condition)
36
+ else
37
+ # Otherwise, call with the user object
38
+ condition.call(user_or_context)
39
+ end
40
+ else
41
+ condition
42
+ end
43
+ end
44
+
45
+ # Check if this item is active based on current path
46
+ # @param current_path [String] The current request path
47
+ # @param context [Object] View context with access to route helpers
48
+ def active?(current_path, context = nil)
49
+ # Use custom active condition if provided (most flexible)
50
+ if active_condition.respond_to?(:call)
51
+ if context&.respond_to?(:instance_exec)
52
+ return context.instance_exec(&active_condition)
53
+ else
54
+ return active_condition.call
55
+ end
56
+ end
57
+
58
+ # Match exact path
59
+ item_url = resolve_url(context)
60
+ return true if item_url && current_path == item_url
61
+
62
+ # Check if any child item is active
63
+ return true if children.any? { |child| child.active?(current_path, context) }
64
+
65
+ # Default: match if path starts with url (handled by active_link_to)
66
+ if item_url
67
+ current_path.start_with?(item_url)
68
+ else
69
+ false
70
+ end
71
+ end
72
+
73
+ # Resolve URL (handles symbols, procs, and strings)
74
+ # @param context [Object] View context with access to route helpers
75
+ def resolve_url(context = nil)
76
+ case url
77
+ when Symbol
78
+ # Try to call the route helper on the context (which has spree routes)
79
+ if context&.respond_to?(url)
80
+ context.send(url)
81
+ elsif context&.respond_to?(:spree)
82
+ context.spree.send(url) rescue url.to_s
83
+ else
84
+ url.to_s
85
+ end
86
+ when Proc
87
+ # Evaluate proc in the context where route helpers are available
88
+ if context&.respond_to?(:instance_exec)
89
+ context.instance_exec(&url)
90
+ else
91
+ url.call
92
+ end
93
+ else
94
+ url
95
+ end
96
+ end
97
+
98
+ # Resolve label (handles i18n keys)
99
+ def resolve_label
100
+ return label unless label.is_a?(String) || label.is_a?(Symbol)
101
+
102
+ # Use Spree.t for translation which handles the spree namespace
103
+ Spree.t(label, default: label.to_s.humanize)
104
+ end
105
+
106
+ # Compute badge value
107
+ # @param view_context [Object] View context with access to helper methods
108
+ def badge_value(view_context = nil)
109
+ return nil unless badge
110
+
111
+ if badge.respond_to?(:call)
112
+ # Evaluate badge in view context if available (for access to helpers)
113
+ if view_context&.respond_to?(:instance_exec)
114
+ view_context.instance_exec(&badge)
115
+ else
116
+ badge.call
117
+ end
118
+ else
119
+ badge
120
+ end
121
+ end
122
+
123
+ # Check if this is a section header
124
+ def section?
125
+ section_label.present?
126
+ end
127
+
128
+ # Add a child item
129
+ def add_child(item)
130
+ children << item
131
+ item.parent_key = key
132
+ sort_children!
133
+ end
134
+
135
+ # Remove a child item
136
+ def remove_child(key)
137
+ children.reject! { |child| child.key == key }
138
+ end
139
+
140
+ # Sort children by position
141
+ def sort_children!
142
+ children.sort_by! { |child| [child.position, child.key.to_s] }
143
+ end
144
+
145
+ # Deep clone for modifications
146
+ def deep_clone
147
+ cloned = self.class.new(key, to_h)
148
+ cloned.children = children.map(&:deep_clone)
149
+ cloned
150
+ end
151
+
152
+ # Convert to hash
153
+ def to_h
154
+ {
155
+ label: label,
156
+ url: url,
157
+ icon: icon,
158
+ position: position,
159
+ parent: parent_key,
160
+ active: active_condition,
161
+ condition: condition,
162
+ badge: badge,
163
+ badge_class: badge_class,
164
+ tooltip: tooltip,
165
+ target: target,
166
+ data_attributes: data_attributes,
167
+ section_label: section_label
168
+ }
169
+ end
170
+
171
+ def inspect
172
+ "#<Spree::Admin::Navigation::Item key=#{key} label=#{label} children=#{children.size}>"
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,193 @@
1
+ module Spree
2
+ module Admin
3
+ class Navigation
4
+ attr_reader :items, :context
5
+
6
+ def initialize(context)
7
+ @context = context
8
+ @items = {}
9
+ end
10
+
11
+ # Add a navigation item
12
+ def add(key, **options, &block)
13
+ key = key.to_sym
14
+ item = Item.new(key, **options)
15
+
16
+ @items[key] = item
17
+
18
+ # If block provided, it's for children
19
+ if block_given?
20
+ builder = Builder.new(self, item)
21
+ # Support both block styles: |nav| nav.add or just add
22
+ if block.arity > 0
23
+ # Block expects parameter: do |nav| nav.add ... end
24
+ block.call(builder)
25
+ else
26
+ # Block uses implicit self: do add ... end
27
+ builder.instance_eval(&block)
28
+ end
29
+ end
30
+
31
+ sort_items!
32
+ item
33
+ end
34
+
35
+ # Remove a navigation item
36
+ def remove(key)
37
+ key = key.to_sym
38
+ removed = @items.delete(key)
39
+
40
+ # Also remove from any parent's children
41
+ @items.each_value do |item|
42
+ item.remove_child(key)
43
+ end
44
+
45
+ removed
46
+ end
47
+
48
+ # Update an existing navigation item
49
+ def update(key, **options)
50
+ key = key.to_sym
51
+ item = @items[key]
52
+
53
+ return nil unless item
54
+
55
+ options.each do |attr, value|
56
+ item.send("#{attr}=", value) if item.respond_to?("#{attr}=")
57
+ end
58
+
59
+ sort_items!
60
+ item
61
+ end
62
+
63
+ # Find a navigation item
64
+ def find(key)
65
+ @items[key.to_sym]
66
+ end
67
+
68
+ # Check if item exists
69
+ def exists?(key)
70
+ @items.key?(key.to_sym)
71
+ end
72
+
73
+ # Insert item before another item
74
+ def insert_before(target_key, new_key, **options)
75
+ target = find(target_key)
76
+ return nil unless target
77
+
78
+ new_position = target.position - 1
79
+ add(new_key, **options.merge(position: new_position))
80
+ end
81
+
82
+ # Insert item after another item
83
+ def insert_after(target_key, new_key, **options)
84
+ target = find(target_key)
85
+ return nil unless target
86
+
87
+ new_position = target.position + 1
88
+ add(new_key, **options.merge(position: new_position))
89
+ end
90
+
91
+ # Move item to a new position
92
+ def move(key, position: nil, before: nil, after: nil)
93
+ item = find(key)
94
+ return nil unless item
95
+
96
+ if before
97
+ target = find(before)
98
+ item.position = target.position - 1 if target
99
+ elsif after
100
+ target = find(after)
101
+ item.position = target.position + 1 if target
102
+ elsif position == :first
103
+ item.position = -999
104
+ elsif position == :last
105
+ item.position = 999
106
+ elsif position.is_a?(Integer)
107
+ item.position = position
108
+ end
109
+
110
+ sort_items!
111
+ item
112
+ end
113
+
114
+ # Replace an item
115
+ def replace(key, **options, &block)
116
+ remove(key)
117
+ add(key, **options, &block)
118
+ end
119
+
120
+ # Get all root items (items without a parent)
121
+ def root_items
122
+ @items.values.select { |item| item.parent_key.nil? }.sort_by { |item| [item.position, item.key.to_s] }
123
+ end
124
+
125
+ # Get all items that are visible to the user
126
+ def visible_items(user = nil, parent_key = nil)
127
+ items_to_filter = if parent_key
128
+ find(parent_key)&.children || []
129
+ else
130
+ root_items
131
+ end
132
+
133
+ items_to_filter.select { |item| item.visible?(user) }
134
+ end
135
+
136
+ # Build tree structure
137
+ def build_tree
138
+ # First, clear all children
139
+ @items.each_value { |item| item.children.clear }
140
+
141
+ # Then rebuild the tree
142
+ @items.each_value do |item|
143
+ if item.parent_key && (parent = @items[item.parent_key])
144
+ parent.add_child(item)
145
+ end
146
+ end
147
+
148
+ root_items
149
+ end
150
+
151
+ # Clear all items
152
+ def clear
153
+ @items.clear
154
+ end
155
+
156
+ # Get all registered paths (for settings_area? detection)
157
+ def registered_paths(context = nil)
158
+ @items.values.map { |item| item.resolve_url(context) }.compact
159
+ end
160
+
161
+ # Add a section
162
+ def section(key, label: nil, &block)
163
+ # Create a section header item
164
+ section_item = add(key, section_label: label || key.to_s.humanize, position: @items.size * 100)
165
+
166
+ if block_given?
167
+ builder = Builder.new(self, section_item)
168
+ builder.instance_eval(&block)
169
+ end
170
+
171
+ section_item
172
+ end
173
+
174
+ # Deep clone the registry
175
+ def deep_clone
176
+ cloned = self.class.new(context)
177
+ @items.each do |key, item|
178
+ cloned.items[key] = item.deep_clone
179
+ end
180
+ cloned.build_tree
181
+ cloned
182
+ end
183
+
184
+ private
185
+
186
+ def sort_items!
187
+ # Sort items by position, then rebuild tree
188
+ @items = @items.sort_by { |_key, item| [item.position, item.key.to_s] }.to_h
189
+ build_tree
190
+ end
191
+ end
192
+ end
193
+ end
@@ -6,7 +6,7 @@
6
6
  </head>
7
7
 
8
8
  <body class="admin min-vh-100 <%= controller_name %> <%= action_name %>"
9
- data-controller="admin"
9
+ data-controller="admin sidebar"
10
10
  data-action="keydown.ctrl+s->admin#save keydown.meta+s->admin#save"
11
11
  >
12
12
  <%= render_admin_partials(:body_start_partials) %>
@@ -2,7 +2,7 @@
2
2
  <%= dialog_header(Spree.t(:edit) + ' ' + Spree.t(:image)) %>
3
3
  <%= form_with model: @asset, url: spree.admin_asset_path(@asset), method: :put, scope: :asset do |f| %>
4
4
  <div class="dialog-body pb-0" data-turbo-permanent id="asset-<%= @asset.key.parameterize %>">
5
- <%= render 'active_storage/upload_form', form: f, field_name: :attachment, width: 200, height: 200, can_delete: false %>
5
+ <%= f.spree_file_field :attachment, width: 200, height: 200, can_delete: false, label: Spree.t(:image) %>
6
6
  </div>
7
7
  <div class="dialog-body" style="min-height: 200px">
8
8
  <%= f.spree_text_area :alt, label: Spree.t(:alt_text), rows: 4 %>
@@ -1,19 +1,7 @@
1
1
  <div class="card mb-3">
2
2
  <div class="card-body">
3
- <div class="form-group">
4
- <%= f.label :url, Spree.t(:domain) %>
5
- <div class="form-control d-flex align-items-center py-0 focus-shadow focus-border">
6
- <span class="text-muted">https://</span>
7
- <%= f.text_field :url, class: 'form-control-plaintext pr-0 pl-1', autofocus: f.object.new_record?, required: true %>
8
- </div>
9
- <%= f.error_message_on :url %>
10
- </div>
3
+ <%= f.spree_text_field :url, label: Spree.t(:domain), prepend: 'https://', autofocus: f.object.new_record?, required: true %>
11
4
 
12
- <div class="custom-control custom-checkbox">
13
- <%= f.check_box :default, class: 'custom-control-input' %>
14
- <%= f.label :default, class: 'custom-control-label font-weight-semibold' %>
15
- <p class="form-text mb-0">We will use this domain in emails to customers.</p>
16
- <%= f.error_message_on :default %>
17
- </div>
5
+ <%= f.spree_check_box :default, help: 'We will use this domain in emails to customers.' %>
18
6
  </div>
19
7
  </div>
@@ -5,18 +5,23 @@
5
5
  url: filter_form_url,
6
6
  class: "filter-wrap",
7
7
  data: {
8
- controller: "filters dropdown",
9
- filters_url_value: request.url,
10
- dropdown_placement_value: "bottom-end"
8
+ controller: "filters dialog",
9
+ filters_url_value: request.url
11
10
  } do |f| %>
12
- <%= hidden_field_tag :frame_name, frame_name if frame_name.present? %>
11
+ <%= hidden_field_tag :frame_name, frame_name if frame_name.present? %>
13
12
 
14
- <div class="d-flex flex-column flex-lg-row gap-2">
15
- <%= render 'spree/admin/shared/filters_search_bar', param: :code_i_cont, label: Spree.t(:code) %>
16
- <%= render "table_filter_dropdown" %>
17
- <%= render "spree/admin/shared/filters_button" %>
18
- </div>
19
- <div data-dropdown-target="menu" id="table-filter" class="hidden">
13
+ <div class="d-flex flex-column flex-lg-row gap-2">
14
+ <%= render 'spree/admin/shared/filters_search_bar', param: :code_i_cont, label: Spree.t(:code) %>
15
+ <%= render "table_filter_dropdown" %>
16
+ <%= button_tag type: 'button', class: 'btn btn-light d-flex align-items-center', data: { action: 'dialog#open' } do %>
17
+ <%= icon "adjustments", class: "mr-1" %>
18
+ <%= Spree.t("admin.filters") %>
19
+ <% end %>
20
+ </div>
21
+
22
+ <dialog class="drawer" data-dialog-target="dialog" id="gift-card-filters-drawer">
23
+ <%= drawer_header(Spree.t(:filter), 'dialog') %>
24
+ <div class="drawer-body">
20
25
  <% if params[:user_id].blank? %>
21
26
  <div class="form-group">
22
27
  <%= f.label :users_email_eq, Spree.t(:email) %>
@@ -30,16 +35,19 @@
30
35
  </div>
31
36
 
32
37
  <%= render_admin_partials(:gift_cards_filters_partials, f: f) %>
33
-
34
- <div class="form-actions">
35
- <%= turbo_save_button_tag Spree.t(:filter_results) do %>
36
- <%= icon("search") %>
37
- <%= Spree.t(:filter_results) %>
38
- <% end %>
39
38
  </div>
40
- </div>
39
+ <div class="drawer-footer">
40
+ <%= drawer_discard_button('dialog') %>
41
+ <div class="form-actions">
42
+ <%= turbo_save_button_tag Spree.t(:filter_results) do %>
43
+ <%= icon("search") %>
44
+ <%= Spree.t(:filter_results) %>
45
+ <% end %>
46
+ </div>
47
+ </div>
48
+ </dialog>
41
49
 
42
- <%= render "spree/admin/shared/filter_badge_template" %>
50
+ <%= render "spree/admin/shared/filter_badge_template" %>
43
51
 
44
52
  <div data-filters-target="badgesContainer" class="filter-badges-container"></div>
45
53
  <% end %>
@@ -12,7 +12,7 @@
12
12
  <%= icon 'plus' %>
13
13
  <%= Spree.t(:new_gift_card) %>
14
14
  <% end %>
15
- <%= dropdown_menu class: 'dropdown-menu-right' do %>
15
+ <%= dropdown_menu do %>
16
16
  <%= link_to spree.new_admin_gift_card_path,
17
17
  class: 'text-left dropdown-item' do %>
18
18
  <%= icon 'add' %>
@@ -4,11 +4,11 @@
4
4
  <%= Spree.t(:customer) %>
5
5
  </h5>
6
6
  <% if can?(:update_customer, @order) || can?(:update_addresses, @order) %>
7
- <%= dropdown id: 'customer-edit-dropdown', class: 'ml-auto' do %>
7
+ <%= dropdown id: 'customer-edit-dropdown', class: 'ml-auto', portal: false do %>
8
8
  <%= dropdown_toggle class: 'btn-light btn-sm px-1', data: { test_id: 'dropdown-toggle' } do %>
9
9
  <%= icon 'dots-vertical', class: 'mr-0' %>
10
10
  <% end %>
11
- <%= dropdown_menu class: 'dropdown-menu-right' do %>
11
+ <%= dropdown_menu do %>
12
12
  <% if can?(:update_customer, @order) && !@order.user.present? %>
13
13
  <%= link_to Spree.t('admin.edit_contact_information'), spree.edit_admin_order_contact_information_path(@order), class: 'dropdown-item', data: { turbo_frame: 'dialog', action: 'dialog#open' } %>
14
14
  <% end %>
@@ -14,33 +14,38 @@
14
14
  url: search_form_path,
15
15
  class: "filter-wrap",
16
16
  data: {
17
- controller: "filters dropdown",
18
- filters_url_value: request.url,
19
- dropdown_placement_value: "bottom-end"
17
+ controller: "filters dialog",
18
+ filters_url_value: request.url
20
19
  } do |f| %>
21
- <%= hidden_field_tag :frame_name, frame_name if frame_name.present? %>
20
+ <%= hidden_field_tag :frame_name, frame_name if frame_name.present? %>
22
21
 
23
- <div class="d-flex flex-column flex-lg-row gap-2">
24
- <%= render 'spree/admin/shared/filters_search_bar', param: :multi_search %>
22
+ <div class="d-flex flex-column flex-lg-row gap-2">
23
+ <%= render 'spree/admin/shared/filters_search_bar', param: :multi_search %>
25
24
 
26
- <% if controller_name == 'checkouts' %>
27
- <%= render "spree/admin/shared/calendar_range_picker",
28
- date_from_input_name: "q[created_at_gt]",
29
- date_to_input_name: "q[created_at_lt]",
30
- date_from_value: params.dig(:q, :created_at_gt),
31
- date_to_value: params.dig(:q, :created_at_lt) %>
32
- <% else %>
33
- <%= render "spree/admin/shared/calendar_range_picker",
34
- date_from_input_name: "q[completed_at_gt]",
35
- date_to_input_name: "q[completed_at_lt]",
36
- date_from_value: params.dig(:q, :completed_at_gt),
37
- date_to_value: params.dig(:q, :completed_at_lt) %>
38
- <% end %>
25
+ <% if controller_name == 'checkouts' %>
26
+ <%= render "spree/admin/shared/calendar_range_picker",
27
+ date_from_input_name: "q[created_at_gt]",
28
+ date_to_input_name: "q[created_at_lt]",
29
+ date_from_value: params.dig(:q, :created_at_gt),
30
+ date_to_value: params.dig(:q, :created_at_lt) %>
31
+ <% else %>
32
+ <%= render "spree/admin/shared/calendar_range_picker",
33
+ date_from_input_name: "q[completed_at_gt]",
34
+ date_to_input_name: "q[completed_at_lt]",
35
+ date_from_value: params.dig(:q, :completed_at_gt),
36
+ date_to_value: params.dig(:q, :completed_at_lt) %>
37
+ <% end %>
38
+
39
+ <%= render "spree/admin/orders/table_filter_dropdown" %>
40
+ <%= button_tag type: 'button', class: 'btn btn-light d-flex align-items-center', data: { action: 'dialog#open' } do %>
41
+ <%= icon "adjustments", class: "mr-1" %>
42
+ <%= Spree.t("admin.filters") %>
43
+ <% end %>
44
+ </div>
39
45
 
40
- <%= render "spree/admin/orders/table_filter_dropdown" %>
41
- <%= render 'spree/admin/shared/filters_button' %>
42
- </div>
43
- <div data-dropdown-target="menu" id="table-filter" class="hidden">
46
+ <dialog class="drawer" data-dialog-target="dialog" id="order-filters-drawer">
47
+ <%= drawer_header(Spree.t(:filter), 'dialog') %>
48
+ <div class="drawer-body">
44
49
  <%= f.hidden_field :payment_state_not_eq, value: params.dig(:q, :payment_state_not_eq) %>
45
50
  <%= f.hidden_field :state_not_eq, value: params.dig(:q, :state_not_eq) %>
46
51
  <%= f.hidden_field :user_id_eq, value: @user.try(:id) %>
@@ -99,8 +104,12 @@
99
104
  </div>
100
105
 
101
106
  <%= render_admin_partials(:orders_filters_partials, f: f) %>
102
- <%= render 'spree/admin/shared/filter_submit' %>
103
- </div>
107
+ </div>
108
+ <div class="drawer-footer">
109
+ <%= drawer_discard_button('dialog') %>
110
+ <%= render 'spree/admin/shared/filter_submit' %>
111
+ </div>
112
+ </dialog>
104
113
 
105
114
  <%= render "spree/admin/shared/filter_badge_template" %>
106
115
  <div data-filters-target="badgesContainer" class="filter-badges-container"></div>
@@ -6,7 +6,7 @@
6
6
  </span>
7
7
  <%= order_filter_dropdown_value %>
8
8
  <% end %>
9
- <%= dropdown_menu class: 'dropdown-menu-right w-100', style: 'min-width: 200px' do %>
9
+ <%= dropdown_menu do %>
10
10
  <%= active_link_to Spree.t('admin.orders.all_orders'), params.to_unsafe_h.deep_merge({q: {shipment_state_eq: '', shipment_state_not_in: '', state_eq: '', state_in: '', refunded: '', partially_refunded: '' }}), class: 'dropdown-item', active: (params[:q] || {}).values_at(*%w[payment_state_not_eq shipment_state_not_in shipment_state_eq state_eq state_in refunded partially_refunded] ).all?(&:blank?) %>
11
11
  <%= link_to params.to_unsafe_h.deep_merge({q: {shipment_state_not_in: [:shipped, :canceled], shipment_state_eq: '', state_eq: '', state_in: '', refunded: '', partially_refunded: ''}}),
12
12
  class: "dropdown-item d-flex align-items-center justify-content-between #{'active' if params[:q][:shipment_state_not_in] == ['shipped', 'canceled']}" do %>
@@ -1,8 +1,5 @@
1
- <%= render 'active_storage/upload_form', form: f, field_name: :asset, width: 512, height: 512, auto_submit: true %>
2
- <div class="mt-2 form-group">
3
- <%= f.label :preferred_image_alt, Spree.t(:alt_text) %>
4
- <%= f.text_field :preferred_image_alt, data: { action: 'auto-submit#submit' }, class: 'form-control' %>
5
- </div>
1
+ <%= f.spree_file_field :asset, width: 512, height: 512, auto_submit: true %>
2
+ <%= f.spree_text_field :preferred_image_alt, label: Spree.t(:alt_text), data: { action: 'auto-submit#submit' } %>
6
3
  <% content_for(:design_tab) do %>
7
4
  <%= render 'spree/admin/page_builder/labeled_range_input',
8
5
  f: f, field: :preferred_height, min: 1, max: 1024, unit: 'px', _label: 'Max image height on desktop'