spree_admin 5.2.0.rc3 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/stylesheets/spree/admin/application.scss +1 -1
  3. data/app/assets/stylesheets/spree/admin/components/_dropdowns.scss +2 -0
  4. data/app/assets/stylesheets/spree/admin/components/_main.scss +2 -233
  5. data/app/assets/stylesheets/spree/admin/components/_sidebar.scss +693 -0
  6. data/app/assets/stylesheets/spree/admin/global/_variables.scss +1 -0
  7. data/app/assets/stylesheets/spree/admin/shared/_base.scss +7 -1
  8. data/app/assets/stylesheets/spree/admin/views/_dashboard.scss +14 -0
  9. data/app/controllers/spree/admin/integrations_controller.rb +1 -1
  10. data/app/controllers/spree/admin/metafields_controller.rb +1 -1
  11. data/app/controllers/spree/admin/page_blocks_controller.rb +1 -1
  12. data/app/controllers/spree/admin/payment_methods_controller.rb +1 -1
  13. data/app/controllers/spree/admin/promotion_actions_controller.rb +1 -1
  14. data/app/controllers/spree/admin/promotion_rules_controller.rb +1 -1
  15. data/app/controllers/spree/admin/promotions_controller.rb +1 -1
  16. data/app/controllers/spree/admin/reports_controller.rb +1 -1
  17. data/app/controllers/spree/admin/taxons_controller.rb +1 -1
  18. data/app/controllers/spree/admin/translations_controller.rb +1 -1
  19. data/app/helpers/spree/admin/base_helper.rb +1 -1
  20. data/app/helpers/spree/admin/drawer_helper.rb +6 -6
  21. data/app/helpers/spree/admin/dropdown_helper.rb +10 -2
  22. data/app/helpers/spree/admin/modal_helper.rb +2 -0
  23. data/app/helpers/spree/admin/navigation_helper.rb +46 -3
  24. data/app/helpers/spree/admin/orders_filters_helper.rb +1 -0
  25. data/app/helpers/spree/admin/promotion_actions_helper.rb +1 -1
  26. data/app/helpers/spree/admin/promotion_rules_helper.rb +1 -1
  27. data/app/helpers/spree/admin/translations_helper.rb +1 -1
  28. data/app/javascript/spree/admin/application.js +2 -1
  29. data/app/javascript/spree/admin/controllers/dropdown_controller.js +85 -14
  30. data/app/javascript/spree/admin/controllers/sidebar_controller.js +231 -0
  31. data/app/javascript/spree/admin/controllers/tooltip_controller.js +84 -31
  32. data/app/models/spree/admin/form_builder.rb +74 -16
  33. data/app/models/spree/admin/navigation/builder.rb +82 -0
  34. data/app/models/spree/admin/navigation/item.rb +177 -0
  35. data/app/models/spree/admin/navigation.rb +193 -0
  36. data/app/views/layouts/spree/admin.html.erb +1 -1
  37. data/app/views/spree/admin/assets/edit.html.erb +1 -1
  38. data/app/views/spree/admin/custom_domains/_form.html.erb +2 -14
  39. data/app/views/spree/admin/gift_cards/_filters.html.erb +26 -18
  40. data/app/views/spree/admin/gift_cards/index.html.erb +1 -1
  41. data/app/views/spree/admin/orders/_customer.html.erb +2 -2
  42. data/app/views/spree/admin/orders/_filters.html.erb +34 -25
  43. data/app/views/spree/admin/orders/_table_filter_dropdown.html.erb +1 -1
  44. data/app/views/spree/admin/page_blocks/forms/_image.html.erb +2 -5
  45. data/app/views/spree/admin/page_links/_form.html.erb +4 -13
  46. data/app/views/spree/admin/page_sections/forms/_header.html.erb +0 -2
  47. data/app/views/spree/admin/payments/_payment.html.erb +7 -0
  48. data/app/views/spree/admin/posts/_form.html.erb +1 -4
  49. data/app/views/spree/admin/posts/filters.html.erb +18 -8
  50. data/app/views/spree/admin/products/_bulk_operations.html.erb +1 -1
  51. data/app/views/spree/admin/products/_filters.html.erb +17 -6
  52. data/app/views/spree/admin/products/_table_filter_dropdown.html.erb +1 -1
  53. data/app/views/spree/admin/profile/edit.html.erb +9 -61
  54. data/app/views/spree/admin/promotions/_filters.html.erb +23 -13
  55. data/app/views/spree/admin/promotions/_table_filter_dropdown.html.erb +1 -1
  56. data/app/views/spree/admin/promotions/form/_settings.html.erb +2 -13
  57. data/app/views/spree/admin/refunds/_form.html.erb +1 -9
  58. data/app/views/spree/admin/return_authorizations/filters.html.erb +1 -1
  59. data/app/views/spree/admin/shared/_audit_nav.html.erb +2 -0
  60. data/app/views/spree/admin/shared/_calendar_range_picker.html.erb +2 -2
  61. data/app/views/spree/admin/shared/_content_header.html.erb +1 -1
  62. data/app/views/spree/admin/shared/_developers_nav.html.erb +2 -4
  63. data/app/views/spree/admin/shared/_header.html.erb +5 -7
  64. data/app/views/spree/admin/shared/_navigation.html.erb +5 -0
  65. data/app/views/spree/admin/shared/_navigation_item.html.erb +64 -0
  66. data/app/views/spree/admin/shared/_new_item_dropdown.html.erb +1 -1
  67. data/app/views/spree/admin/shared/_page_section_image.html.erb +2 -5
  68. data/app/views/spree/admin/shared/_page_section_logo.html.erb +1 -1
  69. data/app/views/spree/admin/shared/_returns_and_refunds_nav.html.erb +2 -3
  70. data/app/views/spree/admin/shared/_shipping_nav.html.erb +3 -2
  71. data/app/views/spree/admin/shared/_sidebar.html.erb +33 -7
  72. data/app/views/spree/admin/shared/_stock_nav.html.erb +6 -3
  73. data/app/views/spree/admin/shared/_tax_nav.html.erb +1 -2
  74. data/app/views/spree/admin/shared/_team_nav.html.erb +2 -3
  75. data/app/views/spree/admin/shared/_user_dropdown.html.erb +26 -17
  76. data/app/views/spree/admin/shared/sidebar/_customers_nav.html.erb +7 -0
  77. data/app/views/spree/admin/shared/sidebar/_orders_nav.html.erb +22 -2
  78. data/app/views/spree/admin/shared/sidebar/_products_nav.html.erb +21 -0
  79. data/app/views/spree/admin/shared/sidebar/_promotions_nav.html.erb +8 -0
  80. data/app/views/spree/admin/shared/sidebar/_returns_nav.html.erb +12 -0
  81. data/app/views/spree/admin/shared/sidebar/_store_dropdown.html.erb +3 -1
  82. data/app/views/spree/admin/shared/sidebar/_store_nav.html.erb +15 -1
  83. data/app/views/spree/admin/shared/sidebar/_storefront_nav.html.erb +25 -3
  84. data/app/views/spree/admin/shared/sortable_tree/_taxonomy.html.erb +1 -1
  85. data/app/views/spree/admin/stock_items/_filters.html.erb +18 -8
  86. data/app/views/spree/admin/stock_locations/_table_row.html.erb +1 -1
  87. data/app/views/spree/admin/stock_transfers/_filters.html.erb +19 -9
  88. data/app/views/spree/admin/storefront/edit.html.erb +2 -14
  89. data/app/views/spree/admin/stores/form/_basic.html.erb +2 -8
  90. data/app/views/spree/admin/stores/form/_emails.html.erb +1 -1
  91. data/app/views/spree/admin/tax_rates/_form.html.erb +1 -10
  92. data/app/views/spree/admin/taxons/_form.html.erb +2 -9
  93. data/app/views/spree/admin/themes/_theme.html.erb +1 -1
  94. data/app/views/spree/admin/translations/translation_rows/_permalink_field_row.html.erb +1 -12
  95. data/app/views/spree/admin/users/_filters.html.erb +23 -13
  96. data/config/initializers/spree_admin_navigation.rb +510 -0
  97. data/config/locales/en.yml +4 -0
  98. data/lib/generators/spree/admin/scaffold/templates/controller.rb.tt +3 -1
  99. data/lib/generators/spree/admin/scaffold/templates/views/_table_row.html.erb.tt +8 -6
  100. data/lib/spree/admin/engine.rb +64 -2
  101. data/lib/spree/admin/runtime_configuration.rb +1 -0
  102. data/lib/spree/admin.rb +20 -0
  103. metadata +17 -15
  104. data/app/assets/stylesheets/spree/admin/components/_offcanvas.scss +0 -26
  105. data/app/javascript/spree/admin/helpers/canvas.js +0 -29
  106. data/app/views/spree/admin/shared/_offcanvas_nav.html.erb +0 -12
