spree_cm_commissioner 2.3.1 → 2.3.2.pre.pre2

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/app/controllers/spree/admin/product_completion_steps_controller.rb +17 -1
  4. data/app/controllers/spree/admin/tenants_controller.rb +1 -1
  5. data/app/controllers/spree/api/v2/storefront/intercity_taxi/draft_orders_controller.rb +44 -3
  6. data/app/controllers/spree/api/v2/tenant/intercity_taxi/draft_orders_controller.rb +81 -0
  7. data/app/controllers/spree/api/v2/tenant/trip_places_controller.rb +48 -0
  8. data/app/controllers/spree/api/v2/tenant/trip_search_controller.rb +103 -0
  9. data/app/controllers/spree/api/v2/tenant/trips_controller.rb +32 -0
  10. data/app/interactors/spree_cm_commissioner/create_ticket.rb +2 -0
  11. data/app/interactors/spree_cm_commissioner/host_matcher.rb +1 -1
  12. data/app/interactors/spree_cm_commissioner/vendor_creation_telegram_alert_sender.rb +1 -1
  13. data/app/mailers/spree/order_mailer_decorator.rb +8 -9
  14. data/app/models/concerns/spree_cm_commissioner/service_recommendations.rb +2 -2
  15. data/app/models/concerns/spree_cm_commissioner/store_preference.rb +1 -0
  16. data/app/models/spree_cm_commissioner/product_completion_step.rb +4 -1
  17. data/app/models/spree_cm_commissioner/product_completion_step_banner.rb +12 -0
  18. data/app/models/spree_cm_commissioner/product_completion_steps/social_entry_url.rb +86 -19
  19. data/app/models/spree_cm_commissioner/tenant.rb +10 -1
  20. data/app/models/spree_cm_commissioner/vendor_place.rb +10 -1
  21. data/app/overrides/spree/admin/stores/_form/store_preferences.html.erb.deface +9 -0
  22. data/app/queries/spree_cm_commissioner/trip_query.rb +23 -17
  23. data/app/request_schemas/spree_cm_commissioner/intercity_taxi_draft_order_schema.rb +10 -0
  24. data/app/request_schemas/spree_cm_commissioner/intercity_taxi_draft_order_update_schema.rb +10 -0
  25. data/app/serializers/spree_cm_commissioner/v2/storefront/trip_vehicle_serializer.rb +1 -1
  26. data/app/services/spree_cm_commissioner/intercity_taxi_order/create.rb +14 -6
  27. data/app/services/spree_cm_commissioner/intercity_taxi_order/update.rb +6 -63
  28. data/app/views/shared/_asset_field.html.erb +13 -0
  29. data/app/views/spree/admin/product_completion_steps/_form.html.erb +28 -2
  30. data/app/views/spree/admin/product_completion_steps/_supported_fields.html.erb +33 -0
  31. data/app/views/spree/admin/tenants/_form.html.erb +1 -1
  32. data/config/initializers/spree_permitted_attributes.rb +1 -0
  33. data/config/locales/en.yml +4 -0
  34. data/config/locales/km.yml +65 -61
  35. data/config/routes.rb +21 -1
  36. data/db/migrate/20251119093000_add_tenant_id_to_cm_vendor_places.rb +7 -0
  37. data/lib/spree_cm_commissioner/test_helper/factories/product_completion_step_banner_factory.rb +7 -0
  38. data/lib/spree_cm_commissioner/version.rb +1 -1
  39. metadata +14 -4
@@ -5,6 +5,7 @@ module SpreeCmCommissioner
5
5
  # - https://t.me/ThePlatformKHBot?start={order.number}
6
6
  # - https://example.com?guest_name={guests[0].first_name}&phone={guests[0].phone_number}
7
7
  # - https://example.com?line_item_id={line_item.id}&quantity={line_item.quantity}
8
+ # - https://example.com?bill_address.first_name={bill_address.first_name}&bill_address.lastname={bill_address.lastname}
8
9
  preference :entry_point_link, :string
9
10
 
10
11
  # Allowed fields per object (excludes private/reference fields like *_id, *_by_id, metadata, etc.)
@@ -14,7 +15,11 @@ module SpreeCmCommissioner
14
15
  state
15
16
  email
16
17
  total
18
+ display_total
17
19
  item_total
