solidus_admin 0.0.2 → 0.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 (251) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.json +7 -0
  3. data/README.md +2 -2
  4. data/Rakefile +26 -0
  5. data/app/assets/builds/.keep +0 -0
  6. data/app/assets/builds/solidus_admin/tailwind.css +2799 -0
  7. data/app/assets/config/solidus_admin_manifest.js +1 -0
  8. data/app/assets/stylesheets/solidus_admin/application.tailwind.css +4 -0
  9. data/app/assets/stylesheets/solidus_admin/dark.css +12 -0
  10. data/app/assets/stylesheets/solidus_admin/dimmed.css +11 -0
  11. data/app/components/solidus_admin/adjustment_reasons/index/component.rb +43 -0
  12. data/app/components/solidus_admin/base_component.rb +29 -1
  13. data/app/components/solidus_admin/layout/feedback/component.html.erb +15 -0
  14. data/app/components/solidus_admin/layout/feedback/component.rb +4 -0
  15. data/app/components/solidus_admin/layout/feedback/component.yml +3 -0
  16. data/app/components/solidus_admin/layout/navigation/account/component.html.erb +74 -0
  17. data/app/components/solidus_admin/layout/navigation/account/component.js +16 -0
  18. data/app/components/solidus_admin/layout/navigation/account/component.rb +36 -0
  19. data/app/components/solidus_admin/{sidebar → layout/navigation}/component.html.erb +9 -9
  20. data/app/components/solidus_admin/layout/navigation/component.rb +26 -0
  21. data/app/components/solidus_admin/{sidebar → layout/navigation}/item/component.html.erb +5 -5
  22. data/app/components/solidus_admin/{sidebar → layout/navigation}/item/component.rb +2 -2
  23. data/app/components/solidus_admin/layout/page_helpers.rb +55 -0
  24. data/app/components/solidus_admin/{skip_link → layout/skip_link}/component.rb +2 -4
  25. data/app/components/solidus_admin/option_types/index/component.rb +71 -0
  26. data/app/components/solidus_admin/orders/cart/component.html.erb +77 -0
  27. data/app/components/solidus_admin/orders/cart/component.js +37 -0
  28. data/app/components/solidus_admin/orders/cart/component.rb +7 -0
  29. data/app/components/solidus_admin/orders/cart/component.yml +3 -0
  30. data/app/components/solidus_admin/orders/cart/result/component.html.erb +26 -0
  31. data/app/components/solidus_admin/orders/cart/result/component.rb +11 -0
  32. data/app/components/solidus_admin/orders/index/component.rb +92 -23
  33. data/app/components/solidus_admin/orders/index/component.yml +10 -4
  34. data/app/components/solidus_admin/orders/show/address/component.html.erb +67 -0
  35. data/app/components/solidus_admin/orders/show/address/component.js +9 -0
  36. data/app/components/solidus_admin/orders/show/address/component.rb +53 -0
  37. data/app/components/solidus_admin/orders/show/address/component.yml +14 -0
  38. data/app/components/solidus_admin/orders/show/component.html.erb +76 -0
  39. data/app/components/solidus_admin/orders/show/component.js +7 -0
  40. data/app/components/solidus_admin/orders/show/component.rb +40 -0
  41. data/app/components/solidus_admin/orders/show/component.yml +21 -0
  42. data/app/components/solidus_admin/orders/show/customer_search/component.html.erb +14 -0
  43. data/app/components/solidus_admin/orders/show/customer_search/component.js +14 -0
  44. data/app/components/solidus_admin/orders/show/customer_search/component.rb +7 -0
  45. data/app/components/solidus_admin/orders/show/customer_search/component.yml +2 -0
  46. data/app/components/solidus_admin/orders/show/customer_search/result/component.html.erb +17 -0
  47. data/app/components/solidus_admin/orders/show/customer_search/result/component.rb +11 -0
  48. data/app/components/solidus_admin/orders/show/email/component.html.erb +18 -0
  49. data/app/components/solidus_admin/orders/show/email/component.rb +15 -0
  50. data/app/components/solidus_admin/orders/show/email/component.yml +8 -0
  51. data/app/components/solidus_admin/orders/show/summary/component.html.erb +14 -0
  52. data/app/components/solidus_admin/orders/show/summary/component.rb +7 -0
  53. data/app/components/solidus_admin/orders/show/summary/component.yml +8 -0
  54. data/app/components/solidus_admin/payment_methods/index/component.rb +103 -0
  55. data/app/components/solidus_admin/payment_methods/index/component.yml +10 -0
  56. data/app/components/solidus_admin/products/index/component.rb +48 -41
  57. data/app/components/solidus_admin/products/index/component.yml +8 -7
  58. data/app/components/solidus_admin/products/show/component.html.erb +32 -38
  59. data/app/components/solidus_admin/products/show/component.rb +2 -0
  60. data/app/components/solidus_admin/products/show/component.yml +5 -5
  61. data/app/components/solidus_admin/products/status/component.rb +20 -18
  62. data/app/components/solidus_admin/products/status/component.yml +1 -0
  63. data/app/components/solidus_admin/products/stock/component.rb +38 -0
  64. data/app/components/solidus_admin/products/stock/component.yml +5 -0
  65. data/app/components/solidus_admin/promotion_categories/index/component.rb +56 -0
  66. data/app/components/solidus_admin/promotions/index/component.rb +104 -0
  67. data/app/components/solidus_admin/promotions/index/component.yml +10 -0
  68. data/app/components/solidus_admin/properties/index/component.rb +64 -0
  69. data/app/components/solidus_admin/refund_reasons/index/component.rb +53 -0
  70. data/app/components/solidus_admin/refunds_and_returns/component.rb +40 -0
  71. data/app/components/solidus_admin/refunds_and_returns/component.yml +3 -0
  72. data/app/components/solidus_admin/reimbursement_types/index/component.rb +27 -0
  73. data/app/components/solidus_admin/return_reasons/index/component.rb +42 -0
  74. data/app/components/solidus_admin/shipping/component.rb +30 -0
  75. data/app/components/solidus_admin/shipping/component.yml +3 -0
  76. data/app/components/solidus_admin/shipping_categories/index/component.rb +46 -0
  77. data/app/components/solidus_admin/shipping_methods/index/component.rb +61 -0
  78. data/app/components/solidus_admin/stock_items/edit/component.html.erb +89 -0
  79. data/app/components/solidus_admin/stock_items/edit/component.js +17 -0
  80. data/app/components/solidus_admin/stock_items/edit/component.rb +23 -0
  81. data/app/components/solidus_admin/stock_items/edit/component.yml +10 -0
  82. data/app/components/solidus_admin/stock_items/index/component.rb +174 -0
  83. data/app/components/solidus_admin/stock_items/index/component.yml +10 -0
  84. data/app/components/solidus_admin/stock_locations/index/component.rb +89 -0
  85. data/app/components/solidus_admin/stock_locations/index/component.yml +3 -0
  86. data/app/components/solidus_admin/store_credit_reasons/index/component.rb +52 -0
  87. data/app/components/solidus_admin/stores/index/component.rb +58 -0
  88. data/app/components/solidus_admin/tax_categories/index/component.rb +58 -0
  89. data/app/components/solidus_admin/tax_rates/index/component.rb +88 -0
  90. data/app/components/solidus_admin/taxes/component.rb +25 -0
  91. data/app/components/solidus_admin/taxes/component.yml +3 -0
  92. data/app/components/solidus_admin/taxonomies/index/component.rb +53 -0
  93. data/app/components/solidus_admin/ui/badge/component.rb +18 -8
  94. data/app/components/solidus_admin/ui/badge/component.yml +3 -0
  95. data/app/components/solidus_admin/ui/button/component.rb +32 -32
  96. data/app/components/solidus_admin/ui/details_list/component.html.erb +10 -0
  97. data/app/components/solidus_admin/ui/details_list/component.rb +7 -0
  98. data/app/components/solidus_admin/ui/dropdown/component.html.erb +50 -0
  99. data/app/components/solidus_admin/ui/dropdown/component.js +16 -0
  100. data/app/components/solidus_admin/ui/dropdown/component.rb +28 -0
  101. data/app/components/solidus_admin/ui/dropdown/component.yml +2 -0
  102. data/app/components/solidus_admin/ui/forms/address/component.html.erb +36 -0
  103. data/app/components/solidus_admin/ui/forms/address/component.js +34 -0
  104. data/app/components/solidus_admin/ui/forms/address/component.rb +14 -0
  105. data/app/components/solidus_admin/ui/forms/field/component.html.erb +5 -4
  106. data/app/components/solidus_admin/ui/forms/field/component.rb +53 -15
  107. data/app/components/solidus_admin/ui/forms/input/component.rb +11 -5
  108. data/app/components/solidus_admin/ui/forms/search/component.html.erb +52 -0
  109. data/app/components/solidus_admin/ui/forms/search/component.js +116 -0
  110. data/app/components/solidus_admin/ui/forms/search/component.rb +8 -0
  111. data/app/components/solidus_admin/ui/forms/search/component.yml +4 -0
  112. data/app/components/solidus_admin/ui/forms/search/result/component.rb +12 -0
  113. data/app/components/solidus_admin/ui/forms/search_field/component.html.erb +20 -0
  114. data/app/{javascript/solidus_admin/controllers/hello_controller.js → components/solidus_admin/ui/forms/search_field/component.js} +4 -2
  115. data/app/components/solidus_admin/ui/forms/search_field/component.rb +10 -0
  116. data/app/components/solidus_admin/ui/forms/search_field/component.yml +2 -0
  117. data/app/components/solidus_admin/ui/forms/switch/component.rb +27 -31
  118. data/app/components/solidus_admin/ui/forms/switch_field/component.html.erb +20 -0
  119. data/app/components/solidus_admin/ui/forms/switch_field/component.rb +11 -0
  120. data/app/components/solidus_admin/ui/icon/component.rb +4 -0
  121. data/app/components/solidus_admin/ui/modal/component.html.erb +37 -0
  122. data/app/components/solidus_admin/ui/modal/component.rb +12 -0
  123. data/app/components/solidus_admin/ui/modal/component.yml +2 -0
  124. data/app/components/solidus_admin/ui/pages/index/component.html.erb +30 -0
  125. data/app/components/solidus_admin/ui/pages/index/component.rb +119 -0
  126. data/app/components/solidus_admin/ui/pages/index/component.yml +4 -0
  127. data/app/components/solidus_admin/ui/panel/component.html.erb +26 -12
  128. data/app/components/solidus_admin/ui/panel/component.rb +17 -0
  129. data/app/components/solidus_admin/ui/panel/component.yml +1 -3
  130. data/app/components/solidus_admin/ui/resource_item/component.html.erb +10 -0
  131. data/app/components/solidus_admin/ui/resource_item/component.rb +9 -0
  132. data/app/components/solidus_admin/ui/tab/component.rb +9 -8
  133. data/app/components/solidus_admin/ui/table/component.html.erb +147 -135
  134. data/app/components/solidus_admin/ui/table/component.js +56 -17
  135. data/app/components/solidus_admin/ui/table/component.rb +94 -70
  136. data/app/components/solidus_admin/ui/table/component.yml +0 -1
  137. data/app/components/solidus_admin/ui/table/ransack_filter/component.html.erb +72 -0
  138. data/app/components/solidus_admin/ui/table/ransack_filter/component.js +73 -0
  139. data/app/components/solidus_admin/ui/table/ransack_filter/component.rb +68 -0
  140. data/app/components/solidus_admin/ui/table/ransack_filter/component.yml +3 -0
  141. data/app/components/solidus_admin/ui/table/toolbar/component.rb +21 -0
  142. data/app/components/solidus_admin/ui/thumbnail/component.rb +46 -0
  143. data/app/components/solidus_admin/ui/toast/component.html.erb +9 -5
  144. data/app/components/solidus_admin/ui/toast/component.js +9 -6
  145. data/app/components/solidus_admin/ui/toast/component.rb +2 -2
  146. data/app/components/solidus_admin/ui/toggletip/component.html.erb +14 -10
  147. data/app/components/solidus_admin/ui/toggletip/component.js +22 -4
  148. data/app/components/solidus_admin/ui/toggletip/component.rb +8 -85
  149. data/app/components/solidus_admin/users/index/component.rb +96 -0
  150. data/app/components/solidus_admin/users/index/component.yml +15 -0
  151. data/app/components/solidus_admin/zones/index/component.rb +63 -0
  152. data/app/controllers/solidus_admin/addresses_controller.rb +92 -0
  153. data/app/controllers/solidus_admin/adjustment_reasons_controller.rb +40 -0
  154. data/app/controllers/solidus_admin/base_controller.rb +1 -0
  155. data/app/controllers/solidus_admin/controller_helpers/authorization.rb +5 -1
  156. data/app/controllers/solidus_admin/controller_helpers/locale.rb +2 -2
  157. data/app/controllers/solidus_admin/controller_helpers/search.rb +48 -0
  158. data/app/controllers/solidus_admin/controller_helpers/theme.rb +30 -0
  159. data/app/controllers/solidus_admin/countries_controller.rb +12 -0
  160. data/app/controllers/solidus_admin/customers_controller.rb +29 -0
  161. data/app/controllers/solidus_admin/line_items_controller.rb +45 -0
  162. data/app/controllers/solidus_admin/option_types_controller.rb +46 -0
  163. data/app/controllers/solidus_admin/orders_controller.rb +104 -7
  164. data/app/controllers/solidus_admin/payment_methods_controller.rb +52 -0
  165. data/app/controllers/solidus_admin/products_controller.rb +26 -17
  166. data/app/controllers/solidus_admin/promotion_categories_controller.rb +29 -0
  167. data/app/controllers/solidus_admin/promotions_controller.rb +46 -0
  168. data/app/controllers/solidus_admin/properties_controller.rb +33 -0
  169. data/app/controllers/solidus_admin/refund_reasons_controller.rb +40 -0
  170. data/app/controllers/solidus_admin/reimbursement_types_controller.rb +31 -0
  171. data/app/controllers/solidus_admin/return_reasons_controller.rb +40 -0
  172. data/app/controllers/solidus_admin/shipping_categories_controller.rb +40 -0
  173. data/app/controllers/solidus_admin/shipping_methods_controller.rb +40 -0
  174. data/app/controllers/solidus_admin/stock_items_controller.rb +67 -0
  175. data/app/controllers/solidus_admin/stock_locations_controller.rb +40 -0
  176. data/app/controllers/solidus_admin/store_credit_reasons_controller.rb +40 -0
  177. data/app/controllers/solidus_admin/stores_controller.rb +40 -0
  178. data/app/controllers/solidus_admin/tax_categories_controller.rb +40 -0
  179. data/app/controllers/solidus_admin/tax_rates_controller.rb +40 -0
  180. data/app/controllers/solidus_admin/taxonomies_controller.rb +46 -0
  181. data/app/controllers/solidus_admin/users_controller.rb +50 -0
  182. data/app/controllers/solidus_admin/zones_controller.rb +40 -0
  183. data/app/javascript/solidus_admin/controllers/components.js +3 -1
  184. data/app/javascript/solidus_admin/controllers/confirm_controller.js +21 -0
  185. data/app/javascript/solidus_admin/controllers/details_click_outside_controller.js +12 -0
  186. data/app/javascript/solidus_admin/controllers/readonly_when_submitting_controller.js +17 -0
  187. data/app/javascript/solidus_admin/controllers/sortable_controller.js +33 -0
  188. data/app/views/layouts/solidus_admin/application.html.erb +16 -10
  189. data/app/views/layouts/solidus_admin/preview.html.erb +2 -1
  190. data/app/views/solidus_admin/base/unauthorized.html.erb +4 -0
  191. data/config/importmap.rb +2 -0
  192. data/config/initializers/view_component.rb +20 -0
  193. data/config/locales/adjustment_reasons.en.yml +6 -0
  194. data/config/locales/customers.en.yml +7 -0
  195. data/config/locales/errors.en.yml +7 -0
  196. data/config/locales/line_items.en.yml +9 -0
  197. data/config/locales/{main_nav.en.yml → menu_item.en.yml} +8 -2
  198. data/config/locales/option_types.en.yml +6 -0
  199. data/config/locales/orders.en.yml +9 -0
  200. data/config/locales/payment_methods.en.yml +6 -0
  201. data/config/locales/promotion_categories.en.yml +6 -0
  202. data/config/locales/promotions.en.yml +6 -0
  203. data/config/locales/properties.en.yml +6 -0
  204. data/config/locales/refund_reasons_.en.yml +6 -0
  205. data/config/locales/reimbursement_types.en.yml +4 -0
  206. data/config/locales/return_reasons.en.yml +6 -0
  207. data/config/locales/shipping_categories.en.yml +6 -0
  208. data/config/locales/shipping_methods.en.yml +6 -0
  209. data/config/locales/stock_items.en.yml +4 -0
  210. data/config/locales/stock_locations.en.yml +6 -0
  211. data/config/locales/store_credit_reasons.en.yml +6 -0
  212. data/config/locales/stores.en.yml +6 -0
  213. data/config/locales/tax_categories.en.yml +6 -0
  214. data/config/locales/tax_rates.en.yml +6 -0
  215. data/config/locales/taxonomies.en.yml +6 -0
  216. data/config/locales/users.en.yml +6 -0
  217. data/config/locales/zones.en.yml +6 -0
  218. data/config/routes.rb +50 -3
  219. data/config/tailwind.config.js +119 -0
  220. data/docs/{customizing_main_navigation.md → customizing_menu_items.md} +2 -2
  221. data/docs/customizing_tailwindcss.md +57 -0
  222. data/docs/customizing_view_components.md +17 -38
  223. data/lib/generators/solidus_admin/install/install_generator.rb +13 -4
  224. data/lib/generators/solidus_admin/install/templates/config/initializers/{solidus_admin.rb → solidus_admin.rb.tt} +10 -14
  225. data/lib/solidus_admin/admin_resources.rb +23 -0
  226. data/lib/solidus_admin/configuration.rb +95 -67
  227. data/lib/solidus_admin/install_tailwindcss.rb +102 -0
  228. data/lib/solidus_admin/{main_nav_item.rb → menu_item.rb} +2 -2
  229. data/lib/solidus_admin/version.rb +1 -1
  230. data/lib/solidus_admin.rb +1 -2
  231. data/lib/tasks/tailwind.rake +10 -0
  232. data/solidus_admin.gemspec +3 -4
  233. metadata +193 -50
  234. data/app/assets/stylesheets/solidus_admin/application.tailwind.css.erb +0 -35
  235. data/app/components/solidus_admin/feedback/component.html.erb +0 -11
  236. data/app/components/solidus_admin/feedback/component.rb +0 -4
  237. data/app/components/solidus_admin/feedback/component.yml +0 -5
  238. data/app/components/solidus_admin/orders/index/component.html.erb +0 -31
  239. data/app/components/solidus_admin/products/index/component.html.erb +0 -30
  240. data/app/components/solidus_admin/sidebar/account_nav/component.html.erb +0 -67
  241. data/app/components/solidus_admin/sidebar/account_nav/component.rb +0 -15
  242. data/app/components/solidus_admin/sidebar/component.rb +0 -21
  243. data/app/components/solidus_admin/ui/panel/component.js +0 -14
  244. data/config/solidus_admin/tailwind.config.js.erb +0 -95
  245. data/docs/customizing_tailwind.md +0 -78
  246. data/lib/solidus_admin/tailwindcss.rb +0 -58
  247. data/lib/tasks/tailwindcss.rake +0 -55
  248. /data/app/components/solidus_admin/{sidebar/account_nav → layout/navigation/account}/component.yml +0 -0
  249. /data/app/components/solidus_admin/{sidebar → layout/navigation}/component.js +0 -0
  250. /data/app/components/solidus_admin/{sidebar → layout/navigation}/component.yml +0 -0
  251. /data/app/components/solidus_admin/{skip_link → layout/skip_link}/component.yml +0 -0
