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
@@ -11,7 +11,7 @@
11
11
  <div class="col-span-6 lg:col-span-4 gap-2">
12
12
  <div class="analytics-card-tab active" data-tabs-target="tab" data-action="click->tabs#select">
13
13
  <div class="flex justify-center flex-col mb-6 lg:mb-0">
14
- <span class="text-sm text-gray-500">Total Sales</span>
14
+ <span class="text-sm text-gray-500"><%= Spree.t(:total_sales) %></span>
15
15
  <span class="text-lg text-gray-900 font-bold mt-1">
16
16
  <%= @sales_total %>
17
17
  </span>
@@ -25,7 +25,7 @@
25
25
  <div class="col-span-6 lg:col-span-2">
26
26
  <div class="analytics-card-tab" data-tabs-target="tab" data-action="click->tabs#select">
27
27
  <div class="flex justify-center flex-col mb-6 lg:mb-0">
28
- <span class="text-sm text-gray-500">Avg. Order Value</span>
28
+ <span class="text-sm text-gray-500"><%= Spree.t('admin.avg_order_value') %></span>
29
29
  <span class="text-lg text-gray-900 font-bold mt-1 mr-3"><%= @orders_average %></span>
30
30
  <div>
31
31
  <%= render 'spree/admin/shared/growth_rate_badge', growth_rate: @orders_average_growth_rate %>
@@ -37,7 +37,7 @@
37
37
  <div class="col-span-6 lg:col-span-2">
38
38
  <div class="analytics-card-tab" data-tabs-target="tab" data-action="click->tabs#select">
39
39
  <div class="flex justify-center flex-col mb-6 lg:mb-0">
40
- <span class="text-sm text-gray-500">Total Orders</span>
40
+ <span class="text-sm text-gray-500"><%= Spree.t('admin.total_orders') %></span>
41
41
  <span class="text-lg text-gray-900 font-bold mt-1 mr-3"><%= @orders_total %></span>
42
42
  <div>
43
43
  <%= render 'spree/admin/shared/growth_rate_badge', growth_rate: @orders_growth_rate %>
@@ -49,7 +49,7 @@
49
49
  <div class="col-span-6 lg:col-span-2">
50
50
  <div class="analytics-card-tab" data-tabs-target="tab" data-action="click->tabs#select">
51
51
  <div class="flex justify-center flex-col">
52
- <span class="text-sm text-gray-500">Visits</span>
52
+ <span class="text-sm text-gray-500"><%= Spree.t(:visits) %></span>
53
53
  <span class="text-lg text-gray-900 font-bold mt-1 mr-3"><%= @visits_total %></span>
54
54
  <div>
55
55
  <%= render 'spree/admin/shared/growth_rate_badge', growth_rate: @visits_growth_rate %>
@@ -63,7 +63,7 @@
63
63
  <div class="col-span-6 lg:col-span-2">
64
64
  <div class="analytics-card-tab" data-tabs-target="tab" data-action="click->tabs#select">
65
65
  <div class="flex justify-center flex-col">
66
- <span class="text-sm text-gray-500">Sign-ups</span>
66
+ <span class="text-sm text-gray-500"><%= Spree.t('admin.sign_ups') %></span>
67
67
  <span class="text-lg text-gray-900 font-bold mt-1 mr-3"><%= @audience_total %></span>
68
68
  <div>
69
69
  <%= render 'spree/admin/shared/growth_rate_badge', growth_rate: @audience_growth_rate %>
@@ -1,16 +1,16 @@
1
1
  <% if done %>
2
2
  <p>
3
- Good job, you've added products to your store!
3
+ <%= Spree.t('admin.store_setup_tasks.add_products_task.done_title') %>
4
4
  </p>
5
5
  <p>
6
- Remember you can always add more!
6
+ <%= Spree.t('admin.store_setup_tasks.add_products_task.done_subtitle') %>
7
7
  </p>
8
8
  <% else %>
9
9
  <p>
10
- Add products to your store, either your own or invite 3rd party vendors to sell together.
10
+ <%= Spree.t('admin.store_setup_tasks.add_products_task.copy') %>
11
11
  </p>
12
12
  <p>
13
- You can also import products in bulk from a CSV or Excel file.
13
+ <%= Spree.t('admin.store_setup_tasks.add_products_task.import_hint') %>
14
14
  </p>
15
15
  <% end %>
16
16
 
@@ -1,18 +1,17 @@
1
1
  <%= turbo_frame_tag :set_customer_support_email, class: 'w-full' do %>
2
2
  <div class="alert alert-info">
3
- We need your customer support email to display on your storefront.
4
- This email will work as a contact point for your customers to reach out to you for any queries or support.
3
+ <%= Spree.t('admin.store_setup_tasks.customer_support_email_task.copy') %>
5
4
  </div>
