solidus_admin 0.2.0 → 0.3.1

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 (189) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +60 -6
  3. data/Rakefile +1 -0
  4. data/app/assets/builds/solidus_admin/tailwind.css +198 -163
  5. data/app/assets/stylesheets/solidus_admin/dark.css +1 -0
  6. data/app/assets/stylesheets/solidus_admin/dimmed.css +1 -0
  7. data/app/components/solidus_admin/adjustment_reasons/edit/component.html.erb +27 -0
  8. data/app/components/solidus_admin/adjustment_reasons/edit/component.rb +12 -0
  9. data/app/components/solidus_admin/adjustment_reasons/edit/component.yml +8 -0
  10. data/app/components/solidus_admin/adjustment_reasons/index/component.rb +19 -2
  11. data/app/components/solidus_admin/adjustment_reasons/new/component.html.erb +28 -0
  12. data/app/components/solidus_admin/adjustment_reasons/new/component.rb +12 -0
  13. data/app/components/solidus_admin/adjustment_reasons/new/component.yml +8 -0
  14. data/app/components/solidus_admin/base_component.rb +0 -25
  15. data/app/components/solidus_admin/option_types/index/component.rb +1 -1
  16. data/app/components/solidus_admin/orders/index/component.rb +8 -8
  17. data/app/components/solidus_admin/orders/index/component.yml +1 -0
  18. data/app/components/solidus_admin/orders/show/address/component.html.erb +56 -54
  19. data/app/components/solidus_admin/orders/show/adjustments/index/adjustable/component.rb +27 -0
  20. data/app/components/solidus_admin/orders/show/adjustments/index/adjustable/spree_line_item/component.rb +14 -0
  21. data/app/components/solidus_admin/orders/show/adjustments/index/adjustable/spree_order/component.rb +11 -0
  22. data/app/components/solidus_admin/orders/show/adjustments/index/adjustable/spree_shipment/component.rb +15 -0
  23. data/app/components/solidus_admin/orders/show/adjustments/index/component.rb +147 -0
  24. data/app/components/solidus_admin/orders/show/adjustments/index/component.yml +21 -0
  25. data/app/components/solidus_admin/orders/show/adjustments/index/source/component.rb +26 -0
  26. data/app/components/solidus_admin/orders/show/adjustments/index/source/spree_tax_rate/component.rb +17 -0
  27. data/app/components/solidus_admin/orders/show/adjustments/index/source/spree_unit_cancel/component.rb +7 -0
  28. data/app/components/solidus_admin/orders/show/component.html.erb +7 -3
  29. data/app/components/solidus_admin/orders/show/component.rb +8 -0
  30. data/app/components/solidus_admin/orders/show/email/component.html.erb +18 -13
  31. data/app/components/solidus_admin/orders/show/email/component.rb +0 -4
  32. data/app/components/solidus_admin/orders/show/summary/component.html.erb +1 -1
  33. data/app/components/solidus_admin/payment_methods/index/component.rb +1 -1
  34. data/app/components/solidus_admin/products/index/component.rb +8 -5
  35. data/app/components/solidus_admin/products/show/component.html.erb +14 -3
  36. data/app/components/solidus_admin/products/status/component.rb +4 -1
  37. data/app/components/solidus_admin/products/status/component.yml +1 -0
  38. data/app/components/solidus_admin/properties/index/component.rb +1 -1
  39. data/app/components/solidus_admin/refund_reasons/edit/component.html.erb +27 -0
  40. data/app/components/solidus_admin/refund_reasons/edit/component.rb +12 -0
  41. data/app/components/solidus_admin/refund_reasons/edit/component.yml +8 -0
  42. data/app/components/solidus_admin/refund_reasons/index/component.rb +11 -4
  43. data/app/components/solidus_admin/refund_reasons/new/component.html.erb +27 -0
  44. data/app/components/solidus_admin/refund_reasons/new/component.rb +12 -0
  45. data/app/components/solidus_admin/refund_reasons/new/component.yml +6 -0
  46. data/app/components/solidus_admin/return_reasons/edit/component.html.erb +26 -0
  47. data/app/components/solidus_admin/return_reasons/edit/component.rb +12 -0
  48. data/app/components/solidus_admin/return_reasons/edit/component.yml +8 -0
  49. data/app/components/solidus_admin/return_reasons/index/component.rb +20 -2
  50. data/app/components/solidus_admin/return_reasons/new/component.html.erb +27 -0
  51. data/app/components/solidus_admin/return_reasons/new/component.rb +12 -0
  52. data/app/components/solidus_admin/return_reasons/new/component.yml +8 -0
  53. data/app/components/solidus_admin/roles/edit/component.html.erb +33 -0
  54. data/app/components/solidus_admin/roles/edit/component.rb +20 -0
  55. data/app/components/solidus_admin/roles/edit/component.yml +19 -0
  56. data/app/components/solidus_admin/roles/index/component.rb +70 -0
  57. data/app/components/solidus_admin/roles/index/component.yml +6 -0
  58. data/app/components/solidus_admin/roles/new/component.html.erb +33 -0
  59. data/app/components/solidus_admin/roles/new/component.rb +20 -0
  60. data/app/components/solidus_admin/roles/new/component.yml +19 -0
  61. data/app/components/solidus_admin/shipping_categories/edit/component.html.erb +16 -0
  62. data/app/components/solidus_admin/shipping_categories/edit/component.rb +12 -0
  63. data/app/components/solidus_admin/shipping_categories/edit/component.yml +6 -0
  64. data/app/components/solidus_admin/shipping_categories/index/component.rb +20 -3
  65. data/app/components/solidus_admin/shipping_categories/new/component.html.erb +17 -0
  66. data/app/components/solidus_admin/shipping_categories/new/component.rb +12 -0
  67. data/app/components/solidus_admin/shipping_categories/new/component.yml +6 -0
  68. data/app/components/solidus_admin/shipping_methods/index/component.rb +2 -2
  69. data/app/components/solidus_admin/stock_items/edit/component.html.erb +66 -72
  70. data/app/components/solidus_admin/stock_items/edit/component.rb +0 -5
  71. data/app/components/solidus_admin/stock_items/index/component.rb +5 -6
  72. data/app/components/solidus_admin/stock_locations/index/component.rb +1 -1
  73. data/app/components/solidus_admin/store_credit_reasons/edit/component.html.erb +26 -0
  74. data/app/components/solidus_admin/store_credit_reasons/edit/component.rb +12 -0
  75. data/app/components/solidus_admin/store_credit_reasons/edit/component.yml +8 -0
  76. data/app/components/solidus_admin/store_credit_reasons/index/component.rb +11 -4
  77. data/app/components/solidus_admin/store_credit_reasons/new/component.html.erb +27 -0
  78. data/app/components/solidus_admin/store_credit_reasons/new/component.rb +12 -0
  79. data/app/components/solidus_admin/store_credit_reasons/new/component.yml +8 -0
  80. data/app/components/solidus_admin/stores/index/component.rb +1 -1
  81. data/app/components/solidus_admin/tax_categories/edit/component.html.erb +28 -0
  82. data/app/components/solidus_admin/tax_categories/edit/component.rb +12 -0
  83. data/app/components/solidus_admin/tax_categories/edit/component.yml +8 -0
  84. data/app/components/solidus_admin/tax_categories/index/component.rb +11 -4
  85. data/app/components/solidus_admin/tax_categories/new/component.html.erb +28 -0
  86. data/app/components/solidus_admin/tax_categories/new/component.rb +12 -0
  87. data/app/components/solidus_admin/tax_categories/new/component.yml +8 -0
  88. data/app/components/solidus_admin/tax_rates/index/component.rb +3 -3
  89. data/app/components/solidus_admin/taxonomies/index/component.rb +1 -1
  90. data/app/components/solidus_admin/ui/button/component.rb +1 -1
  91. data/app/components/solidus_admin/ui/checkbox_row/component.html.erb +29 -0
  92. data/app/components/solidus_admin/ui/checkbox_row/component.rb +11 -0
  93. data/app/components/solidus_admin/ui/forms/address/component.html.erb +27 -9
  94. data/app/components/solidus_admin/ui/forms/address/component.js +38 -13
  95. data/app/components/solidus_admin/ui/forms/search/component.html.erb +2 -3
  96. data/app/components/solidus_admin/ui/forms/search/component.js +3 -3
  97. data/app/components/solidus_admin/ui/modal/component.html.erb +7 -7
  98. data/app/components/solidus_admin/ui/modal/component.js +7 -0
  99. data/app/components/solidus_admin/ui/modal/component.rb +1 -1
  100. data/app/components/solidus_admin/ui/pages/index/component.html.erb +17 -3
  101. data/app/components/solidus_admin/ui/pages/index/component.rb +29 -43
  102. data/app/components/solidus_admin/ui/panel/component.html.erb +5 -16
  103. data/app/components/solidus_admin/ui/panel/component.rb +11 -7
  104. data/app/components/solidus_admin/ui/table/component.js +31 -2
  105. data/app/components/solidus_admin/ui/table/component.rb +23 -7
  106. data/app/components/solidus_admin/ui/table/component.yml +1 -0
  107. data/app/components/solidus_admin/ui/thumbnail/component.rb +1 -1
  108. data/app/components/solidus_admin/ui/thumbnail_with_caption/component.html.erb +17 -0
  109. data/app/components/solidus_admin/ui/thumbnail_with_caption/component.rb +15 -0
  110. data/app/components/solidus_admin/users/addresses/component.html.erb +46 -0
  111. data/app/components/solidus_admin/users/addresses/component.rb +61 -0
  112. data/app/components/solidus_admin/users/addresses/component.yml +14 -0
  113. data/app/components/solidus_admin/users/edit/api_access/component.html.erb +49 -0
  114. data/app/components/solidus_admin/users/edit/api_access/component.js +9 -0
  115. data/app/components/solidus_admin/users/edit/api_access/component.rb +7 -0
  116. data/app/components/solidus_admin/users/edit/api_access/component.yml +10 -0
  117. data/app/components/solidus_admin/users/edit/component.html.erb +52 -0
  118. data/app/components/solidus_admin/users/edit/component.rb +51 -0
  119. data/app/components/solidus_admin/users/edit/component.yml +12 -0
  120. data/app/components/solidus_admin/users/index/component.rb +10 -8
  121. data/app/components/solidus_admin/users/items/component.html.erb +41 -0
  122. data/app/components/solidus_admin/users/items/component.rb +170 -0
  123. data/app/components/solidus_admin/users/items/component.yml +16 -0
  124. data/app/components/solidus_admin/users/orders/component.html.erb +42 -0
  125. data/app/components/solidus_admin/users/orders/component.rb +131 -0
  126. data/app/components/solidus_admin/users/orders/component.yml +12 -0
  127. data/app/components/solidus_admin/users/stats/component.html.erb +11 -0
  128. data/app/components/solidus_admin/users/stats/component.rb +9 -0
  129. data/app/components/solidus_admin/users/stats/component.yml +2 -0
  130. data/app/components/solidus_admin/users_and_roles/component.rb +24 -0
  131. data/app/components/solidus_admin/users_and_roles/component.yml +2 -0
  132. data/app/components/solidus_admin/zones/index/component.rb +1 -1
  133. data/app/controllers/solidus_admin/addresses_controller.rb +6 -1
  134. data/app/controllers/solidus_admin/adjustment_reasons_controller.rb +85 -10
  135. data/app/controllers/solidus_admin/adjustments_controller.rb +57 -0
  136. data/app/controllers/solidus_admin/customers_controller.rb +5 -1
  137. data/app/controllers/solidus_admin/orders_controller.rb +5 -1
  138. data/app/controllers/solidus_admin/products_controller.rb +11 -0
  139. data/app/controllers/solidus_admin/refund_reasons_controller.rb +85 -10
  140. data/app/controllers/solidus_admin/reimbursement_types_controller.rb +0 -5
  141. data/app/controllers/solidus_admin/return_reasons_controller.rb +85 -10
  142. data/app/controllers/solidus_admin/roles_controller.rb +118 -0
  143. data/app/controllers/solidus_admin/shipping_categories_controller.rb +87 -10
  144. data/app/controllers/solidus_admin/shipping_methods_controller.rb +0 -5
  145. data/app/controllers/solidus_admin/stock_items_controller.rb +6 -6
  146. data/app/controllers/solidus_admin/stock_locations_controller.rb +0 -5
  147. data/app/controllers/solidus_admin/store_credit_reasons_controller.rb +85 -10
  148. data/app/controllers/solidus_admin/stores_controller.rb +0 -5
  149. data/app/controllers/solidus_admin/tax_categories_controller.rb +89 -10
  150. data/app/controllers/solidus_admin/tax_rates_controller.rb +0 -5
  151. data/app/controllers/solidus_admin/users_controller.rb +85 -6
  152. data/app/controllers/solidus_admin/zones_controller.rb +0 -5
  153. data/app/helpers/solidus_admin/last_login_helper.rb +16 -0
  154. data/app/helpers/solidus_admin/permission_sets_helper.rb +32 -0
  155. data/app/views/layouts/solidus_admin/application.html.erb +2 -1
  156. data/config/locales/adjustment_reasons.en.yml +5 -1
  157. data/config/locales/adjustments.en.yml +10 -0
  158. data/config/locales/refund_reasons.en.yml +10 -0
  159. data/config/locales/return_reasons.en.yml +5 -1
  160. data/config/locales/roles.en.yml +10 -0
  161. data/config/locales/shipping_categories.en.yml +4 -0
  162. data/config/locales/store_credit_reasons.en.yml +5 -1
  163. data/config/locales/tax_categories.en.yml +4 -0
  164. data/config/locales/users.en.yml +10 -0
  165. data/config/routes.rb +24 -7
  166. data/docs/components.md +109 -0
  167. data/docs/{customizing_view_components.md → customizing_components.md} +2 -7
  168. data/docs/index_pages.md +146 -0
  169. data/docs/{customizing_menu_items.md → menu_items.md} +1 -1
  170. data/docs/stimulusjs.md +85 -0
  171. data/docs/{customizing_tailwindcss.md → tailwindcss.md} +27 -8
  172. data/lib/solidus_admin/component_registry.rb +40 -0
  173. data/lib/solidus_admin/configuration.rb +2 -26
  174. data/lib/solidus_admin/install_tailwindcss.rb +4 -2
  175. data/lib/solidus_admin/testing_support/admin_assets.rb +10 -0
  176. data/lib/solidus_admin/testing_support/component_helpers.rb +27 -0
  177. data/lib/solidus_admin/testing_support/dummy_app/rake_tasks.rb +60 -0
  178. data/lib/solidus_admin/testing_support/feature_helpers.rb +34 -0
  179. data/lib/solidus_admin/version.rb +1 -1
  180. data/solidus_admin.gemspec +7 -3
  181. metadata +108 -19
  182. data/app/components/solidus_admin/promotion_categories/index/component.rb +0 -56
  183. data/app/components/solidus_admin/promotions/index/component.rb +0 -104
  184. data/app/components/solidus_admin/promotions/index/component.yml +0 -10
  185. data/app/controllers/solidus_admin/promotion_categories_controller.rb +0 -29
  186. data/app/controllers/solidus_admin/promotions_controller.rb +0 -46
  187. data/config/locales/promotion_categories.en.yml +0 -6
  188. data/config/locales/promotions.en.yml +0 -6
  189. data/config/locales/refund_reasons_.en.yml +0 -6
