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
@@ -1,19 +1,10 @@
1
1
  <% data_action = (@page_link.persisted? && !@page_link.parent.is_a?(Spree::Store) ? { action: 'blur->auto-submit#submit' } : {}) %>
2
2
 
3
- <div class="form-group">
4
- <%= f.label :linkable_type, 'Link' %>
5
- <%= f.select :linkable_type, @page_link.parent.allowed_linkable_types, { include_blank: false }, { class: 'custom-select mb-3', data: { action: 'auto-submit#submit' } } %>
6
- <%= render 'spree/admin/page_links/linkable_type_dropdown', page_link: @page_link, form_name: 'page_link' %>
7
- </div>
3
+ <%= f.spree_select :linkable_type, @page_link.parent.allowed_linkable_types, { label: 'Link', include_blank: false }, { class: 'custom-select mb-3', data: { action: 'auto-submit#submit' } } %>
4
+ <%= render 'spree/admin/page_links/linkable_type_dropdown', page_link: @page_link, form_name: 'page_link' %>
8
5
 
9
6
  <hr class="my-4" />
10
7
 
11
- <div class="form-group">
12
- <%= f.label :label, Spree.t(:name) %>
13
- <%= f.text_field :label, class: 'form-control', data: data_action, required: true %>
14
- </div>
8
+ <%= f.spree_text_field :label, label: Spree.t(:name), data: data_action, required: true %>
15
9
 
16
- <div class="custom-control custom-checkbox">
17
- <%= f.check_box :open_in_new_tab, class: 'custom-control-input', data: data_action %>
18
- <%= f.label :open_in_new_tab, class: 'custom-control-label' %>
19
- </div>
10
+ <%= f.spree_check_box :open_in_new_tab, data: data_action %>
@@ -1,5 +1,3 @@
1
- <label>Logo image</label>
2
-
3
1
  <%= render 'spree/admin/shared/page_section_logo', f: f %>
4
2
 
5
3
  <p class="text-muted small mt-2">
@@ -46,6 +46,13 @@
46
46
  <% end %>
47
47
  </div>
48
48
  <div class="card-body">
49
+ <% if payment.gateway_processing_error_messages.any? %>
50
+ <div class="alert alert-danger inline-block">
51
+ <strong><%= Spree.t(:payments_gateway_processing_errors, payment_method_name: payment.payment_method&.name) %>:</strong>
52
+ <%= payment.gateway_processing_error_messages.join(', ') %>
53
+ </div>
54
+ <% end %>
55
+
49
56
  <div class="d-flex">
50
57
  <div class="w-50">
51
58
  <p class="mb-1"><%= Spree.t(:amount) %>:</p>
@@ -25,10 +25,7 @@
25
25
  <div class="col-lg-4">
26
26
  <div class="card mb-4">
27
27
  <div class="card-body">
28
- <div class="form-group">
29
- <%= f.label :image, Spree.t(:featured_image) %>
30
- <%= render 'active_storage/upload_form', form: f, field_name: :image, width: 1200, height: 600, crop: true, css: 'flex-col' %>
31
- </div>
28
+ <%= f.spree_file_field :image, width: 1200, height: 600, crop: true, css: 'flex-col', label: Spree.t(:featured_image) %>
32
29
  </div>
33
30
  </div>
34
31
 
@@ -1,10 +1,16 @@
1
- <%= search_form_for [:admin, @search], class: "filter-wrap", data: {controller: "filters dropdown"} do |f| %>
2
- <div class="d-flex flex-column flex-lg-row gap-2">
3
- <%= render 'spree/admin/shared/filters_search_bar', param: :search_by_title, label: Spree.t(:title) %>
1
+ <%= search_form_for [:admin, @search], class: "filter-wrap", data: {controller: "filters dialog"} do |f| %>
2
+ <div class="d-flex flex-column flex-lg-row gap-2">
3
+ <%= render 'spree/admin/shared/filters_search_bar', param: :search_by_title, label: Spree.t(:title) %>
4
4
 