@@ -0,0 +1,510 @@
1
+ # Default Admin Navigation Configuration
2
+ # This file defines the default sidebar and settings navigation for Spree Admin
3
+
4
+ Rails.application.config.after_initialize do
5
+ # ===============================================
6
+ # Sidebar Navigation
7
+ # ===============================================
8
+ sidebar_nav = Spree.admin.navigation.sidebar
9
+
10
+ # Getting Started (onboarding)
11
+ sidebar_nav.add :getting_started,
12
+ label: 'admin.getting_started',
13
+ url: :admin_getting_started_path,
14
+ icon: 'map',
15
+ position: 5,
16
+ if: -> { current_store && can?(:manage, current_store) && !current_store.setup_completed? },
17
+ badge: -> { "#{current_store.setup_tasks_done}/#{current_store.setup_tasks_total}" },
18
+ badge_class: 'badge-info',
19
+ active: -> { controller_name == 'dashboard' && action_name == 'getting_started' }
20
+
21
+ # Dashboard / Home
22
+ sidebar_nav.add :home,
23
+ label: :home,
24
+ url: :admin_path,
25
+ icon: 'home',
26
+ position: 10,
27
+ active: -> { controller_name == 'dashboard' && action_name == 'show' }
28
+
29
+ # Orders with submenu
30
+ sidebar_nav.add :orders,
31
+ label: :orders,
32
+ url: :admin_orders_path,
33
+ icon: 'inbox',
34
+ position: 20,
35
+ if: -> { can?(:manage, Spree::Order) },
36
+ badge: -> {
37
+ # Evaluated in view context with access to helper methods
38
+ ready_to_ship_orders_count if ready_to_ship_orders_count&.positive?
39
+ } do |orders|
40
+ # Orders to Fulfill submenu
41
+ orders.add :orders_to_fulfill,
42
+ label: 'admin.orders.orders_to_fulfill',
43
+ url: -> { spree.admin_orders_path(q: {shipment_state_not_in: [:shipped, :canceled]}) },
44
+ position: 10,
45
+ active: -> { controller_name == 'orders' && params.dig(:q, :shipment_state_not_in).present? },
46
+ if: -> {
47
+ ready_to_ship_orders_count&.positive?
48
+ },
49
+ badge: -> {
50
+ ready_to_ship_orders_count if ready_to_ship_orders_count&.positive?
51
+ }
52
+
53
+ # Draft Orders
54
+ orders.add :draft_orders,
55
+ label: :draft_orders,
56
+ url: :admin_checkouts_path,
57
+ position: 20,
58
+ active: -> { controller_name == 'checkouts' || (@order.present? && !@order.completed?) },
59
+ if: -> { can?(:manage, :checkouts) }
60
+ end
61
+
62
+ # Returns with submenu
63
+ sidebar_nav.add :returns,
64
+ label: :returns,
65
+ url: :admin_customer_returns_path,
66
+ icon: 'receipt-refund',
67
+ position: 25,
68
+ if: -> { can?(:manage, Spree::CustomerReturn) || can?(:manage, Spree::ReturnAuthorization) } do |returns|
69
+ # Return Authorizations
70
+ returns.add :return_authorizations,
71
+ label: :return_authorizations,
72
+ url: :admin_return_authorizations_path,
73
+ position: 10,
74
+ if: -> { can?(:manage, Spree::ReturnAuthorization) }
75
+ end
76
+
77
+ # Products with submenu
78
+ sidebar_nav.add :products,
79
+ label: :products,
80
+ url: :admin_products_path,
81
+ icon: 'package',
82
+ position: 30,
83
+ if: -> { can?(:manage, Spree::Product) } do |products|
84
+ # Stock
85
+ products.add :stock,
86
+ label: :stock,
87
+ url: :admin_stock_items_path,
88
+ position: 10,
89
+ active: -> { %w[stock_items stock_transfers].include?(controller_name) },
90
+ if: -> { can?(:manage, Spree::StockItem) || can?(:manage, Spree::StockTransfer) }
91
+
92
+ # Taxonomies
93
+ products.add :taxonomies,
94
+ label: :taxonomies,
95
+ url: :admin_taxonomies_path,
96
+ position: 20,
97
+ active: -> { %w[taxonomies taxons].include?(controller_name) },
98
+ if: -> { can?(:manage, Spree::Taxonomy) && can?(:manage, Spree::Taxon) }
99
+
100
+ # Options
101
+ products.add :options,
102
+ label: :options,
103
+ url: :admin_option_types_path,
104
+ position: 30,
105
+ active: -> { %w[option_types option_values].include?(controller_name) },
106
+ if: -> { can?(:manage, Spree::OptionType) }
107
+
108
+ # Properties
109
+ products.add :properties,
110
+ label: :properties,
111
+ url: :admin_properties_path,
112
+ position: 40,
113
+ if: -> { can?(:manage, Spree::Property) && Spree::Config.product_properties_enabled }
114
+ end
115
+
116
+ # Vendors (Enterprise Edition)
117
+ sidebar_nav.add :vendors,
118
+ label: :vendors,
119
+ url: 'https://spreecommerce.org/marketplace-ecommerce/',
120
+ icon: 'heart-handshake',
121
+ position: 35,
122
+ if: -> { can?(:manage, current_store) && !defined?(SpreeEnterprise) },
123
+ badge: 'Enterprise',
124
+ tooltip: 'Multi-Vendor Marketplace is available in the Enterprise Edition',
125
+ target: '_blank'
126
+
127
+ # Customers with submenu
128
+ sidebar_nav.add :customers,
129
+ label: :customers,
130
+ url: :admin_users_path,
131
+ icon: 'users',
132
+ position: 40,
133
+ if: -> { can?(:manage, Spree.user_class) } do |customers|
134
+ # Newsletter Subscribers
135
+ customers.add :newsletter_subscribers,
136
+ label: :newsletter_subscribers,
137
+ url: :admin_newsletter_subscribers_path,
138
+ position: 10
139
+ end
140
+
141
+ # Promotions with submenu
142
+ sidebar_nav.add :promotions,
143
+ label: :promotions,
144
+ url: :admin_promotions_path,
145
+ icon: 'discount',
146
+ position: 50,
147
+ if: -> { can?(:manage, Spree::Promotion) } do |promotions|
148
+ # Gift Cards
149
+ promotions.add :gift_cards,
150
+ label: :gift_cards,
151
+ url: :admin_gift_cards_path,
152
+ position: 10,
153
+ active: -> { %w[gift_cards gift_card_batches].include?(controller_name) }
154
+ end
155
+
156
+ # Reports
157
+ sidebar_nav.add :reports,
158
+ label: :reports,
159
+ url: :admin_reports_path,
160
+ icon: 'chart-bar',
161
+ position: 60,
162
+ if: -> { can?(:manage, Spree::Report) }
163
+
164
+ # Storefront with submenu
165
+ sidebar_nav.add :storefront,
166
+ label: 'admin.storefront',
167
+ url: :admin_themes_path,
168
+ icon: 'building-store',
169
+ position: 70,
170
+ if: -> { can?(:manage, Spree::Theme) } do |storefront|
171
+
172
+ # Pages
173
+ storefront.add :pages,
174
+ label: :pages,
175
+ url: :admin_pages_path,
176
+ position: 20,
177
+ if: -> { can?(:manage, Spree::Page) }
178
+
179
+ # Posts (Blog)
180
+ storefront.add :posts,
181
+ label: :posts,
182
+ url: :admin_posts_path,
183
+ position: 30,
184
+ active: -> { %w[posts post_categories].include?(controller_name) },
185
+ if: -> { can?(:manage, Spree::Post) }
186
+
187
+ # Storefront Settings
188
+ storefront.add :storefront_settings,
189
+ label: :settings,
190
+ url: :edit_admin_storefront_path,
191
+ position: 40,
192
+ if: -> { can?(:manage, current_store) }
193
+ end
194
+
195
+ # Integrations
196
+ sidebar_nav.add :integrations,
197
+ label: :integrations,
198
+ url: :admin_integrations_path,
199
+ icon: 'plug-connected',
200
+ position: 80,
201
+ if: -> { can?(:manage, Spree::Integration) }
202
+
203
+ # Section divider before settings
204
+ sidebar_nav.add :settings_section,
205
+ section_label: 'Settings',
206
+ position: 90
207
+
208
+ # Settings (bottom of sidebar)
209
+ sidebar_nav.add :settings,
210
+ label: :settings,
211
+ url: -> { spree.edit_admin_store_path(section: 'general-settings') },
212
+ icon: 'settings',
213
+ position: 100,
214
+ if: -> { can?(:manage, current_store) }
215
+
216
+ # Admin Users (bottom of sidebar)
217
+ sidebar_nav.add :admin_users,
218
+ label: :users,
219
+ url: :admin_admin_users_path,
220
+ icon: 'users',
221
+ position: 110,
222
+ if: -> { can?(:manage, Spree.admin_user_class) }
223
+
224
+ # ===============================================
225
+ # Settings Navigation
226
+ # ===============================================
227
+ settings_nav = Spree.admin.navigation.settings
228
+
229
+ # Store Details
230
+ settings_nav.add :general_settings,
231
+ label: :store_details,
232
+ url: -> { spree.edit_admin_store_path(section: 'general-settings') },
233
+ icon: 'building-store',
234
+ position: 10,
235
+ active: -> { controller_name == 'stores' && params[:section] == 'general-settings' },
236
+ if: -> { can?(:manage, current_store) }
237
+
238
+ # Admin Users
239
+ settings_nav.add :users,
240
+ label: :users,
241
+ url: :admin_admin_users_path,
242
+ icon: 'users',
243
+ position: 20,
244
+ active: -> { %w[admin_users invitations roles].include?(controller_name) },
245
+ if: -> { can?(:manage, Spree.admin_user_class) }
246
+
247
+ # Emails
248
+ settings_nav.add :emails,
249
+ label: :emails,
250
+ url: -> { spree.edit_admin_store_path(section: 'emails') },
251
+ icon: 'send',
252
+ position: 30,
253
+ active: -> { controller_name == 'stores' && params[:section] == 'emails' },
254
+ if: -> { can?(:manage, current_store) }
255
+
256
+ # Policies
257
+ settings_nav.add :policies,
258
+ label: :policies,
259
+ url: :admin_policies_path,
260
+ icon: 'list-check',
261
+ position: 40,
262
+ active: -> { controller_name == 'policies' },
263
+ if: -> { can?(:manage, Spree::Policy) }
264
+
265
+ # Checkout
266
+ settings_nav.add :checkout,
267
+ label: :checkout,
268
+ url: -> { spree.edit_admin_store_path(section: 'checkout') },
269
+ icon: 'shopping-cart',
270
+ position: 50,
271
+ active: -> { controller_name == 'stores' && params[:section] == 'checkout' },
272
+ if: -> { can?(:manage, current_store) }
273
+
274
+ # Domains
275
+ settings_nav.add :domains,
276
+ label: :domains,
277
+ url: :admin_custom_domains_path,
278
+ icon: 'world-www',
279
+ position: 60,
280
+ active: -> { controller_name == 'custom_domains' },
281
+ if: -> { can?(:manage, Spree::CustomDomain) }
282
+
283
+ # Payment Methods
284
+ settings_nav.add :payment_methods,
285
+ label: :payments,
286
+ url: :admin_payment_methods_path,
287
+ icon: 'credit-card',
288
+ position: 70,
289
+ active: -> { controller_name == 'payment_methods' },
290
+ if: -> { can?(:manage, Spree::PaymentMethod) }
291
+
292
+ # Zones
293
+ settings_nav.add :zones,
294
+ label: :zones,
295
+ url: :admin_zones_path,
296
+ icon: 'world',
297
+ position: 80,
298
+ active: -> { %w[zones countries states].include?(controller_name) },
299
+ if: -> { can?(:manage, Spree::Zone) }
300
+
301
+ # Shipping Methods
302
+ settings_nav.add :shipping_methods,
303
+ label: :shipping,
304
+ url: :admin_shipping_methods_path,
305
+ icon: 'truck',
306
+ position: 90,
307
+ active: -> { %w[shipping_methods shipping_categories].include?(controller_name) },
308
+ if: -> { can?(:manage, Spree::ShippingMethod) }
309
+
310
+ # Tax Settings
311
+ settings_nav.add :tax_rates,
312
+ label: :tax,
313
+ url: :admin_tax_rates_path,
314
+ icon: 'receipt-tax',
315
+ position: 100,
316
+ active: -> { %w[tax_rates tax_categories stripe_tax_registrations].include?(controller_name) },
317
+ if: -> { can?(:manage, Spree::TaxRate) }
318
+
319
+ # Returns
320
+ settings_nav.add :return_settings,
321
+ label: :returns,
322
+ url: :admin_return_authorization_reasons_path,
323
+ icon: 'receipt-refund',
324
+ position: 110,
325
+ active: -> { %w[refund_reasons reimbursement_types return_authorization_reasons].include?(controller_name) },
326
+ if: -> { can?(:manage, Spree::ReturnAuthorizationReason) }
327
+
328
+ # Stock Locations
329
+ settings_nav.add :stock_locations,
330
+ label: :stock_locations,
331
+ url: :admin_stock_locations_path,
332
+ icon: 'map-pin',
333
+ position: 120,
334
+ active: -> { controller_name == 'stock_locations' },
335
+ if: -> { can?(:manage, Spree::StockLocation) }
336
+
337
+ # Metafield Definitions
338
+ settings_nav.add :metafield_definitions,
339
+ label: :metafield_definitions,
340
+ url: :admin_metafield_definitions_path,
341
+ icon: 'list-details',
342
+ position: 130,
343
+ active: -> { controller_name == 'metafield_definitions' },
344
+ if: -> { can?(:manage, Spree::MetafieldDefinition) }
345
+
346
+ # Audit Log
347
+ settings_nav.add :audits,
348
+ label: 'admin.audit_log',
349
+ url: :admin_audits_path,
350
+ icon: 'history',
351
+ position: 140,
352
+ active: -> { %w[audits exports imports].include?(controller_name) },
353
+ if: -> {
354
+ # Only show if audits feature exists
355
+ can?(:manage, current_store) &&
356
+ Spree::Core::Engine.routes.url_helpers.respond_to?(:admin_audits_path)
357
+ }
358
+
359
+ # Developers
360
+ settings_nav.add :developers,
361
+ label: :developers,
362
+ url: :admin_oauth_applications_path,
363
+ icon: 'terminal',
364
+ position: 150,
365
+ active: -> { %w[oauth_applications webhooks_subscribers].include?(controller_name) },
366
+ if: -> { can?(:manage, Spree::OauthApplication) }
367
+
368
+ # Edit Profile
369
+ settings_nav.add :edit_profile,
370
+ label: 'admin.edit_profile',
371
+ url: :edit_admin_profile_path,
372
+ icon: 'user-scan',
373
+ position: 200,
374
+ active: -> { controller_name == 'profiles' && action_name == 'edit' }
375
+
376
+ # ===============================================
377
+ # Page Tab Navigations
378
+ # ===============================================
379
+
380
+ # Tax Tab Navigation
381
+ tax_tabs_nav = Spree.admin.navigation.tax_tabs
382
+
383
+ tax_tabs_nav.add :tax_rates,
384
+ label: :tax_rates,
385
+ url: :admin_tax_rates_path,
386
+ position: 10,
387
+ if: -> { can?(:manage, Spree::TaxRate) }
388
+
389
+ tax_tabs_nav.add :tax_categories,
390
+ label: :tax_categories,
391
+ url: :admin_tax_categories_path,
392
+ position: 20,
393
+ if: -> { can?(:manage, Spree::TaxCategory) }
394
+
395
+ # Shipping Tab Navigation
396
+ shipping_tabs_nav = Spree.admin.navigation.shipping_tabs
397
+
398
+ shipping_tabs_nav.add :shipping_methods,
399
+ label: :shipping_methods,
400
+ url: :admin_shipping_methods_path,
401
+ position: 10,
402
+ active: -> { controller_name == 'shipping_methods' && action_name == 'index' },
403
+ if: -> { can?(:manage, Spree::ShippingMethod) }
404
+
405
+ shipping_tabs_nav.add :shipping_categories,
406
+ label: :shipping_categories,
407
+ url: :admin_shipping_categories_path,
408
+ position: 20,
409
+ if: -> { can?(:manage, Spree::ShippingCategory) }
410
+
411
+ # Team Tab Navigation
412
+ team_tabs_nav = Spree.admin.navigation.team_tabs
413
+
414
+ team_tabs_nav.add :admin_users,
415
+ label: :users,
416
+ url: :admin_admin_users_path,
417
+ position: 10,
418
+ if: -> { can?(:manage, Spree.admin_user_class) }
419
+
420
+ team_tabs_nav.add :invitations,
421
+ label: :invitations,
422
+ url: :admin_invitations_path,
423
+ position: 20,
424
+ if: -> { can?(:manage, Spree::Invitation) }
425
+
426
+ team_tabs_nav.add :roles,
427
+ label: :roles,
428
+ url: :admin_roles_path,
429
+ position: 30,
430
+ if: -> { can?(:manage, Spree::Role) }
431
+
432
+ # Stock Tab Navigation
433
+ stock_tabs_nav = Spree.admin.navigation.stock_tabs
434
+
435
+ stock_tabs_nav.add :stock_items,
436
+ label: :stock_items,
437
+ url: :admin_stock_items_path,
438
+ position: 10,
439
+ active: -> { controller_name == 'stock_items' },
440
+ if: -> { can?(:manage, Spree::StockItem) }
441
+
442
+ stock_tabs_nav.add :stock_transfers,
443
+ label: :stock_transfers,
444
+ url: :admin_stock_transfers_path,
445
+ position: 20,
446
+ active: -> { controller_name == 'stock_transfers' },
447
+ if: -> { can?(:manage, Spree::StockTransfer) }
448
+
449
+ # Returns and Refunds Tab Navigation
450
+ returns_tabs_nav = Spree.admin.navigation.returns_tabs
451
+
452
+ returns_tabs_nav.add :return_authorization_reasons,
453
+ label: :return_authorization_reasons,
454
+ url: :admin_return_authorization_reasons_path,
455
+ position: 10,
456
+ if: -> { can?(:manage, Spree::ReturnAuthorizationReason) }
457
+
458
+ returns_tabs_nav.add :refund_reasons,
459
+ label: :refund_reasons,
460
+ url: :admin_refund_reasons_path,
461
+ position: 20,
462
+ if: -> { can?(:manage, Spree::RefundReason) }
463
+
464
+ returns_tabs_nav.add :reimbursement_types,
465
+ label: :reimbursement_types,
466
+ url: :admin_reimbursement_types_path,
467
+ position: 30,
468
+ if: -> { can?(:manage, Spree::ReimbursementType) }
469
+
470
+ # Developers Tab Navigation
471
+ developers_tabs_nav = Spree.admin.navigation.developers_tabs
472
+
473
+ developers_tabs_nav.add :api_keys,
474
+ label: :api_keys,
475
+ url: :admin_oauth_applications_path,
476
+ position: 10,
477
+ active: -> { controller_name == 'oauth_applications' },
478
+ if: -> { can?(:manage, Spree::OauthApplication) }
479
+
480
+ developers_tabs_nav.add :webhooks,
481
+ label: :webhooks,
482
+ url: :admin_webhooks_subscribers_path,
483
+ position: 20,
484
+ active: -> { controller_name == 'webhooks_subscribers' },
485
+ if: -> { can?(:manage, Spree::Webhooks::Subscriber) }
486
+
487
+ # Audit Tab Navigation
488
+ audit_tabs_nav = Spree.admin.navigation.audit_tabs
489
+
490
+ audit_tabs_nav.add :audit_log,
491
+ label: 'admin.audit_log',
492
+ url: :admin_audits_path,
493
+ position: 10,
494
+ active: -> { controller_name == 'audits' },
495
+ if: -> { can?(:manage, current_store) }
496
+
497
+ audit_tabs_nav.add :exports,
498
+ label: :exports,
499
+ url: :admin_exports_path,
500
+ position: 20,
501
+ active: -> { controller_name == 'exports' },
502
+ if: -> { can?(:manage, Spree::Export) }
503
+
504
+ audit_tabs_nav.add :imports,
505
+ label: :imports,
506
+ url: :admin_imports_path,
507
+ position: 30,
508
+ active: -> { controller_name == 'imports' },
509
+ if: -> { can?(:manage, Spree::Import) }
510
+ end
@@ -48,6 +48,7 @@ en:
48
48
  special_instructions:
