agilibox 1.0.0 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +321 -2
  3. data/Rakefile +4 -0
  4. data/app/assets/javascripts/agilibox/autocomplete.coffee +2 -0
  5. data/app/assets/javascripts/agilibox/checkboxes_dropdown.coffee +9 -0
  6. data/app/assets/javascripts/agilibox/modals.coffee +81 -55
  7. data/app/assets/stylesheets/agilibox/actions.sass +2 -0
  8. data/app/assets/stylesheets/agilibox/all.sass +5 -0
  9. data/app/assets/stylesheets/agilibox/buttons.sass +11 -0
  10. data/app/assets/stylesheets/agilibox/filters.sass +13 -3
  11. data/app/assets/stylesheets/agilibox/forms.sass +49 -0
  12. data/app/assets/stylesheets/agilibox/icons.sass +5 -0
  13. data/app/assets/stylesheets/agilibox/modals.sass +15 -7
  14. data/app/assets/stylesheets/agilibox/pagination.sass +7 -0
  15. data/app/assets/stylesheets/agilibox/print.sass +10 -0
  16. data/app/assets/stylesheets/agilibox/tables.sass +26 -0
  17. data/app/controllers/agilibox/application_controller.rb +2 -4
  18. data/app/controllers/agilibox/small_data/filters_controller.rb +8 -6
  19. data/app/controllers/concerns/agilibox/api_controller_concern.rb +69 -0
  20. data/app/controllers/concerns/agilibox/back_url_concern.rb +13 -5
  21. data/app/emails/agilibox/email.rb +113 -0
  22. data/app/filters/agilibox/small_data/filter.rb +19 -1
  23. data/app/filters/agilibox/small_data/filter_strategy.rb +1 -1
  24. data/app/filters/agilibox/small_data/filter_strategy_by_date_begin.rb +3 -2
  25. data/app/filters/agilibox/small_data/filter_strategy_by_date_end.rb +3 -2
  26. data/app/filters/agilibox/small_data/filter_strategy_by_date_or_datetime_period.rb +56 -0
  27. data/app/filters/agilibox/small_data/filter_strategy_by_date_period.rb +5 -0
  28. data/app/filters/agilibox/small_data/filter_strategy_by_datetime_period.rb +5 -0
  29. data/app/filters/agilibox/small_data/filter_strategy_by_key_value.rb +14 -3
  30. data/app/filters/agilibox/small_data/filter_strategy_by_key_values.rb +13 -0
  31. data/app/filters/agilibox/small_data/filter_strategy_by_tags.rb +1 -1
  32. data/app/forms/agilibox/mini_form_object.rb +29 -0
  33. data/app/helpers/agilibox/all_helpers.rb +2 -0
  34. data/app/helpers/agilibox/bootstrap_helper.rb +62 -3
  35. data/app/helpers/agilibox/button_helper.rb +22 -13
  36. data/app/helpers/agilibox/filters_helper.rb +7 -8
  37. data/app/helpers/agilibox/font_awesome_helper.rb +67 -0
  38. data/app/helpers/agilibox/form_helper.rb +37 -14
  39. data/app/helpers/agilibox/i18n_helper.rb +5 -0
  40. data/app/helpers/agilibox/link_helper.rb +1 -1
  41. data/app/helpers/agilibox/pagination_helper.rb +18 -2
  42. data/app/helpers/agilibox/routes_helper.rb +6 -2
  43. data/app/helpers/agilibox/sorting_helper.rb +9 -4
  44. data/app/helpers/agilibox/text_helper.rb +51 -26
  45. data/app/helpers/h.rb +3 -0
  46. data/app/jobs/concerns/agilibox/setup_job_concern.rb +35 -0
  47. data/app/libs/agilibox/collection_update.rb +32 -0
  48. data/app/libs/agilibox/fcm/notifier.rb +28 -0
  49. data/app/libs/agilibox/fcm/request.rb +48 -0
  50. data/app/libs/agilibox/initialize_with.rb +20 -0
  51. data/app/libs/agilibox/mini_model_serializer/serialize.rb +30 -0
  52. data/app/libs/agilibox/mini_model_serializer/serializer.rb +23 -0
  53. data/app/libs/agilibox/monkey.rb +39 -0
  54. data/app/libs/agilibox/phone_number_sanitizer.rb +15 -0
  55. data/app/libs/agilibox/sortable_uuid_generator.rb +1 -1
  56. data/app/libs/agilibox/token_generator.rb +37 -0
  57. data/app/mailers/agilibox/application_mailer.rb +2 -0
  58. data/app/mailers/agilibox/generic_mailer.rb +9 -0
  59. data/app/models/concerns/agilibox/active_record_uuid_concern.rb +4 -2
  60. data/app/models/concerns/agilibox/default_values_concern.rb +4 -3
  61. data/app/models/concerns/agilibox/model_i18n.rb +37 -6
  62. data/app/models/concerns/agilibox/pluck_distinct.rb +13 -0
  63. data/app/models/concerns/agilibox/pluck_to_hash.rb +9 -0
  64. data/app/models/concerns/agilibox/polymorphic_id.rb +5 -2
  65. data/app/models/concerns/agilibox/search.rb +11 -13
  66. data/app/models/concerns/agilibox/timestamp_helpers.rb +17 -0
  67. data/app/serializers/agilibox/serializers/base.rb +47 -1
  68. data/app/serializers/agilibox/serializers/xlsx.rb +25 -23
  69. data/app/services/agilibox/service.rb +17 -0
  70. data/app/sms/agilibox/sms/application_sms.rb +42 -0
  71. data/app/sms/agilibox/sms/message.rb +25 -0
  72. data/app/sms/agilibox/sms/strategies/amazon_sns.rb +48 -0
  73. data/app/sms/agilibox/sms/strategies/base.rb +15 -0
  74. data/app/sms/agilibox/sms/strategies/test.rb +10 -0
  75. data/app/sms/agilibox/sms.rb +35 -0
  76. data/app/sorters/agilibox/sorter.rb +26 -0
  77. data/app/views/agilibox/_flash.html.slim +9 -0
  78. data/app/views/agilibox/forms/_checkboxes_dropdown.html.slim +14 -0
  79. data/app/views/agilibox/search/_form.html.slim +1 -6
  80. data/app/views/agilibox/search/_form_bs3.html.slim +13 -0
  81. data/app/views/agilibox/search/_form_bs4.html.slim +9 -0
  82. data/app/views/agilibox/search/_form_bs5.html.slim +8 -0
  83. data/app/views/kaminari/bootstrap4/_first_page.html.slim +2 -0
  84. data/app/views/kaminari/bootstrap4/_gap.html.slim +2 -0
  85. data/app/views/kaminari/bootstrap4/_last_page.html.slim +2 -0
  86. data/app/views/kaminari/bootstrap4/_next_page.html.slim +2 -0
  87. data/app/views/kaminari/bootstrap4/_page.html.slim +6 -0
  88. data/app/views/kaminari/bootstrap4/_paginator.html.slim +12 -0
  89. data/app/views/kaminari/bootstrap4/_prev_page.html.slim +2 -0
  90. data/config/cucumber.yml +9 -0
  91. data/config/locales/actions.en.yml +76 -0
  92. data/config/locales/actions.fr.yml +76 -0
  93. data/config/locales/attributes.en.yml +108 -0
  94. data/config/locales/attributes.fr.yml +108 -0
  95. data/config/locales/common.en.yml +0 -160
  96. data/config/locales/common.fr.yml +0 -171
  97. data/config/locales/dates.fr.yml +8 -0
  98. data/config/locales/errors.en.yml +5 -0
  99. data/config/locales/errors.fr.yml +5 -0
  100. data/db/migrate/20000101000000_enable_agilibox_extensions.rb +7 -0
  101. data/lib/agilibox/active_model_custom_error_messages.rb +23 -0
  102. data/lib/agilibox/active_model_type_cast.rb +52 -0
  103. data/lib/agilibox/config.rb +19 -0
  104. data/lib/agilibox/core_and_rails_ext.rb +2 -1
  105. data/lib/agilibox/cucumber_config.rb +53 -0
  106. data/lib/agilibox/cucumber_helpers/agilibox.rb +2 -0
  107. data/lib/agilibox/cucumber_helpers/ajax.rb +18 -0
  108. data/lib/agilibox/cucumber_helpers/apparition.rb +18 -0
  109. data/lib/agilibox/cucumber_helpers/capybara.rb +12 -0
  110. data/lib/agilibox/cucumber_helpers/capybara_selectors.rb +7 -0
  111. data/lib/agilibox/cucumber_helpers/chrome_headless.rb +16 -0
  112. data/lib/agilibox/cucumber_helpers/common_steps.rb +139 -0
  113. data/lib/agilibox/cucumber_helpers/cuprite.rb +20 -0
  114. data/lib/agilibox/cucumber_helpers/database_cleaner.rb +8 -0
  115. data/lib/agilibox/cucumber_helpers/factory_bot.rb +1 -0
  116. data/lib/agilibox/cucumber_helpers/rails.rb +1 -0
  117. data/lib/agilibox/cucumber_helpers/rspec.rb +6 -0
  118. data/lib/agilibox/cucumber_helpers/screenshots.rb +3 -0
  119. data/lib/agilibox/cucumber_helpers/select2.rb +23 -0
  120. data/lib/agilibox/cucumber_helpers/sign_in.rb +17 -0
  121. data/lib/agilibox/cucumber_helpers/simplecov.rb +1 -0
  122. data/lib/agilibox/cucumber_helpers/timecop.rb +3 -0
  123. data/lib/agilibox/cucumber_helpers/turbolinks.rb +34 -0
  124. data/lib/agilibox/cucumber_helpers/zonebie.rb +4 -0
  125. data/lib/agilibox/engine.rb +15 -0
  126. data/lib/agilibox/engine_file.rb +10 -0
  127. data/lib/agilibox/errors_middleware.rb +50 -0
  128. data/lib/agilibox/form_back_url.rb +4 -4
  129. data/lib/agilibox/rspec.rb +29 -0
  130. data/lib/agilibox/test_helpers.rb +13 -0
  131. data/lib/agilibox/version.rb +1 -1
  132. data/lib/tasks/cucumber.rake +76 -0
  133. metadata +165 -9
  134. data/app/filters/agilibox/small_data/filter_strategy_by_time_period.rb +0 -37
  135. data/lib/agilibox/active_record_comma_type_cast.rb +0 -12
