spree_admin 5.5.3 → 5.6.0.rc1

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 (146) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/images/vercel-deploy-button.svg +14 -0
  3. data/app/assets/tailwind/spree/admin/components/_alerts.css +5 -1
  4. data/app/assets/tailwind/spree/admin/components/_badges.css +1 -1
  5. data/app/assets/tailwind/spree/admin/components/_breadcrumbs.css +1 -1
  6. data/app/assets/tailwind/spree/admin/components/_buttons.css +1 -1
  7. data/app/assets/tailwind/spree/admin/components/_cards.css +1 -1
  8. data/app/assets/tailwind/spree/admin/components/_dialogs.css +3 -1
  9. data/app/assets/tailwind/spree/admin/components/_dropdowns.css +2 -2
  10. data/app/assets/tailwind/spree/admin/components/_filters.css +1 -1
  11. data/app/assets/tailwind/spree/admin/components/_forms.css +10 -6
  12. data/app/assets/tailwind/spree/admin/components/_layout.css +30 -23
  13. data/app/assets/tailwind/spree/admin/components/_rtl.css +53 -0
  14. data/app/assets/tailwind/spree/admin/components/_search-picker.css +2 -2
  15. data/app/assets/tailwind/spree/admin/components/_tables.css +1 -1
  16. data/app/assets/tailwind/spree/admin/index.css +1 -0
  17. data/app/assets/tailwind/spree/admin/plugins/_tom-select.css +6 -6
  18. data/app/assets/tailwind/spree/admin/plugins/_trix.css +1 -1
  19. data/app/assets/tailwind/spree/admin/plugins/_uppy.css +1 -1
  20. data/app/assets/tailwind/spree/admin/views/_page-builder.css +8 -5
  21. data/app/controllers/concerns/spree/admin/locale_concern.rb +76 -0
  22. data/app/controllers/spree/admin/base_controller.rb +38 -2
  23. data/app/controllers/spree/admin/countries_controller.rb +18 -4
  24. data/app/controllers/spree/admin/exports_controller.rb +7 -0
  25. data/app/controllers/spree/admin/imports_controller.rb +8 -0
  26. data/app/controllers/spree/admin/products_controller.rb +1 -0
  27. data/app/controllers/spree/admin/storefront_controller.rb +100 -0
  28. data/app/controllers/spree/admin/user_sessions_controller.rb +10 -0
  29. data/app/controllers/spree/admin/variants_controller.rb +3 -1
  30. data/app/helpers/spree/admin/base_helper.rb +10 -0
  31. data/app/helpers/spree/admin/channels_helper.rb +17 -0
  32. data/app/helpers/spree/admin/dropdown_helper.rb +14 -0
  33. data/app/helpers/spree/admin/navigation_helper.rb +1 -1
  34. data/app/helpers/spree/admin/onboarding_helper.rb +1 -1
  35. data/app/helpers/spree/admin/orders_helper.rb +9 -34
  36. data/app/helpers/spree/admin/products_helper.rb +1 -1
  37. data/app/helpers/spree/admin/rtl_helper.rb +15 -0
  38. data/app/helpers/spree/admin/shipment_helper.rb +1 -1
  39. data/app/helpers/spree/admin/storefront_helper.rb +46 -0
  40. data/app/helpers/spree/admin/stores_helper.rb +2 -1
  41. data/app/javascript/spree/admin/application.js +2 -0
  42. data/app/javascript/spree/admin/controllers/autocomplete_select_controller.js +4 -0
  43. data/app/javascript/spree/admin/controllers/calendar_range_controller.js +7 -3
  44. data/app/javascript/spree/admin/controllers/display_name_controller.js +18 -0
  45. data/app/javascript/spree/admin/controllers/query_builder_controller.js +39 -28
  46. data/app/javascript/spree/admin/controllers/variants_form_controller.js +49 -1
  47. data/app/javascript/spree/admin/helpers/display_names.js +54 -0
  48. data/app/models/spree/admin/table/column.rb +15 -1
  49. data/app/models/spree/admin/table/filter.rb +23 -16
  50. data/app/models/spree/admin/table/query_builder.rb +11 -2
  51. data/app/views/active_storage/_upload_form.html.erb +1 -1
  52. data/app/views/layouts/spree/admin.html.erb +1 -1
  53. data/app/views/layouts/spree/admin_settings.html.erb +1 -1
  54. data/app/views/layouts/spree/admin_wizard.html.erb +2 -2
  55. data/app/views/layouts/spree/minimal.html.erb +1 -1
  56. data/app/views/spree/admin/admin_users/_audit_log.html.erb +2 -2
  57. data/app/views/spree/admin/admin_users/new.html.erb +1 -1
  58. data/app/views/spree/admin/assets/edit.html.erb +1 -1
  59. data/app/views/spree/admin/channels/_form.html.erb +10 -0
  60. data/app/views/spree/admin/dashboard/_setup_progress.html.erb +4 -4
  61. data/app/views/spree/admin/dashboard/_setup_tasks.html.erb +5 -4
  62. data/app/views/spree/admin/dashboard/_updater.html.erb +4 -4
  63. data/app/views/spree/admin/dashboard/_visits.html.erb +12 -12
  64. data/app/views/spree/admin/dashboard/analytics.html.erb +5 -5
  65. data/app/views/spree/admin/dashboard/setup_tasks/_add_products.html.erb +4 -4
  66. data/app/views/spree/admin/dashboard/setup_tasks/_set_customer_support_email.html.erb +3 -4
  67. data/app/views/spree/admin/dashboard/setup_tasks/_setup_storefront.html.erb +18 -0
  68. data/app/views/spree/admin/gift_card_batches/_form.html.erb +1 -1
  69. data/app/views/spree/admin/import_rows/show.html.erb +2 -2
  70. data/app/views/spree/admin/imports/_footer.html.erb +1 -1
  71. data/app/views/spree/admin/imports/_mapping_form.html.erb +1 -1
  72. data/app/views/spree/admin/imports/show.html.erb +3 -3
  73. data/app/views/spree/admin/integrations/_integration.html.erb +1 -1
  74. data/app/views/spree/admin/integrations/index.html.erb +2 -4
  75. data/app/views/spree/admin/invitations/_invitation.html.erb +1 -1
  76. data/app/views/spree/admin/invitations/new.html.erb +1 -1
  77. data/app/views/spree/admin/invitations/show.html.erb +1 -1
  78. data/app/views/spree/admin/line_items/new.html.erb +1 -1
  79. data/app/views/spree/admin/markets/_form.html.erb +8 -6
  80. data/app/views/spree/admin/metafields/edit.html.erb +1 -1
  81. data/app/views/spree/admin/option_types/_form.html.erb +9 -6
  82. data/app/views/spree/admin/option_types/index.html.erb +2 -3
  83. data/app/views/spree/admin/orders/_customer.html.erb +1 -1
  84. data/app/views/spree/admin/orders/_customer_summary.html.erb +1 -1
  85. data/app/views/spree/admin/orders/_payments.html.erb +1 -1
  86. data/app/views/spree/admin/orders/_shipment.html.erb +7 -2
  87. data/app/views/spree/admin/orders/_shipments.html.erb +1 -1
  88. data/app/views/spree/admin/orders/_user_overview.html.erb +9 -6
  89. data/app/views/spree/admin/orders/return_authorizations/edit.html.erb +1 -1
  90. data/app/views/spree/admin/payment_methods/_form.html.erb +1 -1
  91. data/app/views/spree/admin/payment_methods/index.html.erb +2 -2
  92. data/app/views/spree/admin/payments/_payment.html.erb +1 -1
  93. data/app/views/spree/admin/price_lists/index.html.erb +2 -2
  94. data/app/views/spree/admin/products/form/_inventory.html.erb +19 -0
  95. data/app/views/spree/admin/products/form/_publishing.html.erb +2 -3
  96. data/app/views/spree/admin/products/index.html.erb +1 -2
  97. data/app/views/spree/admin/profile/edit.html.erb +3 -0
  98. data/app/views/spree/admin/promotion_rules/forms/_channel.html.erb +7 -0
  99. data/app/views/spree/admin/promotion_rules/forms/_country.html.erb +1 -1
  100. data/app/views/spree/admin/promotion_rules/forms/_item_total.html.erb +1 -1
  101. data/app/views/spree/admin/promotion_rules/forms/_market.html.erb +7 -0
  102. data/app/views/spree/admin/promotions/_form.html.erb +2 -2
  103. data/app/views/spree/admin/promotions/_sidebar.html.erb +1 -1
  104. data/app/views/spree/admin/promotions/edit.html.erb +2 -2
  105. data/app/views/spree/admin/promotions/form/_kind.html.erb +11 -11
  106. data/app/views/spree/admin/promotions/form/_settings.html.erb +4 -5
  107. data/app/views/spree/admin/reports/new.html.erb +2 -2
  108. data/app/views/spree/admin/shared/_address.html.erb +1 -1
  109. data/app/views/spree/admin/shared/_analytics_loader_skeleton.html.erb +5 -5
  110. data/app/views/spree/admin/shared/_calendar_range_picker.html.erb +11 -1
  111. data/app/views/spree/admin/shared/_content_header.html.erb +2 -2
  112. data/app/views/spree/admin/shared/_edit_resource_links.html.erb +1 -1
  113. data/app/views/spree/admin/shared/_filters_search_bar.html.erb +1 -1
  114. data/app/views/spree/admin/shared/_growth_rate_badge.html.erb +1 -1
  115. data/app/views/spree/admin/shared/_head.html.erb +4 -1
  116. data/app/views/spree/admin/shared/_index_table_options.html.erb +2 -2
  117. data/app/views/spree/admin/shared/_media_asset.html.erb +1 -1
  118. data/app/views/spree/admin/shared/_media_form.html.erb +1 -1
  119. data/app/views/spree/admin/shared/_seo.html.erb +1 -1
  120. data/app/views/spree/admin/shared/_sidebar.html.erb +1 -1
  121. data/app/views/spree/admin/shared/sidebar/_enterprise_edition_notice.html.erb +2 -2
  122. data/app/views/spree/admin/shared/sortable_tree/_taxonomy.html.erb +1 -1
  123. data/app/views/spree/admin/shipping_methods/form/_display.html.erb +2 -2
  124. data/app/views/spree/admin/shipping_methods/form/_estimated_transit_business_days.html.erb +1 -2
  125. data/app/views/spree/admin/shipping_methods/form/_tracking_url.html.erb +1 -1
  126. data/app/views/spree/admin/shipping_methods/form/_zones.html.erb +1 -1
  127. data/app/views/spree/admin/shipping_methods/index.html.erb +1 -1
  128. data/app/views/spree/admin/storefront/show.html.erb +150 -0
  129. data/app/views/spree/admin/stores/form/_basic.html.erb +2 -2
  130. data/app/views/spree/admin/stores/form/_checkout.html.erb +4 -0
  131. data/app/views/spree/admin/stores/form/_emails.html.erb +4 -6
  132. data/app/views/spree/admin/tables/_table.html.erb +3 -2
  133. data/app/views/spree/admin/taxons/_form.html.erb +2 -2
  134. data/app/views/spree/admin/user_sessions/new.html.erb +11 -2
  135. data/app/views/spree/admin/users/_tabs.html.erb +1 -1
  136. data/app/views/spree/admin/variants/_search_result.html.erb +1 -1
  137. data/app/views/spree/admin/variants/_variant.html.erb +1 -1
  138. data/app/views/spree/admin/variants/form/_inventory.html.erb +17 -0
  139. data/app/views/spree/admin/webhook_endpoints/_form.html.erb +2 -2
  140. data/app/views/spree/admin/zones/index.html.erb +1 -1
  141. data/config/i18n-tasks.yml +3 -0
  142. data/config/initializers/spree_admin_navigation.rb +9 -0
  143. data/config/initializers/spree_admin_tables.rb +20 -18
  144. data/config/locales/en.yml +647 -0
  145. data/config/routes.rb +5 -0
  146. metadata +18 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7e631d3077a757dbf4d85a7d7186f3a540aa212194a760bd2fd0b84c868b10f5