20
+ display_item_total
21
+ adjustment_total
22
+ display_adjustment_total
18
23
  created_at
19
24
  completed_at
20
25
  token
@@ -23,12 +28,31 @@ module SpreeCmCommissioner
23
28
  currency
24
29
  item_count
25
30
  channel
31
+ special_instructions
32
+ ].freeze
33
+
34
+ BILL_ADDRESS_ALLOWED_FIELDS = %w[
35
+ first_name
36
+ last_name
37
+ full_name
38
+ address1
39
+ address2
40
+ city
41
+ zipcode
42
+ phone
43
+ state_name
44
+ alternative_phone
45
+ company
46
+ label
47
+ age
48
+ gender
26
49
  ].freeze
27
50
 
28
51
  LINE_ITEM_ALLOWED_FIELDS = %w[
29
52
  qr_data
30
53
  quantity
31
54
  price
55
+ display_price
32
56
  from_date
33
57
  to_date
34
58
  number
@@ -45,7 +69,6 @@ module SpreeCmCommissioner
45
69
  dob
46
70
  gender
47
71
  age
48
- email
49
72
  phone_number
50
73
  address
51
74
  seat_number
@@ -60,9 +83,6 @@ module SpreeCmCommissioner
60
83
  expectation
61
84
  other_occupation
62
85
  other_organization
63
- entry_type
64
- upload_later
65
- data_fill_stage_phase
66
86
  created_at
67
87
  updated_at
68
88
  ].freeze
@@ -72,32 +92,79 @@ module SpreeCmCommissioner
72
92
  return nil if preferred_entry_point_link.blank?
73
93
 
74
94
  url = preferred_entry_point_link.dup
95
+ url = replace_order_placeholders(url, line_item)
96
+ url = replace_bill_address_placeholders(url, line_item)
97
+ url = replace_line_item_placeholders(url, line_item)
98
+
99
+ replace_guest_placeholders(url, line_item)
100
+ end
101
+
102
+ def completed?(line_item)
103
+ return false if line_item.completion_steps.blank?
104
+
105
+ step_data = line_item.completion_steps.find { |step| step['position'].to_s == position.to_s }
106
+ step_data&.dig('completed_at').present?
107
+ end
108
+
109
+ # Returns a hash of all supported placeholder fields grouped by object type.
110
+ # Used by the admin UI to display available placeholders when configuring the entry_point_link template.
111
+ # Example usage in template: {order.number}, {bill_address.first_name}, {line_item.quantity}, {guests[0].email}
112
+ #
113
+ # @return [Hash] Hash with keys :order, :bill_address, :line_item, :guests
114
+ # Each key maps to an array of allowed field names for that object type
115
+ def supported_fields
116
+ {
117
+ order: ORDER_ALLOWED_FIELDS,
118
+ bill_address: BILL_ADDRESS_ALLOWED_FIELDS,
119
+ line_item: LINE_ITEM_ALLOWED_FIELDS,
120
+ guests: GUEST_ALLOWED_FIELDS
121
+ }
122
+ end
123
+
124
+ private
75
125
 
76
- # Replace order placeholders: {order.field_name}
77
- line_item.order.attributes.each do |field, value|
78
- url.gsub!("{order.#{field}}", value.to_s) if ORDER_ALLOWED_FIELDS.include?(field)
126
+ def replace_order_placeholders(url, line_item)
127
+ return url if line_item.order.blank?
128
+
129
+ ORDER_ALLOWED_FIELDS.each do |field|
130
+ value = line_item.order.public_send(field)
131
+ url.gsub!("{order.#{field}}", value.to_s) if value.present?
79
132
  end
80
133
 
81
- # Replace line_item placeholders: {line_item.field_name}
82
- line_item.attributes.each do |field, value|
83
- url.gsub!("{line_item.#{field}}", value.to_s) if LINE_ITEM_ALLOWED_FIELDS.include?(field)
134
+ url
135
+ end
136
+
137
+ def replace_bill_address_placeholders(url, line_item)
138
+ return url if line_item.order&.bill_address.blank?
139
+
140
+ BILL_ADDRESS_ALLOWED_FIELDS.each do |field|
141
+ value = line_item.order.bill_address.public_send(field)
142
+ url.gsub!("{bill_address.#{field}}", value.to_s) if value.present?
84
143
  end
