web47core 3.2.9 → 3.2.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/app/helpers/core_breadcrumb_helper.rb +11 -0
  3. data/app/helpers/core_card_nav_items_helper.rb +60 -0
  4. data/app/helpers/core_dropdown_helper.rb +89 -0
  5. data/app/helpers/core_floating_action_button_helper.rb +82 -0
  6. data/app/helpers/core_html5_form_helper.rb +163 -0
  7. data/app/helpers/core_job_state_helper.rb +21 -0
  8. data/app/helpers/core_remix_icon_helper.rb +23 -0
  9. data/app/helpers/core_select_two_helper.rb +73 -0
  10. data/app/helpers/model_modal_helper.rb +1 -1
  11. data/app/views/cron_servers/index.html.haml +28 -0
  12. data/app/views/cron_tabs/edit.html.haml +17 -0
  13. data/app/views/cron_tabs/index.html.haml +35 -0
  14. data/app/views/delayed_job_metrics/index.html.haml +22 -1
  15. data/app/views/delayed_job_workers/index.html.haml +30 -1
  16. data/app/views/delayed_jobs/index.html.haml +28 -1
  17. data/app/views/delayed_jobs/show.html.haml +55 -1
  18. data/app/views/system_configurations/edit.html.haml +14 -1
  19. data/app/views/system_configurations/show.html.haml +18 -1
  20. data/lib/app/controllers/concerns/core_controller.rb +14 -0
  21. data/lib/app/controllers/concerns/core_cron_servers_controller.rb +31 -0
  22. data/lib/app/controllers/concerns/core_cron_tabs_controller.rb +41 -0
  23. data/lib/app/controllers/concerns/core_delayed_job_metrics_controller.rb +2 -2
  24. data/lib/app/jobs/cron/trim_command_jobs.rb +1 -1
  25. data/lib/app/models/concerns/class_name.rb +26 -0
  26. data/lib/app/models/concerns/tag_able.rb +34 -0
  27. data/lib/app/models/tag.rb +21 -0
  28. data/lib/app/models/user_audit_log.rb +2 -2
  29. data/lib/web47core/version.rb +1 -1
  30. data/lib/web47core.rb +5 -1
  31. metadata +18 -25
  32. data/app/views/admin/cron/edit.html.haml +0 -1
  33. data/app/views/admin/cron/index.html.haml +0 -1
  34. data/app/views/admin/delayed_jobs/index.html.haml +0 -1
  35. data/app/views/admin/delayed_jobs/show.html.haml +0 -1
  36. data/app/views/admin/system_configurations/edit.html.haml +0 -1
  37. data/app/views/admin/system_configurations/show.html.haml +0 -1
  38. data/app/views/cron/_edit.html.haml +0 -19
  39. data/app/views/cron/_index.html.haml +0 -77
  40. data/app/views/cron/edit.html.haml +0 -1
  41. data/app/views/cron/index.html.haml +0 -1
  42. data/app/views/delayed_job_metrics/_index.html.haml +0 -27
  43. data/app/views/delayed_job_workers/_index.html.haml +0 -27
  44. data/app/views/delayed_jobs/_index.html.haml +0 -49
  45. data/app/views/delayed_jobs/_show.html.haml +0 -60
  46. data/app/views/stack/cron/edit.html.haml +0 -1
  47. data/app/views/stack/cron/index.html.haml +0 -1
  48. data/app/views/stack/delayed_jobs/index.html.haml +0 -1
  49. data/app/views/stack/delayed_jobs/show.html.haml +0 -1
  50. data/app/views/stack/system_configurations/edit.html.haml +0 -1
  51. data/app/views/stack/system_configurations/show.html.haml +0 -1
  52. data/app/views/system_configurations/_edit.html.haml +0 -15
  53. data/app/views/system_configurations/_show.html.haml +0 -22
  54. data/lib/app/controllers/concerns/core_cron_controller.rb +0 -79
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d49fb16ecfe61d18dbc79f37425422d9e7655db689aae499816be0d145c3922a
4
- data.tar.gz: 0125066bb00ecfdb0b423d09493fbd34ed457b8805a7e400f82b0bcf5d78458f
3
+ metadata.gz: 327cae83d882b83a5d99350fdf424929fad212fcf77f2d611ad6bb0fd3ac3516
4
+ data.tar.gz: f6ee9926c857f10177ea4b4357b5d70c6dca7de71538e804f7f3ccf4b6165e50
5
5
  SHA512:
