Skip to content

Commit

Permalink
WIP#2045 add nearby places suggestion
Browse files Browse the repository at this point in the history
  • Loading branch information
nangdypanhar committed Nov 26, 2024
1 parent d5fea78 commit 62a6087
Show file tree
Hide file tree
Showing 10 changed files with 254 additions and 21 deletions.
6 changes: 5 additions & 1 deletion app/assets/javascripts/spree_cm_commissioner/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,8 @@ Spree.routes.seat_number_layouts_api_v2 = Spree.pathFor(

Spree.routes.homepage_section_relatables_api_v2 = Spree.pathFor(
"/api/v2/platform/homepage_section_relatable_options"
);
);

Spree.routes.product_places_api_v2 = Spree.pathFor(
"/api/v2/platform/product_places"
);
6 changes: 6 additions & 0 deletions app/controllers/spree/admin/nearby_places_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ module Spree
module Admin
class NearbyPlacesController < Spree::Admin::ResourceController
before_action :load_vendor
before_action :list_nearby_places, only: :new

def index
@nearby_places = @vendor.nearby_places
end

def list_nearby_places
context = SpreeCmCommissioner::VendorNearbyPlaceBuilder.call(vendor: @vendor)
@list_nearby_places = context.nearby_places.sort_by(&:distance).take(10)
end

def new
@name = params[:name]
context = SpreeCmCommissioner::VendorNearbyPlaceBuilder.call(vendor: @vendor, name: @name)
Expand Down
24 changes: 24 additions & 0 deletions app/controllers/spree/admin/product_places_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@ def new
end

def create
place_id = params.dig(:spree_cm_commissioner_product_place, :name)
place = SpreeCmCommissioner::Place.find_or_initialize_by(reference: place_id)

update_place_from_google(place, place_id) if place.new_record?

@product_place = SpreeCmCommissioner::ProductPlace.new(permitted_resource_params)
@product_place.product = parent
@product_place.place_id = place.id

if @product_place.place_id.blank?
flash[:error] = I18n.t('product_place.place_required')
Expand Down Expand Up @@ -60,6 +66,24 @@ def update_positions

private

def update_place_from_google(place, place_id)
context = SpreeCmCommissioner::GooglePlaceFetcher.call(reference: place_id)

flash[:error] = Spree.t('product_place.empty_info') if context.google_place.blank?

google_place = context.google_place.first
place.assign_attributes(
name: google_place.name,
vicinity: google_place.vicinity,
lat: google_place.lat,
lon: google_place.lon,
icon: google_place.icon,
rating: google_place.rating,
types: google_place.types
)
place.save!
end

def filter_product_places(type)
if type.present?
parent.product_places.where(type: SpreeCmCommissioner::ProductPlace.types[type])
Expand Down
66 changes: 66 additions & 0 deletions app/controllers/spree/api/v2/platform/product_places_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
module Spree
module Api
module V2
module Platform
class ProductPlacesController < ResourceController
respond_to :json

def index
result = collection_serializer.new(collection).serializable_hash

respond_with(result) do |format|
format.json { render json: result }
end
end

def collection
@collection ||= begin
filter = params[:filter] || {}
name_filter = filter[:name_i_cont]

new_places = SpreeCmCommissioner::GooglePlacesFetcher.call(name: name_filter)

if new_places.success?
place_struct(new_places.google_places)
else
[]
end
end
end

def model_class
SpreeCmCommissioner::ProductPlace
end

def resource_serializer
SpreeCmCommissioner::Api::V2::Platform::ProductPlaceSerializer
end

def collection_serializer
SpreeCmCommissioner::Api::V2::Platform::ProductPlaceSerializer
end

private

def place_struct(places)
places.each_with_index.map do |place|
PlaceWithId.new(
place.reference,
place.reference,
place.name,
place.vicinity,
place.lat,
place.lon,
place.icon,
place.rating,
place.types
)
end
end

PlaceWithId = Struct.new(:id, :reference, :name, :vicinity, :lat, :lon, :icon, :rating, :types)
end
end
end
end
end
39 changes: 39 additions & 0 deletions app/interactors/spree_cm_commissioner/google_place_fetcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module SpreeCmCommissioner
class GooglePlaceFetcher < BaseInteractor
delegate :reference, to: :context

def call
context.google_place = []
json_response = fetch_google_place_by_reference

json = json_response['result']
place = SpreeCmCommissioner::Place.new(
reference: json['reference'],
types: json['types'].blank? ? '' : json['types'][0],
name: json['name'],
vicinity: json['vicinity'],
lat: json['geometry']['location']['lat'],
lon: json['geometry']['location']['lng'],
icon: json['icon'],
rating: json['rating']
)
context.google_place << place
end

private

def google_map_key
@google_map_key ||= ENV.fetch('GOOGLE_MAP_KEY')
end

def fetch_google_place_by_reference
url = URI("#{GOOGLE_MAP_API_URL}/place/details/json?placeid=#{reference}&key=#{google_map_key}")
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true

request = Net::HTTP::Get.new(url)
response = https.request(request)
JSON.parse(response.read_body)
end
end
end
41 changes: 41 additions & 0 deletions app/interactors/spree_cm_commissioner/google_places_fetcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
module SpreeCmCommissioner
class GooglePlacesFetcher < BaseInteractor
delegate :name, to: :context

def call
context.google_places = []
json_response = fetch_google_place_by_name

json_response['results'].each_with_index do |json, _|
place = SpreeCmCommissioner::Place.new(
reference: json['reference'],
types: json['types'].blank? ? '' : json['types'][0],
name: json['name'],
vicinity: json['vicinity'],
lat: json['geometry']['location']['lat'],
lon: json['geometry']['location']['lng'],
icon: json['icon'],
rating: json['rating']
)