6
5
 
7
6
  <% if done %>
8
7
  <p class="mb-3">
9
- Your customer support email is <strong><%= current_store.customer_support_email %>.</strong>
8
+ <%= Spree.t('admin.store_setup_tasks.customer_support_email_task.current_html', email: tag.strong(current_store.customer_support_email)) %>
10
9
  </p>
11
10
  <%= link_to_with_icon 'edit', Spree.t(:edit), spree.edit_emails_admin_store_path, class: 'btn btn-light' %>
12
11
  <% else %>
13
12
  <%= form_for current_store, url: spree.admin_store_path, as: :store do |f| %>
14
13
  <div class="form-group">
15
- <%= f.label :customer_support_email, 'Customer support email' %>
14
+ <%= f.label :customer_support_email, Spree.t('admin.store_setup_tasks.customer_support_email_task.label') %>
16
15
  <%= f.email_field :customer_support_email, class: 'form-input', required: true %>
17
16
  </div>
18
17
  <%= turbo_save_button_tag %>
@@ -0,0 +1,18 @@
1
+ <% if done %>
2
+ <p>
3
+ <%= Spree.t('admin.store_setup_tasks.storefront.done') %>
4
+ <%= link_to current_store.storefront_url, current_store.storefront_url, target: :_blank, rel: 'noopener', class: 'font-medium' %>
5
+ </p>
6
+ <% if can?(:update, current_store) %>
7
+ <div>
8
+ <%= link_to Spree.t('admin.store_setup_tasks.storefront.manage'), spree.admin_storefront_path, class: 'btn btn-light', data: { turbo_frame: '_top' } %>
9
+ </div>
10
+ <% end %>
11
+ <% else %>
12
+ <p><%= Spree.t('admin.store_setup_tasks.storefront.copy') %></p>
13
+ <% if can?(:update, current_store) %>
14
+ <div>
15
+ <%= link_to Spree.t('admin.store_setup_tasks.storefront.setup'), spree.admin_storefront_path, class: 'btn btn-primary', data: { turbo_frame: '_top' } %>
16
+ </div>
17
+ <% end %>
18
+ <% end %>
@@ -10,6 +10,6 @@
10
10
  <%= f.spree_money_field :amount, currency: f.object.currency, required: true, disabled: f.object.persisted? %>
11
11
  <%= f.spree_select :currency, options_for_select(supported_currency_options, f.object.currency), { label: Spree.t(:currency), autocomplete: true } %>
12
12
  <%= f.spree_number_field :codes_count, required: true, min: 1, max: Spree::Config[:gift_card_batch_limit], label: Spree.t('admin.gift_card_batches.codes_count') %>
13
- <%= f.spree_date_field :expires_at, disabled: f.object.persisted?, help: 'Gift cards will expire after this date.' %>
13
+ <%= f.spree_date_field :expires_at, disabled: f.object.persisted?, help: Spree.t('admin.gift_card_batches.expires_at_help') %>
14
14
  </div>
15
15
  </div>
@@ -4,10 +4,10 @@
4
4
  <div class="drawer-body" data-controller="highlight tabs">
5
5
  <ul class="nav nav-pills mb-6">
6
6
  <li class="nav-item">
7
- <a class="nav-link active" data-tabs-target="tab" data-action="click->tabs#select">JSON</a>
7
+ <a class="nav-link active" data-tabs-target="tab" data-action="click->tabs#select"><%= Spree.t('admin.imports.json') %></a>
8
8
  </li>
9
9
  <li class="nav-item">
10
- <a class="nav-link" data-tabs-target="tab" data-action="click->tabs#select">Schema</a>
10
+ <a class="nav-link" data-tabs-target="tab" data-action="click->tabs#select"><%= Spree.t('admin.imports.schema') %></a>
11
11
  </li>
12
12
  </ul>
13
13
 
@@ -5,7 +5,7 @@
5
5
  </div>
6
6
 
7
7
  <div>
8
- <%= import.rows.processed.count %> / <%= import.rows_count %> rows processed
8
+ <%= Spree.t('admin.imports.rows_processed', processed: import.rows.processed.count, total: import.rows_count) %>
9
9
  </div>
10
10
  <% elsif import.status != 'failed' %>
11
11
  <div>
@@ -6,6 +6,6 @@
6
6
  <% if mapping.file_column.present? %>
7
7
  <% select_options.prepend([mapping.file_column, mapping.file_column]) %>
8
8
  <% end %>
