web47core 3.2.9 → 3.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/helpers/core_breadcrumb_helper.rb +11 -0
- data/app/helpers/core_card_nav_items_helper.rb +60 -0
- data/app/helpers/core_dropdown_helper.rb +89 -0
- data/app/helpers/core_floating_action_button_helper.rb +82 -0
- data/app/helpers/core_html5_form_helper.rb +163 -0
- data/app/helpers/core_job_state_helper.rb +21 -0
- data/app/helpers/core_remix_icon_helper.rb +23 -0
- data/app/helpers/core_select_two_helper.rb +73 -0
- data/app/helpers/model_modal_helper.rb +1 -1
- data/app/views/cron_servers/index.html.haml +28 -0
- data/app/views/cron_tabs/edit.html.haml +17 -0
- data/app/views/cron_tabs/index.html.haml +35 -0
- data/app/views/delayed_job_metrics/index.html.haml +22 -1
- data/app/views/delayed_job_workers/index.html.haml +30 -1
- data/app/views/delayed_jobs/index.html.haml +28 -1
- data/app/views/delayed_jobs/show.html.haml +55 -1
- data/app/views/system_configurations/edit.html.haml +14 -1
- data/app/views/system_configurations/show.html.haml +18 -1
- data/lib/app/controllers/concerns/core_controller.rb +14 -0
- data/lib/app/controllers/concerns/core_cron_servers_controller.rb +31 -0
- data/lib/app/controllers/concerns/core_cron_tabs_controller.rb +41 -0
- data/lib/app/controllers/concerns/core_delayed_job_metrics_controller.rb +2 -2
- data/lib/app/jobs/cron/trim_command_jobs.rb +1 -1
- data/lib/app/models/concerns/class_name.rb +26 -0
- data/lib/app/models/concerns/tag_able.rb +34 -0
- data/lib/app/models/tag.rb +21 -0
- data/lib/app/models/user_audit_log.rb +2 -2
- data/lib/web47core/version.rb +1 -1
- data/lib/web47core.rb +5 -1
- metadata +18 -25
- data/app/views/admin/cron/edit.html.haml +0 -1
- data/app/views/admin/cron/index.html.haml +0 -1
- data/app/views/admin/delayed_jobs/index.html.haml +0 -1
- data/app/views/admin/delayed_jobs/show.html.haml +0 -1
- data/app/views/admin/system_configurations/edit.html.haml +0 -1
- data/app/views/admin/system_configurations/show.html.haml +0 -1
- data/app/views/cron/_edit.html.haml +0 -19
- data/app/views/cron/_index.html.haml +0 -77
- data/app/views/cron/edit.html.haml +0 -1
- data/app/views/cron/index.html.haml +0 -1
- data/app/views/delayed_job_metrics/_index.html.haml +0 -27
- data/app/views/delayed_job_workers/_index.html.haml +0 -27
- data/app/views/delayed_jobs/_index.html.haml +0 -49
- data/app/views/delayed_jobs/_show.html.haml +0 -60
- data/app/views/stack/cron/edit.html.haml +0 -1
- data/app/views/stack/cron/index.html.haml +0 -1
- data/app/views/stack/delayed_jobs/index.html.haml +0 -1
- data/app/views/stack/delayed_jobs/show.html.haml +0 -1
- data/app/views/stack/system_configurations/edit.html.haml +0 -1
- data/app/views/stack/system_configurations/show.html.haml +0 -1
- data/app/views/system_configurations/_edit.html.haml +0 -15
- data/app/views/system_configurations/_show.html.haml +0 -22
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 327cae83d882b83a5d99350fdf424929fad212fcf77f2d611ad6bb0fd3ac3516
|
4
|
+
data.tar.gz: f6ee9926c857f10177ea4b4357b5d70c6dca7de71538e804f7f3ccf4b6165e50
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
@@ -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
|
-
|
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
|
+
|