solidus_admin 0.0.0 → 0.0.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 (122) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE +7 -0
  3. data/README.md +31 -0
  4. data/Rakefile +21 -0
  5. data/app/assets/config/solidus_admin_manifest.js +4 -0
  6. data/app/assets/images/solidus_admin/.keep +0 -0
  7. data/app/assets/images/solidus_admin/arrow_down_s_fill_gray_700.svg +3 -0
  8. data/app/assets/images/solidus_admin/arrow_down_s_fill_red_400.svg +3 -0
  9. data/app/assets/images/solidus_admin/arrow_right_up_line.svg +5 -0
  10. data/app/assets/images/solidus_admin/favicon.ico +0 -0
  11. data/app/assets/images/solidus_admin/remixicon.symbol.svg +11 -0
  12. data/app/assets/stylesheets/solidus_admin/application.css +3 -0
  13. data/app/assets/stylesheets/solidus_admin/application.tailwind.css.erb +35 -0
  14. data/app/components/solidus_admin/base_component.rb +42 -0
  15. data/app/components/solidus_admin/feedback/component.html.erb +11 -0
  16. data/app/components/solidus_admin/feedback/component.rb +4 -0
  17. data/app/components/solidus_admin/feedback/component.yml +5 -0
  18. data/app/components/solidus_admin/orders/index/component.html.erb +31 -0
  19. data/app/components/solidus_admin/orders/index/component.rb +118 -0
  20. data/app/components/solidus_admin/orders/index/component.yml +13 -0
  21. data/app/components/solidus_admin/products/index/component.html.erb +30 -0
  22. data/app/components/solidus_admin/products/index/component.rb +126 -0
  23. data/app/components/solidus_admin/products/index/component.yml +13 -0
  24. data/app/components/solidus_admin/products/show/component.html.erb +149 -0
  25. data/app/components/solidus_admin/products/show/component.js +9 -0
  26. data/app/components/solidus_admin/products/show/component.rb +26 -0
  27. data/app/components/solidus_admin/products/show/component.yml +17 -0
  28. data/app/components/solidus_admin/products/status/component.rb +31 -0
  29. data/app/components/solidus_admin/products/status/component.yml +3 -0
  30. data/app/components/solidus_admin/sidebar/account_nav/component.html.erb +67 -0
  31. data/app/components/solidus_admin/sidebar/account_nav/component.rb +15 -0
  32. data/app/components/solidus_admin/sidebar/account_nav/component.yml +3 -0
  33. data/app/components/solidus_admin/sidebar/component.html.erb +39 -0
  34. data/app/components/solidus_admin/sidebar/component.js +14 -0
  35. data/app/components/solidus_admin/sidebar/component.rb +21 -0
  36. data/app/components/solidus_admin/sidebar/component.yml +2 -0
  37. data/app/components/solidus_admin/sidebar/item/component.html.erb +26 -0
  38. data/app/components/solidus_admin/sidebar/item/component.rb +27 -0
  39. data/app/components/solidus_admin/skip_link/component.rb +24 -0
  40. data/app/components/solidus_admin/skip_link/component.yml +2 -0
  41. data/app/components/solidus_admin/ui/badge/component.rb +34 -0
  42. data/app/components/solidus_admin/ui/button/component.rb +101 -0
  43. data/app/components/solidus_admin/ui/forms/checkbox/component.rb +42 -0
  44. data/app/components/solidus_admin/ui/forms/field/component.html.erb +28 -0
  45. data/app/components/solidus_admin/ui/forms/field/component.rb +72 -0
  46. data/app/components/solidus_admin/ui/forms/input/component.js +16 -0
  47. data/app/components/solidus_admin/ui/forms/input/component.rb +99 -0
  48. data/app/components/solidus_admin/ui/forms/switch/component.rb +47 -0
  49. data/app/components/solidus_admin/ui/icon/component.rb +25 -0
  50. data/app/components/solidus_admin/ui/icon/names.txt +2494 -0
  51. data/app/components/solidus_admin/ui/panel/component.html.erb +36 -0
  52. data/app/components/solidus_admin/ui/panel/component.js +14 -0
  53. data/app/components/solidus_admin/ui/panel/component.rb +19 -0
  54. data/app/components/solidus_admin/ui/panel/component.yml +4 -0
  55. data/app/components/solidus_admin/ui/tab/component.rb +43 -0
  56. data/app/components/solidus_admin/ui/table/component.html.erb +170 -0
  57. data/app/components/solidus_admin/ui/table/component.js +118 -0
  58. data/app/components/solidus_admin/ui/table/component.rb +150 -0
  59. data/app/components/solidus_admin/ui/table/component.yml +11 -0
  60. data/app/components/solidus_admin/ui/table/pagination/component.html.erb +28 -0
  61. data/app/components/solidus_admin/ui/table/pagination/component.rb +14 -0
  62. data/app/components/solidus_admin/ui/table/pagination/component.yml +3 -0
  63. data/app/components/solidus_admin/ui/toast/component.html.erb +26 -0
  64. data/app/components/solidus_admin/ui/toast/component.js +17 -0
  65. data/app/components/solidus_admin/ui/toast/component.rb +18 -0
  66. data/app/components/solidus_admin/ui/toast/component.yml +4 -0
  67. data/app/components/solidus_admin/ui/toggletip/component.html.erb +53 -0
  68. data/app/components/solidus_admin/ui/toggletip/component.js +26 -0
  69. data/app/components/solidus_admin/ui/toggletip/component.rb +98 -0
  70. data/app/components/solidus_admin/ui/toggletip/component.yml +2 -0
  71. data/app/controllers/solidus_admin/accounts_controller.rb +11 -0
  72. data/app/controllers/solidus_admin/authentication_adapters/backend.rb +26 -0
  73. data/app/controllers/solidus_admin/base_controller.rb +21 -0
  74. data/app/controllers/solidus_admin/controller_helpers/authentication.rb +31 -0
  75. data/app/controllers/solidus_admin/controller_helpers/authorization.rb +29 -0
  76. data/app/controllers/solidus_admin/controller_helpers/locale.rb +32 -0
  77. data/app/controllers/solidus_admin/orders_controller.rb +21 -0
  78. data/app/controllers/solidus_admin/products_controller.rb +93 -0
  79. data/app/helpers/solidus_admin/components_helper.rb +9 -0
  80. data/app/helpers/solidus_admin/layout_helper.rb +18 -0
  81. data/app/javascript/solidus_admin/application.js +2 -0
  82. data/app/javascript/solidus_admin/controllers/application.js +9 -0
  83. data/app/javascript/solidus_admin/controllers/components.js +35 -0
  84. data/app/javascript/solidus_admin/controllers/hello_controller.js +7 -0
  85. data/app/javascript/solidus_admin/controllers/index.js +14 -0
  86. data/app/javascript/solidus_admin/utils.js +8 -0
  87. data/app/views/layouts/solidus_admin/application.html.erb +30 -0
  88. data/app/views/layouts/solidus_admin/preview.html.erb +10 -0
  89. data/app/views/solidus_admin/.keep +0 -0
  90. data/bin/rails +13 -0
  91. data/config/importmap.rb +13 -0
  92. data/config/locales/main_nav.en.yml +13 -0
  93. data/config/locales/orders.en.yml +4 -0
  94. data/config/locales/products.en.yml +10 -0
  95. data/config/routes.rb +13 -0
  96. data/config/solidus_admin/tailwind.config.js.erb +95 -0
  97. data/docs/customizing_main_navigation.md +42 -0
  98. data/docs/customizing_tailwind.md +78 -0
  99. data/docs/customizing_view_components.md +153 -0
  100. data/lib/generators/solidus_admin/component/USAGE +13 -0
  101. data/lib/generators/solidus_admin/component/component_generator.rb +130 -0
  102. data/lib/generators/solidus_admin/component/templates/component.html.erb.tt +3 -0
  103. data/lib/generators/solidus_admin/component/templates/component.js.tt +14 -0
  104. data/lib/generators/solidus_admin/component/templates/component.rb.tt +14 -0
  105. data/lib/generators/solidus_admin/component/templates/component.yml.tt +4 -0
  106. data/lib/generators/solidus_admin/component/templates/component_preview.rb.tt +15 -0
  107. data/lib/generators/solidus_admin/component/templates/component_preview_overview.html.erb +7 -0
  108. data/lib/generators/solidus_admin/component/templates/component_spec.rb.tt +16 -0
  109. data/lib/generators/solidus_admin/install/install_generator.rb +44 -0
  110. data/lib/generators/solidus_admin/install/templates/config/initializers/solidus_admin.rb +44 -0
  111. data/lib/solidus_admin/configuration.rb +217 -0
  112. data/lib/solidus_admin/engine.rb +67 -0
  113. data/lib/solidus_admin/importmap.rb +26 -0
  114. data/lib/solidus_admin/main_nav_item.rb +97 -0
  115. data/lib/solidus_admin/preview.rb +81 -0
  116. data/lib/solidus_admin/tailwindcss.rb +58 -0
  117. data/lib/solidus_admin/version.rb +5 -0
  118. data/lib/solidus_admin.rb +15 -0
  119. data/lib/tasks/importmap.rake +10 -0
  120. data/lib/tasks/tailwindcss.rake +55 -0
  121. data/solidus_admin.gemspec +35 -0
  122. metadata +255 -18
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ SolidusAdmin::Config.configure do |config|
4
+ # Path to the logo used in the admin interface.
5
+ #
6
+ # It needs to be a path to an image file accessible by Sprockets.
7
+ # config.logo_path = "my_own_logo.svg"
8
+
9
+ # Add custom paths for TailwindCSS to scan for styles. By default, it already
10
+ # includes the following paths:
11
+ # - public/solidus_admin/*.html
12
+ # - app/helpers/solidus_admin/**/*.rb
13
+ # - app/assets/javascripts/solidus_admin/**/*.js
14
+ # - app/views/solidus_admin/**/*.{erb,haml,html,slim}
15
+ # - app/components/solidus_admin/**/*.{rb,erb,haml,html,slim,js}
16
+ #
17
+ # config.tailwind_content << Rails.root.join("app/my/custom/path/**.rb")
18
+
19
+ # Append custom stylesheets to be compiled by TailwindCSS.
20
+ # config.tailwind_stylesheets << Rails.root.join("app/my/custom/path/style.css")
21
+
22
+ # Add custom folder paths to watch for changes to trigger a cache sweep forcing a
23
+ # regeneration of the importmap.
24
+ # config.importmap_cache_sweepers << Rails.root.join("app/javascript/my_admin_components")
25
+
26
+ # Add custom paths to importmap files to be loaded.
27
+ # config.importmap_paths << Rails.root.join("config/solidus_admin_importmap.rb")
28
+ #
29
+ # Configure the main navigation.
30
+ # See SolidusAdmin::MainNavItem for more details.
31
+ # config.menu_items << {
32
+ # key: :my_custom_link,
33
+ # route: :my_custom_link_path,
34
+ # icon: "solidus_admin/price-tag-3-line.svg",
35
+ # position: 80,
36
+ # children: [
37
+ # {
38
+ # key: :my_custom_child_link,
39
+ # route: :my_custom_child_link_path,
40
+ # position: 10
41
+ # }
42
+ # ]
43
+ # }
44
+ end
@@ -0,0 +1,217 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spree/preferences/configuration'
4
+
5
+ module SolidusAdmin
6
+ # Configuration for the admin interface.
7
+ #
8
+ # Ensure requiring this file after the Rails application has been created,
9
+ # as some defaults depend on the application context.
10
+ class Configuration < Spree::Preferences::Configuration
11
+ ComponentNotFoundError = Class.new(NameError)
12
+ ENGINE_ROOT = File.expand_path("#{__dir__}/../..")
13
+
14
+ # Path to the logo used in the admin interface.
15
+ #
16
+ # It needs to be a path to an image file accessible by Sprockets.
17
+ # The default value is the Solidus logo that lives in the solidus_core gem.
18
+ preference :logo_path, :string, default: "logo/solidus.svg"
19
+
20
+ # The list of paths were Tailwind CSS classes are used.
21
+ #
22
+ # You can modify this list to include your own paths:
23
+ #
24
+ # SolidusAdmin::Config.tailwind_content << Rails.root.join("app/my/custom/path")
25
+ #
26
+ # Recompile with `bin/rails solidus_admin:tailwindcss:build` after changing this list.
27
+ #
28
+ # @see https://tailwindcss.com/docs/configuration#content
29
+ preference :tailwind_content, :array, default: [
30
+ "#{ENGINE_ROOT}/app/helpers/**/*.rb",
31
+ "#{ENGINE_ROOT}/app/assets/javascripts/**/*.js",
32
+ "#{ENGINE_ROOT}/app/views/**/*.erb",
33
+ "#{ENGINE_ROOT}/app/components/**/*.{rb,erb,js}",
34
+ "#{ENGINE_ROOT}/spec/components/previews/**/*.{erb,rb}",
35
+
36
+ Rails.root&.join("public/solidus_admin/*.html"),
37
+ Rails.root&.join("app/helpers/solidus_admin/**/*.rb"),
38
+ Rails.root&.join("app/assets/javascripts/solidus_admin/**/*.js"),
39
+ Rails.root&.join("app/views/solidus_admin/**/*.{erb,haml,html,slim}"),
40
+ Rails.root&.join("app/components/solidus_admin/**/*.{rb,erb,haml,html,slim,js}")
41
+ ].compact
42
+
43
+ # List of Tailwind CSS files to be combined into the final stylesheet.
44
+ #
45
+ # You can modify this list to include your own files:
46
+ #
47
+ # SolidusAdmin::Config.tailwind_stylesheets << Rails.root.join("app/assets/stylesheets/solidus_admin/application.tailwind.css")
48
+ #
49
+ # Recompile with `bin/rails solidus_admin:tailwindcss:build` after changing this list.
50
+ preference :tailwind_stylesheets, :array, default: []
51
+
52
+ # List of paths to watch for changes to trigger a cache sweep forcing a regeneration of the importmap.
53
+ #
54
+ # @see https://github.com/rails/importmap-rails#sweeping-the-cache-in-development-and-test
55
+ preference :importmap_cache_sweepers, :array, default: [
56
+ "#{ENGINE_ROOT}/app/assets/javascripts",
57
+ "#{ENGINE_ROOT}/app/javascript",
58
+ "#{ENGINE_ROOT}/app/components",
59
+ ]
60
+
61
+ # List of paths to importmap files to be loaded.
62
+ #
63
+ # @see https://github.com/rails/importmap-rails#composing-import-maps
64
+ preference :importmap_paths, :array, default: [
65
+ "#{ENGINE_ROOT}/config/importmap.rb",
66
+ ]
67
+
68
+ # @!attribute [rw] orders_per_page
69
+ # @return [Integer] The number of orders to display per page in the admin interface.
70
+ # This preference determines the pagination limit for the order listing.
71
+ # The default value is fetched from the Spree core configuration and currently set to 15.
72
+ preference :orders_per_page, :integer, default: Spree::Config[:orders_per_page]
73
+
74
+ # @!attribute [rw] order_search_key
75
+ # The key that specifies the attributes for searching orders within the admin interface.
76
+ # This preference controls which attributes of an order are used in search queries.
77
+ # By default, it is set to
78
+ # 'number_shipments_number_or_bill_address_name_or_email_order_promotions_promotion_code_value_cont',
79
+ # enabling a search across order number, shipment number, billing address name, email, and promotion code value.
80
+ # @return [String] The search key used to determine order attributes for search.
81
+ preference :order_search_key, :string, default: :number_or_shipments_number_or_bill_address_name_or_email_or_order_promotions_promotion_code_value_cont
82
+
83
+ # @!attribute [rw] products_per_page
84
+ # @return [Integer] The number of products to display per page in the admin interface.
85
+ # This preference determines the pagination limit for the product listing.
86
+ # The default value is fetched from the Spree core configuration and currently set to 10.
87
+ preference :products_per_page, :integer, default: Spree::Config[:admin_products_per_page]
88
+
89
+ # @!attribute [rw] product_search_key
90
+ # @return [String] The key to use when searching for products in the admin interface.
91
+ # This preference determines the product attribute to use for search.
92
+ # By default, it is set to 'name_or_variants_including_master_sku_cont',
93
+ # meaning it will search by product name or product variants sku.
94
+ preference :product_search_key, :string, default: :name_or_variants_including_master_sku_cont
95
+
96
+ preference :storefront_product_path_proc, :proc, default: ->(_version) {
97
+ ->(product) { "/products/#{product.slug}" }
98
+ }
99
+
100
+ def storefront_product_path(product)
101
+ storefront_product_path_proc.call(product)
102
+ end
103
+
104
+ # Gives access to the main navigation configuration
105
+ #
106
+ # @example
107
+ # SolidusAdmin::Config.menu_items << {
108
+ # key: :my_custom_link,
109
+ # route: :products_path,
110
+ # icon: "solidus_admin/price-tag-3-line.svg",
111
+ # position: 80
112
+ # }
113
+ #
114
+ # @api public
115
+ # @return [Array<Hash>]
116
+ def menu_items
117
+ @menu_items ||= [
118
+ {
119
+ key: "orders",
120
+ route: -> { spree.admin_orders_path },
121
+ icon: "inbox-line",
122
+ position: 10
123
+ },
124
+ {
125
+ key: "products",
126
+ route: :products_path,
127
+ icon: "price-tag-3-line",
128
+ position: 20,
129
+ children: [
130
+ {
131
+ key: "products",
132
+ route: -> { solidus_admin.products_path },
133
+ match_path: -> { _1.start_with?("/admin/products/") },
134
+ position: 0
135
+ },
136
+ {
137
+ key: "option_types",
138
+ route: -> { spree.admin_option_types_path },
139
+ position: 10
140
+ },
141
+ {
142
+ key: "property_types",
143
+ route: -> { spree.admin_properties_path },
144
+ position: 20
145
+ },
146
+ {
147
+ key: "taxonomies",
148
+ route: -> { spree.admin_taxonomies_path },
149
+ position: 30
150
+ },
151
+ {
152
+ key: "taxons",
153
+ route: -> { spree.admin_taxons_path },
154
+ position: 40
155
+ }
156
+ ]
157
+ },
158
+
159
+ {
160
+ key: "promotions",
161
+ route: -> { spree.admin_promotions_path },
162
+ icon: "megaphone-line",
163
+ position: 30,
164
+ },
165
+
166
+ {
167
+ key: "stock",
168
+ route: -> { spree.admin_stock_items_path },
169
+ icon: "stack-line",
170
+ position: 40
171
+ },
172
+
173
+ {
174
+ key: "users",
175
+ route: -> { spree.admin_users_path },
176
+ icon: "user-line",
177
+ position: 50
178
+ },
179
+
180
+ {
181
+ key: "settings",
182
+ route: -> { spree.admin_stores_path },
183
+ icon: "settings-line",
184
+ position: 60,
185
+ }
186
+ ]
187
+ end
188
+
189
+ def components
190
+ @components ||= Hash.new do |_h, k|
191
+ "solidus_admin/#{k}/component".classify.constantize
192
+ rescue NameError
193
+ prefix = "#{ENGINE_ROOT}/app/components/solidus_admin/"
194
+ suffix = "/component.rb"
195
+ dictionary = Dir["#{prefix}**#{suffix}"].map { _1.delete_prefix(prefix).delete_suffix(suffix) }
196
+ corrections = DidYouMean::SpellChecker.new(dictionary: dictionary).correct(k.to_s)
197
+
198
+ raise ComponentNotFoundError, "Unknown component #{k}#{DidYouMean.formatter.message_for(corrections)}"
199
+ end
200
+ end
201
+
202
+ # The method used to authenticate the user in the admin interface, it's expected to redirect the user to the login method
203
+ # in case the authentication fails.
204
+ preference :authentication_method, :string, default: :authenticate_solidus_backend_user!
205
+
206
+ # The method used to retrieve the current user in the admin interface.
207
+ preference :current_user_method, :string, default: :spree_current_user
208
+
209
+ # The path used to logout the user in the admin interface.
210
+ preference :logout_link_path, :string, default: '/admin/logout'
211
+
212
+ # The HTTP method used to logout the user in the admin interface.
213
+ preference :logout_link_method, :string, default: :delete
214
+ end
215
+ end
216
+
217
+ SolidusAdmin::Config = SolidusAdmin::Configuration.new
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stimulus-rails"
4
+ require "turbo-rails"
5
+ require "view_component"
6
+
7
+ module SolidusAdmin
8
+ class Engine < ::Rails::Engine
9
+ isolate_namespace SolidusAdmin
10
+
11
+ config.before_initialize do
12
+ require "solidus_admin/configuration"
13
+ end
14
+
15
+ config.autoload_paths << SolidusAdmin::Engine.root.join("spec/components/previews")
16
+
17
+ initializer "solidus_admin.view_component" do |app|
18
+ app.config.view_component.preview_paths << SolidusAdmin::Engine.root.join("spec/components/previews").to_s
19
+
20
+ app.config.to_prepare do
21
+ preview_controller_class = app.config.view_component.preview_controller.constantize
22
+
23
+ # This is needed to make the preview controller have access to the same
24
+ # set of helpers that are available to the Preview class.
25
+ preview_controller_class.include SolidusAdmin::Preview::ControllerHelper
26
+ end
27
+ end
28
+
29
+ initializer "solidus_admin.inflections" do
30
+ # Support for UI as an acronym
31
+ ActiveSupport::Inflector.inflections { |inflect| inflect.acronym 'UI' }
32
+ end
33
+
34
+ initializer "solidus_admin.importmap" do
35
+ SolidusAdmin::Config.importmap_paths.each { |path| SolidusAdmin.importmap.draw(path) }
36
+ end
37
+
38
+ initializer "solidus_admin.importmap.reloader" do |app|
39
+ Importmap::Reloader.new.tap do |reloader|
40
+ reloader.execute
41
+ app.reloaders << reloader
42
+ app.reloader.to_run { reloader.execute }
43
+ end
44
+ end
45
+
46
+ initializer "solidus_admin.assets" do |app|
47
+ app.config.assets.precompile += %w[solidus_admin_manifest.js]
48
+ end
49
+
50
+ initializer "solidus_admin.importmap.cache_sweeper" do |app|
51
+ if app.config.importmap.sweep_cache
52
+ SolidusAdmin.importmap.cache_sweeper(watches: SolidusAdmin::Config.importmap_cache_sweepers)
53
+
54
+ ActiveSupport.on_load(:action_controller_base) do
55
+ before_action { SolidusAdmin.importmap.cache_sweeper.execute_if_updated }
56
+ end
57
+ end
58
+ end
59
+
60
+ initializer "solidus_admin.importmap.assets" do |app|
61
+ app.config.assets.paths += [
62
+ SolidusAdmin::Engine.root.join("app/javascript"),
63
+ SolidusAdmin::Engine.root.join("app/components"),
64
+ ]
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'importmap-rails'
4
+
5
+ module SolidusAdmin::Importmap
6
+ class Reloader
7
+ delegate :execute_if_updated, :execute, :updated?, to: :updater
8
+
9
+ def reload!
10
+ importmap_paths.each { |path| SolidusAdmin.importmap.draw(path) }
11
+ end
12
+
13
+ private
14
+
15
+ def importmap_paths
16
+ SolidusAdmin::Config.importmap_paths
17
+ end
18
+
19
+ def updater
20
+ @updater ||= Rails.application.config.file_watcher.new(importmap_paths) { reload! }
21
+ end
22
+ end
23
+ end
24
+
25
+ SolidusAdmin.singleton_class.attr_accessor :importmap
26
+ SolidusAdmin.importmap = Importmap::Map.new
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusAdmin
4
+ # Encapsulates the data for a main navigation item.
5
+ class MainNavItem
6
+ # @!attribute [r] key
7
+ # @return [String] a unique identifier for this item
8
+ attr_reader :key
9
+
10
+ # @!attribute [r] icon
11
+ # @return [String] icon from RemixIcon to use for this item
12
+ attr_reader :icon
13
+
14
+ # @!attribute [r] position
15
+ # @return [Integer] the position of this item relative to its parent
16
+ attr_reader :position
17
+
18
+ # @!attribute [r] route
19
+ # @return [Symbol, Proc] the route to use for this item. When a symbol is
20
+ # given, it will be called on the solidus_admin url helpers. When a proc is
21
+ # given, it will be evaluated in a context that has access to the
22
+ # solidus url helpers.
23
+ # @see #path
24
+ attr_reader :route
25
+
26
+ # @api private
27
+ attr_reader :children, :top_level
28
+
29
+ def initialize(
30
+ key:,
31
+ position:,
32
+ route:,
33
+ match_path: nil,
34
+ icon: nil,
35
+ children: [],
36
+ top_level: true
37
+ )
38
+ @key = key
39
+ @icon = icon
40
+ @position = position
41
+ @children = children
42
+ @top_level = top_level
43
+ @route = route
44
+ @match_path = match_path
45
+ end
46
+
47
+ def name
48
+ I18n.t("solidus_admin.main_nav.#{key}", default: key.to_s.humanize)
49
+ end
50
+
51
+ # @return [Boolean] whether this item has any children
52
+ def children?
53
+ @children.any?
54
+ end
55
+
56
+ # @param url_helpers [#solidus_admin, #spree] context object giving access
57
+ # to url helpers
58
+ # @return [String] the path for this item
59
+ def path(url_helpers)
60
+ case @route
61
+ when Symbol
62
+ url_helpers.solidus_admin.public_send(@route)
63
+ when Proc
64
+ url_helpers.instance_exec(&@route)
65
+ end
66
+ end
67
+
68
+ # Returns whether the item should be marked as current
69
+ #
70
+ # An item is considered the current one if its base path (that is, the path
71
+ # without any query parameters) matches the given full path.
72
+ #
73
+ # @param url_helpers [#solidus_admin, #spree] context object giving access
74
+ # to url helpers
75
+ # @param fullpath [String] the full path of the current request
76
+ # @return [Boolean]
77
+ def current?(url_helpers, fullpath)
78
+ path(url_helpers) == fullpath.gsub(/\?.*$/, '')
79
+ end
80
+
81
+ # Returns whether the item should be marked as active
82
+ #
83
+ # An item is considered active when it is the current item or any of its
84
+ # children is active.
85
+ #
86
+ # @param url_helpers [#solidus_admin, #spree] context object giving access
87
+ # to url helpers
88
+ # @param fullpath [String] the full path of the current request
89
+ # @return [Boolean]
90
+ # @see #current?
91
+ def active?(url_helpers, fullpath)
92
+ current?(url_helpers, fullpath) ||
93
+ @match_path&.call(fullpath) ||
94
+ children.any? { |child| child.active?(url_helpers, fullpath) }
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This module will add all the necessary harness to a ViewComponent::Preview
4
+ # to be able to render Solidus components.
5
+ #
6
+ # Adds a `current_component` helper method that will return the the component class
7
+ # based on the preview class name. The component class name is inferred by removing
8
+ # the `Preview` suffix from the preview class name.
9
+ #
10
+ # Adds a `helpers` method that will return the helpers module from SolidusAdmin::BaseController
11
+ # making them available to the preview class (which is not a standard controller). All helpers
12
+ # are available to the preview class via `method_missing`.
13
+ #
14
+ # @example
15
+ # class SolidusAdmin::UI::Badge::ComponentPreview < ViewComponent::Preview
16
+ # include SolidusAdmin::Preview
17
+ #
18
+ # def default
19
+ # render current_component.new(name: time_ago_in_words(1.day.ago))
20
+ # end
21
+ # end
22
+ #
23
+ module SolidusAdmin::Preview
24
+ extend ActiveSupport::Concern
25
+
26
+ module ControllerHelper
27
+ extend ActiveSupport::Concern
28
+
29
+ included do
30
+ include SolidusAdmin::ControllerHelpers::Authentication
31
+ helper ActionView::Helpers
32
+ helper SolidusAdmin::ComponentsHelper
33
+ helper_method :current_component
34
+ end
35
+
36
+ private
37
+
38
+ def spree_current_user
39
+ Spree::LegacyUser.new(email: "admin@example.com")
40
+ end
41
+
42
+ def authenticate_solidus_backend_user!
43
+ # noop
44
+ end
45
+
46
+ def current_component
47
+ @current_component ||= begin
48
+ # Lookbook sets the @preview instance variable with a PreviewEntity instance, while ViewComponent uses the preview class.
49
+ # Lookbook's PreviewEntity has `#preview_class_name` as part of its public api.
50
+ # Since we want to support both ways or rendering components, we need to check.
51
+ preview_class_name = @preview.respond_to?(:preview_class_name) ? @preview.preview_class_name : @preview.name
52
+ preview_class_name.chomp("Preview").constantize
53
+ end
54
+ end
55
+ end
56
+
57
+ included do
58
+ layout "solidus_admin/preview"
59
+
60
+ delegate :helpers, to: SolidusAdmin::BaseController
61
+ private :helpers # hide it from preview "example" methods
62
+ end
63
+
64
+ def current_component
65
+ @current_component ||= self.class.name.chomp("Preview").constantize
66
+ end
67
+
68
+ private
69
+
70
+ def method_missing(name, ...)
71
+ if helpers.respond_to?(name)
72
+ helpers.send(name, ...)
73
+ else
74
+ super
75
+ end
76
+ end
77
+
78
+ def respond_to_missing?(name, include_private = false)
79
+ helpers.respond_to?(name) || super
80
+ end
81
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "tailwindcss-rails"
4
+ require "fileutils"
5
+
6
+ module SolidusAdmin
7
+ # @api private
8
+ module Tailwindcss
9
+ module_function
10
+
11
+ def run(args = "")
12
+ config_file_path = compile_to_tempfile(
13
+ [config_app_path, config_engine_path].find(&:exist?),
14
+ "tailwind.config.js"
15
+ )
16
+ stylesheet_file_path = compile_to_tempfile(
17
+ [stylesheet_app_path, stylesheet_engine_path].find(&:exist?),
18
+ "application.tailwind.css"
19
+ )
20
+
21
+ system "#{::Tailwindcss::Engine.root.join('exe/tailwindcss')} \
22
+ -i #{stylesheet_file_path} \
23
+ -o #{Rails.root.join('app/assets/builds/solidus_admin/tailwind.css')} \
24
+ -c #{config_file_path} \
25
+ #{args}"
26
+ end
27
+
28
+ def config_app_path
29
+ Rails.root.join("config/solidus_admin/tailwind.config.js.erb")
30
+ end
31
+
32
+ def config_engine_path
33
+ SolidusAdmin::Engine.root.join("config/solidus_admin/tailwind.config.js.erb")
34
+ end
35
+
36
+ def stylesheet_app_path
37
+ Rails.root.join("app/assets/stylesheets/solidus_admin/application.tailwind.css.erb")
38
+ end
39
+
40
+ def stylesheet_engine_path
41
+ SolidusAdmin::Engine.root.join("app/assets/stylesheets/solidus_admin/application.tailwind.css.erb")
42
+ end
43
+
44
+ def compile_to_tempfile(erb_path, name)
45
+ Rails.root.join("tmp/solidus_admin/#{name}").tap do |file|
46
+ content = ERB.new(File.read(erb_path)).result
47
+
48
+ file.dirname.mkpath
49
+ file.write(content)
50
+ end
51
+ end
52
+
53
+ def copy_file(src, dst)
54
+ FileUtils.mkdir_p(File.dirname(dst))
55
+ FileUtils.cp(src, dst)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SolidusAdmin
4
+ VERSION = "0.0.1"
5
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "solidus_core"
4
+ require "solidus_backend"
5
+
6
+ module SolidusAdmin
7
+ require "solidus_admin/version"
8
+ require "solidus_admin/importmap"
9
+ require "solidus_admin/main_nav_item"
10
+ require "solidus_admin/preview"
11
+ require "solidus_admin/tailwindcss"
12
+
13
+ require "solidus_admin/configuration"
14
+ require "solidus_admin/engine"
15
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :solidus_admin do
4
+ namespace :importmap do
5
+ desc "Render Solidus Admin's importmap JSON"
6
+ task json: :environment do
7
+ puts SolidusAdmin.importmap.to_json(resolver: ActionController::Base.helpers)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :solidus_admin do
4
+ namespace :tailwindcss do
5
+ require "solidus_admin/tailwindcss"
6
+
7
+ desc "Build Solidus Admin's Tailwind's css"
8
+ task build: :environment do
9
+ SolidusAdmin::Tailwindcss.run
10
+ end
11
+
12
+ desc <<~DESC
13
+ Watch and build Solidus Admin's Tailwind css on file changes
14
+
15
+ It needs to be re-run whenever:
16
+
17
+ - `SolidusAdmin::Config.tailwind_content` is updated
18
+ - `SolidusAdmin::Config.tailwind_stylesheets` is updated
19
+ - `bin/rails solidus_admin:tailwindcss:override_config` is run
20
+ - `bin/rails solidus_admin:tailwindcss:override_stylesheet` is run
21
+ - The override files are updated
22
+ DESC
23
+ task watch: :environment do
24
+ SolidusAdmin::Tailwindcss.run("-w")
25
+ end
26
+
27
+ desc <<~DESC
28
+ Override Solidus Admin's Tailwindcss configuration
29
+
30
+ It copies the config file from the engine to the app, so it can be customized.
31
+ DESC
32
+ task override_config: :environment do
33
+ SolidusAdmin::Tailwindcss.copy_file(
34
+ SolidusAdmin::Tailwindcss.config_engine_path,
35
+ SolidusAdmin::Tailwindcss.config_app_path
36
+ )
37
+ end
38
+
39
+ desc <<~DESC
40
+ Override Solidus Admin's Tailwind's stylesheet
41
+
42
+ It copies the stylesheet file from the engine to the app, so it can be customized.
43
+ DESC
44
+ task override_stylesheet: :environment do
45
+ SolidusAdmin::Tailwindcss.copy_file(
46
+ SolidusAdmin::Tailwindcss.stylesheet_engine_path,
47
+ SolidusAdmin::Tailwindcss.stylesheet_app_path
48
+ )
49
+ end
50
+ end
51
+ end
52
+
53
+ if Rake::Task.task_defined?("assets:precompile")
54
+ Rake::Task["assets:precompile"].enhance(["solidus_admin:tailwindcss:build"])
55
+ end