4
- data.tar.gz: 5696eeb0ae858a76b940650bf801ac03466372f1cd5529ae7a7a9c027f46b53f
3
+ metadata.gz: 112e0c3ee899331fe67fe9874d7db6b61b37e9d550acfed787b83122370014e9
4
+ data.tar.gz: 044e5b68c2fea6efe2d86709ca3d450914d9b1605945f38667ffca087ab0ff0e
5
5
  SHA512:
6
- metadata.gz: bf5b38f7ee184476edc920994977207aa759a4aa7eabc6cf7efdc9eaff19715acc483fede552caa935127787d667e6b33a39ea572360ab8fcaf7a6f698f10089
7
- data.tar.gz: a295010282908ae8e0e4f8a00413153b9eea9623213e8a4d2ab57033ccca239d2d969112c12acbe71d8de863272369b18b536c151070a4afdd811663b7dc5e31
6
+ metadata.gz: 35b0083ddb0541ca5bc4e2c4e33ec788335a97636cb907cbea742b5524bf9dbba311057de5fcc3439805a08eda45e92318626dc62650e38fc505cef2e1754794
7
+ data.tar.gz: 361c2693dc6870aab5bc23b95f45b43121e05c76d889058e7a0f69474520314c5cccbcf9af0993746feadc50d050b3b5236a3bef52034f7b4ef1426a32d53f31
@@ -0,0 +1,14 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="103" height="32" fill="none">
2
+ <rect width="103" height="32" fill="#1A1A1A" rx="6"/>
3
+ <rect width="102" height="31" x=".5" y=".5" stroke="#fff" stroke-opacity=".14" rx="5.5" />
4
+ <g clip-path="url(#a)">
5
+ <path fill="#EDEDED" fill-rule="evenodd" d="m16 10.75 6 10.5H10l6-10.5Z" clip-rule="evenodd" />
6
+ </g>
7
+ <path stroke="#fff" stroke-opacity=".14" d="M31.5 1v30" />
8
+ <path fill="#EDEDED" d="M47.4 11.06c3.1 0 4.85 1.8 4.85 4.98 0 3.18-1.7 4.96-4.77 4.96h-3.26v-9.94h3.18Zm-1.7 8.57h1.7c2.22 0 3.31-1.18 3.31-3.59 0-2.43-1.09-3.6-3.31-3.6h-1.7v7.19Zm7.83-2.34c0-2.37 1.38-3.88 3.51-3.88 1.74 0 3.25 1.15 3.33 3.8l.02.5h-5.34c.12 1.39.82 2.2 2 2.2.73 0 1.4-.42 1.7-1.14l1.53.12a3.3 3.3 0 0 1-3.24 2.28c-2.13 0-3.51-1.51-3.51-3.88Zm1.55-.7h3.77c-.2-1.43-.97-1.92-1.8-1.92-1.1 0-1.79.72-1.97 1.92Zm6.98 6.51v-9.52h1.36l.04 1.08c.4-.8 1.17-1.25 2.25-1.25 2.19 0 3.2 1.8 3.2 3.88 0 2.07-1.01 3.88-3.2 3.88-1.02 0-1.78-.4-2.2-1.18v3.11h-1.45Zm1.31-5.81c0 1.33.6 2.6 2 2.6 1.42 0 2-1.26 2-2.6 0-1.33-.58-2.6-2-2.6-1.4 0-2 1.27-2 2.6Zm7.36-6.23h1.45v8.25c0 .33.16.49.48.49h.62V21h-.93c-.98 0-1.62-.63-1.62-1.62v-8.32Zm7.1 10.1c-2.13 0-3.52-1.5-3.52-3.87s1.39-3.88 3.51-3.88c2.13 0 3.52 1.51 3.52 3.88s-1.39 3.88-3.52 3.88Zm-2-3.87c0 1.62.72 2.6 2 2.6 1.27 0 2-.98 2-2.6s-.73-2.6-2-2.6c-1.28 0-2 .98-2 2.6Zm6.07-3.71h1.49l2.11 5.88 2.05-5.88h1.47l-2.97 8.29c-.31.85-.87 1.23-1.78 1.23h-1v-1.19h.8c.38 0 .6-.14.73-.49l.25-.63h-.46l-2.69-7.21Z"/>
9
+ <defs>
10
+ <clipPath id="a">
11
+ <path fill="#fff" d="M0 0h12v12H0z" fill-opacity="1" transform="translate(10 10)"/>
12
+ </clipPath>
13
+ </defs>
14
+ </svg>
@@ -4,11 +4,15 @@
4
4
 
