pg_rails 7.1.4 → 7.1.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97005ad0cfff2f7088c2dbfe667e1fc8339b8873c3935580d374069fdda0c463
4
- data.tar.gz: 938afdb82665e744d1c352e5879523329064bb5cadc7c9becdd88bb6aa85505e
3
+ metadata.gz: e9ea11adc017949414ed89c42127ce6867418dcd461219fb56289a2832a938a6
4
+ data.tar.gz: 30ab308185ff7e68acebf1611161f55fa5d783bc95536db27a2026091637d69a
5
5
  SHA512:
6
- metadata.gz: ce4389f063db55a1761fa50c4335a5b7c98825222b7c541accd5f3e8b4e65c13f9a7ebe3a65787b7c90219f90f11370136faf1efb98f2b2996d9047fc8707e86
7
- data.tar.gz: 6a510173f026e874693cc471f9eb251b733164f3cfc1fd72f105b9b556e7cf918962340584e1479d47d130ba723d0c5642a97f3b8097f417c84d7bf495a53c97
6
+ metadata.gz: 61685a1f498123e153ae0a635805566b69dbf177ad079da95e53221b81c05af957cdc9d8d0de63004d48d74801cf493a7062f303c227f9cdffa08a5834442941
7
+ data.tar.gz: 6c80c211396d351891a1c4071672bae515a629d933a128cd3e9f3b5fae4d948e624b9577645cee199bd10e4d6aa55b1aafa1278e4011dc7d1e7a87a60efede3e
@@ -80,6 +80,19 @@ input[type=datetime-local], input[type=datetime] {
80
80
  // max-width: 17em;
81
81
  }
82
82
 
83
+ .filter label {
84
+ font-size: 0.8em;
85
+ font-weight: bold;
86
+ max-width: 5em;
87
+ text-align: right;
88
+ line-height: 1.2em;
89
+ vertical-align: middle;
90
+ opacity: 0.5;
91
+ min-width: 1em!important;
92
+ padding-left: 0.5em;
93
+ width: min-content;
94
+ }
95
+
83
96
  // Modal
84
97
  .modal-content {
85
98
  box-shadow: 15px 15px 9px 0px rgba(0, 0, 0, 0.6);
@@ -53,7 +53,7 @@ module Admin
53
53
  end
54
54
 
55
55
  def atributos_para_buscar
56
- %i[email_cont nombre_cont apellido_cont developer]
56
+ %i[email nombre apellido developer]
57
57
  end
58
58
 
59
59
  def atributos_para_listar
@@ -20,15 +20,10 @@ module PgEngine
20
20
  def index
21
21
  @collection = filtros_y_policy atributos_para_buscar
22
22
 
23
- shared_context = Ransack::Context.for(clase_modelo)
24
-
23
+ shared_context = Ransack::Adapters::ActiveRecord::Context.new(@collection)
25
24
  @q = @clase_modelo.ransack(params[:q], context: shared_context)
26
- # @collection = @q.result(distinct: true)
27
- @collection = @collection.joins(shared_context.join_sources)
28
- vis = Ransack::Visitor.new.accept(@q.base)
29
- @collection = @collection.where(vis)
25
+ @collection = shared_context.evaluate(@q)
30
26
 
31
- @collection = sort_collection(@collection)
32
27
  pg_respond_index
33
28
  end
34
29
 
@@ -159,7 +154,9 @@ module PgEngine
159
154
  def pg_respond_show(object = nil)
160
155
  object ||= instancia_modelo
161
156
  if params[:modal].present?
162
- render turbo_stream: turbo_stream.append_all('body', partial: 'pg_layout/modal_show', locals: { object: })
157
+ render turbo_stream: turbo_stream.append_all(
158
+ 'body', partial: 'pg_layout/modal_show', locals: { object: }
159
+ )
163
160
  else
164
161
  respond_to do |format|
165
162
  format.json { render json: object }
@@ -303,44 +300,5 @@ module PgEngine
303
300
  self, clase_modelo, []
304
301
  ).filtrar(policy_scope(clase_modelo))
305
302
  end
