geo_labels 0.2.0 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/README.md +127 -6
  4. data/Rakefile +6 -5
  5. data/app/assets/javascripts/geo_labels/application.coffee +13 -2
  6. data/app/assets/javascripts/geo_labels/ratings.coffee +80 -0
  7. data/app/assets/stylesheets/geo_labels/application.sass +21 -0
  8. data/app/controllers/concerns/geo_labels/shared_template_and_instance_methods.rb +3 -0
  9. data/app/controllers/geo_labels/application_controller.rb +3 -0
  10. data/app/controllers/geo_labels/contacts_controller.rb +18 -0
  11. data/app/controllers/geo_labels/dashboard_controller.rb +14 -9
  12. data/app/controllers/geo_labels/labels_controller.rb +3 -1
  13. data/app/controllers/geo_labels/ratings_controller.rb +30 -0
  14. data/app/helpers/geo_labels/application_helper.rb +73 -44
  15. data/app/helpers/geo_labels/rating_helper.rb +15 -0
  16. data/app/jobs/geo_labels/application_job.rb +1 -0
  17. data/app/mailers/geo_labels/application_mailer.rb +4 -2
  18. data/app/models/concerns/geo_labels/ratings_support.rb +102 -0
  19. data/app/models/geo_labels/application_record.rb +22 -0
  20. data/app/models/geo_labels/contact.rb +62 -20
  21. data/app/models/geo_labels/contact_label.rb +43 -7
  22. data/app/models/geo_labels/label.rb +13 -0
  23. data/app/views/geo_labels/contacts/_form.html.slim +22 -6
  24. data/app/views/geo_labels/contacts/index.html.slim +19 -7
  25. data/app/views/geo_labels/contacts/show.html.slim +24 -2
  26. data/app/views/geo_labels/dashboard/main.html.slim +27 -5
  27. data/app/views/geo_labels/labels/index.html.slim +10 -6
  28. data/app/views/geo_labels/labels/show.html.slim +15 -1
  29. data/app/views/layouts/geo_labels/application.html.slim +2 -0
  30. data/config/environment.rb +2 -0
  31. data/config/initializers/human_plural.rb +8 -5
  32. data/config/locales/geo_labels.en.yml +8 -0
  33. data/config/locales/geo_labels.es.yml +17 -0
  34. data/config/routes.rb +12 -1
  35. data/db/migrate/20221019150722_create_geo_labels_contacts.rb +5 -3
  36. data/db/migrate/20221020180213_create_geo_labels_labels.rb +2 -0
  37. data/db/migrate/20221020195346_create_geo_labels_contact_labels.rb +4 -2
  38. data/db/migrate/20230602182522_geo_labels_contacts_complex_name_to_name.rb +9 -0
  39. data/db/migrate/20230726154822_add_state_to_geo_labels_contacts.rb +8 -0
  40. data/db/migrate/20230801145902_add_food_rating_to_geo_labels_contacts.rb +7 -0
  41. data/lib/geo_labels/engine.rb +17 -7
  42. data/lib/geo_labels/exporter.rb +24 -10
  43. data/lib/geo_labels/importer.rb +31 -27
  44. data/lib/geo_labels/version.rb +3 -1
  45. data/lib/geo_labels.rb +2 -0
  46. data/lib/tasks/geo_labels_tasks.rake +1 -0
  47. metadata +37 -15
@@ -1,32 +1,41 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GeoLabels
2
4
  module ApplicationHelper
5
+ include RatingHelper
6
+
3
7
  def link_to_labels_tree
4
- #output = "<ul class='ui list'>"
5
- output = tag.ul(class: 'ui list')[0..-6]
6
- add_punctuation = '*'
7
- tree_formatter = Proc.new do |items, lead_space|
8
+ # output = "<ul class='ui list'>"
9
+ output = tag.ul(class: 'ui list labels-tree')[0..-6]
10
+ #add_punctuation = '*'
11
+ tree_formatter = proc do |items, lead_space|
8
12
  items.each do |item|