5
5
  @layer components {
6
6
  .alert {
7
- @apply inline-flex flex-col justify-center relative bg-no-repeat py-3 px-4 pl-11 mb-4 min-h-10 border border-transparent rounded-lg;
7
+ @apply inline-flex flex-col justify-center relative bg-no-repeat py-3 px-4 ps-11 mb-4 min-h-10 border border-transparent rounded-lg;
8
8
  background-position: 1rem center;
9
9
  background-size: 1rem 1rem;
10
10
  }
11
11
 
12
+ [dir="rtl"] .alert {
13
+ background-position: right 1rem center;
14
+ }
15
+
12
16
  .alert > p:last-child {
13
17
  @apply mb-0;
14
18
  }
@@ -8,7 +8,7 @@
8
8
  }
9
9
 
10
10
  .badge i {
11
- @apply mr-1 text-[100%];
11
+ @apply me-1 text-[100%];
12
12
  }
13
13
 
14
14
  .badge .ti {
@@ -5,7 +5,7 @@
5
5
  @layer components {
6
6
  .breadcrumb {
7
7
  @apply flex flex-wrap items-center mb-0 list-none font-medium bg-transparent rounded-none;
8
- @apply gap-2 p-0 pl-4 h-8 border-l border-gray-100;
8
+ @apply gap-2 p-0 ps-4 h-8 border-s border-gray-100;
9
9
  }
10
10
 
11
11
  .breadcrumb li a {
@@ -109,6 +109,6 @@
109
109
  }
110
110
 
111
111
  .actions .btn {
112
- @apply ml-1;
112
+ @apply ms-1;
113
113
  }
114
114
  }
@@ -127,7 +127,7 @@
127
127
  }