306
-
307
- def do_sort(scope, field, direction)
308
- # TODO: restringir ciertos campos?
309
- unless scope.model.column_names.include?(field.to_s) ||
310
- scope.model.respond_to?("order_by_#{field}")
311
- pg_warn("No existe el campo \"#{field}\"")
312
- return scope
313
- end
314
- unless direction.to_sym.in? %i[asc desc]
315
- pg_warn("Direction not valid: \"#{direction}\"")
316
- return scope
317
- end
318
- scope = if scope.model.respond_to? "order_by_#{field}"
319
- scope.send "order_by_#{field}", direction
320
- else
321
- sql = scope.model.arel_table[field.to_sym].send(direction).to_sql + ' nulls last'
322
- scope.order(sql)
323
- end
324
- instance_variable_set(:@field, field)
325
- instance_variable_set(:@direction, direction)
326
- scope
327
- rescue ArgumentError => e
328
- pg_warn(e)
329
- scope
330
- end
331
-
332
- def sort_collection(scope, options = {})
333
- if params[:order_by].present?
334
- field = params[:order_by]
335
- direction = params[:order_direction]
336
- do_sort(scope, field, direction)
337
- elsif options[:default].present?
338
- field = options[:default].first[0]
339
- direction = options[:default].first[1]
340
- do_sort(scope, field, direction)
341
- else
342
- do_sort(scope, 'id', 'desc')
343
- end
344
- end
345
303
  end
346
304
  end
@@ -91,7 +91,7 @@ module PgEngine
91
91
  end
92
92
 
93
93
  if Current.user.present?
94
- @notifications = Current.user.notifications.order(id: :desc)
94
+ @notifications = Current.user.notifications.order(id: :desc).includes(:event)
95
95
  .where(type: 'SimpleUserNotifier::Notification')
96
96
  unseen = @notifications.unseen.any?
97
97
  # FIXME: testear y fixear, buscar el primero que esté present
@@ -2,43 +2,42 @@
2
2
 
3
3
  module PgEngine
4
4
  module IndexHelper
5
- def encabezado(campo, options = {})
5
+ def encabezado(input, options = {})
6
+ if input.is_a? Array
7
+ campo = input.first
8
+ sort_field = input.last
9
+ else
10
+ campo = sort_field = input
11
+ end
12
+
6
13
  campo = campo.to_s.sub(/_f\z/, '')
7
14
  campo = campo.to_s.sub(/_text\z/, '')
8
- clase = options[:clase] || @clase_modelo
9
- if options[:ordenable]
10
- field = controller.instance_variable_get(:@field)
11
- direction = controller.instance_variable_get(:@direction)
12
- uri = URI.parse(request.url)
13
- cgi = if uri.query.present?
14
- CGI.parse(uri.query)
15
- else
16
- {}
17
- end
18
- cgi['order_by'] = campo
19
- cgi['order_direction'] =
20
- if field.to_s == campo.to_s && direction.to_s == 'desc'
21
- 'asc'
22
- else
23
- 'desc'
24
- end
15
+ sort_field = sort_field.to_s.sub(/_f\z/, '')
16
+ sort_field = sort_field.to_s.sub(/_text\z/, '')
25
17
 
26
- symbol = if field.to_s == campo.to_s
27
- if direction.to_s == 'asc'
28
- '<i class="bi bi-sort-down-alt" />'
29
- elsif direction.to_s == 'desc'
30
- '<i class="bi bi-sort-up" />'
31
- end
32
- else
33
- ''
34
- end
18
+ clase = options[:clase] || @clase_modelo
19
+ key = [controller_name, action_name, 'listado_header', campo].join('.')
20
+ dflt = :"activerecord.attributes.#{clase.model_name.i18n_key}.#{campo}"
21
+ human_name = clase.human_attribute_name(key, default: dflt)
35
22
 
36
- uri.query = cgi.transform_values { |b| (b.length == 1 ? b.first : b) }.to_query
23
+ if options[:ordenable]
24
+ sort_link(@q, sort_field, human_name, default_order: default_order(campo))
25
+ else
26
+ human_name
27
+ end
28
+ end
37
29
 
