spree_cm_commissioner 2.4.1 → 2.4.3
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +32 -0
- data/app/controllers/spree/api/v2/storefront/popular_route_places_controller.rb +3 -1
- data/app/controllers/spree/api/v2/storefront/route_places_controller.rb +4 -3
- data/app/controllers/spree/api/v2/storefront/tenants_controller.rb +30 -0
- data/app/finders/spree_cm_commissioner/places/find_with_route.rb +48 -18
- data/app/finders/spree_cm_commissioner/routes/find_popular.rb +32 -7
- data/app/models/concerns/spree_cm_commissioner/tenant_preference.rb +2 -0
- data/app/models/concerns/spree_cm_commissioner/vehicle_type.rb +1 -1
- data/app/models/spree_cm_commissioner/place.rb +3 -0
- data/app/request_schemas/spree_cm_commissioner/route_places_request_schema.rb +1 -0
- data/app/serializers/spree/v2/storefront/tenant_serializer.rb +14 -0
- data/app/views/spree/admin/tenants/_form.html.erb +79 -36
- data/config/routes.rb +1 -1
- data/lib/spree_cm_commissioner/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5f580394367847906251ac52666c73ae79bb1a117948805217616c0a83f9300a
|
|
4
|
+
data.tar.gz: e8b7ae961151a074aab2084e7600b55da2969405143cf1b9e4cd1b95fcd7040b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 35e303df725eb93bb52a3eceb55672668cea800d77dd7c4e4d938b9caf0585477044e20fe646f2a2a7da5209a4ceff6c78ccf8e6adb862378373a66bd1525d19
|
|
7
|
+
data.tar.gz: f91afe8506292cf778cfb3b8bfdc87f5d2545c2e079abdf5f5d881441c3ae758f218bcbb6798756f860734b5c3af82be30250c2c0a2f5a9e2aea96782d7623ed
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -525,3 +525,35 @@ def wrap_with_multitenant_without(&block)
|
|
|
525
525
|
MultiTenant.without(&block)
|
|
526
526
|
end
|
|
527
527
|
```
|
|
528
|
+
|
|
529
|
+
# Tenant Configuration
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
This gem provides a flexible multi-tenant system. Each tenant can be configured through an admin interface and accessed via API endpoints.
|
|
533
|
+
|
|
534
|
+
### Admin Tenant Form Structure
|
|
535
|
+
|
|
536
|
+
The tenant configuration form is organized into several logical sections:
|
|
537
|
+
|
|
538
|
+
- **Basic Information**: General details such as `name`, `slug`, `description`, `host`, and `status`.
|
|
539
|
+
- **Branding & App Links**: Custom branding options, `Android asset links`, and `Apple app site association` data.
|
|
540
|
+
- **Payment Icons**: Upload and manage icons for different payment statuses (`checkout`, `failed`, `success`, `loader`).
|
|
541
|
+
- **Redirect Settings**: Configure `redirect targets` and `excluded paths` for host-based redirection.
|
|
542
|
+
|
|
543
|
+
All sections are visually separated for clarity and improved user experience.
|
|
544
|
+
|
|
545
|
+
### API: Retrieve Tenant by Host
|
|
546
|
+
|
|
547
|
+
With your `Client-Side` you can retrieve tenant configuration by host using a RESTful API endpoint:
|
|
548
|
+
|
|
549
|
+
```http
|
|
550
|
+
GET /api/v2/storefront/tenants/:host
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
Example request:
|
|
554
|
+
|
|
555
|
+
```
|
|
556
|
+
GET /api/v2/storefront/tenants/example.host.com
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
The response returns the tenant's configuration in JSON format, including preferences and branding information.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# GET /api/v2/storefront/route_places
|
|
4
4
|
#
|
|
5
5
|
# Finds places (origins or destinations) that are connected to a given place through existing routes.
|
|
6
|
-
# Optionally filters results by keyword. Supports two modes:
|
|
6
|
+
# Optionally filters results by keyword or route_type. Supports two modes:
|
|
7
7
|
#
|
|
8
8
|
# Usage 1: Connected places (requires place_id)
|
|
9
9
|
# - Finds places connected to a specific place via routes
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
# - Returns empty collection if place_type is invalid
|
|
28
28
|
# - If place_id provided: returns places connected to that place
|
|
29
29
|
# - If place_id blank: returns all origins/destinations
|
|
30
|
-
# - Filters results by keyword if provided
|
|
30
|
+
# - Filters results by keyword or route_type if provided
|
|
31
31
|
#
|
|
32
32
|
# @example Mode 1: Find destinations connected to origin place ID 123
|
|
33
33
|
# GET /api/v2/storefront/route_places?place_id=123&place_type=destination
|
|
@@ -49,7 +49,8 @@ module Spree
|
|
|
49
49
|
@collection ||= collection_finder.new(
|
|
50
50
|
place_type: params[:place_type],
|
|
51
51
|
place_id: params[:place_id],
|
|
52
|
-
keyword: params[:query]
|
|
52
|
+
keyword: params[:query],
|
|
53
|
+
route_type: params[:route_type]
|
|
53
54
|
).execute
|
|
54
55
|
end
|
|
55
56
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V2
|
|
4
|
+
module Storefront
|
|
5
|
+
class TenantsController < ::Spree::Api::V2::BaseController
|
|
6
|
+
def show
|
|
7
|
+
render_serialized_payload { serialize_resource(resource) }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
# override
|
|
13
|
+
def resource
|
|
14
|
+
model_class.find_by(host: params[:id])
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# override
|
|
18
|
+
def model_class
|
|
19
|
+
SpreeCmCommissioner::Tenant
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# override
|
|
23
|
+
def resource_serializer
|
|
24
|
+
Spree::V2::Storefront::TenantSerializer
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -3,48 +3,78 @@
|
|
|
3
3
|
# @param place_type [String] Required. 'origin' or 'destination'
|
|
4
4
|
# @param place_id [Integer] Optional. Filter by connected place ID
|
|
5
5
|
# @param keyword [String] Optional. Filter by place name (case-insensitive)
|
|
6
|
+
# @param route_type [Symbol, String] Optional. Filter by route type from associated trips (e.g., :ferry, :bus)
|
|
6
7
|
#
|
|
7
8
|
# @return [ActiveRecord::Relation<SpreeCmCommissioner::Place>]
|
|
8
9
|
#
|
|
9
10
|
# Modes:
|
|
10
11
|
# - place_id only: returns connected places (origins TO or destinations FROM given place)
|
|
11
12
|
# - keyword only: filters by name
|
|
12
|
-
# -
|
|
13
|
+
# - route_type only: filters by trip route types on those routes
|
|
14
|
+
# - combinations: connected places filtered by name and/or route type
|
|
13
15
|
# - neither: all origins/destinations in routes
|
|
14
16
|
#
|
|
15
|
-
# @example Origins with
|
|
16
|
-
# FindWithRoute.new(place_type: 'origin', place_id: 123).execute
|
|
17
|
+
# @example Origins with ferry trips to place 123
|
|
18
|
+
# FindWithRoute.new(place_type: 'origin', place_id: 123, route_type: :ferry).execute
|
|
17
19
|
#
|
|
18
|
-
# @example Destinations filtered by keyword
|
|
19
|
-
# FindWithRoute.new(place_type: 'destination', keyword: 'Phnom').execute
|
|
20
|
+
# @example Destinations filtered by keyword and route type
|
|
21
|
+
# FindWithRoute.new(place_type: 'destination', keyword: 'Phnom', route_type: :bus).execute
|
|
20
22
|
module SpreeCmCommissioner
|
|
21
23
|
module Places
|
|
22
24
|
class FindWithRoute
|
|
23
|
-
attr_reader :place_type, :place_id, :keyword
|
|
25
|
+
attr_reader :place_type, :place_id, :keyword, :route_type
|
|
24
26
|
|
|
25
|
-
def initialize(place_type:, place_id: nil, keyword: nil)
|
|
27
|
+
def initialize(place_type:, place_id: nil, keyword: nil, route_type: nil)
|
|
26
28
|
@place_type = place_type
|
|
27
29
|
@place_id = place_id
|
|
28
30
|
@keyword = keyword
|
|
31
|
+
@route_type = route_type
|
|
29
32
|
end
|
|
30
33
|
|
|
31
34
|
def execute
|
|
32
35
|
return SpreeCmCommissioner::Place.none if place_type.blank?
|
|
33
|
-
|
|
34
|
-
result = if place_type == 'origin'
|
|
35
|
-
scope = SpreeCmCommissioner::Place.with_routes_as_origin
|
|
36
|
-
place_id.present? ? scope.where(cm_routes: { destination_place_id: place_id }) : scope
|
|
37
|
-
else
|
|
38
|
-
scope = SpreeCmCommissioner::Place.with_routes_as_destination
|
|
39
|
-
place_id.present? ? scope.where(cm_routes: { origin_place_id: place_id }) : scope
|
|
40
|
-
end
|
|
41
|
-
|
|
42
36
|
return SpreeCmCommissioner::Place.none if place_id.present? && !SpreeCmCommissioner::Place.exists?(place_id)
|
|
43
37
|
|
|
44
|
-
|
|
45
|
-
result = result
|
|
38
|
+
result = scope
|
|
39
|
+
result = apply_keyword_filter(result) if keyword.present?
|
|
40
|
+
result = apply_route_type_filter(result) if route_type.present?
|
|
46
41
|
result
|
|
47
42
|
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def scope
|
|
47
|
+
return SpreeCmCommissioner::Place.none unless valid_place_type?
|
|
48
|
+
|
|
49
|
+
base_scope = origin? ? SpreeCmCommissioner::Place.with_routes_as_origin : SpreeCmCommissioner::Place.with_routes_as_destination
|
|
50
|
+
|
|
51
|
+
return base_scope if place_id.blank?
|
|
52
|
+
|
|
53
|
+
if origin?
|
|
54
|
+
base_scope.where(routes_as_origin: { destination_place_id: place_id })
|
|
55
|
+
else
|
|
56
|
+
base_scope.where(routes_as_destination: { origin_place_id: place_id })
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def apply_keyword_filter(result)
|
|
61
|
+
result.where('cm_places.name ILIKE ?', "%#{keyword}%")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def apply_route_type_filter(result)
|
|
65
|
+
trips_association = origin? ? :trips_as_origin : :trips_as_destination
|
|
66
|
+
result.joins(trips_association)
|
|
67
|
+
.where(cm_trips: { route_type: route_type.to_sym })
|
|
68
|
+
.distinct
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def origin?
|
|
72
|
+
place_type == 'origin'
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def valid_place_type?
|
|
76
|
+
%w[origin destination].include?(place_type)
|
|
77
|
+
end
|
|
48
78
|
end
|
|
49
79
|
end
|
|
50
80
|
end
|
|
@@ -4,21 +4,46 @@
|
|
|
4
4
|
# 1. fulfilled_order_count (completed orders) - DESC
|
|
5
5
|
# 2. order_count (total orders including incomplete) - DESC
|
|
6
6
|
#
|
|
7
|
+
# @param route_type [Symbol, String] Optional. Filter by route type from associated trips (e.g., :ferry, :bus)
|
|
8
|
+
#
|
|
7
9
|
# @return [ActiveRecord::Relation<SpreeCmCommissioner::Route>] Routes with origin and destination places loaded,
|
|
8
10
|
# ordered from most to least popular
|
|
9
11
|
#
|
|
10
|
-
# @example
|
|
12
|
+
# @example Get all popular routes
|
|
13
|
+
# finder = SpreeCmCommissioner::Routes::FindPopular.new
|
|
14
|
+
# finder.execute # => Returns all routes sorted by fulfillment and order counts
|
|
15
|
+
#
|
|
16
|
+
# @example Get popular ferry routes
|
|
17
|
+
# finder = SpreeCmCommissioner::Routes::FindPopular.new
|
|
18
|
+
# finder.execute(route_type: :ferry) # => Returns ferry routes sorted by popularity
|
|
19
|
+
#
|
|
20
|
+
# @example Get popular bus routes
|
|
11
21
|
# finder = SpreeCmCommissioner::Routes::FindPopular.new
|
|
12
|
-
# finder.execute # => Returns routes sorted by
|
|
22
|
+
# finder.execute(route_type: :bus) # => Returns bus routes sorted by popularity
|
|
13
23
|
|
|
14
24
|
module SpreeCmCommissioner
|
|
15
25
|
module Routes
|
|
16
26
|
class FindPopular
|
|
17
|
-
def execute
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
def execute(route_type: nil)
|
|
28
|
+
scope(route_type: route_type)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def scope(route_type: nil)
|
|
34
|
+
result = SpreeCmCommissioner::Route
|
|
35
|
+
.includes(:origin_place, :destination_place)
|
|
36
|
+
|
|
37
|
+
if route_type.present?
|
|
38
|
+
# Filter routes that have trips with the specified route_type
|
|
39
|
+
route_scope = SpreeCmCommissioner::Route
|
|
40
|
+
.joins(:trips)
|
|
41
|
+
.where(cm_trips: { route_type: route_type.to_sym })
|
|
42
|
+
result = result.where(id: route_scope.select(:id))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
result.order(Arel.sql('COALESCE(fulfilled_order_count, 0) DESC'))
|
|
46
|
+
.order(Arel.sql('COALESCE(order_count, 0) DESC'))
|
|
22
47
|
end
|
|
23
48
|
end
|
|
24
49
|
end
|
|
@@ -10,6 +10,8 @@ module SpreeCmCommissioner
|
|
|
10
10
|
preference :payment_success_image, :string, default: ''
|
|
11
11
|
preference :payment_loader, :string, default: ''
|
|
12
12
|
preference :brand_primary_color, :string, default: ''
|
|
13
|
+
preference :redirect_target_host, :string, default: ''
|
|
14
|
+
preference :redirect_excluded_paths, :string, default: ''
|
|
13
15
|
end
|
|
14
16
|
end
|
|
15
17
|
end
|
|
@@ -2,7 +2,7 @@ module SpreeCmCommissioner
|
|
|
2
2
|
module VehicleType
|
|
3
3
|
extend ActiveSupport::Concern
|
|
4
4
|
|
|
5
|
-
VEHICLE_TYPES = %i[suv sedan minivan van sleeping_bus luxury_van air_bus bus].freeze
|
|
5
|
+
VEHICLE_TYPES = %i[suv sedan minivan van sleeping_bus luxury_van air_bus bus ferry].freeze
|
|
6
6
|
|
|
7
7
|
included do
|
|
8
8
|
enum vehicle_type: VEHICLE_TYPES if table_exists? && column_names.include?('vehicle_type')
|
|
@@ -24,6 +24,9 @@ module SpreeCmCommissioner
|
|
|
24
24
|
has_many :routes_as_origin, class_name: 'SpreeCmCommissioner::Route', foreign_key: :origin_place_id
|
|
25
25
|
has_many :routes_as_destination, class_name: 'SpreeCmCommissioner::Route', foreign_key: :destination_place_id
|
|
26
26
|
|
|
27
|
+
has_many :trips_as_origin, through: :routes_as_origin, source: :trips
|
|
28
|
+
has_many :trips_as_destination, through: :routes_as_destination, source: :trips
|
|
29
|
+
|
|
27
30
|
# Scopes for route-based filtering
|
|
28
31
|
scope :with_routes_as_origin, -> { joins(:routes_as_origin).distinct }
|
|
29
32
|
scope :with_routes_as_destination, -> { joins(:routes_as_destination).distinct }
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module V2
|
|
3
|
+
module Storefront
|
|
4
|
+
class TenantSerializer < BaseSerializer
|
|
5
|
+
set_type :tenant
|
|
6
|
+
|
|
7
|
+
attributes :name, :slug, :host, :state
|
|
8
|
+
|
|
9
|
+
attribute :redirect_target_host, &:preferred_redirect_target_host
|
|
10
|
+
attribute :redirect_excluded_paths, &:preferred_redirect_excluded_paths
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
|
|
2
|
+
<!-- ======================== Tenant Basic Info ======================== -->
|
|
3
|
+
<div class="card mb-4">
|
|
4
|
+
<div class="card-body">
|
|
5
|
+
<div data-hook="admin_tenant_form_fields" class="row">
|
|
6
|
+
<!-- Basic Info Section -->
|
|
7
|
+
<div class="col-12 mb-4">
|
|
8
|
+
<h4>Basic Information <span class="text-muted" style="font-size: 1rem; font-weight: normal;">(General details about the tenant)</span></h4>
|
|
9
|
+
</div>
|
|
3
10
|
<div class="col-6">
|
|
4
11
|
<%= f.field_container :name do %>
|
|
5
12
|
<%= f.label :name, raw(Spree.t(:name) + required_span_tag) %>
|
|
@@ -9,8 +16,6 @@
|
|
|
9
16
|
<% end %>
|
|
10
17
|
<% end %>
|
|
11
18
|
</div>
|
|
12
|
-
|
|
13
|
-
<!-- Slug Field -->
|
|
14
19
|
<div class="col-6">
|
|
15
20
|
<%= f.field_container :slug do %>
|
|
16
21
|
<%= f.label :slug, raw(Spree.t(:slug)) %>
|
|
@@ -20,8 +25,6 @@
|
|
|
20
25
|
<% end %>
|
|
21
26
|
<% end %>
|
|
22
27
|
</div>
|
|
23
|
-
|
|
24
|
-
<!-- Description Field -->
|
|
25
28
|
<div class="col-12">
|
|
26
29
|
<%= f.field_container :description do %>
|
|
27
30
|
<%= f.label :description, raw(Spree.t(:description)) %>
|
|
@@ -31,8 +34,6 @@
|
|
|
31
34
|
<% end %>
|
|
32
35
|
<% end %>
|
|
33
36
|
</div>
|
|
34
|
-
|
|
35
|
-
<!-- Host Field -->
|
|
36
37
|
<div class="col-12">
|
|
37
38
|
<%= f.field_container :host do %>
|
|
38
39
|
<%= f.label :host, raw(Spree.t(:host) + required_span_tag) %>
|
|
@@ -42,8 +43,37 @@
|
|
|
42
43
|
<% end %>
|
|
43
44
|
<% end %>
|
|
44
45
|
</div>
|
|
46
|
+
<div class="col-6">
|
|
47
|
+
<%= f.field_container :state do %>
|
|
48
|
+
<%= f.label :state, raw(Spree.t(:state)) %>
|
|
49
|
+
<%= f.select :state,
|
|
50
|
+
options_for_select([["Enabled", "enabled"], ["Disabled", "disabled"]], f.object&.state),
|
|
51
|
+
{ required: true },
|
|
52
|
+
class: 'select2 form-control' %>
|
|
53
|
+
<% if f.object.errors[:state].any? %>
|
|
54
|
+
<div class="error text-danger"><%= f.object.errors[:state].join(', ') %></div>
|
|
55
|
+
<% end %>
|
|
56
|
+
<% end %>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
45
61
|
|
|
46
|
-
|
|
62
|
+
<!-- ======================== Branding & App Links ======================== -->
|
|
63
|
+
<div class="card mb-4">
|
|
64
|
+
<div class="card-body">
|
|
65
|
+
<div data-hook="admin_tenant_form_fields" class="row mt-4">
|
|
66
|
+
<!-- Branding Section -->
|
|
67
|
+
<div class="col-12 mb-4">
|
|
68
|
+
<h4>Branding & App Links <span class="text-muted" style="font-size: 1rem; font-weight: normal;">(Brand colors and mobile app association settings)</span></h4>
|
|
69
|
+
</div>
|
|
70
|
+
<div class="col-md-6">
|
|
71
|
+
<%= f.label :preferred_brand_primary_color, Spree.t(:brand_primary_color), class: 'form-label' %>
|
|
72
|
+
<%= f.text_field :preferred_brand_primary_color, class: 'form-control color-picker', placeholder: '#FF5733', value: @object.preferred_brand_primary_color %>
|
|
73
|
+
<div class="color-preview" style="margin-top: 10px;">
|
|
74
|
+
<%= content_tag :div, nil, style: "background-color: #{@object.preferred_brand_primary_color}; width: 60px; height: 60px; border: 1px solid #ccc; box-sizing: border-box; display: block;" %>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
47
77
|
<div class="col-12">
|
|
48
78
|
<%= f.field_container :preferred_assetlinks do %>
|
|
49
79
|
<%= f.label :preferred_assetlinks, raw(Spree.t(:assetlinks)) %>
|
|
@@ -69,8 +99,6 @@
|
|
|
69
99
|
<% end %>
|
|
70
100
|
<% end %>
|
|
71
101
|
</div>
|
|
72
|
-
|
|
73
|
-
<!-- Apple App Site Association Field -->
|
|
74
102
|
<div class="col-12">
|
|
75
103
|
<%= f.field_container :preferred_apple_app_site_association do %>
|
|
76
104
|
<%= f.label :preferred_apple_app_site_association, raw(Spree.t(:apple_app_site_association)) %>
|
|
@@ -100,32 +128,18 @@
|
|
|
100
128
|
<% end %>
|
|
101
129
|
<% end %>
|
|
102
130
|
</div>
|
|
103
|
-
|
|
104
|
-
<!-- Brand Primary Color Field -->
|
|
105
|
-
<div class="col-md-6">
|
|
106
|
-
<%= f.label :preferred_brand_primary_color, Spree.t(:brand_primary_color), class: 'form-label' %>
|
|
107
|
-
<%= f.text_field :preferred_brand_primary_color, class: 'form-control color-picker', placeholder: '#FF5733', value: @object.preferred_brand_primary_color %>
|
|
108
|
-
<div class="color-preview" style="margin-top: 10px;">
|
|
109
|
-
<%= content_tag :div, nil, style: "background-color: #{@object.preferred_brand_primary_color}; width: 60px; height: 60px; border: 1px solid #ccc; box-sizing: border-box; display: block;" %>
|
|
110
131
|
</div>
|
|
111
132
|
</div>
|
|
112
|
-
|
|
113
|
-
<!-- State Field -->
|
|
114
|
-
<div class="col-6">
|
|
115
|
-
<%= f.field_container :state do %>
|
|
116
|
-
<%= f.label :state, raw(Spree.t(:state)) %>
|
|
117
|
-
<%= f.select :state,
|
|
118
|
-
options_for_select([['Enabled', 'enabled'], ['Disabled', 'disabled']], f.object&.state),
|
|
119
|
-
{ required: true },
|
|
120
|
-
class: 'select2 form-control' %>
|
|
121
|
-
<% if f.object.errors[:state].any? %>
|
|
122
|
-
<div class="error text-danger"><%= f.object.errors[:state].join(', ') %></div>
|
|
123
|
-
<% end %>
|
|
124
|
-
<% end %>
|
|
125
|
-
</div>
|
|
126
133
|
</div>
|
|
127
134
|
|
|
128
|
-
|
|
135
|
+
<!-- ======================== Payment Icons ======================== -->
|
|
136
|
+
<div class="card mb-4">
|
|
137
|
+
<div class="card-body">
|
|
138
|
+
<div data-hook="admin_tenant_form_fields" class="row mt-4">
|
|
139
|
+
<!-- Payment Icons Section -->
|
|
140
|
+
<div class="col-12 mb-4">
|
|
141
|
+
<h4>Payment Icons <span class="text-muted" style="font-size: 1rem; font-weight: normal;">(Customize payment status icons for tenant checkout experience)</span></h4>
|
|
142
|
+
</div>
|
|
129
143
|
<div class="col-12">
|
|
130
144
|
<div class="row gy-4">
|
|
131
145
|
<% payment_icon_fields = [
|
|
@@ -134,7 +148,6 @@
|
|
|
134
148
|
{ field: :preferred_payment_success_image, label: Spree.t(:payment_success_image), target: 'success' },
|
|
135
149
|
{ field: :preferred_payment_loader, label: Spree.t(:payment_loader), target: 'loader' }
|
|
136
150
|
] %>
|
|
137
|
-
|
|
138
151
|
<% payment_icon_fields.each do |icon| %>
|
|
139
152
|
<div class="col-md-3">
|
|
140
153
|
<%= f.field_container icon[:field] do %>
|
|
@@ -142,7 +155,6 @@
|
|
|
142
155
|
<label class="form-label fw-bold mb-2" for="tenant_<%= icon[:field] %>">
|
|
143
156
|
<%= icon[:label] %>
|
|
144
157
|
</label>
|
|
145
|
-
|
|
146
158
|
<div class="icon-preview-container mb-2" id="icon-preview-container-<%= icon[:field] %>">
|
|
147
159
|
<span class="payment-icon-preview" id="icon-preview-<%= icon[:field] %>">
|
|
148
160
|
<% if f.object.send(icon[:field]).present? %>
|
|
@@ -154,7 +166,6 @@
|
|
|
154
166
|
<span class="remove-icon-btn <%= 'hidden' unless f.object.send(icon[:field]).present? %>" data-target="<%= icon[:field] %>">×</span>
|
|
155
167
|
</div>
|
|
156
168
|
</div>
|
|
157
|
-
|
|
158
169
|
<%= f.collection_select icon[:field],
|
|
159
170
|
@vector_icons, :path, :path,
|
|
160
171
|
{ include_blank: 'None' },
|
|
@@ -167,4 +178,36 @@
|
|
|
167
178
|
<% end %>
|
|
168
179
|
</div>
|
|
169
180
|
</div>
|
|
181
|
+
</div>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<!-- ======================== Redirect Settings ======================== -->
|
|
186
|
+
<div class="card mb-4">
|
|
187
|
+
<div class="card-body">
|
|
188
|
+
<div data-hook="admin_tenant_form_fields" class="row mt-4">
|
|
189
|
+
<!-- Redirect Settings Section -->
|
|
190
|
+
<div class="col-12 mb-4">
|
|
191
|
+
<h4>Redirect Settings <span class="text-muted" style="font-size: 1rem; font-weight: normal;">(Configure host redirection and excluded paths for this tenant)</span></h4>
|
|
192
|
+
</div>
|
|
193
|
+
<div class="col-6">
|
|
194
|
+
<%= f.field_container :preferred_redirect_target_host do %>
|
|
195
|
+
<%= f.label :preferred_redirect_target_host, raw(Spree.t(:redirect_target_host)) %>
|
|
196
|
+
<%= f.text_field :preferred_redirect_target_host, class: 'form-control', placeholder: 'target.example.com' %>
|
|
197
|
+
<% if f.object.errors[:preferred_redirect_target_host].any? %>
|
|
198
|
+
<div class="error text-danger"><%= f.object.errors[:preferred_redirect_target_host].join(', ') %></div>
|
|
199
|
+
<% end %>
|
|
200
|
+
<% end %>
|
|
201
|
+
</div>
|
|
202
|
+
<div class="col-12">
|
|
203
|
+
<%= f.field_container :preferred_redirect_excluded_paths do %>
|
|
204
|
+
<%= f.label :preferred_redirect_excluded_paths, raw(Spree.t(:redirect_excluded_paths)) %>
|
|
205
|
+
<%= f.text_area :preferred_redirect_excluded_paths, class: 'form-control', placeholder: 'eg. /vpago_payments,/.well-known' %>
|
|
206
|
+
<% if f.object.errors[:preferred_redirect_excluded_paths].any? %>
|
|
207
|
+
<div class="error text-danger"><%= f.object.errors[:preferred_redirect_excluded_paths].join(', ') %></div>
|
|
208
|
+
<% end %>
|
|
209
|
+
<% end %>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
170
213
|
</div>
|
data/config/routes.rb
CHANGED
|
@@ -579,7 +579,7 @@ Spree::Core::Engine.add_routes do
|
|
|
579
579
|
resource :cart, controller: :cart, only: %i[show create destroy] do
|
|
580
580
|
patch :restart_checkout_flow
|
|
581
581
|
end
|
|
582
|
-
|
|
582
|
+
resources :tenants, only: %i[show], constraints: { id: /.+/ }
|
|
583
583
|
resources :trip_places
|
|
584
584
|
resources :trip_search, only: [:index]
|
|
585
585
|
resources :trips, only: %i[show]
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spree_cm_commissioner
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.4.
|
|
4
|
+
version: 2.4.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- You
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-12-
|
|
11
|
+
date: 2025-12-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: spree
|
|
@@ -979,6 +979,7 @@ files:
|
|
|
979
979
|
- app/controllers/spree/api/v2/storefront/s3_signed_urls_controller.rb
|
|
980
980
|
- app/controllers/spree/api/v2/storefront/seat_layouts_controller.rb
|
|
981
981
|
- app/controllers/spree/api/v2/storefront/self_check_in_controller.rb
|
|
982
|
+
- app/controllers/spree/api/v2/storefront/tenants_controller.rb
|
|
982
983
|
- app/controllers/spree/api/v2/storefront/transit/draft_orders_controller.rb
|
|
983
984
|
- app/controllers/spree/api/v2/storefront/trip_places_controller.rb
|
|
984
985
|
- app/controllers/spree/api/v2/storefront/trip_search_controller.rb
|
|
@@ -1816,6 +1817,7 @@ files:
|
|
|
1816
1817
|
- app/serializers/spree/v2/storefront/stock_location_serializer_decorator.rb
|
|
1817
1818
|
- app/serializers/spree/v2/storefront/store_promotion_serializer.rb
|
|
1818
1819
|
- app/serializers/spree/v2/storefront/taxon_serializer_decorator.rb
|
|
1820
|
+
- app/serializers/spree/v2/storefront/tenant_serializer.rb
|
|
1819
1821
|
- app/serializers/spree/v2/storefront/user_deletion_reason_serializer.rb
|
|
1820
1822
|
- app/serializers/spree/v2/storefront/user_device_token_serializer.rb
|
|
1821
1823
|
- app/serializers/spree/v2/storefront/user_profile_serializer.rb
|