128
128
 
129
129
  .card-link + .card-link {
130
- @apply ml-3;
130
+ @apply ms-3;
131
131
  }
132
132
 
133
133
  .card-img-top {
@@ -69,7 +69,9 @@
69
69
  ========================================================================== */
70
70
 
71
71
  .drawer {
72
- @apply fixed top-0 right-0 left-auto flex-col justify-between p-0 overflow-hidden;
72
+ @apply fixed top-0 flex-col justify-between p-0 overflow-hidden;
73
+ inset-inline-end: 0;
74
+ inset-inline-start: auto;
73
75
  height: calc(100dvh - 1rem);
74
76
  max-height: calc(100dvh - 1rem);
75
77
  width: calc(100vw - 1rem);
@@ -73,11 +73,11 @@
73
73
 
74
74
  .dropdown-item {
75
75
  @apply flex items-center justify-start w-full clear-both text-inherit no-underline whitespace-nowrap bg-transparent border-0 font-normal;
76
- @apply h-8 mb-1 pr-8 rounded-xl transition-colors duration-75 ease-linear gap-3;
76
+ @apply h-8 mb-1 pe-8 rounded-xl transition-colors duration-75 ease-linear gap-3;
77
77
  }
78
78
 
79
79
  .dropdown-item .badge {
80
- @apply ml-auto self-center;
80
+ @apply ms-auto self-center;
81
81
  }
82
82
 
83
83
  .dropdown-item:hover,
@@ -17,7 +17,7 @@
17
17
 
18
18
 
19
19
  #table-filter {
20
- @apply absolute p-4 rounded-2xl shadow-dropdown bg-white mt-2 right-2 w-full overflow-y-auto;
20
+ @apply absolute p-4 rounded-2xl shadow-dropdown bg-white mt-2 end-2 w-full overflow-y-auto;
21
21
  animation: fadeIn 0.2s ease-in-out;
22
22
  z-index: 1050;
23
23
  max-height: calc(100vh - var(--spacing-header-height) - 4rem);
@@ -124,11 +124,11 @@
124
124
  }
125
125
 
126
126
  .custom-control-label {
127
- @apply relative mb-0 align-top cursor-pointer select-none pl-7 font-normal;
127
+ @apply relative mb-0 align-top cursor-pointer select-none ps-7 font-normal;
128
128
  }
129
129
 
130
130
  .custom-control-label::before {
131
- @apply absolute left-0 top-0.5 block pointer-events-none bg-white border border-gray-300 rounded;
131
+ @apply absolute start-0 top-0.5 block pointer-events-none bg-white border border-gray-300 rounded;
132
132
  content: "";
133
133
  width: 1.125rem;
134
134
  height: 1.125rem;
@@ -139,7 +139,7 @@
139
139
  }
140
140
 
141
141
  .custom-control-label::after {
142
- @apply absolute left-0 top-0.5 block bg-no-repeat bg-center;
142
+ @apply absolute start-0 top-0.5 block bg-no-repeat bg-center;
143
143
  content: "";
144
144
  width: 1.125rem;
145
145
  height: 1.125rem;
@@ -207,18 +207,18 @@
207
207
 
208
208
  /* Custom switch styles */
209
209
  .custom-switch {
210
- @apply pl-12;
210
+ @apply ps-12;
211
211
  }
212
212
 
213
213
  .custom-switch .custom-control-label::before {
214
- @apply left-0 rounded-full bg-gray-200 border-gray-200;
214
+ @apply start-0 rounded-full bg-gray-200 border-gray-200;
215
215
  width: 2.5rem;
216
216
  height: 1.25rem;
217
217
  pointer-events: all;
218
218
  }
219
219
 
220
220
  .custom-switch .custom-control-label::after {
221
- @apply left-0.5 top-1 rounded-full bg-white shadow;
221
+ @apply start-0.5 top-1 rounded-full bg-white shadow;
222
222
  width: 1rem;
223
223
  height: 1rem;
224
224
  transition:
@@ -235,6 +235,10 @@
235
235
  transform: translateX(1.25rem);
236
236
  }
237
237
 
238
+ [dir="rtl"] .custom-switch .custom-control-input:checked ~ .custom-control-label::after {
239
+ transform: translateX(-1.25rem);
240
+ }
241
+
238
242
  .custom-switch .custom-control-input:focus ~ .custom-control-label::before {
239
243
  @apply ring-2 ring-blue-500/25 ring-offset-1;
240
244
  }
@@ -8,17 +8,17 @@
8
8
  height: var(--spacing-header-height);
9
9
  background-color: var(--color-white);
10
10
  box-shadow: inset 0 -1px 0 var(--color-gray-200);
11
- transition: margin-left 0.1s ease;
11
+ transition: margin-inline-start 0.1s ease;
12
12
  }
13
13
 
14
14
  @media (min-width: 992px) {
15
15
  #header {
16
- margin-left: var(--spacing-sidebar-width);
16
+ margin-inline-start: var(--spacing-sidebar-width);
17
17
  }
18
18
  }