38
- link_to(clase.human_attribute_name(campo), uri.to_s) + " #{symbol}".html_safe
30
+ def default_order(campo)
31
+ columna = @clase_modelo.columns.find { _1.name == campo.to_s }
32
+ if columna && columna.type.to_s.include?('date')
33
+ :desc
39
34
  else
40
- clase.human_attribute_name(campo)
35
+ :asc
41
36
  end
37
+ rescue StandardError => e
38
+ pg_err e
39
+
40
+ :asc
42
41
  end
43
42
  end
44
43
  end
@@ -7,13 +7,44 @@ module PgEngine
7
7
  include PostgresHelper
8
8
  attr_accessor :controller
9
9
 
10
- SUFIJOS = %w[desde hasta incluye es_igual_a in cont eq].freeze
10
+ # El orden de los sufijos es importante
11
+ SUFIJOS = %w[
12
+ desde
13
+ hasta
14
+ gteq
15
+ gt
16
+ lteq
17
+ lt
18
+ incluye
19
+ es_igual_a
20
+ in
21
+ cont_all
22
+ cont_any
23
+ not_cont
24
+ cont
25
+ eq
26
+ ].freeze
11
27
 
12
28
  def initialize(controller, clase_modelo, campos)
13
29
  @clase_modelo = clase_modelo
14
- @campos = campos
15
- @controller = controller
16
30
  @filtros = {}
31
+ @campos = campos.map do |campo|
32
+ if extraer_sufijo(campo).blank?
33
+ case tipo(campo)
34
+ when :enumerized, :boolean
35
+ :"#{campo}_eq"
36
+ when :asociacion
37
+ :"#{campo.to_s.sub(/_id$/, '')}_id_eq"
38
+ when :date, :datetime
39
+ [:"#{campo}_gteq", :"#{campo}_lteq"]
40
+ else
41
+ :"#{campo}_cont_all"
42
+ end
43
+ else
44
+ campo
45
+ end
46
+ end.flatten
47
+ @controller = controller
17
48
  @campos.each { |campo| @filtros[campo] = {} }
18
49
  end
19
50
 
@@ -33,96 +64,23 @@ module PgEngine
33
64
  @filtros[campo][:scope_asociacion] = block
34
65
  end
35
66
 
36
- def algun_filtro_presente?
37
- @campos.any? { |campo| parametros_controller[campo].present? }
38
- end
39
-
40
67
  def filtrar(query, parametros = nil)
41
68
  parametros = parametros_controller if parametros.nil?
42
69
 
43
70
  # Filtro soft deleted
44
71
  query = query.kept if query.respond_to?(:kept) && parametros[:archived] != 'true'
45
72
 