85
144
 
86
- # Replace guest placeholders: {guests[index].field_name}
87
- line_item.guests.each_with_index do |guest, index|
88
- guest.attributes.each do |field, value|
89
- url.gsub!("{guests[#{index}].#{field}}", value.to_s) if GUEST_ALLOWED_FIELDS.include?(field)
90
- end
145
+ url
146
+ end
147
+
148
+ def replace_line_item_placeholders(url, line_item)
149
+ LINE_ITEM_ALLOWED_FIELDS.each do |field|
150
+ value = line_item.public_send(field)
151
+ url.gsub!("{line_item.#{field}}", value.to_s) if value.present?
91
152
  end
92
153
 
93
154
  url
94
155
  end
95
156
 
96
- def completed?(line_item)
97
- return false if line_item.completion_steps.blank?
157
+ def replace_guest_placeholders(url, line_item)
158
+ return url if line_item.guests.blank?
98
159
 
99
- step_data = line_item.completion_steps.find { |step| step['position'].to_s == position.to_s }
100
- step_data&.dig('completed_at').present?
160
+ line_item.guests.each_with_index do |guest, index|
161
+ GUEST_ALLOWED_FIELDS.each do |field|
162
+ value = guest.public_send(field)
163
+ url.gsub!("{guests[#{index}].#{field}}", value.to_s) if value.present?
164
+ end
165
+ end
166
+
167
+ url
101
168
  end
102
169
  end
103
170
  end
@@ -12,7 +12,10 @@ module SpreeCmCommissioner
12
12
  through: :vendors,
13
13
  source: :payment_methods
14
14
 