19
19
 
20
20
  body.sidebar-collapsed #header {
21
- margin-left: var(--spacing-sidebar-collapsed);
21
+ margin-inline-start: var(--spacing-sidebar-collapsed);
22
22
  }
23
23
 
24
24
  #page-header {
@@ -48,17 +48,17 @@
48
48
  #settings-header {
49
49
  @apply sticky top-0 flex items-center p-0 bg-white z-1000;
50
50
  height: var(--spacing-header-height);
51
- transition: margin-left 0.1s ease;
51
+ transition: margin-inline-start 0.1s ease;
52
52
  }
53
53
 
54
54
  @media (min-width: 992px) {
55
55
  #settings-header {
56
- margin-left: var(--spacing-sidebar-width);
56
+ margin-inline-start: var(--spacing-sidebar-width);
57
57
  }
58
58
  }
59
59
 
60
60
  body.sidebar-collapsed #settings-header {
61
- margin-left: var(--spacing-sidebar-collapsed);
61
+ margin-inline-start: var(--spacing-sidebar-collapsed);
62
62
  }
63
63
 
64
64
  #settings-header #page-header {
@@ -93,18 +93,18 @@
93
93
 
94
94
  /* Main Content */
95
95
  #content {
96
- transition: margin-left 0.1s ease;
96
+ transition: margin-inline-start 0.1s ease;
97
97
  }
98
98
 
99
99
  @media (min-width: 992px) {
100
100
  #content {
101
- margin-left: calc(var(--spacing-sidebar-width) + 1px);
101
+ margin-inline-start: calc(var(--spacing-sidebar-width) + 1px);
102
102
  }
103
103
  }
104
104
 
105
105
  /* Store Dropdown */
106
106
  .store-dropdown {
107
- @apply flex items-center justify-between pr-3 pl-2 mr-px;
107
+ @apply flex items-center justify-between pe-3 ps-2 me-px;
108
108
  transition: padding 0.1s ease;
109
109
  height: var(--spacing-header-height);
110
110
  }
@@ -131,7 +131,7 @@
131
131
 
132
132
  /* User Dropdown */
133
133
  .user-dropdown {
134
- @apply hidden items-center justify-between p-2 bg-gray-25 border-t border-gray-200 mt-auto mr-px;
134
+ @apply hidden items-center justify-between p-2 bg-gray-25 border-t border-gray-200 mt-auto me-px;
135
135
  }
136
136
 
137
137
  @media (min-width: 992px) {
@@ -175,7 +175,8 @@
175
175
  #main-sidebar {
176
176
  @apply flex flex-col gap-0 bg-gray-25 h-screen overflow-x-hidden fixed top-0;
177
177
  width: var(--spacing-sidebar-width);
178
- box-shadow: inset -1px 0 0 var(--color-gray-200);
178
+ inset-inline-start: 0;
179
+ border-inline-end: 1px solid var(--color-gray-200);
179
180
  }
180
181
 
181
182
  @media (max-width: 992px) {
@@ -198,7 +199,7 @@
198
199
  }
199
200
 
200
201
  #main-sidebar .nav-link .ti {
201
- @apply mr-2 rounded-lg shrink-0 text-gray-400 transition-colors duration-200 ease-in-out;
202
+ @apply me-2 rounded-lg shrink-0 text-gray-400 transition-colors duration-200 ease-in-out;
202
203
  padding: 0.2rem;
203
204
  }
204
205
 
@@ -224,11 +225,11 @@
224
225
  }
225
226
 
226
227
  #main-sidebar .nav-submenu {
227
- @apply flex flex-col list-none pt-1 pl-8 relative;
228
+ @apply flex flex-col list-none pt-1 ps-8 relative;
228
229
  }
229
230
 
230
231
  #main-sidebar .nav-submenu:before {
231
- @apply absolute bg-gray-200 left-4 top-0 bottom-1;
232
+ @apply absolute bg-gray-200 start-4 top-0 bottom-1;
232
233
  content: '';
233
234
  width: 1px;
234
235
  }
@@ -252,7 +253,7 @@
252
253
  #main-sidebar .nav-submenu .nav-link:focus:before {
253
254
  @apply absolute bg-zinc-950 rounded-sm;
254
255
  content: '';