5
- <%= render 'spree/admin/shared/filters_button' %>
6
- </div>
7
- <div data-dropdown-target="menu" id="table-filter" class="hidden">
5
+ <%= button_tag type: 'button', class: 'btn btn-light d-flex align-items-center', data: { action: 'dialog#open' } do %>
6
+ <%= icon "adjustments", class: "mr-1" %>
7
+ <%= Spree.t("admin.filters") %>
8
+ <% end %>
9
+ </div>
10
+
11
+ <dialog class="drawer" data-dialog-target="dialog" id="posts-filters-drawer">
12
+ <%= drawer_header(Spree.t(:filter), 'dialog') %>
13
+ <div class="drawer-body">
8
14
  <div class="form-group">
9
15
  <%= f.label :author_id_eq, Spree.t(:author) %>
10
16
  <%= f.select :author_id_eq,
@@ -20,8 +26,12 @@
20
26
  { data: { filters_target: :input, controller: 'autocomplete-select' } } %>
21
27
  </div>
22
28
  <%= render_admin_partials(:posts_filters_partials, f: f) %>
23
- <%= render 'spree/admin/shared/filter_submit' %>
24
- </div>
29
+ </div>
30
+ <div class="drawer-footer">
31
+ <%= drawer_discard_button('dialog') %>
32
+ <%= render 'spree/admin/shared/filter_submit' %>
33
+ </div>
34
+ </dialog>
25
35
 
26
36
  <%= render "spree/admin/shared/filter_badge_template" %>
27
37
 
@@ -18,7 +18,7 @@
18
18
  url: spree.bulk_status_update_admin_products_path(status: :draft)
19
19
  ) %>
20
20
 
21
- <%= dropdown direction: 'top' do %>
21
+ <%= dropdown direction: 'top', portal: false do %>
22
22
  <%= dropdown_toggle do %>
23
23
  <%= icon('dots-vertical', class: "mr-0") %>
24
24
  <% end %>
@@ -1,9 +1,10 @@
1
1
  <% frame_name ||= nil %>
2
2
 
3
+
3
4
  <%= search_form_for product_list_filters_search_form_path,
4
5
  class: "filter-wrap",
5
6
  data: {
6
- controller: "filters dropdown",
7
+ controller: "filters dialog",
7
8
  filters_url_value: request.url
8
9
  } do |f| %>
9
10
  <%= hidden_field_tag :frame_name, frame_name if frame_name.present? %>
@@ -11,9 +12,15 @@
11
12
  <div class="d-flex flex-column flex-lg-row gap-2">
12
13
  <%= render 'spree/admin/shared/filters_search_bar', param: :multi_search %>
13
14
  <%= render "spree/admin/products/table_filter_dropdown" %>
14
- <%= render 'spree/admin/shared/filters_button' %>
15
+ <%= button_tag type: 'button', class: 'btn btn-light d-flex align-items-center', data: { action: 'dialog#open' } do %>
16
+ <%= icon "adjustments", class: "mr-1" %>
17
+ <%= Spree.t("admin.filters") %>
18
+ <% end %>
15
19
  </div>
16
- <div data-dropdown-target="menu" id="table-filter" class="hidden">
20
+
21
+ <dialog class="drawer" data-dialog-target="dialog" id="product-filters-drawer">
22
+ <%= drawer_header(Spree.t(:filter), 'dialog') %>
23
+ <div class="drawer-body">
17
24
  <%= f.hidden_field :status_eq, value: params.dig(:q, :status_eq) %>
18
25
  <div class="form-group">
19
26
  <%= f.label :taxons_id_in, Spree.t(:taxons) %>
@@ -44,10 +51,14 @@
44
51
  filters_target: "input",
45
52
  } %>
46
53
  </div>
47
-
54
+
48
55
  <%= render_admin_partials(:products_filters_partials, f: f) %>
49
- <%= render 'spree/admin/shared/filter_submit' %>
50
- </div>
56
+ </div>
57
+ <div class="drawer-footer">
58
+ <%= drawer_discard_button('dialog') %>
59
+ <%= render 'spree/admin/shared/filter_submit' %>
60
+ </div>
61
+ </dialog>
51
62
 
52
63
  <%= render "spree/admin/shared/filter_badge_template" %>
53
64
 
@@ -13,7 +13,7 @@
13
13
  <%= product_filter_stock_dropdown_value if product_filter_stock_dropdown_value != Spree.t('admin.products.any_stock') %>
