solidus_admin 0.1.0 → 0.3.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 (241) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +61 -7
  3. data/Rakefile +1 -0
  4. data/app/assets/builds/solidus_admin/tailwind.css +197 -162
  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 +26 -19
  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 +4 -1
  15. data/app/components/solidus_admin/option_types/index/component.rb +21 -7
  16. data/app/components/solidus_admin/orders/index/component.rb +28 -23
  17. data/app/components/solidus_admin/orders/index/component.yml +1 -1
  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 +29 -19
  34. data/app/components/solidus_admin/payment_methods/index/component.yml +0 -5
  35. data/app/components/solidus_admin/products/index/component.rb +25 -15
  36. data/app/components/solidus_admin/products/index/component.yml +0 -2
  37. data/app/components/solidus_admin/products/show/component.html.erb +14 -3
  38. data/app/components/solidus_admin/products/status/component.rb +4 -1
  39. data/app/components/solidus_admin/products/status/component.yml +1 -0
  40. data/app/components/solidus_admin/properties/index/component.rb +18 -19
  41. data/app/components/solidus_admin/refund_reasons/edit/component.html.erb +27 -0
  42. data/app/components/solidus_admin/refund_reasons/edit/component.rb +12 -0
  43. data/app/components/solidus_admin/refund_reasons/edit/component.yml +8 -0
  44. data/app/components/solidus_admin/refund_reasons/index/component.rb +26 -19
  45. data/app/components/solidus_admin/refund_reasons/new/component.html.erb +27 -0
  46. data/app/components/solidus_admin/refund_reasons/new/component.rb +12 -0
  47. data/app/components/solidus_admin/refund_reasons/new/component.yml +6 -0
  48. data/app/components/solidus_admin/refunds_and_returns/component.rb +33 -13
  49. data/app/components/solidus_admin/reimbursement_types/index/component.rb +7 -25
  50. data/app/components/solidus_admin/return_reasons/edit/component.html.erb +26 -0
  51. data/app/components/solidus_admin/return_reasons/edit/component.rb +12 -0
  52. data/app/components/solidus_admin/return_reasons/edit/component.yml +8 -0
  53. data/app/components/solidus_admin/return_reasons/index/component.rb +27 -19
  54. data/app/components/solidus_admin/return_reasons/new/component.html.erb +27 -0
  55. data/app/components/solidus_admin/return_reasons/new/component.rb +12 -0
  56. data/app/components/solidus_admin/return_reasons/new/component.yml +8 -0
  57. data/app/components/solidus_admin/roles/edit/component.html.erb +33 -0
  58. data/app/components/solidus_admin/roles/edit/component.rb +20 -0
  59. data/app/components/solidus_admin/roles/edit/component.yml +19 -0
  60. data/app/components/solidus_admin/roles/index/component.rb +70 -0
  61. data/app/components/solidus_admin/roles/index/component.yml +6 -0
  62. data/app/components/solidus_admin/roles/new/component.html.erb +33 -0
  63. data/app/components/solidus_admin/roles/new/component.rb +20 -0
  64. data/app/components/solidus_admin/roles/new/component.yml +19 -0
  65. data/app/components/solidus_admin/shipping/component.rb +23 -11
  66. data/app/components/solidus_admin/shipping_categories/edit/component.html.erb +16 -0
  67. data/app/components/solidus_admin/shipping_categories/edit/component.rb +12 -0
  68. data/app/components/solidus_admin/shipping_categories/edit/component.yml +6 -0
  69. data/app/components/solidus_admin/shipping_categories/index/component.rb +37 -20
  70. data/app/components/solidus_admin/shipping_categories/new/component.html.erb +17 -0
  71. data/app/components/solidus_admin/shipping_categories/new/component.rb +12 -0
  72. data/app/components/solidus_admin/shipping_categories/new/component.yml +6 -0
  73. data/app/components/solidus_admin/shipping_methods/index/component.rb +19 -19
  74. data/app/components/solidus_admin/stock_items/edit/component.html.erb +66 -72
  75. data/app/components/solidus_admin/stock_items/edit/component.rb +0 -5
  76. data/app/components/solidus_admin/stock_items/index/component.rb +13 -20
  77. data/app/components/solidus_admin/stock_locations/index/component.rb +19 -11
  78. data/app/components/solidus_admin/stock_locations/index/component.yml +0 -3
  79. data/app/components/solidus_admin/store_credit_reasons/edit/component.html.erb +26 -0
  80. data/app/components/solidus_admin/store_credit_reasons/edit/component.rb +12 -0
  81. data/app/components/solidus_admin/store_credit_reasons/edit/component.yml +8 -0
  82. data/app/components/solidus_admin/store_credit_reasons/index/component.rb +26 -19
  83. data/app/components/solidus_admin/store_credit_reasons/new/component.html.erb +27 -0
  84. data/app/components/solidus_admin/store_credit_reasons/new/component.rb +12 -0
  85. data/app/components/solidus_admin/store_credit_reasons/new/component.yml +8 -0
  86. data/app/components/solidus_admin/stores/index/component.rb +18 -19
  87. data/app/components/solidus_admin/tax_categories/edit/component.html.erb +28 -0
  88. data/app/components/solidus_admin/tax_categories/edit/component.rb +12 -0
  89. data/app/components/solidus_admin/tax_categories/edit/component.yml +8 -0
  90. data/app/components/solidus_admin/tax_categories/index/component.rb +28 -21
  91. data/app/components/solidus_admin/tax_categories/new/component.html.erb +28 -0
  92. data/app/components/solidus_admin/tax_categories/new/component.rb +12 -0
  93. data/app/components/solidus_admin/tax_categories/new/component.yml +8 -0
  94. data/app/components/solidus_admin/tax_rates/index/component.rb +21 -17
  95. data/app/components/solidus_admin/taxes/component.rb +8 -8
  96. data/app/components/solidus_admin/taxonomies/index/component.rb +18 -12
  97. data/app/components/solidus_admin/ui/button/component.rb +1 -1
  98. data/app/components/solidus_admin/ui/checkbox_row/component.html.erb +29 -0
  99. data/app/components/solidus_admin/ui/checkbox_row/component.rb +11 -0
  100. data/app/components/solidus_admin/ui/forms/address/component.html.erb +27 -9
  101. data/app/components/solidus_admin/ui/forms/address/component.js +38 -13
  102. data/app/components/solidus_admin/ui/forms/search/component.html.erb +2 -3
  103. data/app/components/solidus_admin/ui/forms/search/component.js +3 -3
  104. data/app/components/solidus_admin/ui/modal/component.html.erb +7 -7
  105. data/app/components/solidus_admin/ui/modal/component.js +7 -0
  106. data/app/components/solidus_admin/ui/modal/component.rb +1 -1
  107. data/app/components/solidus_admin/ui/pages/index/component.html.erb +44 -0
  108. data/app/components/solidus_admin/ui/pages/index/component.rb +105 -0
  109. data/app/components/solidus_admin/ui/panel/component.html.erb +5 -16
  110. data/app/components/solidus_admin/ui/panel/component.rb +11 -7
  111. data/app/components/solidus_admin/ui/table/component.html.erb +2 -1
  112. data/app/components/solidus_admin/ui/table/component.js +31 -2
  113. data/app/components/solidus_admin/ui/table/component.rb +24 -8
  114. data/app/components/solidus_admin/ui/table/component.yml +1 -0
  115. data/app/components/solidus_admin/ui/thumbnail/component.rb +1 -1
  116. data/app/components/solidus_admin/ui/thumbnail_with_caption/component.html.erb +17 -0
  117. data/app/components/solidus_admin/ui/thumbnail_with_caption/component.rb +15 -0
  118. data/app/components/solidus_admin/users/addresses/component.html.erb +46 -0
  119. data/app/components/solidus_admin/users/addresses/component.rb +61 -0
  120. data/app/components/solidus_admin/users/addresses/component.yml +14 -0
  121. data/app/components/solidus_admin/users/edit/api_access/component.html.erb +49 -0
  122. data/app/components/solidus_admin/users/edit/api_access/component.js +9 -0
  123. data/app/components/solidus_admin/users/edit/api_access/component.rb +7 -0
  124. data/app/components/solidus_admin/users/edit/api_access/component.yml +10 -0
  125. data/app/components/solidus_admin/users/edit/component.html.erb +52 -0
  126. data/app/components/solidus_admin/users/edit/component.rb +51 -0
  127. data/app/components/solidus_admin/users/edit/component.yml +12 -0
  128. data/app/components/solidus_admin/users/index/component.rb +25 -16
  129. data/app/components/solidus_admin/users/index/component.yml +0 -3
  130. data/app/components/solidus_admin/users/items/component.html.erb +41 -0
  131. data/app/components/solidus_admin/users/items/component.rb +170 -0
  132. data/app/components/solidus_admin/users/items/component.yml +16 -0
  133. data/app/components/solidus_admin/users/orders/component.html.erb +42 -0
  134. data/app/components/solidus_admin/users/orders/component.rb +131 -0
  135. data/app/components/solidus_admin/users/orders/component.yml +12 -0
  136. data/app/components/solidus_admin/users/stats/component.html.erb +11 -0
  137. data/app/components/solidus_admin/users/stats/component.rb +9 -0
  138. data/app/components/solidus_admin/users/stats/component.yml +2 -0
  139. data/app/components/solidus_admin/users_and_roles/component.rb +24 -0
  140. data/app/components/solidus_admin/users_and_roles/component.yml +2 -0
  141. data/app/components/solidus_admin/zones/index/component.rb +19 -11
  142. data/app/controllers/solidus_admin/addresses_controller.rb +6 -1
  143. data/app/controllers/solidus_admin/adjustment_reasons_controller.rb +85 -10
  144. data/app/controllers/solidus_admin/adjustments_controller.rb +57 -0
  145. data/app/controllers/solidus_admin/customers_controller.rb +5 -1
  146. data/app/controllers/solidus_admin/orders_controller.rb +5 -1
  147. data/app/controllers/solidus_admin/products_controller.rb +11 -0
  148. data/app/controllers/solidus_admin/properties_controller.rb +1 -1
  149. data/app/controllers/solidus_admin/refund_reasons_controller.rb +85 -10
  150. data/app/controllers/solidus_admin/reimbursement_types_controller.rb +0 -5
  151. data/app/controllers/solidus_admin/return_reasons_controller.rb +85 -10
  152. data/app/controllers/solidus_admin/roles_controller.rb +118 -0
  153. data/app/controllers/solidus_admin/shipping_categories_controller.rb +87 -10
  154. data/app/controllers/solidus_admin/shipping_methods_controller.rb +0 -5
  155. data/app/controllers/solidus_admin/stock_items_controller.rb +6 -6
  156. data/app/controllers/solidus_admin/stock_locations_controller.rb +0 -5
  157. data/app/controllers/solidus_admin/store_credit_reasons_controller.rb +85 -10
  158. data/app/controllers/solidus_admin/stores_controller.rb +0 -5
  159. data/app/controllers/solidus_admin/tax_categories_controller.rb +89 -10
  160. data/app/controllers/solidus_admin/tax_rates_controller.rb +0 -5
  161. data/app/controllers/solidus_admin/users_controller.rb +85 -6
  162. data/app/controllers/solidus_admin/zones_controller.rb +0 -5
  163. data/app/helpers/solidus_admin/last_login_helper.rb +16 -0
  164. data/app/helpers/solidus_admin/permission_sets_helper.rb +32 -0
  165. data/app/views/layouts/solidus_admin/application.html.erb +2 -1
  166. data/app/views/layouts/solidus_admin/preview.html.erb +2 -0
  167. data/config/initializers/view_component.rb +1 -1
  168. data/config/locales/adjustment_reasons.en.yml +5 -1
  169. data/config/locales/adjustments.en.yml +10 -0
  170. data/config/locales/refund_reasons.en.yml +10 -0
  171. data/config/locales/return_reasons.en.yml +5 -1
  172. data/config/locales/roles.en.yml +10 -0
  173. data/config/locales/shipping_categories.en.yml +4 -0
  174. data/config/locales/store_credit_reasons.en.yml +5 -1
  175. data/config/locales/tax_categories.en.yml +4 -0
  176. data/config/locales/users.en.yml +10 -0
  177. data/config/routes.rb +24 -7
  178. data/docs/components.md +109 -0
  179. data/docs/{customizing_view_components.md → customizing_components.md} +2 -7
  180. data/docs/index_pages.md +146 -0
  181. data/docs/{customizing_menu_items.md → menu_items.md} +1 -1
  182. data/docs/stimulusjs.md +85 -0
  183. data/docs/{customizing_tailwindcss.md → tailwindcss.md} +27 -8
  184. data/lib/solidus_admin/component_registry.rb +40 -0
  185. data/lib/solidus_admin/configuration.rb +5 -27
  186. data/lib/solidus_admin/install_tailwindcss.rb +3 -1
  187. data/lib/solidus_admin/testing_support/admin_assets.rb +10 -0
  188. data/lib/solidus_admin/testing_support/component_helpers.rb +27 -0
  189. data/lib/solidus_admin/testing_support/dummy_app/rake_tasks.rb +60 -0
  190. data/lib/solidus_admin/testing_support/feature_helpers.rb +34 -0
  191. data/lib/solidus_admin/version.rb +1 -1
  192. data/solidus_admin.gemspec +7 -3
  193. metadata +111 -59
  194. data/app/components/solidus_admin/adjustment_reasons/index/component.html.erb +0 -32
  195. data/app/components/solidus_admin/adjustment_reasons/index/component.yml +0 -4
  196. data/app/components/solidus_admin/option_types/index/component.html.erb +0 -30
  197. data/app/components/solidus_admin/orders/index/component.html.erb +0 -35
  198. data/app/components/solidus_admin/payment_methods/index/component.html.erb +0 -38
  199. data/app/components/solidus_admin/products/index/component.html.erb +0 -34
  200. data/app/components/solidus_admin/promotion_categories/index/component.html.erb +0 -26
  201. data/app/components/solidus_admin/promotion_categories/index/component.rb +0 -57
  202. data/app/components/solidus_admin/promotion_categories/index/component.yml +0 -4
  203. data/app/components/solidus_admin/promotions/index/component.html.erb +0 -34
  204. data/app/components/solidus_admin/promotions/index/component.rb +0 -97
  205. data/app/components/solidus_admin/promotions/index/component.yml +0 -14
  206. data/app/components/solidus_admin/properties/index/component.html.erb +0 -34
  207. data/app/components/solidus_admin/properties/index/component.yml +0 -4
  208. data/app/components/solidus_admin/refund_reasons/index/component.html.erb +0 -31
  209. data/app/components/solidus_admin/refund_reasons/index/component.yml +0 -4
  210. data/app/components/solidus_admin/refunds_and_returns/component.html.erb +0 -26
  211. data/app/components/solidus_admin/reimbursement_types/index/component.html.erb +0 -22
  212. data/app/components/solidus_admin/reimbursement_types/index/component.yml +0 -4
  213. data/app/components/solidus_admin/return_reasons/index/component.html.erb +0 -31
  214. data/app/components/solidus_admin/return_reasons/index/component.yml +0 -4
  215. data/app/components/solidus_admin/shipping/component.html.erb +0 -30
  216. data/app/components/solidus_admin/shipping_categories/index/component.html.erb +0 -32
  217. data/app/components/solidus_admin/shipping_categories/index/component.yml +0 -4
  218. data/app/components/solidus_admin/shipping_methods/index/component.html.erb +0 -32
  219. data/app/components/solidus_admin/shipping_methods/index/component.yml +0 -4
  220. data/app/components/solidus_admin/stock_items/index/component.html.erb +0 -26
  221. data/app/components/solidus_admin/stock_locations/index/component.html.erb +0 -32
  222. data/app/components/solidus_admin/store_credit_reasons/index/component.html.erb +0 -31
  223. data/app/components/solidus_admin/store_credit_reasons/index/component.yml +0 -4
  224. data/app/components/solidus_admin/stores/index/component.html.erb +0 -34
  225. data/app/components/solidus_admin/stores/index/component.yml +0 -4
  226. data/app/components/solidus_admin/tax_categories/index/component.html.erb +0 -32
  227. data/app/components/solidus_admin/tax_categories/index/component.yml +0 -6
  228. data/app/components/solidus_admin/tax_rates/index/component.html.erb +0 -32
  229. data/app/components/solidus_admin/tax_rates/index/component.yml +0 -4
  230. data/app/components/solidus_admin/taxes/component.html.erb +0 -24
  231. data/app/components/solidus_admin/taxonomies/index/component.html.erb +0 -28
  232. data/app/components/solidus_admin/taxonomies/index/component.yml +0 -4
  233. data/app/components/solidus_admin/users/index/component.html.erb +0 -34
  234. data/app/components/solidus_admin/zones/index/component.html.erb +0 -35
  235. data/app/components/solidus_admin/zones/index/component.yml +0 -4
  236. data/app/controllers/solidus_admin/promotion_categories_controller.rb +0 -38
  237. data/app/controllers/solidus_admin/promotions_controller.rb +0 -46
  238. data/config/locales/promotion_categories.en.yml +0 -6
  239. data/config/locales/promotions.en.yml +0 -6
  240. data/config/locales/refund_reasons_.en.yml +0 -6
  241. /data/app/components/solidus_admin/{option_types → ui/pages}/index/component.yml +0 -0
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SolidusAdmin::UI::Pages::Index::Component < SolidusAdmin::BaseComponent
4
+ include SolidusAdmin::Layout::PageHelpers
5
+
6
+ Tab = Struct.new(:text, :href, :current, keyword_init: true)
7
+
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
22
+
23
+ def initialize(page:)
24
+ @page = page
25
+ @tabs = tabs&.map { |tab| Tab.new(**tab) }
26
+ end
27
+
28
+ def row_fade(_record)
29
+ false
30
+ end
31
+
32
+ def title
33
+ model_class.model_name.human.pluralize
34
+ end
35
+
36
+ def search_params
37
+ params[:q]
38
+ end
39
+
40
+ def search_name
41
+ :q
42
+ end
43
+
44
+ def rows
45
+ @page.records
46
+ end
47
+
48
+ def prev_page_path
49
+ solidus_admin.url_for(**request.params, page: @page.number - 1, only_path: true) unless @page.first?
50
+ end
51
+
52
+ def next_page_path
53
+ solidus_admin.url_for(**request.params, page: @page.next_param, only_path: true) unless @page.last?
54
+ end
55
+
56
+ def search_options
57
+ return unless search_url
58
+
59
+ {
60
+ name: search_name,
61
+ value: search_params,
62
+ url: search_url,
63
+ searchbar_key: search_key,
64
+ filters: filters,
65
+ scopes: scopes,
66
+ }
67
+ end
68
+
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
+ ]
76
+ end
77
+
78
+ def render_table
79
+ render component('ui/table').new(
80
+ id: stimulus_id,
81
+ data: {
82
+ class: model_class,
83
+ rows: rows,
84
+ fade: -> { row_fade(_1) },
85
+ prev: prev_page_path,
86
+ next: next_page_path,
87
+ columns: columns,
88
+ batch_actions: batch_actions,
89
+ url: -> { row_url(_1) },
90
+ },
91
+ search: search_options,
92
+ sortable: sortable_options,
93
+ )
94
+ end
95
+
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
+ []
104
+ end
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
@@ -3,6 +3,7 @@
3
3
  rounded-lg