@@ -2,23 +2,23 @@ module Agilibox::ButtonHelper
2
2
  def bs_button(url, options = {})
3
3
  action = options.delete(:action)
4
4
  icon = options.delete(:icon)
5
- text = options.delete(:text) || t("actions.#{action}")
5
+ text = options.delete(:text) || ta(action)
6
6
  title = options.delete(:title) || text
7
7
 
8
- text = "#{icon icon} <span>#{text}</span>".html_safe
8
+ text = %(#{icon icon} <span class="text">#{text}</span>).html_safe
9
9
 
10
10
  options = {
11
- :class => "btn btn-xs link_#{action}",
11
+ :class => "btn btn-xs btn-default link_#{action}",
12
12
  :title => title,
13
13
  }.deep_merge(options)
14
14
 
15
- if confirm = options.delete(:confirm)
15
+ if (confirm = options.delete(:confirm))
16
16
  confirm = t("actions.confirm") if confirm == true
17
17
 
18
18
  options.deep_merge!(
19
19
  :data => {
20
20
  :confirm => confirm,
21
- }
21
+ },
22
22
  )
23
23
  end
24
24
 
@@ -40,7 +40,7 @@ module Agilibox::ButtonHelper
40
40
  options = {
41
41
  :icon => :plus,
42
42
  :action => :create,
43
- :class =>"btn btn-xs btn-success link_create"
43
+ :class =>"btn btn-xs btn-success link_create",
44
44
  }.merge(options)