14
14
  <% end %>
15
15
  <% end %>
16
- <%= dropdown_menu class: 'w-100', style: 'min-width: 200px' do %>
16
+ <%= dropdown_menu do %>
17
17
  <h6 class="dropdown-header"><%= Spree.t('admin.products.by_status') %>:</h6>
18
18
 
19
19
  <%= active_link_to Spree.t('admin.products.all_statuses'), params.to_unsafe_h.deep_merge({page: nil, q: {status_eq: nil}}), class: 'dropdown-item', active: (params[:q] || {}).values_at(*%w[status_eq in_stock_items out_of_stock_items] ).all?(&:blank?) %>
@@ -6,72 +6,20 @@
6
6
  <%= turbo_save_button_tag Spree.t('actions.update'), form: 'edit_user', data: { admin_target: 'save' } %>
7
7
  <% end %>
8
8
 
9
- <%= form_for @user, url: spree.admin_profile_path, method: :put, as: :user do |f| %>
10
- <div class="row">
11
- <div class="col-lg-6 offset-lg-3">
9
+ <div class="row">
10
+ <div class="col-lg-6 offset-lg-3">
11
+ <%= form_for @user, url: spree.admin_profile_path, method: :put, as: :user do |f| %>
12
12
  <div class="card mb-4">
13
13
  <div class="card-header">
14
14
  <h5 class="card-title"><%= Spree.t('admin.personal_details') %></h5>
15
15
  </div>
16
16
  <div class="card-body">
17
- <div class="form-group mb-4">
18
- <%= f.label :email, class: 'form-label' %>
19
- <%= f.email_field :email, class: 'form-control', required: true %>
20
- </div>
21
- <div class="form-group mb-4">
22
- <%= f.label :first_name, class: 'form-label' %>
23
- <%= f.text_field :first_name, class: 'form-control', required: true %>
24
- </div>
25
- <div class="form-group mb-4">
26
- <%= f.label :last_name, class: 'form-label' %>
27
- <%= f.text_field :last_name, class: 'form-control', required: true %>
28
- </div>
29
- <div class="form-group mb-0">
30
- <div>
31
- <%= f.label :avatar, class: 'form-label' %>
32
- </div>
33
- <%= render 'active_storage/upload_form', form: f, field_name: :avatar, crop: true %>
34
- </div>
17
+ <%= f.spree_email_field :email, required: true %>
18
+ <%= f.spree_text_field :first_name, required: true %>
19
+ <%= f.spree_text_field :last_name, required: true %>
20
+ <%= f.spree_file_field :avatar, crop: true %>
35
21
  </div>
36
22
  </div>
37
-
38
- <div class="card">
39
- <div class="card-header">
40
- <h5 class="card-title"><%= Spree.t('admin.notifications') %></h5>
41
- </div>
42
- <div class="card-body">
43
- <% if @user.respond_to?(:deliver_invitation_accepted_email) %>
44
- <div class="custom-control custom-switch mb-3">
45
- <%= f.check_box :deliver_invitation_accepted_email, class: 'custom-control-input' %>
46
- <%= f.label :deliver_invitation_accepted_email, 'Someone accepted your team invitation', class: 'custom-control-label' %>
47
- </div>
48
- <% end %>
49
-
50
- <% if @user.respond_to?(:deliver_vendor_new_order_email) %>
51
- <hr />
52
- <div class="custom-control custom-switch mb-3">
53
- <%= f.check_box :deliver_vendor_new_order_email, class: 'custom-control-input' %>
54
- <%= f.label :deliver_vendor_new_order_email, 'New order notification', class: 'custom-control-label' %>
55
- </div>
56
- <% end %>
57
-
58
- <% if @user.respond_to?(:deliver_vendor_onboarding_started_email) %>
59
- <hr />
60
- <div class="custom-control custom-switch mb-3">
61
- <%= f.check_box :deliver_vendor_onboarding_started_email, class: 'custom-control-input' %>
62
- <%= f.label :deliver_vendor_onboarding_started_email, 'Vendor started onboarding', class: 'custom-control-label' %>
63
- </div>
64
- <div class="custom-control custom-switch mb-3">
65
- <%= f.check_box :deliver_vendor_onboarding_completed_email, class: 'custom-control-input' %>
66
- <%= f.label :deliver_vendor_onboarding_completed_email, 'Vendor completed onboarding', class: 'custom-control-label' %>
67
- </div>
68
- <div class="custom-control custom-switch mb-3">
69
- <%= f.check_box :deliver_vendor_approved_email, class: 'custom-control-input' %>
70
- <%= f.label :deliver_vendor_approved_email, 'Vendor has been approved', class: 'custom-control-label' %>
71
- </div>
72
- <% end %>
73
- </div>
74
- </div>
75
- </div>
23
+ <% end %>
76
24
  </div>