46
- @filtros.each_key do |campo|
47
- next if parametros[campo].blank?
48
-
49
- if @filtros[campo.to_sym].present? && @filtros[campo.to_sym][:query].present?
50
- query = @filtros[campo.to_sym][:query].call(query, parametros[campo])
51
- elsif tipo(campo) == :enumerized
52
- columna = @clase_modelo.columns.find { |c| c.name == campo.to_s }
53
- query = if columna.array
54
- query.where("? = any(#{@clase_modelo.table_name}.#{campo})", parametros[campo])
55
- else
56
- query.where("#{@clase_modelo.table_name}.#{campo} = ?", parametros[campo])
57
- end
58
- elsif tipo(campo).in?(%i[integer float decimal])
59
- campo_a_comparar = "#{@clase_modelo.table_name}.#{sin_sufijo(campo)}"
60
- query = query.where("#{campo_a_comparar} #{comparador(campo)} ?", parametros[campo])
61
- elsif tipo(campo) == :asociacion
62
- nombre_campo = sin_sufijo(campo)
63
- suf = extraer_sufijo(campo)
64
- asociacion = obtener_asociacion(nombre_campo)
65
- if asociacion.instance_of?(ActiveRecord::Reflection::HasAndBelongsToManyReflection)
66
- array = parametros[campo].instance_of?(Array) ? parametros[campo].join(',') : parametros[campo]
67
- query = query.joins(nombre_campo.to_sym).group("#{@clase_modelo.table_name}.id")
68
- .having("ARRAY_AGG(#{asociacion.join_table}.#{asociacion.association_foreign_key}) #{comparador_array(suf)} ARRAY[#{array}]::bigint[]")
69
- elsif asociacion.instance_of?(ActiveRecord::Reflection::BelongsToReflection)
70
- query = query.where("#{@clase_modelo.table_name}.#{campo}_id = ?", parametros[campo])
71
- else
72
- raise 'filtro de asociacion no soportado'
73
- end
74
- elsif tipo(campo).in?(%i[string text])
75
- columna = @clase_modelo.columns.find { |c| c.name == campo.to_s }
76
- campo_tabla = "#{@clase_modelo.table_name}.#{campo}"
77
- match_like = "%#{parametros[campo]}%"
78
- query =
79
- if columna&.array
80
- # FIXME: testear
81
- # El CONCAT no sé para qué sirve, pero lo dejo
82
- query.where("unaccent(CONCAT(array_to_string(#{campo_tabla}, ' '))) ILIKE unaccent(?)", I18n.transliterate(match_like).to_s)
83
- else
84
- match_vector = parametros[campo].split.map { |a| "#{a}:*" }.join(' & ')
85
- condicion = "to_tsvector(coalesce(unaccent(#{campo_tabla}), '')) @@ to_tsquery( unaccent(?) )"
86
- condicion += " OR unaccent(CONCAT(#{campo_tabla})) ILIKE unaccent(?)"
87
- query = query.where(condicion, I18n.transliterate(match_vector).to_s,
88
- I18n.transliterate(match_like).to_s)
89
- end
90
- elsif tipo(campo) == :boolean
91
- if campo.to_s == 'discarded'
92
- # Si el nombre del campo es 'discarded' entonces no es un campo
93
- # real sino filtro booleano por presencia de discarded_at
94
- case parametros[campo]
95
- when 'si'
96
- query = query.unscope(where: :discarded_at).where("#{@clase_modelo.table_name}.discarded_at IS NOT NULL")
97
- when 'no'
98
- query = query.unscope(where: :discarded_at).where("#{@clase_modelo.table_name}.discarded_at IS NULL")
99
- end
100
- else
101
- # Si no simplemente hago la query por booleano
102
- query = query.where("#{@clase_modelo.table_name}.#{campo} = ?", parametros[campo] == 'si')
103
- end
104
- elsif tipo(campo) == :date || tipo(campo) == :datetime
105
- begin
106
- fecha = Date.parse(parametros[campo])
107
- fecha = fecha + 1.day - 1.second if tipo(campo) == :datetime && comparador(campo) == '<'
108
- campo_a_comparar = "#{@clase_modelo.table_name}.#{sin_sufijo(campo)}"
109
- query = query.where("#{campo_a_comparar} #{comparador(campo)} ?", fecha)
110
- rescue ArgumentError
111
- end
112
- end
113
- end
114
73
  query
115
74
  end
116
75
 
117
76
  def tipo(campo)
118
77
  nombre_campo = sin_sufijo(campo)
78
+
119
79
  if @filtros[nombre_campo.to_sym].present? && @filtros[nombre_campo.to_sym][:tipo].present?
120
80
  @filtros[nombre_campo.to_sym][:tipo]
121
81
  elsif @clase_modelo.respond_to?(:enumerized_attributes) && @clase_modelo.enumerized_attributes[nombre_campo.to_s].present?
122
82
  :enumerized
123
- elsif @clase_modelo.reflect_on_all_associations.find do |a|
124
- a.name == nombre_campo.to_sym
125
- end.present?
83
+ elsif find_on_all_associations(@clase_modelo, campo).present?
126
84
  :asociacion
127
85
  else
128
86
  columna = @clase_modelo.columns.find { |c| c.name == nombre_campo.to_s }
@@ -133,7 +91,8 @@ module PgEngine
133
91
  # real sino filtro booleano por presencia de discarded_at
134
92
  return :boolean if campo.to_s == 'discarded'
135
93
 
136
- Rails.logger.warn("no existe el campo: #{nombre_campo}")
94
+ pg_warn("no existe el campo: #{nombre_campo}")
95
+
137
96
  return
138
97
  end
139
98
  columna.type
@@ -165,28 +124,39 @@ module PgEngine
165
124
  SUFIJOS.each do |sufijo|