45
45
 
46
46
  bs_button(url, options)
@@ -48,7 +48,7 @@ module Agilibox::ButtonHelper
48
48
 
49
49
  def read_button(url, options = {})
50
50
  options = {
51
- :icon => "info-circle",
51
+ :icon => :info_circle,
52
52
  :action => :read,
53
53
  }.merge(options)
54
54
 
@@ -57,9 +57,10 @@ module Agilibox::ButtonHelper
57
57
 
58
58
  def download_button(url, options = {})
59
59
  options = {
60
- :icon => "cloud-download",
60
+ :icon => :cloud_download_alt,
61
61
  :action => :download,
62
- :download => url,
62
+ :download => File.basename(url),
63
+ :target => "_blank",
63
64
  }.merge(options)
64
65
 
65
66
  bs_button(url, options)
@@ -75,9 +76,17 @@ module Agilibox::ButtonHelper
75
76
  end
76
77
 
77
78
  options = {
78
- :icon => "cloud-download",
79
+ :icon => :download,
79
80
  :action => action,
80
- :download => url,
81
+ }.merge(options)
82
+
83
+ download_button(url, options)
84
+ end
85
+
86
+ def import_button(url, options = {})
87
+ options = {
88
+ :icon => :upload,
89
+ :action => :import,
81
90
  }.merge(options)