77
- <% end %>
25
+ </div>
@@ -1,17 +1,27 @@
1
- <%= search_form_for [:admin, @search], class: "filter-wrap", data: {controller: "filters dropdown"} do |f| %>
2
- <div class="d-flex flex-column flex-lg-row gap-2">
3
- <%= render 'spree/admin/shared/filters_search_bar', param: :name_cont, label: Spree.t(:name) %>
4
- <%= render "spree/admin/promotions/table_filter_dropdown" %>
5
- <%= render 'spree/admin/shared/filters_button' %>
6
- </div>
7
- <div data-dropdown-target="menu" id="table-filter" class="hidden">
8
- <div class="form-group">
9
- <%= label_tag :q_code_or_coupon_codes_code_eq, Spree.t(:code) %>
10
- <%= f.search_field :code_or_coupon_codes_code_eq, class: "form-control", data: { filters_target: :input } %>
1
+ <%= search_form_for [:admin, @search], class: "filter-wrap", data: {controller: "filters dialog"} do |f| %>
2
+ <div class="d-flex flex-column flex-lg-row gap-2">
3
+ <%= render 'spree/admin/shared/filters_search_bar', param: :name_cont, label: Spree.t(:name) %>
4
+ <%= render "spree/admin/promotions/table_filter_dropdown" %>
5
+ <%= button_tag type: 'button', class: 'btn btn-light d-flex align-items-center', data: { action: 'dialog#open' } do %>
6
+ <%= icon "adjustments", class: "mr-1" %>
7
+ <%= Spree.t("admin.filters") %>
8
+ <% end %>
11
9
  </div>
12
- <%= render_admin_partials(:promotions_filters_partials, f: f) %>
13
- <%= render 'spree/admin/shared/filter_submit' %>
14
- </div>
10
+
11
+ <dialog class="drawer" data-dialog-target="dialog" id="promotion-filters-drawer">
12
+ <%= drawer_header(Spree.t(:filter), 'dialog') %>
13
+ <div class="drawer-body">
14
+ <div class="form-group">
15
+ <%= label_tag :q_code_or_coupon_codes_code_eq, Spree.t(:code) %>
16
+ <%= f.search_field :code_or_coupon_codes_code_eq, class: "form-control", data: { filters_target: :input } %>
17
+ </div>
18
+ <%= render_admin_partials(:promotions_filters_partials, f: f) %>
19
+ </div>
20
+ <div class="drawer-footer">
21
+ <%= drawer_discard_button('dialog') %>
22
+ <%= render 'spree/admin/shared/filter_submit' %>
23
+ </div>
24
+ </dialog>
15
25
 
16
26
  <%= render "spree/admin/shared/filter_badge_template" %>
17
27
 
@@ -5,7 +5,7 @@
5
5
  </span>
6
6
  <%= promotion_filter_dropdown_value %>
7
7
  <% end %>
8
- <%= dropdown_menu class: 'dropdown-menu-right w-100', style: 'min-width: 200px' do %>
8
+ <%= dropdown_menu do %>
9
9
  <%= active_link_to 'All Promotions', params.to_unsafe_h.deep_merge({page: nil, q: {active: nil, expired: nil}}), class: 'dropdown-item', active: (params[:q] || {}).values_at(*%w[expired active] ).all?(&:blank?) %>
10
10
  <%= link_to Spree.t(:expired),
11
11
  params.to_unsafe_h.deep_merge({page: nil, q: {active: nil, expired: true}}),
@@ -2,17 +2,6 @@
2
2
  <%= f.spree_number_field :usage_limit, min: 0, step: 1, help: 'Leave this field blank for unlimited usage.' %>