9
- <%= f.select :file_column, options_for_select(select_options, mapping.file_column), { include_blank: 'Please select a column' }, { data: { action: 'auto-submit#submit', controller: 'autocomplete-select' }, id: "import_mapping_file_column_#{mapping.id}" } %>
9
+ <%= f.select :file_column, options_for_select(select_options, mapping.file_column), { include_blank: Spree.t('admin.imports.please_select_a_column') }, { data: { action: 'auto-submit#submit', controller: 'autocomplete-select' }, id: "import_mapping_file_column_#{mapping.id}" } %>
10
10
  <% end %>
11
11
  <% end %>
@@ -3,9 +3,9 @@
3
3
  <% end %>
4
4
 
5
5
  <% content_for :steps do %>
6
- <li class="border-r grow h-full flex items-center justify-center <% if @import.mapping? %>bg-gray-100 font-bold<% end %>">1. Map fields</li>
7
- <li class="border-r grow h-full flex items-center justify-center <% if @import.processing? %>bg-gray-100 font-bold<% end %>">2. Process rows</li>
8
- <li class="grow h-full flex items-center justify-center <% if @import.complete? %>bg-gray-100 font-bold<% end %>">3. Complete</li>
6
+ <li class="border-r grow h-full flex items-center justify-center <% if @import.mapping? %>bg-gray-100 font-bold<% end %>"><%= Spree.t('admin.imports.steps.map_fields') %></li>
7
+ <li class="border-r grow h-full flex items-center justify-center <% if @import.processing? %>bg-gray-100 font-bold<% end %>"><%= Spree.t('admin.imports.steps.process_rows') %></li>
8
+ <li class="grow h-full flex items-center justify-center <% if @import.complete? %>bg-gray-100 font-bold<% end %>"><%= Spree.t('admin.imports.steps.complete') %></li>
9
9
  <% end %>
10
10
 
11
11
  <% if @import.status == 'mapping' %>
@@ -22,7 +22,7 @@
22
22
  <%= Spree.t(:settings) %>
23
23
  <% end if can?(:update, integration) %>
24
24
 
25
- <span class="ml-auto inline-flex items-center text-xs font-medium text-green-700">
25
+ <span class="ms-auto inline-flex items-center text-xs font-medium text-green-700">
26
26
  <span class="w-1.5 h-1.5 rounded-full bg-green-500 mr-1"></span>
27
27
  <%= Spree.t(:active) %>
28
28
  </span>
@@ -28,13 +28,11 @@
28
28
  <% end %>
29
29
 
30
30
  <p class="text-gray-600 text-center my-12">
31
- Find more integrations in <%= external_link_to 'Integrations Directory', 'https://spreecommerce.org/docs/integrations/integrations', target: '_blank' %>
31
+ <%= Spree.t('admin.integrations.find_more_html', integrations_link: external_link_to(Spree.t('admin.integrations_directory'), 'https://spreecommerce.org/docs/integrations/integrations', target: '_blank')) %>
32
32
  </p>
33
33
  <% else %>
34
34
  <p class="text-gray-600 text-center my-12">
35
- No integrations installed yet. Find integrations in
36
-
37
- <%= external_link_to 'Integrations Directory', 'https://spreecommerce.org/docs/integrations/integrations', target: '_blank' %>
35
+ <%= Spree.t('admin.integrations.none_installed_html', integrations_link: external_link_to(Spree.t('admin.integrations_directory'), 'https://spreecommerce.org/docs/integrations/integrations', target: '_blank')) %>
38
36
  </p>
39
37
  <% end %>
40
38
  </div>
@@ -21,7 +21,7 @@
21
21
  <span class="badge badge-warning"><%= Spree.t(:expired) %></span>
22
22
  <% else %>
23
23
  <span class="badge badge-light"><%= Spree.t(:pending) %></span>
24
- <p class="text-gray-600 text-sm mb-0 mt-2 pl-1">Expires at: <%= spree_time(invitation.expires_at) %></p>
24
+ <p class="text-gray-600 text-sm mb-0 mt-2 pl-1"><%= Spree.t('admin.invitations.expires_at') %>: <%= spree_time(invitation.expires_at) %></p>
25
25
  <% end %>
26
26
  </td>
27
27
  <td class="w-10">
@@ -5,7 +5,7 @@
5
5
  <%= dialog_header Spree.t('actions.send_invitation') %>
6
6
  <div class="dialog-body">
7
7
  <%= render 'spree/admin/shared/error_messages', target: @invitation %>
8
- <%= f.spree_email_field :email, required: true, data: { 'enable-button-target': 'input' }, placeholder: 'eg. steve@apple.com', autofocus: true %>
8
+ <%= f.spree_email_field :email, required: true, data: { 'enable-button-target': 'input' }, placeholder: Spree.t('admin.invitations.email_placeholder'), autofocus: true %>
9
9
  <% if @roles.any? %>
10
10
  <%= f.spree_select :role_id, @roles.map { |role| [role.name, role.id] }, { label: Spree.t(:role_id), autocomplete: true, required: true }, { data: { 'enable-button-target': 'input' } } %>