9
- output += "#{lead_space}#{tag.li link_to(item[:name], label_path(item[:id]))}\n".html_safe
10
- if item[:children].present?
11
- output += "#{lead_space}<ul>".html_safe
12
- tree_formatter.call(item[:children], lead_space + ' ')
13
- output += "#{lead_space}</ul>".html_safe
14
- end
13
+ label_name = link_to(item[:name], label_path(item[:id]))
14
+ edit_link = table_edit_link(item, geo_labels.edit_label_path(item[:id]))
15
+ add_child_link = link_to(tag.i(class: 'plus icon'), geo_labels.new_label_path(parent_id: item[:id]))
16
+ output += "#{lead_space}#{tag.li(label_name + add_child_link + edit_link)}\n".html_safe
17
+ next unless item[:children].present?
18
+
19
+ output += "#{lead_space}<ul>".html_safe
20
+ tree_formatter.call(item[:children], "#{lead_space} ")
21
+ output += "#{lead_space}</ul>".html_safe
15
22
  end
16
23
  end
17
24
  tree_formatter.call(GeoLabels::Exporter.labels_tree_h, '')
18
- #output.gsub!(/^(\s*) /, "\1#{add_punctuation} ") if add_punctuation.present?
25
+ # output.gsub!(/^(\s*) /, "\1#{add_punctuation} ") if add_punctuation.present?
19
26
  output += '</ul>'.html_safe
20
27
  output.html_safe
21
28
  end
29
+
22
30
  # Return active or nil based on the given route spec
23
31
  # %li{ class: active_class('users') # true if controller is users, false otherwise
24
32
  # %li{ class: active_class('pages#about') # true if controller is pages and action is about
25
- #NOTE: Taken from the dunlop-core gem. That is the best maintained version
33
+ # NOTE: Taken from the dunlop-core gem. That is the best maintained version
26
34
  def active_class(*route_specs)
27
35
  options = route_specs.extract_options!
28
- return nil if Array.wrap(options[:except]).any?{|exception| current_route_spec?(exception) }
29
- return 'active' if route_specs.any?{|rs| current_route_spec?(rs, options) }
36
+ return nil if Array.wrap(options[:except]).any?{ |exception| current_route_spec?(exception) }
37
+ return 'active' if route_specs.any?{ |rs| current_route_spec?(rs, options) }
38
+
30
39
  nil
31
40
  end
32
41
 
@@ -36,19 +45,23 @@ module GeoLabels
36
45
  # current_route_spec?('products') #=> true if controller name is products, false otherwise
37
46
  # current_route_spec?('products#show') #=> true if controller_name is products AND action_name is show
38
47
  # current_route_spec?('#show') #=> true if action_name is show, false otherwise
39
- #NOTE: this helper is tested through the active_class helper
40
- #NOTE: Taken from the dunlop-core gem. That is the best maintained version
41
- def current_route_spec?(route_spec, options = {})
48
+ # NOTE: this helper is tested through the active_class helper
49
+ # NOTE: Taken from the dunlop-core gem. That is the best maintained version
50
+ def current_route_spec?(route_spec, _options = {})
42
51
  return route_spec.match([controller_path, action_name].join('#')) if route_spec.is_a?(Regexp)
52
+
43
53
  controller, action = route_spec.split('#')
44
54
  return action == params[:id] if controller_path == 'high_voltage/pages'
55
+
45
56
  actual_controller_parts = controller_path.split('/')
46
- if controller #and controller_path == controller
57
+ if controller # and controller_path == controller
47
58
  tested_controller_parts = controller.split('/')
48
59
  return if tested_controller_parts.size > actual_controller_parts.size
60
+
49
61
  if actual_controller_parts[0...tested_controller_parts.size] == tested_controller_parts
50
62
  # controller spec matches
51
63
  return true unless action
64
+
52
65
  action_name == action
53
66
  end
54
67
  else
@@ -56,7 +69,7 @@ module GeoLabels
56
69
  end
57
70
  end
58
71
 
59
- #NOTE: Taken from the dunlop-core gem. That is the best maintained version
72
+ # NOTE: Taken from the dunlop-core gem. That is the best maintained version
60
73
  def page_title(*args, &blk)
61
74
  extra_content = capture(&blk) if blk.present?
62
75
  if resource_title?(args)
@@ -80,16 +93,13 @@ module GeoLabels
80
93
  title = html_escape(args.first)
81
94
  options = args.last.is_a?(Hash) ? args.last : {}
82
95
  if back_options = options[:back]