82
91
 
83
92
  bs_button(url, options)
@@ -85,7 +94,7 @@ module Agilibox::ButtonHelper
85
94
 
86
95
  def update_button(url, options = {})
87
96
  options = {
88
- :icon => :pencil,
97
+ :icon => :pencil_alt,
89
98
  :action => :update,
90
99
  }.merge(options)
91
100
 
@@ -127,7 +136,7 @@ module Agilibox::ButtonHelper
127
136
 
128
137
  def snooze_button(url, options = {})
129
138
  options = {
130
- :icon => :"clock-o",
139
+ :icon => :clock,
131
140
  :action => :snooze,
132
141
  :confirm => true,
133
142
  :method => :patch,
@@ -5,9 +5,9 @@ module Agilibox::FiltersHelper
5
5
  options[:value] ||= "submit"
6
6
 
7
7
  text = options.delete(:text) || t("actions.filter")
8
- icon = options.delete(:icon) || "filter"
8
+ icon = options.delete(:icon) || :filter
9
9
 
10
- content_tag(:button, options) do
10
+ tag.button(**options) do
11
11
  icon(icon) + " " + text
12
12
  end
13
13
  end
@@ -18,9 +18,9 @@ module Agilibox::FiltersHelper
18
18
  options[:value] ||= "reset"
19
19
 
20
20
  text = options.delete(:text) || t("actions.reset")
21
- icon = options.delete(:icon) || "rotate-left"
21
+ icon = options.delete(:icon) || :undo
22
22
 
23
- content_tag(:button, options) do
23
+ tag.button(**options) do
24
24
  icon(icon) + " " + text
25
25
  end
26
26
  end
@@ -31,7 +31,7 @@ module Agilibox::FiltersHelper
31
31
 
32
32
  def filters_form(options = {}, &block)
33
33
  if options.key?(:buttons)
34
- buttons = optins.delete(:buttons)
34
+ buttons = options.delete(:buttons)
35
35
  else
36
36
  buttons = true
37
37
  end
@@ -44,7 +44,7 @@ module Agilibox::FiltersHelper
44
44
  html = simple_form_for(:filters, options, &block)
45
45
 
46
46
  if buttons
47
- html = html.gsub("</form>", "#{filter_buttons}</form>").html_safe
47
+ html = html.gsub("</form>", "#{form_hidden_submit + filter_buttons}</form>").html_safe
48
48
  end
49
49
 
50
50
  html
@@ -54,7 +54,7 @@ module Agilibox::FiltersHelper
54
54
  {
55
55
  t("time_periods.all_time") => "",
56
56
  t("time_periods.today") => "today",
57
- t("time_periods.yesterday") => "yesterday" ,
57
+ t("time_periods.yesterday") => "yesterday",
58
58
  t("time_periods.this_week") => "this_week",
59
59
  t("time_periods.last_week") => "last_week",
60
60
  t("time_periods.this_month") => "this_month",
@@ -64,5 +64,4 @@ module Agilibox::FiltersHelper
64
64
  t("time_periods.custom_date") => "custom_date",
65
65
  }
66
66
  end
67
-
68
67
  end
@@ -0,0 +1,67 @@
1
+ module Agilibox::FontAwesomeHelper
2
+ def icon(id, fa_style: nil, size: nil, spin: false, **options)
3
+ id = id.to_s.tr("_", "-").to_sym
4
+
5
+ if fa_style.nil?
6
+ fa_style = Agilibox::FontAwesomeHelper.default_fa_style_for_id(id)
7
+ end
8
+
9
+ css_classes = options.delete(:class).to_s.split
10
+ css_classes << "icon"
11
+ css_classes << "fa-#{id}"
12
+ css_classes << "fa#{fa_style.to_s[0]}"
13
+ css_classes << "fa-#{size}" if size
14
+ css_classes << "fa-spin" if spin
15
+
16
+ attributes = options.merge(class: css_classes.sort.join(" ")).sort.to_h
17
+
18
+ tag.span(**attributes)
19
+ end
20
+
21
+ class << self
22
+ def database
23
+ @database ||= YAML.safe_load(database_yml).deep_symbolize_keys
24
+ end
25
+
26
+ def database_path
27
+ Rails.root.join("tmp", "fa_database_#{version}.yml")
28
+ end
29
+
30
+ def database_yml
31
+ download_database! unless File.size?(database_path)
32
+ File.read(database_path)
33
+ end
34
+
35
+ def database_url
36
+ short_version = version.split(".")[0, 3].join(".") # 1.20.14.2 => 1.20.14
37
+ url = "https://raw.githubusercontent.com/FortAwesome/Font-Awesome/#{short_version}/metadata/icons.yml"
38
+
39
+ if Gem::Version.new(short_version) < Gem::Version.new("5.6.0")
40
+ url = url.gsub("/metadata", "/advanced-options/metadata")
41
+ end
42
+
43
+ url
44
+ end
45
+
46
+ def download_database!
47
+ require "open-uri"
48
+ data = URI.parse(database_url).open.read
49
+ File.write(database_path, data)
50
+ end
51
+
52
+ def version
53
+ require "font_awesome/sass/version"
54
+ FontAwesome::Sass::VERSION
55
+ end
56
+
57
+ def default_fa_style_for_id(id)
58
+ return if version.start_with?("4")
59
+
60
+ if version.start_with?("5")
61
+ return Agilibox::FontAwesomeHelper.database.dig(id, :styles).to_a.first
62
+ end
63
+
64
+ raise "invalid font-awesome-sass version"
65
+ end
66
+ end # class << self
67
+ end
@@ -1,8 +1,15 @@
1
1
  module Agilibox::FormHelper
2
+ include Agilibox::I18nHelper
3
+
4
+ # Hidden submit to be the default triggered on <enter> keypress on a form
5
+ def form_hidden_submit
6
+ tag(:input, type: "submit", class: "hidden-submit")
7
+ end
8
+
2
9
  def form_buttons(opts = {})
3
- back_url = opts[:back_url]
4
- back_url = url_for(:back).html_safe if back_url.blank?
5
- back_url = URI(back_url).path if back_url.include?("://")
10
+ back_url = opts[:back_url]
11
+ back_url = url_for(:back).html_safe if back_url.nil?
12
+ back_url = URI(back_url).path if back_url.to_s.include?("://")
6
13
 
7
14
  if opts[:obj].present?
8
15
  if opts[:obj].new_record?
@@ -14,13 +21,13 @@ module Agilibox::FormHelper
14
21
  submit_action = :save
15
22
  end
16
23
 
17
- content_tag("div", class: "actions") do
18
- submit = content_tag(:button, type: :submit, class: "btn btn-sm btn-success") do
19
- content_tag(:span, class: "fa fa-save") {} + " " + t("actions.#{submit_action}")
24
+ tag.div(class: "actions form-actions") do
25
+ submit = tag.button(type: :submit, class: "btn btn-sm btn-success form-submit") do
26
+ tag.span(class: "fa fa-save") + " " + ta(submit_action)
20
27
  end
21
28
 
22
- cancel = content_tag("a", href: back_url, class: "btn btn-primary btn-sm") do
23
- content_tag(:span, class: "fa fa-times"){} + " " + t("actions.cancel")
29
+ cancel = tag.a(href: back_url, class: "btn btn-primary btn-sm form-cancel") do
30
+ tag.span(class: "fa fa-times") + " " + ta(:cancel)
24
31
  end
25
32
 
26
33
  cancel = "" if back_url == false
@@ -29,21 +36,37 @@ module Agilibox::FormHelper
29
36
  end
30
37
  end
31
38
 
32
- def horizontal_form_for(obj, opts={}, &block)
39
+ def horizontal_form_for(obj, opts = {}, &block)
33
40
  opts = {
34
41
  :wrapper => "horizontal_form",
35
42
  :html => {
36
- :class => "form-horizontal"
37
- }
43
+ :class => "form-horizontal",
44
+ },
38
45
  }.deep_merge(opts)