11
11
  <% end %>
@@ -3,7 +3,7 @@
3
3
  <%= render_avatar(@invitation.inviter, height: 32, width: 32) %>
4
4
  </div>
5
5
  <div>
6
- <strong><%= @invitation.inviter.name %></strong> has invited you to join <strong><%= @invitation.resource.name %></strong>
6
+ <%= Spree.t('admin.invitations.invited_you_to_join_html', inviter: tag.strong(@invitation.inviter.name), resource: tag.strong(@invitation.resource.name)) %>
7
7
  </div>
8
8
  </div>
9
9
  <%= link_to Spree.t(:accept), spree.accept_admin_invitation_path(@invitation), class: 'btn btn-primary mx-auto px-12', data: { turbo_method: :put } %>
@@ -18,7 +18,7 @@
18
18
  </div>
19
19
  </div>
20
20
  <div class="dialog-footer">
21
- <div class="flex gap-2 m-0 items-center mr-auto">
21
+ <div class="flex gap-2 m-0 items-center me-auto">
22
22
  <%= form.label :quantity, Spree.t(:quantity), class: "mb-0" %>
23
23
  <%= form.number_field :quantity, class: "form-input", min: 1 %>
24
24
  </div>
@@ -8,17 +8,19 @@
8
8
  <div class="card-body">
9
9
  <%= f.spree_text_field :name, required: true %>
10
10
 
11
- <%= f.spree_select :country_ids, @countries.map { |country| ["#{Spree::Country.iso_to_emoji_flag(country.iso)} #{country.name}", country.id] }, { include_blank: false, label: Spree.t(:countries), autocomplete: true, multiple: true } %>
11
+ <%= f.spree_select :country_ids, country_select_options(@countries), { include_blank: false, label: Spree.t(:countries), autocomplete: true, multiple: true } %>
12
12
 
13
- <%= f.spree_select :currency, currency_options(f.object.currency), { label: Spree.t(:currency), required: true, autocomplete: true } %>
13
+ <%= f.spree_select :currency, currency_options(f.object.currency), { label: Spree.t(:currency), required: true, autocomplete: true }, { data: { display_names_type: 'currency' } } %>
14
14
 
15
15
  <%= f.spree_select :default_locale,
16
- options_from_collection_for_select(all_locales_options, :last, :first, f.object.default_locale || I18n.locale),
17
- { required: true, autocomplete: true } %>
16
+ options_from_collection_for_select(translation_locales_options, :last, :first, f.object.default_locale || I18n.locale),
17
+ { required: true, autocomplete: true },
18
+ { data: { display_names_type: 'language' } } %>
18
19
 
19
20
  <%= f.spree_select :supported_locales,
20
- options_from_collection_for_select(all_locales_options, :last, :first, f.object.supported_locales&.split(',')),
21
- { multiple: true, autocomplete: true, hint: Spree.t('admin.markets.supported_locales_hint') } %>
21
+ options_from_collection_for_select(translation_locales_options, :last, :first, f.object.supported_locales&.split(',')),
22
+ { multiple: true, autocomplete: true, hint: Spree.t('admin.markets.supported_locales_hint') },
23
+ { data: { display_names_type: 'language' } } %>
22
24
 
23
25
  <hr class="my-6">
24
26
 
@@ -35,7 +35,7 @@
35
35
  <div class="drawer-body">
36
36
  <div class="text-gray-600 p-12 flex items-center w-full justify-center flex-col gap-4">
37
37
  <%= icon 'map-search', class: 'text-4xl' %>
38
- <p>No metafield definitions configured for <%= model_class.name.demodulize.titleize.pluralize %>.</p>
38
+ <p><%= Spree.t('admin.metafields.none_configured_for', resource: model_class.model_name.human(count: 2)) %></p>
39
39
  <%= link_to_with_icon 'plus', Spree.t(:add_one), spree.new_admin_metafield_definition_path(resource_type: model_class.to_s), class: 'btn btn-primary', data: { 'turbo-frame': '_top' } if can?(:create, Spree::MetafieldDefinition) %>
40
40
  </div>
41
41
  </div>
@@ -5,11 +5,11 @@
5
5
  <div class="card-body" data-controller="slug-form">
6
6
  <div class="grid grid-cols-12 gap-6 mb-6">
7
7
  <div class="col-span-12 md:col-span-6">
8
- <%= f.spree_text_field :presentation, required: true, autofocus: f.object.new_record?, data: { slug_form_target: :name, action: 'slug-form#updateUrlFromName' }, help_text: "This is used to display the option type on the storefront." %>
8
+ <%= f.spree_text_field :presentation, required: true, autofocus: f.object.new_record?, data: { slug_form_target: :name, action: 'slug-form#updateUrlFromName' }, help_text: Spree.t('admin.option_types.presentation_help') %>
9
9
  </div>
