geo_labels 0.2.0 → 0.3.2

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.
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(',')