83
- url =
84
- case back_options
85
- when Array then polymorphic_path(back_options)
86
- when true then :back
87
- else back_options
88
- end
89
- if url
90
- back_link = link_to content_tag(:i, nil, class: 'left arrow icon'), url, class: 'title-back-link'
91
- title = back_link.safe_concat(title)
92
- end
96
+ url = case back_options
97
+ when Array then polymorphic_path(back_options)
98
+ when true then :back
99
+ else back_options
100
+ end
101
+ back_link = link_to content_tag(:i, nil, class: 'left arrow icon'), url, class: 'title-back-link'
102
+ title = back_link.safe_concat(title)
93
103
  end
94
104
  title
95
105
  end
@@ -97,11 +107,11 @@ module GeoLabels
97
107
  def page_title_for_resource(args)
98
108
  options = args.extract_options!
99
109
  model = args[1].respond_to?(:model_name) ? args[1] : args[1].class
100
- if args.first == :index
101
- title = t('action.index.label', models: model.model_name.human_plural).html_safe
102
- else
103
- title = t("action.#{args.first}.label", model: model.model_name.human).html_safe
104
- end
110
+ title = if args.first == :index
111
+ t('action.index.label', models: model.model_name.human_plural).html_safe
112
+ else
113
+ t("action.#{args.first}.label", model: model.model_name.human).html_safe
114
+ end
105
115
  if back_options = options[:back]
106
116
  url =
107
117
  case back_options
@@ -124,7 +134,7 @@ module GeoLabels
124
134
  "#{from_item} - #{to_item} / #{records.total_count}"
125
135
  end
126
136
 
127
- def at(attribute_name, scope_model=nil)
137
+ def at(attribute_name, scope_model = nil)
128
138
  scope_model ||= @scope_model
129
139
  scope_model.human_attribute_name(attribute_name)
130
140
  end
@@ -133,9 +143,9 @@ module GeoLabels
133
143
  def search_row(options = {}, &blk)
134
144
  classes = Array.wrap(options[:class])
135
145
  classes |= ['search']
136
- classes << 'conditions-present' if @q.conditions.present?
146
+ classes << 'conditions-present' if @q.base.conditions.any?
137
147
  content = capture(&blk)
138
- content_tag(:tr, content, class: classes )
148
+ content_tag(:tr, content, class: classes)
139
149
  end
140
150
 
141
151
  # This helper returns the link for showing a record inside a table
@@ -145,7 +155,11 @@ module GeoLabels
145
155
  else
146
156
  return unless can? :show, record
147
157
  end
148
- link_to(content_tag(:i,nil, class: 'folder open icon'), path || record, class: 'table-link show ui mini basic primary icon button')
158
+ link_to(
159
+ content_tag(:i, nil, class: 'folder open icon'),
160
+ path || record,
161
+ class: 'table-link show ui mini basic primary icon button'
162
+ )
149
163
  end
150
164
 
151
165
  # This helper returns the link for showing a record inside a table
@@ -155,7 +169,11 @@ module GeoLabels
155
169
  else
156
170
  return unless can? :download, record
157
171
  end
158
- link_to(content_tag(:i,nil, class: 'download icon'), path || [:download, record], class: 'table-link download ui mini violet icon button')
172
+ link_to(
173
+ content_tag(:i, nil, class: 'download icon'),
174
+ path || [:download, record],
175
+ class: 'table-link download ui mini violet icon button'
176
+ )
159
177
  end
160
178
 
161
179
  # This helper returns the link for editing a record inside a table
@@ -180,16 +198,27 @@ module GeoLabels
180
198
  record_name ||= record.name if record.respond_to?(:name)
181
199
  record_name ||= record.title if record.respond_to?(:title)
182
200
  confirm_text << " #{record_name}" if record_name.present?
183
- confirm_text << "?"
184
- link_to(content_tag(:i, nil, class: 'trash icon'), path || record, method: :delete, data: { confirm: confirm_text }, class: 'table-link destroy ui mini negative icon button')
201
+ confirm_text << '?'
202
+ link_to(
203
+ content_tag(:i, nil, class: 'trash icon'),
204
+ path || record,
205
+ method: :delete,
206
+ data: { confirm: confirm_text },
207
+ class: 'table-link destroy ui mini negative icon button'
208
+ )
209
+ end
210
+
211
+ def address_tag(address, title: nil)
212
+ tag.span('&nbsp;'.html_safe * 10) +
213
+ tag.div(address, class: 'ui basic tag label', title: title)
185
214
  end
186
215
 
187
216
  # https://coderwall.com/p/7gqmog/display-flash-messages-with-semantic-ui-in-rails