255
- left: calc(-1rem - 1px);
256
+ inset-inline-start: calc(-1rem - 1px);
256
257
  top: 10%;
257
258
  width: 3px;
258
259
  height: 80%;
@@ -309,7 +310,7 @@
309
310
  }
310
311
 
311
312
  body.sidebar-collapsed #main-sidebar .sidebar-content .nav > .nav-item > .nav-link .badge {
312
- @apply flex absolute top-0 right-0 justify-center items-center text-2xs px-1 py-0 rounded-full;
313
+ @apply flex absolute top-0 end-0 justify-center items-center text-2xs px-1 py-0 rounded-full;
313
314
  transform: translate(25%, -25%);
314
315
  min-width: 18px;
315
316
  height: 18px;
@@ -414,7 +415,7 @@
414
415
  }
415
416
 
416
417
  body.sidebar-collapsed #content {
417
- margin-left: calc(var(--spacing-sidebar-collapsed) + 1px);
418
+ margin-inline-start: calc(var(--spacing-sidebar-collapsed) + 1px);
418
419
  }
419
420
 
420
421
  /* Mobile Sidebar */
@@ -427,8 +428,14 @@
427
428
  }
428
429
 
429
430
  .sidebar-mobile .sidebar-panel {
430
- @apply absolute left-0 top-0 bottom-0 bg-gray-25 overflow-y-auto flex flex-col shadow-lg transition-transform duration-500 -translate-x-full;
431
+ @apply absolute top-0 bottom-0 bg-gray-25 overflow-y-auto flex flex-col shadow-lg transition-transform duration-500;
432
+ inset-inline-start: 0;
431
433
  width: var(--spacing-sidebar-width);
434
+ transform: translateX(-100%);
435
+ }
436
+
437
+ [dir="rtl"] .sidebar-mobile .sidebar-panel {
438
+ transform: translateX(100%);
432
439
  }
433
440
 
434
441
  .sidebar-mobile .sidebar-mobile-header {
@@ -459,7 +466,7 @@
459
466
  }
460
467
 
461
468
  .sidebar-mobile .sidebar-mobile-content .nav-link .ti {
462
- @apply mr-2 rounded-lg shrink-0;
469
+ @apply me-2 rounded-lg shrink-0;
463
470
  padding: 0.2rem;
464
471
  }
465
472
 
@@ -485,11 +492,11 @@
485
492
  }
486
493
 
487
494
  .sidebar-mobile .sidebar-mobile-content .nav-submenu {
488
- @apply flex flex-col list-none pt-1 pl-8 relative;
495
+ @apply flex flex-col list-none pt-1 ps-8 relative;
489
496
  }
490
497
 
491
498
  .sidebar-mobile .sidebar-mobile-content .nav-submenu:before {
492
- @apply absolute bg-gray-200 left-4 top-0 bottom-1;
499
+ @apply absolute bg-gray-200 start-4 top-0 bottom-1;
493
500
  content: '';
494
501
  width: 1px;
495
502
  }
@@ -513,7 +520,7 @@
513
520
  .sidebar-mobile .sidebar-mobile-content .nav-submenu .nav-link:focus:before {
514
521
  @apply absolute bg-zinc-950 rounded-sm;
515
522
  content: '';
516
- left: calc(-1rem - 1px);
523
+ inset-inline-start: calc(-1rem - 1px);
517
524
  top: 10%;
518
525
  width: 3px;
519
526
  height: 80%;
@@ -565,7 +572,7 @@
565
572
  }
566
573
 
567
574
  #enterprise-edition-notice .btn-close {
568
- @apply absolute right-1 top-1 text-inherit!;
575
+ @apply absolute end-1 top-1 text-inherit!;
569
576
  }
570
577
 
571
578
  #enterprise-edition-notice .btn-close:hover {
@@ -0,0 +1,53 @@
1
+ /* ==========================================================================
2
+ RTL — direction-specific rules that logical properties can't express.
3
+
4
+ Layout/spacing flips are handled in the base component CSS via logical
5
+ utilities (ms-/me-/ps-/pe-/start-/end-/border-s/border-e) and logical
6
+ properties (margin-inline-start, inset-inline-start, …), which mirror
7
+ automatically under <html dir="rtl">. Only transforms, keyframe
8
+ animations, background positions, and the script font remain here.
9
+ ========================================================================== */
10
+
11
+ @layer components {
12
+ /* Arabic-capable typeface for RTL locales */
13
+ [dir="rtl"] body.admin {
14
+ font-family: "Noto Sans Arabic", "Geist", ui-sans-serif, system-ui, sans-serif;
15
+ }
16
+
17
+ /* Sidebar collapse chevron — rotation is a transform, not a logical prop */
18
+ [dir="rtl"] body.sidebar-collapsed .sidebar-toggle-btn {
19
+ transform: rotate(0deg);
20
+ }
21
+
22
+ /* Collapsed nav badge — translate must flip horizontally */
23
+ [dir="rtl"] body.sidebar-collapsed #main-sidebar .sidebar-content .nav > .nav-item > .nav-link .badge {
24
+ transform: translate(-25%, -25%);
25
+ }
26
+
27
+ /* Drawer slides from the inline-start edge in RTL */
28
+ [dir="rtl"] .drawer[open] {
29
+ animation: slideInLeft 0.25s ease-out forwards;
30
+ }
31
+
32
+ [dir="rtl"] dialog.drawer[closing] {
33
+ animation: slideOutLeft 0.2s ease-in forwards;
34
+ }
35
+
36
+ @keyframes slideInLeft {
37
+ from {
38
+ transform: translateX(-100%);
39
+ }
40
+ to {
41
+ transform: translateX(0);
42
+ }
43
+ }
44
+
45
+ @keyframes slideOutLeft {
46
+ from {
47
+ transform: translateX(0);
48
+ }
49
+ to {
50
+ transform: translateX(-100%);
51
+ }
52
+ }
53
+ }
@@ -8,7 +8,7 @@
8
8
  }