39
46
 
40
47
  simple_form_for(obj, opts, &block)
41
48
  end
42
49
 
43
- def search_form(opts = {})
44
- action = opts.delete(:action) || request.fullpath
45
-
50
+ def search_form(action: request.fullpath)
46
51
  render "agilibox/search/form", action: action
47
52
  end
48
53
 
54
+ def checkboxes_dropdown(f, input, collection, label = t("actions.select"))
55
+ render("agilibox/forms/checkboxes_dropdown",
56
+ :f => f,
57
+ :input => input,
58
+ :collection => collection,
59
+ :label => label,
60
+ )
61
+ end
62
+
63
+ def hidden_inputs_for_get_form(url, except: [])
64
+ query_values = Addressable::URI.parse(url).query_values.to_h
65
+ .with_indifferent_access
66
+ .except(*except)
67
+
68
+ return if query_values.empty?
69
+
70
+ query_values.sum { |k, v| tag.input(type: "hidden", name: k, value: v) }
71
+ end
49
72
  end
@@ -0,0 +1,5 @@
1
+ module Agilibox::I18nHelper
2
+ def ta(action)
3
+ t("actions.#{action}")
4
+ end
5
+ end
@@ -34,7 +34,7 @@ module Agilibox::LinkHelper
34
34
  def tel_link(text, opts = {})