4
4
  border
5
5
  border-gray-100
6
+ <%= 'overflow-hidden' unless @search %>
6
7
  "
7
8
  data-controller="<%= stimulus_id %>"
8
9
  data-<%= stimulus_id %>-selected-row-class="bg-gray-15"
@@ -111,7 +112,7 @@
111
112
  </tr>
112
113
  </thead>
113
114
 
114
- <% if @data.batch_actions %>
115
+ <% if @data.batch_actions && @data.rows.any? %>
115
116
  <thead
116
117
  data-<%= stimulus_id %>-target="batchHeader"
117
118
  class="bg-white color-black text-xs leading-none text-left"
@@ -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
@@ -59,7 +63,7 @@ class SolidusAdmin::UI::Table::Component < SolidusAdmin::BaseComponent
59
63
  def initialize(id:, data:, search: nil, sortable: nil)
60
64
  @id = id
61
65
  @data = Data.new(**data)
62
- @data.columns.unshift selectable_column if @data.batch_actions.present?
66
+ @data.columns.unshift selectable_column if @data.batch_actions.present? && @data.rows.present?
63
67
  @search = Search.new(**search) if search
64
68
  @sortable = Sortable.new(**sortable) if sortable
65
69
  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,
@@ -5,6 +5,7 @@ en:
5
5
  select_row: 'Select row'