10
10
 
11
11
  <div class="col-span-12 md:col-span-6">
12
- <%= f.spree_text_field :name, label: Spree.t(:internal_name), data: { slug_form_target: :url }, help_text: raw("This is used internally to identify the option type. <strong>It must be unique.</strong>") %>
12
+ <%= f.spree_text_field :name, label: Spree.t(:internal_name), data: { slug_form_target: :url }, help_text: Spree.t('admin.option_types.name_help_html') %>
13
13
  </div>
14
14
  </div>
15
15
  <div class="grid grid-cols-12 gap-6 mb-6">
@@ -38,7 +38,7 @@
38
38
  <h5 class="card-title"><%= Spree.t(:option_values) %></h5>
39
39
  <% if @option_values_total > 0 %>
40
40
  <span class="text-sm text-gray-500">
41
- <%= @option_values_total %> <%= 'value'.pluralize(@option_values_total) %>
41
+ <%= Spree.t('admin.option_types.value_count', count: @option_values_total) %>
42
42
  </span>
43
43
  <% end %>
44
44
  </div>
@@ -77,14 +77,17 @@
77
77
  <div class="flex items-center justify-between p-3 border-t">
78
78
  <span class="text-sm text-gray-500">
79
79
  <% per_page = Spree::Admin::RuntimeConfig.admin_option_values_per_page %>
80
- Showing <%= (@option_values_page - 1) * per_page + 1 %>-<%= [@option_values_page * per_page, @option_values_total].min %> of <%= @option_values_total %>
80
+ <%= Spree.t('admin.option_types.showing_pagination',
81
+ from: (@option_values_page - 1) * per_page + 1,
82
+ to: [@option_values_page * per_page, @option_values_total].min,
83
+ total: @option_values_total) %>
81
84
  </span>
82
85
  <div class="flex gap-1.5">
83
86
  <% if @option_values_page > 1 %>
84
87
  <%= link_to icon('chevron-left', class: 'text-gray-900 mr-0'),
85
88
  spree.edit_admin_option_type_path(@option_type, option_values_page: @option_values_page - 1),
86
89
  class: "btn btn-light p-2",
87
- 'aria-label': 'Previous page' %>
90
+ 'aria-label': Spree.t('admin.previous_page') %>
88
91
  <% else %>
89
92
  <button class="btn btn-light p-2 cursor-not-allowed opacity-50" disabled>
90
93
  <%= icon('chevron-left', class: 'text-gray-900 mr-0') %>
@@ -95,7 +98,7 @@
95
98
  <%= link_to icon('chevron-right', class: 'text-gray-900 mr-0'),
96
99
  spree.edit_admin_option_type_path(@option_type, option_values_page: @option_values_page + 1),
97
100
  class: "btn btn-light p-2",
98
- 'aria-label': 'Next page' %>
101
+ 'aria-label': Spree.t('admin.next_page') %>
99
102
  <% else %>
100
103
  <button class="btn btn-light p-2 cursor-not-allowed opacity-50" disabled>
101
104
  <%= icon('chevron-right', class: 'text-gray-900 mr-0') %>
@@ -9,8 +9,7 @@
9
9
 
10
10
  <%= content_for(:page_alerts) do %>
11
11
  <div class="alert alert-info">
12
- Options are used to group certain attributes. For example, you can create an option named "shirt size" and then
13
- create an option value named "small" and "medium" for this option.
12
+ <%= Spree.t('admin.option_types.intro_help') %>
14
13
  </div>
15
14
  <% end %>
16
15
 
@@ -19,5 +18,5 @@
19
18
  <%= render_table @collection, :option_types, sortable: true %>
20
19
 
21
20
  <p class="documentation-link-container">
22
- <%= external_link_to "Learn more about options", "https://spreecommerce.org/docs/user/manage-products/product-options" %>
21
+ <%= external_link_to Spree.t('admin.option_types.learn_more'), "https://spreecommerce.org/docs/user/manage-products/product-options" %>
23
22
  </p>
@@ -4,7 +4,7 @@
4
4
  <%= Spree.t(:customer) %>
5
5
  </h5>
6
6
  <% if can?(:update_customer, @order) || can?(:update_addresses, @order) %>
7
- <%= dropdown id: 'customer-edit-dropdown', class: 'ml-auto', portal: false do %>
7
+ <%= dropdown id: 'customer-edit-dropdown', class: 'ms-auto', portal: false do %>
8
8
  <%= dropdown_toggle class: 'btn-light btn-sm px-1', data: { test_id: 'dropdown-toggle' } do %>