9
9
 
10
10
  .search-picker__option {
11
- @apply relative mb-2 flex cursor-pointer items-center border border-transparent rounded-md pr-8;
11
+ @apply relative mb-2 flex cursor-pointer items-center border border-transparent rounded-md pe-8;
12
12
  }
13
13
 
14
14
  .search-picker__option label {
@@ -29,7 +29,7 @@
29
29
  }
30
30
 
31
31
  .search-picker__option:has(input:checked)::after {
32
- @apply inline-block absolute right-2 w-6 h-6;
32
+ @apply inline-block absolute end-2 w-6 h-6;
33
33
  content: '';
34
34
  background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-check text-zinc-950"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M5 12l5 5l10 -10"/></svg>') no-repeat center center;
35
35
  }
@@ -121,7 +121,7 @@
121
121
 
122
122
  /* Filters search bar */
123
123
  #filters-search-bar {
124
- @apply max-w-xs mr-auto;
124
+ @apply max-w-xs me-auto;
125
125
  }
126
126
 
127
127
  /* Table active filters */
@@ -47,6 +47,7 @@
47
47
 
48
48
  /* Components - Layout */
49
49
  @import "./components/_layout.css";
50
+ @import "./components/_rtl.css";
50
51
  @import "./components/_filters.css";
51
52
  @import "./components/_search-picker.css";
52
53
  @import "./components/_sortable-tree.css";
@@ -16,7 +16,7 @@
16
16
  /* Dropdown */
17
17
  .ts-dropdown,
18
18
  .ts-dropdown.form-input {
19
- @apply absolute mt-2! top-full left-0 w-full h-auto p-1 z-[10000000]! bg-white rounded-lg;
19
+ @apply absolute mt-2! top-full start-0 w-full h-auto p-1 z-[10000000]! bg-white rounded-lg;
20
20
  box-shadow: var(--shadow-dropdown);
21
21
  }
22
22
 
@@ -34,7 +34,7 @@
34
34
  }
35
35
 
36
36
  .ts-dropdown .create {
37
- @apply pl-3;
37
+ @apply ps-3;
38
38
  }
39
39
 
40
40
  .ts-dropdown-content {
@@ -131,7 +131,7 @@
131
131
 
132
132
  /* Multi-select items */
133
133
  .ts-wrapper.multi .ts-control > div {
134
- @apply cursor-pointer mr-[3px] mb-[3px] py-[1px] px-[5px] text-gray-800 border-0 border-gray-200 rounded-[calc(var(--radius-md)-1px)];
134
+ @apply cursor-pointer me-[3px] mb-[3px] py-[1px] px-[5px] text-gray-800 border-0 border-gray-200 rounded-[calc(var(--radius-md)-1px)];
135
135
  background: #efefef;
136
136
  }
137
137
 
@@ -140,7 +140,7 @@
140
140
  }
141
141
 
142
142
  .ts-wrapper.multi.has-items .ts-control {
143
- @apply pl-[calc(0.75rem-5px)];
143
+ @apply ps-[calc(0.75rem-5px)];
144
144
  }
145
145
 
146
146
  /* Disabled state */
@@ -266,7 +266,7 @@
266
266
  }
267
267
 
268
268
  .ts-wrapper .ts-control > .item .remove {
269
- @apply inline-block align-middle text-base font-semibold ml-[3px] py-0 px-[3px] text-inherit no-underline rounded-sm opacity-50;
269
+ @apply inline-block align-middle text-base font-semibold ms-[3px] py-0 px-[3px] text-inherit no-underline rounded-sm opacity-50;
270
270
  }
271
271
 
272
272
  .ts-wrapper .ts-control > .item .remove:hover {
@@ -276,7 +276,7 @@
276
276
 
277
277
  /* Clear button plugin */
278
278
  .ts-wrapper .clear-button {
279
- @apply absolute right-[calc(0.75rem+20px)] top-1/2 -translate-y-1/2 opacity-0 cursor-pointer transition-opacity duration-200;
279
+ @apply absolute end-[calc(0.75rem+20px)] top-1/2 -translate-y-1/2 opacity-0 cursor-pointer transition-opacity duration-200;
280
280
  }
281
281
 
282
282
  .ts-wrapper.focus .clear-button,
@@ -85,7 +85,7 @@
85
85
  }
86
86
 
87
87
  trix-toolbar .trix-dialog .trix-button {
88
- @apply inline-block text-center align-middle select-none text-sm leading-normal bg-gray-50 font-normal py-2 px-4 mr-1 mb-0 rounded-lg border border-gray-200!;
88
+ @apply inline-block text-center align-middle select-none text-sm leading-normal bg-gray-50 font-normal py-2 px-4 me-1 mb-0 rounded-lg border border-gray-200!;
89
89
  }
90
90
 
91
91
  trix-toolbar .trix-dialog__link-fields {
@@ -66,7 +66,7 @@
66
66
  }
67
67
 
68
68
  .uppy-Dashboard-browse {
69
- @apply ml-2;
69
+ @apply ms-2;
70
70
  }
71
71
 
72
72
  /* Save button - btn btn-primary btn-sm styles */