6
6
  filter: 'Filter'
7
7
  search_placeholder: 'Search all %{resources}'
8
+ action_confirmation: 'Are you sure you want to %{action} ${count} ${resource}?'
8
9
  refine_search: 'Refine Search'
9
10
  batch_actions: Batch actions
10
11
  cancel: Cancel
@@ -31,7 +31,7 @@ class SolidusAdmin::UI::Thumbnail::Component < SolidusAdmin::BaseComponent
31
31
 
32
32
  def self.for(record, **attrs)
33
33
  case record
34
- when Spree::PromotionAction then new(icon: "megaphone-line", **attrs)
34
+ when *Spree::Config.adjustment_promotion_source_types then new(icon: "megaphone-line", **attrs)
35
35
  when Spree::UnitCancel then new(icon: "close-circle-line", **attrs)
36
36
  when Spree::TaxRate then new(icon: "percent-line", **attrs)
37
37
  when Spree::LineItem then self.for(record.variant, **attrs)
@@ -0,0 +1,17 @@
1
+ <figure class="flex items-center gap-2">
2
+ <% if content? %>
3
+ <%= content %>
4
+ <% else %>
5
+ <%= icon_thumbnail %>
6
+ <% end %>
7
+ <figcaption class="flex flex-col gap-0 max-w-[15rem]">
8
+ <div class="text-black body-small whitespace-nowrap text-ellipsis overflow-hidden">
9
+ <%= caption %>
10
+ </div>
11
+ <% if detail %>
12
+ <div class="text-gray-500 body-small whitespace-nowrap text-ellipsis overflow-hidden">
13
+ <%= detail %>
14
+ </div>
15
+ <% end %>
16
+ </figcaption>
17
+ </figure>
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SolidusAdmin::UI::ThumbnailWithCaption::Component < SolidusAdmin::BaseComponent
4
+ attr_reader :icon, :caption, :detail
5
+
6
+ def initialize(icon: "question-line", caption: "", detail: nil)
7
+ @icon = icon
8
+ @caption = caption
9
+ @detail = detail
10
+ end
11
+
12
+ def icon_thumbnail
13
+ render component("ui/thumbnail").new(icon: icon)
14
+ end
15
+ end
@@ -0,0 +1,46 @@
1
+ <%= page do %>
2
+ <%= page_header do %>
3
+ <%= page_header_back(solidus_admin.users_path) %>
4
+ <%= page_header_title(t(".title", email: @user.email)) %>
5
+
6
+ <%= page_header_actions do %>
7
+ <%= render component("ui/button").new(tag: :a, text: t(".create_order_for_user"), href: spree.new_admin_order_path(user_id: @user.id)) %>
8
+ <% end %>
9
+ <% end %>
10
+
11
+ <%= page_header do %>
12
+ <% tabs.each do |tab| %>
13
+ <%= render(component("ui/button").new(tag: :a, scheme: :ghost, text: tab[:text], 'aria-current': tab[:current], href: tab[:href])) %>
14
+ <% end %>
15
+ <% end %>
16
+
17
+ <%= page_with_sidebar do %>
18
+ <%= page_with_sidebar_main do %>
19
+ <%= render component('ui/panel').new(title: t(".billing_address")) do %>
20
+ <%= form_for @user, url: solidus_admin.update_addresses_user_path(@user), method: :put, html: { id: "#{form_id}_billing", autocomplete: "off", class: "bill_address" } do |f| %>
21
+
22
+ <%= render component('ui/forms/address').new(address: @bill_address, name: "user[bill_address_attributes]") %>
23
+ <div class="py-1.5 text-center">
24
+ <%= render component("ui/button").new(tag: :button, text: t(".update"), form: "#{form_id}_billing") %>
25
+ <%= render component("ui/button").new(tag: :a, text: t(".cancel"), href: solidus_admin.addresses_user_path(@user), scheme: :secondary) %>
26
+ </div>
27
+ <% end %>
28
+ <% end %>
29
+
30
+ <%= render component('ui/panel').new(title: t(".shipping_address")) do %>
31
+ <%= form_for @user, url: solidus_admin.update_addresses_user_path(@user), method: :put, html: { id: "#{form_id}_shipping", autocomplete: "off", class: "ship_address" } do |f| %>
32
+
33
+ <%= render component('ui/forms/address').new(address: @ship_address, name: "user[ship_address_attributes]") %>
34
+ <div class="py-1.5 text-center">
35
+ <%= render component("ui/button").new(tag: :button, text: t(".update"), form: "#{form_id}_shipping") %>
36
+ <%= render component("ui/button").new(tag: :a, text: t(".cancel"), href: solidus_admin.addresses_user_path(@user), scheme: :secondary) %>
37
+ </div>
38
+ <% end %>
39
+ <% end %>
40
+ <% end %>
41
+
42
+ <%= page_with_sidebar_aside do %>
43
+ <%= render component("users/stats").new(user: @user) %>
44
+ <% end %>
45
+ <% end %>
46
+ <% end %>
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SolidusAdmin::Users::Addresses::Component < SolidusAdmin::BaseComponent
4
+ include SolidusAdmin::Layout::PageHelpers
5
+
6
+ def initialize(user:, address: nil, type: nil)
7
+ @user = user
8
+ @bill_address = bill_address(address, type)
9
+ @ship_address = ship_address(address, type)
10
+ end
11
+
12
+ def form_id
13
+ @form_id ||= "#{stimulus_id}--form-#{@user.id}"
14
+ end
15
+
16
+ def tabs
17
+ [
18
+ {
19
+ text: t('.account'),
20
+ href: solidus_admin.user_path(@user),
21
+ current: false,
22
+ },
23
+ {
24
+ text: t('.addresses'),
25
+ href: solidus_admin.addresses_user_path(@user),
26
+ current: true,
27
+ },
28
+ {
29
+ text: t('.order_history'),
30
+ href: solidus_admin.orders_user_path(@user),
31
+ current: false,
32
+ },
33
+ {
34
+ text: t('.items'),
35
+ href: spree.items_admin_user_path(@user),
36
+ current: false,
37
+ },
38
+ {
39
+ text: t('.store_credit'),
40
+ href: spree.admin_user_store_credits_path(@user),
41
+ current: false,
42
+ },
43
+ ]
44
+ end
45
+
46
+ def bill_address(address, type)
47
+ if address.present? && type == "bill"
48
+ address
49
+ else
50
+ @user.bill_address || Spree::Address.build_default
51
+ end
52
+ end
53
+
54
+ def ship_address(address, type)
55
+ if address.present? && type == "ship"
56
+ address
57
+ else
58
+ @user.ship_address || Spree::Address.build_default
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,14 @@
1
+ en:
2
+ title: "Users / %{email} / Addresses"
3
+ account: Account
4
+ addresses: Addresses
5
+ order_history: Order History
6
+ items: Items
7
+ store_credit: Store Credit
8
+ last_active: Last Active
9
+ create_order_for_user: Create order for this user
10
+ update: Update
11
+ cancel: Cancel
12
+ back: Back
13
+ billing_address: Billing Address
14
+ shipping_address: Shipping Address
@@ -0,0 +1,49 @@
1
+ <%= render component('ui/panel').new(title: t('.api_access')) do %>
2
+ <section>
3
+ <% if @user.spree_api_key.present? %>
4
+ <div id="current-api-key">
5
+ <h2 class="py-1.5 font-semibold"><%= t('.key') %></h2>
6
+ <% if @user == helpers.current_solidus_admin_user %>
7
+ <%= @user.spree_api_key %>
8
+ <% else %>
9
+ <i>(<%= t('spree.hidden') %>)</i>
10
+ <% end %>
11
+ </div>
12
+
13
+ <div class="py-1.5 text-center">
14
+ <%= form_with url: spree.admin_user_api_key_path(@user), method: :delete, local: true, html: { class: 'clear_api_key inline-flex' } do %>
15
+ <%= render component("ui/button").new(
16
+ text: t('.clear_key'),
17
+ scheme: :secondary,
18
+ type: :submit,
19
+ "data-action": "click->#{stimulus_id}#confirm",
20
+ "data-#{stimulus_id}-message-param": t(".confirm_clear_key"),
21
+ ) %>
22
+ <% end %>
23
+
24
+ <%= form_with url: spree.admin_user_api_key_path(@user), method: :post, local: true, html: { class: 'regen_api_key inline-flex' } do %>
25
+ <%= render component("ui/button").new(
26
+ text: t('.regenerate_key'),
27
+ scheme: :secondary,
28
+ type: :submit,
29
+ "data-action": "click->#{stimulus_id}#confirm",
30
+ "data-#{stimulus_id}-message-param": t(".confirm_regenerate_key"),
31
+ ) %>
32
+ <% end %>
33
+ </div>
34
+
35
+ <% else %>
36
+ <div class="no-objects-found"><%= t('.no_key') %></div>
37
+ <div class="filter-actions actions">
38
+ <div class="py-1.5 text-center">
39
+ <%= form_with url: spree.admin_user_api_key_path(@user), method: :post, local: true, html: { class: 'generate_api_key inline-flex' } do %>
40
+ <%= render component("ui/button").new(
41
+ text: t('.generate_key'),
42
+ type: :submit,
43
+ ) %>
44
+ <% end %>
45
+ </div>
46
+ </div>
47
+ <% end %>
48
+ </section>
49
+ <% end %>
@@ -0,0 +1,9 @@
1
+ import { Controller } from "@hotwired/stimulus"
2
+
3
+ export default class extends Controller {
4
+ confirm(event) {
5
+ if (!confirm(event.params.message)) {
6
+ event.preventDefault()
7
+ }
8
+ }
9
+ }
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SolidusAdmin::Users::Edit::ApiAccess::Component < SolidusAdmin::BaseComponent
4
+ def initialize(user:)
5
+ @user = user
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ en:
2
+ api_access: API Access
3
+ no_key: No key
4
+ key: "Key"
5
+ generate_key: Generate API key
6
+ clear_key: Clear key
7
+ regenerate_key: Regenerate key
8
+ hidden: Hidden
9
+ confirm_clear_key: Are you sure you want to clear this user's API key? It will invalidate the existing key.
10
+ confirm_regenerate_key: Are you sure you want to regenerate this user's API key? It will invalidate the existing key.
@@ -0,0 +1,52 @@
1
+ <%= page do %>
2
+ <%= page_header do %>
3
+ <%= page_header_back(solidus_admin.users_path) %>
4
+ <%= page_header_title(t(".title", email: @user.email)) %>
5
+
6
+ <% # @todo: I am not sure how we want to handle Cancan stuff in the new admin. %>
7
+ <% # if can?(:admin, Spree::Order) && can?(:create, Spree::Order) %>
8
+ <%= page_header_actions do %>
9
+ <%= render component("ui/button").new(tag: :a, text: t(".create_order_for_user"), href: spree.new_admin_order_path(user_id: @user.id)) %>
10
+ <% end %>
11
+ <% # end %>
12
+ <% end %>
13
+
14
+ <%= page_header do %>
15
+ <% tabs.each do |tab| %>
16
+ <%= render(component("ui/button").new(tag: :a, scheme: :ghost, text: tab[:text], 'aria-current': tab[:current], href: tab[:href])) %>
17
+ <% end %>
18
+ <% end %>
19
+
20
+ <%= page_with_sidebar do %>
21
+ <%= page_with_sidebar_main do %>
22
+
23
+ <%= render component('ui/panel').new(title: Spree.user_class.model_name.human) do %>
24
+ <%= form_for @user, url: solidus_admin.user_path(@user), html: { id: form_id, autocomplete: "off" } do |f| %>
25
+ <div class="py-1.5">
26
+ <%= render component("ui/forms/field").text_field(f, :email) %>
27
+ </div>
28
+ <div class="py-1.5">
29
+ <%= render component("ui/forms/field").text_field(f, :password) %>
30
+ </div>
31
+ <div class="py-1.5">
32
+ <%= render component("ui/forms/field").text_field(f, :password_confirmation) %>
33
+ </div>
34
+ <div class="py-1.5">
35
+ <%= render component("ui/checkbox_row").new(options: role_options, row_title: "Roles", form: f, method: "spree_role_ids", layout: :subsection) %>
36
+ </div>
37
+ <div class="py-1.5 text-center">
38
+ <%= render component("ui/button").new(tag: :button, text: t(".update"), form: form_id) %>
39
+ <%= render component("ui/button").new(tag: :a, text: t(".cancel"), href: solidus_admin.user_path(@user), scheme: :secondary) %>
40
+ </div>
41
+ <% end %>
42
+ <% end %>
43
+
44
+ <%= render component("users/edit/api_access").new(user: @user) %>
45
+
46
+ <% end %>
47
+
48
+ <%= page_with_sidebar_aside do %>
49
+ <%= render component("users/stats").new(user: @user) %>
50
+ <% end %>
51
+ <% end %>
52
+ <% end %>
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SolidusAdmin::Users::Edit::Component < SolidusAdmin::BaseComponent
4
+ include SolidusAdmin::Layout::PageHelpers
5
+
6
+ def initialize(user:)
7
+ @user = user
8
+ end
9
+
10
+ def form_id
11
+ @form_id ||= "#{stimulus_id}--form-#{@user.id}"
12
+ end
13
+
14
+ def tabs
15
+ [
16
+ {
17
+ text: t('.account'),
18
+ href: solidus_admin.user_path(@user),
19
+ current: action_name == "edit",
20
+ },
21
+ {
22
+ text: t('.addresses'),
23
+ href: solidus_admin.addresses_user_path(@user),
24
+ current: action_name == "addresses",
25
+ },
26
+ {
27
+ text: t('.order_history'),
28
+ href: solidus_admin.orders_user_path(@user),
29
+ current: action_name == "orders",
30
+ },
31
+ {
32
+ text: t('.items'),
33
+ href: spree.items_admin_user_path(@user),
34
+ # @todo: update this "current" logic once folded into new admin
35
+ current: action_name != "edit",
36
+ },
37
+ {
38
+ text: t('.store_credit'),
39
+ href: spree.admin_user_store_credits_path(@user),
40
+ # @todo: update this "current" logic once folded into new admin
41
+ current: action_name != "edit",
42
+ },
43
+ ]
44
+ end
45
+
46
+ def role_options
47
+ Spree::Role.all.map do |role|
48
+ { label: role.name, id: role.id }
49
+ end
50
+ end
51
+ end