Skip to content

Commit

Permalink
close #2045 improve adding venue to product admin
Browse files Browse the repository at this point in the history
  • Loading branch information
nangdypanhar committed Nov 28, 2024
1 parent d5fea78 commit 7c74577
Show file tree
Hide file tree
Showing 9 changed files with 164 additions and 90 deletions.
4 changes: 3 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,6 @@ 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.places_api_v2 = Spree.pathFor("/api/v2/platform/places");
113 changes: 37 additions & 76 deletions app/controllers/spree/admin/product_places_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,101 +3,62 @@ module Admin
class ProductPlacesController < Spree::Admin::ResourceController
belongs_to 'spree/product', find_by: :slug

before_action :load_product
before_action :load_places
before_action :load_product_places, only: %i[edit update new]
before_action :load_place_to_product_place, only: [:create]

def index
@product_places = filter_product_places(params[:type])
end
create.fails :set_flash_error_message

def new
@product_place = SpreeCmCommissioner::ProductPlace.new(product_id: @product.id, checkinable_distance: 100)
def collection
@collection ||= if params[:type].present?
parent.product_places.where(type: params[:type])
else
parent.product_places
end
end

def create
@product_place = SpreeCmCommissioner::ProductPlace.new(permitted_resource_params)
@product_place.product = parent

if @product_place.place_id.blank?
flash[:error] = I18n.t('product_place.place_required')
render :new, status: :unprocessable_entity
return
end

begin
if @product_place.save
flash[:success] = I18n.t('product_place.created_successfully')
redirect_to collection_url
else
flash[:error] = @product_place.errors.full_messages.join(', ')
render :new
end
rescue ActiveRecord::RecordNotUnique
flash[:error] = I18n.t('product_place.already_exist')
render :new, status: :unprocessable_entity
end
# override
def collection_url
admin_product_product_places_path(parent)
end

def destroy
@product_place = SpreeCmCommissioner::ProductPlace.find(params[:id])

if @product_place.destroy
flash[:success] = Spree.t('product_place.deleted_successfully')
else
flash[:error] = @product_place.errors.full_messages.join(', ')
end

redirect_to collection_url
# override
def model_class
SpreeCmCommissioner::ProductPlace
end

def update_positions
params[:positions].each do |id, index|
product_place = SpreeCmCommissioner::ProductPlace.find(id)
product_place.update(position: index)
end
# override
def object_name
'product_place'
end

private

def filter_product_places(type)
if type.present?
parent.product_places.where(type: SpreeCmCommissioner::ProductPlace.types[type])
else
parent.product_places
end
# override
def permitted_resource_params
@permitted_resource_params ||= params[:spree_cm_commissioner_product_place].permit(:place_id, :checkinable_distance, :type, :base_64_content)
end

def load_product
@product ||= Spree::Product.find_by(slug: params[:product_id])
end
private

def load_places
@places = parent.vendor.places
end
def load_place_to_product_place
place_base_64_content = permitted_resource_params.delete(:base_64_content)

def load_product_places
@product_places = parent.product_places || []
end
if place_base_64_content.blank?
flash[:error] = I18n.t('product_place.place_required')
render :new, status: :unprocessable_entity
return
end

# override
def load_resource
@object = parent.product_places
end
place_json = Base64.strict_decode64(place_base_64_content)
place_hash = JSON.parse(place_json)

# override
def collection_url
admin_product_product_places_path(parent)
end
@place = SpreeCmCommissioner::Place.where(reference: place_hash['reference']).first_or_initialize do |place|
place.assign_attributes(place_hash)
end

# override
def model_class
SpreeCmCommissioner::ProductPlace
@place.save! if @place.changed?
@product_place.place = @place
end

# override
def permitted_resource_params
params.require(:spree_cm_commissioner_product_place).permit(:place_id, :checkinable_distance, :type)
def set_flash_error_message
flash[:error] = @product_place.errors.full_messages.join(', ')
end
end
end
Expand Down
56 changes: 56 additions & 0 deletions app/controllers/spree/api/v2/platform/places_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
module Spree
module Api
module V2
module Platform
class PlacesController < 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 ||= fetch_places
end

def model_class
SpreeCmCommissioner::Place
end

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

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

private

def fetch_places
filter = params[:filter] || {}
name_filter = filter[:name_i_cont]

new_places = SpreeCmCommissioner::GooglePlacesFetcher.call(name: name_filter)
new_places.success? ? struct_places_with_id(new_places.google_places) : []
end

def struct_places_with_id(places)
places.map do |place|
place_struct = Struct.new(:id, :name, :base_64_content)
place_struct.new(
id: place.reference,
name: place.name,
base_64_content: Base64.strict_encode64(place.to_json)
)
end
end
end
end
end
end
end
37 changes: 37 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,37 @@
module SpreeCmCommissioner
class GooglePlacesFetcher < BaseInteractor
delegate :name, to: :context

def call
json_response = fetch_google_place_by_name

context.google_places = json_response['results'].map do |json|
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']
)
end
end

private

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

def fetch_google_place_by_name
queries = {
query: name,
key: google_map_key
}
response = Faraday.get("#{GOOGLE_MAP_API_URL}/place/textsearch/json", queries)
JSON.parse(response.body)
end
end
end
6 changes: 4 additions & 2 deletions app/models/spree_cm_commissioner/product_place.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ module SpreeCmCommissioner
class ProductPlace < Base
self.inheritance_column = '_type_disabled'

belongs_to :product, class_name: 'Spree::Product', optional: true
belongs_to :place, class_name: 'SpreeCmCommissioner::Place', optional: true
belongs_to :product, class_name: 'Spree::Product', optional: false
belongs_to :place, class_name: 'SpreeCmCommissioner::Place', optional: false

validates :place_id, uniqueness: { scope: :product_id }

enum type: { venue: 0, nearby_place: 1 }
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module SpreeCmCommissioner
module Api
module V2
module Platform
class PlaceSerializer < ::Spree::Api::V2::Platform::BaseSerializer
attributes :name, :base_64_content
end
end
end
end
end
22 changes: 13 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,38 @@
<%= 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 :base_64_content do %>
<%= f.label :base_64_content, Spree.t(:place) %>
<% if can? :modify, SpreeCmCommissioner::Place %>
<%= f.select :base_64_content, [],
{ include_hidden: true },
class: 'select2autocomplete',
data: {
autocomplete_url_value: 'places_api_v2',
autocomplete_return_attr_value: :name,
autocomplete_custom_return_id_value: :base_64_content
} %>
<% 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 +42,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 :places
end

namespace :storefront do
Expand Down
4 changes: 2 additions & 2 deletions spec/models/spree_cm_commissioner/product_place_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

RSpec.describe SpreeCmCommissioner::ProductPlace, type: :model do
describe 'associations' do
it { should belong_to(:product).class_name('Spree::Product').optional }
it { should belong_to(:place).class_name('SpreeCmCommissioner::Place').optional }
it { should belong_to(:product).class_name('Spree::Product') }
it { should belong_to(:place).class_name('SpreeCmCommissioner::Place') }
end
end

0 comments on commit 7c74577

Please sign in to comment.