context.google_places << place
end
end

private

def google_map_key
@google_map_key ||= ENV.fetch('GOOGLE_MAP_KEY')
end

def fetch_google_place_by_name
url = URI("#{GOOGLE_MAP_API_URL}/place/textsearch/json?query=#{name}&key=#{google_map_key}")
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true

request = Net::HTTP::Get.new(url)
response = https.request(request)
JSON.parse(response.read_body)
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module SpreeCmCommissioner
module Api
module V2
module Platform
class ProductPlaceSerializer < ::Spree::Api::V2::Platform::BaseSerializer
attributes :id, :name, :reference, :lat, :lon, :rating, :types
end
end
end
end
end
60 changes: 49 additions & 11 deletions app/views/spree/admin/nearby_places/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,25 @@
<div class="row">
<div class="col-9 col-md-10">
<div class="form-group">
<input placeholder="search by name" type="text" id="name" name="name" class="form-control" value="<%= @name %>">
<input placeholder="search by name" type="text" id="name" name="name" class="form-control" value="<%= @name %>" data-toggle="dropdown" aria-expanded="false">
<div id="dropdownMenu" class="dropdown-menu col-md-10">
<% if @list_nearby_places.any? %>
<% @list_nearby_places.each do |nearby_place| %>
<li class="dropdown-item d-flex justify-content-between" data-name="<%= nearby_place.place.name %>">
<div class="col-md-4">
<%= nearby_place.place.name %>
</div>
<div class="col-md-3">
<%= nearby_place.place.types %>
</div>
<div class="col-md-3">
<%= nearby_place.distance %>
km
</div>
</li>
<% end %>
<% end %>
</div>
</div>
</div>
<div class="col-3 col-md-2">
Expand All @@ -20,18 +38,38 @@
<% end %>
<script>
document.addEventListener('DOMContentLoaded', function() {
var nameInput = document.getElementById('name');
document.getElementById('search').addEventListener('click', function() {
var nameInput = document.getElementById('name');
var nameValue = nameInput.value;

if (nameValue) {
var newUrl = window.location.origin + window.location.pathname +"?name="+ encodeURIComponent(nameValue)
window.location.href = newUrl;
}
else{
var url = window.location.origin + window.location.pathname
window.location.href = url;
}
});
var newUrl = window.location.origin + window.location.pathname +"?name="+ encodeURIComponent(nameValue)
window.location.href = newUrl;
}
else{
var url = window.location.origin + window.location.pathname
window.location.href = url;
}
});


var dropdownItems = document.querySelectorAll('#dropdownMenu .dropdown-item');
dropdownItems.forEach(function(item) {
item.addEventListener('click', function() {
document.getElementById('name').value = item.getAttribute('data-name');
document.getElementById('dropdownMenu').classList.remove('show');
});
});


var dropdown = document.getElementById('dropdownMenu');
nameInput.addEventListener('input', function(){
if(nameInput.value.length >= 1){
dropdown.classList.remove('show');
}else{
dropdown.classList.add('show');
}
})


});
</script>
21 changes: 12 additions & 9 deletions app/views/spree/admin/product_places/new.html.erb
Original file line number Diff line number Diff line change
@@ -1,33 +1,37 @@
<%= render partial: 'spree/admin/shared/product_tabs', locals: { current: :product_place } %>

<% content_for :page_actions do %>
<%= button_link_to Spree.t(:edit_icons),
admin_vectors_option_values_url,
{ icon: 'spree-icon.svg', id: 'admin-option-values-icons-index' }
%>
<% end %>

<%= form_with model: @product_place, url: admin_product_product_places_path(@product), method: :post do |f| %>
<div data-hook="admin_product_form_product_place" style="display: flex; align-items: flex-start;">
<div style="flex: 1; margin-right: 20px;">
<div class="form-group">
<%= f.label :place_id, Spree.t(:place) %>
<%= f.collection_select :place_id, SpreeCmCommissioner::Place.all, :id, :name,
{ include_blank: true },
{ class: 'select2 fullwidth'} %>
<%= f.field_container :name do %>
<%= f.label :name, Spree.t(:place) %>
<% if can? :modify, SpreeCmCommissioner::Place %>
<%= f.select :name, options_from_collection_for_select([] , :id , :name),
{ include_hidden: true },
class: 'select2autocomplete',
data: {
autocomplete_url_value: 'product_places_api_v2',
autocomplete_return_attr_value: :name,
} %>
<% end %>
<% end %>
<div class="form-group" style="margin-top: 5px;">
<%= link_to "Create New Venue", admin_vendor_nearby_places_url(@product.vendor), class: 'btn-link', style: 'text-decoration: underline; color: #007bff;' %>
</div>
</div>
</div>

<div style="flex: 1; margin-right: 20px;">
<div class="form-group">
<%= f.label :checkinable_distance, Spree.t(:checkinable_distance) %>
<%= f.number_field :checkinable_distance, class: 'form-control', value: @product_place.checkinable_distance || 100 %>
</div>
</div>

<div style="flex: 1;">
<div class="form-group">
<%= f.label :type, Spree.t(:type) %>
Expand All @@ -37,7 +41,6 @@
</div>
</div>
</div>

<div class='form-actions' data-hook='buttons'>
<%= button Spree.t('actions.create'), 'save.svg', 'submit', { class: 'btn-success', data: { disable_with: "#{Spree.t(:saving)}..." } } %>
</div>
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,7 @@
namespace :platform do
resources :homepage_section_relatable_options
resources :seat_number_layouts
resources :product_places
end

namespace :storefront do
Expand Down

0 comments on commit 62a6087

Please sign in to comment.