6
- metadata.gz: f64b6ef1ff5cb4c9bdd72a87d2f4fc3a6fe04a29959ef16704bb3cd3d9b5099dbdc3eb2b43122fec0c47d57d3457aacba57ae5dc8bda37377d3cdb7b07d1127a
7
- data.tar.gz: ecc76a77d5fda33705260ef6de28b34bca7bdc456587b9124b87c3463e53318e1529a9aa298a513140f9de4d14a2a880341f975600217bec93ec7769263d5a08
6
+ metadata.gz: 062d8c47cabbbeb0bf02ad9c2884cadff4621981cd106340f2111a4c2520d395affa20691de161e6b136007da72e54f78b2100e20640a22df7ac18d6dff7ff95
7
+ data.tar.gz: 12c2bc3603918649caef8b9086c564eb056a5683deeeacf63d0a8b91673f6ae5a9e7a38d5ef8bb1b5bf7beb76fa57eb4edc8a0d39db1f149f0525308792cb1df
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Render avatar based on a user's profile
4
+ module CoreBreadcrumbHelper
5
+ # @abstract Render a single step for the breadcrumb
6
+ def breadcrumb_step(path, title)
7
+ content_tag(:li, class: "breadcrumb-item active") do
8
+ concat(content_tag(:a, href: path) { title })
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Help with nav items at the top of cards
5
+ #
6
+ module CoreCardNavItemsHelper
7
+ # @abstract Yield the main block for card nav items
8
+ def card_nav_items(&block)
9
+ content_tag(:ul, class: 'nav nav-pills float-end') do
10
+ yield block
11
+ end
12
+ end
13
+
14
+ # @abstract Link to restart, replay a given object
15
+ # @param [Object] obj - The object to operate on, for permission checks
16
+ # @param [String] path - The path/URL for the action
17
+ # @param [String] confirm - Override the default confirmation
18
+ # @param [String] title - Override the default title of the button
19
+ # @return HTML - The HTML for the given tag
20
+ def restart_nav_link(obj, path, confirm: nil, title: 'Restart')
21
+ return unless can?(:edit, obj)
22
+
23
+ confirm ||= "Are you sure you want to restart this #{obj.class_title}?"
24
+ card_nav_item_link(path, title, 'delete-bin', confirm: confirm)
25
+ end
26
+
27
+ def edit_nav_link(obj, path, title: 'Edit')
28
+ return unless can?(:edit, obj)
29
+
30
+ card_nav_item_link(path, title, 'edit')
31
+ end
32
+
33
+ # @abstract Link to delete an object
34
+ # @return HTML - The HTML for the given tag
35
+ def delete_nav_link(obj, path, confirm: nil, title: 'Delete')
36
+ return unless can?(:manage, obj)
37
+
38
+ confirm ||= "Are you sure you want to delete this #{obj.class_title}?"
39
+ card_nav_item_link(path, title, 'delete-bin', confirm: confirm, method: :delete, btn_class: 'btn-danger')
40
+ end
41
+
42
+ # @abstract The work horse for the card nav item
43
+ # @param [String] path - The path/URL for the action
44
+ # @param [String] title - The title of the action
45
+ # @param [String] icon_name - Name of the icon to render
46
+ # @param [String] confirm - If the action requires confirmation before completing
47
+ # @param [String|Symbol] method - The HTTP method to use for the link, default is :get
48
+ # @param [String] btn_class - The class of button that should be used, btn-primary is the default
49
+ # @return HTML - The HTML for the given tag
50
+ def card_nav_item_link(path, title, icon_name, confirm: nil, method: :get, btn_class: 'btn-primary')
51
+ data = { method: method }
52
+ data[:confirm] = confirm if confirm.present?
53
+ content_tag(:li, class: 'nav-item me-2') do
54
+ link_to path, class: "#{btn_class} btn nav-link active", data: data do
55
+ concat(remix_icon(icon_name, classes: ['me-2'], tooltip_text: title))
56
+ concat(content_tag(:span, class: 'd-none d-md-inline') { title })
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Helpful methods rendering tables
5
+ #
6
+ module CoreDropdownHelper
7
+ # @abstract Drop down menu for actions
8
+ def dropdown_menu(&block)
9
+ content_tag(:div, class: 'dropdown') do
10
+ concat(content_tag(:button,
11
+ class: 'btn p-0 dropdown-toggle hide-arrow',
12
+ type: :button,
13
+ data: { 'bs-toggle': :dropdown }) do
14
+ concat(remix_icon('more-2'))
15
+ end)
16
+ concat(content_tag(:div, class: 'dropdown-menu') { yield block })
17
+ end
18
+ end
19
+
20
+ def dropdown_item(path, icon_name, action_name, method: :get, confirm: nil, classes: [])
21
+ data = { method: method }
22
+ data[:confirm] = confirm if confirm.present?
23
+ classes << 'dropdown-item'
24
+ content_tag(:a, class: classes.join(' '), href: path, data: data) do
25
+ concat(menu_remix_icon(icon_name))
26
+ concat(action_name)
27
+ end
28
+ end
29
+
30
+ def demote_dropdown_item(obj, path)
31
+ return unless can? :edit, obj
32
+
33
+ dropdown_item(path, 'thumb-down', 'Demote')
34
+ end
35
+
36
+ def info_dropdown_item(obj, path, name: 'Info')
37
+ return unless can? :view, obj
38
+
39
+ dropdown_item(path, 'information', name)
40
+ end
41
+
42
+ def dashboard_dropdown_item(obj, path, name: 'Show')
43
+ return unless can? :view, obj
44
+
45
+ dropdown_item(path, 'dashboard', name)
46
+ end
47
+
48
+ def jobs_dropdown_item(obj, path, name: 'Jobs')
49
+ return unless can? :view, obj
50
+
51
+ dropdown_item(path, 'file-list', name)
52
+ end
53
+
54
+ def current_job_dropdown_item(obj, path)
55
+ return unless can? :view, obj
56
+
57
+ dropdown_item(path, 'hourglass-2', 'Current Job')
58
+ end
59
+
60
+ def edit_dropdown_item(obj, path)
61
+ return unless can? :edit, obj
62
+
63
+ dropdown_item(path, 'pencil', 'Edit')
64
+ end
65
+
66
+ def delete_dropdown_item(obj, path)
67
+ return unless can? :delete, obj
68
+
69
+ dropdown_item(path, 'delete-bin', 'Delete', method: :delete, confirm: 'are you sure?')
70
+ end
71
+
72
+ def cancel_dropdown_item(obj, path, confirm: 'Are you sure?', method: :delete)
73
+ return unless can? :edit, obj
74
+
75
+ dropdown_item(path, 'close-circle', 'Cancel', method: method, confirm: confirm, classes: %w(text-danger))
76
+ end
77
+
78
+ def run_dropdown_item(obj, path)
79
+ return unless can? :read, obj
80
+
81
+ dropdown_item(path, 'run', 'Run', method: :post)
82
+ end
83
+
84
+ def replay_dropdown_item(obj, path, confirm: nil, title: 'Replay')
85
+ return unless can? :read, obj
86
+
87
+ dropdown_item(path, 'reset-left', title, confirm: confirm)
88
+ end
89
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Draw floating action buttons for the page
5
+ #
6
+ module CoreFloatingActionButtonHelper
7
+ # Single action buttons
8
+ # @abstract Render a creation FAB
9
+ def add_fab_button(clazz, path)
10
+ return unless can?(:create, clazz)
11
+
12
+ floating_action_button(path, title: "Add #{clazz.to_s.humanize}", icon_name: 'add-circle')
13
+ end
14
+
15
+ # @abstract Render a creation FAB
16
+ def edit_fab_button(obj, path)
17
+ return unless can?(:edit, obj)
18
+
19
+ floating_action_button(path, title: 'Edit', icon_name: 'edit')
20
+ end
21
+
22
+ # @abstract Render a floating action button
23
+ def floating_action_button(path, title: 'Add', icon_name: 'add-circle')
24
+ content_tag(:div, class: 'btn-fab') do
25
+ concat(content_tag(:a, class: 'btn btn-primary btn-large rounded-circle', href: path, title: title) do
26
+ concat(content_tag(:i, class: "ri-#{icon_name}-line ri-36px") {})
27
+ end)
28
+ end
29
+ end
30
+
31
+ # Multiple action buttons
32
+ # @abstract Build the FAB that has a "more" icon and yields the buttons
33
+ def multiple_floating_action_button(&block)
34
+ content_tag(:div, class: 'btn-fab') do
35
+ concat(content_tag(:button, class: 'btn btn-primary btn-lg rounded-circle',
36
+ type: :button,
37
+ 'data-bs-toggle': :dropdown,
38
+ haspopup: true,
39
+ 'aria-expanded': false) do
40
+ remix_icon('more-2', classes: %w[ri-24px])
41
+ end)
42
+ concat(content_tag(:ul, class: 'dropdown-menu') { yield block })
43
+ end
44
+ end
45
+
46
+ def delete_floating_action_button(clazz, path, title: 'Delete', icon_name: 'delete-bin', confirm: 'Are you sure you want to delete this item?')
47
+ return unless can?(:destroy, clazz)
48
+
49
+ floating_action_link(path, title, icon_name, confirm: confirm, method: :delete)
50
+ end
51
+
52
+ # @abstract add floating action
53
+ def add_floating_action_link(clazz, path)
54
+ return unless can?(:create, clazz)
55
+
56
+ floating_action_link(path, "New #{clazz.to_s.humanize}", 'add-circle')
57
+ end
58
+
59
+ # @abstract Edit floating action, if the user is allowed to edit the object
60
+ def edit_floating_action_link(clazz, path)
61
+ return unless can?(:edit, clazz)
62
+
63
+ floating_action_link(path, 'Edit', 'edit')
64
+ end
65
+
66
+ # @abstract Edit floating action, if the user is allowed to edit the object
67
+ def refresh_floating_action_link(clazz, path, title: 'Refresh', icon_name: 'refresh', confirm: 'Are you sure you want to refresh this item?')
68
+ return unless can?(:edit, clazz)
69
+
70
+ floating_action_link(path, title, icon_name, confirm: confirm)
71
+ end
72
+
73
+ def floating_action_link(path, title, icon_name, confirm: nil, method: :get)
74
+ data = { confirm: confirm, title: title, method: method }
75
+ content_tag(:li) do
76
+ concat(content_tag(:a, class: 'dropdown-item', href: path, data: data) do
77
+ concat(menu_remix_icon(icon_name))
78
+ concat(content_tag(:span) { title })
79
+ end)
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Helpful methods HTML5 elements in a form
5
+ #
6
+ module CoreHtml5FormHelper
7
+ # @abstract Render a password field
8
+ # @todo Combine this with the html5_text_field, the only thing different is the field type
9
+ def html5_password(model, field, options = {})
10
+ classes = options[:classes] || %w[col-sm-12 col-lg-6]
11
+ value = model.send(field)
12
+ options[:type] = :password
13
+ options[:place_holder] ||= mask_value(value)
14
+ tag_options = html5_text_field_options(model, field, options)
15
+ content_tag(:div, class: (%w[form-floating form-floating-outline mb-3 col] + classes).join(' ')) do
16
+ concat(tag(:input, tag_options))
17
+ concat(form_label_tag(model, field, value, options))
18
+ end
19
+ end
20
+
21
+ def html5_text_field(model, field, options = {})
22
+ classes = options[:classes] || %w[col-sm-12 col-lg-6]
23
+ value = model.send(field)
24
+ options[:type] ||= :text
25
+ options[:value] = value
26
+ options[:disabled] ||= false
27
+ tag_options = html5_text_field_options(model, field, options)
28
+ content_tag(:div, class: (%w[form-floating form-floating-outline mb-3 col] + classes).join(' ')) do
29
+ concat(tag(:input, tag_options))
30
+ concat(html5_label_tag(model, field, value, options))
31
+ end
32
+ end
33
+
34
+ def html5_label_tag(model, field, value, options = {})
35
+ # dont do a label if we are in default browser mode
36
+ return if options[:no_label]
37
+ return if options[:input_classes].present? && options[:input_classes].include?('browser-default')
38
+
39
+ # or if we have a prompt with now value
40
+ place_holder = field_placeholder_text(model, field, default: options[:placeholder])
41
+ return if place_holder.blank? && value.blank? && options[:prompt].present?
42
+
43
+ error = model.errors[field]
44
+ key = "ui_form.#{model.class.to_s.underscore}.labels.#{field}"
45
+ classes = value.nil? && place_holder.blank? ? '' : 'active'
46
+ classes += error.present? ? ' invalid red-text' : ' valid'
47
+ options[:class] = classes
48
+ options[:for] = html5_field_id(model, field, options)
49
+ options['data-error'] = error.join(', ') if error.present?
50
+ content_tag(:label, options) do
51
+ concat(I18n.exists?(key) ? I18n.t(key) : field.to_s.humanize)
52
+ concat(" #{error.join(', ')}") if error.present?
53
+ end
54
+ end
55
+
56
+ def html5_text_field_options(model, field, options)
57
+ hint_key = "ui_form.#{model.class.to_s.underscore}.hints.#{field}"
58
+ if I18n.exists?(hint_key)
59
+ classes = %w[form-control validate tooltipped]
60
+ options[:data] = { tooltip: I18n.t(hint_key), position: :top }
61
+ else
62
+ classes = %w[form-control validate]
63
+ end
64
+ classes += options[:input_classes] if options[:input_classes].present?
65
+ options[:name] = html5_field_name(model, field, options)
66
+ options[:id] = html5_field_id(model, field, options)
67
+ place_holder = options[:place_holder] || html5_field_place_holder(model, field)
68
+ if place_holder.present?
69
+ classes << 'active'
70
+ options[:placeholder] = place_holder
71
+ end
72
+ classes << 'active' if options[:value].present?
73
+ options[:class] = classes.uniq
74
+ options
75
+ end
76
+
77
+ def html5_field_name(model, field, options = {})
78
+ return options[:form_name] if options[:form_name].present?
79
+
80
+ # TODO: Need to handle the other side of the 1:M use case where
81
+ # the field name needs to end in _ids, not _id.
82
+ field = "#{field}_id" if model.class.reflect_on_association(field).present?
83
+ if options[:index].present?
84
+ if options[:array_name].present?
85
+ if options[:base_name].present?
86
+ "#{options[:base_name]}[#{options[:array_name]}[#{options[:index]}][#{field}]]"
87
+ else
88
+ "#{options[:array_name]}[#{options[:index]}][#{field}]"
89
+ end
90
+ else
91
+ "#{model.class.to_s.underscore}[#{options[:index]}][#{field}]"
92
+ end
93
+ else
94
+ "#{model.class.to_s.underscore}[#{field}]"
95
+ end
96
+ end
97
+
98
+ def html5_field_id(model, field, options = {})
99
+ return options[:form_id] if options[:form_id].present?
100
+
101
+ # TODO: Need to handle the other side of the 1:M use case where
102
+ # the field name needs to end in _ids, not _id.
103
+ field = "#{field}_id" if model.class.reflect_on_association(field).present?
104
+ if options[:index].present?
105
+ if options[:array_name].present?
106
+ if options[:base_name].present?
107
+ "#{options[:form_id_prefix]}#{options[:base_name]}_#{options[:array_name]}_#{options[:index]}_#{field}"
108
+ else
109
+ "#{options[:form_id_prefix]}#{options[:array_name]}_#{options[:index]}_#{field}"
110
+ end
111
+ else
112
+ "#{options[:form_id_prefix]}#{model.class.to_s.underscore}_#{options[:index]}_#{field}"
113
+ end
114
+ else
115
+ "#{options[:form_id_prefix]}#{model.class.to_s.underscore}_#{field}"
116
+ end
117
+ end
118
+
119
+ def html5_field_place_holder(model, field)
120
+ place_holder_key = "ui_form.#{model.class.to_s.underscore}.placeholders.#{field}"
121
+ I18n.exists?(place_holder_key) ? I18n.t(place_holder_key) : nil
122
+ end
123
+
124
+ def html5_checkbox(model, field, options = {})
125
+ classes = options[:classes] || %w[col-sm-12 col-lg-6]
126
+ value = model.send(field)
127
+ options[:disabled] ||= false
128
+ properties = {
129
+ class: 'form-check-input',
130
+ id: html5_field_id(model, field, options),
131
+ name: html5_field_name(model, field, options),
132
+ type: :checkbox,
133
+ disabled: options[:disabled]
134
+ }
135
+ properties[:checked] = true if model.send(field)
136
+ checkbox_tag = tag(:input, properties)
137
+ content_tag(:div, class: (%w[form-check mt-4] + classes).join(' ')) do
138
+ concat(content_tag(:label) do
139
+ concat(checkbox_tag)
140
+ concat(html5_checkbox_label_tag(model, field, value, input_classes: classes))
141
+ end)
142
+ end
143
+ end
144
+
145
+ def html5_checkbox_label_tag(model, field, value, options = {})
146
+ # dont do a label if we are in default browser mode
147
+ return if options[:input_classes].present? && options[:input_classes].include?('browser-default')
148
+
149
+ # or if we have a prompt with now value
150
+ place_holder = html5_field_place_holder(model, field)
151
+ return if place_holder.blank? && value.blank? && options[:prompt].present?
152
+
153
+ error = model.errors[field]
154
+ key = "ui_form.#{model.class.to_s.underscore}.labels.#{field}"
155
+ classes = value.nil? && place_holder.blank? ? '' : 'active'
156
+ classes += error.present? ? ' invalid red-text' : ' valid'
157
+ options[:class] = classes
158
+ options['data-error'] = error.join(', ') if error.present?
159
+ content_tag(:span, options) do
160
+ concat(I18n.exists?(key) ? I18n.t(key) : field.to_s.humanize)
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Helpful methods job states
5
+ #
6
+ module CoreJobStateHelper
7
+ # @bstract Color mapping for states
8
+ JOB_STATE_COLORS = {
9
+ CommandJob::STATE_NEW => 'primary',
10
+ CommandJob::STATE_WIP => 'secondary',
11
+ CommandJob::STATE_RETRYING => 'warning',
12
+ CommandJob::STATE_SUCCESS => 'success',
13
+ CommandJob::STATE_FAIL => 'danger',
14
+ CommandJob::STATE_CANCELLED => 'dark'
15
+ }.freeze
16
+
17
+ def job_state_badge(state)
18
+ color = JOB_STATE_COLORS[state] || 'info'
19
+ content_tag(:span, state.humanize, class: "badge rounded-pill bg-label-#{color}")
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Draw remix icons on the page, includes tooltip mark up
5
+ #
6
+ module CoreRemixIconHelper
7
+ def menu_remix_icon(icon_name, classes: [], type: :line, tooltip_text: nil, tooltip_placement: 'top')
8
+ classes += %w[menu-icon tf-icons]
9
+ remix_icon(icon_name, classes: classes, type: type, tooltip_text: tooltip_text, tooltip_placement: tooltip_placement)
10
+ end
11
+
12
+ # Render a material icon tag
13
+ def remix_icon(icon_name, classes: [], type: :line, tooltip_text: nil, tooltip_placement: 'top')
14
+ classes << "ri-#{icon_name}-#{type}"
15
+ options = { class: classes }
16
+ if tooltip_text.present?
17
+ options['data-bs-toggle'] = 'tooltip'
18
+ options['data-bs-placement'] = tooltip_placement
19
+ options['data-bs-title'] = tooltip_text
20
+ end
21
+ content_tag(:i, options) {}
22
+ end
23
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Helpers to render select form fields using select 2 framework: https://select2.org
4
+ module CoreSelectTwoHelper
5
+ # @abstract Create the select two field for the model and field
6
+ # @todo convert generic options hash to parameters, maybe....
7
+ def select_two_field(model, field, options = {})
8
+ value = model.send(field)
9
+ hint_key = "ui_form.#{model.class.to_s.underscore}.hints.#{field}"
10
+ if I18n.exists?(hint_key)
11
+ base_classes = %w[form-floating form-floating-outline col tooltipped]
12
+ data = { tooltip: I18n.t(hint_key), position: :top }
13
+ else
14
+ base_classes = %w[form-floating form-floating-outline col mb-3]
15
+ data = {}
16
+ end
17
+ classes = (base_classes + (options[:classes] || %w[col-sm-12 col-md-6 col-lg-4 col-xl-3 col-xxl-2])).join(' ')
18
+ content_tag(:div, class: classes, data: data) do
19
+ concat(select_two_tag(model, field, options))
20
+ concat(html5_label_tag(model, field, value, options))
21
+ end
22
+ end
23
+
24
+ def select_two_tag(model, field, options = {})
25
+ select_content = {
26
+ id: html5_field_id(model, field, options),
27
+ name: html5_field_name(model, field, options),
28
+ class: [options[:input_classes], 'select2', 'form-select', 'form-select-lg'].compact.join(' '),
29
+ disabled: options[:disabled],
30
+ data: { 'allow-clear': true, placeholder: field_placeholder_text(model, field, default: options[:prompt]) }
31
+ }
32
+ content_tag(:select, select_content) do
33
+ select_two_option_key_values(model.send(field), options).each do |value|
34
+ concat(content_tag(:option, value: value[:key], selected: value[:selected]) do
35
+ concat(value[:value])
36
+ end)
37
+ end
38
+ end
39
+ end
40
+
41
+ def field_placeholder_text(model, field, default: 'Select Option')
42
+ hint_key = "ui_form.#{model.class.to_s.underscore}.placeholders.#{field}"
43
+ I18n.exists?(hint_key) ? I18n.t(hint_key) : default
44
+ end
45
+
46
+ def select_two_option_key_values(value, options)
47
+ select_options = []
48
+ # select_options << { key: '', value: options[:prompt], selected: false } if options[:prompt].present?
49
+ options[:options].each do |option_value|
50
+ option_value[:display_name] = option_value.display_name if option_value.respond_to?(:display_name)
51
+ option_value[:display_name] = option_value.user_display_name if option_value.respond_to?(:user_display_name)
52
+ select_options << case option_value
53
+ when String, Integer
54
+ { key: option_value,
55
+ value: option_value,
56
+ selected: value.eql?(option_value) }
57
+ when Array
58
+ { key: option_value.last,
59
+ value: option_value.first,
60
+ selected: value.to_s.eql?(option_value.last.to_s) }
61
+ when Hash
62
+ { key: option_value[:key],
63
+ value: option_value[:value],
64
+ selected: value.eql?(option_value[:key]) }
65
+ else
66
+ { key: option_value[:_id].to_s,
67
+ value: option_value[:display_name] || option_value[:name],
68
+ selected: value.eql?(option_value) }
69
+ end
70
+ end
71
+ select_options
72
+ end
73
+ end
@@ -198,7 +198,7 @@ module ModelModalHelper
198
198
  value ? 'Yes' : 'No'