49
49
  description: Allow customers to add special instructions to their order.
50
50
  label: Display the special instructions field
51
+ close_sidebar: Close sidebar
51
52
  copied: Copied!
52
53
  copy_payment_link: Copy Payment Link
53
54
  copy_to_clipboard: Copy to clipboard
@@ -75,6 +76,7 @@ en:
75
76
  edit_theme: Edit theme
76
77
  errors: Errors
77
78
  execution_time: Execution Time
79
+ expand_sidebar: Expand sidebar
78
80
  export_all: Export all records (can be slow for large datasets)
79
81
  export_coupons: Export coupons as CSV file
80
82
  export_created: Your export was started. You will receive an email with a download link when it is ready!
@@ -298,6 +300,8 @@ en:
298
300
  compare_at_price_description: This price is used to show marked down price on the product page.
299
301
  stock_locations_link: To add more stock locations please go to <a href="%{link}">Stock Locations</a>
300
302
  total_inventory_html: 'Total inventory at %{stock_location} location: %{count} available'
303
+ vendors:
304
+ enterprise_feature: Multi-Vendor Marketplace is available in the Enterprise Edition
301
305
  webhooks_subscribers:
302
306
  all_events: All Events
303
307
  new_webhooks_subscriber: New Webhooks Subscriber
