From 03c4358848cf08de6c76179df6a2c803b2549775 Mon Sep 17 00:00:00 2001 From: nangdypanhar <181545922+nangdypanhar@users.noreply.github.com> Date: Tue, 26 Nov 2024 18:50:49 +0700 Subject: [PATCH] close #2045 improve adding venue to product admin --- .../spree_cm_commissioner/routes.js | 6 +- .../spree/admin/product_places_controller.rb | 24 +++++++ .../v2/platform/product_places_controller.rb | 66 +++++++++++++++++++ .../google_place_fetcher.rb | 39 +++++++++++ .../google_places_fetcher.rb | 41 ++++++++++++ .../v2/platform/product_place_serializer.rb | 11 ++++ .../spree/admin/nearby_places/new.html.erb | 2 +- .../spree/admin/product_places/new.html.erb | 21 +++--- config/routes.rb | 1 + 9 files changed, 200 insertions(+), 11 deletions(-) create mode 100644 app/controllers/spree/api/v2/platform/product_places_controller.rb create mode 100644 app/interactors/spree_cm_commissioner/google_place_fetcher.rb create mode 100644 app/interactors/spree_cm_commissioner/google_places_fetcher.rb create mode 100644 app/serializers/spree_cm_commissioner/api/v2/platform/product_place_serializer.rb diff --git a/app/assets/javascripts/spree_cm_commissioner/routes.js b/app/assets/javascripts/spree_cm_commissioner/routes.js index 343d92018..0a149eea8 100644 --- a/app/assets/javascripts/spree_cm_commissioner/routes.js +++ b/app/assets/javascripts/spree_cm_commissioner/routes.js @@ -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" -); \ No newline at end of file +); + +Spree.routes.product_places_api_v2 = Spree.pathFor( + "/api/v2/platform/product_places" +); diff --git a/app/controllers/spree/admin/product_places_controller.rb b/app/controllers/spree/admin/product_places_controller.rb index f6350b249..4ca771de6 100644 --- a/app/controllers/spree/admin/product_places_controller.rb +++ b/app/controllers/spree/admin/product_places_controller.rb @@ -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') @@ -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]) diff --git a/app/controllers/spree/api/v2/platform/product_places_controller.rb b/app/controllers/spree/api/v2/platform/product_places_controller.rb new file mode 100644 index 000000000..f2c95b4da --- /dev/null +++ b/app/controllers/spree/api/v2/platform/product_places_controller.rb @@ -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 diff --git a/app/interactors/spree_cm_commissioner/google_place_fetcher.rb b/app/interactors/spree_cm_commissioner/google_place_fetcher.rb new file mode 100644 index 000000000..076f5ee3a --- /dev/null +++ b/app/interactors/spree_cm_commissioner/google_place_fetcher.rb @@ -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 diff --git a/app/interactors/spree_cm_commissioner/google_places_fetcher.rb b/app/interactors/spree_cm_commissioner/google_places_fetcher.rb new file mode 100644 index 000000000..5c937da38 --- /dev/null +++ b/app/interactors/spree_cm_commissioner/google_places_fetcher.rb @@ -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 diff --git a/app/serializers/spree_cm_commissioner/api/v2/platform/product_place_serializer.rb b/app/serializers/spree_cm_commissioner/api/v2/platform/product_place_serializer.rb new file mode 100644 index 000000000..6a7734e77 --- /dev/null +++ b/app/serializers/spree_cm_commissioner/api/v2/platform/product_place_serializer.rb @@ -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 diff --git a/app/views/spree/admin/nearby_places/new.html.erb b/app/views/spree/admin/nearby_places/new.html.erb index f02722941..ff3c28304 100644 --- a/app/views/spree/admin/nearby_places/new.html.erb +++ b/app/views/spree/admin/nearby_places/new.html.erb @@ -34,4 +34,4 @@ } }); }); - + \ No newline at end of file diff --git a/app/views/spree/admin/product_places/new.html.erb b/app/views/spree/admin/product_places/new.html.erb index ce12749da..042f1e23b 100644 --- a/app/views/spree/admin/product_places/new.html.erb +++ b/app/views/spree/admin/product_places/new.html.erb @@ -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| %>
- <%= 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 %>
<%= link_to "Create New Venue", admin_vendor_nearby_places_url(@product.vendor), class: 'btn-link', style: 'text-decoration: underline; color: #007bff;' %>
-
<%= f.label :checkinable_distance, Spree.t(:checkinable_distance) %> <%= f.number_field :checkinable_distance, class: 'form-control', value: @product_place.checkinable_distance || 100 %>
-
<%= f.label :type, Spree.t(:type) %> @@ -37,7 +41,6 @@
-
<%= button Spree.t('actions.create'), 'save.svg', 'submit', { class: 'btn-success', data: { disable_with: "#{Spree.t(:saving)}..." } } %>
diff --git a/config/routes.rb b/config/routes.rb index f5f0ae94a..6792b2aab 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -367,6 +367,7 @@ namespace :platform do resources :homepage_section_relatable_options resources :seat_number_layouts + resources :product_places end namespace :storefront do