15
- validates :host, uniqueness: true, presence: true
15
+ validates :host, presence: true, uniqueness: true,
16
+ format: { without: %r{\Ahttps?://},
17
+ message: :format
18
+ }
16
19
 
17
20
  enum state: { enabled: 0, disabled: 1 }
18
21
 
@@ -22,6 +25,12 @@ module SpreeCmCommissioner
22
25
  vendors.where(state: :active).first
23
26
  end
24
27
 
28
+ def url
29
+ return if host.blank?
30
+
31
+ Rails.env.development? || Rails.env.test? ? "http://#{host}" : "https://#{host}"
32
+ end
33
+
25
34
  private
26
35
 
27
36
  def generate_slug
@@ -1,8 +1,9 @@
1
1
  module SpreeCmCommissioner
2
2
  class VendorPlace < Base
3
- enum place_type: { location: 0, branch: 1, stop: 2 }
3
+ enum place_type: { venue: 0, branch: 1, stop: 2, location: 3 }
4
4
  acts_as_list scope: :vendor
5
5
 
6
+ belongs_to :tenant, class_name: 'SpreeCmCommissioner::Tenant', optional: true
6
7
  belongs_to :vendor, class_name: 'Spree::Vendor', optional: false
7
8
  belongs_to :place, class_name: 'SpreeCmCommissioner::Place', optional: false
8
9
  belongs_to :location, class_name: 'SpreeCmCommissioner::VendorPlace', optional: true
@@ -16,6 +17,8 @@ module SpreeCmCommissioner
16
17
 
17
18
  accepts_nested_attributes_for :place, allow_destroy: true
18
19
 
20
+ before_save :set_tenant_id
21
+
19
22
  # include place when using these delegate to avoid N+1
20
23
  def display_name
21
24
  place&.name
@@ -28,5 +31,11 @@ module SpreeCmCommissioner
28
31
  def selected
29
32
  vendor.selected_place_references.include?(place&.reference)
30
33
  end
34
+
35
+ private
36
+
37
+ def set_tenant_id
38
+ self.tenant_id = vendor&.tenant_id
39
+ end
31
40
  end
32
41
  end
@@ -37,6 +37,15 @@
37
37
  <% end %>
38
38
  </div>
39
39
 
40
+ <!-- Telegram New Vendor Alert Chat ID -->
41
+ <div class="col-6">
42
+ <%= f.field_container :preferred_telegram_new_vendor_alert_chat_id do %>
43
+ <%= f.label :preferred_telegram_new_vendor_alert_chat_id, Spree.t(:telegram_new_vendor_alert_chat_id) %>
44
+ <%= f.text_field :preferred_telegram_new_vendor_alert_chat_id, class: 'form-control' %>
45
+ <%= f.error_message_on :preferred_telegram_new_vendor_alert_chat_id %>
46
+ <% end %>
47
+ </div>
48
+
40
49
  <!-- Assetlinks -->
41
50
  <div class="col-12">
42
51
  <%= f.field_container :preferred_assetlinks do %>
@@ -1,12 +1,13 @@
1
1
  module SpreeCmCommissioner
2
2
  class TripQuery
3
- attr_reader :origin_id, :destination_id, :date, :vendor_id, :number_of_guests, :route_type, :params
3
+ attr_reader :origin_id, :destination_id, :date, :vendor_id, :tenant_id, :number_of_guests, :route_type, :params
4
4
 
5
- def initialize(origin_id:, destination_id:, date:, route_type: nil, vendor_id: nil, number_of_guests: nil, params: {}) # rubocop:disable Metrics/ParameterLists
5
+ def initialize(origin_id:, destination_id:, date:, route_type: nil, vendor_id: nil, tenant_id: nil, number_of_guests: nil, params: {}) # rubocop:disable Metrics/ParameterLists
6
6
  @origin_id = origin_id
7
7
  @destination_id = destination_id
8
8
  @date = date.to_date == Time.zone.now.to_date ? Time.zone.now : Time.zone.parse(date.to_s)
9
9
  @vendor_id = vendor_id
10
+ @tenant_id = tenant_id
10
11
  @number_of_guests = number_of_guests || 1
11
12
  @route_type = route_type
12
13
  @params = params
@@ -36,8 +37,9 @@ module SpreeCmCommissioner
36
37
 
37
38
  def direct_trips
38
39
  result = trip_scope
39
- result = result.where({ vendor_id: vendor_id }.compact)
40
+ result = result.where({ vendor_id: vendor_id }.compact) if vendor_id.present?
40
41
  result = result.where(route_type: route_type) if route_type.present?
42
+ result = result.where(spree_vendors: { tenant_id: tenant_id }) if tenant_id.present?
41
43
  paginate(result)
42
44
  end
43
45
 
@@ -56,20 +58,24 @@ module SpreeCmCommissioner
56
58
  private
57
59
 
58
60
  def trip_scope
59
- SpreeCmCommissioner::Trip
60
- .select(<<~SQL.squish)
61
- cm_trips.*,
62
- boarding.departure_time AS boarding_departure_time,
63
- boarding.stop_place_id AS boarding_stop_id, boarding.stop_name AS boarding_stop_name,
64
- drop_off.stop_name AS drop_off_stop_name, drop_off.stop_place_id AS drop_off_stop_id,
65
- drop_off.arrival_time AS drop_off_arrival_time,
66
- COALESCE(iv.quantity_available, 0) AS quantity_available,
67
- COALESCE(iv.max_capacity, 0) AS max_capacity,
68
- origin_places.name AS origin_place_name, dest_places.name AS destination_place_name,
69
- prices.amount AS amount, prices.compare_at_amount AS compare_at_amount,
70
- prices.currency AS currency
71
- SQL
72
- .includes(vendor: :logo, vehicle: :option_values)
61
+ scope = SpreeCmCommissioner::Trip
62
+ .select(<<~SQL.squish)
63
+ cm_trips.*,
64
+ boarding.departure_time AS boarding_departure_time,
65
+ boarding.stop_place_id AS boarding_stop_id, boarding.stop_name AS boarding_stop_name,
66
+ drop_off.stop_name AS drop_off_stop_name, drop_off.stop_place_id AS drop_off_stop_id,
67
+ drop_off.arrival_time AS drop_off_arrival_time,
68
+ COALESCE(iv.quantity_available, 0) AS quantity_available,
69
+ COALESCE(iv.max_capacity, 0) AS max_capacity,
70
+ origin_places.name AS origin_place_name, dest_places.name AS destination_place_name,
71
+ prices.amount AS amount, prices.compare_at_amount AS compare_at_amount,
72
+ prices.currency AS currency
73
+ SQL
74
+ .includes(vendor: :logo, vehicle: :option_values)
75
+
76
+ scope = scope.joins(:vendor) if tenant_id.present?
77
+
78
+ scope
73
79
  .joins(<<~SQL.squish)
74
80
  INNER JOIN cm_trip_stops AS boarding ON boarding.trip_id = cm_trips.id AND boarding.stop_type = 0
75
81
  INNER JOIN cm_trip_stops AS drop_off ON drop_off.trip_id = cm_trips.id AND drop_off.stop_type = 1
@@ -0,0 +1,10 @@
1
+ module SpreeCmCommissioner
2
+ class IntercityTaxiDraftOrderSchema < ApplicationRequestSchema
3
+ params do
4
+ required(:trip_id).value(:integer)
5
+ required(:from_date).value(:date)
6
+ required(:to_date).value(:date)
7
+ required(:quantity).value(:integer)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module SpreeCmCommissioner
2
+ class IntercityTaxiDraftOrderUpdateSchema < ApplicationRequestSchema
3
+ params do
4
+ required(:order).hash do
5
+ # Rails nested attributes send line_items_attributes as hash with numeric string keys (e.g., "0", "1")
6
+ required(:line_items_attributes).hash
7
+ end
8
+ end
9
+ end
10
+ end
@@ -4,7 +4,7 @@ module SpreeCmCommissioner
4
4
  class TripVehicleSerializer < BaseSerializer
5
5
  set_type :vehicle
6
6
 
7
- attributes :id, :code, :vehicle_type
7
+ attributes :id, :code, :vehicle_type, :number_of_seats
8
8
 
9
9
  has_many :vehicle_photos, serializer: ::SpreeCmCommissioner::V2::Storefront::AssetSerializer
10
10
  has_many :amenities, serializer: ::SpreeCmCommissioner::V2::Storefront::AmenitySerializer
@@ -1,23 +1,23 @@
1
1
  module SpreeCmCommissioner
2
2
  module IntercityTaxiOrder
3
3
  class Create
4
- attr_reader :trip_id, :from_date, :to_date, :user_id, :quantity
4
+ attr_reader :trip_id, :from_date, :to_date, :user, :quantity
5
5
 
6
- def self.call(trip_id:, from_date:, to_date:, user_id: nil, quantity: 1)
6
+ def self.call(trip_id:, from_date:, to_date:, user: nil, quantity: 1)
7
7
  new(
8
8
  trip_id: trip_id,
9
9
  from_date: from_date,
10
10
  to_date: to_date,
11
- user_id: user_id,
11
+ user: user,
12
12
  quantity: quantity
13
13
  ).call
14
14
  end
15
15
 
16
- def initialize(trip_id:, from_date:, to_date:, user_id:, quantity:)
16
+ def initialize(trip_id:, from_date:, to_date:, user:, quantity:)
17
17
  @trip_id = trip_id
18
18
  @from_date = from_date
19
19
  @to_date = to_date
20
- @user_id = user_id
20
+ @user = user
21
21
  @quantity = quantity.to_i <= 0 ? 1 : quantity.to_i
22
22
  end
23
23
 
@@ -29,7 +29,7 @@ module SpreeCmCommissioner
29
29
  variant = trip.product.variants.first
30
30
  raise ArgumentError, 'No variant found for trip' if variant.blank?
31
31
 
32
- order = Spree::Order.new(state: 'cart', use_billing: true, user_id: user_id)
32
+ order = Spree::Order.new(state: 'cart', use_billing: true, user: user)
33
33
 
34
34
  line_item = order.line_items.new(
35
35
  variant_id: variant.id,
@@ -42,6 +42,7 @@ module SpreeCmCommissioner
42
42
  )
43
43
 
44
44
  build_guests_for!(line_item, quantity)
45
+ assign_saved_guests_to_line_item(line_item)
45
46
 
46
47
  order.save!
47
48
  order.update_with_updater!
@@ -62,6 +63,13 @@ module SpreeCmCommissioner
62
63
  def build_guests_for!(line_item, qty)
63
64
  Array.new(qty) { line_item.guests.new }
64
65
  end
66
+
67
+ def assign_saved_guests_to_line_item(line_item)
68
+ line_item.guests.each do |guest|
69
+ saved_guest = SpreeCmCommissioner::SavedGuest.new
70
+ guest.saved_guest = saved_guest
71
+ end
72
+ end
65
73
  end
66
74
  end
67
75
  end
@@ -1,79 +1,22 @@
1
1
  module SpreeCmCommissioner
2
2
  module IntercityTaxiOrder
3
3
  class Update
4
- attr_reader :order_number, :params
4
+ attr_reader :order, :order_params
5
5
 
6
- def self.call(order_number:, params:)
7
- new(order_number: order_number, params: params).call
6
+ def self.call(order:, order_params:)
7
+ new(order: order, order_params: order_params).call
8
8
  end
9
9
 
10
- def initialize(order_number:, params:)
11
- @order_number = order_number
12
- @params = ensure_parameters(params)
10
+ def initialize(order:, order_params:)
11
+ @order = order
12
+ @order_params = order_params
13
13
  end
14
14
 
15
15
  def call
16
- order = Spree::Order.find_by!(number: order_number)
17
16
  order.update!(order_params)
18
17
 
19
- create_billing_address_if_missing(order)
20
-
21
18
  order
22
19
  end
23
-
24
- private
25
-
26
- def ensure_parameters(params)
27
- return params if params.is_a?(::ActionController::Parameters)
28
-
29
- ::ActionController::Parameters.new(params)
30
- end
31
-
32
- def create_billing_address_if_missing(order)
33
- return if order.bill_address.present?
34
-
35
- SpreeCmCommissioner::BillingAddressCreator.call(order: order)
36
- end
37
-
38
- def order_params
39
- params.require(:order).permit(
40
- :intel_phone_number,
41
- :email,
42
- line_items_attributes: [
43
- :id,
44
- :pickup_place_name,
45
- :drop_off_place_name,
46
- :from_date,
47
- :pickup_lat,
48
- :pickup_lng,
49
- :pickup_oob_confirmed,
50
- :drop_off_lat,
51
- :drop_off_lng,
52
- :drop_off_oob_confirmed,
53
- :distance_km,
54
- :ordered_points,
55
- :base_km,
56
- :detour_pickup_km,
57
- :detour_dropoff_km,
58
- :extra_pickup_km,
59
- :extra_dropoff_km,
60
- :extra_pickup_charge_usd,
61
- :extra_dropoff_charge_usd,
62
- :estimated_time_minutes,
63
- :remark,
64
- :passenger_count,
65
- { guests_attributes: %i[
66
- id
67
- first_name
68
- last_name
69
- gender
70
- nationality_id
71
- age
72
- ]
73
- }
74
- ]
75
- )
76
- end
77
20
  end
78
21
  end
79
22
  end
@@ -3,6 +3,8 @@
3
3
  <%# :asset %>
4
4
  <%# :remove_url, optional %>
5
5
  <%# :classes, optional %>
6
+ <%# :aspect_ratio, optional %>
7
+ <%# :size_info, optional %>
6
8
  <%# :form %>
7
9
  <%# :required, optional, default: false %>
8
10
 
@@ -16,6 +18,17 @@
16
18
  class: 'rounded border mb-4 mw-100',
17
19
  style: 'max-height: 100px') if asset.present? && asset.attachment.present? %>