@@ -4,7 +4,9 @@ module Spree
4
4
  private
5
5
 
6
6
  def permitted_resource_params
7
- params.require(:<%= @singular_name %>).permit(:name)
7
+ params.require(:<%= @singular_name %>).permit(
8
+ :name
9
+ )
8
10
  end
9
11
  end
10
12
  end
@@ -1,9 +1,11 @@
1
1
  <tr id="<%%= spree_dom_id <%= @singular_name %> %>" class="cursor-pointer" data-controller="row-link">
2
- <td data-action="click->row-link#openLink" class="w-70"><%%= <%= @singular_name %>.name %></td>
3
- <td data-action="click->row-link#openLink" class="w-20"><%%= spree_date(<%= @singular_name %>.created_at) %></td>
4
- <td class="actions w-10"">
5
- <span class="d-flex justify-content-end">
6
- <%%= link_to_edit(<%= @singular_name %>, data: { row_link_target: :link, turbo_frame: '_top' }) if can? :edit, <%= @singular_name %> %>
7
- </span>
2
+ <td data-action="click->row-link#openLink" class="w-70">
3
+ <%%= <%= @singular_name %>.name %>
4
+ </td>
5
+ <td data-action="click->row-link#openLink" class="w-20">
6
+ <%%= spree_date(<%= @singular_name %>.created_at) %>
7
+ </td>
8
+ <td class="actions w-10">
9
+ <%%= link_to_edit(<%= @singular_name %>, data: { row_link_target: :link, turbo_frame: '_top' }) if can? :edit, <%= @singular_name %> %>
8
10
  </td>