188
217
  def flash_class(level)
189
218
  case level.to_sym
190
- when :success then "ui positive message"
191
- when :error, :alert then "ui negative message"
192
- when :notice then "ui info message"
219
+ when :success then 'ui positive message'
220
+ when :error, :alert then 'ui negative message'
221
+ when :notice then 'ui info message'
193
222
  else "ui #{level} message"
194
223
  end
195
224
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ module GeoLabels
3
+ module RatingHelper
4
+ def show_rating_for(record, helper_topic)
5
+ tag.div class: 'rating-container item', data: {
6
+ topic: helper_topic,
7
+ record: record.class.name,
8
+ record_id: record.id,
9
+ rating: record.rating[helper_topic].value
10
+ } do
11
+ tag.span 'Hi theree'
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module GeoLabels
2
3
  class ApplicationJob < ActiveJob::Base
3
4
  end
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
1
2
  module GeoLabels
2
3
  class ApplicationMailer < ActionMailer::Base
3
- default from: "from@example.com"
4
- layout "mailer"
4
+
5
+ default from: 'from@example.com'
6
+ layout 'mailer'
5
7
  end
6
8
  end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+ require 'active_support/concern'
3
+
4
+ module GeoLabels
5
+ # Represent the rating of one topic as an object
6
+ class RatingObject
7
+
8
+ attr_reader :record, :topic
9
+
10
+ def initialize(record, topic)
11
+ @record, @topic = record, topic
12
+ end
13
+
14
+ def value
15
+ # expect active_record column attribute
16
+ record.public_send("#{topic}_rating")
17
+ end
18
+
19
+ def value=(value)
20
+ record.public_send("#{topic}_rating=", value)
21
+ end
22
+ end
23
+
24
+ # Represent the possible rating objects for the topics as a list
25
+ class RatingList
26
+
27
+ class OutOfBoundIndex < StandardError
28
+ end
29
+
30
+ include Enumerable
31
+ attr_reader :record
32
+
33
+ def initialize(record)
34
+ @record = record
35
+ end
36
+
37
+ def each(&block)
38
+ if block_given?
39
+ object_list.each(&block)
40
+ else
41
+ to_enum(:each)
42
+ end
43
+ end
44
+
45
+ def object_list
46
+ record.class.rating_topics.map { |topic| RatingObject.new(record, topic) }
47
+ end
48
+
49
+ def [](index)
50
+ case index
51
+ when String then RatingObject.new(record, index.to_sym)
52
+ when Symbol then RatingObject.new(record, index)
53
+ when Integer
54
+ topic = record.class.rating_topics[index]
55
+ raise OutOfBoundIndex, 'Rating list index provided is out of bounds' unless topic
56
+
57
+ RatingObject.new(record, topic)
58
+ else
59
+ raise OutOfBoundIndex, 'Rating list index provided is out of bounds'
60
+ end
61
+ end
62
+
63
+ # allow short hand assign
64
+ # record.rating[:food] = 3
65
+ def []=(topic, value)
66
+ self[topic].value = value
67
+ end
68
+ end
69
+
70
+ # Extend an ActiveRecord model with rating support
71
+ module RatingsSupport
72
+ extend ActiveSupport::Concern
73
+
74
+ included do
75
+ @rating_topics ||= {}
76
+ end
77
+
78
+ def rating(topic = nil)
79
+ if topic.present?
80
+ RatingObject.new(self, topic)
81
+ else
82
+ RatingList.new(self)
83
+ end
84
+ end
85
+
86
+ # nodoc
87
+ module ClassMethods
88
+ def has_rating_with_topic(topic, topic_options = {})
89
+ @rating_topics[topic] = topic_options
90
+ # TODO: check existance of integer field of "topic_#{rating}, default: 0"
91
+ end
92
+
93
+ def has_rating_with_topic?(topic)
94
+ @rating_topics.has_key?(topic)
95
+ end
96
+
97
+ def rating_topics
98
+ @rating_topics.keys
99
+ end
100
+ end
101
+ end
102
+ end
@@ -1,5 +1,27 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GeoLabels
2
4
  class ApplicationRecord < ActiveRecord::Base
5
+
3
6
  self.abstract_class = true