18
20
 
21
+ <% if defined?(aspect_ratio) || defined?(size_info) %>
22
+ <small class="mb-2 form-text text-muted d-block">
23
+ <% if defined?(aspect_ratio) && aspect_ratio.present? %>
24
+ <strong>Aspect Ratio:</strong> <%= aspect_ratio %><br>
25
+ <% end %>
26
+ <% if defined?(size_info) && size_info.present? %>
27
+ <strong>Recommended Size:</strong> <%= size_info %>
28
+ <% end %>
29
+ </small>
30
+ <% end %>
31
+
19
32
  <div data-hook="file" class="form-group">
20
33
  <%= form.label field, label || Spree.t(field) %>
21
34
  <%= form.file_field field, required: required %>
@@ -1,7 +1,7 @@
1
1
  <div data-hook="admin_product_completion_step_form_fields">
2
2
  <div data-hook="product_completion_step" class="row">
3
3
  <div class="col-12 col-md-6">
4
- <div class="card mb-3">
4
+ <div class="mb-3 card">
5
5
  <div class="card-header">
6
6
  <h5 class="mb-0"><%= Spree.t(:settings) %></h5>
7
7
  </div>
@@ -32,7 +32,7 @@
32
32
  </div>
33
33
 
34
34
  <div class="col-12 col-md-6">