@@ -7,5 +7,4 @@ en:
7
7
  search_placeholder: 'Search all %{resources}'
8
8
  refine_search: 'Refine Search'
9
9
  batch_actions: Batch actions
10
- clear: Clear
11
10
  cancel: Cancel
@@ -0,0 +1,72 @@
1
+ <fieldset class="<%= stimulus_id %>" data-controller="<%= stimulus_id %>">
2
+ <input type="hidden" form="<%= @form %>"
3
+ name="<%= @combinator.name %>"
4
+ value="<%= @combinator.value %>">
5
+
6
+ <details class="relative inline-block text-left" data-<%= stimulus_id %>-target="details">
7
+ <summary class="
8
+ inline-flex justify-center
9
+ rounded-full border border-gray-300
10
+ shadow-sm
11
+ px-3 py-2
12
+ text-sm font-medium text-gray-700
13
+ hover:bg-gray-50
14
+ focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-indigo-500
15
+ cursor-default
16
+ " data-<%= stimulus_id %>-target="summary">
17
+ <%= @presentation %>
18
+ <%= render component("ui/icon").new(name: 'arrow-down-s-fill', class: "w-[1.4em] h-[1.4em]") %>
19
+ </summary>
20
+
21
+ <div class="
22
+ absolute
23
+ left-0 mt-2 w-56
24
+ rounded-md shadow-lg
25
+ bg-white
26
+ ring-1 ring-black ring-opacity-5
27
+ z-10
28
+ ">
29
+ <div class="relative">
30
+ <% if @selections.size > 6 %>
31
+ <div class="px-4 py-2 sticky top-0 z-50">
32
+ <input type="text"
33
+ placeholder="<%= t('.search') %>"
34
+ class="w-full px-2 py-1 border border-gray-300 rounded focus:border-indigo-500 focus:ring-indigo-500"
35
+ data-action="input-><%= stimulus_id %>#filterOptions">
36
+ </div>
37
+ <% end %>
38
+ <div class="py-1 max-h-[240px] overflow-y-auto" role="menu" aria-orientation="vertical" aria-labelledby="options-menu" data-<%= stimulus_id %>-target="menu">
39
+ <% if @selections.any? %>
40
+ <% @selections.each do |selection| %>
41
+ <div class="px-4 py-2" data-<%= stimulus_id %>-target="option">
42
+ <input type="hidden" form="<%= @form %>"
43
+ name="<%= selection.attribute.name %>"
44
+ value="<%= selection.attribute.value %>">
45
+ <input type="hidden" form="<%= @form %>"
46
+ name="<%= selection.predicate.name %>"
47
+ value="<%= selection.predicate.value %>">
48
+
49
+ <%= render component('ui/forms/checkbox').new(
50
+ id: selection.id,
51
+ name: selection.option.name,
52
+ value: selection.option.value,
53
+ checked: selection.checked,
54
+ size: :s,
55
+ form: @form,
56
+ "data-action": "#{stimulus_id}#search #{stimulus_id}#sortCheckboxes #{stimulus_id}#updateHiddenInputs",
57
+ "data-#{stimulus_id}-target": "checkbox"
58
+ ) %>
59
+
60
+ <%= label_tag selection.id, selection.presentation, class: "ml-2 text-sm text-gray-700" %>
61
+ </div>
62
+ <% end %>
63
+ <% else %>
64
+ <div class="px-4 py-2 text-sm text-gray-700">
65
+ <%= t('.no_filter_options') %>
66
+ </div>
67
+ <% end %>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ </details>
72
+ </fieldset>
@@ -0,0 +1,73 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+ import { useClickOutside, useDebounce } from 'stimulus-use'
3
+
4
+ const BG_GRAY = 'bg-gray-100'
5
+
6
+ export default class extends Controller {
7
+ static targets = ['details', 'summary', 'option', 'checkbox', 'menu']
8
+ static debounces = ['init']
9
+
10
+ connect() {
11
+ useDebounce(this, { wait: 50 })
12
+ useClickOutside(this)
13
+ this.init()
14
+ this.updateHiddenInputs()
15
+ }
16
+
17
+ clickOutside(event) {
18
+ this.detailsTarget.removeAttribute("open")
19
+ }
20
+
21
+ init() {
22
+ this.highlightFilter()
23
+ this.showSearch()
24
+ }
25
+
26
+ highlightFilter() {
27
+ const optionIsSelected = this.isAnyCheckboxChecked()
28
+ this.summaryTarget.classList.toggle(BG_GRAY, optionIsSelected)
29
+ }
30
+
31
+ showSearch() {
32
+ if (this.isAnyCheckboxChecked()) {
33
+ this.dispatch("showSearch", { detail: { avoidFocus: true } })
34
+ }
35
+ }
36
+
37
+ filterOptions(event) {
38
+ const query = event.currentTarget.value.toLowerCase()
39
+ this.optionTargets.forEach((option) => {
40
+ option.style.display = option.textContent.toLowerCase().includes(query) ? 'block' : 'none'
41
+ })
42
+ }
43
+
44
+ search() {
45
+ this.dispatch("search")
46
+ this.highlightFilter()
47
+ }
48
+
49
+ updateHiddenInputs() {
50
+ this.checkboxTargets.forEach((checkbox) => {
51
+ const hiddenElements = checkbox.parentElement.querySelectorAll("input[type='hidden']")
52
+ checkbox.checked
53
+ ? hiddenElements.forEach(e => e.removeAttribute("disabled"))
54
+ : hiddenElements.forEach(e => e.setAttribute("disabled", true))
55
+ })
56
+ }
57
+
58
+ sortCheckboxes() {
59
+ const checkboxes = this.checkboxTargets
60
+
61
+ checkboxes.sort((a, b) => {
62
+ if (a.checked && !b.checked) return -1
63
+ if (!a.checked && b.checked) return 1
64
+ return 0
65
+ }).forEach(checkbox => {
66
+ this.menuTarget.appendChild(checkbox.closest('div'))
67
+ })
68
+ }
69
+
70
+ isAnyCheckboxChecked() {
71
+ return this.checkboxTargets.some(checkbox => checkbox.checked)
72
+ }
73
+ }
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SolidusAdmin::UI::Table::RansackFilter::Component < SolidusAdmin::BaseComponent
4
+ # @param presentation [String] The label for the filter.
5
+ # @param search_param [String] The search parameter for the filter query.
6
+ # @param combinator [String] The combining logic for filter options.
7
+ # @param attribute [String] The database attribute the filter is based on.
8
+ # @param predicate [String] The comparison logic for the filter (e.g., "eq" for equals).
9
+ # @param options [Proc] A callable that returns filter options.
10
+ # @param index [Integer] The index of the filter.
11
+ # @param form [String] The form in which the filter resides.
12
+ def initialize(
13
+ presentation:,
14
+ combinator:, attribute:, predicate:, options:, form:, index:, search_param: :q
15
+ )
16
+ @presentation = presentation
17
+ @group = "#{search_param}[g][#{index}]"
18
+ @combinator = build(:combinator, combinator)
19
+ @attribute = attribute
20
+ @predicate = predicate
21
+ @options = options
22
+ @form = form
23
+ @index = index
24
+ end
25
+
26
+ def before_render
27
+ @selections = @options.map.with_index do |(label, value), opt_index|
28
+ Selection.new(
29
+ "#{stimulus_id}--#{label}-#{value}".parameterize,
30
+ label,
31
+ build(:attribute, @attribute, opt_index),
32
+ build(:predicate, @predicate, opt_index),
33
+ build(:option, value, opt_index),
34
+ checked?(value)
35
+ )
36
+ end
37
+ end
38
+
39
+ # Builds form attributes for filter options.
40
+ #
41
+ # @param type [Symbol] The type of the form attribute.
42
+ # @param value [String] The value of the form attribute.
43
+ # @param opt_index [Integer] The index of the option, if applicable.
44
+ # @return [FormAttribute] The built form attribute.
45
+ def build(type, value, opt_index = nil)
46
+ suffix = SUFFIXES[type] % { index: opt_index || @index }
47
+ Attribute.new("#{@group}#{suffix}", value)
48
+ end
49
+
50
+ # Determines if a given value should be checked based on the params.
51
+ #
52
+ # @param value [String] The value of the checkbox.
53
+ # @return [Boolean] Returns true if the checkbox should be checked, false otherwise.
54
+ def checked?(value)
55
+ conditions = params.dig(:q, :g, @index.to_s, :c)
56
+ conditions && conditions.values.any? { |c| c[:v]&.include?(value.to_s) }
57
+ end
58
+
59
+ SUFFIXES = {
60
+ combinator: '[m]',
61
+ attribute: '[c][%<index>s][a][]',
62
+ predicate: '[c][%<index>s][p]',
63
+ option: '[c][%<index>s][v][]'
64
+ }
65
+
66
+ Selection = Struct.new(:id, :presentation, :attribute, :predicate, :option, :checked)
67
+ Attribute = Struct.new(:name, :value)
68
+ end
@@ -0,0 +1,3 @@
1
+ en:
2
+ search: Search
3
+ no_filter_options: No options available
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SolidusAdmin::UI::Table::Toolbar::Component < SolidusAdmin::BaseComponent
4
+ def initialize(**options)
5
+ @options = options
6
+ end
7
+
8
+ def call
9
+ tag.div(
10
+ content,
11
+ **@options,
12
+ class: "
13
+ min-h-14 p-2 bg-white border-b border-gray-100
14
+ justify-start items-center gap-2
15
+ visible:flex hidden:hidden
16
+ rounded-t-lg
17
+ #{@options[:class]}
18
+ "
19
+ )
20
+ end
21
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SolidusAdmin::UI::Thumbnail::Component < SolidusAdmin::BaseComponent
4
+ SIZES = {
5
+ s: 'h-6 w-6',
6
+ m: 'h-10 w-10',
7
+ l: 'h-20 w-20',
8
+ }.freeze
9
+
10
+ def initialize(icon: nil, size: :m, **attributes)
11
+ @icon = icon
12
+ @size = size
13
+ @attributes = attributes
14
+ end
15
+
16
+ def call
17
+ icon = if @icon
18
+ icon_tag(@icon, class: "bg-gray-25 fill-gray-700 #{SIZES[@size]} p-2")
19
+ else
20
+ tag.img(**@attributes, class: "object-contain #{SIZES[@size]}")
21
+ end
22
+
23
+ tag.div(icon, class: "
24
+ #{SIZES[@size]}
25
+ rounded border border-gray-100
26
+ bg-white overflow-hidden
27
+ content-box
28
+ #{@attributes[:class]}
29
+ ")
30
+ end
31
+
32
+ def self.for(record, **attrs)
33
+ case record
34
+ when Spree::PromotionAction then new(icon: "megaphone-line", **attrs)
35
+ when Spree::UnitCancel then new(icon: "close-circle-line", **attrs)
36
+ when Spree::TaxRate then new(icon: "percent-line", **attrs)
37
+ when Spree::LineItem then self.for(record.variant, **attrs)
38
+ when Spree::Product then self.for((record.images.first || record.master.images.first), **attrs)
39
+ when Spree::Variant then self.for((record.images.first || record.product), **attrs)
40
+ when Spree::Image then new(src: record.attachment&.url(:small), alt: record.alt, **attrs)
41
+ when Spree::Order then new(icon: "shopping-bag-line", **attrs)
42
+ when Spree::Shipment then new(icon: "truck-line", **attrs)
43
+ else new(icon: "question-line", **attrs)
44
+ end
45
+ end
46
+ end
@@ -1,21 +1,25 @@
1
1
  <div
2
2
  class="
3
- rounded inline-block px-3 py-2
3
+ flex items-center justify-between
4
+ rounded px-3 py-2
5
+ transform translate-y-full opacity-0 transition-all duration-500
6
+ pointer-events-auto
4
7
  <%= SCHEMES.fetch(@scheme.to_sym).join(' ') %>
5
8
  "
6
9
  data-controller="<%= stimulus_id %>"
7
- data-<%= stimulus_id %>-closing-class="transform opacity-0 transition duration-500"
10
+ data-<%= stimulus_id %>-animation-class="translate-y-full opacity-0"
8
11
  data-<%= stimulus_id %>-transition-value="500"
9
12
  role="dialog"
10
13
  aria-label="<%= t(".#{@scheme}_label") %>"
11
14
  aria-live="polite"
12
15
  >
13
- <%= icon_tag(@icon, class: 'inline-block w-[1.125rem] h-[1.125rem] mr-3 fill-current') if @icon %>
16
+ <%= icon_tag(@icon, class: 'w-[1.125rem] h-[1.125rem] mr-2 fill-current') if @icon %>
14
17
 
15
- <p class="inline-block body-tiny-bold"><%= @text %></p>
18
+ <p class="font-semibold text-xs leading-none"><%= @text %></p>
16
19
 
17
20
  <button
18
- class="inline-block ml-3 align-text-bottom"
21
+ type="button"
22
+ class="ml-2 align-text-bottom"
19
23
  title="<%= t('.close_text') %>"
20
24
  data-action="<%= stimulus_id %>#close"
21
25
  aria-label="<%= t('.close_text') %>"
@@ -2,16 +2,19 @@ import { Controller } from '@hotwired/stimulus'
2
2
 
3
3
  export default class extends Controller {
4
4
  static targets = ['closeButton']
5
- static classes = ['closing']
5
+ static classes = ['animation']
6
6
  static values = { transition: Number }
7
7
 
8
- connect () {
9
- // Give focus to the close button
10
- this.closeButtonTarget.focus();
8
+ connect() {
9
+ this.closeButtonTarget.focus()
10
+
11
+ requestAnimationFrame(() => {
12
+ this.element.classList.remove(...this.animationClasses)
13
+ })
11
14
  }
12
15
 
13
- close () {
14
- this.element.classList.add(...this.closingClasses);
16
+ close() {
17
+ this.element.classList.add(...this.animationClasses)
15
18
  setTimeout(() => this.element.remove(), this.transitionValue)
16
19
  }
17
20
  }
@@ -3,7 +3,7 @@
3
3
  class SolidusAdmin::UI::Toast::Component < SolidusAdmin::BaseComponent
4
4
  SCHEMES = {
5
5
  default: %w[
6
- bg-gray-800 text-white
6
+ bg-full-black text-white
7
7
  ],
8
8
  error: %w[
9
9
  bg-red-500 text-white
@@ -13,6 +13,6 @@ class SolidusAdmin::UI::Toast::Component < SolidusAdmin::BaseComponent
13
13
  def initialize(text:, icon: nil, scheme: :default)
14
14
  @text = text
15
15
  @icon = icon
16
- @scheme = scheme.to_sym
16
+ @scheme = scheme
17
17
  end
18
18
  end
@@ -3,7 +3,6 @@
3
3
  <%= tag.attributes(**@attributes) %>
4
4
  >
5
5
  <summary
6
- type="button"
7
6
  class="
8
7
  block
9
8
  w-[1rem]
@@ -12,27 +11,27 @@
12
11
  [&::marker]:hidden
13
12
  [&::-webkit-details-marker]:hidden
14
13
  "
15
- data-<%= stimulus_id %>-target="button"
16
14
  data-action="
17
- click:prevent-><%= stimulus_id %>#toggle
15
+ click-><%= stimulus_id %>#toggle
18
16
  keydown.esc@window-><%= stimulus_id %>#close
19
17
  "
20
18
  aria-label="<%= t('.get_help') %>"
21
19
  >
22
- <%= icon_tag("question-fill", class: "w-[1rem] h-[1rem] #{icon_theme_classes}") %>
20
+ <%= icon_tag("question-fill", class: "w-[1rem] h-[1rem] fill-gray-800") %>
23
21
  </summary>
24
22
 
25
23
  <div
26
24
  class="
27
25
  absolute
28
26
  inline-block
29
- w-[9rem]
27
+ w-48
30
28
  px-[0.75rem]
31
- body-tiny-bold
29
+ font-semibold text-xs
32
30
  rounded
33
31
  z-10
34
- <%= bubble_position_classes %>
35
- <%= bubble_theme_classes %>
32
+ text-white
33
+ bg-gray-800
34
+ <%= POSITIONS.fetch(@position)[:bubble] %>
36
35
  "
37
36
  data-<%= stimulus_id %>-target="bubble"
38
37
  >
@@ -43,9 +42,14 @@
43
42
  block
44
43
  bg-inherit
45
44
  py-[0.5rem]
46
- <%= bubble_arrow_pseudo_element %>
45
+ before:content['']
46
+ before:absolute
47
+ before:w-[0.375rem]
48
+ before:h-[0.375rem]
49
+ before:rotate-45
50
+ before:bg-inherit
51
+ <%= POSITIONS.fetch(@position)[:arrow] %>
47
52
  "
48
- data-<%= stimulus_id %>-target="content"
49
53
  >
50
54
  <%= @text %>
51
55
  </span>
@@ -2,10 +2,11 @@ import { Controller } from '@hotwired/stimulus'
2
2
  import { useClickOutside } from 'stimulus-use'
3
3
 
4
4
  export default class extends Controller {
5
- static targets = ['button', 'bubble', 'content']
5
+ static targets = ['bubble']
6
6
 
7
7
  connect () {
8
8
  useClickOutside(this)
9
+ this.open = false
9
10
  }
10
11
 
11
12
  clickOutside () {
@@ -13,14 +14,31 @@ export default class extends Controller {
13
14
  }
14
15
 
15
16
  toggle (e) {
16
- this.element.open = !this.element.open
17
+ e.preventDefault()
18
+ this.open = !this.open
19
+ this.render()
17
20
  }
18
21
 
19
22
  open () {
20
- this.element.open = true
23
+ this.open = true
24
+ this.render()
21
25
  }
22
26
 
23
27
  close () {
24
- this.element.open = false
28
+ this.open = false
29
+ this.render()
30
+ }
31
+
32
+ render() {
33
+ const needsPositioning = this.open && !this.element.open
34
+ this.element.open = this.open
35
+
36
+ if (needsPositioning) {
37
+ const bubbleRect = this.bubbleTarget.getBoundingClientRect()
38
+ if (bubbleRect.right > window.innerWidth) this.bubbleTarget.style.left = `${window.innerWidth - bubbleRect.width}px`
39
+ if (bubbleRect.bottom > window.innerHeight) this.bubbleTarget.style.top = `${window.innerHeight - bubbleRect.height}px`
40
+ if (bubbleRect.left < 0) this.bubbleTarget.style.left = '0px'
41
+ if (bubbleRect.top < 0) this.bubbleTarget.style.top = '0px'
42
+ }
25
43
  }
26
44
  }
@@ -1,98 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class SolidusAdmin::UI::Toggletip::Component < SolidusAdmin::BaseComponent
4
- # Icon size: 1rem
5
- # Arrow size: 0.375rem
6
- # Banner padding x: 0.75rem
7
4
  POSITIONS = {
8
- up: {
9
- arrow: %w[before:top-0 before:left-1/2 before:translate-y-[-50%] before:translate-x-[-50%]],
10
- bubble: %w[translate-x-[calc(-50%+(1rem/2))] translate-y-[calc(0.375rem/2)]]
5
+ below: {
6
+ arrow: "before:top-0 before:left-1/2 before:translate-y-[-50%] before:translate-x-[-50%]",
7
+ bubble: "translate-x-[calc(-50%+(1rem/2))] translate-y-[calc(0.375rem/2)]"
11
8
  },
12
- up_right: {
13
- arrow: %w[before:top-0 before:right-0 before:translate-y-[-50%]],
14
- bubble: %w[translate-x-[calc(-100%+0.75rem+(1rem/2)+(0.375rem/2))] translate-y-[calc(0.375rem/2)]]
9
+ above: {
10
+ arrow: "before:bottom-0 before:left-1/2 before:translate-y-[50%] before:translate-x-[-50%]",
11
+ bubble: "translate-x-[calc(-50%+(1rem/2))] translate-y-[calc(-100%-1rem-(0.375rem/2))]"
15
12
  },
16
- right: {
17
- arrow: %w[before:top-1/2 before:right-0 before:translate-y-[-50%] before:translate-x-[0.93rem]],
18
- bubble: %w[translate-x-[calc(-100%+(-0.375rem/2))] translate-y-[calc(-50%-(1rem/2))]]
19
- },
20
- down_right: {
21
- arrow: %w[before:bottom-0 before:right-0 before:translate-y-[50%]],
22
- bubble: %w[translate-x-[calc(-100%+0.75rem+(1rem/2)+(0.376rem/2))] translate-y-[calc(-100%-1rem-(0.375rem/2))]]
23
- },
24
- down: {
25
- arrow: %w[before:bottom-0 before:left-1/2 before:translate-y-[50%] before:translate-x-[-50%]],
26
- bubble: %w[translate-x-[calc(-50%+(1rem/2))] translate-y-[calc(-100%-1rem-(0.375rem/2))]]
27
- },
28
- down_left: {
29
- arrow: %w[before:bottom-0 before:left-0 before:translate-y-[50%]],
30
- bubble: %w[translate-x-[calc(-1rem/2)] translate-y-[calc(-100%-0.75rem-0.375rem)]]
31
- },
32
- left: {
33
- arrow: %w[before:top-1/2 before:left-0 before:translate-y-[-50%] before:translate-x-[-0.93rem]],
34
- bubble: %w[translate-x-[calc(1rem+(0.375rem/2))] translate-y-[calc(-50%-(1rem/2))]]
35
- },
36
- up_left: {
37
- arrow: %w[before:top-0 before:left-0 before:translate-y-[-50%]],
38
- bubble: %w[translate-x-[calc(-0.75rem+0.375rem)] translate-y-[calc(0.375rem/2)]]
39
- },
40
- none: {
41
- arrow: %w[before:hidden],
42
- bubble: %w[translate-x-[calc(-50%+0.75rem)]]
43
- }
44
- }.freeze
45
-
46
- THEMES = {
47
- light: {
48
- icon: %w[fill-gray-500],
49
- bubble: %w[text-gray-800 bg-gray-50]
50
- },
51
- dark: {
52
- icon: %w[fill-gray-800],
53
- bubble: %w[text-white bg-gray-800]
54
- }
55
13
  }.freeze
56
14
 
57
- # @param text [String] The toggletip text
58
- # @param position [Symbol] The position of the arrow in relation to the
59
- # toggletip. The latter will be positioned accordingly in relation to the
60
- # help icon. Defaults to `:up`. See `POSITIONS` for available options.
61
- # @param theme [Symbol] The theme of the toggletip. Defaults to `:light`. See
62
- # `THEMES` for available options.
63
- def initialize(text:, position: :down, theme: :light, **attributes)
15
+ def initialize(text:, position: :above, **attributes)
64
16
  @text = text
65
17
  @position = position
66
- @theme = theme
67
18
  @attributes = attributes
68
- @attributes[:class] = [
69
- "relative inline-block",
70
- @attributes[:class],
71
- ].join(" ")
72
- end
73
-
74
- def icon_theme_classes
75
- THEMES.fetch(@theme)[:icon].join(" ")
76
- end
77
-
78
- def bubble_theme_classes
79
- THEMES.fetch(@theme)[:bubble].join(" ")
80
- end
81
-
82
- def bubble_position_classes
83
- POSITIONS.fetch(@position)[:bubble].join(" ")
84
- end
85
-
86
- def bubble_arrow_pseudo_element
87
- (
88
- [
89
- "before:content['']",
90
- "before:absolute",
91
- "before:w-[0.375rem]",
92
- "before:h-[0.375rem]",
93
- "before:rotate-45",
94
- "before:bg-inherit",
95
- ] + POSITIONS.fetch(@position)[:arrow]
96
- ).join(" ")
19
+ @attributes[:class] = "relative inline-block #{@attributes[:class]}"
97
20
  end
98
21
  end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SolidusAdmin::Users::Index::Component < SolidusAdmin::UI::Pages::Index::Component
4
+ def model_class
5
+ Spree.user_class
6
+ end
7
+
8
+ def search_key
9
+ :email_cont
10
+ end
11
+
12
+ def search_url
13
+ solidus_admin.users_path
14
+ end
15
+
16
+ def row_url(user)
17
+ spree.admin_user_path(user)
18
+ end
19
+
20
+ def page_actions
21
+ render component("ui/button").new(
22
+ tag: :a,
23
+ text: t('.add'),
24
+ href: spree.new_admin_user_path,
25
+ icon: "add-line",
26
+ )
27
+ end
28
+
29
+ def batch_actions
30
+ [
31
+ {
32
+ display_name: t('.batch_actions.delete'),
33
+ action: solidus_admin.users_path,
34
+ method: :delete,
35
+ icon: 'delete-bin-7-line',
36
+ },
37
+ ]
38
+ end
39
+
40
+ def scopes
41
+ [
42
+ { name: :customers, label: t('.scopes.customers'), default: true },
43
+ { name: :admin, label: t('.scopes.admin') },
44
+ { name: :with_orders, label: t('.scopes.with_orders') },
45
+ { name: :without_orders, label: t('.scopes.without_orders') },
46
+ { name: :all, label: t('.scopes.all') },
47
+ ]
48
+ end
49
+
50
+ def filters
51
+ [
52
+ {
53
+ presentation: Spree::Role.model_name.human.pluralize,
54
+ attribute: "spree_roles_id",
55
+ predicate: "in",
56
+ options: Spree::Role.pluck(:name, :id)
57
+ }
58
+ ]
59
+ end
60
+
61
+ def columns
62
+ [
63
+ {
64
+ header: :email,
65
+ data: :email,
66
+ },
67
+ {
68
+ header: :roles,
69
+ data: ->(user) do
70
+ roles = user.spree_roles.presence || [Spree::Role.new(name: 'customer')]
71
+ safe_join(roles.map {
72
+ color =
73
+ case _1.name
74
+ when 'admin' then :blue
75
+ when 'customer' then :green
76
+ else :graphite_light
77
+ end
78
+ render component('ui/badge').new(name: _1.name, color: color)
79
+ })
80
+ end,
81
+ },
82
+ {
83
+ header: :order_count,
84
+ data: ->(user) { user.order_count },
85
+ },
86
+ {
87
+ header: :lifetime_value,
88
+ data: -> { _1.display_lifetime_value.to_html },
89
+ },
90
+ {
91
+ header: :created_at,
92
+ data: ->(user) { l(user.created_at.to_date, format: :long) },
93
+ },
94
+ ]
95
+ end
96
+ end