3
3
  <% end %>
4
4
 
5
- <div class="form-group">
6
- <%= f.label :starts_at %>
7
- <%= f.datetime_field :starts_at, class: 'form-control' %>
8
- <%= f.error_message_on :starts_at %>
9
- </div>
5
+ <%= f.spree_datetime_field :starts_at %>
10
6
 
11
- <div class="form-group">
12
- <%= f.label :expires_at %>
13
- <%= f.datetime_field :expires_at, class: 'form-control' %>
14
- <%= f.error_message_on :expires_at %>
15
- <span class="form-text mt-2">
16
- Leave this field blank for no expiration.
17
- </span>
18
- </div>
7
+ <%= f.spree_datetime_field :expires_at, help: 'Leave this field blank for no expiration.' %>
@@ -1,13 +1,5 @@
1
1
  <% if f.object.new_record? %>
2
- <div class="form-group">
3
- <%= f.label :amount, Spree.t(:amount) %>
4
- <div class="input-group">
5
- <%= f.text_field :amount, class: 'form-control', required: true %>
6
- <div class="input-group-append">
7
- <span class="input-group-text"><%= currency_symbol(@order.currency) %></span>
8
- </div>
9
- </div>
10
- </div>
2
+ <%= f.spree_text_field :amount, label: Spree.t(:amount), append: currency_symbol(@order.currency), required: true %>
11
3
  <% end %>
12
4
 
13
5
  <%= f.spree_collection_select :refund_reason_id, refund_reasons, :id, :name, { include_blank: true, label: Spree.t(:reason), required: true } %>
@@ -13,7 +13,7 @@
13
13
  <%= Spree.t(:all) %>
14
14
  <% end %>
15
15
  <% end %>
16
- <%= dropdown_menu class: 'dropdown-menu-right w-100', style: 'min-width: 200px' do %>
16
+ <%= dropdown_menu do %>
17
17
  <%= active_link_to Spree.t(:all),
18
18
  params.to_unsafe_h.deep_merge({ q: { state_eq: "" } }),
19
19
  class: "dropdown-item",
@@ -1,3 +1,5 @@
1
1
  <% content_for :page_title do %>
2
2
  <%= Spree.t('admin.audit_log') %>
3
3
  <% end %>
4
+
5
+ <%= render_tab_navigation(:audit_tabs) %>
@@ -7,7 +7,7 @@
7
7
 
8
8
  <% dropdown_direction ||= 'left' %>
9
9
 
10
- <%= dropdown direction: dropdown_direction, data: { controller: 'dropdown calendar-range', action: 'click->calendar-range#open' }, class: 'h-100' do %>
10
+ <%= dropdown direction: dropdown_direction, data: { controller: 'dropdown calendar-range', action: 'click->calendar-range#open' }, class: 'h-100', portal: false do %>
11
11
  <%= dropdown_toggle class: css_classes do %>
12
12
  <%= icon('calendar', class: 'mr-2') %>
13
13
  <div data-calendar-range-target="label">
@@ -18,7 +18,7 @@
18
18
  <% end %>
19
19
  </div>
20
20
  <% end %>
21
- <%= dropdown_menu class: "pt-2 px-2 pb-1" do %>
21
+ <%= dropdown_menu class: "pt-2 px-2 pb-1", style: 'min-width: 29rem' do %>
22
22
  <div data-calendar-range-target="picker" class="d-none"></div>
23
23
  <% end %>
24
24
 
@@ -22,7 +22,7 @@
22
22
  <% end %>
23
23
 
24
24
  <% if show_record_subtitle || content_for(:page_actions_dropdown) %>
25
- <%= dropdown id: 'page_actions_dropdown', class: 'h-100' do %>
25
+ <%= dropdown id: 'page_actions_dropdown', class: 'h-100', portal: false do %>
26
26
  <%= dropdown_toggle id: 'more-actions-link', class: 'btn-light h-100 p-2' do %>
27
27
  <%= icon 'dots-vertical', class: 'mr-0' %>
28
28
  <% end %>
@@ -1,7 +1,5 @@
1
1
  <%= content_for :page_title do %>
2
2
  <%= Spree.t(:developers) %>
3
3
  <% end %>