199
199
  when Date, DateTime, Time
200
200
  current_user.local_time(value, :long)
201
- when Integer, Array, Hash
201
+ when Integer, Array, Hash, Float
202
202
  value.to_s
203
203
  else
204
204
  value
@@ -0,0 +1,28 @@
1
+ - title t('.title')
2
+ .card
3
+ .card-body
4
+ .table-responsive.text-nowrap
5
+ %table.table.card-table.border.table-striped
6
+ %thead
7
+ %tr
8
+ %th Host
9
+ %th State
10
+ %th Last Check In
11
+ %th Actions
12
+ %tbody.table-border-bottom-0
13
+ - Cron::Server.each do |server|
14
+ %tr
15
+ %td=[server.host_name, server.pid].join(':')
16
+ %td.align-content-center
17
+ - if server.alive?
18
+ = remix_icon('heart-2', type: :fill)
19
+ - if server.alive?
20
+ = remix_icon('router', type: :fill)
21
+ %td= current_user.local_time(server.last_check_in_at)
22
+ %td
23
+ =dropdown_menu do
24
+ - if server.primary?
25
+ =demote_dropdown_item(server, demote_cron_server_path(server))
26
+ - else
27
+ =delete_dropdown_item(server, cron_server_path(server))
28
+
@@ -0,0 +1,17 @@
1
+ - title t('.title', name: @cron_tab.name.humanize)
2
+ - content_for :breadcrumbs do
3
+ =breadcrumb_step(cron_tabs_path, t('cron_tabs.index.title'))
4
+ %form{action: cron_tab_path(@cron_tab), method: :post}
5
+ %input{type: :hidden, value: form_authenticity_token, name: :authenticity_token}
6
+ %input{type: :hidden, name: '_method', value: :put}
7
+ .card
8
+ .card-body
9
+ .container
10
+ .row
11
+ = html5_text_field(@cron_tab, :min, classes: %w[col-sm-12 col-md-6 col-lg-4 col-xl-3 xol-xxl-2] )
12
+ = html5_text_field(@cron_tab, :hour, classes: %w[col-sm-12 col-md-6 col-lg-4 col-xl-3 xol-xxl-2] )
13
+ = html5_text_field(@cron_tab, :mday, classes: %w[col-sm-12 col-md-6 col-lg-4 col-xl-3 xol-xxl-2] )
14
+ = html5_text_field(@cron_tab, :month, classes: %w[col-sm-12 col-md-6 col-lg-4 col-xl-3 xol-xxl-2] )
15
+ = html5_text_field(@cron_tab, :wday, classes: %w[col-sm-12 col-md-6 col-lg-4 col-xl-3 xol-xxl-2] )
16
+ = html5_checkbox(@cron_tab, :enabled, classes: %w[col-sm-12 col-md-6 col-lg-4 col-xl-3 xol-xxl-2] )
17
+ .card-footer= render 'common/form_actions', form_cancel_path: cron_tabs_path
@@ -0,0 +1,35 @@
1
+ - title t('.title')
2
+ .card
3
+ .card-body
4
+ .table-responsive.text-nowrap
5
+ %table.table.card-table.border.table-striped
6
+ %thead
7
+ %tr
8
+ %th Name
9
+ %th Last Run At
10
+ %th Min
11
+ %th Hour
12
+ %th MDay
13
+ %th Month
14
+ %th WDay
15
+ %th Enabled
16
+ %th Actions
17
+ %tbody.table-border-bottom-0
18
+ - Cron::JobTab.each do |tab|
19
+ %tr
20
+ %td.name=tab.name.humanize
21
+ %td= current_user.local_time(tab.last_run_at)
22
+ %td=tab.min
23
+ %td=tab.hour
24
+ %td=tab.mday
25
+ %td=tab.month
26
+ %td=tab.wday
27
+ %td
28
+ - if tab.valid_environment?
29
+ = remix_icon('check', type: :fill)
30
+ %td
31
+ =dropdown_menu do
32
+ =edit_dropdown_item(tab, edit_cron_tab_path(tab))
33
+ - if tab.valid_environment?
34
+ =run_dropdown_item(tab, run_now_cron_tab_path(tab))
35
+
@@ -1 +1,22 @@
1
- =render 'index'
1
+ - title t('.title')
2
+ .card
3
+ .card-body
4
+ .table-responsive.text-nowrap
5
+ %table.table.card-table.border.table-striped
6
+ %thead
7
+ %tr
8
+ %th Name
9
+ %th Count
10
+ %th Min/Avg/Max
11
+ %th Last Run At
12
+ %th Actions
13
+ %tbody.table-border-bottom-0
14
+ - @metrics.each do |m|
15
+ %tr
16
+ %td.name=m.name
17
+ %td=m.count
18
+ %td=[m.min.round(3), m.avg.round(3), m.max.round(3)].join('/')
19
+ %td=current_user.local_time(m.last_run_at)
20
+ %td.actions
21
+ = delete_dropdown_item(m, model_path(m))
22
+