35
- <div class="card mb-3">
35
+ <div class="mb-3 card">
36
36
  <div class="card-header">
37
37
  <h5 class="mb-0"><%= Spree.t(:completion_settings) %></h5>
38
38
  </div>
@@ -49,6 +49,32 @@
49
49
  </div>
50
50
  </div>
51
51
  </div>
52
+
53
+ <div class="mb-3 card">
54
+ <div class="card-header">
55
+ <h5 class="mb-0"><%= Spree.t(:banner) %></h5>
56
+ </div>
57
+ <div class="card-body">
58
+ <%= render 'shared/asset_field',
59
+ field: :banner,
60
+ label: Spree.t(:banner),
61
+ asset: @object.banner,
62
+ remove_url: (@object.banner.present? ? remove_banner_admin_product_product_completion_step_url(@object.product, @object) : nil),
63
+ form: f,
64
+ size_info: 'Min: 480x270, Max: 960x540',
65
+ aspect_ratio: '16x9',
66
+ classes: ['col-md-12']
67
+ %>
68
+ </div>
69
+ </div>
52
70
  </div>
53
71
  </div>
72
+
73
+ <% if @object.is_a?(SpreeCmCommissioner::ProductCompletionSteps::SocialEntryUrl) %>
74
+ <div class="row mt-3">
75
+ <div class="col-12">
76
+ <%= render 'supported_fields' %>
77
+ </div>
78
+ </div>
79
+ <% end %>
54
80
  </div>