35
35
  return if text.to_s.blank?
36
36
 
37
- value = text.gsub(" ", "")
37
+ value = text.delete(" ")
38
38
  href = "tel:#{value}"
39
39
 
40
40
  link_to(text, href, opts)
@@ -1,6 +1,22 @@
1
1
  module Agilibox::PaginationHelper
2
+ class << self
3
+ attr_writer :theme
4
+
5
+ def theme
6
+ @theme ||= "twitter-bootstrap-3"
7
+ end
8
+ end
9
+
2
10
  def paginate(objects, options = {})
3
- options = {theme: "twitter-bootstrap-3"}.merge(options)
4
- super(objects, options).gsub(/>(\s+)</, '><').html_safe
11
+ options = {theme: Agilibox::PaginationHelper.theme}.merge(options)
12
+ super(objects, **options).gsub(/>(\s+)</, "><").html_safe
13
+ end
14
+
15
+ def pagination_infos(collection)
16
+ tag.p(class: "pagination-infos") { page_entries_info(collection) }
17
+ end
18
+
19
+ def pagination_and_infos(collection)
20
+ paginate(collection) + pagination_infos(collection)
5
21
  end
6
22
  end
@@ -1,6 +1,10 @@
1
1
  module Agilibox::RoutesHelper
2
2
  def engine_polymorphic_path(obj, opts = {})
3
- engine = obj.class.parents[-2]
3
+ if Rails::VERSION::STRING >= "6.0.0"
4
+ engine = obj.class.module_parents[-2]
5
+ else
6
+ engine = obj.class.parents[-2]
7
+ end
4
8
 
5
9
  if engine.nil?
6
10
  routes = main_app
@@ -12,7 +16,7 @@ module Agilibox::RoutesHelper
12
16
  :controller => "/#{obj.class.to_s.tableize}",
13
17
  :action => :show,
14
18
  :id => obj.to_param,
15
- :only_path => true
19
+ :only_path => true,
16
20
  }.merge(opts)
17
21
 
18
22
  routes.url_for(opts)
@@ -1,5 +1,5 @@
1
1
  module Agilibox::SortingHelper
2
- def sortable_column(name, column)
2
+ def sortable_column(name, column, options = {})
3
3
  unless column.is_a?(Symbol)
4
4
  raise ArgumentError, "invalid column, please use symbol"
5
5
  end
@@ -23,9 +23,14 @@ module Agilibox::SortingHelper
23
23
  klass = "sort"
24
24
  end
25
25
 
26
- url_params = params.to_h.symbolize_keys.merge(sort: new_sort_param)
26
+ unless (url_params = options.delete(:url_params))
27
+ url_params = (params.try(:permit!) || params).to_h.symbolize_keys
28
+ end
29
+ url_params[:sort] = new_sort_param
30
+
31
+ html_options = {class: klass}.merge(options)
27
32
 
28
- link_to(name, url_params, class: klass)
33
+ link_to(name, url_params, html_options)
29
34
  end
30
35
 
31
36
  def sortable_column_order(sort_param = params[:sort])
@@ -33,7 +38,7 @@ module Agilibox::SortingHelper
33
38
 
34
39
  if sort_param.present?
35
40
  if sort_param.start_with?("-")
36
- column = sort_param[1..-1].to_sym
41
+ column = sort_param[1..].to_sym
37
42
  direction = :desc
38
43
  else
39
44
  column = sort_param.to_sym
@@ -3,6 +3,14 @@ module Agilibox::TextHelper
3
3
  include ::ActionView::Helpers::SanitizeHelper
4
4
  include ::ActionView::Helpers::TextHelper
5
5
 
6
+ def nbsp(text = :no_argument)
7
+ if text == :no_argument
8
+ "\u00A0"
9
+ else
10
+ text.to_s.gsub(" ", nbsp)
11
+ end
12
+ end
13
+
6
14
  def euros(n)
7
15
  currency(n, "€")
8
16
  end
