agilibox 1.0.0 → 1.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +321 -2
- data/Rakefile +4 -0
- data/app/assets/javascripts/agilibox/autocomplete.coffee +2 -0
- data/app/assets/javascripts/agilibox/checkboxes_dropdown.coffee +9 -0
- data/app/assets/javascripts/agilibox/modals.coffee +81 -55
- data/app/assets/stylesheets/agilibox/actions.sass +2 -0
- data/app/assets/stylesheets/agilibox/all.sass +5 -0
- data/app/assets/stylesheets/agilibox/buttons.sass +11 -0
- data/app/assets/stylesheets/agilibox/filters.sass +13 -3
- data/app/assets/stylesheets/agilibox/forms.sass +49 -0
- data/app/assets/stylesheets/agilibox/icons.sass +5 -0
- data/app/assets/stylesheets/agilibox/modals.sass +15 -7
- data/app/assets/stylesheets/agilibox/pagination.sass +7 -0
- data/app/assets/stylesheets/agilibox/print.sass +10 -0
- data/app/assets/stylesheets/agilibox/tables.sass +26 -0
- data/app/controllers/agilibox/application_controller.rb +2 -4
- data/app/controllers/agilibox/small_data/filters_controller.rb +8 -6
- data/app/controllers/concerns/agilibox/api_controller_concern.rb +69 -0
- data/app/controllers/concerns/agilibox/back_url_concern.rb +13 -5
- data/app/emails/agilibox/email.rb +113 -0
- data/app/filters/agilibox/small_data/filter.rb +19 -1
- data/app/filters/agilibox/small_data/filter_strategy.rb +1 -1
- data/app/filters/agilibox/small_data/filter_strategy_by_date_begin.rb +3 -2
- data/app/filters/agilibox/small_data/filter_strategy_by_date_end.rb +3 -2
- data/app/filters/agilibox/small_data/filter_strategy_by_date_or_datetime_period.rb +56 -0
- data/app/filters/agilibox/small_data/filter_strategy_by_date_period.rb +5 -0
- data/app/filters/agilibox/small_data/filter_strategy_by_datetime_period.rb +5 -0
- data/app/filters/agilibox/small_data/filter_strategy_by_key_value.rb +14 -3
- data/app/filters/agilibox/small_data/filter_strategy_by_key_values.rb +13 -0
- data/app/filters/agilibox/small_data/filter_strategy_by_tags.rb +1 -1
- data/app/forms/agilibox/mini_form_object.rb +29 -0
- data/app/helpers/agilibox/all_helpers.rb +2 -0
- data/app/helpers/agilibox/bootstrap_helper.rb +62 -3
- data/app/helpers/agilibox/button_helper.rb +22 -13
- data/app/helpers/agilibox/filters_helper.rb +7 -8
- data/app/helpers/agilibox/font_awesome_helper.rb +67 -0
- data/app/helpers/agilibox/form_helper.rb +37 -14
- data/app/helpers/agilibox/i18n_helper.rb +5 -0
- data/app/helpers/agilibox/link_helper.rb +1 -1
- data/app/helpers/agilibox/pagination_helper.rb +18 -2
- data/app/helpers/agilibox/routes_helper.rb +6 -2
- data/app/helpers/agilibox/sorting_helper.rb +9 -4
- data/app/helpers/agilibox/text_helper.rb +51 -26
- data/app/helpers/h.rb +3 -0
- data/app/jobs/concerns/agilibox/setup_job_concern.rb +35 -0
- data/app/libs/agilibox/collection_update.rb +32 -0
- data/app/libs/agilibox/fcm/notifier.rb +28 -0
- data/app/libs/agilibox/fcm/request.rb +48 -0
- data/app/libs/agilibox/initialize_with.rb +20 -0
- data/app/libs/agilibox/mini_model_serializer/serialize.rb +30 -0
- data/app/libs/agilibox/mini_model_serializer/serializer.rb +23 -0
- data/app/libs/agilibox/monkey.rb +39 -0
- data/app/libs/agilibox/phone_number_sanitizer.rb +15 -0
- data/app/libs/agilibox/sortable_uuid_generator.rb +1 -1
- data/app/libs/agilibox/token_generator.rb +37 -0
- data/app/mailers/agilibox/application_mailer.rb +2 -0
- data/app/mailers/agilibox/generic_mailer.rb +9 -0
- data/app/models/concerns/agilibox/active_record_uuid_concern.rb +4 -2
- data/app/models/concerns/agilibox/default_values_concern.rb +4 -3
- data/app/models/concerns/agilibox/model_i18n.rb +37 -6
- data/app/models/concerns/agilibox/pluck_distinct.rb +13 -0
- data/app/models/concerns/agilibox/pluck_to_hash.rb +9 -0
- data/app/models/concerns/agilibox/polymorphic_id.rb +5 -2
- data/app/models/concerns/agilibox/search.rb +11 -13
- data/app/models/concerns/agilibox/timestamp_helpers.rb +17 -0
- data/app/serializers/agilibox/serializers/base.rb +47 -1
- data/app/serializers/agilibox/serializers/xlsx.rb +25 -23
- data/app/services/agilibox/service.rb +17 -0
- data/app/sms/agilibox/sms/application_sms.rb +42 -0
- data/app/sms/agilibox/sms/message.rb +25 -0
- data/app/sms/agilibox/sms/strategies/amazon_sns.rb +48 -0
- data/app/sms/agilibox/sms/strategies/base.rb +15 -0
- data/app/sms/agilibox/sms/strategies/test.rb +10 -0
- data/app/sms/agilibox/sms.rb +35 -0
- data/app/sorters/agilibox/sorter.rb +26 -0
- data/app/views/agilibox/_flash.html.slim +9 -0
- data/app/views/agilibox/forms/_checkboxes_dropdown.html.slim +14 -0
- data/app/views/agilibox/search/_form.html.slim +1 -6
- data/app/views/agilibox/search/_form_bs3.html.slim +13 -0
- data/app/views/agilibox/search/_form_bs4.html.slim +9 -0
- data/app/views/agilibox/search/_form_bs5.html.slim +8 -0
- data/app/views/kaminari/bootstrap4/_first_page.html.slim +2 -0
- data/app/views/kaminari/bootstrap4/_gap.html.slim +2 -0
- data/app/views/kaminari/bootstrap4/_last_page.html.slim +2 -0
- data/app/views/kaminari/bootstrap4/_next_page.html.slim +2 -0
- data/app/views/kaminari/bootstrap4/_page.html.slim +6 -0
- data/app/views/kaminari/bootstrap4/_paginator.html.slim +12 -0
- data/app/views/kaminari/bootstrap4/_prev_page.html.slim +2 -0
- data/config/cucumber.yml +9 -0
- data/config/locales/actions.en.yml +76 -0
- data/config/locales/actions.fr.yml +76 -0
- data/config/locales/attributes.en.yml +108 -0
- data/config/locales/attributes.fr.yml +108 -0
- data/config/locales/common.en.yml +0 -160
- data/config/locales/common.fr.yml +0 -171
- data/config/locales/dates.fr.yml +8 -0
- data/config/locales/errors.en.yml +5 -0
- data/config/locales/errors.fr.yml +5 -0
- data/db/migrate/20000101000000_enable_agilibox_extensions.rb +7 -0
- data/lib/agilibox/active_model_custom_error_messages.rb +23 -0
- data/lib/agilibox/active_model_type_cast.rb +52 -0
- data/lib/agilibox/config.rb +19 -0
- data/lib/agilibox/core_and_rails_ext.rb +2 -1
- data/lib/agilibox/cucumber_config.rb +53 -0
- data/lib/agilibox/cucumber_helpers/agilibox.rb +2 -0
- data/lib/agilibox/cucumber_helpers/ajax.rb +18 -0
- data/lib/agilibox/cucumber_helpers/apparition.rb +18 -0
- data/lib/agilibox/cucumber_helpers/capybara.rb +12 -0
- data/lib/agilibox/cucumber_helpers/capybara_selectors.rb +7 -0
- data/lib/agilibox/cucumber_helpers/chrome_headless.rb +16 -0
- data/lib/agilibox/cucumber_helpers/common_steps.rb +139 -0
- data/lib/agilibox/cucumber_helpers/cuprite.rb +20 -0
- data/lib/agilibox/cucumber_helpers/database_cleaner.rb +8 -0
- data/lib/agilibox/cucumber_helpers/factory_bot.rb +1 -0
- data/lib/agilibox/cucumber_helpers/rails.rb +1 -0
- data/lib/agilibox/cucumber_helpers/rspec.rb +6 -0
- data/lib/agilibox/cucumber_helpers/screenshots.rb +3 -0
- data/lib/agilibox/cucumber_helpers/select2.rb +23 -0
- data/lib/agilibox/cucumber_helpers/sign_in.rb +17 -0
- data/lib/agilibox/cucumber_helpers/simplecov.rb +1 -0
- data/lib/agilibox/cucumber_helpers/timecop.rb +3 -0
- data/lib/agilibox/cucumber_helpers/turbolinks.rb +34 -0
- data/lib/agilibox/cucumber_helpers/zonebie.rb +4 -0
- data/lib/agilibox/engine.rb +15 -0
- data/lib/agilibox/engine_file.rb +10 -0
- data/lib/agilibox/errors_middleware.rb +50 -0
- data/lib/agilibox/form_back_url.rb +4 -4
- data/lib/agilibox/rspec.rb +29 -0
- data/lib/agilibox/test_helpers.rb +13 -0
- data/lib/agilibox/version.rb +1 -1
- data/lib/tasks/cucumber.rake +76 -0
- metadata +165 -9
- data/app/filters/agilibox/small_data/filter_strategy_by_time_period.rb +0 -37
- data/lib/agilibox/active_record_comma_type_cast.rb +0 -12
@@ -19,9 +19,10 @@ body.modal-open
|
|
19
19
|
|
20
20
|
#modal-body
|
21
21
|
position: relative
|
22
|
-
margin:
|
23
|
-
padding:
|
22
|
+
margin: 50px auto
|
23
|
+
padding: 20px
|
24
24
|
width: auto
|
25
|
+
box-sizing: border-box
|
25
26
|
min-width: 50%
|
26
27
|
max-width: 980px
|
27
28
|
background: white
|
@@ -32,14 +33,21 @@ body.modal-open
|
|
32
33
|
overflow: auto
|
33
34
|
|
34
35
|
#modal-close
|
36
|
+
margin: 0
|
37
|
+
padding: 0
|
38
|
+
display: block
|
39
|
+
font-size: 50px
|
40
|
+
line-height: 50px
|
41
|
+
height: 50px
|
42
|
+
width: 50px
|
43
|
+
position: fixed
|
44
|
+
top: 0px
|
45
|
+
right: 0px
|
35
46
|
color: white
|
36
|
-
position: absolute
|
37
|
-
font-size: 4rem
|
38
|
-
line-height: 0.5rem
|
39
|
-
top: 0.25rem
|
40
|
-
right: 0.25rem
|
41
47
|
border: none
|
48
|
+
outline: none
|
42
49
|
background: none
|
50
|
+
text-align: center
|
43
51
|
|
44
52
|
#modal:not(.closable) #modal-close
|
45
53
|
display: none
|
@@ -17,6 +17,7 @@
|
|
17
17
|
width: auto !important
|
18
18
|
height: auto !important
|
19
19
|
|
20
|
+
.card-title,
|
20
21
|
.panel-title,
|
21
22
|
.context-header,
|
22
23
|
thead *,
|
@@ -41,6 +42,7 @@
|
|
41
42
|
#flash,
|
42
43
|
display: none !important
|
43
44
|
|
45
|
+
.card,
|
44
46
|
.panel,
|
45
47
|
.table,
|
46
48
|
.list-group,
|
@@ -49,14 +51,21 @@
|
|
49
51
|
p,
|
50
52
|
margin: 1em 0 !important
|
51
53
|
|
54
|
+
.card p,
|
55
|
+
.card table,
|
52
56
|
.panel table,
|
53
57
|
.well p,
|
54
58
|
td p,
|
55
59
|
margin: 0 !important
|
56
60
|
|
57
61
|
.well,
|
62
|
+
.card,
|
58
63
|
padding: 0.5em !important
|
59
64
|
|
65
|
+
.card-header,
|
66
|
+
.card-title,
|
67
|
+
.card-body,
|
68
|
+
.card-footer,
|
60
69
|
.panel-heading,
|
61
70
|
.panel-title,
|
62
71
|
.panel-body,
|
@@ -71,4 +80,5 @@
|
|
71
80
|
margin: 0 !important
|
72
81
|
|
73
82
|
.panel-heading .panel-title,
|
83
|
+
.card-header .card-title,
|
74
84
|
padding: 0 !important
|
@@ -0,0 +1,26 @@
|
|
1
|
+
table.default
|
2
|
+
@extend .table
|
3
|
+
@extend .table-bordered
|
4
|
+
@extend .table-hover
|
5
|
+
|
6
|
+
th.actions,
|
7
|
+
td.actions,
|
8
|
+
width: 5em
|
9
|
+
white-space: nowrap
|
10
|
+
text-align: center
|
11
|
+
|
12
|
+
th
|
13
|
+
text-align: center
|
14
|
+
vertical-align: middle !important
|
15
|
+
|
16
|
+
td[class*="date"]
|
17
|
+
width: 8em
|
18
|
+
text-align: center
|
19
|
+
|
20
|
+
td[class*="count"],
|
21
|
+
width: 1em
|
22
|
+
text-align: center
|
23
|
+
|
24
|
+
td[class*="total"],
|
25
|
+
td[class*="amount"],
|
26
|
+
text-align: right
|
@@ -1,8 +1,10 @@
|
|
1
1
|
class Agilibox::SmallData::FiltersController < ::Agilibox::ApplicationController
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
skip_before_action :verify_authenticity_token, raise: false
|
3
|
+
skip_before_action :authenticate_user!, raise: false
|
4
|
+
skip_after_action :verify_authorized, raise: false
|
5
|
+
skip_after_action :verify_policy_scoped, raise: false
|
5
6
|
|
7
|
+
def create
|
6
8
|
filters = ::Agilibox::SmallData::Filter.new(cookies)
|
7
9
|
new_filters = params.fetch(:filters, {}).permit!.to_h
|
8
10
|
filters.merge new_filters
|
@@ -23,8 +25,9 @@ class Agilibox::SmallData::FiltersController < ::Agilibox::ApplicationController
|
|
23
25
|
url = [
|
24
26
|
params[:form_url],
|
25
27
|
request.referer,
|
26
|
-
|
27
|
-
|
28
|
+
main_app.try(:root_path),
|
29
|
+
"/",
|
30
|
+
].find(&:present?)
|
28
31
|
|
29
32
|
# Delete page param
|
30
33
|
base, query_string = url.split("?")
|
@@ -32,5 +35,4 @@ class Agilibox::SmallData::FiltersController < ::Agilibox::ApplicationController
|
|
32
35
|
query_string = "?#{query_string}" if query_string.present?
|
33
36
|
base + query_string
|
34
37
|
end
|
35
|
-
|
36
38
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Agilibox::ApiControllerConcern
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
private
|
5
|
+
|
6
|
+
def render_json(json = {}, options = {})
|
7
|
+
json.reverse_merge!(current_user: current_user)
|
8
|
+
options.reverse_merge!(current_user: current_user)
|
9
|
+
|
10
|
+
json = Agilibox::MiniModelSerializer::Serialize.call(json, options)
|
11
|
+
|
12
|
+
render options.merge(json: json)
|
13
|
+
end
|
14
|
+
|
15
|
+
def render_json_error(any_object, options = {})
|
16
|
+
if any_object.is_a?(ActiveModel::Validations)
|
17
|
+
json = {
|
18
|
+
:error => json_error_string_for_model(any_object),
|
19
|
+
:model_errors => json_errors_hash_for_model(any_object),
|
20
|
+
}
|
21
|
+
elsif any_object.is_a?(String)
|
22
|
+
json = {error: any_object}
|
23
|
+
else
|
24
|
+
json = any_object
|
25
|
+
end
|
26
|
+
|
27
|
+
options[:status] ||= :unprocessable_entity
|
28
|
+
|
29
|
+
render_json(json, options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def json_errors_hash_for_model(object)
|
33
|
+
object.errors
|
34
|
+
.to_hash
|
35
|
+
.map { |a, m| [a, message: m.first, full_message: object.errors.full_message(a, m.first)] }
|
36
|
+
.uniq(&:first)
|
37
|
+
.to_h
|
38
|
+
end
|
39
|
+
|
40
|
+
def json_error_string_for_model(object)
|
41
|
+
json_errors_hash_for_model(object).values.pluck(:full_message).join(", ")
|
42
|
+
end
|
43
|
+
|
44
|
+
def render_not_found
|
45
|
+
render_json_error t("errors.not_found"), status: :not_found
|
46
|
+
end
|
47
|
+
|
48
|
+
def render_forbidden
|
49
|
+
render_json_error t("errors.forbidden"), status: :forbidden
|
50
|
+
end
|
51
|
+
|
52
|
+
def render_unauthorized
|
53
|
+
render_json_error t("errors.unauthorized"), status: :unauthorized
|
54
|
+
end
|
55
|
+
|
56
|
+
def render_forbidden_or_unauthorized
|
57
|
+
current_user ? render_forbidden : render_unauthorized
|
58
|
+
end
|
59
|
+
|
60
|
+
included do |controller|
|
61
|
+
if controller < ActionController::Rescue
|
62
|
+
if defined?(Pundit::NotAuthorizedError)
|
63
|
+
rescue_from Pundit::NotAuthorizedError, with: :render_forbidden_or_unauthorized
|
64
|
+
end
|
65
|
+
|
66
|
+
rescue_from ActiveRecord::RecordNotFound, with: :render_not_found
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -3,16 +3,24 @@ module Agilibox::BackUrlConcern
|
|
3
3
|
|
4
4
|
private
|
5
5
|
|
6
|
-
def default_back_url
|
6
|
+
def default_back_url
|
7
|
+
end
|
7
8
|
|
8
9
|
def back_url
|
9
|
-
[
|
10
|
+
url = [
|
10
11
|
params[:back_url],
|
11
12
|
request.referer,
|
12
13
|
default_back_url,
|
13
|
-
main_app.root_path,
|
14
|
+
main_app.try(:root_path),
|
14
15
|
"/",
|
15
|
-
].
|
16
|
-
end
|
16
|
+
].find(&:present?)
|
17
17
|
|
18
|
+
uri = URI.parse(url)
|
19
|
+
uri.host = nil
|
20
|
+
uri.port = nil
|
21
|
+
uri.scheme = nil
|
22
|
+
uri.user = nil
|
23
|
+
uri.password = nil
|
24
|
+
uri.to_s
|
25
|
+
end
|
18
26
|
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
class Agilibox::Email
|
2
|
+
include ActiveModel::Model
|
3
|
+
include Agilibox::ModelToS
|
4
|
+
include Agilibox::ModelI18n
|
5
|
+
|
6
|
+
validates :to, presence: true
|
7
|
+
validates :subject, presence: true
|
8
|
+
validates :body, presence: true
|
9
|
+
|
10
|
+
validate :validate_addr_formats
|
11
|
+
|
12
|
+
attr_accessor(
|
13
|
+
:current_user,
|
14
|
+
:from,
|
15
|
+
:reply_to,
|
16
|
+
:to,
|
17
|
+
:cc,
|
18
|
+
:subject,
|
19
|
+
:body,
|
20
|
+
:attachments,
|
21
|
+
)
|
22
|
+
|
23
|
+
def initialize(*)
|
24
|
+
super
|
25
|
+
assign_default_values
|
26
|
+
end
|
27
|
+
|
28
|
+
def attachment_names
|
29
|
+
attachments.keys.join(", ")
|
30
|
+
end
|
31
|
+
|
32
|
+
def validate_and_deliver
|
33
|
+
ok = valid?
|
34
|
+
deliver if ok
|
35
|
+
ok
|
36
|
+
end
|
37
|
+
|
38
|
+
def data
|
39
|
+
{
|
40
|
+
:from => from,
|
41
|
+
:reply_to => reply_to,
|
42
|
+
:to => to,
|
43
|
+
:cc => cc,
|
44
|
+
:subject => subject,
|
45
|
+
:body => body,
|
46
|
+
:attachments => attachments,
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def deliver
|
51
|
+
deliver_now
|
52
|
+
end
|
53
|
+
|
54
|
+
def deliver_now
|
55
|
+
Agilibox::GenericMailer.generic_email(data).deliver_now
|
56
|
+
end
|
57
|
+
|
58
|
+
def deliver_later
|
59
|
+
Agilibox::GenericMailer.generic_email(data).deliver_later
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def assign_default_values
|
65
|
+
self.from ||= default_from
|
66
|
+
self.reply_to ||= default_reply_to
|
67
|
+
self.to ||= default_to
|
68
|
+
self.cc ||= default_cc
|
69
|
+
self.subject ||= default_subject
|
70
|
+
self.body ||= default_body
|
71
|
+
self.attachments ||= default_attachments
|
72
|
+
end
|
73
|
+
|
74
|
+
def default_from
|
75
|
+
end
|
76
|
+
|
77
|
+
def default_reply_to
|
78
|
+
"#{current_user} <#{current_user.email}>" if current_user
|
79
|
+
end
|
80
|
+
|
81
|
+
def default_to
|
82
|
+
end
|
83
|
+
|
84
|
+
def default_cc
|
85
|
+
end
|
86
|
+
|
87
|
+
def default_subject
|
88
|
+
end
|
89
|
+
|
90
|
+
def default_body
|
91
|
+
end
|
92
|
+
|
93
|
+
def default_attachments
|
94
|
+
{}
|
95
|
+
end
|
96
|
+
|
97
|
+
def validate_addr_formats
|
98
|
+
validate_addr_formats_for(:from)
|
99
|
+
validate_addr_formats_for(:reply_to)
|
100
|
+
validate_addr_formats_for(:to)
|
101
|
+
validate_addr_formats_for(:cc)
|
102
|
+
end
|
103
|
+
|
104
|
+
def validate_addr_formats_for(attr)
|
105
|
+
require "mail"
|
106
|
+
|
107
|
+
string = public_send(attr).to_s
|
108
|
+
return if string.blank?
|
109
|
+
addrs = Mail.new(to: string).to_addrs
|
110
|
+
return true if addrs.any? && addrs.all? { |addr| URI::MailTo::EMAIL_REGEXP.match?(addr) }
|
111
|
+
errors.add(attr, :invalid)
|
112
|
+
end
|
113
|
+
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
class Agilibox::SmallData::Filter
|
2
|
+
include ActiveModel::Model
|
3
|
+
|
2
4
|
STRATEGIES = {}
|
3
5
|
|
4
6
|
attr_reader :jar
|
@@ -8,7 +10,7 @@ class Agilibox::SmallData::Filter
|
|
8
10
|
end
|
9
11
|
|
10
12
|
def strategies
|
11
|
-
self.class::STRATEGIES
|
13
|
+
self.class::STRATEGIES.with_indifferent_access
|
12
14
|
end
|
13
15
|
|
14
16
|
def apply(query)
|
@@ -42,6 +44,10 @@ class Agilibox::SmallData::Filter
|
|
42
44
|
end
|
43
45
|
end
|
44
46
|
|
47
|
+
def respond_to_missing?(method, *)
|
48
|
+
super || strategies.key?(method.to_s) || strategies.key?(method.to_s.chomp("="))
|
49
|
+
end
|
50
|
+
|
45
51
|
def read
|
46
52
|
JSON.parse jar["filters"].to_s
|
47
53
|
rescue JSON::ParserError
|
@@ -56,6 +62,18 @@ class Agilibox::SmallData::Filter
|
|
56
62
|
write read.merge(new_filters)
|
57
63
|
end
|
58
64
|
|
65
|
+
def actives_count
|
66
|
+
read.count { |k, v| strategies.key?(k.to_s) && v.present? }
|
67
|
+
end
|
68
|
+
|
69
|
+
def any?
|
70
|
+
actives_count.positive?
|
71
|
+
end
|
72
|
+
|
73
|
+
def empty?
|
74
|
+
!any?
|
75
|
+
end
|
76
|
+
|
59
77
|
private
|
60
78
|
|
61
79
|
def get(key)
|
@@ -1,6 +1,7 @@
|
|
1
1
|
class Agilibox::SmallData::FilterStrategyByDateBegin < ::Agilibox::SmallData::FilterStrategyByKeyValue
|
2
2
|
def apply(query, value)
|
3
|
-
value =
|
4
|
-
|
3
|
+
value = Date.parse(value)
|
4
|
+
column = column_for(query)
|
5
|
+
query.where("#{column} >= ?", value)
|
5
6
|
end
|
6
7
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
class Agilibox::SmallData::FilterStrategyByDateEnd < ::Agilibox::SmallData::FilterStrategyByKeyValue
|
2
2
|
def apply(query, value)
|
3
|
-
value =
|
4
|
-
|
3
|
+
value = Date.parse(value)
|
4
|
+
column = column_for(query)
|
5
|
+
query.where("#{column} <= ?", value)
|
5
6
|
end
|
6
7
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
class Agilibox::SmallData::FilterStrategyByDateOrDatetimePeriod < ::Agilibox::SmallData::FilterStrategyByKeyValue
|
2
|
+
def initialize(*)
|
3
|
+
if instance_of?(Agilibox::SmallData::FilterStrategyByDateOrDatetimePeriod)
|
4
|
+
raise "please use FilterStrategyByDatePeriod or FilterStrategyByDatetimePeriod"
|
5
|
+
end
|
6
|
+
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def apply(query, value) # rubocop:disable Metrics/MethodLength
|
11
|
+
value = value.to_s
|
12
|
+
|
13
|
+
if value == "today"
|
14
|
+
a = now
|
15
|
+
b = now
|
16
|
+
elsif value == "yesterday"
|
17
|
+
a = (now - 1.day)
|
18
|
+
b = (now - 1.day)
|
19
|
+
elsif value == "this_week"
|
20
|
+
a = now.beginning_of_week
|
21
|
+
b = now.end_of_week
|
22
|
+
elsif value == "this_month"
|
23
|
+
a = now.beginning_of_month
|
24
|
+
b = now.end_of_month
|
25
|
+
elsif value == "this_year"
|
26
|
+
a = now.beginning_of_year
|
27
|
+
b = now.end_of_year
|
28
|
+
elsif value == "last_week"
|
29
|
+
a = (now - 1.week).beginning_of_week
|
30
|
+
b = (now - 1.week).end_of_week
|
31
|
+
elsif value == "last_month"
|
32
|
+
a = (now - 1.month).beginning_of_month
|
33
|
+
b = (now - 1.month).end_of_month
|
34
|
+
elsif value == "last_year"
|
35
|
+
a = (now - 1.year).beginning_of_year
|
36
|
+
b = (now - 1.year).end_of_year
|
37
|
+
elsif (m = value.match(/last\.([0-9]+)\.(days?|weeks?|months?|years?)/))
|
38
|
+
a = now - m[1].to_i.public_send(m[2])
|
39
|
+
b = now
|
40
|
+
else
|
41
|
+
return query
|
42
|
+
end
|
43
|
+
|
44
|
+
if now.is_a?(Time)
|
45
|
+
a = a.beginning_of_day if a
|
46
|
+
b = b.end_of_day if b
|
47
|
+
end
|
48
|
+
|
49
|
+
column = column_for(query)
|
50
|
+
|
51
|
+
query = query.where("#{column} >= ?", a) if a
|
52
|
+
query = query.where("#{column} <= ?", b) if b
|
53
|
+
|
54
|
+
query
|
55
|
+
end
|
56
|
+
end
|
@@ -2,15 +2,26 @@ class Agilibox::SmallData::FilterStrategyByKeyValue < ::Agilibox::SmallData::Fil
|
|
2
2
|
attr_reader :key
|
3
3
|
|
4
4
|
def initialize(key = nil)
|
5
|
+
super()
|
5
6
|
@key = key
|
6
7
|
end
|
7
8
|
|
8
9
|
def apply(query, value)
|
9
10
|
value = true if value == "true"
|
10
11
|
value = false if value == "false"
|
11
|
-
value = nil if value == "nil"
|
12
|
-
value = nil if value == "null"
|
13
12
|
|
14
|
-
|
13
|
+
column = column_for(query)
|
14
|
+
|
15
|
+
if value.to_s.in?(%w(nil null))
|
16
|
+
query.where("#{column} IS NULL")
|
17
|
+
elsif value.to_s.in?(%w(not_nil not_null))
|
18
|
+
query.where("#{column} IS NOT NULL")
|
19
|
+
else
|
20
|
+
query.where("#{column} = ?", value)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def column_for(query)
|
25
|
+
key.is_a?(Symbol) ? "#{query.model.table_name}.#{key}" : key.to_s
|
15
26
|
end
|
16
27
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Agilibox::SmallData::FilterStrategyByKeyValues < ::Agilibox::SmallData::FilterStrategyByKeyValue
|
2
|
+
def apply(query, value)
|
3
|
+
value = value.split if value.is_a?(String)
|
4
|
+
value = value.select(&:present?)
|
5
|
+
column = column_for(query)
|
6
|
+
|
7
|
+
if value.any?
|
8
|
+
query.where("#{column} IN (?)", value)
|
9
|
+
else
|
10
|
+
query
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Agilibox::MiniFormObject < SimpleDelegator
|
2
|
+
include ActiveModel::Validations
|
3
|
+
extend Forwardable
|
4
|
+
|
5
|
+
def valid?
|
6
|
+
__getobj__.valid?
|
7
|
+
run_validations!
|
8
|
+
end
|
9
|
+
|
10
|
+
def invalid?
|
11
|
+
!valid?
|
12
|
+
end
|
13
|
+
|
14
|
+
alias validate valid?
|
15
|
+
|
16
|
+
def validate!
|
17
|
+
valid? || raise_validation_error
|
18
|
+
end
|
19
|
+
|
20
|
+
def_delegators :__getobj__, :errors
|
21
|
+
|
22
|
+
def save
|
23
|
+
valid? && __getobj__.save
|
24
|
+
end
|
25
|
+
|
26
|
+
def save!
|
27
|
+
validate! && __getobj__.save!
|
28
|
+
end
|
29
|
+
end
|
@@ -2,7 +2,9 @@ module Agilibox::AllHelpers
|
|
2
2
|
include Agilibox::BootstrapHelper
|
3
3
|
include Agilibox::ButtonHelper
|
4
4
|
include Agilibox::FiltersHelper
|
5
|
+
include Agilibox::FontAwesomeHelper
|
5
6
|
include Agilibox::FormHelper
|
7
|
+
include Agilibox::I18nHelper
|
6
8
|
include Agilibox::LinkHelper
|
7
9
|
include Agilibox::PaginationHelper
|
8
10
|
include Agilibox::RoutesHelper
|
@@ -1,6 +1,65 @@
|
|
1
1
|
module Agilibox::BootstrapHelper
|
2
|
-
|
3
|
-
|
4
|
-
|
2
|
+
class << self
|
3
|
+
attr_writer :card_classes
|
4
|
+
|
5
|
+
def card_classes
|
6
|
+
@card_classes ||= {
|
7
|
+
:card => "card",
|
8
|
+
:header => "card-header",
|
9
|
+
:body => "card-body",
|
10
|
+
:footer => "card-footer",
|
11
|
+
}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def bs_progress_bar(percentage)
|
16
|
+
tag.div(class: "progress") do
|
17
|
+
tag.div(class: "progress-bar", style: "width:#{percentage}%") do
|
18
|
+
"#{percentage}%"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def bs_card( # rubocop:disable Metrics/ParameterLists
|
24
|
+
header: nil,
|
25
|
+
body: true,
|
26
|
+
footer: nil,
|
27
|
+
card_tag: :div,
|
28
|
+
header_tag: :div,
|
29
|
+
body_tag: :div,
|
30
|
+
footer_tag: :div,
|
31
|
+
card_class: nil,
|
32
|
+
header_class: nil,
|
33
|
+
body_class: nil,
|
34
|
+
footer_class: nil,
|
35
|
+
&block
|
36
|
+
)
|
37
|
+
global_classes = Agilibox::BootstrapHelper.card_classes
|
38
|
+
card_classes = ([global_classes[:card]] + card_class.to_s.split).compact.sort
|
39
|
+
header_classes = ([global_classes[:header]] + header_class.to_s.split).compact.sort
|
40
|
+
body_classes = ([global_classes[:body]] + body_class.to_s.split).compact.sort
|
41
|
+
footer_classes = ([global_classes[:footer]] + footer_class.to_s.split).compact.sort
|
42
|
+
|
43
|
+
if header
|
44
|
+
header_html = content_tag(header_tag, class: header_classes) { header }
|
45
|
+
else
|
46
|
+
header_html = "".html_safe
|
47
|
+
end
|
48
|
+
|
49
|
+
if body
|
50
|
+
body_html = content_tag(body_tag, class: body_classes) { capture(&block) }
|
51
|
+
else
|
52
|
+
body_html = capture(&block)
|
53
|
+
end
|
54
|
+
|
55
|
+
if footer
|
56
|
+
footer_html = content_tag(footer_tag, class: footer_classes) { footer }
|
57
|
+
else
|
58
|
+
footer_html = "".html_safe
|
59
|
+
end
|
60
|
+
|
61
|
+
content_tag(card_tag, class: card_classes) do
|
62
|
+
header_html + body_html + footer_html
|
63
|
+
end
|
5
64
|
end
|
6
65
|
end
|