9
11
  </tr>
@@ -125,9 +125,59 @@ module Spree
125
125
  :webhooks_subscribers_header_partials,
126
126
  :vendors_nav_partials,
127
127
  :zones_actions_partials,
128
- :zones_header_partials
128
+ :zones_header_partials,
129
+ :navigation
129
130
  )
130
131
 
132
+ class NavigationEnvironment
133
+ def initialize
134
+ @contexts = {}
135
+ end
136
+
137
+ # Register a new navigation context
138
+ # @param name [Symbol] The name of the navigation context
139
+ # @return [Spree::Admin::Navigation] The navigation instance
140
+ def register_context(name)
141
+ name = name.to_sym
142
+ @contexts[name] ||= Spree::Admin::Navigation.new(name)
143
+ end
144
+
145
+ # Get a registered navigation context
146
+ # @param name [Symbol] The name of the navigation context
147
+ # @return [Spree::Admin::Navigation] The navigation instance
148
+ # @raise [NoMethodError] if the context hasn't been registered
149
+ def get_context(name)
150
+ name = name.to_sym
151
+ @contexts[name] || raise(NoMethodError, "Navigation context '#{name}' has not been registered. Use Spree.admin.navigation.register_context(:#{name}) first.")
152
+ end
153
+
154
+ # List all registered contexts
155
+ # @return [Array<Symbol>] Array of registered context names
156
+ def contexts
157
+ @contexts.keys
158
+ end
159
+
160
+ # Check if a context exists
161
+ # @param name [Symbol] The context name to check
162
+ # @return [Boolean] true if the context is registered
163
+ def context?(name)
164
+ @contexts.key?(name.to_sym)
165
+ end
166
+
167
+ # Define accessor methods for predefined and custom contexts
168
+ def method_missing(method_name, *args)
169
+ if method_name.to_s.end_with?('=')
170
+ super
171
+ else
172
+ get_context(method_name)
173
+ end
174
+ end
175
+
176
+ def respond_to_missing?(method_name, include_private = false)
177
+ method_name.to_s.end_with?('=') ? false : context?(method_name)
178
+ end
179
+ end
180
+
131
181
  # accessible via Rails.application.config.spree_admin