166
125
  return sufijo if campo.to_s.ends_with?("_#{sufijo}")
167
126
  end
127
+
168
128
  nil
169
129
  end
170
130
 
171
131
  def sin_sufijo(campo)
172
132
  ret = campo.to_s.dup
133
+
173
134
  SUFIJOS.each do |sufijo|
174
135
  ret.gsub!(/_#{sufijo}$/, '')
175
136
  end
137
+
176
138
  ret
177
139
  end
178
140
 
179
141
  def placeholder_campo(campo)
180
142
  suf = extraer_sufijo(campo)
181
- if suf.present?
182
- "#{@clase_modelo.human_attribute_name(sin_sufijo(campo))} #{suf}"
183
- else
184
- @clase_modelo.human_attribute_name(campo)
185
- end
143
+ key = [controller_name, action_name, 'filter', sin_sufijo(campo)].join('.')
144
+ dflt = :"activerecord.attributes.#{@clase_modelo.model_name.i18n_key}.#{sin_sufijo(campo)}"
145
+ human_name = @clase_modelo.human_attribute_name(key, default: dflt)
146
+
147
+ ret =
148
+ if suf.present?
149
+ "#{human_name} #{I18n.t(suf, scope: 'ransack.predicates')}"
150
+ else
151
+ human_name
152
+ end
153
+
154
+ ret.strip.downcase.tap { _1[0] = _1[0].capitalize }
186
155
  end
187
156
 
188
157
  def filtros_html(options = {})
189
158
  @form = options[:form]
159
+
190
160
  raise PgEngine::Error, 'se debe setear el form' if @form.blank?
191
161
 
192
162
  res = ''
@@ -198,8 +168,6 @@ module PgEngine
198
168
  end
199
169
 
200
170
  res += case tipo(campo)
201
- when :select_custom
202
- filtro_select_custom(campo, placeholder_campo(campo))
203
171
  when :enumerized
204
172
  filtro_select(campo, placeholder_campo(campo))
205
173
  when :asociacion
@@ -212,24 +180,30 @@ module PgEngine
212
180
  filtro_texto(campo, placeholder_campo(campo))
213
181
  end
214
182
  end
215
- res += hidden_field_tag('order_by', parametros_controller['order_by'])
216
- res += hidden_field_tag('order_direction', parametros_controller['order_direction'])
183
+
184
+ if params[:q] && params[:q][:s]
185
+ res += @form.hidden_field('s', value: params[:q][:s])
186
+ end
187
+
217
188
  res.html_safe
218
189
  end
219
190
 
220
- def obtener_asociacion(campo)
191
+ def find_on_all_associations(klass, campo)
221
192
  nombre_campo = sin_sufijo(campo)
222
- extraer_sufijo(campo)
223
- asociacion = @clase_modelo.reflect_on_all_associations.find do |a|
224
- a.name == nombre_campo.to_sym
193
+ klass.reflect_on_all_associations.find do |a|
194
+ a.name == nombre_campo.to_sym || a.name == nombre_campo.sub(/_id$/, '').to_sym
225
195
  end
196
+ end
197
+
198
+ def obtener_asociacion(campo)
199
+ asociacion = find_on_all_associations(@clase_modelo, campo)
200
+
226
201
  raise 'no se encontró la asociacion' if asociacion.nil?
227
202
 
228
203
  if asociacion.instance_of?(ActiveRecord::Reflection::ThroughReflection)
229
204
  through_class = asociacion.through_reflection.class_name.constantize
230
- asociacion_posta = through_class.reflect_on_all_associations.find do |a|
231
- a.name == nombre_campo.to_sym
232
- end
205
+ asociacion_posta = find_on_all_associations(through_class, campo)
206
+
233
207
  raise 'no se encontró la asociacion' if asociacion_posta.nil?
234
208
 
235
209
  asociacion_posta
@@ -238,7 +212,7 @@ module PgEngine
238
212
  end
239
213
  end
240
214
 
241
- def filtro_asociacion(campo, _placeholder = '')
215
+ def filtro_asociacion(campo, placeholder = '')
242
216
  asociacion = obtener_asociacion(campo)
243
217
  nombre_clase = asociacion.options[:class_name]
244
218
  nombre_clase = asociacion.name.to_s.camelize if nombre_clase.nil?
@@ -252,18 +226,18 @@ module PgEngine
252
226
  scope = @filtros[campo.to_sym][:scope_asociacion].call(scope)
253
227
  end
254
228
 
229
+ # TODO: default sort
230
+
255
231
  map = scope.map { |o| [o.to_s, o.id] }
256
232
 
257
233
  content_tag :div, class: 'col-auto' do
258
234
  content_tag :div, class: 'filter' do
259
- placeholder = ransack_placeholder(campo)
260
235
  suf = extraer_sufijo(campo)
261
236
  if suf.in? %w[in]
262
- @form.select sin_sufijo(campo) + '_id_in', map, { multiple: true }, placeholder:, 'data-controller': 'selectize',
263
- class: 'form-control form-control-sm pg-input-lg'
237
+ @form.select campo, map, { multiple: true }, placeholder:, 'data-controller': 'selectize',
238
+ class: 'form-control form-control-sm pg-input-lg'
264
239
  else
265
- campo = campo.to_s + '_id_eq'
266
- @form.select campo, map, { include_blank: "Seleccionar #{placeholder}" }, class: 'form-select form-select-sm pg-input-lg'
240
+ @form.select campo, map, { include_blank: "Seleccionar #{placeholder.downcase}" }, class: 'form-select form-select-sm pg-input-lg'
267
241
  end
268
242
  end
269
243
  end
@@ -276,36 +250,19 @@ module PgEngine
276
250
  end
277
251
  content_tag :div, class: 'col-auto' do
278
252
  content_tag :div, class: 'filter' do
279
- placeholder = ransack_placeholder(campo)
280
253
  suf = extraer_sufijo(campo)
281
254
  if suf.in? %w[in]
282
255
  @form.select(campo, map, { multiple: true }, placeholder:, class: 'form-control form-control-sm pg-input-lg', 'data-controller': 'selectize')
283
256
  else
284
- @form.select(campo, map, { include_blank: "Seleccionar #{placeholder}" }, placeholder:, class: 'form-select form-select-sm pg-input-lg')
257
+ @form.select(campo, map, { include_blank: "Seleccionar #{placeholder.downcase}" }, placeholder:, class: 'form-select form-select-sm pg-input-lg')
285
258
  end
286
259
  end
287
260
  end
288
261
  end
289
262
 
290
- # DEPRECADO
291
- def filtro_select_custom(campo, placeholder = '')
292
- map = @filtros[campo.to_sym][:opciones]
293
- unless @filtros[campo.to_sym].present? && @filtros[campo.to_sym][:include_blank] == false
294
- map.unshift ["Seleccionar #{placeholder.downcase}",
295
- nil]
296
- end
297
- default = parametros_controller[campo].nil? ? nil : parametros_controller[campo]
298
- content_tag :div, class: 'col-auto' do
299
- content_tag :div, class: 'filter' do
300
- select_tag campo, options_for_select(map, default), class: 'form-select form-select-sm pg-input-lg'
301
- end
302
- end
303
- end
304
-
305
263
  def filtro_texto(campo, placeholder = '')
306
264
  content_tag :div, class: 'col-auto' do
307
265
  content_tag :div, class: 'filter' do
308
- placeholder = ransack_placeholder(campo)
309
266
  @form.search_field(
310
267
  campo, class: 'form-control form-control-sm allow-enter-submit', placeholder:, autocomplete: 'off'
311
268
  )
@@ -314,15 +271,13 @@ module PgEngine
314
271
  end
315
272
 
316
273
  def filtro_boolean(campo, placeholder = '')
317
- map = [%w[Si si], %w[No no]]
318
- unless @filtros[campo.to_sym].present? && @filtros[campo.to_sym][:include_blank] == false
319
- map.unshift ["¿#{placeholder.titleize}?",
320
- nil]
321
- end
322
- default = parametros_controller[campo].nil? ? nil : parametros_controller[campo]
274
+ map = [%w[ true], %w[No false]]
275
+
276
+ include_blank = "¿#{placeholder.titleize}?"
277
+
323
278
  content_tag :div, class: 'col-auto' do
324
279
  content_tag :div, class: 'filter' do
325
- select_tag campo, options_for_select(map, default), class: 'form-select form-select-sm pg-input-lg'
280
+ @form.select campo.to_sym, map, { include_blank: }, class: 'form-select form-select-sm pg-input-lg'
326
281
  end
327
282
  end
328
283
  end
@@ -330,7 +285,6 @@ module PgEngine
330
285
  def filtro_fecha(campo, placeholder = '')
331
286
  content_tag :div, class: 'col-auto' do
332
287
  content_tag :div, class: 'filter' do
333
- placeholder = ransack_placeholder(campo)
334
288
  label_tag(nil, placeholder, class: 'text-body-secondary') +
335
289
  @form.date_field(
336
290
  campo, class: 'form-control form-control-sm d-inline-block w-auto ms-1', placeholder:, autocomplete: 'off'
@@ -339,10 +293,6 @@ module PgEngine
339
293
  end
340
294
  end
341
295
 
342
- def ransack_placeholder(campo)
343
- @form.object.translate(campo, include_associations: false)
344
- end
345
-
346
296
  def parametros_controller
347
297
  params
348
298
  end
@@ -89,8 +89,8 @@ module PgEngine
89
89
  end
90
90
 
91
91
  def objeto_borrado?
92
- if record.respond_to?(:discarded?)
93
- record.discarded?
92
+ if record.respond_to?(:kept?)
93
+ !record.kept?
94
94
  else
95
95
  false
96
96
  end
@@ -2,12 +2,12 @@
2
2
 
3
3
  wb = xlsx_package.workbook
4
4
  wb.add_worksheet(name: @clase_modelo.nombre_plural) do |sheet|
5
- headers = atributos_para_listar.map { |a| @clase_modelo.human_attribute_name(a) }
5
+ headers = atributos_para_listar.map { |a, _sort_by| @clase_modelo.human_attribute_name(a) }
6
6
  headers.prepend 'ID interno'
7
7
  sheet.add_row(headers)
8
8
 
9
9
  @collection.decorate.each do |object|
10
- array = atributos_para_listar.map do |att|
10
+ array = atributos_para_listar.map do |att, _sort_by|
11
11
  object.send(att)
12
12
  end
13
13
  array.prepend object.to_key
@@ -16,7 +16,7 @@
16
16
 
17
17
  - if @filtros.present? && show_filters?
18
18
  .border-bottom#filtros
19
- .d-flex.align-items-center.p-2
19
+ .d-flex.align-items-center.px-3.py-2
20
20
  .px-2.d-none.d-sm-inline-block
21
21
  span.bi.bi-funnel-fill
22
22
  = search_form_for @q, url: namespaced_path(@clase_modelo) do |f|
@@ -49,7 +49,7 @@ div
49
49
  - @collection.each do |object|
50
50
  - object = object.decorate
51
51
  tr id="#{dom_id(object)}"
52
- - atributos_para_listar.each do |att|
52
+ - atributos_para_listar.each do |att, _sort_field|
53
53
  td.text-nowrap = object.send(att)
54
54
  td.text-nowrap.text-end.ps-5
55
55
  .actions-wrapper
@@ -25,6 +25,14 @@ module Arel
25
25
  end
26
26
 
27
27
  Ransack.configure do |config|
28
+ config.postgres_fields_sort_option = :nulls_always_last
29
+
30
+ config.custom_arrows = {
31
+ up_arrow: '<i class="bi bi-sort-up" />',
32
+ down_arrow: '<i class="bi bi-sort-down-alt" />',
33
+ default_arrow: ''
34
+ }
35
+
28
36
  # Piso predicados cont y not_cont para que usen unaccent
29
37
  config.add_predicate 'cont',
30
38
  arel_predicate: 'matches_unaccent',
@@ -3,10 +3,14 @@ es:
3
3
  predicates:
4
4
  cont: ''
5
5
  cont_all: ''
6
+ cont_any: ''
7
+ not_cont: 'no contiene'
6
8
  in: ''
7
9
  eq: ''
8
10
  lt: 'hasta'
9
11
  gt: 'desde'
12
+ lteq: 'hasta'
13
+ gteq: 'desde'
10
14
  pg_engine:
11
15
  base:
12
16
  index:
@@ -47,65 +47,4 @@ describe PgEngine::Resource do
47
47
  expect(subject).to eq categoria_de_cosa
48
48
  end
49
49
  end
50
-
51
- describe '#do_sort' do
52
- subject do
53
- instancia.send(:do_sort, scope, param, direction)
54
- end
55
-
56
- let!(:cosa_ult) { create :cosa, nombre: 'Z' }
57
- let!(:cosa_pri) { create :cosa, nombre: 'a' }
58
- let(:scope) { Cosa.all }
59
- let(:param) { :nombre }
60
- let(:direction) { :desc }
61
-
62
- context 'asc' do
63
- let(:direction) { :asc }
64
-
65
- it do
66
- expect(subject.to_a).to eq [cosa_pri, cosa_ult]
67
- end
68
- end
69
-
70
- context 'desc' do
71
- let(:direction) { :desc }
72
-
73
- it do
74
- expect(subject.to_a).to eq [cosa_ult, cosa_pri]
75
- end
76
- end
77
-
78
- context 'cuando no existe el param' do
79
- let(:param) { :inexistente }
80
-
81
- it do
82
- expect(subject.to_a).to eq [cosa_ult, cosa_pri]
83
- end
84
- end
85
-
86
- context 'cuando ordeno por categoria' do
87
- let(:param) { :categoria_de_cosa }
88
-
89
- before do
90
- cosa_pri.categoria_de_cosa.update_column(:nombre, 'a')
91
- cosa_ult.categoria_de_cosa.update_column(:nombre, 'z')
92
- end
93
-
94
- context 'si es asc' do
95
- let(:direction) { :asc }
96
-
97
- it do
98
- expect(subject.to_a).to eq [cosa_pri, cosa_ult]
99
- end
100
- end
101
-
102
- context 'si es desc' do
103
- let(:direction) { :desc }
104
-
105
- it do
106
- expect(subject.to_a).to eq [cosa_ult, cosa_pri]
107
- end
108
- end
109
- end
110
- end
111
50
  end
@@ -43,6 +43,20 @@ FactoryBot.define do
43
43
  password { "password#{rand(99_999)}" }
44
44
  confirmed_at { Faker::Date.backward }
45
45
 
46
+ transient do
47
+ account { nil }
48
+ end
49
+
50
+ after(:create) do |model, context|
51
+ if context.account
52
+ model.user_accounts.create!(account: context.account)
53
+ end
54
+ end
55
+
56
+ trait :orphan do
57
+ orphan { true }
58
+ end
59
+
46
60
  trait :admin do
47
61
  developer { true }
48
62
  end
@@ -0,0 +1,9 @@
1
+ module PgRails
2
+ module TomSelectHelpers
3
+ def select_tom(placeholder:, text:)
4
+ find("input[placeholder=\"#{placeholder}\"]").click
5
+ find('.ts-wrapper [role="option"]', text:).click
6
+ send_keys :escape
7
+ end
8
+ end
9
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgRails
4
- VERSION = '7.1.4'
4
+ VERSION = '7.1.6'
5
5
  end
@@ -26,7 +26,6 @@ class <%= controller_class_name.split('::').last %>Controller < <%= parent_contr
26
26
  end
27
27
 
28
28
  def atributos_para_buscar
29
- # FIXME: append _cont
30
29
  %i[<%= atributos_a_filtrar.map(&:name).join(' ') %>]
31
30
  end
32
31
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.1.4
4
+ version: 7.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martín Rosso
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-31 00:00:00.000000000 Z
11
+ date: 2024-08-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -866,6 +866,7 @@ files:
866
866
  - pg_rails/lib/pg_rails/dotenv_support.rb
867
867
  - pg_rails/lib/pg_rails/redis_support.rb
868
868
  - pg_rails/lib/pg_rails/rspec_logger_matchers.rb
869
+ - pg_rails/lib/pg_rails/tom_select_helpers.rb
869
870
  - pg_rails/lib/pg_rails/vcr_support.rb
870
871
  - pg_rails/lib/version.rb
871
872
  - pg_rails/scss/bootstrap_overrides.scss