@@ -13,13 +21,13 @@ module Agilibox::TextHelper
13
21
  I18n.t("number.currency.format.format")
14
22
  .gsub("%n", number(n))
15
23
  .gsub("%u", u)
16
- .gsub(" ", "\u00A0")
24
+ .tr(" ", nbsp)
17
25
  end
18
26
 
19
27
  def percentage(n)
20
28
  return if n.nil?
21
29
 
22
- (number(n) + " %").gsub(" ", "\u00A0")
30
+ number(n) + nbsp + "%"
23
31
  end
24
32
 
25
33
  def number(n)
@@ -27,7 +35,7 @@ module Agilibox::TextHelper
27
35
 
28
36
  opts = {}
29
37
 
30
- if n.class.to_s.match(/Float|Decimal/i)
38
+ if n.class.to_s.match?(/Float|Decimal/i)
31
39
  opts[:precision] = 2
32
40
  else
33
41
  opts[:precision] = 0
@@ -36,12 +44,20 @@ module Agilibox::TextHelper
36
44
  opts[:delimiter] = I18n.t("number.format.delimiter")
37
45
  opts[:separator] = I18n.t("number.format.separator")
38
46
 
39
- number_with_precision(n, opts).gsub(" ", "\u00A0")
47
+ number_with_precision(n, opts).tr(" ", nbsp)
40
48
  end
41
49
 
42
- def date(d)
43
- return if d.nil?
44
- I18n.l(d)
50
+ def date(d, **args)
51
+ I18n.l(d, **args) unless d.nil?
52
+ end
53
+
54
+ def boolean_icon(bool)
55
+ return if bool.nil?
56
+
57
+ return icon(:check, style: "color: green") if bool == true
58
+ return icon(:times, style: "color: red") if bool == false
59
+
60
+ raise "#{bool} is not a boolean"
45
61
  end
46
62
 
47
63
  def hours(n)
@@ -56,16 +72,17 @@ module Agilibox::TextHelper
56
72
  def text2html(str)
57
73
  return if str.to_s.blank?
58
74
 
59
- str = str.gsub("\r", "").strip
75
+ str = str.delete("\r").strip
60
76
  strip_tags(str).gsub("\n", "<br />").html_safe
61
77
  end
62
78
 
63
79
  def lf2br(str)
64
80
  return if str.to_s.blank?
65
81
 
66
- str.gsub("\r", "").gsub("\n", "<br />").html_safe
82
+ str.delete("\r").gsub("\n", "<br />").html_safe
67
83
  end
68
84
 
85
+ # rubocop:disable Metrics/MethodLength
69
86
  def info(object, attribute, value_or_options = nil, options = {})
70
87
  if value_or_options.nil?
71
88
  value = object.public_send(attribute)
@@ -76,47 +93,55 @@ module Agilibox::TextHelper
76
93
  value = value_or_options
77
94
  end
78
95
 
96
+ value = t("yes") if value == true
97
+ value = t("no") if value == false
98
+
79
99
  if value.blank?
80
100
  value = options[:default]
81
101
  return if value == :hide
82
102
  end
83
103
 
84
104
  label = options[:label] || object.t(attribute)
85
- tag = options[:tag] || :div
105
+ info_tag = options[:tag] || :div
86
106
  separator = options[:separator] || " : "
107
+ separator = " :<br/>".html_safe if separator == :br
87
108
  helper = options[:helper]
88
109
  i18n_key = "#{object.class.to_s.tableize.singularize}/#{attribute}"
89
- nested = I18n.t("activerecord.attributes.#{i18n_key}").is_a?(Hash)
110
+ nested = I18n.t("activerecord.attributes.#{i18n_key}", default: "").is_a?(Hash)
90
111
  klass = object.is_a?(Module) ? object : object.class
91
112
  object_type = klass.to_s.split("::").last.underscore
92
113
 
93
- value = t("yes") if value === true
94
- value = t("no") if value === false
95
- value = object.t("#{attribute}.#{value}") if nested
96
- value = send(helper, value) if helper
97
- value = number(value) if value.is_a?(Numeric)
98
- value = l(value) if value.is_a?(Time)
99
- value = l(value) if value.is_a?(Date)
114
+ value = object.tv(attribute) if nested
115
+ value = send(helper, value) if helper
116
+ value = number(value) if value.is_a?(Numeric)
117
+ value = l(value) if value.is_a?(Time)
118
+ value = l(value) if value.is_a?(Date)
100
119
  value = value.to_s
101
120
 