7
+
8
+ RANSACKABLE_ATTRIBUTES = [].freeze
9
+ RANSACKABLE_ASSOCIATIONS = [].freeze
10
+
11
+ def self.ransackable_attributes(_auth_object = nil)
12
+ if const_defined? :RANSACKABLE_ATTRIBUTES
13
+ const_get :RANSACKABLE_ATTRIBUTES
14
+ else
15
+ RANSACKABLE_ATTRIBUTES
16
+ end
17
+ end
18
+
19
+ def self.ransackable_associations(_auth_object = nil)
20
+ if const_defined? :RANSACKABLE_ASSOCIATIONS
21
+ const_get :RANSACKABLE_ASSOCIATIONS
22
+ else
23
+ RANSACKABLE_ASSOCIATIONS
24
+ end
25
+ end
4
26
  end
5
27
  end
@@ -1,19 +1,44 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GeoLabels
2
4
  class Contact < ApplicationRecord
5
+
3
6
  extend Geocoder::Model::ActiveRecord
7
+ include RatingsSupport
8
+
9
+ RANSACKABLE_ATTRIBUTES = %w[
10
+ city country created_at department description
11
+ id latitude longitude name state street
12
+ subsection updated_at
13
+ ].freeze
14
+ RANSACKABLE_ASSOCIATIONS = %w[contact_labels labels].freeze
15
+
16
+ STATE_OPTIONS = %w[approved recommended rejected].freeze
17
+
4
18
  has_many :contact_labels, dependent: :delete_all
5
19
  has_many :labels, through: :contact_labels
6
20
 
7
21
  geocoded_by :address
8
22
 
23
+ has_rating_with_topic :food
24
+
25
+ validates :name, presence: true
26
+ validates :state, inclusion: {in: STATE_OPTIONS}
27
+
9
28
  after_validation :geocode_if_necessary
10
29
 
11
- def self.for_label_ids(label_ids, predication: 'and')
12
- #predication = %w[and or].include?(predication) ? predication : 'and' #whitlelisting
30
+ def self.for_label_ids(label_ids, predication: 'and', states: ['approved'])
31
+ # predication = %w[and or].include?(predication) ? predication : 'and' #whitlelisting
13
32
  if predication == 'or'
14
- joins(:labels).merge(Label.descendants_for_ids(label_ids)).distinct
33
+ labels = Label.find(label_ids)
34
+ labels_scope = labels.shift.self_and_descendants
35
+ labels.each do |label|
36
+ labels_scope = labels_scope.or(label.self_and_descendants)
37
+ end
38
+ # joins(:labels).merge(Label.descendants_for_ids(label_ids)).distinct
39
+ joins(:labels).merge(labels_scope).where(state: states).reorder('').distinct
15
40
  else
16
- where id: ContactLabel.contact_ids_for_label_ids(label_ids)
41
+ where(id: ContactLabel.contact_ids_for_label_ids(label_ids), state: states)
17
42
  end
18
43
  end
19
44
 
@@ -22,43 +47,60 @@ module GeoLabels
22
47
  end
23
48
 
24
49
  def address
25
- [street, city, state, country].map(&:presence).compact.join(', ')
50
+ [street, city, department, country].map(&:presence).compact.join(', ')
26
51
  end
27
52
 
28
53
  def address_changed?
29
- street_changed? or city_changed? or state_changed? or country_changed?
54
+ street_changed? or city_changed? or department_changed? or country_changed?
30
55
  end
31
56
 
32
- def full_name
33
- [first_name, middle_name, last_name].map(&:presence).compact.join(' ')
57
+ STATE_OPTIONS.each do |option|
58
+ define_method "#{option}?" do
59
+ state == option
60
+ end
34
61
  end
35
62
 
36
- ransacker :full_name do |parent|
37
- Arel::Nodes::InfixOperation.new('||', Arel::Nodes::InfixOperation.new('||', parent.table[:first_name], parent.table[:middle_name]), parent.table[:last_name])
38
- end
63
+ #def full_name
64
+ # [first_name, middle_name, last_name].map(&:presence).compact.join(' ')
65
+ #end
66
+
67
+ #ransacker :full_name do |parent|
68
+ # Arel::Nodes::InfixOperation.new(
69
+ # '||',
70
+ # Arel::Nodes::InfixOperation.new('||', parent.table[:first_name], parent.table[:middle_name]),
71
+ # parent.table[:last_name]
72
+ # )
73
+ #end
39
74
 
40
75
  def map_title
41
- full_name
76
+ name
42
77
  end