@@ -11,7 +11,7 @@
11
11
  }
12
12
 
13
13
  #page-builder #content {
14
- @apply ml-0 pt-0;
14
+ @apply ms-0 pt-0;
15
15
  }
16
16
 
17
17
  #page-builder #main-part {
@@ -51,7 +51,9 @@
51
51
  }
52
52
 
53
53
  #page-builder-sidebar {
54
- @apply fixed left-0 bg-white border-r border-gray-200 overflow-x-hidden;
54
+ @apply fixed bg-white overflow-x-hidden;
55
+ inset-inline-start: 0;
56
+ border-inline-end: 1px solid var(--color-gray-200);
55
57
  width: var(--pb-sidebar-width);
56
58
  top: var(--pb-header-height);
57
59
  height: calc(100vh - var(--pb-header-height));
@@ -64,7 +66,8 @@
64
66
  }
65
67
 
66
68
  #page-builder-sidebar .sidebar-footer {
67
- @apply fixed bottom-0 left-0 p-2 bg-white flex items-center justify-between;
69
+ @apply fixed bottom-0 p-2 bg-white flex items-center justify-between;
70
+ inset-inline-start: 0;
68
71
  width: calc(var(--pb-sidebar-width) - 1px);
69
72
  box-shadow: 0 0 0 1px var(--color-gray-200);
70
73
  z-index: 1031;
@@ -115,7 +118,7 @@
115
118
  }
116
119
 
117
120
  .sidebar-page-block {
118
- margin-left: 25px;
121
+ margin-inline-start: 25px;
119
122
  }
120
123
 
121
124
  .edit-block .trix-button--icon-heading-1,
@@ -166,7 +169,7 @@
166
169
 
167
170
  @media (min-width: 768px) {
168
171
  #main-part {
169
- margin-left: 339px;
172
+ margin-inline-start: 339px;
170
173
  }
171
174
 
172
175
  .mobileLiveView #page-builder-preview {
@@ -0,0 +1,76 @@
1
+ module Spree
2
+ module Admin
3
+ # Decouples the admin UI language from the store's content language.
4
+ #
5
+ # `I18n.locale` drives the UI chrome (`Spree.t` labels) and follows the
6
+ # staff member's chosen admin language. `Mobility.locale` and
7
+ # `Spree::Current.content_locale` drive translated record fields
8
+ # (product/store names) and are pinned to the store's content locale —
9
+ # otherwise switching the admin to, say, Polish would make every
10
+ # Mobility-backed field fall back to nil for stores that don't translate
11
+ # their catalog into Polish.
12
+ #
13
+ # Shared by `Spree::Admin::BaseController` and the Devise-based pre-auth
14
+ # controllers (`UserSessionsController`), which can't inherit admin
15
+ # behavior via the controller chain — hence a concern both include.
16
+ # Devise-based includers opt into the login-screen locale handling with
17
+ # `before_action :set_login_locale`.
18
+ module LocaleConcern
19
+ extend ActiveSupport::Concern
20
+
21
+ # Cookie storing the admin UI language chosen on the login screen, so a
22
+ # guest's pre-auth selection carries into the authenticated session. A
23
+ # signed-in user's saved `selected_locale` still takes precedence.
24
+ ADMIN_LOCALE_COOKIE = :spree_admin_locale
25
+
26
+ private
27
+
28
+ # Pin content reads to the store's content locale (its default locale),
29
+ # independent of the chosen UI language. Request-local state only — the
30
+ # content locale must never be written to the process-global
31
+ # `I18n.default_locale`, which every thread in the server process shares.
32
+ # The fallback for storeless hosts lives in the
33
+ # `Spree::Current#content_locale` reader.
34
+ def pin_content_locale!
35
+ Spree::Current.content_locale = (current_store&.default_locale if defined?(current_store))
36
+ Mobility.locale = Spree::Current.content_locale
37
+ end
38
+
39
+ # Pre-auth locale handling for Devise-based screens, where the
40
+ # `set_locale` before_action from `Spree::Core::ControllerHelpers::Locale`
41
+ # never runs. A supported `?locale=` is applied and persisted; an
42
+ # unsupported one is ignored rather than shadowing an already-valid
43
+ # cookie. Falls back to the cookie set on a previous visit, then to the
44
+ # application default — always an explicit assignment, so a plain visit
45
+ # can never render in whatever locale the thread's previous request
46
+ # happened to set.
47
+ def set_login_locale
48
+ pin_content_locale!
49
+
50
+ if supported_admin_locale?(params[:locale])
51
+ cookies[ADMIN_LOCALE_COOKIE] = { value: params[:locale], expires: 1.year }
52
+ I18n.locale = params[:locale]
53
+ elsif supported_admin_locale?(admin_locale_cookie)
54
+ I18n.locale = admin_locale_cookie
55
+ else
56
+ I18n.locale = I18n.default_locale
57
+ end
58
+ end
59
+
60
+ def admin_user_selected_locale
61
+ defined?(try_spree_current_user) && try_spree_current_user&.selected_locale
62
+ end
63
+
64
+ def admin_locale_cookie
65
+ cookies[ADMIN_LOCALE_COOKIE]
66
+ end
67
+
68
+ # True when the admin UI is actually translated into +locale+
69
+ # (`Spree.available_locales`) — distinct from the storefront's
70
+ # store-scoped `supported_locale?`.
71
+ def supported_admin_locale?(locale)
72
+ locale.present? && Spree.available_locales.map(&:to_s).include?(locale.to_s)
73
+ end
74
+ end
75
+ end
76
+ end