@@ -0,0 +1,28 @@
1
+ <%= turbo_frame_tag :new_tax_category_modal do %>
2
+ <%= render component("ui/modal").new(title: t(".title")) do |modal| %>
3
+ <%= form_for @tax_category, url: solidus_admin.tax_categories_path(page: params[:page], q: params[:q]), html: { id: form_id } do |f| %>
4
+ <div class="flex flex-col gap-6 pb-4">
5
+ <%= render component("ui/forms/field").text_field(f, :name) %>
6
+ <%= render component("ui/forms/field").text_field(f, :tax_code) %>
7
+ <%= render component("ui/forms/field").text_field(f, :description) %>
8
+ <label class="flex gap-2 items-center">
9
+ <%= render component("ui/forms/checkbox").new(
10
+ name: "#{f.object_name}[is_default]",
11
+ value: "1",
12
+ checked: f.object.is_default
13
+ ) %>
14
+ <span class="font-semibold text-xs ml-2"><%= Spree::TaxCategory.human_attribute_name :is_default %></span>
15
+ <%= render component("ui/toggletip").new(text: t(".hints.is_default")) %>
16
+ </label>
17
+ </div>
18
+ <% modal.with_actions do %>
19
+ <form method="dialog">
20
+ <%= render component("ui/button").new(scheme: :secondary, text: t('.cancel')) %>
21
+ </form>
22
+ <%= render component("ui/button").new(form: form_id, type: :submit, text: t('.submit')) %>
23
+ <% end %>
24
+ <% end %>
25
+ <% end %>
26
+ <% end %>
27
+
28
+ <%= render component("tax_categories/index").new(page: @page) %>
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SolidusAdmin::TaxCategories::New::Component < SolidusAdmin::TaxCategories::Index::Component
4
+ def initialize(page:, tax_category:)
5
+ @page = page
6
+ @tax_category = tax_category
7
+ end
8
+
9
+ def form_id
10
+ dom_id(@tax_category, "#{stimulus_id}_new_tax_category_form")
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ # Add your component translations here.
2
+ # Use the translation in the example in your template with `t(".hello")`.
3
+ en:
4
+ title: "New Tax Category"
5
+ cancel: "Cancel"
6
+ submit: "Add Tax Category"
7
+ hints:
8
+ is_default: "When checked, this tax category will be selected by default when creating new products or variants."
@@ -30,7 +30,7 @@ class SolidusAdmin::TaxRates::Index::Component < SolidusAdmin::Taxes::Component
30
30
  def batch_actions