43
78
 
44
79
  def map_attributes
45
80
  return {} unless geocoded?
81
+
46
82
  {
47
- lat: latitude,
48
- lng: longitude,
83
+ lat: latitude.try(:to_f),
84
+ lng: longitude.try(:to_f),
49
85
  title: map_title
50
86
  }
51
87
  end
52
88
 
53
89
  def geocode_if_necessary
54
90
  return if new_record? and latitude.present? and longitude.present?
55
- if address_changed?
56
- self.latitude = nil
57
- self.longitude = nil
58
- # Geocode addresses with at least 2 commas
59
- geocode if address.count(',') > 1
60
- end
91
+ return unless address_changed?
92
+
93
+ self.latitude = nil
94
+ self.longitude = nil
95
+ # Geocode addresses with at least 2 commas
96
+ geocode if address.count(',') > 1
61
97
  end
62
98
 
99
+ def lat_lng=(value)
100
+ case value
101
+ when String then self.latitude, self.longitude = value.split(/,\s?/)
102
+ when Array then self.latitude, self.longitude = value
103
+ end
104
+ end
63
105
  end
64
106
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
1
2
  module GeoLabels
2
3
  class ContactLabel < ApplicationRecord
4
+
3
5
  belongs_to :contact
4
6
  belongs_to :label
5
7
 
@@ -9,32 +11,66 @@ module GeoLabels
9
11
  # Sadly this stragegy fails when a contact has to labels assigned (Indonesian AND Vietnamese food), and
10
12
  # the query is for all food categories (parent).
11
13
  # Works for simple and concice situations. Not for multi label definitions
12
- def self.contact_ids_for_label_ids2(label_ids)
14
+ def self.contact_ids_for_label_ids_old1(label_ids)
13
15
  label_ids = label_ids.to_s.split(',') unless label_ids.is_a?(Array)
14
- scope = joins(:label).merge(Label.descendants_for_ids(label_ids)).select(:contact_id).group(:contact_id)
15
- scope = scope.having(arel_table[:contact_id].count.eq label_ids.size).reorder('')
16
- scope
16
+ label_ids_with_descendants = Label.descendants_for_ids(label_ids)
17
+ # scope = joins(:label).merge(label_ids_with_descendants).select(:contact_id).group(:contact_id)
18
+ joins(:label)
19
+ .merge(Label.where(id: label_ids_with_descendants.pluck(:id)))
20
+ .select(:contact_id)
21
+ .group(:contact_id)
22
+ .having(arel_table[:contact_id].count.eq label_ids.size)
23
+ .reorder('')
17
24
  end
18
25
 
19
26
  ### The strategy:
20
27
  # Create a subquery for each label collection possible situation
21
28
  # Then join all these subqueries on the contact_id to remain only
22
29
  # with the records having the contact_id in each of these subqueries
23
- def self.contact_ids_for_label_ids(label_ids)
30
+ def self.contact_ids_for_label_ids_old2(label_ids)
24
31
  label_ids = label_ids.to_s.split(',') unless label_ids.is_a?(Array)
32
+ return none unless label_ids.any?
25
33
 
26
34
  records = Label.find(label_ids)
27
35
 
28
- scope = joins(%|INNER JOIN "geo_labels_labels" ON "geo_labels_labels"."id" = "geo_labels_contact_labels"."label_id"|).merge(records.shift.self_and_descendants).reorder('').select(:contact_id)
36
+ scope = joins(%|INNER JOIN "geo_labels_labels" ON "geo_labels_labels"."id" = "geo_labels_contact_labels"."label_id"|)
37
+ .merge(records.shift.self_and_descendants)
38
+ .reorder('')
39
+ .select(:contact_id)
29
40
  records.each do |record|
30
41
  record_scope = joins(:label).merge(record.self_and_descendants).reorder('').select(:contact_id)
31
42
  join_table_name = "labeltable#{record.id}"
32
43
  join_table_contact_id = Arel::Nodes::SqlLiteral.new("#{join_table_name}.contact_id")
