web47core 3.2.9 → 3.2.12
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|