9
9
  <%= icon 'dots-vertical', class: 'mr-0' %>
10
10
  <% end %>
@@ -11,7 +11,7 @@
11
11
  <span class="text-gray-600">
12
12
  <%= order.full_name %>
13
13
  </span>
14
- <%= help_bubble('This order was placed by a guest customer') %>
14
+ <%= help_bubble(Spree.t('admin.orders.placed_by_guest')) %>
15
15
  <% else %>
16
16
  <span class="text-gray-600">
17
17
  <%= Spree.t(:not_available) %>
@@ -7,7 +7,7 @@
7
7
  </h5>
8
8
  <% if @payments.any? %>
9
9
  <span class="badge badge-info ml-2"><%= @payments.count %></span>
10
- <%= order_payment_state(@order, class: "ml-auto") %>
10
+ <%= order_payment_state(@order, class: "ms-auto") %>
11
11
  <% end %>
12
12
  </div>
13
13
  <div class="card-body flex flex-col gap-2 px-2 pt-0 pb-2">
@@ -51,6 +51,11 @@
51
51
  </span>
52
52
  <% else %>
53
53
  <%= Spree.t(:no_shipping_method_selected) %>
54
+ <% if shipment.cost.positive? %>
55
+ <span>
56
+ <%= shipment.display_cost %>
57
+ </span>
58
+ <% end %>
54
59
  <% end %>
55
60
  </div>
56
61
 
@@ -87,9 +92,9 @@
87
92
  <% if can_ship?(shipment) %>
88
93
  <div class="card-footer flex justify-end border-t bg-gray-25">
89
94
  <% if shipment.tracked? %>
90
- <%= link_to_with_icon 'send.svg', Spree.t(:ship), spree.ship_admin_order_shipment_path(order, shipment), class: 'ml-auto btn btn-primary mb-0', data: {turbo_method: :post, turbo_confirm: Spree.t(:are_you_sure), turbo_frame: '_top'} %>
95
+ <%= link_to_with_icon 'send.svg', Spree.t(:ship), spree.ship_admin_order_shipment_path(order, shipment), class: 'ms-auto btn btn-primary mb-0', data: {turbo_method: :post, turbo_confirm: Spree.t(:are_you_sure), turbo_frame: '_top'} %>
91
96
  <% elsif !shipment.digital? %>
92
- <span class="ship ml-auto btn btn-primary disabled mb-0" data-controller="tooltip" data-tooltip-placement-value="left">
97
+ <span class="ship ms-auto btn btn-primary disabled mb-0" data-controller="tooltip" data-tooltip-placement-value="left">
93
98
  <%= icon 'send' %>
94
99
  <%= Spree.t(:ship) %>
95
100
  <%= tooltip(Spree.t('admin.shipment.please_provide_tracking_details')) %>
@@ -7,7 +7,7 @@
7
7
  <% if @shipments.any? %>
8
8
  <span class="badge badge-info ml-2"><%= @shipments.count %></span>
9
9
 
10
- <%= order_shipment_state(@order, class: "ml-auto") %>
10
+ <%= order_shipment_state(@order, class: "ms-auto") %>
11
11
  <% end %>
12
12
  </div>
13
13
  <div class="card-body px-2 pt-0 pb-2 flex flex-col gap-2">
@@ -1,13 +1,16 @@
1
- <% if @order.user.orders.complete.any? %>
1
+ <% completed_orders = current_store.orders.where(user: @order.user).complete %>
2
+ <% if completed_orders.any? %>
2
3
  <div class="alert alert-info mt-2 mb-0">
3
4
  <div>
4
5
  <h5 class="font-bold">
5
- <%= icon('magic') %> Assistant
6
+ <%= icon('magic') %> <%= Spree.t('admin.assistant') %>
6
7
  </h5>
7
- This is a repeat customer that has spent a total of
8
- <strong><%= Spree::Money.new(@order.user.orders.complete.sum(:total)) %></strong> across
9
- <strong><%= @order.user.orders.complete.count %> orders</strong>, averaging
10
- <strong><%= Spree::Money.new(@order.user.orders.complete.average(:total)) %></strong> per order.
8
+ <%= Spree.t(
9
+ 'admin.orders.repeat_customer_summary_html',
10
+ amount: tag.strong(Spree::Money.new(completed_orders.sum(:total))),
11
+ count: tag.strong(Spree.t('admin.orders.orders_count', count: completed_orders.count)),
12
+ average: tag.strong(Spree::Money.new(completed_orders.average(:total)))
13
+ ) %>
11
14
  </div>
12
15
  </div>
13
16
  <% end %>