4
- <%= content_for :page_tabs do %>
5
- <%= nav_item(Spree.t(:api_keys), spree.admin_oauth_applications_path) if can?(:manage, Spree::OauthApplication) %>
6
- <%= nav_item(Spree.t(:webhooks), spree.admin_webhooks_subscribers_path) if can?(:manage, Spree::Webhooks::Subscriber) %>
7
- <% end %>
4
+
5
+ <%= render_tab_navigation(:developers_tabs) %>
@@ -1,11 +1,9 @@
1
1
  <div id="scroll-header"></div>
2
2
 
3
- <%= render 'spree/admin/shared/offcanvas_nav' %>
4
-
5
3
  <header id="header" class="sticky-top py-0">
6
4
  <nav class="navbar navbar-expand-lg p-0 d-flex align-items-center pr-3">
7
5
  <div class="d-lg-none pr-3 border-right">
8
- <button type="button" class="btn btn-light pull-bs-canvas-left px-2 ml-3 text-decoration-none">
6
+ <button type="button" class="btn btn-light px-2 ml-3 text-decoration-none" data-action="click->sidebar#openMobile">
9
7
  <%= icon('menu-2', class: 'mr-0') %>
10
8
  </button>
11
9
  </div>
@@ -20,13 +18,13 @@
20
18
  <% end %>
21
19
  <% end %>
22
20
 
21
+ <button type="button" class="sidebar-toggle-btn ml-3 mr-3 d-none d-lg-block" data-action="click->sidebar#toggle" aria-label="<%= Spree.t('admin.expand_sidebar') %>">
22
+ <%= icon('layout-sidebar-left-collapse') %>
23
+ </button>
24
+
23
25
  <div class="d-none d-lg-block">
24
26
  <%= render 'spree/admin/shared/breadcrumbs' %>
25
27
  </div>
26
-
27
- <div class="ml-auto d-flex align-items-center">
28
- <%= render 'spree/admin/shared/user_dropdown' %>
29
- </div>
30
28
  </nav>
31
29
  </header>
32
30
 
@@ -0,0 +1,5 @@
1
+ <ul class="nav flex-column">
2
+ <% items.each do |item| %>
3
+ <%= render 'spree/admin/shared/navigation_item', item: item, context: context %>
4
+ <% end %>
5
+ </ul>
@@ -0,0 +1,64 @@
1
+ <%
2
+ # Resolve item properties (pass self as context for route helpers)
3
+ item_url = item.resolve_url(self)
4
+ item_label = item.resolve_label
5
+ badge_value = item.badge_value(self)
6
+ is_active = item.active?(request.path, self)
7
+ has_children = item.children.present?
8
+ tooltip_text = item.tooltip
9
+
10
+ # Build data attributes - only add tooltip controller if tooltip is present
11
+ data_attrs = item.data_attributes.dup
12
+ if tooltip_text.present?
13
+ data_attrs[:controller] = 'tooltip'
14
+ end
15
+
16
+ # Build additional HTML options (like target)
17
+ html_options = {}
18
+ html_options[:target] = item.target if item.target.present?
19
+ html_options[:id] = "nav-link-#{item.key}" if item.key.present?
20
+
21
+ # Build complete label with badge (like old menu pattern)
22
+ complete_label = item_label
23
+ if badge_value.present?
24
+ badge_class = item.badge_class.presence || 'badge-light'
25
+ complete_label += content_tag(:span, badge_value, class: "badge ml-auto #{badge_class}")
26
+ end
27
+ # Add tooltip if present
28
+ if tooltip_text.present?
29
+ complete_label += tooltip(tooltip_text)
30
+ end
31
+ complete_label = complete_label&.html_safe
32
+ %>
33
+
34
+ <% if item.section? %>
35
+ <%# Section header %>
36
+ <li class="nav-item nav-section-header mt-3 border-top pt-3 pl-2">
37
+ <span class="text-muted text-uppercase font-weight-light font-size-sm"><%= item.section_label %></span>
38
+ </li>
39
+ <% else %>
40
+ <% if has_children %>
41
+ <%# Item with children (submenu shows when parent is active) %>
42
+ <%= nav_item(complete_label, item_url, icon: item.icon, active: is_active, data: data_attrs, **html_options) %>
43
+
44
+ <%# Submenu for expanded sidebar (only shown when active) %>
45
+ <ul class="nav-submenu <% unless is_active %>d-none<% end %>" id="nav-submenu-<%= item.key %>">
46
+ <% item.children.select { |child| child.visible?(self) }.each do |child_item| %>
47
+ <%= render 'spree/admin/shared/navigation_item', item: child_item, context: context %>
48
+ <% end %>
49
+ </ul>
50
+
51
+ <%# Submenu dropdown for collapsed sidebar (always rendered, shown on hover) %>
52
+ <ul class="nav-submenu-dropdown d-none dropdown-container" id="nav-submenu-dropdown-<%= item.key %>">
53
+ <%# Add parent item as first item in dropdown %>
54
+ <%= nav_item(item_label, item_url, icon: nil) %>
55
+
56
+ <% item.children.select { |child| child.visible?(self) }.each do |child_item| %>
57
+ <%= render 'spree/admin/shared/navigation_item', item: child_item, context: context %>
58
+ <% end %>
59
+ </ul>
60
+ <% else %>
61
+ <%# Regular item without children %>
62
+ <%= nav_item(complete_label, item_url, icon: item.icon, active: is_active, data: data_attrs, **html_options) %>
63
+ <% end %>
64
+ <% end %>
@@ -1,4 +1,4 @@
1
- <%= dropdown do %>
1
+ <%= dropdown id: 'new-item-dropdown' do %>
2
2
  <%= dropdown_toggle class: 'btn-light btn-sm px-1' do %>