31
31
  [
32
32
  {
33
- display_name: t('.batch_actions.delete'),
33
+ label: t('.batch_actions.delete'),
34
34
  action: solidus_admin.tax_rates_path,
35
35
  method: :delete,
36
36
  icon: 'delete-bin-7-line',
@@ -41,13 +41,13 @@ class SolidusAdmin::TaxRates::Index::Component < SolidusAdmin::Taxes::Component
41
41
  def filters
42
42
  [
43
43
  {
44
- presentation: Spree::Zone.model_name.human,
44
+ label: Spree::Zone.model_name.human,
45
45
  attribute: :zone_id,
46
46
  predicate: :eq,
47
47
  options: Spree::Zone.pluck(:name, :id),
48
48
  },
49
49
  {
50
- presentation: Spree::TaxCategory.model_name.human,
50
+ label: Spree::TaxCategory.model_name.human,
51
51
  attribute: :tax_categories_id,
52
52
  predicate: :in,
53
53
  options: Spree::TaxCategory.pluck(:name, :id),
@@ -28,7 +28,7 @@ class SolidusAdmin::Taxonomies::Index::Component < SolidusAdmin::UI::Pages::Inde
28
28
  def batch_actions
29
29
  [
30
30
  {
31
- display_name: t('.batch_actions.delete'),
31
+ label: t('.batch_actions.delete'),
32
32
  action: solidus_admin.taxonomies_path,
33
33
  method: :delete,
34
34
  icon: 'delete-bin-7-line',
@@ -78,7 +78,7 @@ class SolidusAdmin::UI::Button::Component < SolidusAdmin::BaseComponent
78
78
 
79
79
  @attributes[:class] = [
80
80
  'justify-start items-center justify-center gap-1 inline-flex rounded',
81
- 'focus:ring focus:ring-gray-300 focus:ring-0.5 focus:bg-white focus:ring-offset-0 [&:focus-visible]:outline-none',
81
+ 'focus:ring focus:ring-gray-300 focus:ring-0.5 focus:ring-offset-0 [&:focus-visible]:outline-none',
82
82
  SIZES.fetch(size.to_sym),
83
83
  (TEXT_PADDINGS.fetch(size.to_sym) if @text),
84
84
  SCHEMES.fetch(scheme.to_sym),
@@ -0,0 +1,29 @@
1
+ <% if @layout == :default && @options.present? %>
2
+ <span class="p-2 flex hover:bg-gray-50 rounded-md">
3
+ <span class="w-1/2"><%= @row_title %></span>
4
+ <span class="w-1/2 text-right">
5
+ <% @options.each do |option| %>
6
+ <label class="px-2 cursor-pointer">
7
+ <%= @form.check_box(@method.to_s, { checked: @form.object.try(@method.to_sym)&.include?(option[:id]), multiple: true }, option[:id], nil)%>
8
+ <span class="capitalize"><%= option[:label] %></span>
9
+ </label>
10
+ <% end %>
11
+ </span>
12
+ </span>
13
+ <% end %>
14
+
15
+ <% if @layout == :subsection && @options.present? %>
16
+ <div class="hover:bg-gray-50 rounded-md">
17
+ <div class="font-semibold px-2 py-2 mt-2 mb-1"><%= @row_title %></div>
18
+ <div class="px-4">
19
+ <% @options.each do |option| %>
20
+ <div class="py-1">
21
+ <label class="cursor-pointer">
22
+ <%= @form.check_box(@method.to_s, { checked: @form.object.try(@method.to_sym)&.include?(option[:id]), multiple: true }, option[:id], nil)%>
23
+ <span class="capitalize"><%= option[:label] %></span>
24
+ </label>
25
+ </div>
26
+ <% end %>
27
+ </div>
28
+ </div>
29
+ <% end %>
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SolidusAdmin::UI::CheckboxRow::Component < SolidusAdmin::BaseComponent
4
+ def initialize(options:, row_title:, form:, method:, layout: :default)
5
+ @options = options
6
+ @row_title = row_title
7
+ @form = form
8
+ @method = method
9
+ @layout = layout
10
+ end
11
+ end
@@ -21,15 +21,33 @@
21
21
  "data-action": "change->#{stimulus_id}#loadStates"
22
22
  ) %>
23
23
 
24
- <%= render component("ui/forms/field").select(
25
- @name,
26
- :state_id,
27
- state_options,
28
- object: @address,
29
- value: @address.try(:state_id),
30
- disabled: @address.country&.states&.empty?,
31
- "data-#{stimulus_id}-target": "state"
32
- ) %>
24
+ <%= content_tag(:div,
25
+ data: { "#{stimulus_id}-target": "stateNameWrapper" },
26
+ class: (@address.country&.states&.empty? ? "flex flex-col gap-2 w-full" : "hidden flex flex-col gap-2 w-full")
27
+ ) do %>
28
+ <%= render component("ui/forms/field").text_field(
29
+ @name,
30
+ :state_name,
31
+ object: @address,
32
+ value: @address.try(:state_name),
33
+ "data-#{stimulus_id}-target": "stateName"
34
+ ) %>
35
+ <% end %>
36
+ <input autocomplete="off" type="hidden" name=<%= "#{@name}[state_id]" %>>
37
+
38
+ <%= content_tag(:div,
39
+ data: { "#{stimulus_id}-target": "stateWrapper" },
40
+ class: (@address.country&.states&.empty? ? "hidden flex flex-col gap-2 w-full" : "flex flex-col gap-2 w-full")
41
+ ) do %>
42
+ <%= render component("ui/forms/field").select(
43
+ @name,
44
+ :state_id,
45
+ state_options,
46
+ object: @address,
47
+ value: @address.try(:state_id),
48
+ "data-#{stimulus_id}-target": "state"
49
+ ) %>
50
+ <% end %>
33
51
 
34
52
  <%= render component("ui/forms/field").text_field(@name, :phone, object: @address) %>
35
53
  </div>
@@ -1,7 +1,7 @@
1
1
  import { Controller } from '@hotwired/stimulus'
2
2
 
3
3
  export default class extends Controller {
4
- static targets = ["country", "state"]
4
+ static targets = ["country", "state", "stateName", "stateWrapper", "stateNameWrapper"]
5
5
 
6
6
  loadStates() {
7
7
  const countryId = this.countryTarget.value
@@ -13,22 +13,47 @@ export default class extends Controller {
13
13
  })
14
14
  }
15
15
 
16
- updateStateOptions(data) {
16
+ updateStateOptions(states) {
17
+ if (states.length === 0) {
18
+ // Show state name text input if no states to choose from.
19
+ this.toggleStateFields(false)
20
+ } else {
21
+ // Show state select dropdown.
22
+ this.toggleStateFields(true)
23
+ this.populateStateSelect(states)
24
+ }
25
+ }
26
+
27
+ toggleStateFields(showSelect) {
28
+ const stateWrapper = this.stateWrapperTarget
29
+ const stateNameWrapper = this.stateNameWrapperTarget
17
30
  const stateSelect = this.stateTarget
31
+ const stateName = this.stateNameTarget
18
32
 
19
- stateSelect.innerHTML = ""
20
33
 
21
- if (data.length === 0) {
22
- stateSelect.disabled = true
23
- } else {
34
+ if (showSelect) {
35
+ // Show state select dropdown.
24
36
  stateSelect.disabled = false
25
-
26
- data.forEach((state) => {
27
- const option = document.createElement("option")
28
- option.value = state.id
29
- option.innerText = state.name
30
- stateSelect.appendChild(option)
31
- })
37
+ stateName.value = ""
38
+ stateWrapper.classList.remove('hidden')
39
+ stateNameWrapper.classList.add('hidden')
40
+ } else {
41
+ // Show state name text input if no states to choose from.
42
+ stateSelect.disabled = true
43
+ stateWrapper.classList.add("hidden")
44
+ stateNameWrapper.classList.remove("hidden")
32
45
  }
33
46
  }
47
+
48
+ populateStateSelect(states) {
49
+ const stateSelect = this.stateTarget
50
+ stateSelect.innerHTML = ""
51
+
52
+ states.forEach((state) => {
53
+ const option = document.createElement("option")
54
+ option.value = state.id
55
+ option.innerText = state.name
56
+ stateSelect.appendChild(option)
57
+ })
58
+ }
34
59
  }
@@ -23,8 +23,7 @@
23
23
  ) %>
24
24
  </div>
25
25
 
26
- <details class="px-6 relative overflow-visible">
27
- <summary class="hidden"></summary>
26
+ <div class="px-6 relative overflow-visible">
28
27
  <div
29
28
  class="
30
29
  absolute
@@ -47,6 +46,6 @@
47
46
  data-<%= stimulus_id %>-target="results"
48
47
  >
49
48
  </div>
50
- </details>
49
+ </div>
51
50
  </div>
52
51
  </div>
@@ -95,7 +95,7 @@ export default class extends Controller {
95
95
  }
96
96
 
97
97
  if (this.openResults && resultsHtml && this.query) {
98
- if (!this.resultsTarget.parentNode.open) this.selectedIndex = 0
98
+ if (this.resultsTarget.parentNode.classList.contains("hidden")) this.selectedIndex = 0
99
99
 
100
100
  for (const result of this.resultTargets) {
101
101
  if (result === this.selectedResult) {
@@ -108,9 +108,9 @@ export default class extends Controller {
108
108
  result.removeAttribute("aria-selected")
109
109
  }
110
110
  }
111
- this.resultsTarget.parentNode.open = true
111
+ this.resultsTarget.parentNode.classList.remove("hidden")
112
112
  } else {
113
- this.resultsTarget.parentNode.open = false
113
+ this.resultsTarget.parentNode.classList.add("hidden")
114
114
  }
115
115
  }
116
116
  }
@@ -14,13 +14,13 @@
14
14
  <h3 class="text-xl font-semibold text-gray-900">
15
15
  <%= @title %>
16
16
  </h3>
17
- <%= render component('ui/button').new(
18
- tag: :a,
19
- href: @close_path,
20
- icon: 'close-line',
21
- scheme: :ghost,
22
- title: t('.close'),
23
- ) %>
17
+ <form method="dialog">
18
+ <%= render component('ui/button').new(
19
+ icon: 'close-line',
20
+ scheme: :ghost,
21
+ title: t('.close'),
22
+ ) %>
23
+ </form>
24
24
  </header>
25
25
 
26
26
  <div class="p-4 overflow-auto">
@@ -0,0 +1,7 @@
1
+ import { Controller } from "@hotwired/stimulus";
2
+
3
+ export default class extends Controller {
4
+ connect() {
5
+ this.element.showModal();
6
+ }
7
+ }
@@ -3,7 +3,7 @@
3
3
  class SolidusAdmin::UI::Modal::Component < SolidusAdmin::BaseComponent
4
4
  renders_one :actions
5
5
 
6
- def initialize(title:, close_path: nil, open: true, **attributes)
6
+ def initialize(title:, close_path: nil, open: false, **attributes)
7
7
  @title = title
8
8
  @close_path = close_path
9
9
  @attributes = attributes
@@ -1,7 +1,7 @@
1
1
  <%= page do %>
2
2
  <% if @tabs %>
3
3
  <%= page_header do %>
4
- <%= page_header_title title %>
4
+ <%= render_title %>
5
5
  <% end %>
6
6
 
7
7
  <%= page_header do %>
@@ -18,7 +18,7 @@
18
18
 
19
19
  <% else %>
20
20
  <%= page_header do %>
21
- <%= page_header_title title %>
21
+ <%= render_title %>
22
22
 
23
23
  <%= page_header_actions do %>
24
24
  <%= page_actions %>
@@ -26,5 +26,19 @@
26
26
  <% end %>
27
27
  <% end %>
28
28
 
29
- <%= render_table %>
29
+ <%= page_with_sidebar do %>
30
+ <%= page_with_sidebar_main do %>
31
+ <%= render_table %>
32
+ <% end %>
33
+
34
+ <% if sidebar %>
35
+ <%= page_with_sidebar_aside do %>
36
+ <%= sidebar %>
37
+ <% end %>
38
+ <% end %>
39
+ <% end %>
40
+
41
+ <% turbo_frames.each do |frame| %>
42
+ <%= turbo_frame_tag frame %>
43
+ <% end %>
30
44
  <% end %>
@@ -5,9 +5,20 @@ class SolidusAdmin::UI::Pages::Index::Component < SolidusAdmin::BaseComponent
5
5
 
6
6
  Tab = Struct.new(:text, :href, :current, keyword_init: true)
7
7
 
8
- def tabs
9
- nil
10
- end
8
+ # Template methods
9
+ def tabs; end
10
+ def model_class; end
11
+ def back_url; end
12
+ def search_key; end
13
+ def search_url; end
14
+ def page_actions; end
15
+ def sidebar; end
16
+ def sortable_options; end
17
+ def row_url(_record); end
18
+ def batch_actions; []; end
19
+ def scopes; []; end
20
+ def filters; []; end
21
+ def columns; []; end
11
22
 
12
23
  def initialize(page:)
13
24
  @page = page
@@ -18,18 +29,10 @@ class SolidusAdmin::UI::Pages::Index::Component < SolidusAdmin::BaseComponent
18
29
  false
19
30
  end
20
31
 
21
- def model_class
22
- nil
23
- end
24
-
25
32
  def title
26
33
  model_class.model_name.human.pluralize
27
34
  end
28
35
 
29
- def search_key
30
- nil
31
- end
32
-
33
36
  def search_params
34
37
  params[:q]
35
38
  end
@@ -38,38 +41,10 @@ class SolidusAdmin::UI::Pages::Index::Component < SolidusAdmin::BaseComponent
38
41
  :q
39
42
  end
40
43
 
41
- def search_url
42
- nil
43
- end
44
-
45
- def table_id
46
- stimulus_id
47
- end
48
-
49
44
  def rows
50
45
  @page.records
51
46
  end
52
47
 
53
- def row_url(_record)
54
- nil
55
- end
56
-
57
- def batch_actions
58
- []
59
- end
60
-
61
- def scopes
62
- []
63
- end
64
-
65
- def filters
66
- []
67
- end
68
-
69
- def columns
70
- []
71
- end
72
-
73
48
  def prev_page_path
74
49
  solidus_admin.url_for(**request.params, page: @page.number - 1, only_path: true) unless @page.first?
75
50
  end
@@ -91,8 +66,13 @@ class SolidusAdmin::UI::Pages::Index::Component < SolidusAdmin::BaseComponent
91
66
  }
92
67
  end
93
68
 
94
- def sortable_options
95
- nil
69
+ def render_title
70
+ back_url = self.back_url
71
+
72
+ safe_join [
73
+ (page_header_back back_url if back_url),
74
+ page_header_title(title),
75
+ ]
96
76
  end
97
77
 
98
78
  def render_table
@@ -113,7 +93,13 @@ class SolidusAdmin::UI::Pages::Index::Component < SolidusAdmin::BaseComponent
113
93
  )
114
94
  end
115
95
 
116
- def page_actions
117
- nil
96
+ def render_sidebar
97
+ sidebar = self.sidebar
98
+
99
+ page_with_sidebar_aside { sidebar } if sidebar
100
+ end
101
+
102
+ def turbo_frames
103
+ []
118
104
  end
119
105
  end
@@ -22,29 +22,18 @@
22
22
  <% end %>
23
23
 
24
24
  <% if @title %>
25
- <section class="border-gray-100 border-t w-full first-of-type:border-t-0 p-6">
25
+ <%= render_section do %>
26
26
  <h2>
27
- <span class="font-semibold text-xl"><%= @title %></span>
27
+ <span class="font-semibold text-base"><%= @title %></span>
28
28
  <%= render component("ui/toggletip").new(text: @title_hint) if @title_hint %>
29
29
  </h2>
30
- </section>
30
+ <% end %>
31
31
  <% end %>
32
32
 
33
33
  <% sections.each do |section| %>
34
34
  <%= section %>
35
35
  <% end %>
36
36
 
37
- <% if content.present? %>
38
- <section class="border-gray-100 border-t w-full first-of-type:border-t-0 p-6">
39
- <%= content %>
40
- </section>
41
- <% end %>
42
-
43
- <% if action? %>
44
- <section class="border-gray-100 border-t w-full first-of-type:border-t-0 p-6">
45
- <div class="flex justify-between items-center">
46
- <%= action %>
47
- </div>
48
- </section>
49
- <% end %>
37
+ <%= render_section { content } if content.present? %>
38
+ <%= render_section { tag.div(action, class: "flex justify-between items-center") } if action? %>
50
39
  </div>
@@ -10,13 +10,8 @@ class SolidusAdmin::UI::Panel::Component < SolidusAdmin::BaseComponent
10
10
  )
11
11
  }
12
12
 
13
- renders_many :sections, ->(wide: false, high: false, **args, &block) do
14
- tag.section(**args, class: "
15
- border-gray-100 border-t w-full first-of-type:border-t-0
16
- #{'px-6' unless wide}
17
- #{'py-6' unless high}
18
- #{args[:class]}
19
- ", &block)
13
+ renders_many :sections, ->(**args, &block) do
14
+ render_section(**args, &block)
20
15
  end
21
16
 
22
17
  renders_many :menus, ->(name, url, **args) do
@@ -33,4 +28,13 @@ class SolidusAdmin::UI::Panel::Component < SolidusAdmin::BaseComponent
33
28
  @title = title
34
29
  @title_hint = title_hint
35
30
  end
31
+
32
+ def render_section(wide: false, high: false, **args, &block)
33
+ tag.section(**args, class: "
34
+ border-gray-100 border-t w-full first-of-type:border-t-0
35
+ #{'px-6' unless wide}
36
+ #{'py-4' unless high}
37
+ #{args[:class]}
38
+ ", &block)
39
+ end
36
40
  end
@@ -104,7 +104,15 @@ export default class extends Controller {
104
104
  if (this.modeValue === "batch") {
105
105
  this.toggleCheckbox(event.currentTarget)
106
106
  } else {
107
- window.Turbo.visit(event.params.url)
107
+ const url = new URL(event.params.url, "http://dummy.com")
108
+ const params = new URLSearchParams(url.search)
109
+ const frameId = params.get('_turbo_frame')
110
+ const frame = frameId ? { frame: frameId } : {}
111
+ // remove the custom _turbo_frame param from url search:
112
+ params.delete('_turbo_frame')
113
+ url.search = params.toString()
114
+
115
+ window.Turbo.visit(url.pathname + url.search, frame)
108
116
  }
109
117
  }
110
118
 
@@ -117,8 +125,29 @@ export default class extends Controller {
117
125
  }
118
126
  }
119
127
 
128
+ selectedRows() {
129
+ return this.checkboxTargets.filter((checkbox) => checkbox.checked)
130
+ }
131
+
132
+ confirmAction(event) {
133
+ const message = event.params.message
134
+ .replace(
135
+ "${count}",
136
+ this.selectedRows().length
137
+ ).replace(
138
+ "${resource}",
139
+ this.selectedRows().length > 1 ?
140
+ event.params.resourcePlural :
141
+ event.params.resourceSingular
142
+ )
143
+
144
+ if (!confirm(message)) {
145
+ event.preventDefault()
146
+ }
147
+ }
148
+
120
149
  render() {
121
- const selectedRows = this.checkboxTargets.filter((checkbox) => checkbox.checked)
150
+ const selectedRows = this.selectedRows()
122
151
 
123
152
  if (this.hasSearchFieldTarget) {
124
153
  this.searchToolbarTarget.toggleAttribute("hidden", this.modeValue !== "search")
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class SolidusAdmin::UI::Table::Component < SolidusAdmin::BaseComponent
4
- BatchAction = Struct.new(:display_name, :icon, :action, :method, keyword_init: true) # rubocop:disable Lint/StructNewOverride
5
4
  Column = Struct.new(:header, :data, :col, :wrap, keyword_init: true)
6
- Filter = Struct.new(:presentation, :combinator, :attribute, :predicate, :options, keyword_init: true)
7
- Scope = Struct.new(:name, :label, :default, keyword_init: true)
8
5
  Sortable = Struct.new(:url, :param, :animation, :handle, keyword_init: true)
6
+ Scope = Struct.new(:label, :name, :default, keyword_init: true)
7
+ Filter = Struct.new(:label, :combinator, :attribute, :predicate, :options, keyword_init: true)
8
+ BatchAction = Struct.new(:label, :icon, :action, :require_confirmation, :method, keyword_init: true) # rubocop:disable Lint/StructNewOverride
9
9
  private_constant :BatchAction, :Column, :Filter, :Scope, :Sortable
10
10
 
11
11
  class Data < Struct.new(:rows, :class, :url, :prev, :next, :columns, :fade, :batch_actions, keyword_init: true) # rubocop:disable Lint/StructNewOverride,Style/StructInheritance
@@ -18,6 +18,10 @@ class SolidusAdmin::UI::Table::Component < SolidusAdmin::BaseComponent
18
18
  self.batch_actions = batch_actions.to_a.map { |action| BatchAction.new(**action) }
19
19
  end
20
20
 
21
+ def singular_name
22
+ self[:class].model_name.human if self[:class]
23
+ end
24
+
21
25
  def plural_name
22
26
  self[:class].model_name.human.pluralize if self[:class]
23
27
  end
@@ -97,7 +101,7 @@ class SolidusAdmin::UI::Table::Component < SolidusAdmin::BaseComponent
97
101
  end
98
102
 
99
103
  def render_batch_action_button(batch_action)
100
- render component("ui/button").new(
104
+ params = {
101
105
  name: request_forgery_protection_token,
102
106
  value: form_authenticity_token(form_options: {
103
107
  action: batch_action.action,
@@ -108,14 +112,26 @@ class SolidusAdmin::UI::Table::Component < SolidusAdmin::BaseComponent
108
112
  form: batch_actions_form_id,
109
113
  type: :submit,
110
114
  icon: batch_action.icon,
111
- text: batch_action.display_name,
115
+ text: batch_action.label,
112
116
  scheme: :secondary,
113
- )
117
+ }
118
+
119
+ if batch_action.require_confirmation
120
+ params["data-action"] = "click->#{stimulus_id}#confirmAction"
121
+ params["data-#{stimulus_id}-message-param"] = t(
122
+ ".action_confirmation",
123
+ action: batch_action.label.downcase
124
+ )
125
+ params["data-#{stimulus_id}-resource-singular-param"] = @data.singular_name.downcase
126
+ params["data-#{stimulus_id}-resource-plural-param"] = @data.plural_name.downcase
127
+ end
128
+
129
+ render component("ui/button").new(**params)
114
130
  end
115
131
 
116
132
  def render_ransack_filter_dropdown(filter, index)
117
133
  render component("ui/table/ransack_filter").new(
118
- presentation: filter.presentation,
134
+ presentation: filter.label,
119
135
  search_param: @search.name,
120
136
  combinator: filter.combinator,
121
137
  attribute: filter.attribute,