@@ -20,7 +20,7 @@ locals: {
20
20
 
21
21
  <div class="form-actions">
22
22
  <%= turbo_save_button_tag Spree.t("actions.update") %>
23
- <div class="ml-auto flex gap-6">
23
+ <div class="ms-auto flex gap-6">
24
24
  <% if f.object.can_cancel? && can?(:cancel, f.object) %>
25
25
  <%= link_to Spree.t("actions.cancel"),
26
26
  spree.cancel_admin_return_authorization_path(f.object),
@@ -22,7 +22,7 @@
22
22
  <h5 class="card-title"><%= Spree.t(:display_settings) %></h5>
23
23
  </div>
24
24
  <div class="card-body">
25
- <%= f.spree_text_field :name, help: 'This name will be used to identify the payment method on the storefront' %>
25
+ <%= f.spree_text_field :name, help: Spree.t('admin.payment_methods.name_help') %>
26
26
 
27
27
  <%= f.spree_select :display_on, display_on_options, { label: Spree.t(:display) } %>
28
28
  <%= f.spree_select :auto_capture, [["#{Spree.t(:use_app_default)} (#{Spree::Config[:auto_capture]})", ''], [Spree.t(:say_yes).to_s, true], [Spree.t(:say_no).to_s, false]], { label: Spree.t(:auto_capture) } %>
@@ -26,7 +26,7 @@
26
26
  </div>
27
27
 
28
28
  <% if available_payment_methods.present? && can?(:create, Spree::PaymentMethod) %>
29
- <h6 class="pb-2 pt-6">Available Payment Methods</h6>
29
+ <h6 class="pb-2 pt-6"><%= Spree.t('admin.payment_methods.available_payment_methods') %></h6>
30
30
  <div class="card-lg">
31
31
  <div class="table-responsive">
32
32
  <table class="table">
@@ -46,5 +46,5 @@
46
46
  <% end %>
47
47
 
48
48
  <p class="text-gray-600 text-center my-12">
49
- Find more payment methods in <%= external_link_to 'Integrations Directory', 'https://spreecommerce.org/docs/integrations/integrations', target: '_blank' %>
49
+ <%= Spree.t('admin.payment_methods.find_more_payment_methods_html', integrations_link: external_link_to(Spree.t('admin.integrations_directory'), 'https://spreecommerce.org/docs/integrations/integrations', target: '_blank')) %>
50
50
  </p>
@@ -14,7 +14,7 @@
14
14
  <%= payment_method_icon_tag(payment.payment_method.payment_icon_name) %>
15
15
  <% end %>
16
16
  <strong><%= payment.number.upcase %></strong>
17
- <%= spree_time(payment.created_at, class: 'ml-auto text-gray-600 text-sm') %>
17
+ <%= spree_time(payment.created_at, class: 'ms-auto text-gray-600 text-sm') %>
18
18
 
19
19
  <%= dropdown do %>
20
20
  <%= dropdown_toggle class: 'btn-light btn-sm px-1' do %>
@@ -9,7 +9,7 @@
9
9
 
10
10
  <% content_for :page_alerts do %>
11
11
  <div class="alert alert-info">
12
- Price lists are a powerful tool for managing pricing strategies based on customer groups, products, or other criteria.
12
+ <%= Spree.t('admin.price_lists.page_info') %>
13
13
  </div>
14
14
  <% end %>
15
15
 
@@ -18,5 +18,5 @@
18
18
  <%= render_table @collection, :price_lists, sortable: true %>
19
19
 
20
20
  <p class="documentation-link-container">
21
- <%= external_link_to "Learn more about price lists", "https://spreecommerce.org/docs/user/manage-products/price-lists" %>
21
+ <%= external_link_to Spree.t('admin.price_lists.learn_more'), "https://spreecommerce.org/docs/user/manage-products/price-lists" %>
22
22
  </p>
@@ -17,6 +17,25 @@
17
17
  %>
18
18
  <%= f.label :track_inventory, Spree.t(:track_quantity), class: 'custom-control-label' %>
19
19
  </div>
20
+ <%= f.fields_for :master, @product.master do |vf| %>
21
+ <div class="mb-2">
22
+ <%= vf.spree_check_box :preorderable, label: Spree.t('admin.variants_form.preorderable_label') %>
23
+ <small class="text-muted text-xs d-block"><%= Spree.t('admin.variants_form.preorderable_hint') %></small>
24
+ </div>
25
+ <div class="mb-2">
26
+ <%# datetime-local inputs have no timezone; render in the store's timezone so admins see and submit store-local times (parsed back in ProductsController#permitted_resource_params). %>
27
+ <% preorder_ships_at_local = vf.object.preorder_ships_at&.in_time_zone(current_timezone)&.strftime('%Y-%m-%dT%H:%M') %>
28
+ <%= vf.spree_datetime_field :preorder_ships_at,
29
+ value: preorder_ships_at_local,
30
+ label: Spree.t('admin.variants_form.preorder_ships_at_label'),
31
+ help: "#{Spree.t('admin.variants_form.preorder_ships_at_hint')} #{Spree.t('admin.datetime_field_timezone_help', zone: current_timezone.name)}" %>
32
+ </div>
33
+ <div class="mb-6">
34
+ <%= vf.spree_number_field :backorder_limit, min: 0,
35
+ label: Spree.t('admin.variants_form.backorder_limit_label'),
36
+ help: Spree.t('admin.variants_form.backorder_limit_hint') %>
37
+ </div>
38
+ <% end %>
20
39
  <div
21
40
  class="master-variant-quantity-form <%= @product.has_variants? || !@product.track_inventory ? 'hidden' : '' %>"
22
41
  data-product-form-target="<%= @product.has_variants? ? '' : 'quantityForm' %>"
@@ -13,9 +13,8 @@
13
13
  %>
14
14
 
15
15
  <%
16
- store_timezone = ActiveSupport::TimeZone[current_store.preferred_timezone] || Time.zone
17
- to_store_local = ->(value) { value&.in_time_zone(store_timezone)&.strftime('%Y-%m-%dT%H:%M') }
18
- tz_help = Spree.t('admin.datetime_field_timezone_help', zone: store_timezone.name)
16
+ to_store_local = ->(value) { value&.in_time_zone(current_timezone)&.strftime('%Y-%m-%dT%H:%M') }
17
+ tz_help = Spree.t('admin.datetime_field_timezone_help', zone: current_timezone.name)
19
18
  store_channels = current_store.channels.order(:name).to_a
20
19
  publications_by_channel = @product.product_publications.includes(:channel).index_by(&:channel_id)
21
20
  can_manage_publications = can?(:manage, Spree::ProductPublication)
@@ -14,6 +14,5 @@
14
14
  <%= render_table @collection, :products, bulk_operations: true, export_type: Spree::Exports::Products %>
15
15
 
16
16
  <p class="documentation-link-container">
17
- Learn more about
18
- <%= external_link_to 'managing products', 'https://spreecommerce.org/docs/user/manage-products' %>
17
+ <%= external_link_to Spree.t('admin.products.learn_more'), 'https://spreecommerce.org/docs/user/manage-products' %>
19
18
  </p>
@@ -17,6 +17,9 @@
17
17
  <%= f.spree_email_field :email, required: true %>
18
18
  <%= f.spree_text_field :first_name, required: true %>
19
19
  <%= f.spree_text_field :last_name, required: true %>
20
+ <%= f.spree_select :selected_locale,
21
+ options_from_collection_for_select(admin_locales_options, :last, :first, @user.selected_locale || I18n.locale),
22
+ { label: Spree.t(:language), help_bubble: Spree.t(:admin_language_help), autocomplete: true } %>
20
23
  <%= f.spree_file_field :avatar, crop: true %>
21
24
  </div>
22
25
  </div>
@@ -0,0 +1,7 @@
1
+ <div class="form-group">
2
+ <%= f.label :preferred_channel_ids, Spree.t(:channels) %>
3
+ <%= f.select :preferred_channel_ids,
4
+ current_store.channels.order(:name).pluck(:name, :id),
5
+ { selected: f.object.preferred_channel_ids },
6
+ { multiple: true, data: { controller: 'autocomplete-select' } } %>
7
+ </div>
@@ -1,7 +1,7 @@
1
1
  <div class="form-group">
2
2
  <%= f.label :preferred_country_iso, Spree.t(:country) %>
3
3
  <%= f.select :preferred_country_iso,
4
- available_countries_iso.map { |iso| [Spree.t(iso, scope: 'country_names', default: iso), iso] },
4
+ available_countries.map { |country| [country.localized_name, country.iso] },
5
5
  { selected: f.object.preferred_country_iso || current_store.default_market&.default_country&.iso },
6
6
  { data: { controller: 'autocomplete-select' } } %>
7
7
  </div>
@@ -20,7 +20,7 @@
20
20
  <%= f.number_field :preferred_amount_max, class: 'form-input' %>
21
21
 
22
22
  <span class="form-text mt-2">
23
- Leave this field blank to not limit the maximum amount.
23
+ <%= Spree.t('item_total_rule.max_amount_help') %>
24
24
  </span>
25
25
  </div>
26
26
  </div>
@@ -0,0 +1,7 @@
1
+ <div class="form-group">
2
+ <%= f.label :preferred_market_ids, Spree.t(:markets) %>
3
+ <%= f.select :preferred_market_ids,
4
+ current_store.markets.order(:name).pluck(:name, :id),
5
+ { selected: f.object.preferred_market_ids },
6
+ { multiple: true, data: { controller: 'autocomplete-select' } } %>
7
+ </div>