3
3
  <%= icon 'plus', class: 'mr-0' %>
4
4
  <% end %>
@@ -1,7 +1,4 @@
1
- <%= render 'active_storage/upload_form', form: f, field_name: :asset, width: 400, height: 128, auto_submit: true %>
1
+ <%= f.spree_file_field :asset, width: 400, height: 128, auto_submit: true, label: Spree.t(:image) %>
2
2
  <% if f.object.has_preference?(:image_alt) %>
3
- <div class="form-group">
4
- <%= f.label :preferred_image_alt, Spree.t(:alt_text) %>
5
- <%= f.text_field :preferred_image_alt, data: { action: 'auto-submit#submit' }, class: 'form-control' %>
6
- </div>
3
+ <%= f.spree_text_field :preferred_image_alt, label: Spree.t(:alt_text), data: { action: 'auto-submit#submit' } %>
7
4
  <% end %>
@@ -1 +1 @@
1
- <%= render 'active_storage/upload_form', form: f, field_name: :asset, width: 400, height: 128, auto_submit: true %>
1
+ <%= f.spree_file_field :asset, width: 400, height: 128, auto_submit: true, label: Spree.t(:logo) %>
@@ -2,9 +2,8 @@
2
2
  <%= Spree.t(:returns) %> & <%= Spree.t(:refunds) %>
3
3
  <% end %>
4
4
 
5
+ <%= render_tab_navigation(:returns_tabs) %>
6
+
5
7
  <% content_for(:page_tabs) do %>
6
- <%= nav_item(Spree.t(:return_authorization_reasons), spree.admin_return_authorization_reasons_path) if can?(:manage, Spree::ReturnAuthorizationReason) %>
7
- <%= nav_item(Spree.t(:refund_reasons), spree.admin_refund_reasons_path) if can?(:manage, Spree::RefundReason) %>
8
- <%= nav_item(Spree.t(:reimbursement_types), spree.admin_reimbursement_types_path) if can?(:manage, Spree::ReimbursementType) %>
9
8
  <%= render_admin_partials(:returns_and_refunds_nav_partials) %>
10
9
  <% end %>
@@ -1,5 +1,6 @@
1
+ <%= render_tab_navigation(:shipping_tabs) %>
2
+
1
3
  <%= content_for(:page_tabs) do %>
2
- <%= nav_item(Spree.t(:shipping_methods), spree.admin_shipping_methods_path, active: controller_name == 'shipping_methods' && action_name == 'index') if can?(:manage, Spree::ShippingMethod) %>
3
- <%= nav_item(Spree.t(:shipping_categories), spree.admin_shipping_categories_path) if can?(:manage, Spree::ShippingCategory) %>
4
4
  <%= render_admin_partials(:shipping_nav_partials) %>