132
182
  initializer 'spree.admin.environment', before: :load_config_initializers do |app|
133
183
  app.config.spree_admin = Environment.new
@@ -182,12 +232,24 @@ module Spree
182
232
  end
183
233
  end
184
234
 
185
- config.after_initialize do
235
+ config.after_initialize do |app|
186
236
  Environment.new.tap do |env|
187
237
  env.members.each do |key|
188
238
  Rails.application.config.spree_admin.send("#{key}=", [])
189
239
  end
190
240
  end
241
+
242
+ # Register predefined navigation contexts
243
+ app.config.spree_admin.navigation = NavigationEnvironment.new
244
+ app.config.spree_admin.navigation.register_context(:sidebar)
245
+ app.config.spree_admin.navigation.register_context(:settings)
246
+ app.config.spree_admin.navigation.register_context(:tax_tabs)
247
+ app.config.spree_admin.navigation.register_context(:shipping_tabs)
248
+ app.config.spree_admin.navigation.register_context(:team_tabs)
249
+ app.config.spree_admin.navigation.register_context(:stock_tabs)
250
+ app.config.spree_admin.navigation.register_context(:returns_tabs)
251
+ app.config.spree_admin.navigation.register_context(:developers_tabs)
252
+ app.config.spree_admin.navigation.register_context(:audit_tabs)
191
253
  end
192
254
  end
193
255
  end
@@ -12,6 +12,7 @@ module Spree
12
12
  preference :admin_orders_per_page, :integer, default: Kaminari.config.default_per_page
13
13
 
14
14
  preference :include_application_importmap, :boolean, default: false
15
+ preference :legacy_sidebar_navigation, :boolean, default: false
15
16
  end
16
17
  end
17
18
  end
data/lib/spree/admin.rb CHANGED
@@ -18,5 +18,25 @@ require 'inline_svg'
18
18
  require 'spree/admin/action_callbacks'
19
19
  require 'spree/admin/callbacks'
20
20
  require 'spree/admin/engine'
21
+ require 'spree/core/partials'
21
22
 
22
23
  require 'tinymce-rails'
24
+
25
+ module Spree
26
+ def self.admin
27
+ @admin ||= AdminConfig.new
28
+ end
29
+
30
+ class AdminConfig
31
+ def partials
32
+ @partials ||= Spree::Core::Partials.new(
33
+ Rails.application.config.spree_admin,
34
+ Spree::Admin::Engine::Environment
35
+ )
36
+ end
37
+
38
+ def navigation
39
+ Rails.application.config.spree_admin.navigation
40
+ end
41
+ end
42
+ end