102
- html_label = content_tag(:strong, class: "info-label") { label }
121
+ html_label = tag.strong(class: "info-label") { label }
103
122
  span_css_class = "info-value #{object_type}-#{attribute}"
104
- html_value = content_tag(:span, class: span_css_class) { value }
105
- separator_html = content_tag(:span, class: "info-separator") { separator }
123
+ html_value = tag.span(class: span_css_class) { value }
124
+ separator_html = tag.span(class: "info-separator") { separator }
106
125
 
107
- content_tag(tag, class: "info") do
126
+ if value.blank?
127
+ container_css_class = "info blank"
128
+ else
129
+ container_css_class = "info"
130
+ end
131
+
132
+ content_tag(info_tag, class: container_css_class) do
108
133
  [html_label, separator_html, html_value].join.html_safe
109
134
  end
110
135
  end # def info
136
+ # rubocop:enable Metrics/MethodLength
111
137
 
112
138
  def tags(object)
113
139
  return "" if object.tag_list.empty?
114
140
 
115
- object.tag_list.map { |tag|
116
- content_tag(:span, class: "tag label label-primary"){
117
- "#{icon :tag} #{tag}".html_safe
141
+ object.tag_list.map { |tag_name|
142
+ tag.span(class: "tag label label-primary") {
143
+ "#{icon :tag} #{tag_name}".html_safe
118
144
  }
119
145
  }.join(" ").html_safe
120
146
  end
121
-
122
147
  end
data/app/helpers/h.rb ADDED
@@ -0,0 +1,3 @@
1
+ # Short alias
2
+ # Example : H.number(123)
3
+ H = Agilibox::AllHelpers
@@ -0,0 +1,35 @@
1
+ module Agilibox::SetupJobConcern
2
+ extend ActiveSupport::Concern
3
+
4
+ def perform(*args)
5
+ setup(*args)
6
+ call
7
+ end
8
+
9
+ def call
10
+ raise NotImplementedError
11
+ end
12
+
13
+ class_methods do
14
+ def setup_with(*attrs)
15
+ attr_reader(*attrs)
16
+
17
+ define_method(:setup) do |*args|
18
+ if attrs.length != args.length
19
+ message = "wrong number of arguments (given #{args.length}, expected #{attrs.length})"
20
+ raise ArgumentError, message
21
+ end
22
+
23
+ attrs.length.times do |i|
24
+ instance_variable_set("@#{attrs[i]}", args[i])
25
+ end
26
+ end
27
+ end
28
+
29
+ private :setup_with
30
+
31
+ def setup(*args)
32
+ new.tap { |instance| instance.setup(*args) }
33
+ end
34
+ end # class_methods
35
+ end
@@ -0,0 +1,32 @@
1
+ class Agilibox::CollectionUpdate < Agilibox::Service
2
+ initialize_with :scope, :params_array
3
+
4
+ def call
5
+ update # rubocop:disable Rails/SaveBang
6
+ end
7
+
8
+ def update!
9
+ ApplicationRecord.transaction do
10
+ params_array.each do |object_param|
11
+ if object_param[:id].present?
12
+ object = scope.find(object_param[:id])
13
+ else
14
+ object = scope.new
15
+ end
16
+
17
+ if object_param.delete(:_destroy).to_i == 1
18
+ object.destroy!
19
+ else
20
+ object.update!(object_param)
21
+ end
22
+ end
23
+ end
24
+ true
25
+ end
26
+
27
+ def update
28
+ update!
29
+ rescue ActiveRecord::RecordInvalid
30
+ false
31
+ end
32
+ end
@@ -0,0 +1,28 @@
1
+ class Agilibox::FCM::Notifier < Agilibox::Service
2
+ attr_reader :to, :title, :body, :badge, :sound, :data
3
+
4
+ # rubocop:disable Metrics/ParameterLists
5
+ def initialize(to:, title: nil, body:, badge: 0, sound: "default", data: {})
6
+ super()
7
+ @to = to
8
+ @title = title
9
+ @body = body
10
+ @badge = badge
11
+ @sound = sound
12
+ @data = data
13
+ end
14
+ # rubocop:enable Metrics/ParameterLists
15
+
16
+ def call
17
+ Agilibox::FCM::Request.call(
18
+ :to => to,
19
+ :notification => {
20
+ :title => title,
21
+ :body => body,
22
+ :badge => badge,
23
+ :sound => sound,
24
+ },
25
+ :data => data,
26
+ )
27
+ end
28
+ end