5
5
  <% end %>
6
+
@@ -1,7 +1,33 @@
1
- <aside id="main-sidebar" class="overflow-y-auto">
2
- <% if defined?(current_vendor) && current_vendor.present? %>
3
- <%= render 'spree/admin/shared/sidebar/vendor_nav' %>
4
- <% else %>
5
- <%= render 'spree/admin/shared/sidebar/store_nav' %>
6
- <% end %>
7
- </aside>
1
+ <%# Desktop Sidebar %>
2
+ <aside id="main-sidebar" class="overflow-y-auto" data-sidebar-target="desktop">
3
+ <div class="sidebar-content">
4
+ <% if defined?(current_vendor) && current_vendor.present? %>
5
+ <%= render 'spree/admin/shared/sidebar/vendor_nav' %>
6
+ <% else %>
7
+ <%= render 'spree/admin/shared/sidebar/store_nav' %>
8
+ <% end %>
9
+ </div>
10
+
11
+ <%= render 'spree/admin/shared/user_dropdown' %>
12
+ </aside>
13
+
14
+ <%# Mobile Sidebar Overlay %>
15
+ <div class="sidebar-mobile d-lg-none" data-sidebar-target="mobile">
16
+ <div class="sidebar-backdrop" data-action="click->sidebar#closeMobile"></div>
17
+ <div class="sidebar-panel">
18
+ <div class="sidebar-mobile-header">
19
+ <button type="button" class="btn btn-sm" data-action="click->sidebar#closeMobile" aria-label="<%= Spree.t('admin.close_sidebar') %>">
20
+ <%= icon('x') %>
21
+ </button>
22
+ </div>
23
+ <div class="sidebar-mobile-content">
24
+ <% if defined?(current_vendor) && current_vendor.present? %>
25
+ <%= render 'spree/admin/shared/sidebar/vendor_nav' %>
26
+ <% else %>
27
+ <%= render 'spree/admin/shared/sidebar/store_nav' %>
28
+ <% end %>
29
+ </div>
30
+
31
+ <%= render 'spree/admin/shared/user_dropdown' %>
32
+ </div>
33
+ </div>
@@ -1,3 +1,6 @@
1
- <%= nav_item(Spree.t(:stock_items), spree.admin_stock_items_path) if can?(:manage, Spree::StockItem) %>
2
- <%= nav_item(Spree.t(:stock_transfers), spree.admin_stock_transfers_path) if can?(:manage, Spree::StockTransfer) %>
3
- <%= render_admin_partials(:stock_nav_partials) %>
1
+ <%= render_tab_navigation(:stock_tabs) %>
2
+
3
+ <%= content_for(:page_tabs) do %>
4
+ <%= render_admin_partials(:stock_nav_partials) %>
5
+ <% end %>
6
+
@@ -1,5 +1,4 @@
1
+ <%= render_tab_navigation(:tax_tabs) %>
1
2
  <%= content_for(:page_tabs) do %>
2
- <%= nav_item(Spree.t(:tax_rates), spree.admin_tax_rates_path) if can?(:manage, Spree::TaxRate) %>
3
- <%= nav_item(Spree.t(:tax_categories), spree.admin_tax_categories_path) if can?(:manage, Spree::TaxCategory) %>
4
3
  <%= render_admin_partials(:tax_nav_partials) %>
5
4
  <% end %>
@@ -1,9 +1,8 @@
1
1
  <% content_for :page_title, Spree.t(:users) %>
2
2
 
3
+ <%= render_tab_navigation(:team_tabs) %>
4
+
3
5
  <%= content_for(:page_tabs) do %>
4
- <%= nav_item(Spree.t(:users), spree.admin_admin_users_path) if can?(:manage, Spree.admin_user_class) %>
5
- <%= nav_item(Spree.t(:invitations), spree.admin_invitations_path) if can?(:manage, Spree::Invitation) %>
6
- <%= nav_item(Spree.t(:roles), spree.admin_roles_path) if can?(:manage, Spree::Role) %>
7
6
  <%= render_admin_partials(:team_nav_partials) %>
8
7
  <% end %>
9
8