33
44
  query_str = %|INNER JOIN (#{record_scope.to_sql}) #{join_table_name} ON #{arel_table[:contact_id].eq(join_table_contact_id).to_sql}|
34
45
  scope = scope.joins(query_str)
35
- #descendants_scope = descendants_scope.or(record.self_and_descendants)
46
+ # descendants_scope = descendants_scope.or(record.self_and_descendants)
36
47
  end
37
48
  scope
38
49
  end
50
+
51
+ ### The strategy predication AND:
52
+ # Create a query for all labels with descendents getting the contact ids to ruby
53
+ # Then find the overlapping ids [] & []
54
+ # Then find the resulting contacts
55
+ def self.contact_ids_for_label_ids(label_ids)
56
+ label_ids = label_ids.to_s.split(',') unless label_ids.is_a?(Array)
57
+ return [] unless label_ids.any?
58
+
59
+ found_contact_ids = nil
60
+ Label.find(label_ids).each do |label|
61
+ label_contact_ids = ContactLabel.where(label_id: label.self_and_descendants.pluck(:id)).pluck(:contact_id)
62
+ next found_contact_ids = label_contact_ids unless found_contact_ids
63
+
64
+ found_contact_ids = found_contact_ids.intersection(label_contact_ids)
65
+ #if found_contact_ids
66
+ # # merge the overlapping ids
67
+ # found_contact_ids = found_contact_ids.intersection(label_contact_ids)
68
+ #else
69
+ # # initialize with found results
70
+ # found_contact_ids = label_contact_ids
71
+ #end
72
+ end
73
+ found_contact_ids
74
+ end
39
75
  end
40
76
  end
@@ -1,8 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module GeoLabels
2
4
  class Label < ApplicationRecord
5
+ RANSACKABLE_ATTRIBUTES = %w[name].freeze
6
+
3
7
  has_many :contact_labels, dependent: :delete_all
8
+ has_many :contacts, through: :contact_labels
9
+
4
10
  acts_as_nested_set
5
11
 
12
+ validates :name, presence: true
13
+
6
14
  def self.descendants_for_ids(ids)
7
15
  records = find(ids)
8
16
  descendants_scope = records.shift.self_and_descendants
@@ -11,5 +19,10 @@ module GeoLabels
11
19
  end
12
20
  descendants_scope
13
21
  end
22
+
23
+ def associated_contacts
24
+ ids = ContactLabel.joins(:label).merge(self_and_descendants).pluck(:contact_id).uniq
25
+ Contact.order(:name).find(ids)
26
+ end
14
27
  end
15
28
  end
@@ -3,10 +3,7 @@
3
3
  .ui.top.attached.segment
4
4
  .field
5
5
  label= t 'form.name'
6
- .three.fields
7
- .field= f.text_field :first_name, placeholder: at(:first_name)
8
- .field= f.text_field :middle_name, placeholder: at(:middle_name)
9
- .field= f.text_field :last_name, placeholder: at(:last_name)
6
+ .field= f.text_field :name, placeholder: at(:name)
10
7
  .field
11
8
  label= t 'form.address'
12
9
  .two.fields
@@ -17,8 +14,8 @@
17
14
  label= at :city
18
15
  = f.text_field :city, placeholder: at(:city)
19
16
  .field
20
- label= at :state
21
- = f.text_field :state, placeholder: at(:state)
17
+ label= at :department
18
+ = f.text_field :department, placeholder: at(:department)
22
19
  .field
23
20
  label= at :country
24
21
  .ui.fluid.search.selection.dropdown
@@ -30,6 +27,25 @@
30
27
  .item data-value=country.iso_short_name
31
28
  i.flag class=country.alpha2.downcase
32
29
  = country.iso_short_name
30
+ .field
31
+ label= at :state
32
+ .ui.fluid.selection.dropdown
33
+ = f.hidden_field :state
34
+ i.dropdown.icon
35
+ .default.text= at(:state)
36
+ .menu
37
+ - GeoLabels::Contact::STATE_OPTIONS.each do |option|
38
+ .item data-value=option = option
39
+ .field
40
+ label= at :description
41
+ = f.text_field :description
42
+ .field
43
+ .ui.compact.accordion
44
+ .title
45
+ i.dropdown.icon
46
+ => t('geo_labels.manual_lat_lng.title')
47
+ i.info.icon data-html=t('geo_labels.manual_lat_lng.explanation', default: 'missing translation geo_labels.manual_lat_lng.explanation') data-variation='wide'
48
+ .content= f.text_field :lat_lng
33
49
  .ui.attached.segment
34
50
  .ui.fluid.multiple.search.selection.dropdown
35
51
  = f.hidden_field :label_ids, value: @record.label_ids.join(',')