@@ -0,0 +1,33 @@
1
+ <div class="mb-3 card">
2
+ <div class="card-header">
3
+ <h5 class="mb-0">Supported Placeholder Fields</h5>
4
+ </div>
5
+ <div class="card-body">
6
+ <p class="text-muted mb-3">Use these placeholders in your entry point link template:</p>
7
+
8
+ <% if @object.is_a?(SpreeCmCommissioner::ProductCompletionSteps::SocialEntryUrl) && @object.supported_fields.present? %>
9
+ <% @object.supported_fields.each do |category, fields| %>
10
+ <div class="mb-3">
11
+ <h6 class="fw-bold text-capitalize">
12
+ <%= category.to_s.humanize %>
13
+ <span class="badge bg-secondary"><%= fields.count %></span>
14
+ </h6>
15
+ <div class="field-list">
16
+ <% fields.each do |field| %>
17
+ <div class="field-item d-inline-block me-2 mb-2">
18
+ <code class="bg-light p-2 rounded">
19
+ {<%= category %><%= category == :guests ? '[index]' : '' %>.<%=field %>}
20
+ </code>
21
+ </div>
22
+ <% end %>
23
+ </div>
24
+ </div>
25
+ <% end %>
26
+
27
+ <div class="alert alert-info mt-3">
28
+ <strong>Example:</strong><br>
29
+ <code>https://example.com?order={order.number}&name={bill_address.first_name}&guest={guests[0].first_name}</code>
30
+ </div>
31
+ <% end %>
32
+ </div>
33
+ </div>
@@ -36,7 +36,7 @@
36
36
  <div class="col-12">
37
37
  <%= f.field_container :host do %>
38
38
  <%= f.label :host, raw(Spree.t(:host) + required_span_tag) %>
39
- <%= f.text_field :host, required: true, class: 'form-control', placeholder: 'host' %>
39
+ <%= f.text_field :host, required: true, class: 'form-control', placeholder: 'example.com' %>
40
40
  <% if f.object.errors[:host].any? %>
41
41
  <div class="error text-danger"><%= f.object.errors[:host].join(', ') %></div>
42
42
  <% end %>
@@ -48,6 +48,7 @@ module Spree
48
48
  :preferred_sms_sender_id,
49
49
  :preferred_telegram_order_alert_chat_id,
50
50
  :preferred_telegram_order_request_alert_chat_id,
51
+ :preferred_telegram_new_vendor_alert_chat_id,
51
52
  :preferred_assetlinks,
52
53
  :preferred_apple_app_site_association,
53
54
  { default_notification_image_attributes: {} },
@@ -509,6 +509,10 @@ en:
509
509
  attributes:
510
510
  place_id:
511
511
  taken: "has already existed with this place type"
512
+ spree_cm_commissioner/tenant:
513
+ attributes:
514
+ host:
515
+ format: "must not include protocol (http:// or https://)"
512
516
 
513
517
  mail:
514
518
  order_mailer: